US"base62d",
US"base64",
US"base64d",
+ US"bless",
US"domain",
US"escape",
US"escape8bit",
EOP_BASE62D,
EOP_BASE64,
EOP_BASE64D,
+ EOP_BLESS,
EOP_DOMAIN,
EOP_ESCAPE,
EOP_ESCAPE8BIT,
static uschar * fn_recipients(void);
typedef uschar * stringptr_fn_t(void);
+static uschar * fn_queue_size(void);
/* This table must be kept in alphabetical order. */
{ "local_part", vtype_stringptr, &deliver_localpart },
{ "local_part_data", vtype_stringptr, &deliver_localpart_data },
{ "local_part_prefix", vtype_stringptr, &deliver_localpart_prefix },
+ { "local_part_prefix_v", vtype_stringptr, &deliver_localpart_prefix_v },
{ "local_part_suffix", vtype_stringptr, &deliver_localpart_suffix },
+ { "local_part_suffix_v", vtype_stringptr, &deliver_localpart_suffix_v },
{ "local_part_verified", vtype_stringptr, &deliver_localpart_verified },
#ifdef HAVE_LOCAL_SCAN
{ "local_scan_data", vtype_stringptr, &local_scan_data },
{ "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 },
{ "rcpt_count", vtype_int, &rcpt_count },
{ "rcpt_defer_count", vtype_int, &rcpt_defer_count },
{ "rcpt_fail_count", vtype_int, &rcpt_fail_count },
expand_getkeyed(uschar * key, const uschar * s)
{
int length = Ustrlen(key);
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
/* Loop to search for the key */
while (*s && *s != '=' && !isspace(*s)) s++;
dkeylength = s - dkey;
- while (isspace(*s)) s++;
- if (*s == '=') while (isspace((*(++s))));
+ if (Uskip_whitespace(&s) == '=') while (isspace((*(++s))));
data = string_dequote(&s);
if (length == dkeylength && strncmpic(key, dkey, length) == 0)
return data;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
}
return NULL;
/* Trim the header roughly if we're approaching limits */
inc = t - s;
- if ((g ? g->ptr : 0) + inc > header_insert_maxlen)
- inc = header_insert_maxlen - (g ? g->ptr : 0);
+ if (gstring_length(g) + inc > header_insert_maxlen)
+ inc = header_insert_maxlen - gstring_length(g);
/* For raw just copy the data; for a list, add the data as a colon-sep
list-element; for comma-list add as an unchecked comma,newline sep
if (flags & FH_WANT_LIST)
g = string_append_listele_n(g, ':', s, (unsigned)inc);
else if (flags & FH_WANT_RAW)
- {
g = string_catn(g, s, (unsigned)inc);
- (void) string_from_gstring(g);
- }
else if (inc > 0)
- if (comma)
- g = string_append2_listele_n(g, US",\n", s, (unsigned)inc);
- else
- g = string_append2_listele_n(g, US"\n", s, (unsigned)inc);
+ g = string_append2_listele_n(g, comma ? US",\n" : US"\n",
+ s, (unsigned)inc);
- if (g && g->ptr >= header_insert_maxlen) break;
+ if (gstring_length(g) >= header_insert_maxlen) break;
}
if (!found) return NULL; /* No header found */
*newsize = g->size;
if (flags & FH_WANT_RAW)
- return g->s;
+ return string_from_gstring(g);
/* Otherwise do RFC 2047 decoding, translating the charset if requested.
The rfc2047_decode2() function can return an error with decoded data if the
else
{
- uschar *decoded, *error;
-
- decoded = rfc2047_decode2(g->s, check_rfc2047_length, charset, '?', NULL,
- newsize, &error);
+ uschar * error, * decoded = rfc2047_decode2(string_from_gstring(g),
+ check_rfc2047_length, charset, '?', NULL, newsize, &error);
if (error)
- {
DEBUG(D_any) debug_printf("*** error in RFC 2047 decoding: %s\n"
" input was: %s\n", error, g->s);
- }
- return decoded ? decoded : g->s;
+ return decoded ? decoded : string_from_gstring(g);
}
}
}
+/*************************************************
+* Return size of queue *
+*************************************************/
+/* Ask the daemon for the queue size */
+
+static uschar *
+fn_queue_size(void)
+{
+struct sockaddr_un sa_un = {.sun_family = AF_UNIX};
+uschar buf[16];
+int fd;
+ssize_t len;
+const uschar * where;
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+uschar * sname;
+#endif
+fd_set fds;
+struct timeval tv;
+
+if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
+ {
+ DEBUG(D_expand) debug_printf(" socket: %s\n", strerror(errno));
+ return NULL;
+ }
+
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
+len = offsetof(struct sockaddr_un, sun_path) + 1
+ + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "exim_%d", getpid());
+#else
+sname = string_sprintf("%s/p_%d", spool_directory, getpid());
+len = offsetof(struct sockaddr_un, sun_path)
+ + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", sname);
+#endif
+
+if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0)
+ { where = US"bind"; goto bad; }
+
+#ifdef notdef
+debug_printf("local addr '%s%s'\n",
+ *sa_un.sun_path ? "" : "@",
+ sa_un.sun_path + (*sa_un.sun_path ? 0 : 1));
+#endif
+
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
+len = offsetof(struct sockaddr_un, sun_path) + 1
+ + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s",
+ expand_string(notifier_socket));
+#else
+len = offsetof(struct sockaddr_un, sun_path)
+ + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s",
+ expand_string(notifier_socket));
+#endif
+
+if (connect(fd, (const struct sockaddr *)&sa_un, len) < 0)
+ { where = US"connect"; goto bad2; }
+
+buf[0] = NOTIFY_QUEUE_SIZE_REQ;
+if (send(fd, buf, 1, 0) < 0) { where = US"send"; goto bad; }
+
+FD_ZERO(&fds); FD_SET(fd, &fds);
+tv.tv_sec = 2; tv.tv_usec = 0;
+if (select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tv) != 1)
+ {
+ DEBUG(D_expand) debug_printf("no daemon response; using local evaluation\n");
+ len = snprintf(CS buf, sizeof(buf), "%u", queue_count_cached());
+ }
+else if ((len = recv(fd, buf, sizeof(buf), 0)) < 0)
+ { where = US"recv"; goto bad2; }
+
+close(fd);
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+Uunlink(sname);
+#endif
+return string_copyn(buf, len);
+
+bad2:
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+ Uunlink(sname);
+#endif
+bad:
+ close(fd);
+ DEBUG(D_expand) debug_printf(" %s: %s\n", where, strerror(errno));
+ return NULL;
+}
+
+
/*************************************************
* Find value of a variable *
*************************************************/
s = find_header(US"reply-to:", newsize,
exists_only ? FH_EXISTS_ONLY|FH_WANT_RAW : FH_WANT_RAW,
headers_charset);
- if (s) while (isspace(*s)) s++;
+ if (s) Uskip_whitespace(&s);
if (!s || !*s)
{
*newsize = 0; /* For the *s==0 case */
if (s)
{
uschar *t;
- while (isspace(*s)) s++;
- for (t = s; *t != 0; t++) if (*t == '\n') *t = ' ';
+ Uskip_whitespace(&s);
+ for (t = s; *t; t++) if (*t == '\n') *t = ' ';
while (t > s && isspace(t[-1])) t--;
*t = 0;
}
{
const uschar *s = *sptr;
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
for (int i = 0; i < n; i++)
{
if (*s != '{')
if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok)))
return 3;
if (*s++ != '}') return 1;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
}
if (check_end && *s++ != '}')
{
unsigned depth = 0;
BOOL quotesmode = wrap[0] == wrap[1];
-while (isspace(*p)) p++;
-
-if (*p == *wrap)
+if (Uskip_whitespace(&p) == *wrap)
{
s = ++p;
wrap++;
break;
case ECOND_MATCH: /* Regular expression match */
- if (!(re = pcre_compile(CS sub[1], PCRE_COPT, (const char **)&rerror,
+ if (!(re = pcre_compile(CS sub[1], PCRE_COPT, CCSS &rerror,
&roffset, NULL)))
{
expand_string_message = string_sprintf("regular expression error in "
int_eximarith_t n;
uschar *s = *sptr;
-while (isspace(*s)) s++;
-if (isdigit((c = *s)))
+if (isdigit((c = Uskip_whitespace(&s))))
{
int count;
(void)sscanf(CS s, (decimal? SC_EXIM_DEC "%n" : SC_EXIM_ARITH "%n"), &n, &count);
case 'm': n *= 1024*1024; s++; break;
case 'g': n *= 1024*1024*1024; s++; break;
}
- while (isspace (*s)) s++;
+ Uskip_whitespace(&s);
}
else if (c == '(')
{
{
uschar *s = *sptr;
int_eximarith_t x;
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
if (*s == '+' || *s == '-' || *s == '~')
{
int op = *s++;
int expand_setup = 0;
int nameptr = 0;
uschar *key, *filename;
- const uschar *affix;
+ const uschar * affix, * opts;
uschar *save_lookup_value = lookup_value;
int save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
kinds. Allow everything except space or { to appear; the actual content
is checked by search_findtype_partial. */ /*}*/
- while (*s != 0 && *s != '{' && !isspace(*s)) /*}*/
+ while (*s && *s != '{' && !isspace(*s)) /*}*/
{
if (nameptr < sizeof(name) - 1) name[nameptr++] = *s;
s++;
}
- name[nameptr] = 0;
+ name[nameptr] = '\0';
while (isspace(*s)) s++;
/* Now check for the individual search type and any partial or default
options. Only those types that are actually in the binary are valid. */
- stype = search_findtype_partial(name, &partial, &affix, &affixlen,
- &starflags);
- if (stype < 0)
+ if ((stype = search_findtype_partial(name, &partial, &affix, &affixlen,
+ &starflags, &opts)) < 0)
{
expand_string_message = search_error_message;
goto EXPAND_FAILED;
if (mac_islookup(stype, lookup_querystyle))
filename = NULL;
else
- {
- if (*filename != '/')
- {
- expand_string_message = string_sprintf(
- "absolute file name expected for \"%s\" lookup", name);
- goto EXPAND_FAILED;
- }
- while (*key != 0 && !isspace(*key)) key++;
- if (*key != 0) *key++ = 0;
- }
+ if (*filename == '/')
+ {
+ while (*key && !isspace(*key)) key++;
+ if (*key) *key++ = '\0';
+ }
+ else
+ filename = NULL;
}
/* If skipping, don't do the next bit - just lookup_value == NULL, as if
goto EXPAND_FAILED;
}
lookup_value = search_find(handle, filename, key, partial, affix,
- affixlen, starflags, &expand_setup);
+ affixlen, starflags, &expand_setup, opts);
if (f.search_find_defer)
{
expand_string_message =
{
client_conn_ctx cctx;
int timeout = 5;
- int save_ptr = yield->ptr;
+ int save_ptr = gstring_length(yield);
FILE * fp = NULL;
uschar * arg;
uschar * sub_arg[4];
uschar * item;
int sep = 0;
- item = string_nextinlist(&list, &sep, NULL, 0);
- if ((timeout = readconf_readtime(item, 0, FALSE)) < 0)
+ if ( !(item = string_nextinlist(&list, &sep, NULL, 0))
+ || !*item
+ || (timeout = readconf_readtime(item, 0, FALSE)) < 0)
{
expand_string_message = string_sprintf("bad time value %s", item);
goto EXPAND_FAILED;
if (sigalrm_seen)
{
- yield->ptr = save_ptr;
+ if (yield) yield->ptr = save_ptr;
expand_string_message = US "socket read timed out";
goto SOCK_FAIL;
}
/* Create the child process, making it a group leader. */
- if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE)) < 0)
+ if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE,
+ US"expand-run")) < 0)
{
expand_string_message =
string_sprintf("couldn't create child process: %s", strerror(errno));
case EITEM_TR:
{
- int oldptr = yield->ptr;
+ int oldptr = gstring_length(yield);
int o2m;
uschar *sub[3];
/* Compile the regular expression */
- if (!(re = pcre_compile(CS sub[1], PCRE_COPT, (const char **)&rerror,
+ if (!(re = pcre_compile(CS sub[1], PCRE_COPT, CCSS &rerror,
&roffset, NULL)))
{
expand_string_message = string_sprintf("regular expression error in "
case EITEM_REDUCE:
{
int sep = 0;
- int save_ptr = yield->ptr;
+ int save_ptr = gstring_length(yield);
uschar outsep[2] = { '\0', '\0' };
const uschar *list, *expr, *temp;
uschar *save_iterate_item = iterate_item;
item of the output list, add in a space if the new item begins with the
separator character, or is an empty string. */
- if (yield->ptr != save_ptr && (temp[0] == *outsep || temp[0] == 0))
+ if ( yield && yield->ptr != save_ptr
+ && (temp[0] == *outsep || temp[0] == 0))
yield = string_catn(yield, US" ", 1);
/* Add the string in "temp" to the output list that we are building,
the redundant final separator. Even though an empty item at the end of a
list does not count, this is tidier. */
- else if (yield->ptr != save_ptr) yield->ptr--;
+ else if (yield && yield->ptr != save_ptr) yield->ptr--;
/* Restore preserved $item */
continue;
}
+ case EOP_BLESS:
+ /* This is purely for the convenience of the test harness. Do not enable
+ it otherwise as it defeats the taint-checking security. */
+
+ if (f.running_in_test_harness)
+ yield = string_cat(yield, is_tainted(sub)
+ ? string_copy_taint(sub, FALSE) : sub);
+ else
+ {
+ DEBUG(D_expand) debug_printf_indent("bless operator not supported\n");
+ yield = string_cat(yield, sub);
+ }
+ continue;
+
case EOP_EXPAND:
{
uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok);
{
uschar outsep[2] = { ':', '\0' };
uschar *address, *error;
- int save_ptr = yield->ptr;
+ int save_ptr = gstring_length(yield);
int start, end, domain; /* Not really used */
while (isspace(*sub)) sub++;
if (address)
{
- if (yield->ptr != save_ptr && address[0] == *outsep)
+ if (yield && yield->ptr != save_ptr && address[0] == *outsep)
yield = string_catn(yield, US" ", 1);
for (;;)
/* If we have generated anything, remove the redundant final
separator. */
- if (yield->ptr != save_ptr) yield->ptr--;
+ if (yield && yield->ptr != save_ptr) yield->ptr--;
f.parse_allow_group = FALSE;
continue;
}
case EOP_QUOTE_LOCAL_PART:
if (!arg)
{
- BOOL needs_quote = (*sub == 0); /* TRUE for empty string */
+ BOOL needs_quote = (!*sub); /* TRUE for empty string */
uschar *t = sub - 1;
if (c == EOP_QUOTE)
case EOP_FROM_UTF8:
{
- while (*sub != 0)
+ uschar * buff = store_get(4, is_tainted(sub));
+ while (*sub)
{
int c;
- uschar buff[4];
GETUTF8INC(c, sub);
if (c > 255) c = '_';
buff[0] = c;
continue;
}
- /* replace illegal UTF-8 sequences by replacement character */
+ /* replace illegal UTF-8 sequences by replacement character */
#define UTF8_REPLACEMENT_CHAR US"?"
int complete;
uschar seq_buff[4]; /* accumulate utf-8 here */
- while (*sub != 0)
+ /* Manually track tainting, as we deal in individual chars below */
+
+ if (is_tainted(sub))
+ if (yield->s && yield->ptr)
+ gstring_rebuffer(yield);
+ else
+ yield->s = store_get(yield->size = Ustrlen(sub), TRUE);
+
+ /* Check the UTF-8, byte-by-byte */
+
+ while (*sub)
{
complete = 0;
uschar c = *sub++;
}
else /* no bytes left: new sequence */
{
- if((c & 0x80) == 0) /* 1-byte sequence, US-ASCII, keep it */
+ if(!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */
{
yield = string_catn(yield, &c, 1);
continue;
* Eg, ${length_1:フィル} is one byte, not one character, so we expect
* ${utf8clean:${length_1:フィル}} to yield '?' */
if (bytes_left != 0)
- {
yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1);
- }
+
continue;
}
{
int fd, off = 0, len;
-if ((fd = open(CS filename, O_RDONLY)) < 0)
+if ((fd = exim_open2(CS filename, O_RDONLY)) < 0)
{
log_write(0, LOG_MAIN | LOG_PANIC, "unable to open file for reading: %s",
filename);