* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
/* Recursively called function */
-static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL, BOOL, BOOL *);
-static int_eximarith_t expanded_string_integer(uschar *, 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
-#ifndef nelements
-# define nelements(arr) (sizeof(arr) / sizeof(*arr))
-#endif
-
/*************************************************
* Local statics and tables *
*************************************************/
US"acl",
US"certextract",
US"dlfunc",
+ US"env",
US"extract",
US"filter",
US"hash",
US"hmac",
US"if",
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ US"imapfolder",
+#endif
US"length",
US"listextract",
US"lookup",
EITEM_ACL,
EITEM_CERTEXTRACT,
EITEM_DLFUNC,
+ EITEM_ENV,
EITEM_EXTRACT,
EITEM_FILTER,
EITEM_HASH,
EITEM_HMAC,
EITEM_IF,
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ EITEM_IMAPFOLDER,
+#endif
EITEM_LENGTH,
EITEM_LISTEXTRACT,
EITEM_LOOKUP,
US"quote_local_part",
US"reverse_ip",
US"time_eval",
- US"time_interval"};
+ US"time_interval"
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ ,US"utf8_domain_from_alabel",
+ US"utf8_domain_to_alabel",
+ US"utf8_localpart_from_alabel",
+ US"utf8_localpart_to_alabel"
+#endif
+ };
enum {
EOP_FROM_UTF8,
EOP_QUOTE_LOCAL_PART,
EOP_REVERSE_IP,
EOP_TIME_EVAL,
- EOP_TIME_INTERVAL };
+ EOP_TIME_INTERVAL
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ ,EOP_UTF8_DOMAIN_FROM_ALABEL,
+ EOP_UTF8_DOMAIN_TO_ALABEL,
+ EOP_UTF8_LOCALPART_FROM_ALABEL,
+ EOP_UTF8_LOCALPART_TO_ALABEL
+#endif
+ };
static uschar *op_table_main[] = {
US"address",
US"utf8clean" };
enum {
- EOP_ADDRESS = sizeof(op_table_underscore)/sizeof(uschar *),
+ EOP_ADDRESS = nelem(op_table_underscore),
EOP_ADDRESSES,
EOP_BASE62,
EOP_BASE62D,
{ "message_id", vtype_stringptr, &message_id },
{ "message_linecount", vtype_int, &message_linecount },
{ "message_size", vtype_int, &message_size },
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ { "message_smtputf8", vtype_bool, &message_smtputf8 },
+#endif
#ifdef WITH_CONTENT_SCAN
{ "mime_anomaly_level", vtype_int, &mime_anomaly_level },
{ "mime_anomaly_text", vtype_stringptr, &mime_anomaly_text },
{ "sender_address_local_part", vtype_localpart, &sender_address },
{ "sender_data", vtype_stringptr, &sender_data },
{ "sender_fullhost", vtype_stringptr, &sender_fullhost },
+ { "sender_helo_dnssec", vtype_bool, &sender_helo_dnssec },
{ "sender_helo_name", vtype_stringptr, &sender_helo_name },
{ "sender_host_address", vtype_stringptr, &sender_host_address },
{ "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated },
{ "warnmsg_recipients", vtype_stringptr, &warnmsg_recipients }
};
-static int var_table_size = sizeof(var_table)/sizeof(var_entry);
+static int var_table_size = nelem(var_table);
static uschar var_buffer[256];
static BOOL malformed_header;
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;
return tls_cert_ext_by_oid(*(void **)vp->value, field, 0);
for(cp = certfields;
- cp < certfields + nelements(certfields);
+ cp < certfields + nelem(certfields);
cp++)
if (Ustrncmp(cp->name, field, cp->namelen) == 0)
{
while (*s != 0)
{
- if (i == 0) i = sizeof(prime)/sizeof(int) - 1;
+ if (i == 0) i = nelem(prime) - 1;
total += prime[i--] * (unsigned int)(*s++);
}
*/
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 = NULL;
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);
+if(--nsub > nelem(acl_arg)) nsub = nelem(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;
/* Find which condition we are dealing with, and switch on it */
-cond_type = chop_match(name, cond_table, sizeof(cond_table)/sizeof(uschar *));
+cond_type = chop_match(name, cond_table, nelem(cond_table));
switch(cond_type)
{
/* def: tests for a non-empty variable, or for the existence of a header. If
case ECOND_ACL:
/* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} */
{
+ uschar *sub[10];
uschar *user_msg;
BOOL cond = FALSE;
int size = 0;
while (isspace(*s)) s++;
if (*s++ != '{') goto COND_FAILED_CURLY_START; /*}*/
- switch(read_subs(sub, sizeof(sub)/sizeof(*sub), 1,
+ switch(read_subs(sub, nelem(sub), 1,
&s, yield == NULL, TRUE, US"acl", resetok))
{
case 1: expand_string_message = US"too few arguments or bracketing "
}
*resetok = FALSE;
- if (yield != NULL) switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg))
+ if (yield != NULL) switch(eval_acl(sub, nelem(sub), &user_msg))
{
case OK:
cond = TRUE;
in their own set of braces. */
case ECOND_SASLAUTHD:
- #ifndef CYRUS_SASLAUTHD_SOCKET
- goto COND_FAILED_NOT_COMPILED;
- #else
- while (isspace(*s)) s++;
- if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
- switch(read_subs(sub, 4, 2, &s, yield == NULL, TRUE, US"saslauthd", resetok))
- {
- case 1: expand_string_message = US"too few arguments or bracketing "
- "error for saslauthd";
- case 2:
- case 3: return NULL;
- }
- if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */
- if (yield != NULL)
+#ifndef CYRUS_SASLAUTHD_SOCKET
+ goto COND_FAILED_NOT_COMPILED;
+#else
{
- int rc;
- rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3],
- &expand_string_message);
- if (rc == ERROR || rc == DEFER) return NULL;
- *yield = (rc == OK) == testfor;
+ uschar *sub[4];
+ while (isspace(*s)) s++;
+ if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
+ switch(read_subs(sub, nelem(sub), 2, &s, yield == NULL, TRUE, US"saslauthd",
+ resetok))
+ {
+ case 1: expand_string_message = US"too few arguments or bracketing "
+ "error for saslauthd";
+ case 2:
+ case 3: return NULL;
+ }
+ if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */
+ if (yield != NULL)
+ {
+ int rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3],
+ &expand_string_message);
+ if (rc == ERROR || rc == DEFER) return NULL;
+ *yield = (rc == OK) == testfor;
+ }
+ return s;
}
- return s;
- #endif /* CYRUS_SASLAUTHD_SOCKET */
+#endif /* CYRUS_SASLAUTHD_SOCKET */
/* symbolic operators for numeric and string comparison, and a number of
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;
}
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";
OK. */
s = read_name(name, sizeof(name), s, US"_-");
- item_type = chop_match(name, item_table, sizeof(item_table)/sizeof(uschar *));
+ item_type = chop_match(name, item_table, nelem(item_table));
switch(item_type)
{
uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */
uschar *user_msg;
- switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl", &resetok))
+ switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, US"acl",
+ &resetok))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
if (skipping) continue;
resetok = FALSE;
- switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg))
+ switch(eval_acl(sub, nelem(sub), &user_msg))
{
case OK:
case FAIL:
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);
continue;
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ case EITEM_IMAPFOLDER:
+ { /* ${imapfolder {name}{sep]{specials}} */
+ uschar *sub_arg[3];
+ uschar *encoded;
+
+ switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
+ &resetok))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+
+ if (sub_arg[1] == NULL) /* One argument */
+ {
+ sub_arg[1] = US"/"; /* default separator */
+ sub_arg[2] = NULL;
+ }
+ else if (Ustrlen(sub_arg[1]) != 1)
+ {
+ expand_string_message =
+ string_sprintf(
+ "IMAP folder separator must be one character, found \"%s\"",
+ sub_arg[1]);
+ goto EXPAND_FAILED;
+ }
+
+ if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset,
+ sub_arg[1][0], sub_arg[2], &expand_string_message)))
+ goto EXPAND_FAILED;
+ if (!skipping)
+ yield = string_cat(yield, &size, &ptr, encoded, Ustrlen(encoded));
+ continue;
+ }
+#endif
+
/* Handle database lookups unless locked out. If "skipping" is TRUE, we are
expanding an internal string that isn't actually going to be used. All we
need to do is check the syntax, so don't do a lookup at all. Preserve the
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);
{
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)
{
{
int ovector[3*(EXPAND_MAXN+1)];
int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra,
- PCRE_EOPT | emptyopt, ovector, sizeof(ovector)/sizeof(int));
+ PCRE_EOPT | emptyopt, ovector, nelem(ovector));
int nn;
uschar *insert;
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);
- if (temp == NULL) goto EXPAND_FAILED;
- lookup_value = temp;
+ t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+ if (!t) goto EXPAND_FAILED;
+ 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 */
}
}
case EITEM_SORT:
{
int sep = 0;
- uschar *srclist, *cmp, *xtract;
+ const uschar *srclist, *cmp, *xtract;
uschar *srcitem;
- uschar *dstlist = NULL;
- uschar *dstkeylist = NULL;
+ const uschar *dstlist = NULL, *dstkeylist = NULL;
uschar * tmp;
uschar *save_iterate_item = iterate_item;
#define EXPAND_DLFUNC_MAX_ARGS 8
case EITEM_DLFUNC:
- #ifndef EXPAND_DLFUNC
- expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/
- "is not included in this binary";
- goto EXPAND_FAILED;
+#ifndef EXPAND_DLFUNC
+ expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/
+ "is not included in this binary";
+ goto EXPAND_FAILED;
- #else /* EXPAND_DLFUNC */
+#else /* EXPAND_DLFUNC */
{
tree_node *t;
exim_dlfunc_t *func;
goto EXPAND_FAILED;
}
}
- #endif /* EXPAND_DLFUNC */
+#endif /* EXPAND_DLFUNC */
+
+ case EITEM_ENV: /* ${env {name} {val_if_found} {val_if_unfound}} */
+ {
+ uschar * key;
+ uschar *save_lookup_value = lookup_value;
+
+ while (isspace(*s)) s++;
+ if (*s != '{') /*}*/
+ goto EXPAND_FAILED;
+
+ key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+ if (!key) goto EXPAND_FAILED; /*{*/
+ if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+
+ lookup_value = US getenv(CS key);
+
+ switch(process_yesno(
+ skipping, /* were previously skipping */
+ lookup_value != NULL, /* success/failure indicator */
+ save_lookup_value, /* value to reset for string2 */
+ &s, /* input pointer */
+ &yield, /* output pointer */
+ &size, /* output size */
+ &ptr, /* output current point */
+ US"env", /* condition type */
+ &resetok))
+ {
+ case 1: goto EXPAND_FAILED; /* when all is well, the */
+ case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */
+ }
+ continue;
+ }
} /* EITEM_* switch */
/* Control reaches here if the name is not recognized as one of the more
the arguments and then scan the main table. */
if ((c = chop_match(name, op_table_underscore,
- sizeof(op_table_underscore)/sizeof(uschar *))) < 0)
+ nelem(op_table_underscore))) < 0)
{
arg = Ustrchr(name, '_');
if (arg != NULL) *arg = 0;
- c = chop_match(name, op_table_main,
- sizeof(op_table_main)/sizeof(uschar *));
- if (c >= 0) c += sizeof(op_table_underscore)/sizeof(uschar *);
+ c = chop_match(name, op_table_main, nelem(op_table_main));
+ if (c >= 0) c += nelem(op_table_underscore);
if (arg != NULL) *arg++ = '_'; /* Put back for error messages */
}
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; /*{*/
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;
if (bytes_left)
{
if ((c & 0xc0) != 0x80)
- {
/* wrong continuation byte; invalidate all bytes */
complete = 1; /* error */
- }
else
{
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
yield = string_cat(yield, &size, &ptr, seq_buff, seq_len);
index = 0;
}
- }
}
}
else /* no bytes left: new sequence */
yield = string_cat(yield, &size, &ptr, UTF8_REPLACEMENT_CHAR, 1);
}
if ((complete == 1) && ((c & 0x80) == 0))
- { /* ASCII character follows incomplete sequence */
+ /* ASCII character follows incomplete sequence */
yield = string_cat(yield, &size, &ptr, &c, 1);
- }
}
continue;
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ case EOP_UTF8_DOMAIN_TO_ALABEL:
+ {
+ uschar * error = NULL;
+ uschar * s = string_domain_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, &size, &ptr, s, Ustrlen(s));
+ continue;
+ }
+
+ case EOP_UTF8_DOMAIN_FROM_ALABEL:
+ {
+ 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, &size, &ptr, s, Ustrlen(s));
+ continue;
+ }
+
+ case EOP_UTF8_LOCALPART_TO_ALABEL:
+ {
+ 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, &size, &ptr, s, Ustrlen(s));
+ DEBUG(D_expand) debug_printf("yield: '%s'\n", yield);
+ continue;
+ }
+
+ case EOP_UTF8_LOCALPART_FROM_ALABEL:
+ {
+ 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, &size, &ptr, s, Ustrlen(s));
+ continue;
+ }
+#endif /* EXPERIMENTAL_INTERNATIONAL */
+
/* escape turns all non-printing characters into escape sequences. */
case EOP_ESCAPE:
{
- uschar *t = string_printing(sub);
+ const uschar *t = string_printing(sub);
yield = string_cat(yield, &size, &ptr, t, Ustrlen(t));
continue;
}
+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;
}
*/
static int_eximarith_t
-expanded_string_integer(uschar *s, BOOL isplus)
+expanded_string_integer(const uschar *s, BOOL isplus)
{
int_eximarith_t value;
uschar *msg = US"invalid integer \"%s\"";
{
int ovector[3*(EXPAND_MAXN+1)];
int n = pcre_exec(re, NULL, subject, Ustrlen(subject), 0, PCRE_EOPT|options,
- ovector, sizeof(ovector)/sizeof(int));
+ ovector, nelem(ovector));
BOOL yield = n >= 0;
if (n == 0) n = EXPAND_MAXN + 1;
if (yield)