{ "regex_match_string", vtype_stringptr, ®ex_match_string },
#endif
{ "reply_address", vtype_reply, NULL },
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ { "requiretls", vtype_bool, &tls_requiretls },
+#endif
{ "return_path", vtype_stringptr, &return_path },
{ "return_size_limit", vtype_int, &bounce_return_size_limit },
{ "router_name", vtype_stringptr, &router_name },
uschar *ss = expand_string(condition);
if (ss == NULL)
{
- if (!expand_string_forcedfail && !search_find_defer)
+ if (!f.expand_string_forcedfail && !f.search_find_defer)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand condition \"%s\" "
"for %s %s: %s", condition, m1, m2, expand_string_message);
return FALSE;
*/
static uschar *
-expand_getkeyed(uschar *key, const uschar *s)
+expand_getkeyed(uschar * key, const uschar * s)
{
int length = Ustrlen(key);
while (isspace(*s)) s++;
/* Loop to search for the key */
-while (*s != 0)
+while (*s)
{
int dkeylength;
- uschar *data;
- const uschar *dkey = s;
+ uschar * data;
+ const uschar * dkey = s;
- while (*s != 0 && *s != '=' && !isspace(*s)) s++;
+ while (*s && *s != '=' && !isspace(*s)) s++;
dkeylength = s - dkey;
while (isspace(*s)) s++;
if (*s == '=') while (isspace((*(++s))));
static uschar *
expand_getlistele(int field, const uschar * list)
{
-const uschar * tlist= list;
-int sep= 0;
+const uschar * tlist = list;
+int sep = 0;
uschar dummy;
-if(field<0)
+if (field < 0)
{
- for(field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++;
- sep= 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))) ;
+if (field == 0) return NULL;
+while (--field > 0 && (string_nextinlist(&list, &sep, &dummy, 1))) ;
return string_nextinlist(&list, &sep, NULL, 0);
}
expand_getcertele(uschar * field, uschar * certvar)
{
var_entry * vp;
-certfield * cp;
if (!(vp = find_var_ent(certvar)))
{
if (*field >= '0' && *field <= '9')
return tls_cert_ext_by_oid(*(void **)vp->value, field, 0);
-for(cp = certfields;
- cp < certfields + nelem(certfields);
- cp++)
+for (certfield * cp = certfields;
+ cp < certfields + nelem(certfields);
+ cp++)
if (Ustrncmp(cp->name, field, cp->namelen) == 0)
{
uschar * modifier = *(field += cp->namelen) == ','
BOOL found = !name;
int len = name ? Ustrlen(name) : 0;
BOOL comma = FALSE;
-header_line * h;
gstring * g = NULL;
-for (h = header_list; h; h = h->next)
+for (header_line * h = header_list; h; h = h->next)
if (h->type != htype_old && h->text) /* NULL => Received: placeholder */
if (!name || (len <= h->slen && strncmpic(name, h->text, len) == 0))
{
static gstring *
authres_local(gstring * g, const uschar * sysname)
{
-if (!authentication_local)
+if (!f.authentication_local)
return g;
g = string_append(g, 3, US";\n\tlocal=pass (non-smtp, ", sysname, US")");
if (authenticated_id) g = string_append(g, 2, " u=", authenticated_id);
g = string_catn(g, US";\n\tiprev=temperror", 19);
else if (host_lookup_failed)
g = string_catn(g, US";\n\tiprev=fail", 13);
-else
+else
return g;
if (sender_host_address)
- g = string_append(g, 2, US" smtp.client-ip=", sender_host_address);
+ g = string_append(g, 2, US" smtp.remote-ip=", sender_host_address);
return g;
}
{
uschar * s;
gstring * g = NULL;
-int i;
-if (!enable_dollar_recipients) return NULL;
+if (!f.enable_dollar_recipients) return NULL;
-for (i = 0; i < recipients_count; i++)
+for (int i = 0; i < recipients_count; i++)
{
s = recipients_list[i].address;
g = string_append2_listele_n(g, US", ", s, Ustrlen(s));
switch (vp->type)
{
case vtype_filter_int:
- if (!filter_running) return NULL;
+ if (!f.filter_running) return NULL;
/* Fall through */
/* VVVVVVVVVVVV */
case vtype_int:
case vtype_pspace:
{
int inodes;
- sprintf(CS var_buffer, "%d",
+ sprintf(CS var_buffer, PR_EXIM_ARITH,
receive_statvfs(val == (void *)TRUE, &inodes));
}
return var_buffer;
read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping,
BOOL check_end, uschar *name, BOOL *resetok)
{
-int i;
const uschar *s = *sptr;
while (isspace(*s)) s++;
-for (i = 0; i < n; i++)
+for (int i = 0; i < n; i++)
{
if (*s != '{')
{
/* first_delivery tests for first delivery attempt */
case ECOND_FIRST_DELIVERY:
- if (yield != NULL) *yield = deliver_firsttime == testfor;
+ if (yield != NULL) *yield = f.deliver_firsttime == testfor;
return s;
break;
case DEFER:
- expand_string_forcedfail = TRUE;
+ f.expand_string_forcedfail = TRUE;
/*FALLTHROUGH*/
default:
expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]);
case ECOND_STR_GE:
case ECOND_STR_GEI:
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
{
/* Sometimes, we don't expand substrings; too many insecure configurations
created using match_address{}{} and friends, where the second param
}
else if (sublen == 32)
{
- int i;
uschar coded[36];
- for (i = 0; i < 16; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
+ for (int i = 0; i < 16; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
coded[32] = 0;
DEBUG(D_auth) debug_printf("crypteq: using MD5+hex hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+5);
}
else if (sublen == 40)
{
- int i;
uschar coded[44];
- for (i = 0; i < 20; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
+ for (int i = 0; i < 20; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
coded[40] = 0;
DEBUG(D_auth) debug_printf("crypteq: using SHA1+hex hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+6);
static int
save_expand_strings(uschar **save_expand_nstring, int *save_expand_nlength)
{
-int i;
-for (i = 0; i <= expand_nmax; i++)
+for (int i = 0; i <= expand_nmax; i++)
{
save_expand_nstring[i] = expand_nstring[i];
save_expand_nlength[i] = expand_nlength[i];
restore_expand_strings(int save_expand_nmax, uschar **save_expand_nstring,
int *save_expand_nlength)
{
-int i;
expand_nmax = save_expand_nmax;
-for (i = 0; i <= expand_nmax; i++)
+for (int i = 0; i <= expand_nmax; i++)
{
expand_nstring[i] = save_expand_nstring[i];
expand_nlength[i] = save_expand_nlength[i];
be the case if we were already skipping). */
sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok);
-if (sub1 == NULL && (yes || !expand_string_forcedfail)) goto FAILED;
-expand_string_forcedfail = FALSE;
+if (sub1 == NULL && (yes || !f.expand_string_forcedfail)) goto FAILED;
+f.expand_string_forcedfail = FALSE;
if (*s++ != '}')
{
errwhere = US"'yes' part did not end with '}'";
if (*s == '{')
{
sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok);
- if (sub2 == NULL && (!yes || !expand_string_forcedfail)) goto FAILED;
- expand_string_forcedfail = FALSE;
+ if (sub2 == NULL && (!yes || !f.expand_string_forcedfail)) goto FAILED;
+ f.expand_string_forcedfail = FALSE;
if (*s++ != '}')
{
errwhere = US"'no' part did not start with '{'";
}
expand_string_message =
string_sprintf("\"%s\" failed and \"fail\" requested", type);
- expand_string_forcedfail = TRUE;
+ f.expand_string_forcedfail = TRUE;
goto FAILED;
}
}
{
gstring * hash_source;
uschar * p;
-int i;
hctx h;
uschar innerhash[20];
uschar finalhash[20];
memset(innerkey, 0x36, 64);
memset(outerkey, 0x5c, 64);
-for (i = 0; i < Ustrlen(key); i++)
+for (int i = 0; i < Ustrlen(key); i++)
{
innerkey[i] ^= key[i];
outerkey[i] ^= key[i];
chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash);
p = finalhash_hex;
-for (i = 0; i < 3; i++)
+for (int i = 0; i < 3; i++)
{
*p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
*p++ = hex_digits[finalhash[i] & 0x0f];
cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol)
{
int rc;
-uschar * s;
uschar buffer[1024];
+/*XXX could we read direct into a pre-grown string? */
+
while ((rc = tls_read(tls_ctx, buffer, sizeof(buffer))) > 0)
- for (s = buffer; rc--; s++)
+ for (uschar * s = buffer; rc--; s++)
yield = eol && *s == '\n'
? string_cat(yield, eol) : string_catn(yield, s, 1);
+/* Return pointer to dewrapped string, with enclosing specified chars removed.
+The given string is modified on return. Leading whitespace is skipped while
+looking for the opening wrap character, then the rest is scanned for the trailing
+(non-escaped) wrap character. A backslash in the string will act as an escape.
+
+A nul is written over the trailing wrap, and a pointer to the char after the
+leading wrap is returned.
+
+Arguments:
+ s String for de-wrapping
+ wrap Two-char string, the first being the opener, second the closer wrapping
+ character
+Return:
+ Pointer to de-wrapped string, or NULL on error (with expand_string_message set).
+*/
+
+static uschar *
+dewrap(uschar * s, const uschar * wrap)
+{
+uschar * p = s;
+unsigned depth = 0;
+BOOL quotesmode = wrap[0] == wrap[1];
+
+while (isspace(*p)) p++;
+
+if (*p == *wrap)
+ {
+ s = ++p;
+ wrap++;
+ while (*p)
+ {
+ if (*p == '\\') p++;
+ else if (!quotesmode && *p == wrap[-1]) depth++;
+ else if (*p == *wrap)
+ if (depth == 0)
+ {
+ *p = '\0';
+ return s;
+ }
+ else
+ depth--;
+ p++;
+ }
+ }
+expand_string_message = string_sprintf("missing '%c'", *wrap);
+return NULL;
+}
+
+
+/* Pull off the leading array or object element, returning
+a copy in an allocated string. Update the list pointer.
+
+The element may itself be an abject or array.
+*/
+
+uschar *
+json_nextinlist(const uschar ** list)
+{
+unsigned array_depth = 0, object_depth = 0;
+const uschar * s = *list, * item;
+
+while (isspace(*s)) s++;
+
+for (item = s;
+ *s && (*s != ',' || array_depth != 0 || object_depth != 0);
+ s++)
+ switch (*s)
+ {
+ case '[': array_depth++; break;
+ case ']': array_depth--; break;
+ case '{': object_depth++; break;
+ case '}': object_depth--; break;
+ }
+*list = *s ? s+1 : s;
+item = string_copyn(item, s - item);
+DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item);
+return US item;
+}
+
+
+
/*************************************************
* Expand string *
*************************************************/
expand_level++;
DEBUG(D_expand)
- debug_printf_indent(UTF8_DOWN_RIGHT "%s: %s\n",
- skipping
- ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning"
- : "considering",
- string);
+ DEBUG(D_noutf8)
+ debug_printf_indent("/%s: %s\n",
+ skipping ? "---scanning" : "considering", string);
+ else
+ debug_printf_indent(UTF8_DOWN_RIGHT "%s: %s\n",
+ skipping
+ ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning"
+ : "considering",
+ string);
-expand_string_forcedfail = FALSE;
+f.expand_string_forcedfail = FALSE;
expand_string_message = US"";
while (*s != 0)
continue;
case DEFER:
- expand_string_forcedfail = TRUE;
+ f.expand_string_forcedfail = TRUE;
/*FALLTHROUGH*/
default:
expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]);
if (next_s == NULL) goto EXPAND_FAILED; /* message already set */
DEBUG(D_expand)
- {
- debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
- "condition: %.*s\n",
- (int)(next_s - s), s);
- debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
- UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
- "result: %s\n",
- cond ? "true" : "false");
- }
+ DEBUG(D_noutf8)
+ {
+ debug_printf_indent("|--condition: %.*s\n", (int)(next_s - s), s);
+ debug_printf_indent("|-----result: %s\n", cond ? "true" : "false");
+ }
+ else
+ {
+ debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
+ "condition: %.*s\n",
+ (int)(next_s - s), s);
+ debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
+ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
+ "result: %s\n",
+ cond ? "true" : "false");
+ }
s = next_s;
}
lookup_value = search_find(handle, filename, key, partial, affix,
affixlen, starflags, &expand_setup);
- if (search_find_defer)
+ if (f.search_find_defer)
{
expand_string_message =
string_sprintf("lookup of \"%s\" gave DEFER: %s",
expand_string_message =
string_sprintf("Perl subroutine \"%s\" returned undef to force "
"failure", sub_arg[0]);
- expand_string_forcedfail = TRUE;
+ f.expand_string_forcedfail = TRUE;
}
goto EXPAND_FAILED;
}
/* Yield succeeded. Ensure forcedfail is unset, just in case it got
set during a callback from Perl. */
- expand_string_forcedfail = FALSE;
+ f.expand_string_forcedfail = FALSE;
yield = new_yield;
continue;
}
int fd;
int timeout = 5;
int save_ptr = yield->ptr;
- FILE *f;
+ FILE * fp;
uschar * arg;
uschar * sub_arg[4];
uschar * server_name = NULL;
port = ntohs(service_info->s_port);
}
+ /*XXX we trust that the request is idempotent. Hmm. */
fd = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
timeout, &host, &expand_string_message,
do_tls ? NULL : &reqstr);
sockun.sun_family = AF_UNIX;
sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
sub_arg[0]);
- server_name = sockun.sun_path;
+ server_name = US sockun.sun_path;
sigalrm_seen = FALSE;
- alarm(timeout);
+ ALARM(timeout);
rc = connect(fd, (struct sockaddr *)(&sockun), sizeof(sockun));
- alarm(0);
+ ALARM_CLR(0);
if (sigalrm_seen)
{
expand_string_message = US "socket connect timed out";
#endif
/* Allow sequencing of test actions */
- if (running_in_test_harness) millisleep(100);
+ if (f.running_in_test_harness) millisleep(100);
/* Write the request string, if not empty or already done */
if (!tls_ctx && do_shutdown) shutdown(fd, SHUT_WR);
#endif
- if (running_in_test_harness) millisleep(100);
+ if (f.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. */
if (!tls_ctx)
- f = fdopen(fd, "rb");
+ fp = fdopen(fd, "rb");
sigalrm_seen = FALSE;
- alarm(timeout);
+ ALARM(timeout);
yield =
#ifdef SUPPORT_TLS
tls_ctx ? cat_file_tls(tls_ctx, yield, sub_arg[3]) :
#endif
- cat_file(f, yield, sub_arg[3]);
- alarm(0);
+ cat_file(fp, yield, sub_arg[3]);
+ ALARM_CLR(0);
#ifdef SUPPORT_TLS
if (tls_ctx)
}
else
#endif
- (void)fclose(f);
+ (void)fclose(fp);
/* After a timeout, we restore the pointer in the result, that is,
make sure we add nothing from the socket. */
resetok = FALSE;
f = fdopen(fd_out, "rb");
sigalrm_seen = FALSE;
- alarm(60);
+ ALARM(60);
lookup_value = string_from_gstring(cat_file(f, NULL, NULL));
- alarm(0);
+ ALARM_CLR(0);
(void)fclose(f);
/* Wait for the process to finish, applying the timeout, and inspect its
}
}
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++) if (sub[i])
{
- if (sub[i] == NULL) continue;
val[i] = (int)Ustrtol(sub[i], &ret, 10);
if (*ret != 0 || (i != 0 && val[i] < 0))
{
md5 md5_base;
hctx sha1_ctx;
void *use_base;
- int type, i;
+ int type;
int hashlen; /* Number of octets for the hash algorithm's output */
int hashblocklen; /* Number of octets the hash algorithm processes */
uschar *keyptr, *p;
memset(innerkey, 0x36, hashblocklen);
memset(outerkey, 0x5c, hashblocklen);
- for (i = 0; i < keylen; i++)
+ for (int i = 0; i < keylen; i++)
{
innerkey[i] ^= keyptr[i];
outerkey[i] ^= keyptr[i];
/* Encode the final hash as a hex string */
p = finalhash_hex;
- for (i = 0; i < hashlen; i++)
+ for (int i = 0; i < hashlen; i++)
{
*p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
*p++ = hex_digits[finalhash[i] & 0x0f];
int ovector[3*(EXPAND_MAXN+1)];
int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra,
PCRE_EOPT | emptyopt, ovector, nelem(ovector));
- int nn;
uschar *insert;
/* No match - if we previously set PCRE_NOTEMPTY after a null match, this
if (n == 0) n = EXPAND_MAXN + 1;
expand_nmax = 0;
- for (nn = 0; nn < n*2; nn += 2)
+ for (int nn = 0; nn < n*2; nn += 2)
{
expand_nstring[expand_nmax] = subject + ovector[nn];
expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
case EITEM_EXTRACT:
{
- int i;
- int j;
int field_number = 1;
BOOL field_number_set = FALSE;
uschar *save_lookup_value = lookup_value;
uschar *sub[3];
int save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
+ enum {extract_basic, extract_json} fmt = extract_basic;
+
+ while (isspace(*s)) s++;
+
+ /* Check for a format-variant specifier */
+
+ if (*s != '{') /*}*/
+ if (Ustrncmp(s, "json", 4) == 0)
+ {fmt = extract_json; s += 4;}
/* While skipping we cannot rely on the data for expansions being
available (eg. $item) hence cannot decide on numeric vs. keyed.
if (skipping)
{
- while (isspace(*s)) s++;
- for (j = 5; j > 0 && *s == '{'; j--)
+ for (int j = 5; j > 0 && *s == '{'; j--) /*'}'*/
{
if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
- goto EXPAND_FAILED; /*{*/
+ goto EXPAND_FAILED; /*'{'*/
if (*s++ != '}')
{
expand_string_message = US"missing '{' for arg of extract";
}
while (isspace(*s)) s++;
}
- if ( Ustrncmp(s, "fail", 4) == 0
+ if ( Ustrncmp(s, "fail", 4) == 0 /*'{'*/
&& (s[4] == '}' || s[4] == ' ' || s[4] == '\t' || !s[4])
)
{
s += 4;
while (isspace(*s)) s++;
- }
+ } /*'{'*/
if (*s != '}')
{
expand_string_message = US"missing '}' closing extract";
}
}
- else for (i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */
+ else for (int i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */
{
- while (isspace(*s)) s++;
- if (*s == '{') /*}*/
+ while (isspace(*s)) s++;
+ if (*s == '{') /*'}'*/
{
sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
- if (sub[i] == NULL) goto EXPAND_FAILED; /*{*/
+ if (sub[i] == NULL) goto EXPAND_FAILED; /*'{'*/
if (*s++ != '}')
{
expand_string_message = string_sprintf(
/* After removal of leading and trailing white space, the first
argument must not be empty; if it consists entirely of digits
(optionally preceded by a minus sign), this is a numerical
- extraction, and we expect 3 arguments. */
+ extraction, and we expect 3 arguments (normal) or 2 (json). */
if (i == 0)
{
if (*p == 0)
{
field_number *= x;
- j = 3; /* Need 3 args */
+ if (fmt != extract_json) j = 3; /* Need 3 args */
field_number_set = TRUE;
}
}
/* Extract either the numbered or the keyed substring into $value. If
skipping, just pretend the extraction failed. */
- lookup_value = skipping? NULL : field_number_set?
- expand_gettokened(field_number, sub[1], sub[2]) :
- expand_getkeyed(sub[0], sub[1]);
+ if (skipping)
+ lookup_value = NULL;
+ else switch (fmt)
+ {
+ case extract_basic:
+ lookup_value = field_number_set
+ ? expand_gettokened(field_number, sub[1], sub[2])
+ : expand_getkeyed(sub[0], sub[1]);
+ break;
+
+ case extract_json:
+ {
+ uschar * s, * item;
+ const uschar * list;
+
+ /* Array: Bracket-enclosed and comma-separated.
+ Object: Brace-enclosed, comma-sep list of name:value pairs */
+
+ if (!(s = dewrap(sub[1], field_number_set ? US"[]" : US"{}")))
+ {
+ expand_string_message =
+ string_sprintf("%s wrapping %s for extract json",
+ expand_string_message,
+ field_number_set ? "array" : "object");
+ goto EXPAND_FAILED_CURLY;
+ }
+
+ list = s;
+ if (field_number_set)
+ {
+ if (field_number <= 0)
+ {
+ expand_string_message = US"first argument of \"extract\" must "
+ "be greater than zero";
+ goto EXPAND_FAILED;
+ }
+ while (field_number > 0 && (item = json_nextinlist(&list)))
+ field_number--;
+ s = item;
+ lookup_value = s;
+ while (*s) s++;
+ while (--s >= lookup_value && isspace(*s)) *s = '\0';
+ }
+ else
+ {
+ lookup_value = NULL;
+ while ((item = json_nextinlist(&list)))
+ {
+ /* Item is: string name-sep value. string is quoted.
+ Dequote the string and compare with the search key. */
+
+ if (!(item = dewrap(item, US"\"\"")))
+ {
+ expand_string_message =
+ string_sprintf("%s wrapping string key for extract json",
+ expand_string_message);
+ goto EXPAND_FAILED_CURLY;
+ }
+ if (Ustrcmp(item, sub[0]) == 0) /*XXX should be a UTF8-compare */
+ {
+ s = item + Ustrlen(item) + 1;
+ while (isspace(*s)) s++;
+ if (*s != ':')
+ {
+ expand_string_message = string_sprintf(
+ "missing object value-separator for extract json");
+ goto EXPAND_FAILED_CURLY;
+ }
+ s++;
+ while (isspace(*s)) s++;
+ lookup_value = s;
+ break;
+ }
+ }
+ }
+ }
+ }
/* If no string follows, $value gets substituted; otherwise there can
be yes/no strings, as for lookup or if. */
case EITEM_LISTEXTRACT:
{
- int i;
int field_number = 1;
uschar *save_lookup_value = lookup_value;
uschar *sub[2];
/* Read the field & list arguments */
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
{
while (isspace(*s)) s++;
- if (*s != '{') /*}*/
+ if (*s != '{') /*'}'*/
{
expand_string_message = string_sprintf(
"missing '{' for arg %d of listextract", i+1);
/* Extract the numbered element into $value. If
skipping, just pretend the extraction failed. */
- lookup_value = skipping? NULL : expand_getlistele(field_number, sub[1]);
+ lookup_value = skipping ? NULL : expand_getlistele(field_number, sub[1]);
/* If no string follows, $value gets substituted; otherwise there can
be yes/no strings, as for lookup or if. */
/* Look up the dynamically loaded object handle in the tree. If it isn't
found, dlopen() the file and put the handle in the tree for next time. */
- t = tree_search(dlobj_anchor, argv[0]);
- if (t == NULL)
+ if (!(t = tree_search(dlobj_anchor, argv[0])))
{
void *handle = dlopen(CS argv[0], RTLD_LAZY);
if (handle == NULL)
else
{
expand_string_message = result == NULL ? US"(no message)" : result;
- if(status == FAIL_FORCED) expand_string_forcedfail = TRUE;
+ if(status == FAIL_FORCED) f.expand_string_forcedfail = TRUE;
else if(status != FAIL)
log_write(0, LOG_MAIN|LOG_PANIC, "dlfunc{%s}{%s} failed (%d): %s",
argv[0], argv[1], status, expand_string_message);
int c;
uschar *arg = NULL;
uschar *sub;
+#ifdef SUPPORT_TLS
var_entry *vp = NULL;
+#endif
/* 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
case EOP_BASE62D:
{
- uschar buf[16];
uschar *tt = sub;
unsigned long int n = 0;
while (*tt != 0)
}
n = n * BASE_62 + (t - base62_chars);
}
- (void)sprintf(CS buf, "%ld", n);
- yield = string_cat(yield, buf);
+ yield = string_fmt_append(yield, "%ld", n);
continue;
}
{
md5 base;
uschar digest[16];
- int j;
- char st[33];
md5_start(&base);
md5_end(&base, sub, Ustrlen(sub), digest);
- for(j = 0; j < 16; j++) sprintf(st+2*j, "%02x", digest[j]);
- yield = string_cat(yield, US st);
+ for (int j = 0; j < 16; j++)
+ yield = string_fmt_append(yield, "%02x", digest[j]);
}
continue;
{
hctx h;
uschar digest[20];
- int j;
- char st[41];
sha1_start(&h);
sha1_end(&h, sub, Ustrlen(sub), digest);
- for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]);
- yield = string_catn(yield, US st, 40);
+ for (int j = 0; j < 20; j++)
+ yield = string_fmt_append(yield, "%02X", digest[j]);
}
continue;
{
hctx h;
blob b;
- char st[3];
if (!exim_sha_init(&h, HASH_SHA2_256))
{
exim_sha_update(&h, sub, Ustrlen(sub));
exim_sha_finish(&h, &b);
while (b.len-- > 0)
- {
- sprintf(st, "%02X", *b.data++);
- yield = string_catn(yield, US st, 2);
- }
+ yield = string_fmt_append(yield, "%02X", *b.data++);
}
#else
expand_string_message = US"sha256 only supported with TLS";
{
hctx h;
blob b;
- char st[3];
hashmethod m = !arg ? HASH_SHA3_256
: Ustrcmp(arg, "224") == 0 ? HASH_SHA3_224
: Ustrcmp(arg, "256") == 0 ? HASH_SHA3_256
exim_sha_update(&h, sub, Ustrlen(sub));
exim_sha_finish(&h, &b);
while (b.len-- > 0)
- {
- sprintf(st, "%02X", *b.data++);
- yield = string_catn(yield, US st, 2);
- }
+ yield = string_fmt_append(yield, "%02X", *b.data++);
}
continue;
#else
uschar *out = sub;
uschar *enc;
- for (enc = sub; *enc != 0; enc++)
+ for (enc = sub; *enc; enc++)
{
if (!isxdigit(*enc))
{
if (isdigit(c)) c -= '0';
else c = toupper(c) - 'A' + 10;
if (b == -1)
- {
b = c << 4;
- }
else
{
*out++ = b | c;
while (*(++t) != 0)
{
if (*t < 0x21 || 0x7E < *t)
- yield = string_catn(yield, string_sprintf("\\x%02x", *t), 4);
+ yield = string_fmt_append(yield, "\\x%02x", *t);
else
yield = string_catn(yield, t, 1);
}
{
int cnt = 0;
int sep = 0;
- uschar * cp;
uschar buffer[256];
while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
- cp = string_sprintf("%d", cnt);
- yield = string_cat(yield, cp);
+ yield = string_fmt_append(yield, "%d", cnt);
continue;
}
"missing in expanding ${addresses:%s}", --sub);
goto EXPAND_FAILED;
}
- parse_allow_group = TRUE;
+ f.parse_allow_group = TRUE;
for (;;)
{
separator. */
if (yield->ptr != save_ptr) yield->ptr--;
- parse_allow_group = FALSE;
+ f.parse_allow_group = FALSE;
continue;
}
case EOP_RFC2047:
{
uschar buffer[2048];
- const uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset,
- buffer, sizeof(buffer), FALSE);
- yield = string_cat(yield, string);
+ yield = string_cat(yield,
+ parse_quote_2047(sub, Ustrlen(sub), headers_charset,
+ buffer, sizeof(buffer), FALSE));
continue;
}
{
int seq_len = 0, index = 0;
int bytes_left = 0;
- long codepoint = -1;
+ long codepoint = -1;
+ int complete;
uschar seq_buff[4]; /* accumulate utf-8 here */
while (*sub != 0)
{
- int complete = 0;
+ complete = 0;
uschar c = *sub++;
if (bytes_left)
/* ASCII character follows incomplete sequence */
yield = string_catn(yield, &c, 1);
}
+ /* If given a sequence truncated mid-character, we also want to report ?
+ * 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;
}
case EOP_ESCAPE8BIT:
{
- const uschar * s = sub;
uschar c;
- for (s = sub; (c = *s); s++)
+ for (const uschar * s = sub; (c = *s); s++)
yield = c < 127 && c != '\\'
? string_catn(yield, s, 1)
- : string_catn(yield, string_sprintf("\\%03o", c), 4);
+ : string_fmt_append(yield, "\\%03o", c);
continue;
}
uschar *save_sub = sub;
uschar *error = NULL;
int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE);
- if (error != NULL)
+ if (error)
{
expand_string_message = string_sprintf("error in expression "
"evaluation: %s (after processing \"%.*s\")", error,
(int)(sub-save_sub), save_sub);
goto EXPAND_FAILED;
}
- sprintf(CS var_buffer, PR_EXIM_ARITH, n);
- yield = string_cat(yield, var_buffer);
+ yield = string_fmt_append(yield, PR_EXIM_ARITH, n);
continue;
}
"Exim time interval in \"%s\" operator", sub, name);
goto EXPAND_FAILED;
}
- sprintf(CS var_buffer, "%d", n);
- yield = string_cat(yield, var_buffer);
+ yield = string_fmt_append(yield, "%d", n);
continue;
}
/* strlen returns the length of the string */
case EOP_STRLEN:
- {
- uschar buff[24];
- (void)sprintf(CS buff, "%d", Ustrlen(sub));
- yield = string_cat(yield, buff);
+ yield = string_fmt_append(yield, "%d", Ustrlen(sub));
continue;
- }
/* length_n or l_n takes just the first n characters or the whole string,
whichever is the shorter;
int len;
uschar *ret;
- if (arg == NULL)
+ if (!arg)
{
expand_string_message = string_sprintf("missing values after %s",
name);
case EOP_STAT:
{
- uschar *s;
uschar smode[12];
uschar **modetable[3];
- int i;
mode_t mode;
struct stat st;
- if ((expand_forbid & RDO_EXISTS) != 0)
+ if (expand_forbid & RDO_EXISTS)
{
expand_string_message = US"Use of the stat() expansion is not permitted";
goto EXPAND_FAILED;
modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid;
modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid;
- for (i = 0; i < 3; i++)
+ for (int i = 0; i < 3; i++)
{
memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3);
mode >>= 3;
}
smode[10] = 0;
- s = string_sprintf("mode=%04lo smode=%s inode=%ld device=%ld links=%ld "
+ yield = string_fmt_append(yield,
+ "mode=%04lo smode=%s inode=%ld device=%ld links=%ld "
"uid=%ld gid=%ld size=" OFF_T_FMT " atime=%ld mtime=%ld ctime=%ld",
(long)(st.st_mode & 077777), smode, (long)st.st_ino,
(long)st.st_dev, (long)st.st_nlink, (long)st.st_uid,
(long)st.st_gid, st.st_size, (long)st.st_atime,
(long)st.st_mtime, (long)st.st_ctime);
- yield = string_cat(yield, s);
continue;
}
case EOP_RANDINT:
{
- int_eximarith_t max;
- uschar *s;
+ int_eximarith_t max = expanded_string_integer(sub, TRUE);
- max = expanded_string_integer(sub, TRUE);
- if (expand_string_message != NULL)
+ if (expand_string_message)
goto EXPAND_FAILED;
- s = string_sprintf("%d", vaguely_random_number((int)max));
- yield = string_cat(yield, s);
+ yield = string_fmt_append(yield, "%d", vaguely_random_number((int)max));
continue;
}
/* Unknown operator */
default:
- expand_string_message =
- string_sprintf("unknown expansion operator \"%s\"", name);
- goto EXPAND_FAILED;
+ expand_string_message =
+ string_sprintf("unknown expansion operator \"%s\"", name);
+ goto EXPAND_FAILED;
}
}
else if (resetok_p) *resetok_p = FALSE;
DEBUG(D_expand)
- {
- debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
- "expanding: %.*s\n",
- (int)(s - string), string);
- debug_printf_indent("%s"
- UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
- "result: %s\n",
- skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT,
- yield->s);
- if (skipping)
- debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
- "skipping: result is not used\n");
- }
+ DEBUG(D_noutf8)
+ {
+ debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string);
+ debug_printf_indent("%sresult: %s\n",
+ skipping ? "|-----" : "\\_____", yield->s);
+ if (skipping)
+ debug_printf_indent("\\___skipping: result is not used\n");
+ }
+ else
+ {
+ debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
+ "expanding: %.*s\n",
+ (int)(s - string), string);
+ debug_printf_indent("%s"
+ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
+ "result: %s\n",
+ skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT,
+ yield->s);
+ if (skipping)
+ debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
+ "skipping: result is not used\n");
+ }
expand_level--;
return yield->s;
EXPAND_FAILED:
if (left) *left = s;
DEBUG(D_expand)
- {
- debug_printf_indent(UTF8_VERT_RIGHT "failed to expand: %s\n",
- string);
- debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
- "error message: %s\n",
- expand_string_forcedfail ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT,
- expand_string_message);
- if (expand_string_forcedfail)
- debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n");
- }
+ DEBUG(D_noutf8)
+ {
+ debug_printf_indent("|failed to expand: %s\n", string);
+ debug_printf_indent("%serror message: %s\n",
+ f.expand_string_forcedfail ? "|---" : "\\___", expand_string_message);
+ if (f.expand_string_forcedfail)
+ debug_printf_indent("\\failure was forced\n");
+ }
+ else
+ {
+ debug_printf_indent(UTF8_VERT_RIGHT "failed to expand: %s\n",
+ string);
+ debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
+ "error message: %s\n",
+ f.expand_string_forcedfail ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT,
+ expand_string_message);
+ if (f.expand_string_forcedfail)
+ debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n");
+ }
if (resetok_p && !resetok) *resetok_p = FALSE;
expand_level--;
return NULL;
int old_pool = store_pool;
uschar * s;
- search_find_defer = FALSE;
+ f.search_find_defer = FALSE;
malformed_header = FALSE;
store_pool = POOL_MAIN;
s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL);
expanded = expand_string(svalue);
if (expanded == NULL)
{
- if (expand_string_forcedfail)
+ if (f.expand_string_forcedfail)
{
DEBUG(dbg_opt) debug_printf("expansion of \"%s\" forced failure\n", oname);
*rvalue = bvalue;
{
return ( ( Ustrstr(s, "failed to expand") != NULL
|| Ustrstr(s, "expansion of ") != NULL
- )
+ )
&& ( Ustrstr(s, "mysql") != NULL
|| Ustrstr(s, "pgsql") != NULL
|| Ustrstr(s, "redis") != NULL
|| Ustrstr(s, "ldapi:") != NULL
|| Ustrstr(s, "ldapdn:") != NULL
|| Ustrstr(s, "ldapm:") != NULL
- ) )
+ ) )
? US"Temporary internal error" : s;
}
{
err_ctx e = { .region_start = ptr, .region_end = US ptr + len,
.var_name = NULL, .var_data = NULL };
-int i;
-var_entry * v;
/* check acl_ variables */
tree_walk(acl_var_c, assert_variable_notin, &e);
tree_walk(acl_var_m, assert_variable_notin, &e);
/* check auth<n> variables */
-for (i = 0; i < AUTH_VARS; i++) if (auth_vars[i])
+for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i])
assert_variable_notin(US"auth<n>", auth_vars[i], &e);
/* check regex<n> variables */
-for (i = 0; i < REGEX_VARS; i++) if (regex_vars[i])
+for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i])
assert_variable_notin(US"regex<n>", regex_vars[i], &e);
/* check known-name variables */
-for (v = var_table; v < var_table + var_table_size; v++)
+for (var_entry * v = var_table; v < var_table + var_table_size; v++)
if (v->type == vtype_stringptr)
assert_variable_notin(US v->name, *(USS v->value), &e);
if (n == 0) n = EXPAND_MAXN + 1;
if (yield)
{
- int nn;
- expand_nmax = (setup < 0)? 0 : setup + 1;
- for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2)
+ expand_nmax = setup < 0 ? 0 : setup + 1;
+ for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
{
expand_nstring[expand_nmax] = subject + ovector[nn];
expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
int main(int argc, uschar **argv)
{
-int i;
uschar buffer[1024];
debug_selector = D_v;
debug_fd = fileno(debug_file);
big_buffer = malloc(big_buffer_size);
-for (i = 1; i < argc; i++)
+for (int i = 1; i < argc; i++)
{
if (argv[i][0] == '+')
{
}
else
{
- if (search_find_defer) printf("search_find deferred\n");
+ if (f.search_find_defer) printf("search_find deferred\n");
printf("Failed: %s\n", expand_string_message);
- if (expand_string_forcedfail) printf("Forced failure\n");
+ if (f.expand_string_forcedfail) printf("Forced failure\n");
printf("\n");
}
}