X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=b4cc79d4b46a7eee0aa387c2424732bef3d1de39;hp=b4e2a5a834163f3561ef6d80e6a9476cc8fddc83;hb=13559da6973c1cd590467eec74fda18717fe0116;hpb=0539a19dc27efcfc77713a87aba6b61fef947249 diff --git a/src/src/expand.c b/src/src/expand.c index b4e2a5a83..b4cc79d4b 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -105,12 +105,13 @@ static uschar *item_table[] = { US"acl", US"certextract", US"dlfunc", + US"env", US"extract", US"filter", US"hash", US"hmac", US"if", -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N US"imapfolder", #endif US"length", @@ -134,12 +135,13 @@ enum { EITEM_ACL, EITEM_CERTEXTRACT, EITEM_DLFUNC, + EITEM_ENV, EITEM_EXTRACT, EITEM_FILTER, EITEM_HASH, EITEM_HMAC, EITEM_IF, -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N EITEM_IMAPFOLDER, #endif EITEM_LENGTH, @@ -171,7 +173,7 @@ static uschar *op_table_underscore[] = { US"reverse_ip", US"time_eval", US"time_interval" -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N ,US"utf8_domain_from_alabel", US"utf8_domain_to_alabel", US"utf8_localpart_from_alabel", @@ -186,7 +188,7 @@ enum { EOP_REVERSE_IP, EOP_TIME_EVAL, EOP_TIME_INTERVAL -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N ,EOP_UTF8_DOMAIN_FROM_ALABEL, EOP_UTF8_DOMAIN_TO_ALABEL, EOP_UTF8_LOCALPART_FROM_ALABEL, @@ -199,6 +201,8 @@ static uschar *op_table_main[] = { US"addresses", US"base62", US"base62d", + US"base64", + US"base64d", US"domain", US"escape", US"eval", @@ -208,6 +212,8 @@ static uschar *op_table_main[] = { US"hash", US"hex2b64", US"hexquote", + US"ipv6denorm", + US"ipv6norm", US"l", US"lc", US"length", @@ -237,6 +243,8 @@ enum { EOP_ADDRESSES, EOP_BASE62, EOP_BASE62D, + EOP_BASE64, + EOP_BASE64D, EOP_DOMAIN, EOP_ESCAPE, EOP_EVAL, @@ -246,6 +254,8 @@ enum { EOP_HASH, EOP_HEX2B64, EOP_HEXQUOTE, + EOP_IPV6DENORM, + EOP_IPV6NORM, EOP_L, EOP_LC, EOP_LENGTH, @@ -460,6 +470,7 @@ static var_entry var_table[] = { { "bounce_return_size_limit", vtype_int, &bounce_return_size_limit }, { "caller_gid", vtype_gid, &real_gid }, { "caller_uid", vtype_uid, &real_uid }, + { "callout_address", vtype_stringptr, &callout_address }, { "compile_date", vtype_stringptr, &version_date }, { "compile_number", vtype_stringptr, &version_cnumber }, { "config_dir", vtype_stringptr, &config_main_directory }, @@ -486,6 +497,7 @@ static var_entry var_table[] = { { "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES }, { "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY }, { "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY }, + { "dkim_key_length", vtype_int, &dkim_key_length }, { "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS }, { "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES }, { "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE }, @@ -508,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*/ @@ -565,7 +577,7 @@ static var_entry var_table[] = { { "message_id", vtype_stringptr, &message_id }, { "message_linecount", vtype_int, &message_linecount }, { "message_size", vtype_int, &message_size }, -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N { "message_smtputf8", vtype_bool, &message_smtputf8 }, #endif #ifdef WITH_CONTENT_SCAN @@ -603,13 +615,16 @@ static var_entry var_table[] = { { "parent_domain", vtype_stringptr, &deliver_domain_parent }, { "parent_local_part", vtype_stringptr, &deliver_localpart_parent }, { "pid", vtype_pid, NULL }, +#ifndef DISABLE_PRDR + { "prdr_requested", vtype_bool, &prdr_requested }, +#endif { "primary_hostname", vtype_stringptr, &primary_hostname }, -#ifdef EXPERIMENTAL_PROXY - { "proxy_host_address", vtype_stringptr, &proxy_host_address }, - { "proxy_host_port", vtype_int, &proxy_host_port }, +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) + { "proxy_external_address",vtype_stringptr, &proxy_external_address }, + { "proxy_external_port", vtype_int, &proxy_external_port }, + { "proxy_local_address", vtype_stringptr, &proxy_local_address }, + { "proxy_local_port", vtype_int, &proxy_local_port }, { "proxy_session", vtype_bool, &proxy_session }, - { "proxy_target_address",vtype_stringptr, &proxy_target_address }, - { "proxy_target_port", vtype_int, &proxy_target_port }, #endif { "prvscheck_address", vtype_stringptr, &prvscheck_address }, { "prvscheck_keynum", vtype_stringptr, &prvscheck_keynum }, @@ -1058,6 +1073,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) @@ -1264,7 +1281,7 @@ certfield * cp; if (!(vp = find_var_ent(certvar))) { - expand_string_message = + expand_string_message = string_sprintf("no variable named \"%s\"", certvar); return NULL; /* Unknown variable name */ } @@ -1272,7 +1289,7 @@ if (!(vp = find_var_ent(certvar))) want to do that in future */ if (vp->type != vtype_cert) { - expand_string_message = + expand_string_message = string_sprintf("\"%s\" is not a certificate", certvar); return NULL; /* Unknown variable name */ } @@ -1292,7 +1309,7 @@ for(cp = certfields; return (*cp->getfn)( *(void **)vp->value, modifier ); } -expand_string_message = +expand_string_message = string_sprintf("bad field selector \"%s\" for certextract", field); return NULL; } @@ -1714,7 +1731,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. */ @@ -1724,7 +1741,14 @@ if (Ustrncmp(name, "auth", 4) == 0) uschar *endptr; int n = Ustrtoul(name + 4, &endptr, 10); if (*endptr == 0 && n != 0 && n <= AUTH_VARS) - return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1]; + return !auth_vars[n-1] ? US"" : auth_vars[n-1]; + } +else if (Ustrncmp(name, "regex", 5) == 0) + { + uschar *endptr; + int n = Ustrtoul(name + 5, &endptr, 10); + if (*endptr == 0 && n != 0 && n <= REGEX_VARS) + return !regex_vars[n-1] ? US"" : regex_vars[n-1]; } /* For all other variables, search the table */ @@ -1742,153 +1766,148 @@ 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) - { - 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; - } - } - lseek(deliver_datafile, start_offset, SEEK_SET); - len = read(deliver_datafile, body, len); - if (len > 0) + ss = (uschar **)(val); + if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */ { - body[len] = 0; - if (message_body_newlines) /* Separate loops for efficiency */ + 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) { - 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 + lseek(deliver_datafile, start_offset, SEEK_SET); + 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: { @@ -1913,12 +1932,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 } @@ -2674,7 +2693,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); @@ -2712,7 +2731,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); @@ -2769,7 +2788,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 @@ -2778,8 +2797,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 */ @@ -3125,7 +3152,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. @@ -3156,7 +3184,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; @@ -3183,10 +3211,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; @@ -3417,9 +3446,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 @@ -3434,21 +3463,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; } @@ -3905,16 +3932,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 @@ -4001,7 +4024,8 @@ while (*s != 0) uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; - switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl", &resetok)) + switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, US"acl", + &resetok)) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -4076,7 +4100,7 @@ while (*s != 0) continue; } -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N case EITEM_IMAPFOLDER: { /* ${imapfolder {name}{sep]{specials}} */ uschar *sub_arg[3]; @@ -4097,9 +4121,9 @@ while (*s != 0) } else if (Ustrlen(sub_arg[1]) != 1) { - expand_string_message = + expand_string_message = string_sprintf( - "IMAP folder separator must be one character, found \"%s\"", + "IMAP folder separator must be one character, found \"%s\"", sub_arg[1]); goto EXPAND_FAILED; } @@ -4847,8 +4871,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) { @@ -4876,15 +4899,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)); @@ -4897,12 +4916,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); @@ -5450,7 +5471,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 */ @@ -5522,7 +5543,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 */ @@ -5859,12 +5880,12 @@ while (*s != 0) #define EXPAND_DLFUNC_MAX_ARGS 8 case EITEM_DLFUNC: - #ifndef EXPAND_DLFUNC - expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/ - "is not included in this binary"; - goto EXPAND_FAILED; +#ifndef EXPAND_DLFUNC + expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/ + "is not included in this binary"; + goto EXPAND_FAILED; - #else /* EXPAND_DLFUNC */ +#else /* EXPAND_DLFUNC */ { tree_node *t; exim_dlfunc_t *func; @@ -5950,7 +5971,39 @@ while (*s != 0) goto EXPAND_FAILED; } } - #endif /* EXPAND_DLFUNC */ +#endif /* EXPAND_DLFUNC */ + + case EITEM_ENV: /* ${env {name} {val_if_found} {val_if_unfound}} */ + { + uschar * key; + uschar *save_lookup_value = lookup_value; + + while (isspace(*s)) s++; + if (*s != '{') /*}*/ + goto EXPAND_FAILED; + + key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + if (!key) goto EXPAND_FAILED; /*{*/ + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + lookup_value = US getenv(CS key); + + switch(process_yesno( + skipping, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + &size, /* output size */ + &ptr, /* output current point */ + US"env", /* condition type */ + &resetok)) + { + case 1: goto EXPAND_FAILED; /* when all is well, the */ + case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ + } + continue; + } } /* EITEM_* switch */ /* Control reaches here if the name is not recognized as one of the more @@ -5988,6 +6041,7 @@ while (*s != 0) case EOP_MD5: case EOP_SHA1: case EOP_SHA256: + case EOP_BASE64: if (s[1] == '$') { const uschar * s1 = s; @@ -6191,7 +6245,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; } @@ -6360,6 +6414,39 @@ while (*s != 0) continue; } + case EOP_IPV6NORM: + case EOP_IPV6DENORM: + { + int type = string_is_ip_address(sub, NULL); + int binary[4]; + uschar buffer[44]; + + switch (type) + { + case 6: + (void) host_aton(sub, binary); + break; + + case 4: /* convert to IPv4-mapped IPv6 */ + binary[0] = binary[1] = 0; + binary[2] = 0x0000ffff; + (void) host_aton(sub, binary+3); + break; + + case 0: + expand_string_message = + string_sprintf("\"%s\" is not an IP address", sub); + goto EXPAND_FAILED; + } + + yield = string_cat(yield, &size, &ptr, buffer, + c == EOP_IPV6NORM + ? ipv6_nmtoa(binary, buffer) + : host_nmtoa(4, binary, -1, buffer, ':') + ); + continue; + } + case EOP_ADDRESS: case EOP_LOCAL_PART: case EOP_DOMAIN: @@ -6593,7 +6680,7 @@ while (*s != 0) } /* replace illegal UTF-8 sequences by replacement character */ - + #define UTF8_REPLACEMENT_CHAR US"?" case EOP_UTF8CLEAN: @@ -6602,7 +6689,7 @@ while (*s != 0) int bytes_left = 0; long codepoint = -1; uschar seq_buff[4]; /* accumulate utf-8 here */ - + while (*sub != 0) { int complete = 0; @@ -6673,7 +6760,7 @@ while (*s != 0) continue; } -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N case EOP_UTF8_DOMAIN_TO_ALABEL: { uschar * error = NULL; @@ -6799,9 +6886,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; } @@ -7398,22 +7506,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);