* 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. */
-#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
+#ifdef SUPPORT_I18N
US"imapfolder",
#endif
US"length",
EITEM_ACL,
EITEM_CERTEXTRACT,
EITEM_DLFUNC,
+ EITEM_ENV,
EITEM_EXTRACT,
EITEM_FILTER,
EITEM_HASH,
EITEM_HMAC,
EITEM_IF,
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
EITEM_IMAPFOLDER,
#endif
EITEM_LENGTH,
US"reverse_ip",
US"time_eval",
US"time_interval"
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
,US"utf8_domain_from_alabel",
US"utf8_domain_to_alabel",
US"utf8_localpart_from_alabel",
EOP_REVERSE_IP,
EOP_TIME_EVAL,
EOP_TIME_INTERVAL
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
,EOP_UTF8_DOMAIN_FROM_ALABEL,
EOP_UTF8_DOMAIN_TO_ALABEL,
EOP_UTF8_LOCALPART_FROM_ALABEL,
US"addresses",
US"base62",
US"base62d",
+ US"base64",
+ US"base64d",
US"domain",
US"escape",
US"eval",
US"hash",
US"hex2b64",
US"hexquote",
+ US"ipv6denorm",
+ US"ipv6norm",
US"l",
US"lc",
US"length",
US"utf8clean" };
enum {
- EOP_ADDRESS = sizeof(op_table_underscore)/sizeof(uschar *),
+ EOP_ADDRESS = nelem(op_table_underscore),
EOP_ADDRESSES,
EOP_BASE62,
EOP_BASE62D,
+ EOP_BASE64,
+ EOP_BASE64D,
EOP_DOMAIN,
EOP_ESCAPE,
EOP_EVAL,
EOP_HASH,
EOP_HEX2B64,
EOP_HEXQUOTE,
+ EOP_IPV6DENORM,
+ EOP_IPV6NORM,
EOP_L,
EOP_LC,
EOP_LENGTH,
{ "bounce_return_size_limit", vtype_int, &bounce_return_size_limit },
{ "caller_gid", vtype_gid, &real_gid },
{ "caller_uid", vtype_uid, &real_uid },
+ { "callout_address", vtype_stringptr, &callout_address },
{ "compile_date", vtype_stringptr, &version_date },
{ "compile_number", vtype_stringptr, &version_cnumber },
{ "config_dir", vtype_stringptr, &config_main_directory },
{ "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES },
{ "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY },
{ "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY },
+ { "dkim_key_length", vtype_int, &dkim_key_length },
{ "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS },
{ "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES },
{ "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE },
{ "dnslist_value", vtype_stringptr, &dnslist_value },
{ "domain", vtype_stringptr, &deliver_domain },
{ "domain_data", vtype_stringptr, &deliver_domain_data },
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
{ "event_data", vtype_stringptr, &event_data },
/*XXX want to use generic vars for as many of these as possible*/
{ "host_lookup_deferred",vtype_int, &host_lookup_deferred },
{ "host_lookup_failed", vtype_int, &host_lookup_failed },
{ "host_port", vtype_int, &deliver_host_port },
+ { "initial_cwd", vtype_stringptr, &initial_cwd },
{ "inode", vtype_ino, &deliver_inode },
{ "interface_address", vtype_stringptr, &interface_address },
{ "interface_port", vtype_int, &interface_port },
{ "message_id", vtype_stringptr, &message_id },
{ "message_linecount", vtype_int, &message_linecount },
{ "message_size", vtype_int, &message_size },
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
{ "message_smtputf8", vtype_bool, &message_smtputf8 },
#endif
#ifdef WITH_CONTENT_SCAN
{ "parent_domain", vtype_stringptr, &deliver_domain_parent },
{ "parent_local_part", vtype_stringptr, &deliver_localpart_parent },
{ "pid", vtype_pid, NULL },
+#ifndef DISABLE_PRDR
+ { "prdr_requested", vtype_bool, &prdr_requested },
+#endif
{ "primary_hostname", vtype_stringptr, &primary_hostname },
-#ifdef EXPERIMENTAL_PROXY
- { "proxy_host_address", vtype_stringptr, &proxy_host_address },
- { "proxy_host_port", vtype_int, &proxy_host_port },
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS)
+ { "proxy_external_address",vtype_stringptr, &proxy_external_address },
+ { "proxy_external_port", vtype_int, &proxy_external_port },
+ { "proxy_local_address", vtype_stringptr, &proxy_local_address },
+ { "proxy_local_port", vtype_int, &proxy_local_port },
{ "proxy_session", vtype_bool, &proxy_session },
- { "proxy_target_address",vtype_stringptr, &proxy_target_address },
- { "proxy_target_port", vtype_int, &proxy_target_port },
#endif
{ "prvscheck_address", vtype_stringptr, &prvscheck_address },
{ "prvscheck_keynum", vtype_stringptr, &prvscheck_keynum },
{ "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;
Returns: a pointer to the character after the last digit
*/
+/*XXX consider expanding to int_eximarith_t. But the test for
+"overbig numbers" in 0002 still needs to overflow it. */
static uschar *
read_number(int *n, uschar *s)
if (!(vp = find_var_ent(certvar)))
{
- expand_string_message =
+ expand_string_message =
string_sprintf("no variable named \"%s\"", certvar);
return NULL; /* Unknown variable name */
}
want to do that in future */
if (vp->type != vtype_cert)
{
- expand_string_message =
+ expand_string_message =
string_sprintf("\"%s\" is not a certificate", certvar);
return NULL; /* Unknown variable name */
}
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)
{
return (*cp->getfn)( *(void **)vp->value, modifier );
}
-expand_string_message =
+expand_string_message =
string_sprintf("bad field selector \"%s\" for certextract", field);
return NULL;
}
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++);
}
{
tree_node *node =
tree_search((name[4] == 'c')? acl_var_c : acl_var_m, name + 4);
- return (node == NULL)? (strict_acl_vars? NULL : US"") : node->data.ptr;
+ return node ? node->data.ptr : strict_acl_vars ? NULL : US"";
}
/* Handle $auth<n> variables. */
uschar *endptr;
int n = Ustrtoul(name + 4, &endptr, 10);
if (*endptr == 0 && n != 0 && n <= AUTH_VARS)
- return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1];
+ return !auth_vars[n-1] ? US"" : auth_vars[n-1];
+ }
+else if (Ustrncmp(name, "regex", 5) == 0)
+ {
+ uschar *endptr;
+ int n = Ustrtoul(name + 5, &endptr, 10);
+ if (*endptr == 0 && n != 0 && n <= REGEX_VARS)
+ return !regex_vars[n-1] ? US"" : regex_vars[n-1];
}
/* For all other variables, search the table */
switch (vp->type)
{
case vtype_filter_int:
- if (!filter_running) return NULL;
- /* Fall through */
- /* VVVVVVVVVVVV */
+ if (!filter_running) return NULL;
+ /* Fall through */
+ /* VVVVVVVVVVVV */
case vtype_int:
- sprintf(CS var_buffer, "%d", *(int *)(val)); /* Integer */
- return var_buffer;
+ sprintf(CS var_buffer, "%d", *(int *)(val)); /* Integer */
+ return var_buffer;
case vtype_ino:
- sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(val))); /* Inode */
- return var_buffer;
+ sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(val))); /* Inode */
+ return var_buffer;
case vtype_gid:
- sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(val))); /* gid */
- return var_buffer;
+ sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(val))); /* gid */
+ return var_buffer;
case vtype_uid:
- sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(val))); /* uid */
- return var_buffer;
+ sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(val))); /* uid */
+ return var_buffer;
case vtype_bool:
- sprintf(CS var_buffer, "%s", *(BOOL *)(val) ? "yes" : "no"); /* bool */
- return var_buffer;
+ sprintf(CS var_buffer, "%s", *(BOOL *)(val) ? "yes" : "no"); /* bool */
+ return var_buffer;
case vtype_stringptr: /* Pointer to string */
- s = *((uschar **)(val));
- return (s == NULL)? US"" : s;
+ return (s = *((uschar **)(val))) ? s : US"";
case vtype_pid:
- sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */
- return var_buffer;
+ sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */
+ return var_buffer;
case vtype_load_avg:
- sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */
- return var_buffer;
+ sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */
+ return var_buffer;
case vtype_host_lookup: /* Lookup if not done so */
- if (sender_host_name == NULL && sender_host_address != NULL &&
- !host_lookup_failed && host_name_lookup() == OK)
- host_build_sender_fullhost();
- return (sender_host_name == NULL)? US"" : sender_host_name;
+ if (sender_host_name == NULL && sender_host_address != NULL &&
+ !host_lookup_failed && host_name_lookup() == OK)
+ host_build_sender_fullhost();
+ return (sender_host_name == NULL)? US"" : sender_host_name;
case vtype_localpart: /* Get local part from address */
- s = *((uschar **)(val));
- if (s == NULL) return US"";
- domain = Ustrrchr(s, '@');
- if (domain == NULL) return s;
- if (domain - s > sizeof(var_buffer) - 1)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT
- " in string expansion", sizeof(var_buffer));
- Ustrncpy(var_buffer, s, domain - s);
- var_buffer[domain - s] = 0;
- return var_buffer;
+ s = *((uschar **)(val));
+ if (s == NULL) return US"";
+ domain = Ustrrchr(s, '@');
+ if (domain == NULL) return s;
+ if (domain - s > sizeof(var_buffer) - 1)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT
+ " in string expansion", sizeof(var_buffer));
+ Ustrncpy(var_buffer, s, domain - s);
+ var_buffer[domain - s] = 0;
+ return var_buffer;
case vtype_domain: /* Get domain from address */
- s = *((uschar **)(val));
- if (s == NULL) return US"";
- domain = Ustrrchr(s, '@');
- return (domain == NULL)? US"" : domain + 1;
+ s = *((uschar **)(val));
+ if (s == NULL) return US"";
+ domain = Ustrrchr(s, '@');
+ return (domain == NULL)? US"" : domain + 1;
case vtype_msgheaders:
- return find_header(NULL, exists_only, newsize, FALSE, NULL);
+ return find_header(NULL, exists_only, newsize, FALSE, NULL);
case vtype_msgheaders_raw:
- return find_header(NULL, exists_only, newsize, TRUE, NULL);
+ return find_header(NULL, exists_only, newsize, TRUE, NULL);
case vtype_msgbody: /* Pointer to msgbody string */
case vtype_msgbody_end: /* Ditto, the end of the msg */
- ss = (uschar **)(val);
- if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */
- {
- uschar *body;
- off_t start_offset = SPOOL_DATA_START_OFFSET;
- int len = message_body_visible;
- if (len > message_size) len = message_size;
- *ss = body = store_malloc(len+1);
- body[0] = 0;
- if (vp->type == vtype_msgbody_end)
+ ss = (uschar **)(val);
+ if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */
{
- struct stat statbuf;
- if (fstat(deliver_datafile, &statbuf) == 0)
+ uschar *body;
+ off_t start_offset = SPOOL_DATA_START_OFFSET;
+ int len = message_body_visible;
+ if (len > message_size) len = message_size;
+ *ss = body = store_malloc(len+1);
+ body[0] = 0;
+ if (vp->type == vtype_msgbody_end)
{
- start_offset = statbuf.st_size - len;
- if (start_offset < SPOOL_DATA_START_OFFSET)
- start_offset = SPOOL_DATA_START_OFFSET;
- }
- }
- lseek(deliver_datafile, start_offset, SEEK_SET);
- len = read(deliver_datafile, body, len);
- if (len > 0)
- {
- body[len] = 0;
- if (message_body_newlines) /* Separate loops for efficiency */
- {
- while (len > 0)
- { if (body[--len] == 0) body[len] = ' '; }
+ struct stat statbuf;
+ 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;
+ }
}
- else
+ if (lseek(deliver_datafile, start_offset, SEEK_SET) < 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "deliver_datafile lseek: %s",
+ strerror(errno));
+ len = read(deliver_datafile, body, len);
+ if (len > 0)
{
- while (len > 0)
- { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; }
+ body[len] = 0;
+ if (message_body_newlines) /* Separate loops for efficiency */
+ while (len > 0)
+ { if (body[--len] == 0) body[len] = ' '; }
+ else
+ while (len > 0)
+ { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; }
}
}
- }
- return (*ss == NULL)? US"" : *ss;
+ return (*ss == NULL)? US"" : *ss;
case vtype_todbsdin: /* BSD inbox time of day */
- return tod_stamp(tod_bsdin);
+ return tod_stamp(tod_bsdin);
case vtype_tode: /* Unix epoch time of day */
- return tod_stamp(tod_epoch);
+ return tod_stamp(tod_epoch);
case vtype_todel: /* Unix epoch/usec time of day */
- return tod_stamp(tod_epoch_l);
+ return tod_stamp(tod_epoch_l);
case vtype_todf: /* Full time of day */
- return tod_stamp(tod_full);
+ return tod_stamp(tod_full);
case vtype_todl: /* Log format time of day */
- return tod_stamp(tod_log_bare); /* (without timezone) */
+ return tod_stamp(tod_log_bare); /* (without timezone) */
case vtype_todzone: /* Time zone offset only */
- return tod_stamp(tod_zone);
+ return tod_stamp(tod_zone);
case vtype_todzulu: /* Zulu time */
- return tod_stamp(tod_zulu);
+ return tod_stamp(tod_zulu);
case vtype_todlf: /* Log file datestamp tod */
- return tod_stamp(tod_log_datestamp_daily);
+ return tod_stamp(tod_log_datestamp_daily);
case vtype_reply: /* Get reply address */
- s = find_header(US"reply-to:", exists_only, newsize, TRUE,
- headers_charset);
- if (s != NULL) while (isspace(*s)) s++;
- if (s == NULL || *s == 0)
- {
- *newsize = 0; /* For the *s==0 case */
- s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset);
- }
- if (s != NULL)
- {
- uschar *t;
- while (isspace(*s)) s++;
- for (t = s; *t != 0; t++) if (*t == '\n') *t = ' ';
- while (t > s && isspace(t[-1])) t--;
- *t = 0;
- }
- return (s == NULL)? US"" : s;
+ s = find_header(US"reply-to:", exists_only, newsize, TRUE,
+ headers_charset);
+ if (s != NULL) while (isspace(*s)) s++;
+ if (s == NULL || *s == 0)
+ {
+ *newsize = 0; /* For the *s==0 case */
+ s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset);
+ }
+ if (s != NULL)
+ {
+ uschar *t;
+ while (isspace(*s)) s++;
+ for (t = s; *t != 0; t++) if (*t == '\n') *t = ' ';
+ while (t > s && isspace(t[-1])) t--;
+ *t = 0;
+ }
+ return (s == NULL)? US"" : s;
case vtype_string_func:
{
return var_buffer;
case vtype_cert:
- return *(void **)val ? US"<cert>" : US"";
+ return *(void **)val ? US"<cert>" : US"";
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
case vtype_dkim:
- return dkim_exim_expand_query((int)(long)val);
- #endif
+ return dkim_exim_expand_query((int)(long)val);
+#endif
}
Sub array will be corrupted on return.
Returns: OK access is granted by an ACCEPT verb
- DISCARD access is granted by a DISCARD verb
+ DISCARD access is (apparently) granted by a DISCARD verb
FAIL access is denied
FAIL_DROP access is denied; drop the connection
DEFER can't tell at the moment
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++)
{
uschar * tmp = acl_arg[i];
/* 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
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 "
case 3: return NULL;
}
- *resetok = FALSE;
- if (yield != NULL) switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg))
+ *resetok = FALSE; /* eval_acl() might allocate; do not reclaim */
+ if (yield != NULL) switch(eval_acl(sub, nelem(sub), &user_msg))
{
case OK:
cond = TRUE;
case DEFER:
expand_string_forcedfail = TRUE;
+ /*FALLTHROUGH*/
default:
expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]);
return NULL;
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
if (sublen == 24)
{
- uschar *coded = auth_b64encode((uschar *)digest, 16);
+ uschar *coded = b64encode((uschar *)digest, 16);
DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+5);
tempcond = (Ustrcmp(coded, sub[1]+5) == 0);
if (sublen == 28)
{
- uschar *coded = auth_b64encode((uschar *)digest, 20);
+ uschar *coded = b64encode((uschar *)digest, 20);
DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+6);
tempcond = (Ustrcmp(coded, sub[1]+6) == 0);
#define XSTR(s) STR(s)
DEBUG(D_auth) debug_printf("crypteq: using %s()\n"
" subject=%s\n crypted=%s\n",
- (which == 0)? XSTR(DEFAULT_CRYPT) : (which == 1)? "crypt" : "crypt16",
+ which == 0 ? XSTR(DEFAULT_CRYPT) : which == 1 ? "crypt" : "crypt16",
coded, sub[1]);
#undef STR
#undef XSTR
salt), force failure. Otherwise we get false positives: with an empty
string the yield of crypt() is an empty string! */
- tempcond = (Ustrlen(sub[1]) < 2)? FALSE :
- (Ustrcmp(coded, sub[1]) == 0);
+ if (coded)
+ tempcond = Ustrlen(sub[1]) < 2 ? FALSE : Ustrcmp(coded, sub[1]) == 0;
+ else if (errno == EINVAL)
+ tempcond = FALSE;
+ else
+ {
+ expand_string_message = string_sprintf("crypt error: %s\n",
+ US strerror(errno));
+ return NULL;
+ }
}
break;
#endif /* SUPPORT_CRYPTEQ */
yieldptr points to the output string pointer
sizeptr points to the output string size
ptrptr points to the output string pointer
- type "lookup" or "if" or "extract" or "run", for error message
+ type "lookup", "if", "extract", "run", "env", "listextract" or
+ "certextract" for error message
resetok if not NULL, pointer to flag - write FALSE if unsafe to reset
the store.
}
else
{
- if (yes && lookup_value != NULL)
+ if (yes && lookup_value)
*yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, lookup_value,
Ustrlen(lookup_value));
lookup_value = save_lookup;
if (yes)
*yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, sub1, Ustrlen(sub1));
-/* If this is called from a lookup or an extract, we want to restore $value to
-what it was at the start of the item, so that it has this value during the
-second string expansion. For the call from "if" or "run" to this function,
-save_lookup is set to lookup_value, so that this statement does nothing. */
+/* If this is called from a lookup/env or a (cert)extract, we want to restore
+$value to what it was at the start of the item, so that it has this value
+during the second string expansion. For the call from "if" or "run" to this
+function, save_lookup is set to lookup_value, so that this statement does
+nothing. */
lookup_value = save_lookup;
return NULL;
hash_source = string_cat(NULL,&size,&offset,key_num,1);
-string_cat(hash_source,&size,&offset,daystamp,3);
-string_cat(hash_source,&size,&offset,address,Ustrlen(address));
+hash_source = string_cat(hash_source,&size,&offset,daystamp,3);
+hash_source = string_cat(hash_source,&size,&offset,address,Ustrlen(address));
hash_source[offset] = '\0';
DEBUG(D_expand) debug_printf("prvs: hash source is '%s'\n", hash_source);
* Join a file onto the output string *
*************************************************/
-/* This is used for readfile and after a run expansion. It joins the contents
-of a file onto the output string, globally replacing newlines with a given
-string (optionally). The file is closed at the end.
+/* This is used for readfile/readsock and after a run expansion.
+It joins the contents of a file onto the output string, globally replacing
+newlines with a given string (optionally).
Arguments:
f the FILE
static uschar *
cat_file(FILE *f, uschar *yield, int *sizep, int *ptrp, uschar *eol)
{
-int eollen;
+int eollen = eol ? Ustrlen(eol) : 0;
uschar buffer[1024];
-eollen = (eol == NULL)? 0 : Ustrlen(eol);
-
-while (Ufgets(buffer, sizeof(buffer), f) != NULL)
+while (Ufgets(buffer, sizeof(buffer), f))
{
int len = Ustrlen(buffer);
- if (eol != NULL && buffer[len-1] == '\n') len--;
+ if (eol && buffer[len-1] == '\n') len--;
yield = string_cat(yield, sizep, ptrp, buffer, len);
if (buffer[len] != 0)
yield = string_cat(yield, sizep, ptrp, eol, eollen);
}
-if (yield != NULL) yield[*ptrp] = 0;
+if (yield) yield[*ptrp] = 0;
return yield;
}
{
uschar *s = *sptr;
int_eximarith_t x = eval_op_mult(&s, decimal, error);
-if (*error == NULL)
+if (!*error)
{
while (*s == '+' || *s == '-')
{
int op = *s++;
int_eximarith_t y = eval_op_mult(&s, decimal, error);
- if (*error != NULL) break;
+ if (*error) break;
+ if ( (x >= EXIM_ARITH_MAX/2 && x >= EXIM_ARITH_MAX/2)
+ || (x <= -(EXIM_ARITH_MAX/2) && y <= -(EXIM_ARITH_MAX/2)))
+ { /* over-conservative check */
+ *error = op == '+'
+ ? US"overflow in sum" : US"overflow in difference";
+ break;
+ }
if (op == '+') x += y; else x -= y;
}
}
/* Variable */
- else
+ else if (!(value = find_variable(name, FALSE, skipping, &newsize)))
{
- value = find_variable(name, FALSE, skipping, &newsize);
- if (value == NULL)
- {
- expand_string_message =
- string_sprintf("unknown variable name \"%s\"", name);
- check_variable_error_message(name);
- goto EXPAND_FAILED;
- }
+ expand_string_message =
+ string_sprintf("unknown variable name \"%s\"", name);
+ check_variable_error_message(name);
+ goto EXPAND_FAILED;
}
/* If the data is known to be in a new buffer, newsize will be set to the
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 DEFER:
expand_string_forcedfail = TRUE;
+ /*FALLTHROUGH*/
default:
expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]);
goto EXPAND_FAILED;
continue;
}
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
case EITEM_IMAPFOLDER:
{ /* ${imapfolder {name}{sep]{specials}} */
uschar *sub_arg[3];
uschar *encoded;
- switch(read_subs(sub_arg, 3, 1, &s, skipping, TRUE, name, &resetok))
+ switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
+ &resetok))
{
case 1: goto EXPAND_FAILED_CURLY;
case 2:
if (sub_arg[1] == NULL) /* One argument */
{
- sub_arg[1] = "/"; /* default separator */
+ sub_arg[1] = US"/"; /* default separator */
sub_arg[2] = NULL;
}
- else if (sub_arg[2] == NULL) /* Two arguments */
- sub_arg[2] = NULL;
-
- if (Ustrlen(sub_arg[1]) != 1)
+ else if (Ustrlen(sub_arg[1]) != 1)
{
- expand_string_message =
+ expand_string_message =
string_sprintf(
- "IMAP folder separator must be one character, found \"%s\"",
+ "IMAP folder separator must be one character, found \"%s\"",
sub_arg[1]);
goto EXPAND_FAILED;
}
const uschar **argv;
pid_t pid;
int fd_in, fd_out;
- int lsize = 0;
- int lptr = 0;
+ int lsize = 0, lptr = 0;
if ((expand_forbid & RDO_RUN) != 0)
{
NULL, /* no transporting address */
US"${run} expansion", /* for error messages */
&expand_string_message)) /* where to put error message */
- {
goto EXPAND_FAILED;
- }
/* Create the child process, making it a group leader. */
- pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE);
-
- if (pid < 0)
+ if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE)) < 0)
{
expand_string_message =
string_sprintf("couldn't create child process: %s", strerror(errno));
/* Read the pipe to get the command's output into $value (which is kept
in lookup_value). Read during execution, so that if the output exceeds
- the OS pipe buffer limit, we don't block forever. */
+ the OS pipe buffer limit, we don't block forever. Remember to not release
+ memory just allocated for $value. */
+ resetok = FALSE;
f = fdopen(fd_out, "rb");
sigalrm_seen = FALSE;
alarm(60);
- lookup_value = cat_file(f, lookup_value, &lsize, &lptr, NULL);
+ lookup_value = cat_file(f, NULL, &lsize, &lptr, NULL);
alarm(0);
(void)fclose(f);
{
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 save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
- /* Read the arguments */
+ /* While skipping we cannot rely on the data for expansions being
+ available (eg. $item) hence cannot decide on numeric vs. keyed.
+ Just read as many arguments as there are. */
- for (i = 0; i < j; i++)
+ if (skipping)
+ {
+ while (isspace(*s)) s++;
+ while (*s == '{')
+ {
+ if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
+ goto EXPAND_FAILED; /*{*/
+ if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ while (isspace(*s)) s++;
+ }
+ if (*s != '}')
+ goto EXPAND_FAILED_CURLY;
+ }
+
+ else for (i = 0; i < j; i++) /* Read the proper number of arguments */
{
while (isspace(*s)) s++;
if (*s == '{') /*}*/
while (len > 0 && isspace(p[len-1])) len--;
p[len] = 0;
- if (!skipping)
+ if (*p == 0)
{
- if (*p == 0)
- {
- expand_string_message = US"first argument of \"extract\" must "
- "not be empty";
- goto EXPAND_FAILED;
- }
+ 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;
}
}
}
&yield, /* output pointer */
&size, /* output size */
&ptr, /* output current point */
- US"extract", /* condition type */
+ US"listextract", /* condition type */
&resetok))
{
case 1: goto EXPAND_FAILED; /* when all is well, the */
&yield, /* output pointer */
&size, /* output size */
&ptr, /* output current point */
- US"extract", /* condition type */
+ US"certextract", /* condition type */
&resetok))
{
case 1: goto EXPAND_FAILED; /* when all is well, the */
#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_MD5:
case EOP_SHA1:
case EOP_SHA256:
+ case EOP_BASE64:
if (s[1] == '$')
{
const uschar * s1 = s;
}
}
- enc = auth_b64encode(sub, out - sub);
+ enc = b64encode(sub, out - sub);
yield = string_cat(yield, &size, &ptr, enc, Ustrlen(enc));
continue;
}
continue;
}
+ case EOP_IPV6NORM:
+ case EOP_IPV6DENORM:
+ {
+ int type = string_is_ip_address(sub, NULL);
+ int binary[4];
+ uschar buffer[44];
+
+ switch (type)
+ {
+ case 6:
+ (void) host_aton(sub, binary);
+ break;
+
+ case 4: /* convert to IPv4-mapped IPv6 */
+ binary[0] = binary[1] = 0;
+ binary[2] = 0x0000ffff;
+ (void) host_aton(sub, binary+3);
+ break;
+
+ case 0:
+ expand_string_message =
+ string_sprintf("\"%s\" is not an IP address", sub);
+ goto EXPAND_FAILED;
+ }
+
+ yield = string_cat(yield, &size, &ptr, buffer,
+ c == EOP_IPV6NORM
+ ? ipv6_nmtoa(binary, buffer)
+ : host_nmtoa(4, binary, -1, buffer, ':')
+ );
+ continue;
+ }
+
case EOP_ADDRESS:
case EOP_LOCAL_PART:
case EOP_DOMAIN:
}
/* replace illegal UTF-8 sequences by replacement character */
-
+
#define UTF8_REPLACEMENT_CHAR US"?"
case EOP_UTF8CLEAN:
int bytes_left = 0;
long codepoint = -1;
uschar seq_buff[4]; /* accumulate utf-8 here */
-
+
while (*sub != 0)
{
int complete = 0;
continue;
}
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
case EOP_UTF8_DOMAIN_TO_ALABEL:
{
uschar * error = NULL;
/* Convert string to base64 encoding */
case EOP_STR2B64:
+ case EOP_BASE64:
+ {
+#ifdef SUPPORT_TLS
+ uschar * s = vp && *(void **)vp->value
+ ? tls_cert_der_b64(*(void **)vp->value)
+ : b64encode(sub, Ustrlen(sub));
+#else
+ uschar * s = b64encode(sub, Ustrlen(sub));
+#endif
+ yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
+ continue;
+ }
+
+ case EOP_BASE64D:
{
- uschar *encstr = auth_b64encode(sub, Ustrlen(sub));
- yield = string_cat(yield, &size, &ptr, encstr, Ustrlen(encstr));
+ 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, &size, &ptr, s, Ustrlen(s));
continue;
}
{
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)
if (Ustrspn(argv[i], "abcdefghijklmnopqrtsuvwxyz0123456789-.:/") ==
Ustrlen(argv[i]))
{
- #ifdef LOOKUP_LDAP
+#ifdef LOOKUP_LDAP
eldap_default_servers = argv[i];
- #endif
- #ifdef LOOKUP_MYSQL
+#endif
+#ifdef LOOKUP_MYSQL
mysql_servers = argv[i];
- #endif
- #ifdef LOOKUP_PGSQL
+#endif
+#ifdef LOOKUP_PGSQL
pgsql_servers = argv[i];
- #endif
- #ifdef EXPERIMENTAL_REDIS
+#endif
+#ifdef LOOKUP_REDIS
redis_servers = argv[i];
- #endif
+#endif
}
- #ifdef EXIM_PERL
+#ifdef EXIM_PERL
else opt_perl_startup = argv[i];
- #endif
+#endif
}
printf("Testing string expansion: debug_level = %d\n\n", debug_level);