*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
cases to introduce arguments, whereas for other it is part of the name. This is
an historical mis-design. */
cases to introduce arguments, whereas for other it is part of the name. This is
an historical mis-design. */
{ "spool_directory", vtype_stringptr, &spool_directory },
{ "spool_inodes", vtype_pinodes, (void *)TRUE },
{ "spool_space", vtype_pspace, (void *)TRUE },
{ "spool_directory", vtype_stringptr, &spool_directory },
{ "spool_inodes", vtype_pinodes, (void *)TRUE },
{ "spool_space", vtype_pspace, (void *)TRUE },
-#ifdef EXPERIMENTAL_SRS_ALT
- { "srs_db_address", vtype_stringptr, &srs_db_address },
- { "srs_db_key", vtype_stringptr, &srs_db_key },
- { "srs_orig_recipient", vtype_stringptr, &srs_orig_recipient },
- { "srs_orig_sender", vtype_stringptr, &srs_orig_sender },
-#endif
-#if defined(EXPERIMENTAL_SRS_ALT) || defined(SUPPORT_SRS)
{ "srs_recipient", vtype_stringptr, &srs_recipient },
{ "srs_recipient", vtype_stringptr, &srs_recipient },
#endif
{ "thisaddress", vtype_stringptr, &filter_thisaddress },
#endif
{ "thisaddress", vtype_stringptr, &filter_thisaddress },
-find_header(uschar *name, int *newsize, unsigned flags, uschar *charset)
+find_header(uschar *name, int *newsize, unsigned flags, const uschar *charset)
buf[0] = NOTIFY_QUEUE_SIZE_REQ;
if (send(fd, buf, 1, 0) < 0) { where = US"send"; goto bad; }
buf[0] = NOTIFY_QUEUE_SIZE_REQ;
if (send(fd, buf, 1, 0) < 0) { where = US"send"; goto bad; }
-FD_ZERO(&fds); FD_SET(fd, &fds);
-tv.tv_sec = 2; tv.tv_usec = 0;
-if (select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tv) != 1)
+if (poll_one_fd(fd, POLLIN, 2 * 1000) != 1)
{
DEBUG(D_expand) debug_printf("no daemon response; using local evaluation\n");
len = snprintf(CS buf, sizeof(buf), "%u", queue_count_cached());
{
DEBUG(D_expand) debug_printf("no daemon response; using local evaluation\n");
len = snprintf(CS buf, sizeof(buf), "%u", queue_count_cached());
find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize)
{
var_entry * vp;
find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize)
{
var_entry * vp;
}
else if (Ustrncmp(name, "regex", 5) == 0)
{
uschar *endptr;
int n = Ustrtoul(name + 5, &endptr, 10);
}
else if (Ustrncmp(name, "regex", 5) == 0)
{
uschar *endptr;
int n = Ustrtoul(name + 5, &endptr, 10);
uschar errbuf[128];
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
expand_string_message = string_sprintf("regular expression error in "
uschar errbuf[128];
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
expand_string_message = string_sprintf("regular expression error in "
- "\"%s\": %s at offset %d", sub[1], errbuf, offset);
+ "\"%s\": %s at offset %ld", sub[1], errbuf, (long)offset);
/* If a zero-length secret was given, we're done. Otherwise carry on
and validate the given SRS local_part againt our secret. */
/* If a zero-length secret was given, we're done. Otherwise carry on
and validate the given SRS local_part againt our secret. */
- case 'a': t = tree_search(addresslist_anchor, name); suffix = US"_a"; break;
- case 'd': t = tree_search(domainlist_anchor, name); suffix = US"_d"; break;
- case 'h': t = tree_search(hostlist_anchor, name); suffix = US"_h"; break;
- case 'l': t = tree_search(localpartlist_anchor, name); suffix = US"_l"; break;
+ case 'a': t = tree_search(addresslist_anchor, name); break;
+ case 'd': t = tree_search(domainlist_anchor, name); break;
+ case 'h': t = tree_search(hostlist_anchor, name); break;
+ case 'l': t = tree_search(localpartlist_anchor, name); break;
+/************************************************/
+static void
+debug_expansion_interim(const uschar * what, const uschar * value, int nchar,
+ BOOL skipping)
+{
+DEBUG(D_noutf8)
+ debug_printf_indent("|");
+else
+ debug_printf_indent(UTF8_VERT_RIGHT);
+
+for (int fill = 11 - Ustrlen(what); fill > 0; fill--)
+ DEBUG(D_noutf8)
+ debug_printf("-");
+ else
+ debug_printf(UTF8_HORIZ);
+
+debug_printf("%s: %.*s\n", what, nchar, value);
+if (is_tainted(value))
+ {
+ DEBUG(D_noutf8)
+ debug_printf_indent("%s \\__", skipping ? "| " : " ");
+ else
+ debug_printf_indent("%s",
+ skipping
+ ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ);
+ debug_printf("(tainted)\n");
+ }
+}
+
+
/*************************************************
* Expand string *
*************************************************/
/*************************************************
* Expand string *
*************************************************/
-DEBUG(D_expand)
- 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);
-
+ DEBUG(D_expand)
+ {
+ DEBUG(D_noutf8)
+ debug_printf_indent("%c%s: %s\n",
+ first ? '/' : '|',
+ skipping ? "---scanning" : "considering", s);
+ else
+ debug_printf_indent("%s%s: %s\n",
+ first ? UTF8_DOWN_RIGHT : UTF8_VERT_RIGHT,
+ skipping
+ ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning"
+ : "considering",
+ s);
+ first = FALSE;
+ }
+
/* \ escapes the next character, which must exist, or else
the expansion fails. There's a special escape, \N, which causes
copying of the subject verbatim up to the next \N. Otherwise,
/* \ escapes the next character, which must exist, or else
the expansion fails. There's a special escape, \N, which causes
copying of the subject verbatim up to the next \N. Otherwise,
- for (s = t; *s != 0; s++) if (*s == '\\' && s[1] == 'N') break;
+ for (s = t; *s ; s++) if (*s == '\\' && s[1] == 'N') break;
+
+ DEBUG(D_expand)
+ debug_expansion_interim(US"protected", t, (int)(s - t), skipping);
ch[0] = string_interpret_escape(&s);
s++;
yield = string_catn(yield, ch, 1);
}
ch[0] = string_interpret_escape(&s);
s++;
yield = string_catn(yield, ch, 1);
}
/* Anything other than $ is just copied verbatim, unless we are
looking for a terminating } character. */
/* Anything other than $ is just copied verbatim, unless we are
looking for a terminating } character. */
- yield = string_catn(yield, s++, 1);
+ int i = 1; /*{*/
+ for (const uschar * t = s+1;
+ *t && *t != '$' && *t != '}' && *t != '\\'; t++) i++;
+
+ DEBUG(D_expand) debug_expansion_interim(US"text", s, i, skipping);
+
+ yield = string_catn(yield, s, i);
+ s += i;
s = read_header_name(name, sizeof(name), s);
value = find_header(name, &newsize, flags, charset);
s = read_header_name(name, sizeof(name), s);
value = find_header(name, &newsize, flags, charset);
/* Allow "-" in names to cater for substrings with negative
arguments. Since we are checking for known names after { this is
/* Allow "-" in names to cater for substrings with negative
arguments. Since we are checking for known names after { this is
s = read_name(name, sizeof(name), s, US"_-");
item_type = chop_match(name, item_table, nelem(item_table));
s = read_name(name, sizeof(name), s, US"_-");
item_type = chop_match(name, item_table, nelem(item_table));
int rc;
switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, name,
int rc;
switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, name,
debug_printf_indent("acl expansion yield: %s\n", user_msg);
if (user_msg)
yield = string_cat(yield, user_msg);
debug_printf_indent("acl expansion yield: %s\n", user_msg);
if (user_msg)
yield = string_cat(yield, user_msg);
switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
&resetok))
switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
&resetok))
- 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");
- }
+ {
+ debug_expansion_interim(US"condition", s, (int)(next_s - s), skipping);
+ debug_expansion_interim(US"result",
+ cond ? US"true" : US"false", cond ? 4 : 5, skipping);
+ }
- if (!skipping)
- {
- if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset,
- sub_arg[1][0], sub_arg[2], &expand_string_message)))
- goto EXPAND_FAILED;
- yield = string_cat(yield, encoded);
- }
- continue;
+ if (skipping) continue;
+
+ if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset,
+ sub_arg[1][0], sub_arg[2], &expand_string_message)))
+ goto EXPAND_FAILED;
+ yield = string_cat(yield, encoded);
+ break;
int stype, partial, affixlen, starflags;
int expand_setup = 0;
int nameptr = 0;
int stype, partial, affixlen, starflags;
int expand_setup = 0;
int nameptr = 0;
- else
- {
- if (key)
- {
- expand_string_message = string_sprintf("a single key was given for "
- "lookup type \"%s\", which is not a single-key lookup type", name);
- goto EXPAND_FAILED;
- }
- }
+ else if (key)
+ {
+ expand_string_message = string_sprintf("a single key was given for "
+ "lookup type \"%s\", which is not a single-key lookup type", name);
+ goto EXPAND_FAILED;
+ }
/* Get the next string in brackets and expand it. It is the file name for
single-key+file lookups, and the whole query otherwise. In the case of
/* Get the next string in brackets and expand it. It is the file name for
single-key+file lookups, and the whole query otherwise. In the case of
}
if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
goto EXPAND_FAILED;
}
if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
goto EXPAND_FAILED;
- void *handle = search_open(filename, stype, 0, NULL, NULL);
+ void * handle = search_open(filename, stype, 0, NULL, NULL);
- #ifndef EXIM_PERL
- expand_string_message = US"\"${perl\" encountered, but this facility " /*}*/
- "is not included in this binary";
- goto EXPAND_FAILED;
+#ifndef EXIM_PERL
+ expand_string_message = US"\"${perl\" encountered, but this facility " /*}*/
+ "is not included in this binary";
+ goto EXPAND_FAILED;
switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok))
{
switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok))
{
yield = string_catn(yield, US"@", 1);
yield = string_cat (yield, domain);
yield = string_catn(yield, US"@", 1);
yield = string_cat (yield, domain);
/* TF: Ugliness: We want to expand parameter 1 first, then set
up expansion variables that are used in the expansion of
/* TF: Ugliness: We want to expand parameter 1 first, then set
up expansion variables that are used in the expansion of
PH: Actually, that isn't necessary. The read_subs() function is
designed to work this way for the ${if and ${lookup expansions. I've
tidied the code.
PH: Actually, that isn't necessary. The read_subs() function is
designed to work this way for the ${if and ${lookup expansions. I've
tidied the code.
if (regex_match_and_setup(re,sub_arg[0],0,-1))
{
if (regex_match_and_setup(re,sub_arg[0],0,-1))
{
- uschar *local_part = string_copyn(expand_nstring[4],expand_nlength[4]);
- uschar *key_num = string_copyn(expand_nstring[1],expand_nlength[1]);
- uschar *daystamp = string_copyn(expand_nstring[2],expand_nlength[2]);
- uschar *hash = string_copyn(expand_nstring[3],expand_nlength[3]);
- uschar *domain = string_copyn(expand_nstring[5],expand_nlength[5]);
+ uschar * local_part = string_copyn(expand_nstring[4],expand_nlength[4]);
+ uschar * key_num = string_copyn(expand_nstring[1],expand_nlength[1]);
+ uschar * daystamp = string_copyn(expand_nstring[2],expand_nlength[2]);
+ 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 localpart: %s\n", local_part);
DEBUG(D_expand) debug_printf_indent("prvscheck key number: %s\n", key_num);
/* The whole thing has worked (or we were skipping). If there is a
failure string following, we need to skip it. */
/* The whole thing has worked (or we were skipping). If there is a
failure string following, we need to skip it. */
{
if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok))
{
if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok))
/* Come here on failure to create socket, connect socket, write to the
socket, or timeout on reading. If another substring follows, expand and
use it. Otherwise, those conditions give expand errors. */
SOCK_FAIL:
/* Come here on failure to create socket, connect socket, write to the
socket, or timeout on reading. If another substring follows, expand and
use it. Otherwise, those conditions give expand errors. */
SOCK_FAIL:
DEBUG(D_any) debug_printf("%s\n", expand_string_message);
if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok)))
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)))
goto EXPAND_FAILED;
switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
{
switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
{
}
/* Handle "hash", "length", "nhash", and "substr" when they are given with
}
/* Handle "hash", "length", "nhash", and "substr" when they are given with
/* "length" takes only 2 arguments whereas the others take 2 or 3.
Ensure that sub[2] is set in the ${length } case. */
/* "length" takes only 2 arguments whereas the others take 2 or 3.
Ensure that sub[2] is set in the ${length } case. */
int type;
int hashlen; /* Number of octets for the hash algorithm's output */
int hashblocklen; /* Number of octets the hash algorithm processes */
int type;
int hashlen; /* Number of octets for the hash algorithm's output */
int hashblocklen; /* Number of octets the hash algorithm processes */
- if (Ustrcmp(sub[0], "md5") == 0)
- {
- type = HMAC_MD5;
- use_base = &md5_base;
- hashlen = 16;
- hashblocklen = 64;
- }
- else if (Ustrcmp(sub[0], "sha1") == 0)
- {
- type = HMAC_SHA1;
- use_base = &sha1_ctx;
- hashlen = 20;
- hashblocklen = 64;
- }
- else
- {
- expand_string_message =
- string_sprintf("hmac algorithm \"%s\" is not recognised", sub[0]);
- goto EXPAND_FAILED;
- }
+ type = HMAC_MD5;
+ use_base = &md5_base;
+ hashlen = 16;
+ hashblocklen = 64;
+ }
+ else if (Ustrcmp(sub[0], "sha1") == 0)
+ {
+ type = HMAC_SHA1;
+ use_base = &sha1_ctx;
+ hashlen = 20;
+ hashblocklen = 64;
+ }
+ else
+ {
+ expand_string_message =
+ string_sprintf("hmac algorithm \"%s\" is not recognised", sub[0]);
+ goto EXPAND_FAILED;
+ }
- if (keylen > hashblocklen)
- {
- chash_start(type, use_base);
- chash_end(type, use_base, keyptr, keylen, keyhash);
- keyptr = keyhash;
- keylen = hashlen;
- }
+ if (keylen > hashblocklen)
+ {
+ chash_start(type, use_base);
+ chash_end(type, use_base, keyptr, keylen, keyhash);
+ keyptr = keyhash;
+ keylen = hashlen;
+ }
- memset(innerkey, 0x36, hashblocklen);
- memset(outerkey, 0x5c, hashblocklen);
+ memset(innerkey, 0x36, hashblocklen);
+ memset(outerkey, 0x5c, hashblocklen);
- for (int i = 0; i < keylen; i++)
- {
- innerkey[i] ^= keyptr[i];
- outerkey[i] ^= keyptr[i];
- }
+ for (int i = 0; i < keylen; i++)
+ {
+ innerkey[i] ^= keyptr[i];
+ outerkey[i] ^= keyptr[i];
+ }
- chash_start(type, use_base);
- chash_mid(type, use_base, innerkey);
- chash_end(type, use_base, sub[2], Ustrlen(sub[2]), innerhash);
+ chash_start(type, use_base);
+ chash_mid(type, use_base, innerkey);
+ chash_end(type, use_base, sub[2], Ustrlen(sub[2]), innerhash);
- chash_start(type, use_base);
- chash_mid(type, use_base, outerkey);
- chash_end(type, use_base, innerhash, hashlen, finalhash);
+ chash_start(type, use_base);
+ chash_mid(type, use_base, outerkey);
+ chash_end(type, use_base, innerhash, hashlen, finalhash);
- p = finalhash_hex;
- for (int i = 0; i < hashlen; i++)
- {
- *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
- *p++ = hex_digits[finalhash[i] & 0x0f];
- }
+ p = finalhash_hex;
+ for (int i = 0; i < hashlen; i++)
+ {
+ *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
+ *p++ = hex_digits[finalhash[i] & 0x0f];
+ }
- DEBUG(D_any) debug_printf("HMAC[%s](%.*s,%s)=%.*s\n",
- sub[0], (int)keylen, keyptr, sub[2], hashlen*2, finalhash_hex);
+ DEBUG(D_any) debug_printf("HMAC[%s](%.*s,%s)=%.*s\n",
+ sub[0], (int)keylen, keyptr, sub[2], hashlen*2, finalhash_hex);
/* Compile the regular expression */
if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
/* Compile the regular expression */
if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
uschar errbuf[128];
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
expand_string_message = string_sprintf("regular expression error in "
uschar errbuf[128];
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
expand_string_message = string_sprintf("regular expression error in "
- "\"%s\": %s at offset %l", sub[1], errbuf, (long)roffset);
+ "\"%s\": %s at offset %ld", sub[1], errbuf, (long)roffset);
PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
int n = pcre2_match(re, (PCRE2_SPTR)subject, slen, moffset + moffsetextra,
PCRE_EOPT | emptyopt, md, pcre_mtc_ctx);
PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
int n = pcre2_match(re, (PCRE2_SPTR)subject, slen, moffset + moffsetextra,
PCRE_EOPT | emptyopt, md, pcre_mtc_ctx);
/* No match - if we previously set PCRE_NOTEMPTY after a null match, this
is not necessarily the end. We want to repeat the match from one
/* No match - if we previously set PCRE_NOTEMPTY after a null match, this
is not necessarily the end. We want to repeat the match from one
if (!(insert = expand_string(sub[2])))
goto EXPAND_FAILED;
yield = string_cat(yield, insert);
if (!(insert = expand_string(sub[2])))
goto EXPAND_FAILED;
yield = string_cat(yield, insert);
{
if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
goto EXPAND_FAILED; /*'{'*/
{
if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
goto EXPAND_FAILED; /*'{'*/
{
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)))
goto EXPAND_FAILED_CURLY;
}
sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
goto EXPAND_FAILED_CURLY;
}
sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
yield = string_catn(yield, sub[1], 1);
}
else yield = string_catn(yield, US" ", 1);
yield = string_catn(yield, sub[1], 1);
}
else yield = string_catn(yield, US" ", 1);
if (Uskip_whitespace(&s) != '{') /*}*/
{
expand_string_message = US"missing '{' for field arg of certextract";
if (Uskip_whitespace(&s) != '{') /*}*/
{
expand_string_message = US"missing '{' for field arg of certextract";
}
sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
}
sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (Uskip_whitespace(&s) != '{') /*}*/
{
expand_string_message = US"missing '{' for cert variable arg of certextract";
if (Uskip_whitespace(&s) != '{') /*}*/
{
expand_string_message = US"missing '{' for cert variable arg of certextract";
goto EXPAND_FAILED;
}
sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok);
goto EXPAND_FAILED;
}
sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok);
uschar outsep[2] = { '\0', '\0' };
const uschar *list, *expr, *temp;
uschar outsep[2] = { '\0', '\0' };
const uschar *list, *expr, *temp;
}
if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
}
if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
}
t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
if (!t) goto EXPAND_FAILED;
}
t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
if (!t) goto EXPAND_FAILED;
expand_string_message = string_sprintf("missing } at end of condition "
"or expression inside \"%s\"; could be an unquoted } in the content",
name);
goto EXPAND_FAILED;
}
expand_string_message = string_sprintf("missing } at end of condition "
"or expression inside \"%s\"; could be an unquoted } in the content",
name);
goto EXPAND_FAILED;
}
- int cond_type;
- int sep = 0;
- const uschar *srclist, *cmp, *xtract;
+ int sep = 0, cond_type;
+ const uschar * srclist, * cmp, * xtract;
- const uschar *dstlist = NULL, *dstkeylist = NULL;
- uschar * tmp;
- uschar *save_iterate_item = iterate_item;
+ const uschar * dstlist = NULL, * dstkeylist = NULL;
+ uschar * tmp, * save_iterate_item = iterate_item;
}
srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
}
srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
}
cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok);
}
cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok);
}
xtract = s;
if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok)))
goto EXPAND_FAILED;
xtract = string_copyn(xtract, s - xtract);
}
xtract = s;
if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok)))
goto EXPAND_FAILED;
xtract = string_copyn(xtract, s - xtract);
while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0)))
{
uschar * srcfield, * dstitem;
while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0)))
{
uschar * srcfield, * dstitem;
/* field for comparison */
if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
/* field for comparison */
if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0)))
{
if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0)))
{
if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
newlist = string_append_listele(newlist, sep, dstitem);
newkeylist = string_append_listele(newkeylist, sep, dstfield);
}
newlist = string_append_listele(newlist, sep, dstitem);
newkeylist = string_append_listele(newkeylist, sep, dstfield);
}
- for (argc = 0; argv[argc]; argc++);
- status = func(&result, argc - 2, &argv[2]);
- if(status == OK)
- {
- if (!result) result = US"";
- yield = string_cat(yield, result);
- continue;
- }
- else
+ for (argc = 0; argv[argc]; argc++) ;
+
+ if ((status = func(&result, argc - 2, &argv[2])) != OK)
argv[0], argv[1], status, expand_string_message);
goto EXPAND_FAILED;
}
argv[0], argv[1], status, expand_string_message);
goto EXPAND_FAILED;
}
goto EXPAND_FAILED;
key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
goto EXPAND_FAILED;
key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
case 1: goto EXPAND_FAILED; /* when all is well, the */
case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */
}
case 1: goto EXPAND_FAILED; /* when all is well, the */
case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */
}
/* @$original_domain */
yield = string_catn(yield, US"@", 1);
yield = string_cat(yield, sub[2]);
/* @$original_domain */
yield = string_catn(yield, US"@", 1);
yield = string_cat(yield, sub[2]);
+ /*NOTREACHED*/
+
+ DEBUG(D_expand)
+ if (yield && (start > 0 || *s)) /* only if not the sole expansion of the line */
+ debug_expansion_interim(US"item-res",
+ yield->s + start, yield->ptr - start, skipping);
+ continue;
+
+NOT_ITEM: ;
+ }
/* Control reaches here if the name is not recognized as one of the more
complicated expansion items. Check for the "operator" syntax (name terminated
/* Control reaches here if the name is not recognized as one of the more
complicated expansion items. Check for the "operator" syntax (name terminated
expand_string_message =
string_sprintf("missing '}' closing cert arg of %s", name);
goto EXPAND_FAILED_CURLY;
expand_string_message =
string_sprintf("missing '}' closing cert arg of %s", name);
goto EXPAND_FAILED_CURLY;
- if (*t != 0)
- {
- expand_string_message = string_sprintf("argument for base32 "
- "operator is \"%s\", which is not a decimal number", sub);
- goto EXPAND_FAILED;
- }
+ if (*t != 0)
+ {
+ expand_string_message = string_sprintf("argument for base32 "
+ "operator is \"%s\", which is not a decimal number", sub);
+ goto EXPAND_FAILED;
+ }
for ( ; n; n >>= 5)
g = string_catn(g, &base32_chars[n & 0x1f], 1);
if (g) while (g->ptr > 0) yield = string_catn(yield, &g->s[--g->ptr], 1);
for ( ; n; n >>= 5)
g = string_catn(g, &base32_chars[n & 0x1f], 1);
if (g) while (g->ptr > 0) yield = string_catn(yield, &g->s[--g->ptr], 1);
- {
- uschar *tt = sub;
- unsigned long int n = 0;
- while (*tt)
- {
- uschar * t = Ustrchr(base32_chars, *tt++);
- if (!t)
- {
- expand_string_message = string_sprintf("argument for base32d "
- "operator is \"%s\", which is not a base 32 number", sub);
- goto EXPAND_FAILED;
- }
- n = n * 32 + (t - base32_chars);
- }
- yield = string_fmt_append(yield, "%ld", n);
- continue;
- }
+ {
+ uschar *tt = sub;
+ unsigned long int n = 0;
+ while (*tt)
+ {
+ uschar * t = Ustrchr(base32_chars, *tt++);
+ if (!t)
+ {
+ expand_string_message = string_sprintf("argument for base32d "
+ "operator is \"%s\", which is not a base 32 number", sub);
+ goto EXPAND_FAILED;
+ }
+ n = n * 32 + (t - base32_chars);
+ }
+ yield = string_fmt_append(yield, "%ld", n);
+ break;
+ }
- {
- uschar *t;
- unsigned long int n = Ustrtoul(sub, &t, 10);
- if (*t != 0)
- {
- 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));
- continue;
- }
-
- /* Note that for Darwin and Cygwin, BASE_62 actually has the value 36 */
-
- case EOP_BASE62D:
- {
- uschar *tt = sub;
- unsigned long int n = 0;
- while (*tt != 0)
- {
- uschar *t = Ustrchr(base62_chars, *tt++);
- if (!t)
- {
- expand_string_message = string_sprintf("argument for base62d "
- "operator is \"%s\", which is not a base %d number", sub,
- BASE_62);
- goto EXPAND_FAILED;
- }
- n = n * BASE_62 + (t - base62_chars);
- }
- yield = string_fmt_append(yield, "%ld", n);
- continue;
- }
+ {
+ uschar *t;
+ unsigned long int n = Ustrtoul(sub, &t, 10);
+ if (*t != 0)
+ {
+ 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));
+ break;
+ }
+
+ /* Note that for Darwin and Cygwin, BASE_62 actually has the value 36 */
+
+ case EOP_BASE62D:
+ {
+ uschar *tt = sub;
+ unsigned long int n = 0;
+ while (*tt != 0)
+ {
+ uschar *t = Ustrchr(base62_chars, *tt++);
+ if (!t)
+ {
+ expand_string_message = string_sprintf("argument for base62d "
+ "operator is \"%s\", which is not a base %d number", sub,
+ BASE_62);
+ goto EXPAND_FAILED;
+ }
+ n = n * BASE_62 + (t - base62_chars);
+ }
+ yield = string_fmt_append(yield, "%ld", n);
+ break;
+ }
- {
- uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok);
- if (!expanded)
- {
- expand_string_message =
- string_sprintf("internal expansion of \"%s\" failed: %s", sub,
- expand_string_message);
- goto EXPAND_FAILED;
- }
- yield = string_cat(yield, expanded);
- continue;
- }
+ {
+ uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok);
+ if (!expanded)
+ {
+ expand_string_message =
+ string_sprintf("internal expansion of \"%s\" failed: %s", sub,
+ expand_string_message);
+ goto EXPAND_FAILED;
+ }
+ yield = string_cat(yield, expanded);
+ break;
+ }
- {
- int count = 0;
- uschar *t = sub - 1;
- while (*(++t) != 0) { *t = tolower(*t); count++; }
- yield = string_catn(yield, sub, count);
- continue;
- }
+ {
+ int count = 0;
+ uschar *t = sub - 1;
+ while (*(++t) != 0) { *t = tolower(*t); count++; }
+ yield = string_catn(yield, sub, count);
+ break;
+ }
- {
- int count = 0;
- uschar *t = sub - 1;
- while (*(++t) != 0) { *t = toupper(*t); count++; }
- yield = string_catn(yield, sub, count);
- continue;
- }
+ {
+ int count = 0;
+ uschar *t = sub - 1;
+ while (*(++t) != 0) { *t = toupper(*t); count++; }
+ yield = string_catn(yield, sub, count);
+ break;
+ }
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_md5(*(void **)vp->value);
yield = string_cat(yield, cp);
}
else
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_md5(*(void **)vp->value);
yield = string_cat(yield, cp);
}
else
for (int j = 0; j < 16; j++)
yield = string_fmt_append(yield, "%02x", digest[j]);
}
for (int j = 0; j < 16; j++)
yield = string_fmt_append(yield, "%02x", digest[j]);
}
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value);
yield = string_cat(yield, cp);
}
else
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value);
yield = string_cat(yield, cp);
}
else
for (int j = 0; j < 20; j++)
yield = string_fmt_append(yield, "%02X", digest[j]);
}
for (int j = 0; j < 20; j++)
yield = string_fmt_append(yield, "%02X", digest[j]);
}
if (vp && *(void **)vp->value)
if (c == EOP_SHA256)
yield = string_cat(yield, tls_cert_fprt_sha256(*(void **)vp->value));
if (vp && *(void **)vp->value)
if (c == EOP_SHA256)
yield = string_cat(yield, tls_cert_fprt_sha256(*(void **)vp->value));
- if ((c & 1) != 0)
- {
- expand_string_message = string_sprintf("\"%s\" contains an odd "
- "number of characters", sub);
- goto EXPAND_FAILED;
- }
+ if ((c & 1) != 0)
+ {
+ expand_string_message = string_sprintf("\"%s\" contains an odd "
+ "number of characters", sub);
+ goto EXPAND_FAILED;
+ }
- while ((c = *in++) != 0)
- {
- if (isdigit(c)) c -= '0';
- else c = toupper(c) - 'A' + 10;
- if (b == -1)
- b = c << 4;
- else
- {
- *out++ = b | c;
- b = -1;
- }
- }
+ while ((c = *in++) != 0)
+ {
+ if (isdigit(c)) c -= '0';
+ else c = toupper(c) - 'A' + 10;
+ if (b == -1)
+ b = c << 4;
+ else
+ {
+ *out++ = b | c;
+ b = -1;
+ }
+ }
- enc = b64encode(CUS sub, out - sub);
- yield = string_cat(yield, enc);
- continue;
- }
+ enc = b64encode(CUS sub, out - sub);
+ yield = string_cat(yield, enc);
+ break;
+ }
- uschar *t = sub - 1;
- while (*(++t) != 0)
- {
- if (*t < 0x21 || 0x7E < *t)
- yield = string_fmt_append(yield, "\\x%02x", *t);
+ uschar *t = sub - 1;
+ while (*(++t) != 0)
+ {
+ if (*t < 0x21 || 0x7E < *t)
+ yield = string_fmt_append(yield, "\\x%02x", *t);
int cnt = 0, sep = 0;
uschar * buf = store_get(2, is_tainted(sub));
while (string_nextinlist(CUSS &sub, &sep, buf, 1)) cnt++;
yield = string_fmt_append(yield, "%d", cnt);
int cnt = 0, sep = 0;
uschar * buf = store_get(2, is_tainted(sub));
while (string_nextinlist(CUSS &sub, &sep, buf, 1)) cnt++;
yield = string_fmt_append(yield, "%d", cnt);
/* expand a named list given the name */
/* handles nested named lists; requotes as colon-sep list */
/* expand a named list given the name */
/* handles nested named lists; requotes as colon-sep list */
yield = expand_listnamed(yield, sub, arg);
if (expand_string_message)
goto EXPAND_FAILED;
yield = expand_listnamed(yield, sub, arg);
if (expand_string_message)
goto EXPAND_FAILED;
- if ((type = string_is_ip_address(sub, &maskoffset)) == 0)
- {
- expand_string_message = string_sprintf("\"%s\" is not an IP address",
- sub);
- goto EXPAND_FAILED;
- }
+ if ((type = string_is_ip_address(sub, &maskoffset)) == 0)
+ {
+ expand_string_message = string_sprintf("\"%s\" is not an IP address",
+ sub);
+ goto EXPAND_FAILED;
+ }
- if (*endptr || mask < 0 || mask > (type == 4 ? 32 : 128))
- {
- expand_string_message = string_sprintf("mask value too big in \"%s\"",
- sub);
- goto EXPAND_FAILED;
- }
+ if (*endptr || mask < 0 || mask > (type == 4 ? 32 : 128))
+ {
+ expand_string_message = string_sprintf("mask value too big in \"%s\"",
+ sub);
+ goto EXPAND_FAILED;
+ }
/* If an optional 'n' was given, ipv6 gets normalised output:
colons rather than dots, and zero-compressed. */
normalised = arg && *arg == 'n';
/* If an optional 'n' was given, ipv6 gets normalised output:
colons rather than dots, and zero-compressed. */
normalised = arg && *arg == 'n';
- sub[maskoffset] = 0;
- count = host_aton(sub, binary);
- host_mask(count, binary, mask);
+ sub[maskoffset] = 0;
+ count = host_aton(sub, binary);
+ host_mask(count, binary, mask);
if (type == 4 || !normalised)
yield = string_catn(yield, buffer,
if (type == 4 || !normalised)
yield = string_catn(yield, buffer,
ipv6_nmtoa(binary, buffer);
yield = string_fmt_append(yield, "%s/%d", buffer, mask);
}
ipv6_nmtoa(binary, buffer);
yield = string_fmt_append(yield, "%s/%d", buffer, mask);
}
? ipv6_nmtoa(binary, buffer)
: host_nmtoa(4, binary, -1, buffer, ':')
);
? ipv6_nmtoa(binary, buffer)
: host_nmtoa(4, binary, -1, buffer, ':')
);
- {
- uschar * error;
- int start, end, domain;
- uschar * t = parse_extract_address(sub, &error, &start, &end, &domain,
- FALSE);
- if (t)
+ {
+ uschar * error;
+ int start, end, domain;
+ uschar * t = parse_extract_address(sub, &error, &start, &end, &domain,
+ FALSE);
+ if (t)
if (c != EOP_DOMAIN)
yield = c == EOP_LOCAL_PART && domain > 0
? string_catn(yield, t, domain - 1)
: string_cat(yield, t);
else if (domain > 0)
yield = string_cat(yield, t + domain);
if (c != EOP_DOMAIN)
yield = c == EOP_LOCAL_PART && domain > 0
? string_catn(yield, t, domain - 1)
: string_cat(yield, t);
else if (domain > 0)
yield = string_cat(yield, t + domain);
- {
- uschar outsep[2] = { ':', '\0' };
- uschar *address, *error;
- int save_ptr = gstring_length(yield);
- int start, end, domain; /* Not really used */
+ {
+ uschar outsep[2] = { ':', '\0' };
+ uschar *address, *error;
+ int save_ptr = gstring_length(yield);
+ int start, end, domain; /* Not really used */
- expand_string_message = string_sprintf("output separator "
- "missing in expanding ${addresses:%s}", --sub);
- goto EXPAND_FAILED;
- }
- f.parse_allow_group = TRUE;
+ expand_string_message = string_sprintf("output separator "
+ "missing in expanding ${addresses:%s}", --sub);
+ goto EXPAND_FAILED;
+ }
+ f.parse_allow_group = TRUE;
- for (;;)
- {
- uschar * p = parse_find_address_end(sub, FALSE);
- uschar saveend = *p;
- *p = '\0';
- address = parse_extract_address(sub, &error, &start, &end, &domain,
- FALSE);
- *p = saveend;
-
- /* Add the address to the output list that we are building. This is
- done in chunks by searching for the separator character. At the
- start, unless we are dealing with the first address of the output
- list, add in a space if the new address begins with the separator
- character, or is an empty string. */
-
- if (address)
- {
- if (yield && yield->ptr != save_ptr && address[0] == *outsep)
- yield = string_catn(yield, US" ", 1);
+ for (;;)
+ {
+ uschar * p = parse_find_address_end(sub, FALSE);
+ uschar saveend = *p;
+ *p = '\0';
+ address = parse_extract_address(sub, &error, &start, &end, &domain,
+ FALSE);
+ *p = saveend;
+
+ /* Add the address to the output list that we are building. This is
+ done in chunks by searching for the separator character. At the
+ start, unless we are dealing with the first address of the output
+ list, add in a space if the new address begins with the separator
+ character, or is an empty string. */
+
+ if (address)
+ {
+ if (yield && yield->ptr != save_ptr && address[0] == *outsep)
+ yield = string_catn(yield, US" ", 1);
- for (;;)
- {
- size_t seglen = Ustrcspn(address, outsep);
- yield = string_catn(yield, address, seglen + 1);
+ for (;;)
+ {
+ size_t seglen = Ustrcspn(address, outsep);
+ yield = string_catn(yield, address, seglen + 1);
- if (address[seglen] == '\0') { yield->ptr--; break; }
- yield = string_catn(yield, outsep, 1);
- address += seglen + 1;
- }
+ if (address[seglen] == '\0') { yield->ptr--; break; }
+ yield = string_catn(yield, outsep, 1);
+ address += seglen + 1;
+ }
- if (c == EOP_QUOTE)
- while (!needs_quote && *++t)
- needs_quote = !isalnum(*t) && !strchr("_-.", *t);
+ if (c == EOP_QUOTE)
+ while (!needs_quote && *++t)
+ needs_quote = !isalnum(*t) && !strchr("_-.", *t);
- else /* EOP_QUOTE_LOCAL_PART */
- while (!needs_quote && *++t)
- needs_quote = !isalnum(*t)
- && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL
- && (*t != '.' || t == sub || !t[1]);
+ else /* EOP_QUOTE_LOCAL_PART */
+ while (!needs_quote && *++t)
+ needs_quote = !isalnum(*t)
+ && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL
+ && (*t != '.' || t == sub || !t[1]);
- if (needs_quote)
- {
- yield = string_catn(yield, US"\"", 1);
- t = sub - 1;
- while (*++t)
- if (*t == '\n')
- yield = string_catn(yield, US"\\n", 2);
- else if (*t == '\r')
- yield = string_catn(yield, US"\\r", 2);
- else
- {
- if (*t == '\\' || *t == '"')
- yield = string_catn(yield, US"\\", 1);
- yield = string_catn(yield, t, 1);
- }
- yield = string_catn(yield, US"\"", 1);
- }
- else
- yield = string_cat(yield, sub);
- continue;
- }
+ if (needs_quote)
+ {
+ yield = string_catn(yield, US"\"", 1);
+ t = sub - 1;
+ while (*++t)
+ if (*t == '\n')
+ yield = string_catn(yield, US"\\n", 2);
+ else if (*t == '\r')
+ yield = string_catn(yield, US"\\r", 2);
+ else
+ {
+ if (*t == '\\' || *t == '"')
+ yield = string_catn(yield, US"\\", 1);
+ yield = string_catn(yield, t, 1);
+ }
+ yield = string_catn(yield, US"\"", 1);
+ }
+ else
+ yield = string_cat(yield, sub);
+ break;
+ }
- if ((n = search_findtype(arg, Ustrlen(arg))) < 0)
- {
- expand_string_message = search_error_message;
- goto EXPAND_FAILED;
- }
+ if ((n = search_findtype(arg, Ustrlen(arg))) < 0)
+ {
+ expand_string_message = search_error_message;
+ goto EXPAND_FAILED;
+ }
if (lookup_list[n]->quote)
sub = (lookup_list[n]->quote)(sub, opt);
else if (opt)
sub = NULL;
if (lookup_list[n]->quote)
sub = (lookup_list[n]->quote)(sub, opt);
else if (opt)
sub = NULL;
- if (!sub)
- {
- expand_string_message = string_sprintf(
- "\"%s\" unrecognized after \"${quote_%s\"",
- opt, arg);
- goto EXPAND_FAILED;
- }
-
- yield = string_cat(yield, sub);
- continue;
- }
-
- /* rx quote sticks in \ before any non-alphameric character so that
- the insertion works in a regular expression. */
-
- case EOP_RXQUOTE:
- {
- uschar *t = sub - 1;
- while (*(++t) != 0)
- {
- if (!isalnum(*t))
- yield = string_catn(yield, US"\\", 1);
- yield = string_catn(yield, t, 1);
- }
- continue;
- }
-
- /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as
- prescribed by the RFC, if there are characters that need to be encoded */
+ if (!sub)
+ {
+ expand_string_message = string_sprintf(
+ "\"%s\" unrecognized after \"${quote_%s\"", /*}*/
+ opt, arg);
+ goto EXPAND_FAILED;
+ }
- case EOP_RFC2047:
- yield = string_cat(yield,
- parse_quote_2047(sub, Ustrlen(sub), headers_charset,
- FALSE));
- continue;
+ yield = string_cat(yield, sub);
+ break;
+ }
- case EOP_RFC2047D:
- {
- int len;
- uschar *error;
- uschar *decoded = rfc2047_decode(sub, check_rfc2047_length,
- headers_charset, '?', &len, &error);
- if (error)
- {
- expand_string_message = error;
- goto EXPAND_FAILED;
- }
- yield = string_catn(yield, decoded, len);
- continue;
- }
+ case EOP_RXQUOTE:
+ {
+ uschar *t = sub - 1;
+ while (*(++t) != 0)
+ {
+ if (!isalnum(*t))
+ yield = string_catn(yield, US"\\", 1);
+ yield = string_catn(yield, t, 1);
+ }
+ break;
+ }
- /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into
- underscores */
+ /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as
+ prescribed by the RFC, if there are characters that need to be encoded */
- case EOP_FROM_UTF8:
- {
- uschar * buff = store_get(4, is_tainted(sub));
- while (*sub)
- {
- int c;
- GETUTF8INC(c, sub);
- if (c > 255) c = '_';
- buff[0] = c;
- yield = string_catn(yield, buff, 1);
- }
- continue;
- }
+ case EOP_RFC2047:
+ yield = string_cat(yield,
+ parse_quote_2047(sub, Ustrlen(sub), headers_charset,
+ FALSE));
+ break;
- #define UTF8_REPLACEMENT_CHAR US"?"
+ case EOP_RFC2047D:
+ {
+ int len;
+ uschar *error;
+ uschar *decoded = rfc2047_decode(sub, check_rfc2047_length,
+ headers_charset, '?', &len, &error);
+ if (error)
+ {
+ expand_string_message = error;
+ goto EXPAND_FAILED;
+ }
+ yield = string_catn(yield, decoded, len);
+ break;
+ }
- /* Manually track tainting, as we deal in individual chars below */
+ case EOP_FROM_UTF8:
+ {
+ uschar * buff = store_get(4, is_tainted(sub));
+ while (*sub)
+ {
+ int c;
+ GETUTF8INC(c, sub);
+ if (c > 255) c = '_';
+ buff[0] = c;
+ yield = string_catn(yield, buff, 1);
+ }
+ break;
+ }
- if (is_tainted(sub))
- {
- if (yield->s && yield->ptr)
- gstring_rebuffer(yield);
- else
- yield->s = store_get(yield->size = Ustrlen(sub), TRUE);
- }
+ /* replace illegal UTF-8 sequences by replacement character */
- if ((c & 0xc0) != 0x80)
- /* wrong continuation byte; invalidate all bytes */
- complete = 1; /* error */
+ if (yield->s && yield->ptr)
+ gstring_rebuffer(yield);
- {
- codepoint = (codepoint << 6) | (c & 0x3f);
- seq_buff[index++] = c;
- if (--bytes_left == 0) /* codepoint complete */
- if(codepoint > 0x10FFFF) /* is it too large? */
- complete = -1; /* error (RFC3629 limit) */
- else
- { /* finished; output utf-8 sequence */
- yield = string_catn(yield, seq_buff, seq_len);
- index = 0;
- }
- }
+ yield->s = store_get(yield->size = Ustrlen(sub), is_tainted(sub));
- if(!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */
- {
- yield = string_catn(yield, &c, 1);
- continue;
- }
- if((c & 0xe0) == 0xc0) /* 2-byte sequence */
+ complete = 0;
+ uschar c = *sub++;
+
+ if (bytes_left)
- bytes_left = 1;
- codepoint = c & 0x1f;
+ codepoint = (codepoint << 6) | (c & 0x3f);
+ seq_buff[index++] = c;
+ if (--bytes_left == 0) /* codepoint complete */
+ if(codepoint > 0x10FFFF) /* is it too large? */
+ complete = -1; /* error (RFC3629 limit) */
+ else
+ { /* finished; output utf-8 sequence */
+ yield = string_catn(yield, seq_buff, seq_len);
+ index = 0;
+ }
- bytes_left = 2;
- codepoint = c & 0x0f;
- }
- else if((c & 0xf8) == 0xf0) /* 4-byte sequence */
+ 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 */
+ complete = -1;
+ else
+ {
+ bytes_left = 1;
+ codepoint = c & 0x1f;
+ }
+ }
+ else if((c & 0xf0) == 0xe0) /* 3-byte sequence */
+ {
+ bytes_left = 2;
+ codepoint = c & 0x0f;
+ }
+ else if((c & 0xf8) == 0xf0) /* 4-byte sequence */
+ {
+ bytes_left = 3;
+ codepoint = c & 0x07;
+ }
+ else /* invalid or too long (RFC3629 allows only 4 bytes) */
+ complete = -1;
+
+ seq_buff[index++] = c;
+ seq_len = bytes_left + 1;
+ } /* if(bytes_left) */
+
+ if (complete != 0)
- else /* invalid or too long (RFC3629 allows only 4 bytes) */
- complete = -1;
+ if ((complete == 1) && ((c & 0x80) == 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);
- bytes_left = index = 0;
- yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1);
+ expand_string_message = string_sprintf(
+ "error converting utf8 (%s) to alabel: %s",
+ string_printing(sub), error);
+ goto EXPAND_FAILED;
- if ((complete == 1) && ((c & 0x80) == 0))
- /* ASCII character follows incomplete sequence */
- yield = string_catn(yield, &c, 1);
+ yield = string_cat(yield, s);
+ break;
- /* 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);
- expand_string_message = string_sprintf(
- "error converting utf8 (%s) to alabel: %s",
- string_printing(sub), error);
- goto EXPAND_FAILED;
+ uschar * error = NULL;
+ uschar * s = string_domain_alabel_to_utf8(sub, &error);
+ if (error)
+ {
+ expand_string_message = string_sprintf(
+ "error converting alabel (%s) to utf8: %s",
+ string_printing(sub), error);
+ goto EXPAND_FAILED;
+ }
+ yield = string_cat(yield, s);
+ break;
- expand_string_message = string_sprintf(
- "error converting alabel (%s) to utf8: %s",
- string_printing(sub), error);
- goto EXPAND_FAILED;
+ uschar * error = NULL;
+ uschar * s = string_localpart_utf8_to_alabel(sub, &error);
+ if (error)
+ {
+ expand_string_message = string_sprintf(
+ "error converting utf8 (%s) to alabel: %s",
+ string_printing(sub), error);
+ goto EXPAND_FAILED;
+ }
+ yield = string_cat(yield, s);
+ DEBUG(D_expand) debug_printf_indent("yield: '%s'\n", yield->s);
+ break;
- expand_string_message = string_sprintf(
- "error converting utf8 (%s) to alabel: %s",
- string_printing(sub), error);
- goto EXPAND_FAILED;
+ uschar * error = NULL;
+ uschar * s = string_localpart_alabel_to_utf8(sub, &error);
+ if (error)
+ {
+ expand_string_message = string_sprintf(
+ "error converting alabel (%s) to utf8: %s",
+ string_printing(sub), error);
+ goto EXPAND_FAILED;
+ }
+ yield = string_cat(yield, s);
+ break;
- expand_string_message = string_sprintf(
- "error converting alabel (%s) to utf8: %s",
- string_printing(sub), error);
- goto EXPAND_FAILED;
+ const uschar * t = string_printing(sub);
+ yield = string_cat(yield, t);
+ break;
- case EOP_ESCAPE8BIT:
- {
- uschar c;
+ for (const uschar * s = sub; (c = *s); s++)
+ yield = c < 127 && c != '\\'
+ ? string_catn(yield, s, 1)
+ : string_fmt_append(yield, "\\%03o", c);
+ break;
+ }
- for (const uschar * s = sub; (c = *s); s++)
- yield = c < 127 && c != '\\'
- ? string_catn(yield, s, 1)
- : string_fmt_append(yield, "\\%03o", c);
- continue;
- }
+ /* Handle numeric expression evaluation */
- /* Handle numeric expression evaluation */
+ case EOP_EVAL:
+ case EOP_EVAL10:
+ {
+ uschar *save_sub = sub;
+ uschar *error = NULL;
+ int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE);
+ 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;
+ }
+ yield = string_fmt_append(yield, PR_EXIM_ARITH, n);
+ break;
+ }
- case EOP_EVAL:
- case EOP_EVAL10:
- {
- uschar *save_sub = sub;
- uschar *error = NULL;
- int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE);
- 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;
- }
- yield = string_fmt_append(yield, PR_EXIM_ARITH, n);
- continue;
- }
+ /* Handle time period formatting */
- /* Handle time period formatting */
+ case EOP_TIME_EVAL:
+ {
+ int n = readconf_readtime(sub, 0, FALSE);
+ if (n < 0)
+ {
+ expand_string_message = string_sprintf("string \"%s\" is not an "
+ "Exim time interval in \"%s\" operator", sub, name);
+ goto EXPAND_FAILED;
+ }
+ yield = string_fmt_append(yield, "%d", n);
+ break;
+ }
- case EOP_TIME_EVAL:
- {
- int n = readconf_readtime(sub, 0, FALSE);
- if (n < 0)
- {
- expand_string_message = string_sprintf("string \"%s\" is not an "
- "Exim time interval in \"%s\" operator", sub, name);
- goto EXPAND_FAILED;
- }
- yield = string_fmt_append(yield, "%d", n);
- continue;
- }
+ case EOP_TIME_INTERVAL:
+ {
+ int n;
+ uschar *t = read_number(&n, sub);
+ if (*t != 0) /* Not A Number*/
+ {
+ expand_string_message = string_sprintf("string \"%s\" is not a "
+ "positive number in \"%s\" operator", sub, name);
+ goto EXPAND_FAILED;
+ }
+ t = readconf_printtime(n);
+ yield = string_cat(yield, t);
+ break;
+ }
- case EOP_TIME_INTERVAL:
- {
- int n;
- uschar *t = read_number(&n, sub);
- if (*t != 0) /* Not A Number*/
- {
- expand_string_message = string_sprintf("string \"%s\" is not a "
- "positive number in \"%s\" operator", sub, name);
- goto EXPAND_FAILED;
- }
- t = readconf_printtime(n);
- yield = string_cat(yield, t);
- continue;
- }
+ /* Convert string to base64 encoding */
- /* Convert string to base64 encoding */
+ case EOP_STR2B64:
+ case EOP_BASE64:
+ {
+ #ifndef DISABLE_TLS
+ uschar * s = vp && *(void **)vp->value
+ ? tls_cert_der_b64(*(void **)vp->value)
+ : b64encode(CUS sub, Ustrlen(sub));
+ #else
+ uschar * s = b64encode(CUS sub, Ustrlen(sub));
+ #endif
+ yield = string_cat(yield, s);
+ break;
+ }
- case EOP_STR2B64:
- case EOP_BASE64:
- {
-#ifndef DISABLE_TLS
- uschar * s = vp && *(void **)vp->value
- ? tls_cert_der_b64(*(void **)vp->value)
- : b64encode(CUS sub, Ustrlen(sub));
-#else
- uschar * s = b64encode(CUS sub, Ustrlen(sub));
-#endif
- yield = string_cat(yield, s);
- continue;
- }
+ case EOP_BASE64D:
+ {
+ uschar * s;
+ int len = b64decode(sub, &s);
+ if (len < 0)
+ {
+ expand_string_message = string_sprintf("string \"%s\" is not "
+ "well-formed for \"%s\" operator", sub, name);
+ goto EXPAND_FAILED;
+ }
+ yield = string_cat(yield, s);
+ break;
+ }
- case EOP_BASE64D:
- {
- uschar * s;
- int len = b64decode(sub, &s);
- if (len < 0)
- {
- expand_string_message = string_sprintf("string \"%s\" is not "
- "well-formed for \"%s\" operator", sub, name);
- goto EXPAND_FAILED;
- }
- yield = string_cat(yield, s);
- continue;
- }
+ /* strlen returns the length of the string */
- case EOP_STRLEN:
- 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;
+
+ substr_m_n, and s_m_n take n characters from offset m; negative m take
+ from the end; l_n is synonymous with s_0_n. If n is omitted in substr it
+ takes the rest, either to the right or to the left.
+
+ hash_n or h_n makes a hash of length n from the string, yielding n
+ characters from the set a-z; hash_n_m makes a hash of length n, but
+ uses m characters from the set a-zA-Z0-9.
+
+ nhash_n returns a single number between 0 and n-1 (in text form), while
+ nhash_n_m returns a div/mod hash as two numbers "a/b". The first lies
+ between 0 and n-1 and the second between 0 and m-1. */
+
+ case EOP_LENGTH:
+ case EOP_L:
+ case EOP_SUBSTR:
+ case EOP_S:
+ case EOP_HASH:
+ case EOP_H:
+ case EOP_NHASH:
+ case EOP_NH:
+ {
+ int sign = 1;
+ int value1 = 0;
+ int value2 = -1;
+ int *pn;
+ int len;
+ uschar *ret;
- /* length_n or l_n takes just the first n characters or the whole string,
- whichever is the shorter;
-
- substr_m_n, and s_m_n take n characters from offset m; negative m take
- from the end; l_n is synonymous with s_0_n. If n is omitted in substr it
- takes the rest, either to the right or to the left.
-
- hash_n or h_n makes a hash of length n from the string, yielding n
- characters from the set a-z; hash_n_m makes a hash of length n, but
- uses m characters from the set a-zA-Z0-9.
-
- nhash_n returns a single number between 0 and n-1 (in text form), while
- nhash_n_m returns a div/mod hash as two numbers "a/b". The first lies
- between 0 and n-1 and the second between 0 and m-1. */
-
- case EOP_LENGTH:
- case EOP_L:
- case EOP_SUBSTR:
- case EOP_S:
- case EOP_HASH:
- case EOP_H:
- case EOP_NHASH:
- case EOP_NH:
- {
- int sign = 1;
- int value1 = 0;
- int value2 = -1;
- int *pn;
- int len;
- uschar *ret;
-
- if (!arg)
- {
- expand_string_message = string_sprintf("missing values after %s",
- name);
- goto EXPAND_FAILED;
- }
+ if (!arg)
+ {
+ expand_string_message = string_sprintf("missing values after %s",
+ name);
+ goto EXPAND_FAILED;
+ }
- /* The others have one or two arguments; for "substr" the first may be
- negative. The second being negative means "not supplied". */
+ /* The others have one or two arguments; for "substr" the first may be
+ negative. The second being negative means "not supplied". */
- ret = arg;
- while (*arg != 0)
- {
- if (arg != ret && *arg == '_' && pn == &value1)
- {
- pn = &value2;
- value2 = 0;
- if (arg[1] != 0) arg++;
- }
- else if (!isdigit(*arg))
- {
- expand_string_message =
- string_sprintf("non-digit after underscore in \"%s\"", name);
- goto EXPAND_FAILED;
- }
- else *pn = (*pn)*10 + *arg++ - '0';
- }
- value1 *= sign;
+ ret = arg;
+ while (*arg != 0)
+ {
+ if (arg != ret && *arg == '_' && pn == &value1)
+ {
+ pn = &value2;
+ value2 = 0;
+ if (arg[1] != 0) arg++;
+ }
+ else if (!isdigit(*arg))
+ {
+ expand_string_message =
+ string_sprintf("non-digit after underscore in \"%s\"", name);
+ goto EXPAND_FAILED;
+ }
+ else *pn = (*pn)*10 + *arg++ - '0';
+ }
+ value1 *= sign;
- ret = c == EOP_HASH || c == EOP_H
- ? compute_hash(sub, value1, value2, &len)
- : c == EOP_NHASH || c == EOP_NH
- ? compute_nhash(sub, value1, value2, &len)
- : extract_substr(sub, value1, value2, &len);
- if (!ret) goto EXPAND_FAILED;
+ ret = c == EOP_HASH || c == EOP_H
+ ? compute_hash(sub, value1, value2, &len)
+ : c == EOP_NHASH || c == EOP_NH
+ ? compute_nhash(sub, value1, value2, &len)
+ : extract_substr(sub, value1, value2, &len);
+ if (!ret) goto EXPAND_FAILED;
- if (expand_forbid & RDO_EXISTS)
- {
- expand_string_message = US"Use of the stat() expansion is not permitted";
- goto EXPAND_FAILED;
- }
+ if (expand_forbid & RDO_EXISTS)
+ {
+ expand_string_message = US"Use of the stat() expansion is not permitted";
+ goto EXPAND_FAILED;
+ }
- if (stat(CS sub, &st) < 0)
- {
- expand_string_message = string_sprintf("stat(%s) failed: %s",
- sub, strerror(errno));
- goto EXPAND_FAILED;
- }
- mode = st.st_mode;
- switch (mode & S_IFMT)
- {
- case S_IFIFO: smode[0] = 'p'; break;
- case S_IFCHR: smode[0] = 'c'; break;
- case S_IFDIR: smode[0] = 'd'; break;
- case S_IFBLK: smode[0] = 'b'; break;
- case S_IFREG: smode[0] = '-'; break;
- default: smode[0] = '?'; break;
- }
+ if (stat(CS sub, &st) < 0)
+ {
+ expand_string_message = string_sprintf("stat(%s) failed: %s",
+ sub, strerror(errno));
+ goto EXPAND_FAILED;
+ }
+ mode = st.st_mode;
+ switch (mode & S_IFMT)
+ {
+ case S_IFIFO: smode[0] = 'p'; break;
+ case S_IFCHR: smode[0] = 'c'; break;
+ case S_IFDIR: smode[0] = 'd'; break;
+ case S_IFBLK: smode[0] = 'b'; break;
+ case S_IFREG: smode[0] = '-'; break;
+ default: smode[0] = '?'; break;
+ }
- modetable[0] = ((mode & 01000) == 0)? mtable_normal : mtable_sticky;
- modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid;
- modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid;
+ modetable[0] = ((mode & 01000) == 0)? mtable_normal : mtable_sticky;
+ modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid;
+ modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid;
- for (int i = 0; i < 3; i++)
- {
- memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3);
- mode >>= 3;
- }
+ for (int i = 0; i < 3; i++)
+ {
+ memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3);
+ mode >>= 3;
+ }
- smode[10] = 0;
- 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);
- continue;
- }
+ smode[10] = 0;
+ 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);
+ break;
+ }
- if (expand_string_message)
- goto EXPAND_FAILED;
- yield = string_fmt_append(yield, "%d", vaguely_random_number((int)max));
- continue;
- }
+ if (expand_string_message)
+ goto EXPAND_FAILED;
+ yield = string_fmt_append(yield, "%d", vaguely_random_number((int)max));
+ break;
+ }
- family = string_is_ip_address(sub, &maskptr);
- if (family == 0)
- {
- expand_string_message = string_sprintf(
- "reverse_ip() not given an IP address [%s]", sub);
- goto EXPAND_FAILED;
- }
- invert_address(reversed, sub);
- yield = string_cat(yield, reversed);
- continue;
- }
+ family = string_is_ip_address(sub, &maskptr);
+ if (family == 0)
+ {
+ expand_string_message = string_sprintf(
+ "reverse_ip() not given an IP address [%s]", sub);
+ goto EXPAND_FAILED;
+ }
+ invert_address(reversed, sub);
+ yield = string_cat(yield, reversed);
+ break;
+ }
- default:
- expand_string_message =
- string_sprintf("unknown expansion operator \"%s\"", name);
- goto EXPAND_FAILED;
- }
+ default:
+ expand_string_message =
+ string_sprintf("unknown expansion operator \"%s\"", name);
+ goto EXPAND_FAILED;
+ } /* EOP_* switch */
+
+ DEBUG(D_expand)
+ if (start > 0 || *s) /* only if not the sole expansion of the line */
+ debug_expansion_interim(US"op-res",
+ yield->s + start, yield->ptr - start, skipping);
+ continue;
+ }
/* Handle a plain name. If this is the first thing in the expansion, release
the pre-allocated buffer. If the result data is known to be in a new buffer,
newsize will be set to the size of that buffer, and we can just point at that
/* Handle a plain name. If this is the first thing in the expansion, release
the pre-allocated buffer. If the result data is known to be in a new buffer,
newsize will be set to the size of that buffer, and we can just point at that
{
expand_string_message = malformed_header
? US"missing } at end of string - could be header name not terminated by colon"
{
expand_string_message = malformed_header
? US"missing } at end of string - could be header name not terminated by colon"
tree_walk(acl_var_c, assert_variable_notin, &e);
tree_walk(acl_var_m, assert_variable_notin, &e);
tree_walk(acl_var_c, assert_variable_notin, &e);
tree_walk(acl_var_m, assert_variable_notin, &e);
/* check known-name variables */
for (var_entry * v = var_table; v < var_table + var_table_size; v++)
/* check known-name variables */
for (var_entry * v = var_table; v < var_table + var_table_size; v++)