/* Recursively called function */
-static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL, BOOL, BOOL *);
+static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *);
+static int_eximarith_t expanded_string_integer(const uschar *, BOOL);
#ifdef STAND_ALONE
#ifndef SUPPORT_CRYPTEQ
US"reduce",
US"run",
US"sg",
+ US"sort",
US"substr",
US"tr" };
EITEM_REDUCE,
EITEM_RUN,
EITEM_SG,
+ EITEM_SORT,
EITEM_SUBSTR,
EITEM_TR };
US"rxquote",
US"s",
US"sha1",
+ US"sha256",
US"stat",
US"str2b64",
US"strlen",
EOP_RXQUOTE,
EOP_S,
EOP_SHA1,
+ EOP_SHA256,
EOP_STAT,
EOP_STR2B64,
EOP_STRLEN,
};
-/* Type for main variable table */
-
-typedef struct {
- const char *name;
- int type;
- void *value;
-} var_entry;
-
-/* Type for entries pointing to address/length pairs. Not currently
-in use. */
-
-typedef struct {
- uschar **address;
- int *length;
-} alblock;
-
/* Types of table entry */
-enum {
+enum vtypes {
vtype_int, /* value is address of int */
vtype_filter_int, /* ditto, but recognized only when filtering */
vtype_ino, /* value is address of ino_t (not always an int) */
#ifndef DISABLE_DKIM
,vtype_dkim /* Lookup of value in DKIM signature */
#endif
- };
+};
+
+/* Type for main variable table */
+
+typedef struct {
+ const char *name;
+ enum vtypes type;
+ void *value;
+} var_entry;
+
+/* Type for entries pointing to address/length pairs. Not currently
+in use. */
+
+typedef struct {
+ uschar **address;
+ int *length;
+} alblock;
static uschar * fn_recipients(void);
{ "caller_uid", vtype_uid, &real_uid },
{ "compile_date", vtype_stringptr, &version_date },
{ "compile_number", vtype_stringptr, &version_cnumber },
+ { "config_dir", vtype_stringptr, &config_main_directory },
+ { "config_file", vtype_stringptr, &config_main_filename },
{ "csa_status", vtype_stringptr, &csa_status },
#ifdef EXPERIMENTAL_DCC
{ "dcc_header", vtype_stringptr, &dcc_header },
{ "dnslist_value", vtype_stringptr, &dnslist_value },
{ "domain", vtype_stringptr, &deliver_domain },
{ "domain_data", vtype_stringptr, &deliver_domain_data },
+#ifdef EXPERIMENTAL_EVENT
+ { "event_data", vtype_stringptr, &event_data },
+
+ /*XXX want to use generic vars for as many of these as possible*/
+ { "event_defer_errno", vtype_int, &event_defer_errno },
+
+ { "event_name", vtype_stringptr, &event_name },
+#endif
{ "exim_gid", vtype_gid, &exim_gid },
{ "exim_path", vtype_stringptr, &exim_path },
{ "exim_uid", vtype_uid, &exim_uid },
+ { "exim_version", vtype_stringptr, &version_string },
#ifdef WITH_OLD_DEMIME
{ "found_extension", vtype_stringptr, &found_extension },
#endif
{ "host_data", vtype_stringptr, &host_data },
{ "host_lookup_deferred",vtype_int, &host_lookup_deferred },
{ "host_lookup_failed", vtype_int, &host_lookup_failed },
+ { "host_port", vtype_int, &deliver_host_port },
{ "inode", vtype_ino, &deliver_inode },
{ "interface_address", vtype_stringptr, &interface_address },
{ "interface_port", vtype_int, &interface_port },
{ "sn8", vtype_filter_int, &filter_sn[8] },
{ "sn9", vtype_filter_int, &filter_sn[9] },
#ifdef WITH_CONTENT_SCAN
+ { "spam_action", vtype_stringptr, &spam_action },
{ "spam_bar", vtype_stringptr, &spam_bar },
{ "spam_report", vtype_stringptr, &spam_report },
{ "spam_score", vtype_stringptr, &spam_score },
{ "tls_in_ourcert", vtype_cert, &tls_in.ourcert },
{ "tls_in_peercert", vtype_cert, &tls_in.peercert },
{ "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn },
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+#if defined(SUPPORT_TLS)
{ "tls_in_sni", vtype_stringptr, &tls_in.sni },
#endif
{ "tls_out_bits", vtype_int, &tls_out.bits },
{ "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified },
{ "tls_out_cipher", vtype_stringptr, &tls_out.cipher },
+#ifdef EXPERIMENTAL_DANE
+ { "tls_out_dane", vtype_bool, &tls_out.dane_verified },
+#endif
{ "tls_out_ocsp", vtype_int, &tls_out.ocsp },
{ "tls_out_ourcert", vtype_cert, &tls_out.ourcert },
{ "tls_out_peercert", vtype_cert, &tls_out.peercert },
{ "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn },
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+#if defined(SUPPORT_TLS)
{ "tls_out_sni", vtype_stringptr, &tls_out.sni },
#endif
+#ifdef EXPERIMENTAL_DANE
+ { "tls_out_tlsa_usage", vtype_int, &tls_out.tlsa_usage },
+#endif
{ "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+#if defined(SUPPORT_TLS)
{ "tls_sni", vtype_stringptr, &tls_in.sni }, /* mind the alphabetical order! */
#endif
{ "tod_logfile", vtype_todlf, NULL },
{ "tod_zone", vtype_todzone, NULL },
{ "tod_zulu", vtype_todzulu, NULL },
-#ifdef EXPERIMENTAL_TPDA
- { "tpda_defer_errno", vtype_int, &tpda_defer_errno },
- { "tpda_defer_errstr", vtype_stringptr, &tpda_defer_errstr },
- { "tpda_delivery_confirmation", vtype_stringptr, &tpda_delivery_confirmation },
- { "tpda_delivery_domain", vtype_stringptr, &tpda_delivery_domain },
- { "tpda_delivery_fqdn", vtype_stringptr, &tpda_delivery_fqdn },
- { "tpda_delivery_ip", vtype_stringptr, &tpda_delivery_ip },
- { "tpda_delivery_local_part",vtype_stringptr,&tpda_delivery_local_part },
- { "tpda_delivery_port", vtype_int, &tpda_delivery_port },
-#endif
{ "transport_name", vtype_stringptr, &transport_name },
{ "value", vtype_stringptr, &lookup_value },
+ { "verify_mode", vtype_stringptr, &verify_mode },
{ "version_number", vtype_stringptr, &version_string },
{ "warn_message_delay", vtype_stringptr, &warnmsg_delay },
{ "warn_message_recipient",vtype_stringptr, &warnmsg_recipients },
#ifdef HAVE_ARC4RANDOM
/* cryptographically strong randomness, common on *BSD platforms, not
so much elsewhere. Alas. */
+#ifndef NOT_HAVE_ARC4RANDOM_STIR
arc4random_stir();
+#endif
#elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV)
#ifdef HAVE_SRANDOMDEV
/* uses random(4) for seeding */
Ustrchr() yields non-NULL if the character is zero (which is not something
I expected). */
-static uschar *
-read_name(uschar *name, int max, uschar *s, uschar *extras)
+static const uschar *
+read_name(uschar *name, int max, const uschar *s, uschar *extras)
{
int ptr = 0;
while (*s != 0 && (isalnum(*s) || Ustrchr(extras, *s) != NULL))
Returns: a pointer to the first character after the header name
*/
-static uschar *
-read_header_name(uschar *name, int max, uschar *s)
+static const uschar *
+read_header_name(uschar *name, int max, const uschar *s)
{
int prelen = Ustrchr(name, '_') - name + 1;
int ptr = Ustrlen(name) - prelen;
return s;
}
+static const uschar *
+read_cnumber(int *n, const uschar *s)
+{
+*n = 0;
+while (isdigit(*s)) *n = *n * 10 + (*s++ - '0');
+return s;
+}
+
/*************************************************
*/
static uschar *
-expand_getkeyed(uschar *key, uschar *s)
+expand_getkeyed(uschar *key, const uschar *s)
{
int length = Ustrlen(key);
while (isspace(*s)) s++;
{
int dkeylength;
uschar *data;
- uschar *dkey = s;
+ const uschar *dkey = s;
while (*s != 0 && *s != '=' && !isspace(*s)) s++;
dkeylength = s - dkey;
static uschar *
-expand_getlistele(int field, uschar * list)
+expand_getlistele(int field, const uschar * list)
{
-uschar * tlist= list;
+const uschar * tlist= list;
int sep= 0;
uschar dummy;
if(field<0)
-{
+ {
for(field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++;
sep= 0;
-}
+ }
if(field==0) return NULL;
while(--field>0 && (string_nextinlist(&list, &sep, &dummy, 1))) ;
return string_nextinlist(&list, &sep, NULL, 0);
#endif
}
+
+return NULL; /* Unknown variable. Silences static checkers. */
}
*/
static int
-read_subs(uschar **sub, int n, int m, uschar **sptr, BOOL skipping,
+read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping,
BOOL check_end, uschar *name, BOOL *resetok)
{
int i;
-uschar *s = *sptr;
+const uschar *s = *sptr;
while (isspace(*s)) s++;
for (i = 0; i < n; i++)
eval_acl(uschar ** sub, int nsub, uschar ** user_msgp)
{
int i;
-uschar *tmp;
int sav_narg = acl_narg;
int ret;
+uschar * dummy_logmsg;
extern int acl_where;
if(--nsub > sizeof(acl_arg)/sizeof(*acl_arg)) nsub = sizeof(acl_arg)/sizeof(*acl_arg);
for (i = 0; i < nsub && sub[i+1]; i++)
{
- tmp = acl_arg[i];
+ uschar * tmp = acl_arg[i];
acl_arg[i] = sub[i+1]; /* place callers args in the globals */
sub[i+1] = tmp; /* stash the old args using our caller's storage */
}
acl_narg>0 ? acl_arg[0] : US"<none>",
acl_narg>1 ? " +more" : "");
-ret = acl_eval(acl_where, sub[0], user_msgp, &tmp);
+ret = acl_eval(acl_where, sub[0], user_msgp, &dummy_logmsg);
for (i = 0; i < nsub; i++)
acl_arg[i] = sub[i+1]; /* restore old args */
NULL after an error
*/
-static uschar *
-eval_condition(uschar *s, BOOL *resetok, BOOL *yield)
+static const uschar *
+eval_condition(const uschar *s, BOOL *resetok, BOOL *yield)
{
BOOL testfor = TRUE;
BOOL tempcond, combined_cond;
int_eximarith_t num[2];
struct stat statbuf;
uschar name[256];
-uschar *sub[10];
+const uschar *sub[10];
const pcre *re;
const uschar *rerror;
case ECOND_ACL:
/* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} */
{
+ uschar *sub[10];
uschar *user_msg;
BOOL cond = FALSE;
int size = 0;
}
else
{
- num[i] = expand_string_integer(sub[i], FALSE);
+ num[i] = expanded_string_integer(sub[i], FALSE);
if (expand_string_message != NULL) return NULL;
}
}
case ECOND_INLIST:
case ECOND_INLISTI:
{
+ const uschar * list = sub[1];
int sep = 0;
uschar *save_iterate_item = iterate_item;
int (*compare)(const uschar *, const uschar *);
+ DEBUG(D_expand) debug_printf("condition: %s\n", name);
+
tempcond = FALSE;
- if (cond_type == ECOND_INLISTI)
- compare = strcmpic;
- else
- compare = (int (*)(const uschar *, const uschar *)) strcmp;
+ compare = cond_type == ECOND_INLISTI
+ ? strcmpic : (int (*)(const uschar *, const uschar *)) strcmp;
- while ((iterate_item = string_nextinlist(&sub[1], &sep, NULL, 0)) != NULL)
+ while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)))
if (compare(sub[0], iterate_item) == 0)
{
tempcond = TRUE;
case ECOND_FORALL:
case ECOND_FORANY:
{
+ const uschar * list;
int sep = 0;
uschar *save_iterate_item = iterate_item;
+ DEBUG(D_expand) debug_printf("condition: %s\n", name);
+
while (isspace(*s)) s++;
if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL), TRUE, resetok);
}
if (yield != NULL) *yield = !testfor;
- while ((iterate_item = string_nextinlist(&sub[0], &sep, NULL, 0)) != NULL)
+ list = sub[0];
+ while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL)
{
DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, iterate_item);
if (!eval_condition(sub[1], resetok, &tempcond))
*/
static int
-process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, uschar **sptr,
+process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, const uschar **sptr,
uschar **yieldptr, int *sizeptr, int *ptrptr, uschar *type, BOOL *resetok)
{
int rc = 0;
-uschar *s = *sptr; /* Local value */
+const uschar *s = *sptr; /* Local value */
uschar *sub1, *sub2;
/* If there are no following strings, we substitute the contents of $value for
else if (*s != '}')
{
uschar name[256];
- s = read_name(name, sizeof(name), s, US"_");
+ /* deconst cast ok here as source is s anyway */
+ s = US read_name(name, sizeof(name), s, US"_");
if (Ustrcmp(name, "fail") == 0)
{
if (!yes && !skipping)
*/
static uschar *
-expand_string_internal(uschar *string, BOOL ket_ends, uschar **left,
+expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left,
BOOL skipping, BOOL honour_dollar, BOOL *resetok_p)
{
int ptr = 0;
int size = Ustrlen(string)+ 64;
int item_type;
uschar *yield = store_get(size);
-uschar *s = string;
+const uschar *s = string;
uschar *save_expand_nstring[EXPAND_MAXN+1];
int save_expand_nlength[EXPAND_MAXN+1];
BOOL resetok = TRUE;
if (s[1] == 'N')
{
- uschar *t = s + 2;
+ const uschar * t = s + 2;
for (s = t; *s != 0; s++) if (*s == '\\' && s[1] == 'N') break;
yield = string_cat(yield, &size, &ptr, t, s - t);
if (*s != 0) s += 2;
if (isdigit(*s))
{
int n;
- s = read_number(&n, s);
+ s = read_cnumber(&n, s);
if (n >= 0 && n <= expand_nmax)
yield = string_cat(yield, &size, &ptr, expand_nstring[n],
expand_nlength[n]);
if (isdigit((*(++s))))
{
int n;
- s = read_number(&n, s); /*{*/
+ s = read_cnumber(&n, s); /*{*/
if (*s++ != '}')
{ /*{*/
expand_string_message = US"} expected after number";
case EITEM_IF:
{
BOOL cond = FALSE;
- uschar *next_s;
+ const uschar *next_s;
int save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
int stype, partial, affixlen, starflags;
int expand_setup = 0;
int nameptr = 0;
- uschar *key, *filename, *affix;
+ uschar *key, *filename;
+ const uschar *affix;
uschar *save_lookup_value = lookup_value;
int save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
DEBUG(D_expand) debug_printf("connected to socket %s\n", sub_arg[0]);
+ /* Allow sequencing of test actions */
+ if (running_in_test_harness) millisleep(100);
+
/* Write the request string, if not empty */
if (sub_arg[1][0] != 0)
shutdown(fd, SHUT_WR);
#endif
+ if (running_in_test_harness) millisleep(100);
+
/* Now we need to read from the socket, under a timeout. The function
that reads a file can be used. */
{
FILE *f;
uschar *arg;
- uschar **argv;
+ const uschar **argv;
pid_t pid;
int fd_in, fd_out;
int lsize = 0;
/* Create the child process, making it a group leader. */
- pid = child_open(argv, NULL, 0077, &fd_in, &fd_out, TRUE);
+ pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE);
if (pid < 0)
{
while (len > 0 && isspace(p[len-1])) len--;
p[len] = 0;
- if (*p == 0 && !skipping)
- {
- expand_string_message = US"first argument of \"extract\" must "
- "not be empty";
- goto EXPAND_FAILED;
- }
+ if (!skipping)
+ {
+ if (*p == 0)
+ {
+ expand_string_message = US"first argument of \"extract\" must "
+ "not be empty";
+ goto EXPAND_FAILED;
+ }
- if (*p == '-')
- {
- field_number = -1;
- p++;
- }
- while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0';
- if (*p == 0)
- {
- field_number *= x;
- j = 3; /* Need 3 args */
- field_number_set = TRUE;
- }
+ if (*p == '-')
+ {
+ field_number = -1;
+ p++;
+ }
+ while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0';
+ if (*p == 0)
+ {
+ field_number *= x;
+ j = 3; /* Need 3 args */
+ field_number_set = TRUE;
+ }
+ }
}
}
else goto EXPAND_FAILED_CURLY;
#ifdef SUPPORT_TLS
case EITEM_CERTEXTRACT:
{
- int i;
- int field_number = 1;
uschar *save_lookup_value = lookup_value;
uschar *sub[2];
int save_expand_nmax =
/* strip spaces fore & aft */
{
int len;
- int x = 0;
uschar *p = sub[0];
while (isspace(*p)) p++;
int sep = 0;
int save_ptr = ptr;
uschar outsep[2] = { '\0', '\0' };
- uschar *list, *expr, *temp;
+ const uschar *list, *expr, *temp;
uschar *save_iterate_item = iterate_item;
uschar *save_lookup_value = lookup_value;
if (item_type == EITEM_REDUCE)
{
+ uschar * t;
while (isspace(*s)) s++;
if (*s++ != '{') goto EXPAND_FAILED_CURLY;
- temp = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+ t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
if (temp == NULL) goto EXPAND_FAILED;
- lookup_value = temp;
+ lookup_value = t;
if (*s++ != '}') goto EXPAND_FAILED_CURLY;
}
if (temp != NULL) s = temp;
}
else
- {
temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok);
- }
if (temp == NULL)
{
else
{
- temp = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok);
+ uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok);
+ temp = t;
if (temp == NULL)
{
iterate_item = save_iterate_item;
}
if (item_type == EITEM_REDUCE)
{
- lookup_value = temp; /* Update the value of $value */
+ lookup_value = t; /* Update the value of $value */
continue; /* and continue the iteration */
}
}
continue;
}
+ case EITEM_SORT:
+ {
+ int sep = 0;
+ const uschar *srclist, *cmp, *xtract;
+ uschar *srcitem;
+ const uschar *dstlist = NULL, *dstkeylist = NULL;
+ uschar * tmp;
+ uschar *save_iterate_item = iterate_item;
+
+ while (isspace(*s)) s++;
+ if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+
+ srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+ if (!srclist) goto EXPAND_FAILED;
+ if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+
+ while (isspace(*s)) s++;
+ if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+
+ cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok);
+ if (!cmp) goto EXPAND_FAILED;
+ if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+
+ while (isspace(*s)) s++;
+ if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+
+ xtract = s;
+ tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok);
+ if (!tmp) goto EXPAND_FAILED;
+ xtract = string_copyn(xtract, s - xtract);
+
+ if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ /*{*/
+ if (*s++ != '}')
+ { /*{*/
+ expand_string_message = US"missing } at end of \"sort\"";
+ goto EXPAND_FAILED;
+ }
+
+ if (skipping) continue;
+
+ while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0)))
+ {
+ uschar * dstitem;
+ uschar * newlist = NULL;
+ uschar * newkeylist = NULL;
+ uschar * srcfield;
+
+ DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, srcitem);
+
+ /* extract field for comparisons */
+ iterate_item = srcitem;
+ if ( !(srcfield = expand_string_internal(xtract, FALSE, NULL, FALSE,
+ TRUE, &resetok))
+ || !*srcfield)
+ {
+ expand_string_message = string_sprintf(
+ "field-extract in sort: \"%s\"", xtract);
+ goto EXPAND_FAILED;
+ }
+
+ /* Insertion sort */
+
+ /* copy output list until new-item < list-item */
+ while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0)))
+ {
+ uschar * dstfield;
+ uschar * expr;
+ BOOL before;
+
+ /* field for comparison */
+ if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
+ goto sort_mismatch;
+
+ /* build and run condition string */
+ expr = string_sprintf("%s{%s}{%s}", cmp, srcfield, dstfield);
+
+ DEBUG(D_expand) debug_printf("%s: cond = \"%s\"\n", name, expr);
+ if (!eval_condition(expr, &resetok, &before))
+ {
+ expand_string_message = string_sprintf("comparison in sort: %s",
+ expr);
+ goto EXPAND_FAILED;
+ }
+
+ if (before)
+ {
+ /* New-item sorts before this dst-item. Append new-item,
+ then dst-item, then remainder of dst list. */
+
+ newlist = string_append_listele(newlist, sep, srcitem);
+ newkeylist = string_append_listele(newkeylist, sep, srcfield);
+ srcitem = NULL;
+
+ newlist = string_append_listele(newlist, sep, dstitem);
+ newkeylist = string_append_listele(newkeylist, sep, dstfield);
+
+ while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0)))
+ {
+ if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
+ goto sort_mismatch;
+ newlist = string_append_listele(newlist, sep, dstitem);
+ newkeylist = string_append_listele(newkeylist, sep, dstfield);
+ }
+
+ break;
+ }
+
+ newlist = string_append_listele(newlist, sep, dstitem);
+ newkeylist = string_append_listele(newkeylist, sep, dstfield);
+ }
+
+ /* If we ran out of dstlist without consuming srcitem, append it */
+ if (srcitem)
+ {
+ newlist = string_append_listele(newlist, sep, srcitem);
+ newkeylist = string_append_listele(newkeylist, sep, srcfield);
+ }
+
+ dstlist = newlist;
+ dstkeylist = newkeylist;
+
+ DEBUG(D_expand) debug_printf("%s: dstlist = \"%s\"\n", name, dstlist);
+ DEBUG(D_expand) debug_printf("%s: dstkeylist = \"%s\"\n", name, dstkeylist);
+ }
+
+ if (dstlist)
+ yield = string_cat(yield, &size, &ptr, dstlist, Ustrlen(dstlist));
+
+ /* Restore preserved $item */
+ iterate_item = save_iterate_item;
+ continue;
+
+ sort_mismatch:
+ expand_string_message = US"Internal error in sort (list mismatch)";
+ goto EXPAND_FAILED;
+ }
+
/* If ${dlfunc } support is configured, handle calling dynamically-loaded
functions, unless locked out at this time. Syntax is ${dlfunc{file}{func}}
int c;
uschar *arg = NULL;
uschar *sub;
- var_entry *vp;
+ var_entry *vp = NULL;
/* Owing to an historical mis-design, an underscore may be part of the
operator name, or it may introduce arguments. We therefore first scan the
as we do not want to do the usual expansion. For most, expand the string.*/
switch(c)
{
- case EOP_SHA1:
+#ifdef SUPPORT_TLS
case EOP_MD5:
+ case EOP_SHA1:
+ case EOP_SHA256:
if (s[1] == '$')
{
- uschar * s1 = s;
+ const uschar * s1 = s;
sub = expand_string_internal(s+2, TRUE, &s1, skipping,
FALSE, &resetok);
if (!sub) goto EXPAND_FAILED; /*{*/
s = s1+1;
break;
}
+ vp = NULL;
}
- vp = NULL;
/*FALLTHROUGH*/
+#endif
default:
sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (!sub) goto EXPAND_FAILED;
}
case EOP_MD5:
+#ifdef SUPPORT_TLS
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_md5(*(void **)vp->value);
- yield = string_cat(yield, &size, &ptr, cp, (int)strlen(cp));
+ yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
}
else
+#endif
{
md5 base;
uschar digest[16];
continue;
case EOP_SHA1:
+#ifdef SUPPORT_TLS
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value);
- yield = string_cat(yield, &size, &ptr, cp, (int)strlen(cp));
+ yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
}
else
+#endif
{
sha1 base;
uschar digest[20];
}
continue;
+ case EOP_SHA256:
+#ifdef SUPPORT_TLS
+ if (vp && *(void **)vp->value)
+ {
+ uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value);
+ yield = string_cat(yield, &size, &ptr, cp, (int)Ustrlen(cp));
+ }
+ else
+#endif
+ expand_string_message = US"sha256 only supported for certificates";
+ continue;
+
/* Convert hex encoding to base64 encoding */
case EOP_HEX2B64:
uschar * cp;
uschar buffer[256];
- while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
+ while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
cp = string_sprintf("%d", cnt);
yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
continue;
case EOP_LISTNAMED:
{
tree_node *t = NULL;
- uschar * list;
+ const uschar * list;
int sep = 0;
uschar * item;
uschar * suffix = US"";
case EOP_RFC2047:
{
uschar buffer[2048];
- uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset,
+ const uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset,
buffer, sizeof(buffer), FALSE);
yield = string_cat(yield, &size, &ptr, string, Ustrlen(string));
continue;
case EOP_UTF8CLEAN:
{
- int seq_len, index = 0;
- int bytes_left = 0;
+ int seq_len = 0, index = 0;
+ int bytes_left = 0;
+ long codepoint = -1;
uschar seq_buff[4]; /* accumulate utf-8 here */
while (*sub != 0)
{
- int complete;
- long codepoint;
- uschar c;
+ int complete = 0;
+ uschar c = *sub++;
- complete = 0;
- c = *sub++;
- if(bytes_left)
+ if (bytes_left)
{
if ((c & 0xc0) != 0x80)
{
if (--bytes_left == 0) /* codepoint complete */
{
if(codepoint > 0x10FFFF) /* is it too large? */
- complete = -1; /* error */
+ complete = -1; /* error (RFC3629 limit) */
else
{ /* finished; output utf-8 sequence */
yield = string_cat(yield, &size, &ptr, seq_buff, seq_len);
case EOP_ESCAPE:
{
- uschar *t = string_printing(sub);
+ const uschar *t = string_printing(sub);
yield = string_cat(yield, &size, &ptr, t, Ustrlen(t));
continue;
}
int_eximarith_t max;
uschar *s;
- max = expand_string_integer(sub, TRUE);
+ max = expanded_string_integer(sub, TRUE);
if (expand_string_message != NULL)
goto EXPAND_FAILED;
s = string_sprintf("%d", vaguely_random_number((int)max));
+const uschar *
+expand_cstring(const uschar *string)
+{
+search_find_defer = FALSE;
+malformed_header = FALSE;
+return (Ustrpbrk(string, "$\\") == NULL)? string :
+ expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL);
+}
+
+
+
/*************************************************
* Expand and copy *
*************************************************/
/* Now and again we want to expand a string and be sure that the result is in a
new bit of store. This function does that.
+Since we know it has been copied, the de-const cast is safe.
Argument: the string to be expanded
Returns: the expanded string, always in a new bit of store, or NULL
*/
uschar *
-expand_string_copy(uschar *string)
+expand_string_copy(const uschar *string)
{
-uschar *yield = expand_string(string);
+const uschar *yield = expand_cstring(string);
if (yield == string) yield = string_copy(string);
-return yield;
+return US yield;
}
int_eximarith_t
expand_string_integer(uschar *string, BOOL isplus)
{
+return expanded_string_integer(expand_string(string), isplus);
+}
+
+
+/*************************************************
+ * Interpret string as an integer *
+ *************************************************/
+
+/* Convert a string (that has already been expanded) into an integer.
+
+This function is used inside the expansion code.
+
+Arguments:
+ s the string to be expanded
+ isplus TRUE if a non-negative number is expected
+
+Returns: the integer value, or
+ -1 if string is NULL (which implies an expansion error)
+ -2 for an integer interpretation error
+ expand_string_message is set NULL for an OK integer
+*/
+
+static int_eximarith_t
+expanded_string_integer(const uschar *s, BOOL isplus)
+{
int_eximarith_t value;
-uschar *s = expand_string(string);
uschar *msg = US"invalid integer \"%s\"";
uschar *endptr;
}
+/* These values are usually fixed boolean values, but they are permitted to be
+expanded strings.
+
+Arguments:
+ addr address being routed
+ mtype the module type
+ mname the module name
+ dbg_opt debug selectors
+ oname the option name
+ bvalue the router's boolean value
+ svalue the router's string value
+ rvalue where to put the returned value
+
+Returns: OK value placed in rvalue
+ DEFER expansion failed
+*/
+
+int
+exp_bool(address_item *addr,
+ uschar *mtype, uschar *mname, unsigned dbg_opt,
+ uschar *oname, BOOL bvalue,
+ uschar *svalue, BOOL *rvalue)
+{
+uschar *expanded;
+if (svalue == NULL) { *rvalue = bvalue; return OK; }
+
+expanded = expand_string(svalue);
+if (expanded == NULL)
+ {
+ if (expand_string_forcedfail)
+ {
+ DEBUG(dbg_opt) debug_printf("expansion of \"%s\" forced failure\n", oname);
+ *rvalue = bvalue;
+ return OK;
+ }
+ addr->message = string_sprintf("failed to expand \"%s\" in %s %s: %s",
+ oname, mname, mtype, expand_string_message);
+ DEBUG(dbg_opt) debug_printf("%s\n", addr->message);
+ return DEFER;
+ }
+
+DEBUG(dbg_opt) debug_printf("expansion of \"%s\" yields \"%s\"\n", oname,
+ expanded);
+
+if (strcmpic(expanded, US"true") == 0 || strcmpic(expanded, US"yes") == 0)
+ *rvalue = TRUE;
+else if (strcmpic(expanded, US"false") == 0 || strcmpic(expanded, US"no") == 0)
+ *rvalue = FALSE;
+else
+ {
+ addr->message = string_sprintf("\"%s\" is not a valid value for the "
+ "\"%s\" option in the %s %s", expanded, oname, mname, mtype);
+ return DEFER;
+ }
+
+return OK;
+}
+
+
+
+
/*************************************************
**************************************************
* Stand-alone test program *