X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=aa8bfe643b9494c6e6de86a8c4ab2728a6482784;hb=e2fe20104068e079266859fbe7a95fdab5d3fee2;hp=6def3c102a40be4ca82f0385bc0823051b264e5c;hpb=fc37f2acaaa440c5265dc01fd693d8f5406f5cf9;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/expand.c b/src/src/expand.c index 6def3c102..aa8bfe643 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -23,16 +23,11 @@ typedef unsigned esi_flags; #define ESI_HONOR_DOLLAR BIT(1) /* $ is meaningfull */ #define ESI_SKIPPING BIT(2) /* value will not be needed */ -/* Recursively called function */ - -static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *); -static int_eximarith_t expanded_string_integer(const uschar *, BOOL); - #ifdef STAND_ALONE # ifndef SUPPORT_CRYPTEQ # define SUPPORT_CRYPTEQ # endif -#endif +#endif /*!STAND_ALONE*/ #ifdef LOOKUP_LDAP # include "lookups/ldap.h" @@ -235,6 +230,7 @@ static uschar *op_table_main[] = { US"expand", US"h", US"hash", + US"headerwrap", US"hex2b64", US"hexquote", US"ipv6denorm", @@ -282,6 +278,7 @@ enum { EOP_EXPAND, EOP_H, EOP_HASH, + EOP_HEADERWRAP, EOP_HEX2B64, EOP_HEXQUOTE, EOP_IPV6DENORM, @@ -476,8 +473,9 @@ typedef struct { int *length; } alblock; -static uschar * fn_recipients(void); typedef uschar * stringptr_fn_t(void); +static uschar * fn_recipients(void); +static uschar * fn_recipients_list(void); static uschar * fn_queue_size(void); /* This table must be kept in alphabetical order. */ @@ -683,7 +681,7 @@ static var_entry var_table[] = { { "qualify_domain", vtype_stringptr, &qualify_domain_sender }, { "qualify_recipient", vtype_stringptr, &qualify_domain_recipient }, { "queue_name", vtype_stringptr, &queue_name }, - { "queue_size", vtype_string_func, &fn_queue_size }, + { "queue_size", vtype_string_func, (void *) &fn_queue_size }, { "rcpt_count", vtype_int, &rcpt_count }, { "rcpt_defer_count", vtype_int, &rcpt_defer_count }, { "rcpt_fail_count", vtype_int, &rcpt_fail_count }, @@ -697,6 +695,7 @@ static var_entry var_table[] = { { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure }, { "recipients", vtype_string_func, (void *) &fn_recipients }, { "recipients_count", vtype_int, &recipients_count }, + { "recipients_list", vtype_string_func, (void *) &fn_recipients_list }, { "regex_cachesize", vtype_int, ®ex_cachesize },/* undocumented; devel observability */ #ifdef WITH_CONTENT_SCAN { "regex_match_string", vtype_stringptr, ®ex_match_string }, @@ -715,6 +714,7 @@ static var_entry var_table[] = { { "sender_fullhost", vtype_stringptr, &sender_fullhost }, { "sender_helo_dnssec", vtype_bool, &sender_helo_dnssec }, { "sender_helo_name", vtype_stringptr, &sender_helo_name }, + { "sender_helo_verified",vtype_string_func, (void *) &sender_helo_verified_boolstr }, { "sender_host_address", vtype_stringptr, &sender_host_address }, { "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated }, { "sender_host_dnssec", vtype_bool, &sender_host_dnssec }, @@ -834,8 +834,6 @@ static var_entry var_table[] = { { "warnmsg_recipients", vtype_stringptr, &warnmsg_recipients } }; -static int var_table_size = nelem(var_table); - #ifdef MACRO_PREDEF /* dummies */ @@ -843,6 +841,7 @@ uschar * fn_arc_domains(void) {return NULL;} uschar * fn_hdrs_added(void) {return NULL;} uschar * fn_queue_size(void) {return NULL;} uschar * fn_recipients(void) {return NULL;} +uschar * fn_recipients_list(void) {return NULL;} uschar * sender_helo_verified_boolstr(void) {return NULL;} uschar * smtp_cmd_hist(void) {return NULL;} @@ -939,6 +938,10 @@ static uschar *mtable_sticky[] = #define FH_WANT_RAW BIT(1) #define FH_WANT_LIST BIT(2) +/* Recursively called function */ +static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *); +static int_eximarith_t expanded_string_integer(const uschar *, BOOL); + /************************************************* * Tables for UTF-8 support * @@ -1277,7 +1280,7 @@ static var_entry * find_var_ent(uschar * name) { int first = 0; -int last = var_table_size; +int last = nelem(var_table); while (last > first) { @@ -1666,12 +1669,13 @@ Returns: NULL if the header does not exist, else a pointer to a new */ static uschar * -find_header(uschar *name, int *newsize, unsigned flags, const uschar *charset) +find_header(uschar * name, int * newsize, unsigned flags, const uschar * charset) { BOOL found = !name; int len = name ? Ustrlen(name) : 0; BOOL comma = FALSE; gstring * g = NULL; +uschar * rawhdr; for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old && h->text) /* NULL => Received: placeholder */ @@ -1734,8 +1738,9 @@ if (!g) return US""; /* That's all we do for raw header expansion. */ *newsize = g->size; +rawhdr = string_from_gstring(g); if (flags & FH_WANT_RAW) - return string_from_gstring(g); + return rawhdr; /* Otherwise do RFC 2047 decoding, translating the charset if requested. The rfc2047_decode2() function can return an error with decoded data if the @@ -1743,12 +1748,12 @@ charset translation fails. If decoding fails, it returns NULL. */ else { - uschar * error, * decoded = rfc2047_decode2(string_from_gstring(g), + uschar * error, * decoded = rfc2047_decode2(rawhdr, check_rfc2047_length, charset, '?', NULL, newsize, &error); if (error) DEBUG(D_any) debug_printf("*** error in RFC 2047 decoding: %s\n" - " input was: %s\n", error, g->s); - return decoded ? decoded : string_from_gstring(g); + " input was: %s\n", error, rawhdr); + return decoded ? decoded : rawhdr; } } @@ -1798,7 +1803,9 @@ return g; *************************************************/ /* A recipients list is available only during system message filtering, during ACL processing after DATA, and while expanding pipe commands -generated from a system filter, but not elsewhere. */ +generated from a system filter, but not elsewhere. Note that this does +not check for comman in the elements, and uses comma-space as seperator - +so cannot be used as an exim list as-is. */ static uschar * fn_recipients(void) @@ -1813,7 +1820,24 @@ for (int i = 0; i < recipients_count; i++) s = recipients_list[i].address; g = string_append2_listele_n(g, US", ", s, Ustrlen(s)); } -return g ? g->s : NULL; +gstring_release_unused(g); +return string_from_gstring(g); +} + +/* Similar, but as a properly-quoted exim list */ + + +static uschar * +fn_recipients_list(void) +{ +gstring * g = NULL; + +if (!f.enable_dollar_recipients) return NULL; + +for (int i = 0; i < recipients_count; i++) + g = string_append_listele(g, ':', recipients_list[i].address); +gstring_release_unused(g); +return string_from_gstring(g); } @@ -2035,7 +2059,8 @@ switch (vp->type) if (!*ss && deliver_datafile >= 0) /* Read body when needed */ { uschar * body; - off_t start_offset = SPOOL_DATA_START_OFFSET; + off_t start_offset_o = spool_data_start_offset(message_id); + off_t start_offset = start_offset_o; int len = message_body_visible; if (len > message_size) len = message_size; @@ -2047,8 +2072,8 @@ switch (vp->type) if (fstat(deliver_datafile, &statbuf) == 0) { start_offset = statbuf.st_size - len; - if (start_offset < SPOOL_DATA_START_OFFSET) - start_offset = SPOOL_DATA_START_OFFSET; + if (start_offset < start_offset_o) + start_offset = start_offset_o; } } if (lseek(deliver_datafile, start_offset, SEEK_SET) < 0) @@ -2116,7 +2141,7 @@ switch (vp->type) case vtype_string_func: { stringptr_fn_t * fn = (stringptr_fn_t *) val; - uschar* s = fn(); + uschar * s = fn(); return s ? s : US""; } @@ -2381,19 +2406,26 @@ static uschar * json_nextinlist(const uschar ** list) { unsigned array_depth = 0, object_depth = 0; +BOOL quoted = FALSE; const uschar * s = *list, * item; skip_whitespace(&s); for (item = s; - *s && (*s != ',' || array_depth != 0 || object_depth != 0); + *s && (*s != ',' || array_depth != 0 || object_depth != 0 || quoted); s++) - switch (*s) + if (!quoted) switch (*s) { case '[': array_depth++; break; case ']': array_depth--; break; case '{': object_depth++; break; case '}': object_depth--; break; + case '"': quoted = TRUE; + } + else switch(*s) + { + case '\\': s++; break; /* backslash protects one char */ + case '"': quoted = FALSE; break; } *list = *s ? s+1 : s; if (item == s) return NULL; @@ -3521,7 +3553,7 @@ switch(cond_type = identify_operator(&s, &opname)) /* Match the given local_part against the SRS-encoded pattern */ - re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$", + re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]{2})=([^=]*)=(.*)$", MCS_CASELESS | MCS_CACHEABLE, FALSE); md = pcre2_match_data_create(4+1, pcre_gen_ctx); if (pcre2_match(re, sub[0], PCRE2_ZERO_TERMINATED, 0, PCRE_EOPT, @@ -3954,10 +3986,9 @@ if (Ustrlen(key) > 64) hash_source = string_catn(NULL, key_num, 1); hash_source = string_catn(hash_source, daystamp, 3); hash_source = string_cat(hash_source, address); -(void) string_from_gstring(hash_source); DEBUG(D_expand) - debug_printf_indent("prvs: hash source is '%s'\n", hash_source->s); + debug_printf_indent("prvs: hash source is '%Y'\n", hash_source); memset(innerkey, 0x36, 64); memset(outerkey, 0x5c, 64); @@ -4732,6 +4763,7 @@ while (*s) reset in the middle of the buffer will make it inaccessible. */ len = Ustrlen(value); + DEBUG(D_expand) debug_expansion_interim(US"value", value, len, !!(flags & ESI_SKIPPING)); if (!yield && newsize != 0) { yield = g; @@ -4745,12 +4777,15 @@ while (*s) continue; } - if (isdigit(*s)) + if (isdigit(*s)) /* A $ variable */ { int n; s = read_cnumber(&n, s); if (n >= 0 && n <= expand_nmax) + { + DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING)); yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); + } continue; } @@ -4775,7 +4810,10 @@ while (*s) goto EXPAND_FAILED; } if (n >= 0 && n <= expand_nmax) + { + DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING)); yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); + } continue; } @@ -4796,7 +4834,7 @@ while (*s) skipping, but "break" otherwise so we get debug output for the item expansion. */ { - int start = gstring_length(yield); + int expansion_start = gstring_length(yield); switch(item_type) { /* Call an ACL from an expansion. We feed data in via $acl_arg1 - $acl_arg9. @@ -4852,15 +4890,15 @@ while (*s) switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* If skipping, we don't actually do anything */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /*XXX no skipping-optimisation? */ yield = string_append(yield, 3, US"Authentication-Results: ", sub_arg[0], US"; none"); - yield->ptr -= 6; + yield->ptr -= 6; /* ignore tha ": none" for now */ yield = authres_local(yield, sub_arg[0]); yield = authres_iprev(yield); @@ -4943,7 +4981,6 @@ while (*s) case 2: case 3: goto EXPAND_FAILED; } - /*XXX no skipping-optimisation? */ if (!sub_arg[1]) /* One argument */ { @@ -5374,18 +5411,18 @@ while (*s) if (iexpire >= inow) { prvscheck_result = US"1"; - DEBUG(D_expand) debug_printf_indent("prvscheck: success, $pvrs_result set to 1\n"); + DEBUG(D_expand) debug_printf_indent("prvscheck: success, $prvscheck_result set to 1\n"); } else { prvscheck_result = NULL; - DEBUG(D_expand) debug_printf_indent("prvscheck: signature expired, $pvrs_result unset\n"); + DEBUG(D_expand) debug_printf_indent("prvscheck: signature expired, $prvscheck_result unset\n"); } } else { prvscheck_result = NULL; - DEBUG(D_expand) debug_printf_indent("prvscheck: hash failure, $pvrs_result unset\n"); + DEBUG(D_expand) debug_printf_indent("prvscheck: hash failure, $prvscheck_result unset\n"); } /* Now expand the final argument. We leave this till now so that @@ -5438,15 +5475,12 @@ while (*s) switch(read_subs(sub_arg, 2, 1, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* If skipping, we don't actually do anything */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /* If skipping, we don't actually do anything */ - - if (flags & ESI_SKIPPING) continue; - /* Open the file and read it */ if (!(f = Ufopen(sub_arg[0], "rb"))) @@ -5611,9 +5645,9 @@ while (*s) { FILE * f; const uschar * arg, ** argv; - BOOL late_expand = TRUE; + unsigned late_expand = TSUC_EXPAND_ARGS | TSUC_ALLOW_TAINTED_ARGS | TSUC_ALLOW_RECIPIENTS; - if ((expand_forbid & RDO_RUN) != 0) + if (expand_forbid & RDO_RUN) { expand_string_message = US"running a command is not permitted"; goto EXPAND_FAILED; @@ -5622,9 +5656,8 @@ while (*s) /* Handle options to the "run" */ while (*s == ',') - { if (Ustrncmp(++s, "preexpand", 9) == 0) - { late_expand = FALSE; s += 9; } + { late_expand = 0; s += 9; } else { const uschar * t = s; @@ -5633,7 +5666,6 @@ while (*s) (int)(t-s), s); goto EXPAND_FAILED; } - } Uskip_whitespace(&s); if (*s != '{') /*}*/ @@ -5644,13 +5676,20 @@ while (*s) s++; if (late_expand) /* this is the default case */ - { /*{*/ - int n = Ustrcspn(s, "}"); + { + int n; + const uschar * t; + /* Locate the end of the args */ + (void) expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | ESI_SKIPPING, &t, NULL, NULL); + n = t - s; arg = flags & ESI_SKIPPING ? NULL : string_copyn(s, n); s += n; } else { + DEBUG(D_expand) + debug_printf_indent("args string for ${run} expand before split\n"); if (!(arg = expand_string_internal(s, ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL))) goto EXPAND_FAILED; @@ -5678,7 +5717,6 @@ while (*s) late_expand, /* expand args if not already done */ 0, /* not relevant when... */ NULL, /* no transporting address */ - late_expand, /* allow tainted args, when expand-after-split */ US"${run} expansion", /* for error messages */ &expand_string_message)) /* where to put error message */ goto EXPAND_FAILED; @@ -5769,16 +5807,15 @@ while (*s) case 3: goto EXPAND_FAILED; } - yield = string_cat(yield, sub[0]); - o2m = Ustrlen(sub[2]) - 1; - - if (o2m >= 0) for (; oldptr < yield->ptr; oldptr++) + if ( (yield = string_cat(yield, sub[0])) + && (o2m = Ustrlen(sub[2]) - 1) >= 0) + for (; oldptr < yield->ptr; oldptr++) { - uschar *m = Ustrrchr(sub[1], yield->s[oldptr]); + uschar * m = Ustrrchr(sub[1], yield->s[oldptr]); if (m) { int o = m - sub[1]; - yield->s[oldptr] = sub[2][(o < o2m)? o : o2m]; + yield->s[oldptr] = sub[2][o < o2m ? o : o2m]; } } @@ -6319,6 +6356,7 @@ while (*s) save_expand_strings(save_expand_nstring, save_expand_nlength); /* Read the field & list arguments */ + /*XXX Could we use read_subs here (and get better efficiency for skipping)? */ for (int i = 0; i < 2; i++) { @@ -7036,6 +7074,7 @@ while (*s) case 2: case 3: goto EXPAND_FAILED; } + if (flags & ESI_SKIPPING) continue; if (sub[1] && *(sub[1])) { @@ -7050,13 +7089,11 @@ while (*s) { struct timeval now; unsigned long i; - gstring * h = NULL; gettimeofday(&now, NULL); - for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5) - h = string_catn(h, &base32_chars[i & 0x1f], 1); - if (h) while (h->ptr > 0) - g = string_catn(g, &h->s[--h->ptr], 1); + i = (now.tv_sec / 86400) & 0x3ff; + g = string_catn(g, &base32_chars[i >> 5], 1); + g = string_catn(g, &base32_chars[i & 0x1f], 1); } g = string_catn(g, US"=", 1); @@ -7095,7 +7132,7 @@ while (*s) it was for good reason */ if (quoted) yield = string_catn(yield, US"\"", 1); - yield = string_catn(yield, g->s, g->ptr); + yield = gstring_append(yield, g); if (quoted) yield = string_catn(yield, US"\"", 1); /* @$original_domain */ @@ -7114,10 +7151,11 @@ while (*s) } /* EITEM_* switch */ /*NOTREACHED*/ - DEBUG(D_expand) - if (yield && (start > 0 || *s)) /* only if not the sole expansion of the line */ + DEBUG(D_expand) /* only if not the sole expansion of the line */ + if (yield && (expansion_start > 0 || *s)) debug_expansion_interim(US"item-res", - yield->s + start, yield->ptr - start, !!(flags & ESI_SKIPPING)); + yield->s + expansion_start, yield->ptr - expansion_start, + !!(flags & ESI_SKIPPING)); continue; NOT_ITEM: ; @@ -7153,6 +7191,7 @@ NOT_ITEM: ; /* Deal specially with operators that might take a certificate variable as we do not want to do the usual expansion. For most, expand the string.*/ + switch(c) { #ifndef DISABLE_TLS @@ -7201,16 +7240,16 @@ NOT_ITEM: ; to the main loop top. */ { - int start = yield->ptr; + unsigned expansion_start = gstring_length(yield); switch(c) { case EOP_BASE32: { - uschar *t; + uschar * t; unsigned long int n = Ustrtoul(sub, &t, 10); gstring * g = NULL; - if (*t != 0) + if (*t) { expand_string_message = string_sprintf("argument for base32 " "operator is \"%s\", which is not a decimal number", sub); @@ -7246,13 +7285,13 @@ NOT_ITEM: ; { uschar *t; unsigned long int n = Ustrtoul(sub, &t, 10); - if (*t != 0) + if (*t) { expand_string_message = string_sprintf("argument for base62 " "operator is \"%s\", which is not a decimal number", sub); goto EXPAND_FAILED; } - yield = string_cat(yield, string_base62(n)); + yield = string_cat(yield, string_base62_32(n)); /*XXX only handles 32b input range. Need variants? */ break; } @@ -7262,7 +7301,7 @@ NOT_ITEM: ; { uschar *tt = sub; unsigned long int n = 0; - while (*tt != 0) + while (*tt) { uschar *t = Ustrchr(base62_chars, *tt++); if (!t) @@ -7412,6 +7451,29 @@ NOT_ITEM: ; goto EXPAND_FAILED; #endif + /* Line-wrap a string as if it is a header line */ + + case EOP_HEADERWRAP: + { + unsigned col = 80, lim = 998; + uschar * s; + + if (arg) + { + const uschar * list = arg; + int sep = '_'; + if ((s = string_nextinlist(&list, &sep, NULL, 0))) + { + col = atoi(CS s); + if ((s = string_nextinlist(&list, &sep, NULL, 0))) + lim = atoi(CS s); + } + } + if ((s = wrap_header(sub, col, lim, US"\t", 8))) + yield = string_cat(yield, s); + } + break; + /* Convert hex encoding to base64 encoding */ case EOP_HEX2B64: @@ -7819,16 +7881,19 @@ NOT_ITEM: ; case EOP_UTF8CLEAN: { - int seq_len = 0, index = 0; - int bytes_left = 0; - long codepoint = -1; - int complete; + int seq_len = 0, index = 0, bytes_left = 0, complete; + u_long codepoint = (u_long)-1; uschar seq_buff[4]; /* accumulate utf-8 here */ /* Manually track tainting, as we deal in individual chars below */ - if (!yield->s || !yield->ptr) + if (!yield) + yield = string_get_tainted(Ustrlen(sub), sub); + else if (!yield->s || !yield->ptr) + { yield->s = store_get(yield->size = Ustrlen(sub), sub); + gstring_reset(yield); + } else if (is_incompatible(yield->s, sub)) gstring_rebuffer(yield, sub); @@ -7851,6 +7916,15 @@ NOT_ITEM: ; if (--bytes_left == 0) /* codepoint complete */ if(codepoint > 0x10FFFF) /* is it too large? */ complete = -1; /* error (RFC3629 limit) */ + else if ( (codepoint & 0x1FF800 ) == 0xD800 ) /* surrogate */ + /* A UTF-16 surrogate (which should be one of a pair that + encode a Unicode codepoint that is outside the Basic + Multilingual Plane). Error, not UTF8. + RFC2279.2 is slightly unclear on this, but + https://unicodebook.readthedocs.io/issues.html#strict-utf8-decoder + says "Surrogates characters are also invalid in UTF-8: + characters in U+D800—U+DFFF have to be rejected." */ + complete = -1; else { /* finished; output utf-8 sequence */ yield = string_catn(yield, seq_buff, seq_len); @@ -7860,27 +7934,25 @@ NOT_ITEM: ; } else /* no bytes left: new sequence */ { - if(!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */ + if (!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */ { yield = string_catn(yield, &c, 1); continue; } - if((c & 0xe0) == 0xc0) /* 2-byte sequence */ - { - if(c == 0xc0 || c == 0xc1) /* 0xc0 and 0xc1 are illegal */ + if ((c & 0xe0) == 0xc0) /* 2-byte sequence */ + if (c == 0xc0 || c == 0xc1) /* 0xc0 and 0xc1 are illegal */ complete = -1; else { - bytes_left = 1; - codepoint = c & 0x1f; + bytes_left = 1; + codepoint = c & 0x1f; } - } - else if((c & 0xf0) == 0xe0) /* 3-byte sequence */ + else if ((c & 0xf0) == 0xe0) /* 3-byte sequence */ { bytes_left = 2; codepoint = c & 0x0f; } - else if((c & 0xf8) == 0xf0) /* 4-byte sequence */ + else if ((c & 0xf8) == 0xf0) /* 4-byte sequence */ { bytes_left = 3; codepoint = c & 0x07; @@ -7954,7 +8026,7 @@ NOT_ITEM: ; goto EXPAND_FAILED; } yield = string_cat(yield, s); - DEBUG(D_expand) debug_printf_indent("yield: '%s'\n", yield->s); + DEBUG(D_expand) debug_printf_indent("yield: '%Y'\n", yield); break; } @@ -8062,7 +8134,7 @@ NOT_ITEM: ; case EOP_BASE64D: { uschar * s; - int len = b64decode(sub, &s); + int len = b64decode(sub, &s, sub); if (len < 0) { expand_string_message = string_sprintf("string \"%s\" is not " @@ -8263,8 +8335,9 @@ NOT_ITEM: ; DEBUG(D_expand) { - const uschar * s = yield->s + start; - int i = yield->ptr - start; + const uschar * res = string_from_gstring(yield); + const uschar * s = res + expansion_start; + int i = gstring_length(yield) - expansion_start; BOOL tainted = is_tainted(s); DEBUG(D_noutf8) @@ -8273,7 +8346,7 @@ NOT_ITEM: ; if (tainted) { debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); - debug_print_taint(yield->s); + debug_print_taint(res); } } else @@ -8286,7 +8359,7 @@ NOT_ITEM: ; debug_printf_indent("%s", flags & ESI_SKIPPING ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_print_taint(yield->s); + debug_print_taint(res); } } } @@ -8361,58 +8434,62 @@ if (flags & ESI_BRACE_ENDS && !*s) added to the string. If so, set up an empty string. Add a terminating zero. If left != NULL, return a pointer to the terminator. */ -if (!yield) - yield = string_get(1); -(void) string_from_gstring(yield); -if (left) *left = s; + { + uschar * res; -/* Any stacking store that was used above the final string is no longer needed. -In many cases the final string will be the first one that was got and so there -will be optimal store usage. */ + if (!yield) + yield = string_get(1); + res = string_from_gstring(yield); + if (left) *left = s; -if (resetok) gstring_release_unused(yield); -else if (resetok_p) *resetok_p = FALSE; + /* Any stacking store that was used above the final string is no longer needed. + In many cases the final string will be the first one that was got and so there + will be optimal store usage. */ -DEBUG(D_expand) - { - BOOL tainted = is_tainted(yield->s); - DEBUG(D_noutf8) + if (resetok) gstring_release_unused(yield); + else if (resetok_p) *resetok_p = FALSE; + + DEBUG(D_expand) { - debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); - debug_printf_indent("%sresult: %s\n", - flags & ESI_SKIPPING ? "|-----" : "\\_____", yield->s); - if (tainted) + BOOL tainted = is_tainted(res); + DEBUG(D_noutf8) { - debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); - debug_print_taint(yield->s); + debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); + debug_printf_indent("%sresult: %s\n", + flags & ESI_SKIPPING ? "|-----" : "\\_____", res); + if (tainted) + { + debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); + debug_print_taint(res); + } + if (flags & ESI_SKIPPING) + debug_printf_indent("\\___skipping: result is not used\n"); } - if (flags & ESI_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", - flags & ESI_SKIPPING ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, - yield->s); - if (tainted) + else { - debug_printf_indent("%s", - flags & ESI_SKIPPING - ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_print_taint(yield->s); + 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", + flags & ESI_SKIPPING ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, + res); + if (tainted) + { + debug_printf_indent("%s", + flags & ESI_SKIPPING + ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); + debug_print_taint(res); + } + if (flags & ESI_SKIPPING) + debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + "skipping: result is not used\n"); } - if (flags & ESI_SKIPPING) - debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "skipping: result is not used\n"); } - } -if (textonly_p) *textonly_p = textonly; -expand_level--; -return yield->s; + if (textonly_p) *textonly_p = textonly; + expand_level--; + return res; + } /* This is the failure exit: easiest to program with a goto. We still need to update the pointer to the terminator, for cases of nested calls with "fail". @@ -8801,7 +8878,7 @@ for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i]) #endif /* check known-name variables */ -for (var_entry * v = var_table; v < var_table + var_table_size; v++) +for (var_entry * v = var_table; v < var_table + nelem(var_table); v++) if (v->type == vtype_stringptr) assert_variable_notin(US v->name, *(USS v->value), &e);