X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=47f1453bebc2cf89cf906e8d1d75c698f1cfeffd;hp=55b3d8c293c9c1ec5008890c3a9640855cf81767;hb=93cc2d6e053031a7008f73ca1ea072038bfa3fc8;hpb=8c5d388a6e12d1a8bd4aa565920238f8a921414a diff --git a/src/src/expand.c b/src/src/expand.c index 55b3d8c29..47f1453be 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -201,6 +201,8 @@ static uschar *op_table_main[] = { US"addresses", US"base62", US"base62d", + US"base64", + US"base64d", US"domain", US"escape", US"eval", @@ -241,6 +243,8 @@ enum { EOP_ADDRESSES, EOP_BASE62, EOP_BASE62D, + EOP_BASE64, + EOP_BASE64D, EOP_DOMAIN, EOP_ESCAPE, EOP_EVAL, @@ -516,7 +520,7 @@ static var_entry var_table[] = { { "dnslist_value", vtype_stringptr, &dnslist_value }, { "domain", vtype_stringptr, &deliver_domain }, { "domain_data", vtype_stringptr, &deliver_domain_data }, -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT { "event_data", vtype_stringptr, &event_data }, /*XXX want to use generic vars for as many of these as possible*/ @@ -539,6 +543,7 @@ static var_entry var_table[] = { { "host_lookup_deferred",vtype_int, &host_lookup_deferred }, { "host_lookup_failed", vtype_int, &host_lookup_failed }, { "host_port", vtype_int, &deliver_host_port }, + { "initial_cwd", vtype_stringptr, &initial_cwd }, { "inode", vtype_ino, &deliver_inode }, { "interface_address", vtype_stringptr, &interface_address }, { "interface_port", vtype_int, &interface_port }, @@ -1069,6 +1074,8 @@ return s; Returns: a pointer to the character after the last digit */ +/*XXX consider expanding to int_eximarith_t. But the test for +"overbig numbers" in 0002 still needs to overflow it. */ static uschar * read_number(int *n, uschar *s) @@ -1725,7 +1732,7 @@ if ((Ustrncmp(name, "acl_c", 5) == 0 || Ustrncmp(name, "acl_m", 5) == 0) && { tree_node *node = tree_search((name[4] == 'c')? acl_var_c : acl_var_m, name + 4); - return (node == NULL)? (strict_acl_vars? NULL : US"") : node->data.ptr; + return node ? node->data.ptr : strict_acl_vars ? NULL : US""; } /* Handle $auth variables. */ @@ -1760,153 +1767,150 @@ val = vp->value; switch (vp->type) { case vtype_filter_int: - if (!filter_running) return NULL; - /* Fall through */ - /* VVVVVVVVVVVV */ + if (!filter_running) return NULL; + /* Fall through */ + /* VVVVVVVVVVVV */ case vtype_int: - sprintf(CS var_buffer, "%d", *(int *)(val)); /* Integer */ - return var_buffer; + sprintf(CS var_buffer, "%d", *(int *)(val)); /* Integer */ + return var_buffer; case vtype_ino: - sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(val))); /* Inode */ - return var_buffer; + sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(val))); /* Inode */ + return var_buffer; case vtype_gid: - sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(val))); /* gid */ - return var_buffer; + sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(val))); /* gid */ + return var_buffer; case vtype_uid: - sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(val))); /* uid */ - return var_buffer; + sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(val))); /* uid */ + return var_buffer; case vtype_bool: - sprintf(CS var_buffer, "%s", *(BOOL *)(val) ? "yes" : "no"); /* bool */ - return var_buffer; + sprintf(CS var_buffer, "%s", *(BOOL *)(val) ? "yes" : "no"); /* bool */ + return var_buffer; case vtype_stringptr: /* Pointer to string */ - s = *((uschar **)(val)); - return (s == NULL)? US"" : s; + return (s = *((uschar **)(val))) ? s : US""; case vtype_pid: - sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */ - return var_buffer; + sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */ + return var_buffer; case vtype_load_avg: - sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */ - return var_buffer; + sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */ + return var_buffer; case vtype_host_lookup: /* Lookup if not done so */ - if (sender_host_name == NULL && sender_host_address != NULL && - !host_lookup_failed && host_name_lookup() == OK) - host_build_sender_fullhost(); - return (sender_host_name == NULL)? US"" : sender_host_name; + if (sender_host_name == NULL && sender_host_address != NULL && + !host_lookup_failed && host_name_lookup() == OK) + host_build_sender_fullhost(); + return (sender_host_name == NULL)? US"" : sender_host_name; case vtype_localpart: /* Get local part from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; - domain = Ustrrchr(s, '@'); - if (domain == NULL) return s; - if (domain - s > sizeof(var_buffer) - 1) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT - " in string expansion", sizeof(var_buffer)); - Ustrncpy(var_buffer, s, domain - s); - var_buffer[domain - s] = 0; - return var_buffer; + s = *((uschar **)(val)); + if (s == NULL) return US""; + domain = Ustrrchr(s, '@'); + if (domain == NULL) return s; + if (domain - s > sizeof(var_buffer) - 1) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT + " in string expansion", sizeof(var_buffer)); + Ustrncpy(var_buffer, s, domain - s); + var_buffer[domain - s] = 0; + return var_buffer; case vtype_domain: /* Get domain from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; - domain = Ustrrchr(s, '@'); - return (domain == NULL)? US"" : domain + 1; + s = *((uschar **)(val)); + if (s == NULL) return US""; + domain = Ustrrchr(s, '@'); + return (domain == NULL)? US"" : domain + 1; case vtype_msgheaders: - return find_header(NULL, exists_only, newsize, FALSE, NULL); + return find_header(NULL, exists_only, newsize, FALSE, NULL); case vtype_msgheaders_raw: - return find_header(NULL, exists_only, newsize, TRUE, NULL); + return find_header(NULL, exists_only, newsize, TRUE, NULL); case vtype_msgbody: /* Pointer to msgbody string */ case vtype_msgbody_end: /* Ditto, the end of the msg */ - ss = (uschar **)(val); - if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */ - { - uschar *body; - off_t start_offset = SPOOL_DATA_START_OFFSET; - int len = message_body_visible; - if (len > message_size) len = message_size; - *ss = body = store_malloc(len+1); - body[0] = 0; - if (vp->type == vtype_msgbody_end) + ss = (uschar **)(val); + if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */ { - struct stat statbuf; - if (fstat(deliver_datafile, &statbuf) == 0) + uschar *body; + off_t start_offset = SPOOL_DATA_START_OFFSET; + int len = message_body_visible; + if (len > message_size) len = message_size; + *ss = body = store_malloc(len+1); + body[0] = 0; + if (vp->type == vtype_msgbody_end) { - start_offset = statbuf.st_size - len; - if (start_offset < SPOOL_DATA_START_OFFSET) - start_offset = SPOOL_DATA_START_OFFSET; - } - } - lseek(deliver_datafile, start_offset, SEEK_SET); - len = read(deliver_datafile, body, len); - if (len > 0) - { - body[len] = 0; - if (message_body_newlines) /* Separate loops for efficiency */ - { - while (len > 0) - { if (body[--len] == 0) body[len] = ' '; } + struct stat statbuf; + if (fstat(deliver_datafile, &statbuf) == 0) + { + start_offset = statbuf.st_size - len; + if (start_offset < SPOOL_DATA_START_OFFSET) + start_offset = SPOOL_DATA_START_OFFSET; + } } - else + if (lseek(deliver_datafile, start_offset, SEEK_SET) < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "deliver_datafile lseek: %s", + strerror(errno)); + len = read(deliver_datafile, body, len); + if (len > 0) { - while (len > 0) - { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; } + body[len] = 0; + if (message_body_newlines) /* Separate loops for efficiency */ + while (len > 0) + { if (body[--len] == 0) body[len] = ' '; } + else + while (len > 0) + { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; } } } - } - return (*ss == NULL)? US"" : *ss; + return (*ss == NULL)? US"" : *ss; case vtype_todbsdin: /* BSD inbox time of day */ - return tod_stamp(tod_bsdin); + return tod_stamp(tod_bsdin); case vtype_tode: /* Unix epoch time of day */ - return tod_stamp(tod_epoch); + return tod_stamp(tod_epoch); case vtype_todel: /* Unix epoch/usec time of day */ - return tod_stamp(tod_epoch_l); + return tod_stamp(tod_epoch_l); case vtype_todf: /* Full time of day */ - return tod_stamp(tod_full); + return tod_stamp(tod_full); case vtype_todl: /* Log format time of day */ - return tod_stamp(tod_log_bare); /* (without timezone) */ + return tod_stamp(tod_log_bare); /* (without timezone) */ case vtype_todzone: /* Time zone offset only */ - return tod_stamp(tod_zone); + return tod_stamp(tod_zone); case vtype_todzulu: /* Zulu time */ - return tod_stamp(tod_zulu); + return tod_stamp(tod_zulu); case vtype_todlf: /* Log file datestamp tod */ - return tod_stamp(tod_log_datestamp_daily); + return tod_stamp(tod_log_datestamp_daily); case vtype_reply: /* Get reply address */ - s = find_header(US"reply-to:", exists_only, newsize, TRUE, - headers_charset); - if (s != NULL) while (isspace(*s)) s++; - if (s == NULL || *s == 0) - { - *newsize = 0; /* For the *s==0 case */ - s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset); - } - if (s != NULL) - { - uschar *t; - while (isspace(*s)) s++; - for (t = s; *t != 0; t++) if (*t == '\n') *t = ' '; - while (t > s && isspace(t[-1])) t--; - *t = 0; - } - return (s == NULL)? US"" : s; + s = find_header(US"reply-to:", exists_only, newsize, TRUE, + headers_charset); + if (s != NULL) while (isspace(*s)) s++; + if (s == NULL || *s == 0) + { + *newsize = 0; /* For the *s==0 case */ + s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset); + } + if (s != NULL) + { + uschar *t; + while (isspace(*s)) s++; + for (t = s; *t != 0; t++) if (*t == '\n') *t = ' '; + while (t > s && isspace(t[-1])) t--; + *t = 0; + } + return (s == NULL)? US"" : s; case vtype_string_func: { @@ -1931,12 +1935,12 @@ switch (vp->type) return var_buffer; case vtype_cert: - return *(void **)val ? US"" : US""; + return *(void **)val ? US"" : US""; - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM case vtype_dkim: - return dkim_exim_expand_query((int)(long)val); - #endif + return dkim_exim_expand_query((int)(long)val); +#endif } @@ -2692,7 +2696,7 @@ switch(cond_type) if (sublen == 24) { - uschar *coded = auth_b64encode((uschar *)digest, 16); + uschar *coded = b64encode((uschar *)digest, 16); DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n" " subject=%s\n crypted=%s\n", coded, sub[1]+5); tempcond = (Ustrcmp(coded, sub[1]+5) == 0); @@ -2730,7 +2734,7 @@ switch(cond_type) if (sublen == 28) { - uschar *coded = auth_b64encode((uschar *)digest, 20); + uschar *coded = b64encode((uschar *)digest, 20); DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n" " subject=%s\n crypted=%s\n", coded, sub[1]+6); tempcond = (Ustrcmp(coded, sub[1]+6) == 0); @@ -2787,7 +2791,7 @@ switch(cond_type) #define XSTR(s) STR(s) DEBUG(D_auth) debug_printf("crypteq: using %s()\n" " subject=%s\n crypted=%s\n", - (which == 0)? XSTR(DEFAULT_CRYPT) : (which == 1)? "crypt" : "crypt16", + which == 0 ? XSTR(DEFAULT_CRYPT) : which == 1 ? "crypt" : "crypt16", coded, sub[1]); #undef STR #undef XSTR @@ -2796,8 +2800,16 @@ switch(cond_type) salt), force failure. Otherwise we get false positives: with an empty string the yield of crypt() is an empty string! */ - tempcond = (Ustrlen(sub[1]) < 2)? FALSE : - (Ustrcmp(coded, sub[1]) == 0); + if (coded) + tempcond = Ustrlen(sub[1]) < 2 ? FALSE : Ustrcmp(coded, sub[1]) == 0; + else if (errno == EINVAL) + tempcond = FALSE; + else + { + expand_string_message = string_sprintf("crypt error: %s\n", + US strerror(errno)); + return NULL; + } } break; #endif /* SUPPORT_CRYPTEQ */ @@ -3143,7 +3155,8 @@ Arguments: yieldptr points to the output string pointer sizeptr points to the output string size ptrptr points to the output string pointer - type "lookup" or "if" or "extract" or "run", for error message + type "lookup", "if", "extract", "run", "env", "listextract" or + "certextract" for error message resetok if not NULL, pointer to flag - write FALSE if unsafe to reset the store. @@ -3174,7 +3187,7 @@ if (*s == '}') } else { - if (yes && lookup_value != NULL) + if (yes && lookup_value) *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, lookup_value, Ustrlen(lookup_value)); lookup_value = save_lookup; @@ -3201,10 +3214,11 @@ if (*s++ != '}') goto FAILED_CURLY; if (yes) *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, sub1, Ustrlen(sub1)); -/* If this is called from a lookup or an extract, we want to restore $value to -what it was at the start of the item, so that it has this value during the -second string expansion. For the call from "if" or "run" to this function, -save_lookup is set to lookup_value, so that this statement does nothing. */ +/* If this is called from a lookup/env or a (cert)extract, we want to restore +$value to what it was at the start of the item, so that it has this value +during the second string expansion. For the call from "if" or "run" to this +function, save_lookup is set to lookup_value, so that this statement does +nothing. */ lookup_value = save_lookup; @@ -3394,8 +3408,8 @@ if (Ustrlen(key) > 64) return NULL; hash_source = string_cat(NULL,&size,&offset,key_num,1); -string_cat(hash_source,&size,&offset,daystamp,3); -string_cat(hash_source,&size,&offset,address,Ustrlen(address)); +hash_source = string_cat(hash_source,&size,&offset,daystamp,3); +hash_source = string_cat(hash_source,&size,&offset,address,Ustrlen(address)); hash_source[offset] = '\0'; DEBUG(D_expand) debug_printf("prvs: hash source is '%s'\n", hash_source); @@ -3435,9 +3449,9 @@ return finalhash_hex; * Join a file onto the output string * *************************************************/ -/* This is used for readfile and after a run expansion. It joins the contents -of a file onto the output string, globally replacing newlines with a given -string (optionally). The file is closed at the end. +/* This is used for readfile/readsock and after a run expansion. +It joins the contents of a file onto the output string, globally replacing +newlines with a given string (optionally). Arguments: f the FILE @@ -3452,21 +3466,19 @@ Returns: new value of string pointer static uschar * cat_file(FILE *f, uschar *yield, int *sizep, int *ptrp, uschar *eol) { -int eollen; +int eollen = eol ? Ustrlen(eol) : 0; uschar buffer[1024]; -eollen = (eol == NULL)? 0 : Ustrlen(eol); - -while (Ufgets(buffer, sizeof(buffer), f) != NULL) +while (Ufgets(buffer, sizeof(buffer), f)) { int len = Ustrlen(buffer); - if (eol != NULL && buffer[len-1] == '\n') len--; + if (eol && buffer[len-1] == '\n') len--; yield = string_cat(yield, sizep, ptrp, buffer, len); if (buffer[len] != 0) yield = string_cat(yield, sizep, ptrp, eol, eollen); } -if (yield != NULL) yield[*ptrp] = 0; +if (yield) yield[*ptrp] = 0; return yield; } @@ -3923,16 +3935,12 @@ while (*s != 0) /* Variable */ - else + else if (!(value = find_variable(name, FALSE, skipping, &newsize))) { - value = find_variable(name, FALSE, skipping, &newsize); - if (value == NULL) - { - expand_string_message = - string_sprintf("unknown variable name \"%s\"", name); - check_variable_error_message(name); - goto EXPAND_FAILED; - } + expand_string_message = + string_sprintf("unknown variable name \"%s\"", name); + check_variable_error_message(name); + goto EXPAND_FAILED; } /* If the data is known to be in a new buffer, newsize will be set to the @@ -4866,8 +4874,7 @@ while (*s != 0) const uschar **argv; pid_t pid; int fd_in, fd_out; - int lsize = 0; - int lptr = 0; + int lsize = 0, lptr = 0; if ((expand_forbid & RDO_RUN) != 0) { @@ -4895,15 +4902,11 @@ while (*s != 0) NULL, /* no transporting address */ US"${run} expansion", /* for error messages */ &expand_string_message)) /* where to put error message */ - { goto EXPAND_FAILED; - } /* Create the child process, making it a group leader. */ - pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE); - - if (pid < 0) + if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE)) < 0) { expand_string_message = string_sprintf("couldn't create child process: %s", strerror(errno)); @@ -4916,12 +4919,14 @@ while (*s != 0) /* Read the pipe to get the command's output into $value (which is kept in lookup_value). Read during execution, so that if the output exceeds - the OS pipe buffer limit, we don't block forever. */ + the OS pipe buffer limit, we don't block forever. Remember to not release + memory just allocated for $value. */ + resetok = FALSE; f = fdopen(fd_out, "rb"); sigalrm_seen = FALSE; alarm(60); - lookup_value = cat_file(f, lookup_value, &lsize, &lptr, NULL); + lookup_value = cat_file(f, NULL, &lsize, &lptr, NULL); alarm(0); (void)fclose(f); @@ -5302,9 +5307,25 @@ while (*s != 0) int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); - /* Read the arguments */ + /* While skipping we cannot rely on the data for expansions being + available (eg. $item) hence cannot decide on numeric vs. keyed. + Just read as many arguments as there are. */ - for (i = 0; i < j; i++) + if (skipping) + { + while (isspace(*s)) s++; + while (*s == '{') + { + if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)) + goto EXPAND_FAILED; /*{*/ + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + while (isspace(*s)) s++; + } + if (*s != '}') + goto EXPAND_FAILED_CURLY; + } + + else for (i = 0; i < j; i++) /* Read the proper number of arguments */ { while (isspace(*s)) s++; if (*s == '{') /*}*/ @@ -5331,27 +5352,24 @@ while (*s != 0) while (len > 0 && isspace(p[len-1])) len--; p[len] = 0; - if (!skipping) + if (*p == 0) { - if (*p == 0) - { - expand_string_message = US"first argument of \"extract\" must " - "not be empty"; - goto EXPAND_FAILED; - } + expand_string_message = US"first argument of \"extract\" must " + "not be empty"; + goto EXPAND_FAILED; + } - if (*p == '-') - { - field_number = -1; - p++; - } - while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0'; - if (*p == 0) - { - field_number *= x; - j = 3; /* Need 3 args */ - field_number_set = TRUE; - } + if (*p == '-') + { + field_number = -1; + p++; + } + while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0'; + if (*p == 0) + { + field_number *= x; + j = 3; /* Need 3 args */ + field_number_set = TRUE; } } } @@ -5469,7 +5487,7 @@ while (*s != 0) &yield, /* output pointer */ &size, /* output size */ &ptr, /* output current point */ - US"extract", /* condition type */ + US"listextract", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -5541,7 +5559,7 @@ while (*s != 0) &yield, /* output pointer */ &size, /* output size */ &ptr, /* output current point */ - US"extract", /* condition type */ + US"certextract", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -6039,6 +6057,7 @@ while (*s != 0) case EOP_MD5: case EOP_SHA1: case EOP_SHA256: + case EOP_BASE64: if (s[1] == '$') { const uschar * s1 = s; @@ -6242,7 +6261,7 @@ while (*s != 0) } } - enc = auth_b64encode(sub, out - sub); + enc = b64encode(sub, out - sub); yield = string_cat(yield, &size, &ptr, enc, Ustrlen(enc)); continue; } @@ -6883,9 +6902,30 @@ while (*s != 0) /* Convert string to base64 encoding */ case EOP_STR2B64: + case EOP_BASE64: + { +#ifdef SUPPORT_TLS + uschar * s = vp && *(void **)vp->value + ? tls_cert_der_b64(*(void **)vp->value) + : b64encode(sub, Ustrlen(sub)); +#else + uschar * s = b64encode(sub, Ustrlen(sub)); +#endif + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } + + case EOP_BASE64D: { - uschar *encstr = auth_b64encode(sub, Ustrlen(sub)); - yield = string_cat(yield, &size, &ptr, encstr, Ustrlen(encstr)); + uschar * s; + int len = b64decode(sub, &s); + if (len < 0) + { + expand_string_message = string_sprintf("string \"%s\" is not " + "well-formed for \"%s\" operator", sub, name); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); continue; } @@ -7482,22 +7522,22 @@ for (i = 1; i < argc; i++) if (Ustrspn(argv[i], "abcdefghijklmnopqrtsuvwxyz0123456789-.:/") == Ustrlen(argv[i])) { - #ifdef LOOKUP_LDAP +#ifdef LOOKUP_LDAP eldap_default_servers = argv[i]; - #endif - #ifdef LOOKUP_MYSQL +#endif +#ifdef LOOKUP_MYSQL mysql_servers = argv[i]; - #endif - #ifdef LOOKUP_PGSQL +#endif +#ifdef LOOKUP_PGSQL pgsql_servers = argv[i]; - #endif - #ifdef EXPERIMENTAL_REDIS +#endif +#ifdef LOOKUP_REDIS redis_servers = argv[i]; - #endif +#endif } - #ifdef EXIM_PERL +#ifdef EXIM_PERL else opt_perl_startup = argv[i]; - #endif +#endif } printf("Testing string expansion: debug_level = %d\n\n", debug_level);