X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/7e75538e9b2d735ebe7a2832482353861ec4dff0..774ef2d7d0f7fffbfd114271b8567e36485898dc:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index 9d73803a5..623d3f224 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -14,6 +14,7 @@ /* Recursively called function */ static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL, BOOL, BOOL *); +static int_eximarith_t expanded_string_integer(uschar *, BOOL); #ifdef STAND_ALONE #ifndef SUPPORT_CRYPTEQ @@ -126,6 +127,7 @@ static uschar *item_table[] = { US"reduce", US"run", US"sg", + US"sort", US"substr", US"tr" }; @@ -151,6 +153,7 @@ enum { EITEM_REDUCE, EITEM_RUN, EITEM_SG, + EITEM_SORT, EITEM_SUBSTR, EITEM_TR }; @@ -487,6 +490,14 @@ static var_entry var_table[] = { { "dnslist_value", vtype_stringptr, &dnslist_value }, { "domain", vtype_stringptr, &deliver_domain }, { "domain_data", vtype_stringptr, &deliver_domain_data }, +#ifdef EXPERIMENTAL_EVENT + { "event_data", vtype_stringptr, &event_data }, + + /*XXX want to use generic vars for as many of these as possible*/ + { "event_defer_errno", vtype_int, &event_defer_errno }, + + { "event_name", vtype_stringptr, &event_name }, +#endif { "exim_gid", vtype_gid, &exim_gid }, { "exim_path", vtype_stringptr, &exim_path }, { "exim_uid", vtype_uid, &exim_uid }, @@ -500,6 +511,7 @@ static var_entry var_table[] = { { "host_data", vtype_stringptr, &host_data }, { "host_lookup_deferred",vtype_int, &host_lookup_deferred }, { "host_lookup_failed", vtype_int, &host_lookup_failed }, + { "host_port", vtype_int, &deliver_host_port }, { "inode", vtype_ino, &deliver_inode }, { "interface_address", vtype_stringptr, &interface_address }, { "interface_port", vtype_int, &interface_port }, @@ -677,22 +689,28 @@ static var_entry var_table[] = { { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +#if defined(SUPPORT_TLS) { "tls_in_sni", vtype_stringptr, &tls_in.sni }, #endif { "tls_out_bits", vtype_int, &tls_out.bits }, { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified }, { "tls_out_cipher", vtype_stringptr, &tls_out.cipher }, +#ifdef EXPERIMENTAL_DANE + { "tls_out_dane", vtype_bool, &tls_out.dane_verified }, +#endif { "tls_out_ocsp", vtype_int, &tls_out.ocsp }, { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, { "tls_out_peercert", vtype_cert, &tls_out.peercert }, { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn }, -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +#if defined(SUPPORT_TLS) { "tls_out_sni", vtype_stringptr, &tls_out.sni }, #endif +#ifdef EXPERIMENTAL_DANE + { "tls_out_tlsa_usage", vtype_int, &tls_out.tlsa_usage }, +#endif { "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */ -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +#if defined(SUPPORT_TLS) { "tls_sni", vtype_stringptr, &tls_in.sni }, /* mind the alphabetical order! */ #endif @@ -704,16 +722,6 @@ static var_entry var_table[] = { { "tod_logfile", vtype_todlf, NULL }, { "tod_zone", vtype_todzone, NULL }, { "tod_zulu", vtype_todzulu, NULL }, -#ifdef EXPERIMENTAL_TPDA - { "tpda_defer_errno", vtype_int, &tpda_defer_errno }, - { "tpda_defer_errstr", vtype_stringptr, &tpda_defer_errstr }, - { "tpda_delivery_confirmation", vtype_stringptr, &tpda_delivery_confirmation }, - { "tpda_delivery_domain", vtype_stringptr, &tpda_delivery_domain }, - { "tpda_delivery_fqdn", vtype_stringptr, &tpda_delivery_fqdn }, - { "tpda_delivery_ip", vtype_stringptr, &tpda_delivery_ip }, - { "tpda_delivery_local_part",vtype_stringptr,&tpda_delivery_local_part }, - { "tpda_delivery_port", vtype_int, &tpda_delivery_port }, -#endif { "transport_name", vtype_stringptr, &transport_name }, { "value", vtype_stringptr, &lookup_value }, { "version_number", vtype_stringptr, &version_string }, @@ -912,7 +920,9 @@ vaguely_random_number(int max) #ifdef HAVE_ARC4RANDOM /* cryptographically strong randomness, common on *BSD platforms, not so much elsewhere. Alas. */ +#ifndef NOT_HAVE_ARC4RANDOM_STIR arc4random_stir(); +#endif #elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) #ifdef HAVE_SRANDOMDEV /* uses random(4) for seeding */ @@ -1878,6 +1888,8 @@ switch (vp->type) #endif } + +return NULL; /* Unknown variable. Silences static checkers. */ } @@ -2445,7 +2457,7 @@ switch(cond_type) } else { - num[i] = expand_string_integer(sub[i], FALSE); + num[i] = expanded_string_integer(sub[i], FALSE); if (expand_string_message != NULL) return NULL; } } @@ -2742,6 +2754,8 @@ switch(cond_type) 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; @@ -2829,6 +2843,8 @@ switch(cond_type) int sep = 0; uschar *save_iterate_item = iterate_item; + DEBUG(D_expand) debug_printf("condition: %s\n", name); + while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL), TRUE, resetok); @@ -5219,25 +5235,28 @@ while (*s != 0) while (len > 0 && isspace(p[len-1])) len--; p[len] = 0; - if (*p == 0 && !skipping) - { - expand_string_message = US"first argument of \"extract\" must " - "not be empty"; - goto EXPAND_FAILED; - } + if (!skipping) + { + if (*p == 0) + { + expand_string_message = US"first argument of \"extract\" must " + "not be empty"; + goto EXPAND_FAILED; + } - if (*p == '-') - { - field_number = -1; - p++; - } - while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0'; - if (*p == 0) - { - field_number *= x; - j = 3; /* Need 3 args */ - field_number_set = TRUE; - } + if (*p == '-') + { + field_number = -1; + p++; + } + while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0'; + if (*p == 0) + { + field_number *= x; + j = 3; /* Need 3 args */ + field_number_set = TRUE; + } + } } } else goto EXPAND_FAILED_CURLY; @@ -5616,6 +5635,145 @@ while (*s != 0) continue; } + case EITEM_SORT: + { + int sep = 0; + uschar *srclist, *cmp, *xtract; + uschar *srcitem; + uschar *dstlist = NULL; + uschar *dstkeylist = NULL; + uschar * tmp; + uschar *save_iterate_item = iterate_item; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); + if (!srclist) goto EXPAND_FAILED; + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok); + if (!cmp) goto EXPAND_FAILED; + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + xtract = s; + tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); + if (!tmp) goto EXPAND_FAILED; + xtract = string_copyn(xtract, s - xtract); + + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + /*{*/ + if (*s++ != '}') + { /*{*/ + expand_string_message = US"missing } at end of \"sort\""; + goto EXPAND_FAILED; + } + + if (skipping) continue; + + while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0))) + { + uschar * dstitem; + uschar * newlist = NULL; + uschar * newkeylist = NULL; + uschar * srcfield; + + DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, srcitem); + + /* extract field for comparisons */ + iterate_item = srcitem; + if ( !(srcfield = expand_string_internal(xtract, FALSE, NULL, FALSE, + TRUE, &resetok)) + || !*srcfield) + { + expand_string_message = string_sprintf( + "field-extract in sort: \"%s\"", xtract); + goto EXPAND_FAILED; + } + + /* Insertion sort */ + + /* copy output list until new-item < list-item */ + while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) + { + uschar * dstfield; + uschar * expr; + BOOL before; + + /* field for comparison */ + if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) + goto sort_mismatch; + + /* build and run condition string */ + expr = string_sprintf("%s{%s}{%s}", cmp, srcfield, dstfield); + + DEBUG(D_expand) debug_printf("%s: cond = \"%s\"\n", name, expr); + if (!eval_condition(expr, &resetok, &before)) + { + expand_string_message = string_sprintf("comparison in sort: %s", + expr); + goto EXPAND_FAILED; + } + + if (before) + { + /* New-item sorts before this dst-item. Append new-item, + then dst-item, then remainder of dst list. */ + + newlist = string_append_listele(newlist, sep, srcitem); + newkeylist = string_append_listele(newkeylist, sep, srcfield); + srcitem = NULL; + + newlist = string_append_listele(newlist, sep, dstitem); + newkeylist = string_append_listele(newkeylist, sep, dstfield); + + while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) + { + if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) + goto sort_mismatch; + newlist = string_append_listele(newlist, sep, dstitem); + newkeylist = string_append_listele(newkeylist, sep, dstfield); + } + + break; + } + + newlist = string_append_listele(newlist, sep, dstitem); + newkeylist = string_append_listele(newkeylist, sep, dstfield); + } + + /* If we ran out of dstlist without consuming srcitem, append it */ + if (srcitem) + { + newlist = string_append_listele(newlist, sep, srcitem); + newkeylist = string_append_listele(newkeylist, sep, srcfield); + } + + dstlist = newlist; + dstkeylist = newkeylist; + + DEBUG(D_expand) debug_printf("%s: dstlist = \"%s\"\n", name, dstlist); + DEBUG(D_expand) debug_printf("%s: dstkeylist = \"%s\"\n", name, dstkeylist); + } + + if (dstlist) + yield = string_cat(yield, &size, &ptr, dstlist, Ustrlen(dstlist)); + + /* Restore preserved $item */ + iterate_item = save_iterate_item; + continue; + + sort_mismatch: + expand_string_message = US"Internal error in sort (list mismatch)"; + goto EXPAND_FAILED; + } + /* If ${dlfunc } support is configured, handle calling dynamically-loaded functions, unless locked out at this time. Syntax is ${dlfunc{file}{func}} @@ -5908,7 +6066,7 @@ while (*s != 0) if (vp && *(void **)vp->value) { uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value); - yield = string_cat(yield, &size, &ptr, cp, (int)strlen(cp)); + yield = string_cat(yield, &size, &ptr, cp, (int)Ustrlen(cp)); } else #endif @@ -6365,18 +6523,16 @@ while (*s != 0) case EOP_UTF8CLEAN: { - int seq_len, index = 0; + int seq_len = 0, index = 0; int bytes_left = 0; + long codepoint = -1; uschar seq_buff[4]; /* accumulate utf-8 here */ while (*sub != 0) { - int complete; - long codepoint; - uschar c; + int complete = 0; + uschar c = *sub++; - complete = 0; - c = *sub++; if (bytes_left) { if ((c & 0xc0) != 0x80) @@ -6391,7 +6547,7 @@ while (*s != 0) if (--bytes_left == 0) /* codepoint complete */ { if(codepoint > 0x10FFFF) /* is it too large? */ - complete = -1; /* error */ + complete = -1; /* error (RFC3629 limit) */ else { /* finished; output utf-8 sequence */ yield = string_cat(yield, &size, &ptr, seq_buff, seq_len); @@ -6679,7 +6835,7 @@ while (*s != 0) int_eximarith_t max; uschar *s; - max = expand_string_integer(sub, TRUE); + max = expanded_string_integer(sub, TRUE); if (expand_string_message != NULL) goto EXPAND_FAILED; s = string_sprintf("%d", vaguely_random_number((int)max)); @@ -6879,8 +7035,32 @@ Returns: the integer value, or int_eximarith_t expand_string_integer(uschar *string, BOOL isplus) { +return expanded_string_integer(expand_string(string), isplus); +} + + +/************************************************* + * Interpret string as an integer * + *************************************************/ + +/* Convert a string (that has already been expanded) into an integer. + +This function is used inside the expansion code. + +Arguments: + s the string to be expanded + isplus TRUE if a non-negative number is expected + +Returns: the integer value, or + -1 if string is NULL (which implies an expansion error) + -2 for an integer interpretation error + expand_string_message is set NULL for an OK integer +*/ + +static int_eximarith_t +expanded_string_integer(uschar *s, BOOL isplus) +{ int_eximarith_t value; -uschar *s = expand_string(string); uschar *msg = US"invalid integer \"%s\""; uschar *endptr;