X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=989e97b84bed7cfc9fe34442f51de71434c9ad40;hb=3327394394a45c79cd48b2091536f6a6b8ba32a3;hp=cdc914f5e13c31a6789b5d4f0327d427410b1235;hpb=9ad27f49dae5a6375d7951679c90c2e26ad82b5c;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/expand.c b/src/src/expand.c index cdc914f5e..989e97b84 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -130,7 +131,7 @@ static uschar *item_table[] = { US"run", US"sg", US"sort", -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS US"srs_encode", #endif US"substr", @@ -165,7 +166,7 @@ enum { EITEM_RUN, EITEM_SG, EITEM_SORT, -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS EITEM_SRS_ENCODE, #endif EITEM_SUBSTR, @@ -215,7 +216,6 @@ static uschar *op_table_main[] = { US"base62d", US"base64", US"base64d", - US"bless", US"domain", US"escape", US"escape8bit", @@ -263,7 +263,6 @@ enum { EOP_BASE62D, EOP_BASE64, EOP_BASE64D, - EOP_BLESS, EOP_DOMAIN, EOP_ESCAPE, EOP_ESCAPE8BIT, @@ -333,7 +332,7 @@ static uschar *cond_table[] = { US"gei", US"gt", US"gti", -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS US"inbound_srs", #endif US"inlist", @@ -386,7 +385,7 @@ enum { ECOND_STR_GEI, ECOND_STR_GT, ECOND_STR_GTI, -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS ECOND_INBOUND_SRS, #endif ECOND_INLIST, @@ -594,7 +593,6 @@ static var_entry var_table[] = { { "local_part_prefix_v", vtype_stringptr, &deliver_localpart_prefix_v }, { "local_part_suffix", vtype_stringptr, &deliver_localpart_suffix }, { "local_part_suffix_v", vtype_stringptr, &deliver_localpart_suffix_v }, - { "local_part_verified", vtype_stringptr, &deliver_localpart_verified }, #ifdef HAVE_LOCAL_SCAN { "local_scan_data", vtype_stringptr, &local_scan_data }, #endif @@ -752,16 +750,16 @@ static var_entry var_table[] = { { "spool_directory", vtype_stringptr, &spool_directory }, { "spool_inodes", vtype_pinodes, (void *)TRUE }, { "spool_space", vtype_pspace, (void *)TRUE }, -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT { "srs_db_address", vtype_stringptr, &srs_db_address }, { "srs_db_key", vtype_stringptr, &srs_db_key }, { "srs_orig_recipient", vtype_stringptr, &srs_orig_recipient }, { "srs_orig_sender", vtype_stringptr, &srs_orig_sender }, #endif -#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) +#if defined(EXPERIMENTAL_SRS_ALT) || defined(SUPPORT_SRS) { "srs_recipient", vtype_stringptr, &srs_recipient }, #endif -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT { "srs_status", vtype_stringptr, &srs_status }, #endif { "thisaddress", vtype_stringptr, &filter_thisaddress }, @@ -779,7 +777,7 @@ static var_entry var_table[] = { { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME { "tls_in_resumption", vtype_int, &tls_in.resumption }, #endif #ifndef DISABLE_TLS @@ -797,7 +795,7 @@ static var_entry var_table[] = { { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, { "tls_out_peercert", vtype_cert, &tls_out.peercert }, { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn }, -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME { "tls_out_resumption", vtype_int, &tls_out.resumption }, #endif #ifndef DISABLE_TLS @@ -1172,8 +1170,8 @@ Returns: NULL if the subfield was not found, or a pointer to the subfield's data */ -static uschar * -expand_getkeyed(uschar * key, const uschar * s) +uschar * +expand_getkeyed(const uschar * key, const uschar * s) { int length = Ustrlen(key); Uskip_whitespace(&s); @@ -1298,15 +1296,16 @@ expand_getlistele(int field, const uschar * list) { const uschar * tlist = list; int sep = 0; -uschar dummy; +/* Tainted mem for the throwaway element copies */ +uschar * dummy = store_get(2, TRUE); if (field < 0) { - for (field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++; + for (field++; string_nextinlist(&tlist, &sep, dummy, 1); ) field++; sep = 0; } if (field == 0) return NULL; -while (--field > 0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; +while (--field > 0 && (string_nextinlist(&list, &sep, dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); } @@ -1708,9 +1707,9 @@ authres_iprev(gstring * g) if (sender_host_name) g = string_append(g, 3, US";\n\tiprev=pass (", sender_host_name, US")"); else if (host_lookup_deferred) - g = string_catn(g, US";\n\tiprev=temperror", 19); + g = string_cat(g, US";\n\tiprev=temperror"); else if (host_lookup_failed) - g = string_catn(g, US";\n\tiprev=fail", 13); + g = string_cat(g, US";\n\tiprev=fail"); else return g; @@ -1984,11 +1983,12 @@ switch (vp->type) ss = (uschar **)(val); if (!*ss && deliver_datafile >= 0) /* Read body when needed */ { - uschar *body; + 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); + *ss = body = store_get(len+1, TRUE); body[0] = 0; if (vp->type == vtype_msgbody_end) { @@ -2003,8 +2003,7 @@ switch (vp->type) 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) + if ((len = read(deliver_datafile, body, len)) > 0) { body[len] = 0; if (message_body_newlines) /* Separate loops for efficiency */ @@ -2066,7 +2065,8 @@ switch (vp->type) case vtype_string_func: { stringptr_fn_t * fn = (stringptr_fn_t *) val; - return fn(); + uschar* s = fn(); + return s ? s : US""; } case vtype_pspace: @@ -2438,6 +2438,7 @@ else +#ifdef SUPPORT_SRS /* Do an hmac_md5. The result is _not_ nul-terminated, and is sized as the smaller of a full hmac_md5 result (16 bytes) or the supplied output buffer. @@ -2512,6 +2513,7 @@ for (int i = 0, j = len; i < MD5_HASHLEN; i++) } return; } +#endif /*SUPPORT_SRS*/ /************************************************* @@ -3441,14 +3443,14 @@ switch(cond_type = identify_operator(&s, &opname)) return s; } -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS case ECOND_INBOUND_SRS: /* ${if inbound_srs {local_part}{secret} {yes}{no}} */ { uschar * sub[2]; const pcre * re; int ovec[3*(4+1)]; - int n; + int n, quoting = 0; uschar cksum[4]; BOOL boolvalue = FALSE; @@ -3471,10 +3473,20 @@ switch(cond_type = identify_operator(&s, &opname)) goto srs_result; } - /* Side-effect: record the decoded recipient */ + if (sub[0][0] == '"') + quoting = 1; + else for (uschar * s = sub[0]; *s; s++) + if (!isalnum(*s) && Ustrchr(".!#$%&'*+-/=?^_`{|}~", *s) == NULL) + { quoting = 1; break; } + if (quoting) + DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n"); + + /* Record the (quoted, if needed) decoded recipient as $srs_recipient */ - srs_recipient = string_sprintf("%.*S@%.*S", /* lowercased */ + srs_recipient = string_sprintf("%.*s%.*S%.*s@%.*S", /* lowercased */ + quoting, "\"", ovec[9]-ovec[8], sub[0] + ovec[8], /* substring 4 */ + quoting, "\"", ovec[7]-ovec[6], sub[0] + ovec[6]); /* substring 3 */ /* If a zero-length secret was given, we're done. Otherwise carry on @@ -3533,7 +3545,7 @@ srs_result: if (yield) *yield = (boolvalue == testfor); return s; } -#endif /*EXPERIMENTAL_SRS_NATIVE*/ +#endif /*SUPPORT_SRS*/ /* Unknown condition */ @@ -3927,7 +3939,7 @@ Arguments: Returns: new pointer for expandable string, terminated if non-null */ -static gstring * +gstring * cat_file(FILE *f, gstring *yield, uschar *eol) { uschar buffer[1024]; @@ -3947,7 +3959,7 @@ return yield; #ifndef DISABLE_TLS -static gstring * +gstring * cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol) { int rc; @@ -4289,6 +4301,98 @@ return FALSE; /* should not happen */ } +/* Expand a named list. Return false on failure. */ +static gstring * +expand_listnamed(gstring * yield, const uschar * name, const uschar * listtype) +{ +tree_node *t = NULL; +const uschar * list; +int sep = 0; +uschar * item; +uschar * suffix = US""; +BOOL needsep = FALSE; +#define LISTNAMED_BUF_SIZE 256 +uschar b[LISTNAMED_BUF_SIZE]; +uschar * buffer = b; + +if (*name == '+') name++; +if (!listtype) /* no-argument version */ + { + if ( !(t = tree_search(addresslist_anchor, name)) + && !(t = tree_search(domainlist_anchor, name)) + && !(t = tree_search(hostlist_anchor, name))) + t = tree_search(localpartlist_anchor, name); + } +else switch(*listtype) /* specific list-type version */ + { + case 'a': t = tree_search(addresslist_anchor, name); suffix = US"_a"; break; + case 'd': t = tree_search(domainlist_anchor, name); suffix = US"_d"; break; + case 'h': t = tree_search(hostlist_anchor, name); suffix = US"_h"; break; + case 'l': t = tree_search(localpartlist_anchor, name); suffix = US"_l"; break; + default: + expand_string_message = US"bad suffix on \"list\" operator"; + return yield; + } + +if(!t) + { + expand_string_message = string_sprintf("\"%s\" is not a %snamed list", + name, !listtype?"" + : *listtype=='a'?"address " + : *listtype=='d'?"domain " + : *listtype=='h'?"host " + : *listtype=='l'?"localpart " + : 0); + return yield; + } + +list = ((namedlist_block *)(t->data.ptr))->string; + +/* The list could be quite long so we (re)use a buffer for each element +rather than getting each in new memory */ + +if (is_tainted(list)) buffer = store_get(LISTNAMED_BUF_SIZE, TRUE); +while ((item = string_nextinlist(&list, &sep, buffer, LISTNAMED_BUF_SIZE))) + { + uschar * buf = US" : "; + if (needsep) + yield = string_catn(yield, buf, 3); + else + needsep = TRUE; + + if (*item == '+') /* list item is itself a named list */ + { + yield = expand_listnamed(yield, item, listtype); + if (expand_string_message) + return yield; + } + + else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */ + { + char tok[3]; + tok[0] = sep; tok[1] = ':'; tok[2] = 0; + + for(char * cp; cp = strpbrk(CCS item, tok); item = US cp) + { + yield = string_catn(yield, item, cp - CS item); + if (*cp++ == ':') /* colon in a non-colon-sep list item, needs doubling */ + yield = string_catn(yield, US"::", 2); + else /* sep in item; should already be doubled; emit once */ + { + yield = string_catn(yield, US tok, 1); + if (*cp == sep) cp++; + } + } + yield = string_cat(yield, item); + } + else + yield = string_cat(yield, item); + } +return yield; +} + + + /************************************************* * Expand string * *************************************************/ @@ -4387,7 +4491,7 @@ if (is_tainted(string)) goto EXPAND_FAILED; } -while (*s != 0) +while (*s) { uschar *value; uschar name[256]; @@ -4773,7 +4877,7 @@ while (*s != 0) int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); - if ((expand_forbid & RDO_LOOKUP) != 0) + if (expand_forbid & RDO_LOOKUP) { expand_string_message = US"lookup expansions are not permitted"; goto EXPAND_FAILED; @@ -4872,21 +4976,7 @@ while (*s != 0) file types, the query (i.e. "key") starts with a file name. */ if (!key) - { - Uskip_whitespace(&filename); - key = filename; - - if (mac_islookup(stype, lookup_querystyle)) - filename = NULL; - else - if (*filename == '/') - { - while (*key && !isspace(*key)) key++; - if (*key) *key++ = '\0'; - } - else - filename = NULL; - } + key = search_args(stype, name, filename, &filename, opts); /* If skipping, don't do the next bit - just lookup_value == NULL, as if the entry was not found. Note that there is no search_close() function. @@ -4917,7 +5007,7 @@ while (*s != 0) { expand_string_message = string_sprintf("lookup of \"%s\" gave DEFER: %s", - string_printing2(key, FALSE), search_error_message); + string_printing2(key, SP_TAB), search_error_message); goto EXPAND_FAILED; } if (expand_setup > 0) expand_nmax = expand_setup; @@ -5262,7 +5352,7 @@ while (*s != 0) if (!(f = Ufopen(sub_arg[0], "rb"))) { - expand_string_message = string_open_failed(errno, "%s", sub_arg[0]); + expand_string_message = string_open_failed("%s", sub_arg[0]); goto EXPAND_FAILED; } @@ -5276,17 +5366,8 @@ while (*s != 0) case EITEM_READSOCK: { - client_conn_ctx cctx; - int timeout = 5; - int save_ptr = gstring_length(yield); - FILE * fp = NULL; uschar * arg; uschar * sub_arg[4]; - uschar * server_name = NULL; - host_item host; - BOOL do_shutdown = TRUE; - BOOL do_tls = FALSE; /* Only set under ! DISABLE_TLS */ - blob reqstr; if (expand_forbid & RDO_READSOCK) { @@ -5304,218 +5385,80 @@ while (*s != 0) case 3: goto EXPAND_FAILED; } - /* Grab the request string, if any */ - - reqstr.data = sub_arg[1]; - reqstr.len = Ustrlen(sub_arg[1]); - - /* Sort out timeout, if given. The second arg is a list with the first element - being a time value. Any more are options of form "name=value". Currently the - only options recognised are "shutdown" and "tls". */ - - if (sub_arg[2]) - { - const uschar * list = sub_arg[2]; - uschar * item; - int sep = 0; - - if ( !(item = string_nextinlist(&list, &sep, NULL, 0)) - || !*item - || (timeout = readconf_readtime(item, 0, FALSE)) < 0) - { - expand_string_message = string_sprintf("bad time value %s", item); - goto EXPAND_FAILED; - } - - while ((item = string_nextinlist(&list, &sep, NULL, 0))) - if (Ustrncmp(item, US"shutdown=", 9) == 0) - { if (Ustrcmp(item + 9, US"no") == 0) do_shutdown = FALSE; } -#ifndef DISABLE_TLS - else if (Ustrncmp(item, US"tls=", 4) == 0) - { if (Ustrcmp(item + 9, US"no") != 0) do_tls = TRUE; } -#endif - } - else - sub_arg[3] = NULL; /* No eol if no timeout */ - /* If skipping, we don't actually do anything. Otherwise, arrange to connect to either an IP or a Unix socket. */ if (!skipping) { - /* Handle an IP (internet) domain */ - - if (Ustrncmp(sub_arg[0], "inet:", 5) == 0) - { - int port; - uschar * port_name; - - server_name = sub_arg[0] + 5; - port_name = Ustrrchr(server_name, ':'); - - /* Sort out the port */ - - if (!port_name) - { - expand_string_message = - string_sprintf("missing port for readsocket %s", sub_arg[0]); - goto EXPAND_FAILED; - } - *port_name++ = 0; /* Terminate server name */ - - if (isdigit(*port_name)) - { - uschar *end; - port = Ustrtol(port_name, &end, 0); - if (end != port_name + Ustrlen(port_name)) - { - expand_string_message = - string_sprintf("invalid port number %s", port_name); - goto EXPAND_FAILED; - } - } - else - { - struct servent *service_info = getservbyname(CS port_name, "tcp"); - if (!service_info) - { - expand_string_message = string_sprintf("unknown port \"%s\"", - port_name); - goto EXPAND_FAILED; - } - port = ntohs(service_info->s_port); - } - - /*XXX we trust that the request is idempotent for TFO. Hmm. */ - cctx.sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port, - timeout, &host, &expand_string_message, - do_tls ? NULL : &reqstr); - callout_address = NULL; - if (cctx.sock < 0) - goto SOCK_FAIL; - if (!do_tls) - reqstr.len = 0; - } - - /* Handle a Unix domain socket */ - - else - { - struct sockaddr_un sockun; /* don't call this "sun" ! */ - int rc; - - if ((cctx.sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) - { - expand_string_message = string_sprintf("failed to create socket: %s", - strerror(errno)); - goto SOCK_FAIL; - } - - sockun.sun_family = AF_UNIX; - sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), - sub_arg[0]); - server_name = US sockun.sun_path; - - sigalrm_seen = FALSE; - ALARM(timeout); - rc = connect(cctx.sock, (struct sockaddr *)(&sockun), sizeof(sockun)); - ALARM_CLR(0); - if (sigalrm_seen) - { - expand_string_message = US "socket connect timed out"; - goto SOCK_FAIL; - } - if (rc < 0) - { - expand_string_message = string_sprintf("failed to connect to socket " - "%s: %s", sub_arg[0], strerror(errno)); - goto SOCK_FAIL; - } - host.name = server_name; - host.address = US""; - } + int stype = search_findtype(US"readsock", 8); + gstring * g = NULL; + void * handle; + int expand_setup = -1; + uschar * s; - DEBUG(D_expand) debug_printf_indent("connected to socket %s\n", sub_arg[0]); + /* If the reqstr is empty, flag that and set a dummy */ -#ifndef DISABLE_TLS - if (do_tls) + if (!sub_arg[1][0]) { - smtp_connect_args conn_args = {.host = &host }; - tls_support tls_dummy = {.sni=NULL}; - uschar * errstr; - - if (!tls_client_start(&cctx, &conn_args, NULL, &tls_dummy, &errstr)) - { - expand_string_message = string_sprintf("TLS connect failed: %s", errstr); - goto SOCK_FAIL; - } + g = string_append_listele(g, ',', US"send=no"); + sub_arg[1] = US"DUMMY"; } -#endif - /* Allow sequencing of test actions */ - testharness_pause_ms(100); + /* Re-marshall the options */ - /* Write the request string, if not empty or already done */ - - if (reqstr.len) - { - DEBUG(D_expand) debug_printf_indent("writing \"%s\" to socket\n", - reqstr.data); - if ( ( -#ifndef DISABLE_TLS - do_tls ? tls_write(cctx.tls_ctx, reqstr.data, reqstr.len, FALSE) : -#endif - write(cctx.sock, reqstr.data, reqstr.len)) != reqstr.len) - { - expand_string_message = string_sprintf("request write to socket " - "failed: %s", strerror(errno)); - goto SOCK_FAIL; - } - } - - /* Shut down the sending side of the socket. This helps some servers to - recognise that it is their turn to do some work. Just in case some - system doesn't have this function, make it conditional. */ - -#ifdef SHUT_WR - if (!do_tls && do_shutdown) shutdown(cctx.sock, SHUT_WR); -#endif + if (sub_arg[2]) + { + const uschar * list = sub_arg[2]; + uschar * item; + int sep = 0; + + /* First option has no tag and is timeout */ + if ((item = string_nextinlist(&list, &sep, NULL, 0))) + g = string_append_listele(g, ',', + string_sprintf("timeout=%s", item)); + + /* The rest of the options from the expansion */ + while ((item = string_nextinlist(&list, &sep, NULL, 0))) + g = string_append_listele(g, ',', item); + + /* possibly plus an EOL string. Process with escapes, to protect + from list-processing. The only current user of eol= in search + options is the readsock expansion. */ + + if (sub_arg[3] && *sub_arg[3]) + g = string_append_listele(g, ',', + string_sprintf("eol=%s", + string_printing2(sub_arg[3], SP_TAB|SP_SPACE))); + } - testharness_pause_ms(100); + /* Gat a (possibly cached) handle for the connection */ - /* Now we need to read from the socket, under a timeout. The function - that reads a file can be used. */ + if (!(handle = search_open(sub_arg[0], stype, 0, NULL, NULL))) + { + if (*expand_string_message) goto EXPAND_FAILED; + expand_string_message = search_error_message; + search_error_message = NULL; + goto SOCK_FAIL; + } - if (!do_tls) - fp = fdopen(cctx.sock, "rb"); - sigalrm_seen = FALSE; - ALARM(timeout); - yield = -#ifndef DISABLE_TLS - do_tls ? cat_file_tls(cctx.tls_ctx, yield, sub_arg[3]) : -#endif - cat_file(fp, yield, sub_arg[3]); - ALARM_CLR(0); + /* Get (possibly cached) results for the lookup */ + /* sspec: sub_arg[0] req: sub_arg[1] opts: g */ -#ifndef DISABLE_TLS - if (do_tls) + if ((s = search_find(handle, sub_arg[0], sub_arg[1], -1, NULL, 0, 0, + &expand_setup, string_from_gstring(g)))) + yield = string_cat(yield, s); + else if (f.search_find_defer) { - tls_close(cctx.tls_ctx, TRUE); - close(cctx.sock); + expand_string_message = search_error_message; + search_error_message = NULL; + goto SOCK_FAIL; } else -#endif - (void)fclose(fp); - - /* After a timeout, we restore the pointer in the result, that is, - make sure we add nothing from the socket. */ - - if (sigalrm_seen) - { - if (yield) yield->ptr = save_ptr; - expand_string_message = US "socket read timed out"; - goto SOCK_FAIL; - } + { /* should not happen, at present */ + expand_string_message = search_error_message; + search_error_message = NULL; + goto SOCK_FAIL; + } } /* The whole thing has worked (or we were skipping). If there is a @@ -6334,11 +6277,12 @@ while (*s != 0) case 2: case 3: goto EXPAND_FAILED; } - for (uschar sep = *sub[0], c; c = *sub[1]; sub[1]++) + if (*sub[1]) for (uschar sep = *sub[0], c; c = *sub[1]; sub[1]++) { if (c == sep) yield = string_catn(yield, sub[1], 1); yield = string_catn(yield, sub[1], 1); } + else yield = string_catn(yield, US" ", 1); continue; } @@ -6487,13 +6431,10 @@ while (*s != 0) condition for real. For EITEM_MAP and EITEM_REDUCE, do the same, using the normal internal expansion function. */ - if (item_type == EITEM_FILTER) - { - if ((temp = eval_condition(expr, &resetok, NULL))) - s = temp; - } - else + if (item_type != EITEM_FILTER) temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); + else + if ((temp = eval_condition(expr, &resetok, NULL))) s = temp; if (!temp) { @@ -6502,7 +6443,7 @@ while (*s != 0) goto EXPAND_FAILED; } - Uskip_whitespace(&s); + Uskip_whitespace(&s); /*{*/ if (*s++ != '}') { /*{*/ expand_string_message = string_sprintf("missing } at end of condition " @@ -6931,12 +6872,14 @@ while (*s != 0) continue; } -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS case EITEM_SRS_ENCODE: /* ${srs_encode {secret} {return_path} {orig_domain}} */ { uschar * sub[3]; uschar cksum[4]; + gstring * g = NULL; + BOOL quoted = FALSE; switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok)) { @@ -6945,47 +6888,71 @@ while (*s != 0) case 3: goto EXPAND_FAILED; } - yield = string_catn(yield, US"SRS0=", 5); + g = string_catn(g, US"SRS0=", 5); /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */ hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum)); - yield = string_catn(yield, cksum, sizeof(cksum)); - yield = string_catn(yield, US"=", 1); + g = string_catn(g, cksum, sizeof(cksum)); + g = string_catn(g, US"=", 1); /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */ { struct timeval now; unsigned long i; - gstring * g = NULL; + gstring * h = NULL; gettimeofday(&now, NULL); for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5) - g = string_catn(g, &base32_chars[i & 0x1f], 1); - if (g) while (g->ptr > 0) - yield = string_catn(yield, &g->s[--g->ptr], 1); + h = string_catn(h, &base32_chars[i & 0x1f], 1); + if (h) while (h->ptr > 0) + g = string_catn(g, &h->s[--h->ptr], 1); } - yield = string_catn(yield, US"=", 1); + g = string_catn(g, US"=", 1); /* ${domain:$return_path}=${local_part:$return_path} */ { int start, end, domain; uschar * t = parse_extract_address(sub[1], &expand_string_message, &start, &end, &domain, FALSE); + uschar * s; + if (!t) goto EXPAND_FAILED; - if (domain > 0) yield = string_cat(yield, t + domain); - yield = string_catn(yield, US"=", 1); - yield = domain > 0 - ? string_catn(yield, t, domain - 1) : string_cat(yield, t); + if (domain > 0) g = string_cat(g, t + domain); + g = string_catn(g, US"=", 1); + + s = domain > 0 ? string_copyn(t, domain - 1) : t; + if ((quoted = Ustrchr(s, '"') != NULL)) + { + gstring * h = NULL; + DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n"); + while (*s) /* de-quote */ + { + while (*s && *s != '"') h = string_catn(h, s++, 1); + if (*s) s++; + while (*s && *s != '"') h = string_catn(h, s++, 1); + if (*s) s++; + } + gstring_release_unused(h); + s = string_from_gstring(h); + } + g = string_cat(g, s); } + /* Assume that if the original local_part had quotes + it was for good reason */ + + if (quoted) yield = string_catn(yield, US"\"", 1); + yield = string_catn(yield, g->s, g->ptr); + if (quoted) yield = string_catn(yield, US"\"", 1); + /* @$original_domain */ yield = string_catn(yield, US"@", 1); yield = string_cat(yield, sub[2]); continue; } -#endif /*EXPERIMENTAL_SRS_NATIVE*/ +#endif /*SUPPORT_SRS*/ } /* EITEM_* switch */ /* Control reaches here if the name is not recognized as one of the more @@ -7140,20 +7107,6 @@ while (*s != 0) continue; } - case EOP_BLESS: - /* This is purely for the convenience of the test harness. Do not enable - it otherwise as it defeats the taint-checking security. */ - - if (f.running_in_test_harness) - yield = string_cat(yield, is_tainted(sub) - ? string_copy_taint(sub, FALSE) : sub); - else - { - DEBUG(D_expand) debug_printf_indent("bless operator not supported\n"); - yield = string_cat(yield, sub); - } - continue; - case EOP_EXPAND: { uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok); @@ -7352,11 +7305,10 @@ while (*s != 0) case EOP_LISTCOUNT: { - int cnt = 0; - int sep = 0; - uschar buffer[256]; + int cnt = 0, sep = 0; + uschar * buf = store_get(2, is_tainted(sub)); - while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer))) cnt++; + while (string_nextinlist(CUSS &sub, &sep, buf, 1)) cnt++; yield = string_fmt_append(yield, "%d", cnt); continue; } @@ -7365,86 +7317,11 @@ while (*s != 0) /* handles nested named lists; requotes as colon-sep list */ case EOP_LISTNAMED: - { - tree_node *t = NULL; - const uschar * list; - int sep = 0; - uschar * item; - uschar * suffix = US""; - BOOL needsep = FALSE; - uschar buffer[256]; - - if (*sub == '+') sub++; - if (!arg) /* no-argument version */ - { - if (!(t = tree_search(addresslist_anchor, sub)) && - !(t = tree_search(domainlist_anchor, sub)) && - !(t = tree_search(hostlist_anchor, sub))) - t = tree_search(localpartlist_anchor, sub); - } - else switch(*arg) /* specific list-type version */ - { - case 'a': t = tree_search(addresslist_anchor, sub); suffix = US"_a"; break; - case 'd': t = tree_search(domainlist_anchor, sub); suffix = US"_d"; break; - case 'h': t = tree_search(hostlist_anchor, sub); suffix = US"_h"; break; - case 'l': t = tree_search(localpartlist_anchor, sub); suffix = US"_l"; break; - default: - expand_string_message = US"bad suffix on \"list\" operator"; - goto EXPAND_FAILED; - } - - if(!t) - { - expand_string_message = string_sprintf("\"%s\" is not a %snamed list", - sub, !arg?"" - : *arg=='a'?"address " - : *arg=='d'?"domain " - : *arg=='h'?"host " - : *arg=='l'?"localpart " - : 0); + expand_string_message = NULL; + yield = expand_listnamed(yield, sub, arg); + if (expand_string_message) goto EXPAND_FAILED; - } - - list = ((namedlist_block *)(t->data.ptr))->string; - - while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) - { - uschar * buf = US" : "; - if (needsep) - yield = string_catn(yield, buf, 3); - else - needsep = TRUE; - - if (*item == '+') /* list item is itself a named list */ - { - uschar * sub = string_sprintf("${listnamed%s:%s}", suffix, item); - item = expand_string_internal(sub, FALSE, NULL, FALSE, TRUE, &resetok); - } - else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */ - { - char * cp; - char tok[3]; - tok[0] = sep; tok[1] = ':'; tok[2] = 0; - while ((cp= strpbrk(CCS item, tok))) - { - yield = string_catn(yield, item, cp - CS item); - if (*cp++ == ':') /* colon in a non-colon-sep list item, needs doubling */ - { - yield = string_catn(yield, US"::", 2); - item = US cp; - } - else /* sep in item; should already be doubled; emit once */ - { - yield = string_catn(yield, US tok, 1); - if (*cp == sep) cp++; - item = US cp; - } - } - } - yield = string_cat(yield, item); - } continue; - } /* quote a list-item for the given list-separator */ @@ -7632,24 +7509,20 @@ while (*s != 0) uschar *t = sub - 1; if (c == EOP_QUOTE) - { - while (!needs_quote && *(++t) != 0) + while (!needs_quote && *++t) needs_quote = !isalnum(*t) && !strchr("_-.", *t); - } + else /* EOP_QUOTE_LOCAL_PART */ - { - while (!needs_quote && *(++t) != 0) - needs_quote = !isalnum(*t) && - strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL && - (*t != '.' || t == sub || t[1] == 0); - } + while (!needs_quote && *++t) + needs_quote = !isalnum(*t) + && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL + && (*t != '.' || t == sub || !t[1]); if (needs_quote) { yield = string_catn(yield, US"\"", 1); t = sub - 1; - while (*(++t) != 0) - { + while (*++t) if (*t == '\n') yield = string_catn(yield, US"\\n", 2); else if (*t == '\r') @@ -7660,10 +7533,10 @@ while (*s != 0) yield = string_catn(yield, US"\\", 1); yield = string_catn(yield, t, 1); } - } yield = string_catn(yield, US"\"", 1); } - else yield = string_cat(yield, sub); + else + yield = string_cat(yield, sub); continue; } @@ -7718,13 +7591,10 @@ while (*s != 0) prescribed by the RFC, if there are characters that need to be encoded */ case EOP_RFC2047: - { - uschar buffer[2048]; yield = string_cat(yield, parse_quote_2047(sub, Ustrlen(sub), headers_charset, - buffer, sizeof(buffer), FALSE)); + FALSE)); continue; - } /* RFC 2047 decode */ @@ -8752,6 +8622,7 @@ debug_selector = D_v; debug_file = stderr; debug_fd = fileno(debug_file); big_buffer = malloc(big_buffer_size); +store_init(); for (int i = 1; i < argc; i++) {