X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=14d7ce451471a8d734311c565360c16ac437e3bb;hp=2feaf957b6462f26497452fc6c18c0a3a7472b11;hb=c05bdbd6fcc573e071652f88b468091f57a0430d;hpb=4833456868dd4370c3ab5c79dc1e1812dbb8a36d diff --git a/src/src/expand.c b/src/src/expand.c index 2feaf957b..14d7ce451 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -312,7 +312,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 +362,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, @@ -660,6 +668,9 @@ static var_entry var_table[] = { { "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 }, @@ -738,6 +749,7 @@ 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 }, @@ -748,6 +760,7 @@ static var_entry var_table[] = { { "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 @@ -924,7 +937,7 @@ int rc; uschar *ss = expand_string(condition); if (ss == NULL) { - if (!expand_string_forcedfail && !search_find_defer) + if (!f.expand_string_forcedfail && !f.search_find_defer) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand condition \"%s\" " "for %s %s: %s", condition, m1, m2, expand_string_message); return FALSE; @@ -1130,20 +1143,20 @@ Returns: NULL if the subfield was not found, or */ static uschar * -expand_getkeyed(uschar *key, const uschar *s) +expand_getkeyed(uschar * key, const uschar * s) { int length = Ustrlen(key); while (isspace(*s)) s++; /* Loop to search for the key */ -while (*s != 0) +while (*s) { int dkeylength; - uschar *data; - const uschar *dkey = s; + uschar * data; + const uschar * dkey = s; - while (*s != 0 && *s != '=' && !isspace(*s)) s++; + while (*s && *s != '=' && !isspace(*s)) s++; dkeylength = s - dkey; while (isspace(*s)) s++; if (*s == '=') while (isspace((*(++s)))); @@ -1254,17 +1267,17 @@ return fieldtext; static uschar * expand_getlistele(int field, const uschar * list) { -const uschar * tlist= list; -int sep= 0; +const uschar * tlist = list; +int sep = 0; uschar dummy; -if(field<0) +if (field < 0) { - for(field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++; - sep= 0; + 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))) ; +if (field == 0) return NULL; +while (--field > 0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); } @@ -1298,7 +1311,6 @@ static uschar * expand_getcertele(uschar * field, uschar * certvar) { var_entry * vp; -certfield * cp; if (!(vp = find_var_ent(certvar))) { @@ -1320,9 +1332,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) == ',' @@ -1558,10 +1570,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)) { @@ -1659,7 +1670,7 @@ if this was a non-smtp message. static gstring * authres_local(gstring * g, const uschar * sysname) { -if (!authentication_local) +if (!f.authentication_local) return g; g = string_append(g, 3, US";\n\tlocal=pass (non-smtp, ", sysname, US")"); if (authenticated_id) g = string_append(g, 2, " u=", authenticated_id); @@ -1680,11 +1691,11 @@ else if (host_lookup_deferred) g = string_catn(g, US";\n\tiprev=temperror", 19); else if (host_lookup_failed) g = string_catn(g, US";\n\tiprev=fail", 13); -else +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; } @@ -1702,11 +1713,10 @@ fn_recipients(void) { uschar * s; gstring * g = NULL; -int i; -if (!enable_dollar_recipients) return NULL; +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)); @@ -1796,7 +1806,7 @@ val = vp->value; switch (vp->type) { case vtype_filter_int: - if (!filter_running) return NULL; + if (!f.filter_running) return NULL; /* Fall through */ /* VVVVVVVVVVVV */ case vtype_int: @@ -1954,7 +1964,7 @@ switch (vp->type) 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; @@ -2025,11 +2035,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 != '{') { @@ -2145,6 +2154,89 @@ 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; +} + + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -2171,7 +2263,8 @@ 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 name[256]; @@ -2273,7 +2366,7 @@ switch(cond_type) /* first_delivery tests for first delivery attempt */ case ECOND_FIRST_DELIVERY: - if (yield != NULL) *yield = deliver_firsttime == testfor; + if (yield != NULL) *yield = f.deliver_firsttime == testfor; return s; @@ -2428,7 +2521,7 @@ switch(cond_type) break; case DEFER: - expand_string_forcedfail = TRUE; + f.expand_string_forcedfail = TRUE; /*FALLTHROUGH*/ default: expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); @@ -2521,7 +2614,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 @@ -2738,16 +2831,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); @@ -2776,16 +2868,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); @@ -2948,8 +3039,14 @@ 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; @@ -2990,10 +3087,22 @@ switch(cond_type) 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))) { + 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", name, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) { @@ -3005,8 +3114,8 @@ switch(cond_type) DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", name, 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; @@ -3142,8 +3251,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]; @@ -3171,9 +3279,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]; @@ -3255,8 +3362,8 @@ want this string. Set skipping in the call in the fail case (this will always be the case if we were already skipping). */ sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok); -if (sub1 == NULL && (yes || !expand_string_forcedfail)) goto FAILED; -expand_string_forcedfail = FALSE; +if (sub1 == NULL && (yes || !f.expand_string_forcedfail)) goto FAILED; +f.expand_string_forcedfail = FALSE; if (*s++ != '}') { errwhere = US"'yes' part did not end with '}'"; @@ -3285,8 +3392,8 @@ while (isspace(*s)) s++; if (*s == '{') { sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok); - if (sub2 == NULL && (!yes || !expand_string_forcedfail)) goto FAILED; - expand_string_forcedfail = FALSE; + if (sub2 == NULL && (!yes || !f.expand_string_forcedfail)) goto FAILED; + f.expand_string_forcedfail = FALSE; if (*s++ != '}') { errwhere = US"'no' part did not start with '{'"; @@ -3321,7 +3428,7 @@ else if (*s != '}') } expand_string_message = string_sprintf("\"%s\" failed and \"fail\" requested", type); - expand_string_forcedfail = TRUE; + f.expand_string_forcedfail = TRUE; goto FAILED; } } @@ -3462,7 +3569,6 @@ 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]; @@ -3487,7 +3593,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]; @@ -3502,7 +3608,7 @@ chash_mid(HMAC_SHA1, &h, outerkey); chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash); p = finalhash_hex; -for (i = 0; i < 3; i++) +for (int i = 0; i < 3; i++) { *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; *p++ = hex_digits[finalhash[i] & 0x0f]; @@ -3555,11 +3661,12 @@ 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); @@ -3922,13 +4029,17 @@ BOOL resetok = TRUE; expand_level++; DEBUG(D_expand) - debug_printf_indent(UTF8_DOWN_RIGHT "%s: %s\n", - skipping - ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning" - : "considering", - string); + DEBUG(D_noutf8) + debug_printf_indent("/%s: %s\n", + skipping ? "---scanning" : "considering", string); + else + debug_printf_indent(UTF8_DOWN_RIGHT "%s: %s\n", + skipping + ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning" + : "considering", + string); -expand_string_forcedfail = FALSE; +f.expand_string_forcedfail = FALSE; expand_string_message = US""; while (*s != 0) @@ -4155,7 +4266,7 @@ while (*s != 0) continue; case DEFER: - expand_string_forcedfail = TRUE; + f.expand_string_forcedfail = TRUE; /*FALLTHROUGH*/ default: expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); @@ -4215,15 +4326,21 @@ while (*s != 0) if (next_s == NULL) goto EXPAND_FAILED; /* message already set */ DEBUG(D_expand) - { - debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ - "condition: %.*s\n", - (int)(next_s - s), s); - debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ - UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "result: %s\n", - cond ? "true" : "false"); - } + DEBUG(D_noutf8) + { + debug_printf_indent("|--condition: %.*s\n", (int)(next_s - s), s); + debug_printf_indent("|-----result: %s\n", cond ? "true" : "false"); + } + else + { + debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ + "condition: %.*s\n", + (int)(next_s - s), s); + debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ + UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + "result: %s\n", + cond ? "true" : "false"); + } s = next_s; @@ -4453,7 +4570,7 @@ while (*s != 0) } lookup_value = search_find(handle, filename, key, partial, affix, affixlen, starflags, &expand_setup); - if (search_find_defer) + if (f.search_find_defer) { expand_string_message = string_sprintf("lookup of \"%s\" gave DEFER: %s", @@ -4562,7 +4679,7 @@ while (*s != 0) expand_string_message = string_sprintf("Perl subroutine \"%s\" returned undef to force " "failure", sub_arg[0]); - expand_string_forcedfail = TRUE; + f.expand_string_forcedfail = TRUE; } goto EXPAND_FAILED; } @@ -4570,7 +4687,7 @@ while (*s != 0) /* Yield succeeded. Ensure forcedfail is unset, just in case it got set during a callback from Perl. */ - expand_string_forcedfail = FALSE; + f.expand_string_forcedfail = FALSE; yield = new_yield; continue; } @@ -4720,7 +4837,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; @@ -4817,17 +4934,16 @@ while (*s != 0) case EITEM_READSOCK: { - int fd; + client_conn_ctx cctx; int timeout = 5; int save_ptr = yield->ptr; - FILE *f; + FILE * fp; 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 */ blob reqstr; if (expand_forbid & RDO_READSOCK) @@ -4927,11 +5043,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. 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; @@ -4944,7 +5061,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)); @@ -4954,12 +5071,12 @@ while (*s != 0) sockun.sun_family = AF_UNIX; sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sub_arg[0]); - server_name = sockun.sun_path; + 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"; @@ -4980,14 +5097,11 @@ while (*s != 0) #ifdef SUPPORT_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; @@ -4996,7 +5110,7 @@ while (*s != 0) #endif /* Allow sequencing of test actions */ - if (running_in_test_harness) millisleep(100); + if (f.running_in_test_harness) millisleep(100); /* Write the request string, if not empty or already done */ @@ -5006,9 +5120,9 @@ while (*s != 0) reqstr.data); if ( ( #ifdef SUPPORT_TLS - tls_ctx ? tls_write(tls_ctx, reqstr.data, reqstr.len, FALSE) : + cctx.tls_ctx ? 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)); @@ -5021,34 +5135,34 @@ 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 (!cctx.tls_ctx && do_shutdown) shutdown(cctx.sock, SHUT_WR); #endif - if (running_in_test_harness) millisleep(100); + if (f.running_in_test_harness) millisleep(100); /* Now we need to read from the socket, under a timeout. The function that reads a file can be used. */ - if (!tls_ctx) - f = fdopen(fd, "rb"); + if (!cctx.tls_ctx) + 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]) : + cctx.tls_ctx ? cat_file_tls(cctx.tls_ctx, yield, sub_arg[3]) : #endif - cat_file(f, yield, sub_arg[3]); - alarm(0); + cat_file(fp, yield, sub_arg[3]); + ALARM_CLR(0); #ifdef SUPPORT_TLS - if (tls_ctx) + if (cctx.tls_ctx) { - tls_close(tls_ctx, TRUE); - close(fd); + tls_close(cctx.tls_ctx, TRUE); + close(cctx.sock); } else #endif - (void)fclose(f); + (void)fclose(fp); /* After a timeout, we restore the pointer in the result, that is, make sure we add nothing from the socket. */ @@ -5171,9 +5285,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 @@ -5257,7 +5371,6 @@ while (*s != 0) case EITEM_NHASH: case EITEM_SUBSTR: { - int i; int len; uschar *ret; int val[2] = { 0, -1 }; @@ -5290,9 +5403,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)) { @@ -5330,7 +5442,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; @@ -5392,7 +5504,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]; @@ -5411,7 +5523,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]; @@ -5473,7 +5585,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 @@ -5499,7 +5610,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]; @@ -5543,8 +5654,6 @@ 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; @@ -5552,17 +5661,34 @@ while (*s != 0) int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); + /* 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) + 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. Read a maximum of 5 arguments (including the yes/no) */ if (skipping) { - while (isspace(*s)) s++; - 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; /*{*/ + goto EXPAND_FAILED; /*'{'*/ if (*s++ != '}') { expand_string_message = US"missing '{' for arg of extract"; @@ -5570,13 +5696,13 @@ while (*s != 0) } while (isspace(*s)) s++; } - if ( Ustrncmp(s, "fail", 4) == 0 + if ( Ustrncmp(s, "fail", 4) == 0 /*'{'*/ && (s[4] == '}' || s[4] == ' ' || s[4] == '\t' || !s[4]) ) { s += 4; while (isspace(*s)) s++; - } + } /*'{'*/ if (*s != '}') { expand_string_message = US"missing '}' closing extract"; @@ -5584,13 +5710,13 @@ 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 == '{') /*}*/ + while (isspace(*s)) s++; + if (*s == '{') /*'}'*/ { sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); - if (sub[i] == NULL) goto EXPAND_FAILED; /*{*/ + if (sub[i] == NULL) goto EXPAND_FAILED; /*'{'*/ if (*s++ != '}') { expand_string_message = string_sprintf( @@ -5601,7 +5727,7 @@ while (*s != 0) /* After removal of leading and trailing white space, the first argument must not be empty; if it consists entirely of digits (optionally preceded by a minus sign), this is a numerical - extraction, and we expect 3 arguments. */ + extraction, and we expect 3 arguments (normal) or 2 (json). */ if (i == 0) { @@ -5632,7 +5758,7 @@ while (*s != 0) if (*p == 0) { field_number *= x; - j = 3; /* Need 3 args */ + if (fmt == extract_basic) j = 3; /* Need 3 args */ field_number_set = TRUE; } } @@ -5648,9 +5774,96 @@ while (*s != 0) /* Extract either the numbered or the keyed substring into $value. If skipping, just pretend the extraction failed. */ - lookup_value = skipping? NULL : field_number_set? - expand_gettokened(field_number, sub[1], sub[2]) : - expand_getkeyed(sub[0], sub[1]); + if (skipping) + lookup_value = NULL; + else switch (fmt) + { + case extract_basic: + lookup_value = field_number_set + ? expand_gettokened(field_number, sub[1], sub[2]) + : expand_getkeyed(sub[0], sub[1]); + break; + + case extract_json: + case extract_jsons: + { + uschar * s, * item; + const uschar * list; + + /* Array: Bracket-enclosed and comma-separated. + Object: Brace-enclosed, comma-sep list of name:value pairs */ + + if (!(s = dewrap(sub[1], field_number_set ? US"[]" : US"{}"))) + { + expand_string_message = + string_sprintf("%s wrapping %s for extract json", + expand_string_message, + field_number_set ? "array" : "object"); + goto EXPAND_FAILED_CURLY; + } + + list = s; + if (field_number_set) + { + if (field_number <= 0) + { + expand_string_message = US"first argument of \"extract\" must " + "be greater than zero"; + goto EXPAND_FAILED; + } + while (field_number > 0 && (item = json_nextinlist(&list))) + field_number--; + if ((lookup_value = s = item)) + { + while (*s) s++; + while (--s >= lookup_value && isspace(*s)) *s = '\0'; + } + } + else + { + lookup_value = NULL; + while ((item = json_nextinlist(&list))) + { + /* Item is: string name-sep value. string is quoted. + Dequote the string and compare with the search key. */ + + if (!(item = dewrap(item, US"\"\""))) + { + expand_string_message = + string_sprintf("%s wrapping string key for extract json", + expand_string_message); + goto EXPAND_FAILED_CURLY; + } + if (Ustrcmp(item, sub[0]) == 0) /*XXX should be a UTF8-compare */ + { + s = item + Ustrlen(item) + 1; + while (isspace(*s)) s++; + if (*s != ':') + { + expand_string_message = string_sprintf( + "missing object value-separator for extract json"); + goto EXPAND_FAILED_CURLY; + } + s++; + while (isspace(*s)) s++; + lookup_value = s; + break; + } + } + } + } + + 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 be yes/no strings, as for lookup or if. */ @@ -5680,7 +5893,6 @@ while (*s != 0) case EITEM_LISTEXTRACT: { - int i; int field_number = 1; uschar *save_lookup_value = lookup_value; uschar *sub[2]; @@ -5689,10 +5901,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); @@ -5750,7 +5962,7 @@ while (*s != 0) /* Extract the numbered element into $value. If skipping, just pretend the extraction failed. */ - lookup_value = skipping? NULL : expand_getlistele(field_number, sub[1]); + lookup_value = skipping ? NULL : expand_getlistele(field_number, sub[1]); /* If no string follows, $value gets substituted; otherwise there can be yes/no strings, as for lookup or if. */ @@ -6266,8 +6478,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) @@ -6315,7 +6526,7 @@ while (*s != 0) else { expand_string_message = result == NULL ? US"(no message)" : result; - if(status == FAIL_FORCED) expand_string_forcedfail = TRUE; + if(status == FAIL_FORCED) f.expand_string_forcedfail = TRUE; else if(status != FAIL) log_write(0, LOG_MAIN|LOG_PANIC, "dlfunc{%s}{%s} failed (%d): %s", argv[0], argv[1], status, expand_string_message); @@ -6369,7 +6580,9 @@ while (*s != 0) int c; uschar *arg = NULL; uschar *sub; +#ifdef SUPPORT_TLS var_entry *vp = NULL; +#endif /* Owing to an historical mis-design, an underscore may be part of the operator name, or it may introduce arguments. We therefore first scan the @@ -6494,7 +6707,6 @@ while (*s != 0) case EOP_BASE62D: { - uschar buf[16]; uschar *tt = sub; unsigned long int n = 0; while (*tt != 0) @@ -6509,8 +6721,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; } @@ -6558,12 +6769,10 @@ 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; @@ -6579,12 +6788,10 @@ 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; @@ -6599,7 +6806,6 @@ while (*s != 0) { hctx h; blob b; - char st[3]; if (!exim_sha_init(&h, HASH_SHA2_256)) { @@ -6609,10 +6815,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++); } #else expand_string_message = US"sha256 only supported with TLS"; @@ -6624,7 +6827,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 @@ -6641,10 +6843,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 @@ -6662,7 +6861,7 @@ while (*s != 0) uschar *out = sub; uschar *enc; - for (enc = sub; *enc != 0; enc++) + for (enc = sub; *enc; enc++) { if (!isxdigit(*enc)) { @@ -6685,9 +6884,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; @@ -6695,7 +6892,7 @@ while (*s != 0) } } - enc = b64encode(sub, out - sub); + enc = b64encode(CUS sub, out - sub); yield = string_cat(yield, enc); continue; } @@ -6708,7 +6905,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); } @@ -6721,12 +6918,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; } @@ -6904,16 +7099,11 @@ 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); - } + yield = c == EOP_DOMAIN + ? string_cat(yield, t + domain) + : c == EOP_LOCAL_PART && domain > 0 + ? string_catn(yield, t, domain - 1 ) + : string_cat(yield, t); continue; } @@ -6933,11 +7123,11 @@ while (*s != 0) "missing in expanding ${addresses:%s}", --sub); goto EXPAND_FAILED; } - parse_allow_group = TRUE; + f.parse_allow_group = TRUE; 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, @@ -6950,7 +7140,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); @@ -6982,7 +7172,7 @@ while (*s != 0) separator. */ if (yield->ptr != save_ptr) yield->ptr--; - parse_allow_group = FALSE; + f.parse_allow_group = FALSE; continue; } @@ -7092,9 +7282,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; } @@ -7140,12 +7330,13 @@ while (*s != 0) { int seq_len = 0, index = 0; int bytes_left = 0; - long codepoint = -1; + long codepoint = -1; + int complete; uschar seq_buff[4]; /* accumulate utf-8 here */ while (*sub != 0) { - int complete = 0; + complete = 0; uschar c = *sub++; if (bytes_left) @@ -7210,6 +7401,13 @@ while (*s != 0) /* ASCII character follows incomplete sequence */ yield = string_catn(yield, &c, 1); } + /* If given a sequence truncated mid-character, we also want to report ? + * Eg, ${length_1:フィル} is one byte, not one character, so we expect + * ${utf8clean:${length_1:フィル}} to yield '?' */ + if (bytes_left != 0) + { + yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1); + } continue; } @@ -7287,13 +7485,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; } @@ -7305,19 +7502,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: { @@ -7328,8 +7524,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; } @@ -7356,9 +7551,9 @@ while (*s != 0) #ifdef SUPPORT_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; @@ -7381,12 +7576,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; @@ -7419,7 +7610,7 @@ while (*s != 0) int len; uschar *ret; - if (arg == NULL) + if (!arg) { expand_string_message = string_sprintf("missing values after %s", name); @@ -7483,14 +7674,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; @@ -7517,20 +7706,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; } @@ -7538,14 +7727,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; } @@ -7571,9 +7757,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; } } @@ -7654,19 +7840,28 @@ if (resetok) store_reset(yield->s + (yield->size = yield->ptr + 1)); else if (resetok_p) *resetok_p = FALSE; 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 - "result: %s\n", - skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, - yield->s); - if (skipping) - debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "skipping: result is not used\n"); - } + DEBUG(D_noutf8) + { + debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); + debug_printf_indent("%sresult: %s\n", + skipping ? "|-----" : "\\_____", yield->s); + if (skipping) + debug_printf_indent("\\___skipping: result is not used\n"); + } + else + { + 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 + "result: %s\n", + skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, + yield->s); + 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; @@ -7688,16 +7883,25 @@ that is a bad idea, because expand_string_message is in dynamic store. */ EXPAND_FAILED: if (left) *left = s; DEBUG(D_expand) - { - debug_printf_indent(UTF8_VERT_RIGHT "failed to expand: %s\n", - string); - debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "error message: %s\n", - expand_string_forcedfail ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, - expand_string_message); - if (expand_string_forcedfail) - debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n"); - } + DEBUG(D_noutf8) + { + debug_printf_indent("|failed to expand: %s\n", string); + debug_printf_indent("%serror message: %s\n", + f.expand_string_forcedfail ? "|---" : "\\___", expand_string_message); + if (f.expand_string_forcedfail) + debug_printf_indent("\\failure was forced\n"); + } + else + { + debug_printf_indent(UTF8_VERT_RIGHT "failed to expand: %s\n", + string); + debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + "error message: %s\n", + f.expand_string_forcedfail ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, + expand_string_message); + if (f.expand_string_forcedfail) + debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n"); + } if (resetok_p && !resetok) *resetok_p = FALSE; expand_level--; return NULL; @@ -7720,7 +7924,7 @@ if (Ustrpbrk(string, "$\\") != NULL) int old_pool = store_pool; uschar * s; - search_find_defer = FALSE; + f.search_find_defer = FALSE; malformed_header = FALSE; store_pool = POOL_MAIN; s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL); @@ -7913,7 +8117,7 @@ if (svalue == NULL) { *rvalue = bvalue; return OK; } expanded = expand_string(svalue); if (expanded == NULL) { - if (expand_string_forcedfail) + if (f.expand_string_forcedfail) { DEBUG(dbg_opt) debug_printf("expansion of \"%s\" forced failure\n", oname); *rvalue = bvalue; @@ -7951,7 +8155,7 @@ expand_hide_passwords(uschar * s) { return ( ( Ustrstr(s, "failed to expand") != NULL || Ustrstr(s, "expansion of ") != NULL - ) + ) && ( Ustrstr(s, "mysql") != NULL || Ustrstr(s, "pgsql") != NULL || Ustrstr(s, "redis") != NULL @@ -7961,7 +8165,7 @@ return ( ( Ustrstr(s, "failed to expand") != NULL || Ustrstr(s, "ldapi:") != NULL || Ustrstr(s, "ldapdn:") != NULL || Ustrstr(s, "ldapm:") != NULL - ) ) + ) ) ? US"Temporary internal error" : s; } @@ -8032,23 +8236,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); @@ -8085,9 +8287,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]; @@ -8100,7 +8301,6 @@ return yield; int main(int argc, uschar **argv) { -int i; uschar buffer[1024]; debug_selector = D_v; @@ -8108,7 +8308,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] == '+') { @@ -8170,9 +8370,9 @@ while (fgets(buffer, sizeof(buffer), stdin) != NULL) } else { - if (search_find_defer) printf("search_find deferred\n"); + if (f.search_find_defer) printf("search_find deferred\n"); printf("Failed: %s\n", expand_string_message); - if (expand_string_forcedfail) printf("Forced failure\n"); + if (f.expand_string_forcedfail) printf("Forced failure\n"); printf("\n"); } }