/* Recursively called function */
-static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *);
+static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *, BOOL *);
static int_eximarith_t expanded_string_integer(const uschar *, BOOL);
#ifdef STAND_ALONE
int fd;
ssize_t len;
const uschar * where;
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
uschar * sname;
-#endif
if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
{
return NULL;
}
-#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
-sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
-len = offsetof(struct sockaddr_un, sun_path) + 1
- + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "exim_%d", getpid());
-#else
-sname = string_sprintf("%s/p_%d", spool_directory, getpid());
-len = offsetof(struct sockaddr_un, sun_path)
- + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", sname);
-#endif
+len = daemon_client_sockname(&sa_un, &sname);
-if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0)
+if (bind(fd, (const struct sockaddr *)&sa_un, (socklen_t)len) < 0)
{ where = US"bind"; goto bad; }
#ifdef notdef
check_end if TRUE, check for final '}'
name name of item, for error message
resetok if not NULL, pointer to flag - write FALSE if unsafe to reset
- the store.
+ the store
+ textonly_p if not NULL, pointer to bitmask of which subs were text-only
+ (did not change when expended)
Returns: 0 OK; string pointer updated
1 curly bracketing error (too few arguments)
static int
read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping,
- BOOL check_end, uschar *name, BOOL *resetok)
+ BOOL check_end, uschar *name, BOOL *resetok, unsigned * textonly_p)
{
-const uschar *s = *sptr;
+const uschar * s = *sptr;
+unsigned textonly_l = 0;
Uskip_whitespace(&s);
for (int i = 0; i < n; i++)
{
+ BOOL textonly;
if (*s != '{')
{
if (i < m)
sub[i] = NULL;
break;
}
- if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok)))
+ if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok,
+ textonly_p ? &textonly : NULL)))
return 3;
if (*s++ != '}') return 1;
+ if (textonly_p && textonly) textonly_l |= BIT(i);
Uskip_whitespace(&s);
}
if (check_end && *s++ != '}')
return 1;
}
+if (textonly_p) *textonly_p = textonly_l;
*sptr = s;
return 0;
}
*/
static const uschar *
-eval_condition(const uschar *s, BOOL *resetok, BOOL *yield)
+eval_condition(const uschar * s, BOOL * resetok, BOOL * yield)
{
BOOL testfor = TRUE;
BOOL tempcond, combined_cond;
-BOOL *subcondptr;
+BOOL * subcondptr;
BOOL sub2_honour_dollar = TRUE;
BOOL is_forany, is_json, is_jsons;
int rc, cond_type;
struct stat statbuf;
uschar * opname;
uschar name[256];
-const uschar *sub[10];
+const uschar * sub[10];
+unsigned sub_textonly = 0;
for (;;)
if (Uskip_whitespace(&s) == '!') { testfor = !testfor; s++; } else break;
if (Uskip_whitespace(&s) != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
- sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE, resetok);
- if (!sub[0]) return NULL;
+ {
+ BOOL textonly;
+ sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE, resetok, &textonly);
+ if (!sub[0]) return NULL;
+ if (textonly) sub_textonly |= BIT(0);
+ }
/* {-for-text-editors */
if (*s++ != '}') goto COND_FAILED_CURLY_END;
if (*s++ != '{') goto COND_FAILED_CURLY_START; /*}*/
switch(read_subs(sub, nelem(sub), 1,
- &s, yield == NULL, TRUE, name, resetok))
+ &s, yield == NULL, TRUE, name, resetok, NULL))
{
case 1: expand_string_message = US"too few arguments or bracketing "
"error for acl";
Uskip_whitespace(&s);
if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
switch(read_subs(sub, nelem(sub), 2, &s, yield == NULL, TRUE, name,
- resetok))
+ resetok, NULL))
{
case 1: expand_string_message = US"too few arguments or bracketing "
"error for saslauthd";
for (int i = 0; i < 2; i++)
{
+ BOOL textonly;
/* Sometimes, we don't expand substrings; too many insecure configurations
created using match_address{}{} and friends, where the second param
includes information from untrustworthy sources. */
+ /*XXX is this moot given taint-tracking? */
BOOL honour_dollar = TRUE;
if ((i > 0) && !sub2_honour_dollar)
honour_dollar = FALSE;
return NULL;
}
if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL,
- honour_dollar, resetok)))
+ honour_dollar, resetok, &textonly)))
return NULL;
+ if (textonly) sub_textonly |= BIT(i);
DEBUG(D_expand) if (i == 1 && !sub2_honour_dollar && Ustrchr(sub[1], '$'))
debug_printf_indent("WARNING: the second arg is NOT expanded,"
" for security reasons\n");
case ECOND_MATCH: /* Regular expression match */
{
- const pcre2_code * re;
- PCRE2_SIZE offset;
- int err;
-
- if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
- PCRE_COPT, &err, &offset, pcre_gen_cmp_ctx)))
- {
- uschar errbuf[128];
- pcre2_get_error_message(err, errbuf, sizeof(errbuf));
- expand_string_message = string_sprintf("regular expression error in "
- "\"%s\": %s at offset %ld", sub[1], errbuf, (long)offset);
+ const pcre2_code * re = regex_compile(sub[1],
+ sub_textonly & BIT(1) ? MCS_CACHEABLE : MCS_NOFLAGS,
+ &expand_string_message, pcre_gen_cmp_ctx);
+ if (!re)
return NULL;
- }
tempcond = regex_match_and_setup(re, sub[0], 0, -1);
break;
Uskip_whitespace(&s);
if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
- if (!(sub[0] = expand_string_internal(s, TRUE, &s, yield == NULL, TRUE, resetok)))
+ if (!(sub[0] = expand_string_internal(s, TRUE, &s, yield == NULL, TRUE, resetok, NULL)))
return NULL;
/* {-for-text-editors */
if (*s++ != '}') goto COND_FAILED_CURLY_END;
if (Uskip_whitespace(&s) != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
ourname = cond_type == ECOND_BOOL_LAX ? US"bool_lax" : US"bool";
- switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname, resetok))
+ switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname, resetok, NULL))
{
case 1: expand_string_message = string_sprintf(
"too few arguments or bracketing error for %s",
uschar cksum[4];
BOOL boolvalue = FALSE;
- switch(read_subs(sub, 2, 2, CUSS &s, yield == NULL, FALSE, name, resetok))
+ switch(read_subs(sub, 2, 2, CUSS &s, yield == NULL, FALSE, name, resetok, NULL))
{
case 1: expand_string_message = US"too few arguments or bracketing "
"error for inbound_srs";
/* Match the given local_part against the SRS-encoded pattern */
re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$",
- TRUE, FALSE);
+ 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,
md, pcre_gen_mtc_ctx) < 0)
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);
+sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok, NULL);
if (sub1 == NULL && (yes || !f.expand_string_forcedfail)) goto FAILED;
f.expand_string_forcedfail = FALSE;
if (*s++ != '}')
if (skip_whitespace(&s) == '{')
{
- sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok);
+ sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok, NULL);
if (sub2 == NULL && (!yes || !f.expand_string_forcedfail)) goto FAILED;
f.expand_string_forcedfail = FALSE;
if (*s++ != '}')
FALSE if it's just another character
resetok_p if not NULL, pointer to flag - write FALSE if unsafe to reset
the store.
+ textonly_p if not NULL, pointer to flag - write bool for only-met-text
Returns: NULL if expansion fails:
expand_string_forcedfail is set TRUE if failure was forced
static uschar *
expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left,
- BOOL skipping, BOOL honour_dollar, BOOL *resetok_p)
+ BOOL skipping, BOOL honour_dollar, BOOL *resetok_p, BOOL * textonly_p)
{
rmark reset_point = store_mark();
gstring * yield = string_get(Ustrlen(string) + 64);
const uschar * s = string;
const uschar * save_expand_nstring[EXPAND_MAXN+1];
int save_expand_nlength[EXPAND_MAXN+1];
-BOOL resetok = TRUE, first = TRUE;
+BOOL resetok = TRUE, first = TRUE, textonly = TRUE;
expand_level++;
f.expand_string_forcedfail = FALSE;
s += i;
continue;
}
+ textonly = FALSE;
/* No { after the $ - must be a plain name or a number for string
match variable. There has to be a fudge for variables that are the
int rc;
switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, name,
- &resetok))
+ &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
uschar * sub_arg[1];
switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
- &resetok))
+ &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
uschar *encoded;
switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
- &resetok))
+ &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
if (Uskip_whitespace(&s) == '{') /*}*/
{
- key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+ key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!key) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
expand_string_message = US"missing '{' for lookup file-or-query arg";
goto EXPAND_FAILED_CURLY; /*}}*/
}
- if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
+ if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL)))
goto EXPAND_FAILED;
/*{{*/
if (*s++ != '}')
}
switch(read_subs(sub_arg, EXIM_PERL_MAX_ARGS + 1, 1, &s, skipping, TRUE,
- name, &resetok))
+ name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
{
uschar * sub_arg[3], * p, * domain;
- switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
prvscheck_address = NULL;
prvscheck_keynum = NULL;
- switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok))
+ switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
case 3: goto EXPAND_FAILED;
}
- re = regex_must_compile(US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$",
- TRUE,FALSE);
+ re = regex_must_compile(
+ US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$",
+ MCS_CASELESS | MCS_CACHEABLE, FALSE);
if (regex_match_and_setup(re,sub_arg[0],0,-1))
{
uschar * hash = string_copyn(expand_nstring[3],expand_nlength[3]);
uschar * domain = string_copyn(expand_nstring[5],expand_nlength[5]);
- DEBUG(D_expand) debug_printf_indent("prvscheck localpart: %s\n", local_part);
- DEBUG(D_expand) debug_printf_indent("prvscheck key number: %s\n", key_num);
- DEBUG(D_expand) debug_printf_indent("prvscheck daystamp: %s\n", daystamp);
- DEBUG(D_expand) debug_printf_indent("prvscheck hash: %s\n", hash);
- DEBUG(D_expand) debug_printf_indent("prvscheck domain: %s\n", domain);
+ DEBUG(D_expand)
+ {
+ debug_printf_indent("prvscheck localpart: %s\n", local_part);
+ debug_printf_indent("prvscheck key number: %s\n", key_num);
+ debug_printf_indent("prvscheck daystamp: %s\n", daystamp);
+ debug_printf_indent("prvscheck hash: %s\n", hash);
+ debug_printf_indent("prvscheck domain: %s\n", domain);
+ }
/* Set up expansion variables */
g = string_cat (NULL, local_part);
prvscheck_keynum = string_copy(key_num);
/* Now expand the second argument */
- switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok))
+ switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
p = prvs_hmac_sha1(prvscheck_address, sub_arg[0], prvscheck_keynum,
daystamp);
-
if (!p)
{
expand_string_message = US"hmac-sha1 conversion failed";
/* Now expand the final argument. We leave this till now so that
it can include $prvscheck_result. */
- switch(read_subs(sub_arg, 1, 0, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub_arg, 1, 0, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
We need to make sure all subs are expanded first, so as to skip over
the entire item. */
- switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
goto EXPAND_FAILED;
}
- switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
/* Read up to 4 arguments, but don't do the end of item check afterwards,
because there may be a string for expansion on failure. */
- switch(read_subs(sub_arg, 4, 2, &s, skipping, FALSE, name, &resetok))
+ switch(read_subs(sub_arg, 4, 2, &s, skipping, FALSE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2: /* Won't occur: no end check */
if (*s == '{') /*}*/
{
- if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok))
+ if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok, NULL))
goto EXPAND_FAILED; /*{*/
if (*s++ != '}')
{ /*{*/
SOCK_FAIL:
if (*s != '{') goto EXPAND_FAILED; /*}*/
DEBUG(D_any) debug_printf("%s\n", expand_string_message);
- if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok)))
+ if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok, NULL)))
goto EXPAND_FAILED;
yield = string_cat(yield, arg); /*{*/
if (*s++ != '}')
s++;
if (late_expand) /* this is the default case */
- {
+ { /*{*/
int n = Ustrcspn(s, "}");
arg = skipping ? NULL : string_copyn(s, n);
s += n;
}
else
{
- if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
+ if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL)))
goto EXPAND_FAILED;
Uskip_whitespace(&s);
}
int o2m;
uschar * sub[3];
- switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
sub[2] = NULL;
switch(read_subs(sub, (item_type == EITEM_LENGTH)? 2:3, 2, &s, skipping,
- TRUE, name, &resetok))
+ TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
uschar innerkey[MAX_HASHBLOCKLEN];
uschar outerkey[MAX_HASHBLOCKLEN];
- switch (read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
+ switch (read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
{
const pcre2_code * re;
int moffset, moffsetextra, slen;
- PCRE2_SIZE roffset;
pcre2_match_data * md;
- int err, emptyopt;
+ int emptyopt;
uschar * subject, * sub[3];
int save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
+ unsigned sub_textonly = 0;
- switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok, &sub_textonly))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
/* Compile the regular expression */
- if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
- PCRE_COPT, &err, &roffset, pcre_gen_cmp_ctx)))
- {
- uschar errbuf[128];
- pcre2_get_error_message(err, errbuf, sizeof(errbuf));
- expand_string_message = string_sprintf("regular expression error in "
- "\"%s\": %s at offset %ld", sub[1], errbuf, (long)roffset);
+ re = regex_compile(sub[1],
+ sub_textonly & BIT(1) ? MCS_CACHEABLE : MCS_NOFLAGS,
+ &expand_string_message, pcre_gen_cmp_ctx);
+ if (!re)
goto EXPAND_FAILED;
- }
+
md = pcre2_match_data_create(EXPAND_MAXN + 1, pcre_gen_ctx);
/* Now run a loop to do the substitutions as often as necessary. It ends
{
for (int j = 5; j > 0 && *s == '{'; j--) /*'}'*/
{
- if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
+ if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL))
goto EXPAND_FAILED; /*'{'*/
if (*s++ != '}')
{
{
if (Uskip_whitespace(&s) == '{') /*'}'*/
{
- if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
+ if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL)))
goto EXPAND_FAILED; /*'{'*/
if (*s++ != '}')
{
goto EXPAND_FAILED_CURLY;
}
- sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+ sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!sub[i]) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
case EITEM_LISTQUOTE:
{
uschar * sub[2];
- switch(read_subs(sub, 2, 2, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub, 2, 2, &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
expand_string_message = US"missing '{' for field arg of certextract";
goto EXPAND_FAILED_CURLY; /*}*/
}
- sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+ sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!sub[0]) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
"be a certificate variable";
goto EXPAND_FAILED;
}
- sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok);
+ sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok, NULL);
if (!sub[1]) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
goto EXPAND_FAILED_CURLY; /*}*/
}
- if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
+ if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL)))
goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
expand_string_message = US"missing '{' for second arg of reduce";
goto EXPAND_FAILED_CURLY; /*}*/
}
- t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+ t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!t) goto EXPAND_FAILED;
lookup_value = t; /*{{*/
if (*s++ != '}')
the normal internal expansion function. */
if (item_type != EITEM_FILTER)
- temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok);
+ temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok, NULL);
else
if ((temp = eval_condition(expr, &resetok, NULL))) s = temp;
else
{
- uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok);
+ uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok, NULL);
temp = t;
if (!temp)
{
goto EXPAND_FAILED_CURLY; /*}*/
}
- srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+ srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!srclist) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
goto EXPAND_FAILED_CURLY; /*}*/
}
- cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok);
+ cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok, NULL);
if (!cmp) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
}
xtract = s;
- if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok)))
+ if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok, NULL)))
goto EXPAND_FAILED;
xtract = string_copyn(xtract, s - xtract);
/*{{*/
/* extract field for comparisons */
iterate_item = srcitem;
if ( !(srcfield = expand_string_internal(xtract, FALSE, NULL, FALSE,
- TRUE, &resetok))
+ TRUE, &resetok, NULL))
|| !*srcfield)
{
expand_string_message = string_sprintf(
}
switch(read_subs(argv, EXPAND_DLFUNC_MAX_ARGS + 2, 2, &s, skipping,
- TRUE, name, &resetok))
+ TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
if (Uskip_whitespace(&s) != '{') /*}*/
goto EXPAND_FAILED;
- key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+ key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!key) goto EXPAND_FAILED; /*{{*/
if (*s++ != '}')
{
gstring * g = NULL;
BOOL quoted = FALSE;
- switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok))
+ switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok, NULL))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
{
const uschar * s1 = s;
sub = expand_string_internal(s+2, TRUE, &s1, skipping,
- FALSE, &resetok);
+ FALSE, &resetok, NULL);
if (!sub) goto EXPAND_FAILED; /*{*/
if (*s1 != '}')
{ /*{*/
/*FALLTHROUGH*/
#endif
default:
- sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+ sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
if (!sub) goto EXPAND_FAILED;
s++;
break;
case EOP_EXPAND:
{
- uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok);
+ uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok, NULL);
if (!expanded)
{
expand_string_message =
terminating brace. */
if (ket_ends && !*s)
- {
+ { /*{{*/
expand_string_message = malformed_header
? US"missing } at end of string - could be header name not terminated by colon"
: US"missing } at end of string";
"skipping: result is not used\n");
}
}
+if (textonly_p) *textonly_p = textonly;
expand_level--;
return yield->s;
}
+
/* This is the external function call. Do a quick check for any expansion
metacharacters, and if there are none, just return the input string.
-Argument: the string to be expanded
+Arguments
+ the string to be expanded
+ optional pointer for return boolean indicating no-dynamic-expansions
+
Returns: the expanded string, or NULL if expansion failed; if failure was
due to a lookup deferring, search_find_defer will be TRUE
*/
const uschar *
-expand_cstring(const uschar * string)
+expand_string_2(const uschar * string, BOOL * textonly_p)
{
if (Ustrpbrk(string, "$\\") != NULL)
{
f.search_find_defer = FALSE;
malformed_header = FALSE;
store_pool = POOL_MAIN;
- s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL);
+ s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL, textonly_p);
store_pool = old_pool;
return s;
}
+if (textonly_p) *textonly_p = TRUE;
return string;
}
+const uschar *
+expand_cstring(const uschar * string)
+{ return expand_string_2(string, NULL); }
uschar *
expand_string(uschar * string)
-{
-return US expand_cstring(CUS string);
-}
+{ return US expand_string_2(CUS string, NULL); }
+