X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/ae8f9024d8d4fad31457d758022e3186d782929c..e54893330b92ed765b6872a1c47ba61d5e20ff7c:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index ca7d36838..f38d7a492 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -235,6 +235,7 @@ static uschar *op_table_main[] = { US"rxquote", US"s", US"sha1", + US"sha2", US"sha256", US"sha3", US"stat", @@ -281,6 +282,7 @@ enum { EOP_RXQUOTE, EOP_S, EOP_SHA1, + EOP_SHA2, EOP_SHA256, EOP_SHA3, EOP_STAT, @@ -312,7 +314,11 @@ static uschar *cond_table[] = { US"exists", US"first_delivery", US"forall", + US"forall_json", + US"forall_jsons", US"forany", + US"forany_json", + US"forany_jsons", US"ge", US"gei", US"gt", @@ -358,7 +364,11 @@ enum { ECOND_EXISTS, ECOND_FIRST_DELIVERY, ECOND_FORALL, + ECOND_FORALL_JSON, + ECOND_FORALL_JSONS, ECOND_FORANY, + ECOND_FORANY_JSON, + ECOND_FORANY_JSONS, ECOND_STR_GE, ECOND_STR_GEI, ECOND_STR_GT, @@ -441,6 +451,7 @@ typedef struct { } alblock; static uschar * fn_recipients(void); +typedef uschar * stringptr_fn_t(void); /* This table must be kept in alphabetical order. */ @@ -462,7 +473,7 @@ static var_entry var_table[] = { { "address_file", vtype_stringptr, &address_file }, { "address_pipe", vtype_stringptr, &address_pipe }, #ifdef EXPERIMENTAL_ARC - { "arc_domains", vtype_string_func, &fn_arc_domains }, + { "arc_domains", vtype_string_func, (void *) &fn_arc_domains }, { "arc_oldest_pass", vtype_int, &arc_oldest_pass }, { "arc_state", vtype_stringptr, &arc_state }, { "arc_state_reason", vtype_stringptr, &arc_state_reason }, @@ -543,7 +554,7 @@ static var_entry var_table[] = { { "exim_path", vtype_stringptr, &exim_path }, { "exim_uid", vtype_uid, &exim_uid }, { "exim_version", vtype_stringptr, &version_string }, - { "headers_added", vtype_string_func, &fn_hdrs_added }, + { "headers_added", vtype_string_func, (void *) &fn_hdrs_added }, { "home", vtype_stringptr, &deliver_home }, { "host", vtype_stringptr, &deliver_host }, { "host_address", vtype_stringptr, &deliver_host_address }, @@ -654,15 +665,12 @@ static var_entry var_table[] = { { "received_time", vtype_int, &received_time.tv_sec }, { "recipient_data", vtype_stringptr, &recipient_data }, { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure }, - { "recipients", vtype_string_func, &fn_recipients }, + { "recipients", vtype_string_func, (void *) &fn_recipients }, { "recipients_count", vtype_int, &recipients_count }, #ifdef WITH_CONTENT_SCAN { "regex_match_string", vtype_stringptr, ®ex_match_string }, #endif { "reply_address", vtype_reply, NULL }, -#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) - { "requiretls", vtype_bool, &tls_requiretls }, -#endif { "return_path", vtype_stringptr, &return_path }, { "return_size_limit", vtype_int, &bounce_return_size_limit }, { "router_name", vtype_stringptr, &router_name }, @@ -692,7 +700,7 @@ static var_entry var_table[] = { { "smtp_active_hostname", vtype_stringptr, &smtp_active_hostname }, { "smtp_command", vtype_stringptr, &smtp_cmd_buffer }, { "smtp_command_argument", vtype_stringptr, &smtp_cmd_argument }, - { "smtp_command_history", vtype_string_func, &smtp_cmd_hist }, + { "smtp_command_history", vtype_string_func, (void *) &smtp_cmd_hist }, { "smtp_count_at_connection_start", vtype_int, &smtp_accept_count }, { "smtp_notquit_reason", vtype_stringptr, &smtp_notquit_reason }, { "sn0", vtype_filter_int, &filter_sn[0] }, @@ -741,16 +749,21 @@ static var_entry var_table[] = { { "tls_in_bits", vtype_int, &tls_in.bits }, { "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified }, { "tls_in_cipher", vtype_stringptr, &tls_in.cipher }, + { "tls_in_cipher_std", vtype_stringptr, &tls_in.cipher_stdname }, { "tls_in_ocsp", vtype_int, &tls_in.ocsp }, { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, -#if defined(SUPPORT_TLS) +#ifdef EXPERIMENTAL_TLS_RESUME + { "tls_in_resumption", vtype_int, &tls_in.resumption }, +#endif +#ifndef DISABLE_TLS { "tls_in_sni", vtype_stringptr, &tls_in.sni }, #endif { "tls_out_bits", vtype_int, &tls_out.bits }, { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified }, { "tls_out_cipher", vtype_stringptr, &tls_out.cipher }, + { "tls_out_cipher_std", vtype_stringptr, &tls_out.cipher_stdname }, #ifdef SUPPORT_DANE { "tls_out_dane", vtype_bool, &tls_out.dane_verified }, #endif @@ -758,7 +771,10 @@ 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 }, -#if defined(SUPPORT_TLS) +#ifdef EXPERIMENTAL_TLS_RESUME + { "tls_out_resumption", vtype_int, &tls_out.resumption }, +#endif +#ifndef DISABLE_TLS { "tls_out_sni", vtype_stringptr, &tls_out.sni }, #endif #ifdef SUPPORT_DANE @@ -766,7 +782,7 @@ static var_entry var_table[] = { #endif { "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */ -#if defined(SUPPORT_TLS) +#ifndef DISABLE_TLS { "tls_sni", vtype_stringptr, &tls_in.sni }, /* mind the alphabetical order! */ #endif @@ -952,7 +968,7 @@ weirdness they'll twist this into. The result should ideally handle fork(). However, if we're stuck unable to provide this, then we'll fall back to appallingly bad randomness. -If SUPPORT_TLS is defined then this will not be used except as an emergency +If DISABLE_TLS is not defined then this will not be used except as an emergency fallback. Arguments: @@ -960,56 +976,55 @@ Arguments: Returns a random number in range [0, max-1] */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS # define vaguely_random_number vaguely_random_number_fallback #endif int vaguely_random_number(int max) { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS # undef vaguely_random_number #endif - static pid_t pid = 0; - pid_t p2; -#if defined(HAVE_SRANDOM) && !defined(HAVE_SRANDOMDEV) - struct timeval tv; -#endif +static pid_t pid = 0; +pid_t p2; - p2 = getpid(); - if (p2 != pid) +if ((p2 = getpid()) != pid) + { + if (pid != 0) { - if (pid != 0) - { #ifdef HAVE_ARC4RANDOM - /* cryptographically strong randomness, common on *BSD platforms, not - so much elsewhere. Alas. */ -#ifndef NOT_HAVE_ARC4RANDOM_STIR - arc4random_stir(); -#endif + /* cryptographically strong randomness, common on *BSD platforms, not + so much elsewhere. Alas. */ +# ifndef NOT_HAVE_ARC4RANDOM_STIR + arc4random_stir(); +# endif #elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) -#ifdef HAVE_SRANDOMDEV - /* uses random(4) for seeding */ - srandomdev(); -#else - gettimeofday(&tv, NULL); - srandom(tv.tv_sec | tv.tv_usec | getpid()); -#endif +# ifdef HAVE_SRANDOMDEV + /* uses random(4) for seeding */ + srandomdev(); +# else + { + struct timeval tv; + gettimeofday(&tv, NULL); + srandom(tv.tv_sec | tv.tv_usec | getpid()); + } +# endif #else - /* Poor randomness and no seeding here */ + /* Poor randomness and no seeding here */ #endif - } - pid = p2; } + pid = p2; + } #ifdef HAVE_ARC4RANDOM - return arc4random() % max; +return arc4random() % max; #elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) - return random() % max; +return random() % max; #else - /* This one returns a 16-bit number, definitely not crypto-strong */ - return random_number(max); +/* This one returns a 16-bit number, definitely not crypto-strong */ +return random_number(max); #endif } @@ -1275,7 +1290,7 @@ return string_nextinlist(&list, &sep, NULL, 0); /* Certificate fields, by name. Worry about by-OID later */ /* Names are chosen to not have common prefixes */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS typedef struct { uschar * name; @@ -1301,7 +1316,6 @@ static uschar * expand_getcertele(uschar * field, uschar * certvar) { var_entry * vp; -certfield * cp; if (!(vp = find_var_ent(certvar))) { @@ -1323,9 +1337,9 @@ if (!*(void **)vp->value) if (*field >= '0' && *field <= '9') return tls_cert_ext_by_oid(*(void **)vp->value, field, 0); -for(cp = certfields; - cp < certfields + nelem(certfields); - cp++) +for (certfield * cp = certfields; + cp < certfields + nelem(certfields); + cp++) if (Ustrncmp(cp->name, field, cp->namelen) == 0) { uschar * modifier = *(field += cp->namelen) == ',' @@ -1337,7 +1351,7 @@ expand_string_message = string_sprintf("bad field selector \"%s\" for certextract", field); return NULL; } -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ /************************************************* * Extract a substring from a string * @@ -1561,10 +1575,9 @@ find_header(uschar *name, int *newsize, unsigned flags, uschar *charset) BOOL found = !name; int len = name ? Ustrlen(name) : 0; BOOL comma = FALSE; -header_line * h; gstring * g = NULL; -for (h = header_list; h; h = h->next) +for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old && h->text) /* NULL => Received: placeholder */ if (!name || (len <= h->slen && strncmpic(name, h->text, len) == 0)) { @@ -1687,7 +1700,7 @@ else return g; if (sender_host_address) - g = string_append(g, 2, US" smtp.client-ip=", sender_host_address); + g = string_append(g, 2, US" smtp.remote-ip=", sender_host_address); return g; } @@ -1705,11 +1718,10 @@ fn_recipients(void) { uschar * s; gstring * g = NULL; -int i; if (!f.enable_dollar_recipients) return NULL; -for (i = 0; i < recipients_count; i++) +for (int i = 0; i < recipients_count; i++) { s = recipients_list[i].address; g = string_append2_listele_n(g, US", ", s, Ustrlen(s)); @@ -1762,8 +1774,13 @@ set, in which case give an error. */ if ((Ustrncmp(name, "acl_c", 5) == 0 || Ustrncmp(name, "acl_m", 5) == 0) && !isalpha(name[5])) { - tree_node *node = - tree_search((name[4] == 'c')? acl_var_c : acl_var_m, name + 4); + tree_node * node = + tree_search(name[4] == 'c' ? acl_var_c : acl_var_m, name + 4); + return node ? node->data.ptr : strict_acl_vars ? NULL : US""; + } +else if (Ustrncmp(name, "r_", 2) == 0) + { + tree_node * node = tree_search(router_var, name + 2); return node ? node->data.ptr : strict_acl_vars ? NULL : US""; } @@ -1840,22 +1857,17 @@ switch (vp->type) return sender_host_name ? sender_host_name : US""; 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 (!(s = *((uschar **)(val)))) return US""; + if (!(domain = Ustrrchr(s, '@'))) 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; + return string_copyn(s, domain - s); case vtype_domain: /* Get domain from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; + if (!(s = *((uschar **)(val)))) return US""; domain = Ustrrchr(s, '@'); - return (domain == NULL)? US"" : domain + 1; + return domain ? domain + 1 : US""; case vtype_msgheaders: return find_header(NULL, newsize, exists_only ? FH_EXISTS_ONLY : 0, NULL); @@ -1950,14 +1962,14 @@ switch (vp->type) case vtype_string_func: { - uschar * (*fn)() = val; + stringptr_fn_t * fn = (stringptr_fn_t *) val; return fn(); } case vtype_pspace: { int inodes; - sprintf(CS var_buffer, "%d", + sprintf(CS var_buffer, PR_EXIM_ARITH, receive_statvfs(val == (void *)TRUE, &inodes)); } return var_buffer; @@ -2028,11 +2040,10 @@ static int read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping, BOOL check_end, uschar *name, BOOL *resetok) { -int i; const uschar *s = *sptr; while (isspace(*s)) s++; -for (i = 0; i < n; i++) +for (int i = 0; i < n; i++) { if (*s != '{') { @@ -2148,6 +2159,139 @@ return ret; +/* Return pointer to dewrapped string, with enclosing specified chars removed. +The given string is modified on return. Leading whitespace is skipped while +looking for the opening wrap character, then the rest is scanned for the trailing +(non-escaped) wrap character. A backslash in the string will act as an escape. + +A nul is written over the trailing wrap, and a pointer to the char after the +leading wrap is returned. + +Arguments: + s String for de-wrapping + wrap Two-char string, the first being the opener, second the closer wrapping + character +Return: + Pointer to de-wrapped string, or NULL on error (with expand_string_message set). +*/ + +static uschar * +dewrap(uschar * s, const uschar * wrap) +{ +uschar * p = s; +unsigned depth = 0; +BOOL quotesmode = wrap[0] == wrap[1]; + +while (isspace(*p)) p++; + +if (*p == *wrap) + { + s = ++p; + wrap++; + while (*p) + { + if (*p == '\\') p++; + else if (!quotesmode && *p == wrap[-1]) depth++; + else if (*p == *wrap) + if (depth == 0) + { + *p = '\0'; + return s; + } + else + depth--; + p++; + } + } +expand_string_message = string_sprintf("missing '%c'", *wrap); +return NULL; +} + + +/* Pull off the leading array or object element, returning +a copy in an allocated string. Update the list pointer. + +The element may itself be an abject or array. +Return NULL when the list is empty. +*/ + +static uschar * +json_nextinlist(const uschar ** list) +{ +unsigned array_depth = 0, object_depth = 0; +const uschar * s = *list, * item; + +while (isspace(*s)) s++; + +for (item = s; + *s && (*s != ',' || array_depth != 0 || object_depth != 0); + s++) + switch (*s) + { + case '[': array_depth++; break; + case ']': array_depth--; break; + case '{': object_depth++; break; + case '}': object_depth--; break; + } +*list = *s ? s+1 : s; +if (item == s) return NULL; +item = string_copyn(item, s - item); +DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item); +return US item; +} + + + +/************************************************/ +/* Return offset in ops table, or -1 if not found. +Repoint to just after the operator in the string. + +Argument: + ss string representation of operator + opname split-out operator name +*/ + +static int +identify_operator(const uschar ** ss, uschar ** opname) +{ +const uschar * s = *ss; +uschar name[256]; + +/* Numeric comparisons are symbolic */ + +if (*s == '=' || *s == '>' || *s == '<') + { + int p = 0; + name[p++] = *s++; + if (*s == '=') + { + name[p++] = '='; + s++; + } + name[p] = 0; + } + +/* All other conditions are named */ + +else + s = read_name(name, sizeof(name), s, US"_"); +*ss = s; + +/* If we haven't read a name, it means some non-alpha character is first. */ + +if (!name[0]) + { + expand_string_message = string_sprintf("condition name expected, " + "but found \"%.16s\"", s); + return -1; + } +if (opname) + *opname = string_copy(name); + +return chop_match(name, cond_table, nelem(cond_table)); +} + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -2174,9 +2318,11 @@ BOOL testfor = TRUE; BOOL tempcond, combined_cond; BOOL *subcondptr; BOOL sub2_honour_dollar = TRUE; -int i, rc, cond_type, roffset; +BOOL is_forany, is_json, is_jsons; +int rc, cond_type, roffset; int_eximarith_t num[2]; struct stat statbuf; +uschar * opname; uschar name[256]; const uschar *sub[10]; @@ -2189,37 +2335,7 @@ for (;;) if (*s == '!') { testfor = !testfor; s++; } else break; } -/* Numeric comparisons are symbolic */ - -if (*s == '=' || *s == '>' || *s == '<') - { - int p = 0; - name[p++] = *s++; - if (*s == '=') - { - name[p++] = '='; - s++; - } - name[p] = 0; - } - -/* All other conditions are named */ - -else s = read_name(name, 256, s, US"_"); - -/* If we haven't read a name, it means some non-alpha character is first. */ - -if (name[0] == 0) - { - expand_string_message = string_sprintf("condition name expected, " - "but found \"%.16s\"", s); - return NULL; - } - -/* Find which condition we are dealing with, and switch on it */ - -cond_type = chop_match(name, cond_table, nelem(cond_table)); -switch(cond_type) +switch(cond_type = identify_operator(&s, &opname)) { /* def: tests for a non-empty variable, or for the existence of a header. If yield == NULL we are in a skipping state, and don't care about the answer. */ @@ -2234,7 +2350,7 @@ switch(cond_type) return NULL; } - s = read_name(name, 256, s+1, US"_"); + s = read_name(name, sizeof(name), s+1, US"_"); /* Test for a header's existence. If the name contains a closing brace character, this may be a user error where the terminating colon has been @@ -2246,7 +2362,7 @@ switch(cond_type) && (*++t == '_' || Ustrncmp(t, "eader_", 6) == 0) ) { - s = read_header_name(name, 256, s); + s = read_header_name(name, sizeof(name), s); /* {-for-text-editors */ if (Ustrchr(name, '}') != NULL) malformed_header = TRUE; if (yield) *yield = @@ -2260,9 +2376,9 @@ switch(cond_type) { if (!(t = find_variable(name, TRUE, yield == NULL, NULL))) { - expand_string_message = (name[0] == 0)? - string_sprintf("variable name omitted after \"def:\"") : - string_sprintf("unknown variable \"%s\" after \"def:\"", name); + expand_string_message = name[0] + ? string_sprintf("unknown variable \"%s\" after \"def:\"", name) + : US"variable name omitted after \"def:\""; check_variable_error_message(name); return NULL; } @@ -2418,8 +2534,9 @@ switch(cond_type) if (yield != NULL) { + int rc; *resetok = FALSE; /* eval_acl() might allocate; do not reclaim */ - switch(eval_acl(sub, nelem(sub), &user_msg)) + switch(rc = eval_acl(sub, nelem(sub), &user_msg)) { case OK: cond = TRUE; @@ -2434,7 +2551,8 @@ switch(cond_type) f.expand_string_forcedfail = TRUE; /*FALLTHROUGH*/ default: - expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + expand_string_message = string_sprintf("%s from acl \"%s\"", + rc_names[rc], sub[0]); return NULL; } } @@ -2524,7 +2642,7 @@ switch(cond_type) case ECOND_STR_GE: case ECOND_STR_GEI: - for (i = 0; i < 2; i++) + for (int i = 0; i < 2; i++) { /* Sometimes, we don't expand substrings; too many insecure configurations created using match_address{}{} and friends, where the second param @@ -2538,7 +2656,7 @@ switch(cond_type) { if (i == 0) goto COND_FAILED_CURLY_START; expand_string_message = string_sprintf("missing 2nd string in {} " - "after \"%s\"", name); + "after \"%s\"", opname); return NULL; } if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL, @@ -2553,7 +2671,7 @@ switch(cond_type) conditions that compare numbers do not start with a letter. This just saves checking for them individually. */ - if (!isalpha(name[0]) && yield != NULL) + if (!isalpha(opname[0]) && yield != NULL) if (sub[i][0] == 0) { num[i] = 0; @@ -2741,16 +2859,15 @@ switch(cond_type) if (sublen == 24) { - uschar *coded = b64encode(digest, 16); + uschar *coded = b64encode(CUS 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); } else if (sublen == 32) { - int i; uschar coded[36]; - for (i = 0; i < 16; i++) sprintf(CS (coded+2*i), "%02X", digest[i]); + for (int i = 0; i < 16; i++) sprintf(CS (coded+2*i), "%02X", digest[i]); coded[32] = 0; DEBUG(D_auth) debug_printf("crypteq: using MD5+hex hashing\n" " subject=%s\n crypted=%s\n", coded, sub[1]+5); @@ -2779,16 +2896,15 @@ switch(cond_type) if (sublen == 28) { - uschar *coded = b64encode(digest, 20); + uschar *coded = b64encode(CUS 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); } else if (sublen == 40) { - int i; uschar coded[44]; - for (i = 0; i < 20; i++) sprintf(CS (coded+2*i), "%02X", digest[i]); + for (int i = 0; i < 20; i++) sprintf(CS (coded+2*i), "%02X", digest[i]); coded[40] = 0; DEBUG(D_auth) debug_printf("crypteq: using SHA1+hex hashing\n" " subject=%s\n crypted=%s\n", coded, sub[1]+6); @@ -2867,7 +2983,7 @@ switch(cond_type) uschar *save_iterate_item = iterate_item; int (*compare)(const uschar *, const uschar *); - DEBUG(D_expand) debug_printf_indent("condition: %s item: %s\n", name, sub[0]); + DEBUG(D_expand) debug_printf_indent("condition: %s item: %s\n", opname, sub[0]); tempcond = FALSE; compare = cond_type == ECOND_INLISTI @@ -2909,14 +3025,14 @@ switch(cond_type) if (*s != '{') /* }-for-text-editors */ { expand_string_message = string_sprintf("each subcondition " - "inside an \"%s{...}\" condition must be in its own {}", name); + "inside an \"%s{...}\" condition must be in its own {}", opname); return NULL; } if (!(s = eval_condition(s+1, resetok, subcondptr))) { expand_string_message = string_sprintf("%s inside \"%s{...}\" condition", - expand_string_message, name); + expand_string_message, opname); return NULL; } while (isspace(*s)) s++; @@ -2926,7 +3042,7 @@ switch(cond_type) { /* {-for-text-editors */ expand_string_message = string_sprintf("missing } at end of condition " - "inside \"%s\" group", name); + "inside \"%s\" group", opname); return NULL; } @@ -2951,14 +3067,20 @@ switch(cond_type) /* forall/forany: iterates a condition with different values */ - case ECOND_FORALL: - case ECOND_FORANY: + case ECOND_FORALL: is_forany = FALSE; is_json = FALSE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORANY: is_forany = TRUE; is_json = FALSE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORALL_JSON: is_forany = FALSE; is_json = TRUE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORANY_JSON: is_forany = TRUE; is_json = TRUE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORALL_JSONS: is_forany = FALSE; is_json = TRUE; is_jsons = TRUE; goto FORMANY; + case ECOND_FORANY_JSONS: is_forany = TRUE; is_json = TRUE; is_jsons = TRUE; goto FORMANY; + + FORMANY: { const uschar * list; int sep = 0; uschar *save_iterate_item = iterate_item; - DEBUG(D_expand) debug_printf_indent("condition: %s\n", name); + DEBUG(D_expand) debug_printf_indent("condition: %s\n", opname); while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ @@ -2979,7 +3101,7 @@ switch(cond_type) if (!(s = eval_condition(sub[1], resetok, NULL))) { expand_string_message = string_sprintf("%s inside \"%s\" condition", - expand_string_message, name); + expand_string_message, opname); return NULL; } while (isspace(*s)) s++; @@ -2989,27 +3111,39 @@ switch(cond_type) { /* {-for-text-editors */ expand_string_message = string_sprintf("missing } at end of condition " - "inside \"%s\"", name); + "inside \"%s\"", opname); return NULL; } - if (yield != NULL) *yield = !testfor; + if (yield) *yield = !testfor; list = sub[0]; - while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL) + if (is_json) list = dewrap(string_copy(list), US"[]"); + while ((iterate_item = is_json + ? json_nextinlist(&list) : string_nextinlist(&list, &sep, NULL, 0))) { - DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, iterate_item); + if (is_jsons) + if (!(iterate_item = dewrap(iterate_item, US"\"\""))) + { + expand_string_message = + string_sprintf("%s wrapping string result for extract jsons", + expand_string_message); + iterate_item = save_iterate_item; + return NULL; + } + + DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", opname, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) { expand_string_message = string_sprintf("%s inside \"%s\" condition", - expand_string_message, name); + expand_string_message, opname); iterate_item = save_iterate_item; return NULL; } - DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", name, + DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", opname, tempcond? "true":"false"); - if (yield != NULL) *yield = (tempcond == testfor); - if (tempcond == (cond_type == ECOND_FORANY)) break; + if (yield) *yield = (tempcond == testfor); + if (tempcond == is_forany) break; } iterate_item = save_iterate_item; @@ -3098,19 +3232,20 @@ switch(cond_type) /* Unknown condition */ default: - expand_string_message = string_sprintf("unknown condition \"%s\"", name); - return NULL; + if (!expand_string_message || !*expand_string_message) + expand_string_message = string_sprintf("unknown condition \"%s\"", opname); + return NULL; } /* End switch on condition type */ /* Missing braces at start and end of data */ COND_FAILED_CURLY_START: -expand_string_message = string_sprintf("missing { after \"%s\"", name); +expand_string_message = string_sprintf("missing { after \"%s\"", opname); return NULL; COND_FAILED_CURLY_END: expand_string_message = string_sprintf("missing } at end of \"%s\" condition", - name); + opname); return NULL; /* A condition requires code that is not compiled */ @@ -3120,7 +3255,7 @@ return NULL; !defined(SUPPORT_CRYPTEQ) || !defined(CYRUS_SASLAUTHD_SOCKET) COND_FAILED_NOT_COMPILED: expand_string_message = string_sprintf("support for \"%s\" not compiled", - name); + opname); return NULL; #endif } @@ -3145,8 +3280,7 @@ Returns: the value of expand max to save static int save_expand_strings(uschar **save_expand_nstring, int *save_expand_nlength) { -int i; -for (i = 0; i <= expand_nmax; i++) +for (int i = 0; i <= expand_nmax; i++) { save_expand_nstring[i] = expand_nstring[i]; save_expand_nlength[i] = expand_nlength[i]; @@ -3174,9 +3308,8 @@ static void restore_expand_strings(int save_expand_nmax, uschar **save_expand_nstring, int *save_expand_nlength) { -int i; expand_nmax = save_expand_nmax; -for (i = 0; i <= expand_nmax; i++) +for (int i = 0; i <= expand_nmax; i++) { expand_nstring[i] = save_expand_nstring[i]; expand_nlength[i] = save_expand_nlength[i]; @@ -3433,7 +3566,7 @@ Returns: pointer to string containing the last three static uschar * prvs_daystamp(int day_offset) { -uschar *days = store_get(32); /* Need at least 24 for cases */ +uschar *days = store_get(32, FALSE); /* Need at least 24 for cases */ (void)string_format(days, 32, TIME_T_FMT, /* where TIME_T_FMT is %lld */ (time(NULL) + day_offset*86400)/86400); return (Ustrlen(days) >= 3) ? &days[Ustrlen(days)-3] : US"100"; @@ -3465,13 +3598,12 @@ prvs_hmac_sha1(uschar *address, uschar *key, uschar *key_num, uschar *daystamp) { gstring * hash_source; uschar * p; -int i; hctx h; uschar innerhash[20]; uschar finalhash[20]; uschar innerkey[64]; uschar outerkey[64]; -uschar *finalhash_hex = store_get(40); +uschar *finalhash_hex; if (key_num == NULL) key_num = US"0"; @@ -3490,7 +3622,7 @@ DEBUG(D_expand) memset(innerkey, 0x36, 64); memset(outerkey, 0x5c, 64); -for (i = 0; i < Ustrlen(key); i++) +for (int i = 0; i < Ustrlen(key); i++) { innerkey[i] ^= key[i]; outerkey[i] ^= key[i]; @@ -3504,8 +3636,10 @@ chash_start(HMAC_SHA1, &h); chash_mid(HMAC_SHA1, &h, outerkey); chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash); -p = finalhash_hex; -for (i = 0; i < 3; i++) +/* Hashing is deemed sufficient to de-taint any input data */ + +p = finalhash_hex = store_get(40, FALSE); +for (int i = 0; i < 3; i++) { *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; *p++ = hex_digits[finalhash[i] & 0x0f]; @@ -3553,16 +3687,17 @@ return yield; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS static gstring * cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol) { int rc; -uschar * s; uschar buffer[1024]; +/*XXX could we read direct into a pre-grown string? */ + while ((rc = tls_read(tls_ctx, buffer, sizeof(buffer))) > 0) - for (s = buffer; rc--; s++) + for (uschar * s = buffer; rc--; s++) yield = eol && *s == '\n' ? string_cat(yield, eol) : string_catn(yield, s, 1); @@ -3601,17 +3736,15 @@ eval_expr(uschar **sptr, BOOL decimal, uschar **error, BOOL endket) { uschar *s = *sptr; int_eximarith_t x = eval_op_or(&s, decimal, error); -if (*error == NULL) - { + +if (!*error) if (endket) - { if (*s != ')') *error = US"expecting closing parenthesis"; else while (isspace(*(++s))); - } - else if (*s != 0) *error = US"expecting operator"; - } + else if (*s) + *error = US"expecting operator"; *sptr = s; return x; } @@ -3620,12 +3753,12 @@ return x; static int_eximarith_t eval_number(uschar **sptr, BOOL decimal, uschar **error) { -register int c; +int c; int_eximarith_t n; uschar *s = *sptr; + while (isspace(*s)) s++; -c = *s; -if (isdigit(c)) +if (isdigit((c = *s))) { int count; (void)sscanf(CS s, (decimal? SC_EXIM_DEC "%n" : SC_EXIM_ARITH "%n"), &n, &count); @@ -3668,9 +3801,8 @@ if (*s == '+' || *s == '-' || *s == '~') else if (op == '~') x = ~x; } else - { x = eval_number(&s, decimal, error); - } + *sptr = s; return x; } @@ -3849,87 +3981,56 @@ return x; -/* Return pointer to dewrapped string, with enclosing specified chars removed. -The given string is modified on return. Leading whitespace is skipped while -looking for the opening wrap character, then the rest is scanned for the trailing -(non-escaped) wrap character. A backslash in the string will act as an escape. - -A nul is written over the trailing wrap, and a pointer to the char after the -leading wrap is returned. +/************************************************/ +/* Comparison operation for sort expansion. We need to avoid +re-expanding the fields being compared, so need a custom routine. Arguments: - s String for de-wrapping - wrap Two-char string, the first being the opener, second the closer wrapping - character -Return: - Pointer to de-wrapped string, or NULL on error (with expand_string_message set). + cond_type Comparison operator code + leftarg, rightarg Arguments for comparison + +Return true iff (leftarg compare rightarg) */ -static uschar * -dewrap(uschar * s, const uschar * wrap) +static BOOL +sortsbefore(int cond_type, BOOL alpha_cond, + const uschar * leftarg, const uschar * rightarg) { -uschar * p = s; -unsigned depth = 0; -BOOL quotesmode = wrap[0] == wrap[1]; +int_eximarith_t l_num, r_num; -while (isspace(*p)) p++; - -if (*p == *wrap) +if (!alpha_cond) { - s = ++p; - wrap++; - while (*p) + l_num = expanded_string_integer(leftarg, FALSE); + if (expand_string_message) return FALSE; + r_num = expanded_string_integer(rightarg, FALSE); + if (expand_string_message) return FALSE; + + switch (cond_type) { - if (*p == '\\') p++; - else if (!quotesmode && *p == wrap[-1]) depth++; - else if (*p == *wrap) - if (depth == 0) - { - *p = '\0'; - return s; - } - else - depth--; - p++; + case ECOND_NUM_G: return l_num > r_num; + case ECOND_NUM_GE: return l_num >= r_num; + case ECOND_NUM_L: return l_num < r_num; + case ECOND_NUM_LE: return l_num <= r_num; + default: break; } } -expand_string_message = string_sprintf("missing '%c'", *wrap); -return NULL; -} - - -/* Pull off the leading array or object element, returning -a copy in an allocated string. Update the list pointer. - -The element may itself be an abject or array. -*/ - -uschar * -json_nextinlist(const uschar ** list) -{ -unsigned array_depth = 0, object_depth = 0; -const uschar * s = *list, * item; - -while (isspace(*s)) s++; - -for (item = s; - *s && (*s != ',' || array_depth != 0 || object_depth != 0); - s++) - switch (*s) +else + switch (cond_type) { - case '[': array_depth++; break; - case ']': array_depth--; break; - case '{': object_depth++; break; - case '}': object_depth--; break; + case ECOND_STR_LT: return Ustrcmp (leftarg, rightarg) < 0; + case ECOND_STR_LTI: return strcmpic(leftarg, rightarg) < 0; + case ECOND_STR_LE: return Ustrcmp (leftarg, rightarg) <= 0; + case ECOND_STR_LEI: return strcmpic(leftarg, rightarg) <= 0; + case ECOND_STR_GT: return Ustrcmp (leftarg, rightarg) > 0; + case ECOND_STR_GTI: return strcmpic(leftarg, rightarg) > 0; + case ECOND_STR_GE: return Ustrcmp (leftarg, rightarg) >= 0; + case ECOND_STR_GEI: return strcmpic(leftarg, rightarg) >= 0; + default: break; } -*list = *s ? s+1 : s; -item = string_copyn(item, s - item); -DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item); -return US item; +return FALSE; /* should not happen */ } - /************************************************* * Expand string * *************************************************/ @@ -3997,6 +4098,7 @@ static uschar * expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, BOOL skipping, BOOL honour_dollar, BOOL *resetok_p) { +rmark reset_point = store_mark(); gstring * yield = string_get(Ustrlen(string) + 64); int item_type; const uschar *s = string; @@ -4019,6 +4121,14 @@ DEBUG(D_expand) f.expand_string_forcedfail = FALSE; expand_string_message = US""; +if (is_tainted(string)) + { + expand_string_message = + string_sprintf("attempt to expand tainted string '%s'", s); + log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); + goto EXPAND_FAILED; + } + while (*s != 0) { uschar *value; @@ -4090,12 +4200,13 @@ while (*s != 0) buffer. */ if (!yield) - g = store_get(sizeof(gstring)); + g = store_get(sizeof(gstring), FALSE); else if (yield->ptr == 0) { - if (resetok) store_reset(yield); + if (resetok) reset_point = store_reset(reset_point); yield = NULL; - g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */ + reset_point = store_mark(); + g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ } /* Header */ @@ -4221,6 +4332,7 @@ while (*s != 0) { uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; + int rc; switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, US"acl", &resetok)) @@ -4232,7 +4344,7 @@ while (*s != 0) if (skipping) continue; resetok = FALSE; - switch(eval_acl(sub, nelem(sub), &user_msg)) + switch(rc = eval_acl(sub, nelem(sub), &user_msg)) { case OK: case FAIL: @@ -4246,7 +4358,8 @@ while (*s != 0) f.expand_string_forcedfail = TRUE; /*FALLTHROUGH*/ default: - expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + expand_string_message = string_sprintf("%s from acl \"%s\"", + rc_names[rc], sub[0]); goto EXPAND_FAILED; } } @@ -4814,7 +4927,7 @@ while (*s != 0) (void)sscanf(CS now,"%u",&inow); (void)sscanf(CS daystamp,"%u",&iexpire); - /* When "iexpire" is < 7, a "flip" has occured. + /* When "iexpire" is < 7, a "flip" has occurred. Adjust "inow" accordingly. */ if ( (iexpire < 7) && (inow >= 993) ) inow = 0; @@ -4911,17 +5024,16 @@ while (*s != 0) case EITEM_READSOCK: { - int fd; + client_conn_ctx cctx; int timeout = 5; int save_ptr = yield->ptr; - FILE * fp; + 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 SUPPORT_TLS */ - void * tls_ctx = NULL; /* ditto */ + BOOL do_tls = FALSE; /* Only set under ! DISABLE_TLS */ blob reqstr; if (expand_forbid & RDO_READSOCK) @@ -4965,7 +5077,7 @@ while (*s != 0) 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; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS else if (Ustrncmp(item, US"tls=", 4) == 0) { if (Ustrcmp(item + 9, US"no") != 0) do_tls = TRUE; } #endif @@ -5021,11 +5133,12 @@ while (*s != 0) port = ntohs(service_info->s_port); } - fd = ip_connectedsocket(SOCK_STREAM, server_name, port, 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 (fd < 0) + if (cctx.sock < 0) goto SOCK_FAIL; if (!do_tls) reqstr.len = 0; @@ -5038,7 +5151,7 @@ while (*s != 0) struct sockaddr_un sockun; /* don't call this "sun" ! */ int rc; - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + if ((cctx.sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { expand_string_message = string_sprintf("failed to create socket: %s", strerror(errno)); @@ -5051,9 +5164,9 @@ while (*s != 0) server_name = US sockun.sun_path; sigalrm_seen = FALSE; - alarm(timeout); - rc = connect(fd, (struct sockaddr *)(&sockun), sizeof(sockun)); - alarm(0); + 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"; @@ -5071,17 +5184,14 @@ while (*s != 0) DEBUG(D_expand) debug_printf_indent("connected to socket %s\n", sub_arg[0]); -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (do_tls) { + smtp_connect_args conn_args = {.host = &host }; tls_support tls_dummy = {.sni=NULL}; uschar * errstr; - if (!(tls_ctx = tls_client_start(fd, &host, NULL, NULL, -# ifdef SUPPORT_DANE - NULL, -# endif - &tls_dummy, &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; @@ -5099,10 +5209,10 @@ while (*s != 0) DEBUG(D_expand) debug_printf_indent("writing \"%s\" to socket\n", reqstr.data); if ( ( -#ifdef SUPPORT_TLS - tls_ctx ? tls_write(tls_ctx, reqstr.data, reqstr.len, FALSE) : +#ifndef DISABLE_TLS + do_tls ? tls_write(cctx.tls_ctx, reqstr.data, reqstr.len, FALSE) : #endif - write(fd, reqstr.data, reqstr.len)) != reqstr.len) + write(cctx.sock, reqstr.data, reqstr.len)) != reqstr.len) { expand_string_message = string_sprintf("request write to socket " "failed: %s", strerror(errno)); @@ -5115,7 +5225,7 @@ while (*s != 0) system doesn't have this function, make it conditional. */ #ifdef SHUT_WR - if (!tls_ctx && do_shutdown) shutdown(fd, SHUT_WR); + if (!do_tls && do_shutdown) shutdown(cctx.sock, SHUT_WR); #endif if (f.running_in_test_harness) millisleep(100); @@ -5123,22 +5233,22 @@ while (*s != 0) /* Now we need to read from the socket, under a timeout. The function that reads a file can be used. */ - if (!tls_ctx) - fp = fdopen(fd, "rb"); + if (!do_tls) + fp = fdopen(cctx.sock, "rb"); sigalrm_seen = FALSE; - alarm(timeout); + ALARM(timeout); yield = -#ifdef SUPPORT_TLS - tls_ctx ? cat_file_tls(tls_ctx, yield, sub_arg[3]) : +#ifndef DISABLE_TLS + do_tls ? cat_file_tls(cctx.tls_ctx, yield, sub_arg[3]) : #endif cat_file(fp, yield, sub_arg[3]); - alarm(0); + ALARM_CLR(0); -#ifdef SUPPORT_TLS - if (tls_ctx) +#ifndef DISABLE_TLS + if (do_tls) { - tls_close(tls_ctx, TRUE); - close(fd); + tls_close(cctx.tls_ctx, TRUE); + close(cctx.sock); } else #endif @@ -5265,9 +5375,9 @@ while (*s != 0) resetok = FALSE; f = fdopen(fd_out, "rb"); sigalrm_seen = FALSE; - alarm(60); + ALARM(60); lookup_value = string_from_gstring(cat_file(f, NULL, NULL)); - alarm(0); + ALARM_CLR(0); (void)fclose(f); /* Wait for the process to finish, applying the timeout, and inspect its @@ -5278,7 +5388,7 @@ while (*s != 0) { if (sigalrm_seen || runrc == -256) { - expand_string_message = string_sprintf("command timed out"); + expand_string_message = US"command timed out"; killpg(pid, SIGKILL); /* Kill the whole process group */ } @@ -5351,7 +5461,6 @@ while (*s != 0) case EITEM_NHASH: case EITEM_SUBSTR: { - int i; int len; uschar *ret; int val[2] = { 0, -1 }; @@ -5384,9 +5493,8 @@ while (*s != 0) } } - for (i = 0; i < 2; i++) + for (int i = 0; i < 2; i++) if (sub[i]) { - if (sub[i] == NULL) continue; val[i] = (int)Ustrtol(sub[i], &ret, 10); if (*ret != 0 || (i != 0 && val[i] < 0)) { @@ -5424,7 +5532,7 @@ while (*s != 0) md5 md5_base; hctx sha1_ctx; void *use_base; - int type, i; + int type; int hashlen; /* Number of octets for the hash algorithm's output */ int hashblocklen; /* Number of octets the hash algorithm processes */ uschar *keyptr, *p; @@ -5486,7 +5594,7 @@ while (*s != 0) memset(innerkey, 0x36, hashblocklen); memset(outerkey, 0x5c, hashblocklen); - for (i = 0; i < keylen; i++) + for (int i = 0; i < keylen; i++) { innerkey[i] ^= keyptr[i]; outerkey[i] ^= keyptr[i]; @@ -5505,7 +5613,7 @@ while (*s != 0) /* Encode the final hash as a hex string */ p = finalhash_hex; - for (i = 0; i < hashlen; i++) + for (int i = 0; i < hashlen; i++) { *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; *p++ = hex_digits[finalhash[i] & 0x0f]; @@ -5567,7 +5675,6 @@ while (*s != 0) int ovector[3*(EXPAND_MAXN+1)]; int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra, PCRE_EOPT | emptyopt, ovector, nelem(ovector)); - int nn; uschar *insert; /* No match - if we previously set PCRE_NOTEMPTY after a null match, this @@ -5593,7 +5700,7 @@ while (*s != 0) if (n == 0) n = EXPAND_MAXN + 1; expand_nmax = 0; - for (nn = 0; nn < n*2; nn += 2) + for (int nn = 0; nn < n*2; nn += 2) { expand_nstring[expand_nmax] = subject + ovector[nn]; expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; @@ -5637,24 +5744,30 @@ while (*s != 0) case EITEM_EXTRACT: { - int i; - int j; int field_number = 1; BOOL field_number_set = FALSE; uschar *save_lookup_value = lookup_value; uschar *sub[3]; int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); - enum {extract_basic, extract_json} fmt = extract_basic; + + /* On reflection the original behaviour of extract-json for a string + result, leaving it quoted, was a mistake. But it was already published, + hence the addition of jsons. In a future major version, make json + work like josons, and withdraw jsons. */ + + enum {extract_basic, extract_json, extract_jsons} fmt = extract_basic; while (isspace(*s)) s++; /* Check for a format-variant specifier */ if (*s != '{') /*}*/ - { - if (Ustrncmp(s, "json", 4) == 0) {fmt = extract_json; s += 4;} - } + if (Ustrncmp(s, "json", 4) == 0) + if (*(s += 4) == 's') + {fmt = extract_jsons; s++;} + else + fmt = extract_json; /* While skipping we cannot rely on the data for expansions being available (eg. $item) hence cannot decide on numeric vs. keyed. @@ -5662,7 +5775,7 @@ while (*s != 0) if (skipping) { - for (j = 5; j > 0 && *s == '{'; j--) /*'}'*/ + for (int j = 5; j > 0 && *s == '{'; j--) /*'}'*/ { if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)) goto EXPAND_FAILED; /*'{'*/ @@ -5687,7 +5800,7 @@ while (*s != 0) } } - else for (i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */ + else for (int i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */ { while (isspace(*s)) s++; if (*s == '{') /*'}'*/ @@ -5735,7 +5848,7 @@ while (*s != 0) if (*p == 0) { field_number *= x; - if (fmt != extract_json) j = 3; /* Need 3 args */ + if (fmt == extract_basic) j = 3; /* Need 3 args */ field_number_set = TRUE; } } @@ -5762,6 +5875,7 @@ while (*s != 0) break; case extract_json: + case extract_jsons: { uschar * s, * item; const uschar * list; @@ -5789,10 +5903,11 @@ while (*s != 0) } while (field_number > 0 && (item = json_nextinlist(&list))) field_number--; - s = item; - lookup_value = s; - while (*s) s++; - while (--s >= lookup_value && isspace(*s)) *s = '\0'; + if ((lookup_value = s = item)) + { + while (*s) s++; + while (--s >= lookup_value && isspace(*s)) *s = '\0'; + } } else { @@ -5811,12 +5926,12 @@ while (*s != 0) } if (Ustrcmp(item, sub[0]) == 0) /*XXX should be a UTF8-compare */ { - s = item + strlen(item) + 1; + s = item + Ustrlen(item) + 1; while (isspace(*s)) s++; if (*s != ':') { - expand_string_message = string_sprintf( - "missing object value-separator for extract json"); + expand_string_message = + US"missing object value-separator for extract json"; goto EXPAND_FAILED_CURLY; } s++; @@ -5827,6 +5942,17 @@ while (*s != 0) } } } + + if ( fmt == extract_jsons + && lookup_value + && !(lookup_value = dewrap(lookup_value, US"\"\""))) + { + expand_string_message = + string_sprintf("%s wrapping string result for extract jsons", + expand_string_message); + goto EXPAND_FAILED_CURLY; + } + break; /* json/s */ } /* If no string follows, $value gets substituted; otherwise there can @@ -5857,7 +5983,6 @@ while (*s != 0) case EITEM_LISTEXTRACT: { - int i; int field_number = 1; uschar *save_lookup_value = lookup_value; uschar *sub[2]; @@ -5866,10 +5991,10 @@ while (*s != 0) /* Read the field & list arguments */ - for (i = 0; i < 2; i++) + for (int i = 0; i < 2; i++) { while (isspace(*s)) s++; - if (*s != '{') /*}*/ + if (*s != '{') /*'}'*/ { expand_string_message = string_sprintf( "missing '{' for arg %d of listextract", i+1); @@ -5953,7 +6078,7 @@ while (*s != 0) continue; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS case EITEM_CERTEXTRACT: { uschar *save_lookup_value = lookup_value; @@ -6033,7 +6158,7 @@ while (*s != 0) save_expand_nlength); continue; } -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ /* Handle list operations */ @@ -6239,9 +6364,10 @@ while (*s != 0) case EITEM_SORT: { + int cond_type; int sep = 0; const uschar *srclist, *cmp, *xtract; - uschar *srcitem; + uschar * opname, * srcitem; const uschar *dstlist = NULL, *dstkeylist = NULL; uschar * tmp; uschar *save_iterate_item = iterate_item; @@ -6276,6 +6402,25 @@ while (*s != 0) goto EXPAND_FAILED_CURLY; } + if ((cond_type = identify_operator(&cmp, &opname)) == -1) + { + if (!expand_string_message) + expand_string_message = string_sprintf("unknown condition \"%s\"", s); + goto EXPAND_FAILED; + } + switch(cond_type) + { + case ECOND_NUM_L: case ECOND_NUM_LE: + case ECOND_NUM_G: case ECOND_NUM_GE: + case ECOND_STR_GE: case ECOND_STR_GEI: case ECOND_STR_GT: case ECOND_STR_GTI: + case ECOND_STR_LE: case ECOND_STR_LEI: case ECOND_STR_LT: case ECOND_STR_LTI: + break; + + default: + expand_string_message = US"comparator not handled for sort"; + goto EXPAND_FAILED; + } + while (isspace(*s)) s++; if (*s++ != '{') { @@ -6284,8 +6429,8 @@ while (*s != 0) } xtract = s; - tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); - if (!tmp) goto EXPAND_FAILED; + if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok))) + goto EXPAND_FAILED; xtract = string_copyn(xtract, s - xtract); if (*s++ != '}') @@ -6303,11 +6448,10 @@ while (*s != 0) if (skipping) continue; while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0))) - { - uschar * dstitem; + { + uschar * srcfield, * dstitem; gstring * newlist = NULL; gstring * newkeylist = NULL; - uschar * srcfield; DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, srcitem); @@ -6328,25 +6472,15 @@ while (*s != 0) while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) { uschar * dstfield; - uschar * expr; - BOOL before; /* field for comparison */ if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) goto sort_mismatch; - /* build and run condition string */ - expr = string_sprintf("%s{%s}{%s}", cmp, srcfield, dstfield); + /* String-comparator names start with a letter; numeric names do not */ - DEBUG(D_expand) debug_printf_indent("%s: cond = \"%s\"\n", name, expr); - if (!eval_condition(expr, &resetok, &before)) - { - expand_string_message = string_sprintf("comparison in sort: %s", - expr); - goto EXPAND_FAILED; - } - - if (before) + if (sortsbefore(cond_type, isalpha(opname[0]), + srcfield, dstfield)) { /* New-item sorts before this dst-item. Append new-item, then dst-item, then remainder of dst list. */ @@ -6358,6 +6492,7 @@ while (*s != 0) newlist = string_append_listele(newlist, sep, dstitem); newkeylist = string_append_listele(newkeylist, sep, dstfield); +/*XXX why field-at-a-time copy? Why not just dup the rest of the list? */ while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) { if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) @@ -6443,8 +6578,7 @@ while (*s != 0) /* Look up the dynamically loaded object handle in the tree. If it isn't found, dlopen() the file and put the handle in the tree for next time. */ - t = tree_search(dlobj_anchor, argv[0]); - if (t == NULL) + if (!(t = tree_search(dlobj_anchor, argv[0]))) { void *handle = dlopen(CS argv[0], RTLD_LAZY); if (handle == NULL) @@ -6454,7 +6588,7 @@ while (*s != 0) log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); goto EXPAND_FAILED; } - t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0])); + t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]), is_tainted(argv[0])); Ustrcpy(t->name, argv[0]); t->data.ptr = handle; (void)tree_insertnode(&dlobj_anchor, t); @@ -6546,7 +6680,7 @@ while (*s != 0) int c; uschar *arg = NULL; uschar *sub; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS var_entry *vp = NULL; #endif @@ -6569,7 +6703,7 @@ while (*s != 0) as we do not want to do the usual expansion. For most, expand the string.*/ switch(c) { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS case EOP_MD5: case EOP_SHA1: case EOP_SHA256: @@ -6673,7 +6807,6 @@ while (*s != 0) case EOP_BASE62D: { - uschar buf[16]; uschar *tt = sub; unsigned long int n = 0; while (*tt != 0) @@ -6688,8 +6821,7 @@ while (*s != 0) } n = n * BASE_62 + (t - base62_chars); } - (void)sprintf(CS buf, "%ld", n); - yield = string_cat(yield, buf); + yield = string_fmt_append(yield, "%ld", n); continue; } @@ -6726,7 +6858,7 @@ while (*s != 0) } case EOP_MD5: -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (vp && *(void **)vp->value) { uschar * cp = tls_cert_fprt_md5(*(void **)vp->value); @@ -6737,17 +6869,15 @@ while (*s != 0) { md5 base; uschar digest[16]; - int j; - char st[33]; md5_start(&base); md5_end(&base, sub, Ustrlen(sub), digest); - for(j = 0; j < 16; j++) sprintf(st+2*j, "%02x", digest[j]); - yield = string_cat(yield, US st); + for (int j = 0; j < 16; j++) + yield = string_fmt_append(yield, "%02x", digest[j]); } continue; case EOP_SHA1: -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (vp && *(void **)vp->value) { uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value); @@ -6758,40 +6888,41 @@ while (*s != 0) { hctx h; uschar digest[20]; - int j; - char st[41]; sha1_start(&h); sha1_end(&h, sub, Ustrlen(sub), digest); - for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]); - yield = string_catn(yield, US st, 40); + for (int j = 0; j < 20; j++) + yield = string_fmt_append(yield, "%02X", digest[j]); } continue; + case EOP_SHA2: case EOP_SHA256: #ifdef EXIM_HAVE_SHA2 if (vp && *(void **)vp->value) - { - uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value); - yield = string_cat(yield, cp); - } + if (c == EOP_SHA256) + yield = string_cat(yield, tls_cert_fprt_sha256(*(void **)vp->value)); + else + expand_string_message = US"sha2_N not supported with certificates"; else { hctx h; blob b; - char st[3]; + hashmethod m = !arg ? HASH_SHA2_256 + : Ustrcmp(arg, "256") == 0 ? HASH_SHA2_256 + : Ustrcmp(arg, "384") == 0 ? HASH_SHA2_384 + : Ustrcmp(arg, "512") == 0 ? HASH_SHA2_512 + : HASH_BADTYPE; - if (!exim_sha_init(&h, HASH_SHA2_256)) + if (m == HASH_BADTYPE || !exim_sha_init(&h, m)) { - expand_string_message = US"unrecognised sha256 variant"; + expand_string_message = US"unrecognised sha2 variant"; goto EXPAND_FAILED; } + exim_sha_update(&h, sub, Ustrlen(sub)); exim_sha_finish(&h, &b); while (b.len-- > 0) - { - sprintf(st, "%02X", *b.data++); - yield = string_catn(yield, US st, 2); - } + yield = string_fmt_append(yield, "%02X", *b.data++); } #else expand_string_message = US"sha256 only supported with TLS"; @@ -6803,7 +6934,6 @@ while (*s != 0) { hctx h; blob b; - char st[3]; hashmethod m = !arg ? HASH_SHA3_256 : Ustrcmp(arg, "224") == 0 ? HASH_SHA3_224 : Ustrcmp(arg, "256") == 0 ? HASH_SHA3_256 @@ -6820,10 +6950,7 @@ while (*s != 0) exim_sha_update(&h, sub, Ustrlen(sub)); exim_sha_finish(&h, &b); while (b.len-- > 0) - { - sprintf(st, "%02X", *b.data++); - yield = string_catn(yield, US st, 2); - } + yield = string_fmt_append(yield, "%02X", *b.data++); } continue; #else @@ -6841,7 +6968,7 @@ while (*s != 0) uschar *out = sub; uschar *enc; - for (enc = sub; *enc != 0; enc++) + for (enc = sub; *enc; enc++) { if (!isxdigit(*enc)) { @@ -6864,9 +6991,7 @@ while (*s != 0) if (isdigit(c)) c -= '0'; else c = toupper(c) - 'A' + 10; if (b == -1) - { b = c << 4; - } else { *out++ = b | c; @@ -6874,7 +6999,7 @@ while (*s != 0) } } - enc = b64encode(sub, out - sub); + enc = b64encode(CUS sub, out - sub); yield = string_cat(yield, enc); continue; } @@ -6887,7 +7012,7 @@ while (*s != 0) while (*(++t) != 0) { if (*t < 0x21 || 0x7E < *t) - yield = string_catn(yield, string_sprintf("\\x%02x", *t), 4); + yield = string_fmt_append(yield, "\\x%02x", *t); else yield = string_catn(yield, t, 1); } @@ -6900,12 +7025,10 @@ while (*s != 0) { int cnt = 0; int sep = 0; - uschar * cp; uschar buffer[256]; while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; - cp = string_sprintf("%d", cnt); - yield = string_cat(yield, cp); + yield = string_fmt_append(yield, "%d", cnt); continue; } @@ -6937,7 +7060,7 @@ while (*s != 0) 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 = string_sprintf("bad suffix on \"list\" operator"); + expand_string_message = US"bad suffix on \"list\" operator"; goto EXPAND_FAILED; } @@ -7083,16 +7206,12 @@ while (*s != 0) uschar * t = parse_extract_address(sub, &error, &start, &end, &domain, FALSE); if (t) - if (c != EOP_DOMAIN) - { - if (c == EOP_LOCAL_PART && domain != 0) end = start + domain - 1; - yield = string_catn(yield, sub+start, end-start); - } - else if (domain != 0) - { - domain += start; - yield = string_catn(yield, sub+domain, end-domain); - } + if (c != EOP_DOMAIN) + yield = c == EOP_LOCAL_PART && domain > 0 + ? string_catn(yield, t, domain - 1) + : string_cat(yield, t); + else if (domain > 0) + yield = string_cat(yield, t + domain); continue; } @@ -7116,7 +7235,7 @@ while (*s != 0) for (;;) { - uschar *p = parse_find_address_end(sub, FALSE); + uschar * p = parse_find_address_end(sub, FALSE); uschar saveend = *p; *p = '\0'; address = parse_extract_address(sub, &error, &start, &end, &domain, @@ -7129,7 +7248,7 @@ while (*s != 0) list, add in a space if the new address begins with the separator character, or is an empty string. */ - if (address != NULL) + if (address) { if (yield->ptr != save_ptr && address[0] == *outsep) yield = string_catn(yield, US" ", 1); @@ -7271,9 +7390,9 @@ while (*s != 0) case EOP_RFC2047: { uschar buffer[2048]; - const uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset, - buffer, sizeof(buffer), FALSE); - yield = string_cat(yield, string); + yield = string_cat(yield, + parse_quote_2047(sub, Ustrlen(sub), headers_charset, + buffer, sizeof(buffer), FALSE)); continue; } @@ -7474,13 +7593,12 @@ while (*s != 0) case EOP_ESCAPE8BIT: { - const uschar * s = sub; uschar c; - for (s = sub; (c = *s); s++) + for (const uschar * s = sub; (c = *s); s++) yield = c < 127 && c != '\\' ? string_catn(yield, s, 1) - : string_catn(yield, string_sprintf("\\%03o", c), 4); + : string_fmt_append(yield, "\\%03o", c); continue; } @@ -7492,19 +7610,18 @@ while (*s != 0) uschar *save_sub = sub; uschar *error = NULL; int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE); - if (error != NULL) + if (error) { expand_string_message = string_sprintf("error in expression " "evaluation: %s (after processing \"%.*s\")", error, (int)(sub-save_sub), save_sub); goto EXPAND_FAILED; } - sprintf(CS var_buffer, PR_EXIM_ARITH, n); - yield = string_cat(yield, var_buffer); + yield = string_fmt_append(yield, PR_EXIM_ARITH, n); continue; } - /* Handle time period formating */ + /* Handle time period formatting */ case EOP_TIME_EVAL: { @@ -7515,8 +7632,7 @@ while (*s != 0) "Exim time interval in \"%s\" operator", sub, name); goto EXPAND_FAILED; } - sprintf(CS var_buffer, "%d", n); - yield = string_cat(yield, var_buffer); + yield = string_fmt_append(yield, "%d", n); continue; } @@ -7540,12 +7656,12 @@ while (*s != 0) case EOP_STR2B64: case EOP_BASE64: { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS uschar * s = vp && *(void **)vp->value ? tls_cert_der_b64(*(void **)vp->value) - : b64encode(sub, Ustrlen(sub)); + : b64encode(CUS sub, Ustrlen(sub)); #else - uschar * s = b64encode(sub, Ustrlen(sub)); + uschar * s = b64encode(CUS sub, Ustrlen(sub)); #endif yield = string_cat(yield, s); continue; @@ -7568,12 +7684,8 @@ while (*s != 0) /* strlen returns the length of the string */ case EOP_STRLEN: - { - uschar buff[24]; - (void)sprintf(CS buff, "%d", Ustrlen(sub)); - yield = string_cat(yield, buff); + yield = string_fmt_append(yield, "%d", Ustrlen(sub)); continue; - } /* length_n or l_n takes just the first n characters or the whole string, whichever is the shorter; @@ -7606,7 +7718,7 @@ while (*s != 0) int len; uschar *ret; - if (arg == NULL) + if (!arg) { expand_string_message = string_sprintf("missing values after %s", name); @@ -7670,14 +7782,12 @@ while (*s != 0) case EOP_STAT: { - uschar *s; uschar smode[12]; uschar **modetable[3]; - int i; mode_t mode; struct stat st; - if ((expand_forbid & RDO_EXISTS) != 0) + if (expand_forbid & RDO_EXISTS) { expand_string_message = US"Use of the stat() expansion is not permitted"; goto EXPAND_FAILED; @@ -7704,20 +7814,20 @@ while (*s != 0) modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid; modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid; - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3); mode >>= 3; } smode[10] = 0; - s = string_sprintf("mode=%04lo smode=%s inode=%ld device=%ld links=%ld " + yield = string_fmt_append(yield, + "mode=%04lo smode=%s inode=%ld device=%ld links=%ld " "uid=%ld gid=%ld size=" OFF_T_FMT " atime=%ld mtime=%ld ctime=%ld", (long)(st.st_mode & 077777), smode, (long)st.st_ino, (long)st.st_dev, (long)st.st_nlink, (long)st.st_uid, (long)st.st_gid, st.st_size, (long)st.st_atime, (long)st.st_mtime, (long)st.st_ctime); - yield = string_cat(yield, s); continue; } @@ -7725,14 +7835,11 @@ while (*s != 0) case EOP_RANDINT: { - int_eximarith_t max; - uschar *s; + int_eximarith_t max = expanded_string_integer(sub, TRUE); - max = expanded_string_integer(sub, TRUE); - if (expand_string_message != NULL) + if (expand_string_message) goto EXPAND_FAILED; - s = string_sprintf("%d", vaguely_random_number((int)max)); - yield = string_cat(yield, s); + yield = string_fmt_append(yield, "%d", vaguely_random_number((int)max)); continue; } @@ -7758,9 +7865,9 @@ while (*s != 0) /* Unknown operator */ default: - expand_string_message = - string_sprintf("unknown expansion operator \"%s\"", name); - goto EXPAND_FAILED; + expand_string_message = + string_sprintf("unknown expansion operator \"%s\"", name); + goto EXPAND_FAILED; } } @@ -7778,12 +7885,13 @@ while (*s != 0) gstring * g = NULL; if (!yield) - g = store_get(sizeof(gstring)); + g = store_get(sizeof(gstring), FALSE); else if (yield->ptr == 0) { - if (resetok) store_reset(yield); + if (resetok) reset_point = store_reset(reset_point); yield = NULL; - g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */ + reset_point = store_mark(); + g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ } if (!(value = find_variable(name, FALSE, skipping, &newsize))) { @@ -7837,15 +7945,20 @@ if (left) *left = s; In many cases the final string will be the first one that was got and so there will be optimal store usage. */ -if (resetok) store_reset(yield->s + (yield->size = yield->ptr + 1)); +if (resetok) gstring_release_unused(yield); else if (resetok_p) *resetok_p = FALSE; DEBUG(D_expand) + { + BOOL tainted = is_tainted(yield->s); DEBUG(D_noutf8) { debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); debug_printf_indent("%sresult: %s\n", skipping ? "|-----" : "\\_____", yield->s); + if (tainted) + debug_printf_indent("%s \\__(tainted)\n", + skipping ? "| " : " "); if (skipping) debug_printf_indent("\\___skipping: result is not used\n"); } @@ -7854,15 +7967,19 @@ DEBUG(D_expand) debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ "expanding: %.*s\n", (int)(s - string), string); - debug_printf_indent("%s" - UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "result: %s\n", skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, yield->s); + if (tainted) + debug_printf_indent("%s(tainted)\n", + skipping + ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); if (skipping) debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "skipping: result is not used\n"); } + } expand_level--; return yield->s; @@ -8237,23 +8354,21 @@ assert_no_variables(void * ptr, int len, const char * filename, int linenumber) { err_ctx e = { .region_start = ptr, .region_end = US ptr + len, .var_name = NULL, .var_data = NULL }; -int i; -var_entry * v; /* check acl_ variables */ tree_walk(acl_var_c, assert_variable_notin, &e); tree_walk(acl_var_m, assert_variable_notin, &e); /* check auth variables */ -for (i = 0; i < AUTH_VARS; i++) if (auth_vars[i]) +for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i]) assert_variable_notin(US"auth", auth_vars[i], &e); /* check regex variables */ -for (i = 0; i < REGEX_VARS; i++) if (regex_vars[i]) +for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i]) assert_variable_notin(US"regex", regex_vars[i], &e); /* check known-name variables */ -for (v = var_table; v < var_table + var_table_size; v++) +for (var_entry * v = var_table; v < var_table + var_table_size; v++) if (v->type == vtype_stringptr) assert_variable_notin(US v->name, *(USS v->value), &e); @@ -8290,9 +8405,8 @@ BOOL yield = n >= 0; if (n == 0) n = EXPAND_MAXN + 1; if (yield) { - int nn; - expand_nmax = (setup < 0)? 0 : setup + 1; - for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2) + expand_nmax = setup < 0 ? 0 : setup + 1; + for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2) { expand_nstring[expand_nmax] = subject + ovector[nn]; expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; @@ -8305,7 +8419,6 @@ return yield; int main(int argc, uschar **argv) { -int i; uschar buffer[1024]; debug_selector = D_v; @@ -8313,7 +8426,7 @@ debug_file = stderr; debug_fd = fileno(debug_file); big_buffer = malloc(big_buffer_size); -for (i = 1; i < argc; i++) +for (int i = 1; i < argc; i++) { if (argv[i][0] == '+') { @@ -8364,15 +8477,15 @@ if (opt_perl_startup != NULL) } #endif /* EXIM_PERL */ +/* Thie deliberately regards the input as untainted, so that it can be +expanded; only reasonable since this is a test for string-expansions. */ + while (fgets(buffer, sizeof(buffer), stdin) != NULL) { - void *reset_point = store_get(0); + rmark reset_point = store_mark(); uschar *yield = expand_string(buffer); - if (yield != NULL) - { + if (yield) printf("%s\n", yield); - store_reset(reset_point); - } else { if (f.search_find_defer) printf("search_find deferred\n"); @@ -8380,6 +8493,7 @@ while (fgets(buffer, sizeof(buffer), stdin) != NULL) if (f.expand_string_forcedfail) printf("Forced failure\n"); printf("\n"); } + store_reset(reset_point); } search_tidyup();