* 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 */
# ifndef SUPPORT_CRYPTEQ
# define SUPPORT_CRYPTEQ
# endif
-#else
-
-/* 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);
-
#endif /*!STAND_ALONE*/
#ifdef LOOKUP_LDAP
US"expand",
US"h",
US"hash",
+ US"headerwrap",
US"hex2b64",
US"hexquote",
US"ipv6denorm",
EOP_EXPAND,
EOP_H,
EOP_HASH,
+ EOP_HEADERWRAP,
EOP_HEX2B64,
EOP_HEXQUOTE,
EOP_IPV6DENORM,
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. */
{ "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 },
{ "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 },
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;}
#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 *
*************************************************/
/* 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)
s = recipients_list[i].address;
g = string_append2_listele_n(g, US", ", s, Ustrlen(s));
}
+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);
}
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;
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)
case vtype_string_func:
{
stringptr_fn_t * fn = (stringptr_fn_t *) val;
- uschar* s = fn();
+ uschar * s = fn();
return s ? s : US"";
}
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;
/* 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,
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);
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
{
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)
{
/* 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;
(int)(t-s), s);
goto EXPAND_FAILED;
}
- }
Uskip_whitespace(&s);
if (*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;
case 2:
case 3: goto EXPAND_FAILED;
}
+ if (flags & ESI_SKIPPING) continue;
if (sub[1] && *(sub[1]))
{
{
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);
{
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;
}
{
uschar *tt = sub;
unsigned long int n = 0;
- while (*tt != 0)
+ while (*tt)
{
uschar *t = Ustrchr(base62_chars, *tt++);
if (!t)
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:
case EOP_UTF8CLEAN:
{
int seq_len = 0, index = 0, bytes_left = 0, complete;
- long codepoint = -1;
+ 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 (--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);
}
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;
goto EXPAND_FAILED;
}
yield = string_cat(yield, s);
- DEBUG(D_expand) debug_printf_indent("yield: '%s'\n", string_from_gstring(yield));
+ DEBUG(D_expand) debug_printf_indent("yield: '%Y'\n", yield);
break;
}
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 "