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. */
{ "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;}
*************************************************/
/* 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);
}
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;
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);
{
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)
{
while (*s == ',')
if (Ustrncmp(++s, "preexpand", 9) == 0)
- { late_expand = FALSE; s += 9; }
+ { late_expand = 0; s += 9; }
else
{
const uschar * t = 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 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;
}