X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9f01e50d7efc5c625614e4e055790ca4a92a52a8..eb24befc3e9ad0a36ef8f0bc3527a9bc6c923a7b:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index 8be10c14f..fddad3179 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -129,6 +129,9 @@ static uschar *item_table[] = { US"run", US"sg", US"sort", +#ifdef EXPERIMENTAL_SRS_NATIVE + US"srs_encode", +#endif US"substr", US"tr" }; @@ -160,6 +163,9 @@ enum { EITEM_RUN, EITEM_SG, EITEM_SORT, +#ifdef EXPERIMENTAL_SRS_NATIVE + EITEM_SRS_ENCODE, +#endif EITEM_SUBSTR, EITEM_TR }; @@ -207,6 +213,7 @@ static uschar *op_table_main[] = { US"base62d", US"base64", US"base64d", + US"bless", US"domain", US"escape", US"escape8bit", @@ -254,6 +261,7 @@ enum { EOP_BASE62D, EOP_BASE64, EOP_BASE64D, + EOP_BLESS, EOP_DOMAIN, EOP_ESCAPE, EOP_ESCAPE8BIT, @@ -323,6 +331,9 @@ static uschar *cond_table[] = { US"gei", US"gt", US"gti", +#ifdef EXPERIMENTAL_SRS_NATIVE + US"inbound_srs", +#endif US"inlist", US"inlisti", US"isip", @@ -373,6 +384,9 @@ enum { ECOND_STR_GEI, ECOND_STR_GT, ECOND_STR_GTI, +#ifdef EXPERIMENTAL_SRS_NATIVE + ECOND_INBOUND_SRS, +#endif ECOND_INLIST, ECOND_INLISTI, ECOND_ISIP, @@ -452,6 +466,7 @@ typedef struct { 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. */ @@ -574,7 +589,10 @@ static var_entry var_table[] = { { "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 }, #endif @@ -654,6 +672,7 @@ static var_entry var_table[] = { { "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 }, @@ -736,7 +755,11 @@ static var_entry var_table[] = { { "srs_db_key", vtype_stringptr, &srs_db_key }, { "srs_orig_recipient", vtype_stringptr, &srs_orig_recipient }, { "srs_orig_sender", vtype_stringptr, &srs_orig_sender }, +#endif +#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) { "srs_recipient", vtype_stringptr, &srs_recipient }, +#endif +#ifdef EXPERIMENTAL_SRS { "srs_status", vtype_stringptr, &srs_status }, #endif { "thisaddress", vtype_stringptr, &filter_thisaddress }, @@ -760,6 +783,7 @@ static var_entry var_table[] = { #ifndef DISABLE_TLS { "tls_in_sni", vtype_stringptr, &tls_in.sni }, #endif + { "tls_in_ver", vtype_stringptr, &tls_in.ver }, { "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 }, @@ -780,6 +804,7 @@ static var_entry var_table[] = { #ifdef SUPPORT_DANE { "tls_out_tlsa_usage", vtype_int, &tls_out.tlsa_usage }, #endif + { "tls_out_ver", vtype_stringptr, &tls_out.ver }, { "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */ #ifndef DISABLE_TLS @@ -939,18 +964,16 @@ Returns: TRUE if condition is met, FALSE if not BOOL expand_check_condition(uschar *condition, uschar *m1, uschar *m2) { -int rc; -uschar *ss = expand_string(condition); -if (ss == NULL) +uschar * ss = expand_string(condition); +if (!ss) { 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; } -rc = ss[0] != 0 && Ustrcmp(ss, "0") != 0 && strcmpic(ss, US"no") != 0 && +return *ss && Ustrcmp(ss, "0") != 0 && strcmpic(ss, US"no") != 0 && strcmpic(ss, US"false") != 0; -return rc; } @@ -1053,7 +1076,7 @@ static const uschar * read_name(uschar *name, int max, const uschar *s, uschar *extras) { int ptr = 0; -while (*s != 0 && (isalnum(*s) || Ustrchr(extras, *s) != NULL)) +while (*s && (isalnum(*s) || Ustrchr(extras, *s) != NULL)) { if (ptr < max-1) name[ptr++] = *s; s++; @@ -1611,8 +1634,8 @@ for (header_line * h = header_list; h; h = h->next) /* 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 @@ -1624,17 +1647,12 @@ for (header_line * h = header_list; h; h = h->next) 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 */ @@ -1644,7 +1662,7 @@ if (!g) return US""; *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 @@ -1652,16 +1670,12 @@ charset translation fails. If decoding fails, it returns NULL. */ 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); } } @@ -1730,6 +1744,94 @@ return g ? g->s : NULL; } +/************************************************* +* 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 * *************************************************/ @@ -2292,6 +2394,127 @@ return chop_match(name, cond_table, nelem(cond_table)); } +/************************************************* +* Handle MD5 or SHA-1 computation for HMAC * +*************************************************/ + +/* These are some wrapping functions that enable the HMAC code to be a bit +cleaner. A good compiler will spot the tail recursion. + +Arguments: + type HMAC_MD5 or HMAC_SHA1 + remaining are as for the cryptographic hash functions + +Returns: nothing +*/ + +static void +chash_start(int type, void * base) +{ +if (type == HMAC_MD5) + md5_start((md5 *)base); +else + sha1_start((hctx *)base); +} + +static void +chash_mid(int type, void * base, const uschar * string) +{ +if (type == HMAC_MD5) + md5_mid((md5 *)base, string); +else + sha1_mid((hctx *)base, string); +} + +static void +chash_end(int type, void * base, const uschar * string, int length, + uschar * digest) +{ +if (type == HMAC_MD5) + md5_end((md5 *)base, string, length, digest); +else + sha1_end((hctx *)base, string, length, digest); +} + + + + +/* Do an hmac_md5. The result is _not_ nul-terminated, and is sized as +the smaller of a full hmac_md5 result (16 bytes) or the supplied output buffer. + +Arguments: + key encoding key, nul-terminated + src data to be hashed, nul-terminated + buf output buffer + len size of output buffer +*/ + +static void +hmac_md5(const uschar * key, const uschar * src, uschar * buf, unsigned len) +{ +md5 md5_base; +const uschar * keyptr; +uschar * p; +unsigned int keylen; + +#define MD5_HASHLEN 16 +#define MD5_HASHBLOCKLEN 64 + +uschar keyhash[MD5_HASHLEN]; +uschar innerhash[MD5_HASHLEN]; +uschar finalhash[MD5_HASHLEN]; +uschar innerkey[MD5_HASHBLOCKLEN]; +uschar outerkey[MD5_HASHBLOCKLEN]; + +keyptr = key; +keylen = Ustrlen(keyptr); + +/* If the key is longer than the hash block length, then hash the key +first */ + +if (keylen > MD5_HASHBLOCKLEN) + { + chash_start(HMAC_MD5, &md5_base); + chash_end(HMAC_MD5, &md5_base, keyptr, keylen, keyhash); + keyptr = keyhash; + keylen = MD5_HASHLEN; + } + +/* Now make the inner and outer key values */ + +memset(innerkey, 0x36, MD5_HASHBLOCKLEN); +memset(outerkey, 0x5c, MD5_HASHBLOCKLEN); + +for (int i = 0; i < keylen; i++) + { + innerkey[i] ^= keyptr[i]; + outerkey[i] ^= keyptr[i]; + } + +/* Now do the hashes */ + +chash_start(HMAC_MD5, &md5_base); +chash_mid(HMAC_MD5, &md5_base, innerkey); +chash_end(HMAC_MD5, &md5_base, src, Ustrlen(src), innerhash); + +chash_start(HMAC_MD5, &md5_base); +chash_mid(HMAC_MD5, &md5_base, outerkey); +chash_end(HMAC_MD5, &md5_base, innerhash, MD5_HASHLEN, finalhash); + +/* Encode the final hash as a hex string, limited by output buffer size */ + +p = buf; +for (int i = 0, j = len; i < MD5_HASHLEN; i++) + { + if (j-- <= 0) break; + *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; + if (j-- <= 0) break; + *p++ = hex_digits[finalhash[i] & 0x0f]; + } +return; +} + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -2392,14 +2615,14 @@ switch(cond_type = identify_operator(&s, &opname)) /* first_delivery tests for first delivery attempt */ case ECOND_FIRST_DELIVERY: - if (yield != NULL) *yield = f.deliver_firsttime == testfor; + if (yield) *yield = f.deliver_firsttime == testfor; return s; /* queue_running tests for any process started by a queue runner */ case ECOND_QUEUE_RUNNING: - if (yield != NULL) *yield = (queue_run_pid != (pid_t)0) == testfor; + if (yield) *yield = (queue_run_pid != (pid_t)0) == testfor; return s; @@ -2426,11 +2649,11 @@ switch(cond_type = identify_operator(&s, &opname)) if (*s != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE, resetok); - if (sub[0] == NULL) return NULL; + if (!sub[0]) return NULL; /* {-for-text-editors */ if (*s++ != '}') goto COND_FAILED_CURLY_END; - if (yield == NULL) return s; /* No need to run the test if skipping */ + if (!yield) return s; /* No need to run the test if skipping */ switch(cond_type) { @@ -2532,7 +2755,7 @@ switch(cond_type = identify_operator(&s, &opname)) case 3: return NULL; } - if (yield != NULL) + if (yield) { int rc; *resetok = FALSE; /* eval_acl() might allocate; do not reclaim */ @@ -2583,8 +2806,8 @@ switch(cond_type = identify_operator(&s, &opname)) case 2: case 3: return NULL; } - if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */ - if (yield != NULL) + if (!sub[2]) sub[3] = NULL; /* realm if no service */ + if (yield) { int rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3], &expand_string_message); @@ -2671,7 +2894,7 @@ switch(cond_type = identify_operator(&s, &opname)) conditions that compare numbers do not start with a letter. This just saves checking for them individually. */ - if (!isalpha(opname[0]) && yield != NULL) + if (!isalpha(opname[0]) && yield) if (sub[i][0] == 0) { num[i] = 0; @@ -2681,13 +2904,13 @@ switch(cond_type = identify_operator(&s, &opname)) else { num[i] = expanded_string_integer(sub[i], FALSE); - if (expand_string_message != NULL) return NULL; + if (expand_string_message) return NULL; } } /* Result not required */ - if (yield == NULL) return s; + if (!yield) return s; /* Do an appropriate comparison */ @@ -2755,9 +2978,8 @@ switch(cond_type = identify_operator(&s, &opname)) break; case ECOND_MATCH: /* Regular expression match */ - re = pcre_compile(CS sub[1], PCRE_COPT, (const char **)&rerror, &roffset, - NULL); - if (re == NULL) + if (!(re = pcre_compile(CS sub[1], PCRE_COPT, CCSS &rerror, + &roffset, NULL))) { expand_string_message = string_sprintf("regular expression error in " "\"%s\": %s at offset %d", sub[1], rerror, roffset); @@ -3011,7 +3233,7 @@ switch(cond_type = identify_operator(&s, &opname)) case ECOND_AND: case ECOND_OR: - subcondptr = (yield == NULL)? NULL : &tempcond; + subcondptr = (yield == NULL) ? NULL : &tempcond; combined_cond = (cond_type == ECOND_AND); while (isspace(*s)) s++; @@ -3046,8 +3268,7 @@ switch(cond_type = identify_operator(&s, &opname)) return NULL; } - if (yield != NULL) - { + if (yield) if (cond_type == ECOND_AND) { combined_cond &= tempcond; @@ -3058,10 +3279,9 @@ switch(cond_type = identify_operator(&s, &opname)) combined_cond |= tempcond; if (combined_cond) subcondptr = NULL; /* once true, don't */ } /* evaluate any more */ - } } - if (yield != NULL) *yield = (combined_cond == testfor); + if (yield) *yield = (combined_cond == testfor); return ++s; @@ -3084,8 +3304,8 @@ switch(cond_type = identify_operator(&s, &opname)) 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); - if (sub[0] == NULL) return NULL; + if (!(sub[0] = expand_string_internal(s, TRUE, &s, yield == NULL, TRUE, resetok))) + return NULL; /* {-for-text-editors */ if (*s++ != '}') goto COND_FAILED_CURLY_END; @@ -3225,9 +3445,103 @@ switch(cond_type = identify_operator(&s, &opname)) } DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", ourname, boolvalue? "true":"false"); - if (yield != NULL) *yield = (boolvalue == testfor); + if (yield) *yield = (boolvalue == testfor); + return s; + } + +#ifdef EXPERIMENTAL_SRS_NATIVE + case ECOND_INBOUND_SRS: + /* ${if inbound_srs {local_part}{secret} {yes}{no}} */ + { + uschar * sub[2]; + const pcre * re; + int ovec[3*(4+1)]; + int n; + uschar cksum[4]; + BOOL boolvalue = FALSE; + + switch(read_subs(sub, 2, 2, CUSS &s, yield == NULL, FALSE, US"inbound_srs", resetok)) + { + case 1: expand_string_message = US"too few arguments or bracketing " + "error for inbound_srs"; + case 2: + case 3: return NULL; + } + + /* Match the given local_part against the SRS-encoded pattern */ + + re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$", + TRUE, FALSE); + if (pcre_exec(re, NULL, CS sub[0], Ustrlen(sub[0]), 0, PCRE_EOPT, + ovec, nelem(ovec)) < 0) + { + DEBUG(D_expand) debug_printf("no match for SRS'd local-part pattern\n"); + goto srs_result; + } + + /* Side-effect: record the decoded recipient */ + + srs_recipient = string_sprintf("%.*S@%.*S", /* lowercased */ + ovec[9]-ovec[8], sub[0] + ovec[8], /* substring 4 */ + ovec[7]-ovec[6], sub[0] + ovec[6]); /* substring 3 */ + + /* If a zero-length secret was given, we're done. Otherwise carry on + and validate the given SRS local_part againt our secret. */ + + if (!*sub[1]) + { + boolvalue = TRUE; + goto srs_result; + } + + /* check the timestamp */ + { + struct timeval now; + uschar * ss = sub[0] + ovec[4]; /* substring 2, the timestamp */ + long d; + + gettimeofday(&now, NULL); + now.tv_sec /= 86400; /* days since epoch */ + + /* Decode substring 2 from base32 to a number */ + + for (d = 0, n = ovec[5]-ovec[4]; n; n--) + { + uschar * t = Ustrchr(base32_chars, *ss++); + d = d * 32 + (t - base32_chars); + } + + if (((now.tv_sec - d) & 0x3ff) > 10) /* days since SRS generated */ + { + DEBUG(D_expand) debug_printf("SRS too old\n"); + goto srs_result; + } + } + + /* check length of substring 1, the offered checksum */ + + if (ovec[3]-ovec[2] != 4) + { + DEBUG(D_expand) debug_printf("SRS checksum wrong size\n"); + goto srs_result; + } + + /* Hash the address with our secret, and compare that computed checksum + with the one extracted from the arg */ + + hmac_md5(sub[1], srs_recipient, cksum, sizeof(cksum)); + if (Ustrncmp(cksum, sub[0] + ovec[2], 4) != 0) + { + DEBUG(D_expand) debug_printf("SRS checksum mismatch\n"); + goto srs_result; + } + boolvalue = TRUE; + +srs_result: + if (yield) *yield = (boolvalue == testfor); return s; } +#endif /*EXPERIMENTAL_SRS_NATIVE*/ /* Unknown condition */ @@ -3501,51 +3815,6 @@ FAILED: -/************************************************* -* Handle MD5 or SHA-1 computation for HMAC * -*************************************************/ - -/* These are some wrapping functions that enable the HMAC code to be a bit -cleaner. A good compiler will spot the tail recursion. - -Arguments: - type HMAC_MD5 or HMAC_SHA1 - remaining are as for the cryptographic hash functions - -Returns: nothing -*/ - -static void -chash_start(int type, void *base) -{ -if (type == HMAC_MD5) - md5_start((md5 *)base); -else - sha1_start((hctx *)base); -} - -static void -chash_mid(int type, void *base, uschar *string) -{ -if (type == HMAC_MD5) - md5_mid((md5 *)base, string); -else - sha1_mid((hctx *)base, string); -} - -static void -chash_end(int type, void *base, uschar *string, int length, uschar *digest) -{ -if (type == HMAC_MD5) - md5_end((md5 *)base, string, length, digest); -else - sha1_end((hctx *)base, string, length, digest); -} - - - - - /******************************************************** * prvs: Get last three digits of days since Jan 1, 1970 * ********************************************************/ @@ -3605,7 +3874,7 @@ uschar innerkey[64]; uschar outerkey[64]; uschar *finalhash_hex; -if (key_num == NULL) +if (!key_num) key_num = US"0"; if (Ustrlen(key) > 64) @@ -3813,13 +4082,13 @@ eval_op_mult(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; int_eximarith_t x = eval_op_unary(&s, decimal, error); -if (*error == NULL) +if (!*error) { while (*s == '*' || *s == '/' || *s == '%') { int op = *s++; int_eximarith_t y = eval_op_unary(&s, decimal, error); - if (*error != NULL) break; + if (*error) break; /* SIGFPE both on div/mod by zero and on INT_MIN / -1, which would give * a value of INT_MAX+1. Note that INT_MIN * -1 gives INT_MIN for me, which * is a bug somewhere in [gcc 4.2.1, FreeBSD, amd64]. In fact, -N*-M where @@ -3900,7 +4169,7 @@ eval_op_shift(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; int_eximarith_t x = eval_op_sum(&s, decimal, error); -if (*error == NULL) +if (!*error) { while ((*s == '<' || *s == '>') && s[1] == s[0]) { @@ -3908,7 +4177,7 @@ if (*error == NULL) int op = *s++; s++; y = eval_op_sum(&s, decimal, error); - if (*error != NULL) break; + if (*error) break; if (op == '<') x <<= y; else x >>= y; } } @@ -3922,14 +4191,14 @@ eval_op_and(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; int_eximarith_t x = eval_op_shift(&s, decimal, error); -if (*error == NULL) +if (!*error) { while (*s == '&') { int_eximarith_t y; s++; y = eval_op_shift(&s, decimal, error); - if (*error != NULL) break; + if (*error) break; x &= y; } } @@ -3943,14 +4212,14 @@ eval_op_xor(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; int_eximarith_t x = eval_op_and(&s, decimal, error); -if (*error == NULL) +if (!*error) { while (*s == '^') { int_eximarith_t y; s++; y = eval_op_and(&s, decimal, error); - if (*error != NULL) break; + if (*error) break; x ^= y; } } @@ -3964,14 +4233,14 @@ eval_op_or(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; int_eximarith_t x = eval_op_xor(&s, decimal, error); -if (*error == NULL) +if (!*error) { while (*s == '|') { int_eximarith_t y; s++; y = eval_op_xor(&s, decimal, error); - if (*error != NULL) break; + if (*error) break; x |= y; } } @@ -4232,7 +4501,7 @@ while (*s != 0) if (!value) { - if (Ustrchr(name, '}') != NULL) malformed_header = TRUE; + if (Ustrchr(name, '}')) malformed_header = TRUE; continue; } } @@ -4412,8 +4681,8 @@ while (*s != 0) save_expand_strings(save_expand_nstring, save_expand_nlength); while (isspace(*s)) s++; - next_s = eval_condition(s, &resetok, skipping ? NULL : &cond); - if (next_s == NULL) goto EXPAND_FAILED; /* message already set */ + if (!(next_s = eval_condition(s, &resetok, skipping ? NULL : &cond))) + goto EXPAND_FAILED; /* message already set */ DEBUG(D_expand) DEBUG(D_noutf8) @@ -4472,7 +4741,7 @@ while (*s != 0) case 3: goto EXPAND_FAILED; } - if (sub_arg[1] == NULL) /* One argument */ + if (!sub_arg[1]) /* One argument */ { sub_arg[1] = US"/"; /* default separator */ sub_arg[2] = NULL; @@ -4574,7 +4843,7 @@ while (*s != 0) if (!mac_islookup(stype, lookup_querystyle|lookup_absfilequery)) { - if (key == NULL) + if (!key) { expand_string_message = string_sprintf("missing {key} for single-" "key \"%s\" lookup", name); @@ -4583,7 +4852,7 @@ while (*s != 0) } else { - if (key != NULL) + if (key) { expand_string_message = string_sprintf("a single key was given for " "lookup type \"%s\", which is not a single-key lookup type", name); @@ -4601,8 +4870,8 @@ while (*s != 0) expand_string_message = US"missing '{' for lookup file-or-query arg"; goto EXPAND_FAILED_CURLY; } - filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); - if (filename == NULL) goto EXPAND_FAILED; + if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))) + goto EXPAND_FAILED; if (*s++ != '}') { expand_string_message = US"missing '}' closing lookup file-or-query arg"; @@ -4653,7 +4922,7 @@ while (*s != 0) else { void *handle = search_open(filename, stype, 0, NULL, NULL); - if (handle == NULL) + if (!handle) { expand_string_message = search_error_message; goto EXPAND_FAILED; @@ -4735,15 +5004,14 @@ while (*s != 0) if (!opt_perl_started) { uschar *initerror; - if (opt_perl_startup == NULL) + if (!opt_perl_startup) { expand_string_message = US"A setting of perl_startup is needed when " "using the Perl interpreter"; goto EXPAND_FAILED; } DEBUG(D_any) debug_printf("Starting Perl interpreter\n"); - initerror = init_perl(opt_perl_startup); - if (initerror != NULL) + if ((initerror = init_perl(opt_perl_startup))) { expand_string_message = string_sprintf("error in perl_startup code: %s\n", initerror); @@ -4762,9 +5030,9 @@ while (*s != 0) NULL, the yield was undef, indicating a forced failure. Otherwise the message will indicate some kind of Perl error. */ - if (new_yield == NULL) + if (!new_yield) { - if (expand_string_message == NULL) + if (!expand_string_message) { expand_string_message = string_sprintf("Perl subroutine \"%s\" returned undef to force " @@ -5026,7 +5294,7 @@ while (*s != 0) { 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]; @@ -5259,7 +5527,7 @@ while (*s != 0) if (sigalrm_seen) { - yield->ptr = save_ptr; + if (yield) yield->ptr = save_ptr; expand_string_message = US "socket read timed out"; goto SOCK_FAIL; } @@ -5270,7 +5538,7 @@ while (*s != 0) if (*s == '{') { - if (expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok) == NULL) + if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok)) goto EXPAND_FAILED; if (*s++ != '}') { @@ -5329,8 +5597,8 @@ while (*s != 0) expand_string_message = US"missing '{' for command arg of run"; goto EXPAND_FAILED_CURLY; } - arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); - if (arg == NULL) goto EXPAND_FAILED; + if (!(arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))) + goto EXPAND_FAILED; while (isspace(*s)) s++; if (*s++ != '}') { @@ -5356,7 +5624,8 @@ while (*s != 0) /* 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)); @@ -5426,7 +5695,7 @@ while (*s != 0) case EITEM_TR: { - int oldptr = yield->ptr; + int oldptr = gstring_length(yield); int o2m; uschar *sub[3]; @@ -5443,7 +5712,7 @@ while (*s != 0) if (o2m >= 0) for (; oldptr < yield->ptr; oldptr++) { uschar *m = Ustrrchr(sub[1], yield->s[oldptr]); - if (m != NULL) + if (m) { int o = m - sub[1]; yield->s[oldptr] = sub[2][(o < o2m)? o : o2m]; @@ -5482,7 +5751,7 @@ while (*s != 0) string to the last position and make ${length{n}{str}} equivalent to ${substr{0}{n}{str}}. See the defaults for val[] above. */ - if (sub[2] == NULL) + if (!sub[2]) { sub[2] = sub[1]; sub[1] = NULL; @@ -5505,13 +5774,13 @@ while (*s != 0) } ret = - (item_type == EITEM_HASH)? - compute_hash(sub[2], val[0], val[1], &len) : - (item_type == EITEM_NHASH)? - compute_nhash(sub[2], val[0], val[1], &len) : - extract_substr(sub[2], val[0], val[1], &len); - - if (ret == NULL) goto EXPAND_FAILED; + item_type == EITEM_HASH + ? compute_hash(sub[2], val[0], val[1], &len) + : item_type == EITEM_NHASH + ? compute_nhash(sub[2], val[0], val[1], &len) + : extract_substr(sub[2], val[0], val[1], &len); + if (!ret) + goto EXPAND_FAILED; yield = string_catn(yield, ret, len); continue; } @@ -5651,10 +5920,8 @@ while (*s != 0) /* Compile the regular expression */ - re = pcre_compile(CS sub[1], PCRE_COPT, (const char **)&rerror, &roffset, - NULL); - - if (re == NULL) + if (!(re = pcre_compile(CS sub[1], PCRE_COPT, CCSS &rerror, + &roffset, NULL))) { expand_string_message = string_sprintf("regular expression error in " "\"%s\": %s at offset %d", sub[1], rerror, roffset); @@ -5710,8 +5977,8 @@ while (*s != 0) /* Copy the characters before the match, plus the expanded insertion. */ yield = string_catn(yield, subject + moffset, ovector[0] - moffset); - insert = expand_string(sub[2]); - if (insert == NULL) goto EXPAND_FAILED; + if (!(insert = expand_string(sub[2]))) + goto EXPAND_FAILED; yield = string_cat(yield, insert); moffset = ovector[1]; @@ -5805,8 +6072,8 @@ while (*s != 0) 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] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))) + goto EXPAND_FAILED; /*'{'*/ if (*s++ != '}') { expand_string_message = string_sprintf( @@ -6167,7 +6434,7 @@ while (*s != 0) 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; @@ -6181,8 +6448,8 @@ while (*s != 0) goto EXPAND_FAILED_CURLY; } - list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); - if (list == NULL) goto EXPAND_FAILED; + if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok))) + goto EXPAND_FAILED; if (*s++ != '}') { expand_string_message = @@ -6227,13 +6494,13 @@ while (*s != 0) if (item_type == EITEM_FILTER) { - temp = eval_condition(expr, &resetok, NULL); - if (temp != NULL) s = temp; + if ((temp = eval_condition(expr, &resetok, NULL))) + s = temp; } else temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); - if (temp == NULL) + if (!temp) { expand_string_message = string_sprintf("%s inside \"%s\" item", expand_string_message, name); @@ -6271,7 +6538,7 @@ while (*s != 0) if (item_type == EITEM_FILTER) { BOOL condresult; - if (eval_condition(expr, &resetok, &condresult) == NULL) + if (!eval_condition(expr, &resetok, &condresult)) { iterate_item = save_iterate_item; lookup_value = save_lookup_value; @@ -6293,7 +6560,7 @@ while (*s != 0) { uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok); temp = t; - if (temp == NULL) + if (!temp) { iterate_item = save_iterate_item; expand_string_message = string_sprintf("%s inside \"%s\" item", @@ -6314,7 +6581,8 @@ while (*s != 0) 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, @@ -6354,7 +6622,7 @@ while (*s != 0) 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 */ @@ -6581,7 +6849,7 @@ while (*s != 0) if (!(t = tree_search(dlobj_anchor, argv[0]))) { void *handle = dlopen(CS argv[0], RTLD_LAZY); - if (handle == NULL) + if (!handle) { expand_string_message = string_sprintf("dlopen \"%s\" failed: %s", argv[0], dlerror()); @@ -6597,8 +6865,7 @@ while (*s != 0) /* Having obtained the dynamically loaded object handle, look up the function pointer. */ - func = (exim_dlfunc_t *)dlsym(t->data.ptr, CS argv[1]); - if (func == NULL) + if (!(func = (exim_dlfunc_t *)dlsym(t->data.ptr, CS argv[1]))) { expand_string_message = string_sprintf("dlsym \"%s\" in \"%s\" failed: " "%s", argv[1], argv[0], dlerror()); @@ -6615,20 +6882,21 @@ while (*s != 0) resetok = FALSE; result = NULL; - for (argc = 0; argv[argc] != NULL; argc++); + for (argc = 0; argv[argc]; argc++); status = func(&result, argc - 2, &argv[2]); if(status == OK) { - if (result == NULL) result = US""; + if (!result) result = US""; yield = string_cat(yield, result); continue; } else { - expand_string_message = result == NULL ? US"(no message)" : result; - 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", + expand_string_message = result ? result : US"(no message)"; + 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); goto EXPAND_FAILED; } @@ -6668,6 +6936,62 @@ while (*s != 0) } continue; } + +#ifdef EXPERIMENTAL_SRS_NATIVE + case EITEM_SRS_ENCODE: + /* ${srs_encode {secret} {return_path} {orig_domain}} */ + { + uschar * sub[3]; + uschar cksum[4]; + + switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok)) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + yield = string_catn(yield, US"SRS0=", 5); + + /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */ + hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum)); + yield = string_catn(yield, cksum, sizeof(cksum)); + yield = string_catn(yield, US"=", 1); + + /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */ + { + struct timeval now; + unsigned long i; + gstring * g = NULL; + + gettimeofday(&now, NULL); + for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5) + g = string_catn(g, &base32_chars[i & 0x1f], 1); + if (g) while (g->ptr > 0) + yield = string_catn(yield, &g->s[--g->ptr], 1); + } + yield = string_catn(yield, US"=", 1); + + /* ${domain:$return_path}=${local_part:$return_path} */ + { + int start, end, domain; + uschar * t = parse_extract_address(sub[1], &expand_string_message, + &start, &end, &domain, FALSE); + if (!t) + goto EXPAND_FAILED; + + if (domain > 0) yield = string_cat(yield, t + domain); + yield = string_catn(yield, US"=", 1); + yield = domain > 0 + ? string_catn(yield, t, domain - 1) : string_cat(yield, t); + } + + /* @$original_domain */ + yield = string_catn(yield, US"@", 1); + yield = string_cat(yield, sub[2]); + continue; + } +#endif /*EXPERIMENTAL_SRS_NATIVE*/ } /* EITEM_* switch */ /* Control reaches here if the name is not recognized as one of the more @@ -6692,11 +7016,11 @@ while (*s != 0) if ((c = chop_match(name, op_table_underscore, nelem(op_table_underscore))) < 0) { - arg = Ustrchr(name, '_'); - if (arg != NULL) *arg = 0; - 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 */ + if ((arg = Ustrchr(name, '_'))) + *arg = 0; + if ((c = chop_match(name, op_table_main, nelem(op_table_main))) >= 0) + c += nelem(op_table_underscore); + if (arg) *arg++ = '_'; /* Put back for error messages */ } /* Deal specially with operators that might take a certificate variable @@ -6771,11 +7095,10 @@ while (*s != 0) { uschar *tt = sub; unsigned long int n = 0; - uschar * s; while (*tt) { uschar * t = Ustrchr(base32_chars, *tt++); - if (t == NULL) + if (!t) { expand_string_message = string_sprintf("argument for base32d " "operator is \"%s\", which is not a base 32 number", sub); @@ -6783,8 +7106,7 @@ while (*s != 0) } n = n * 32 + (t - base32_chars); } - s = string_sprintf("%ld", n); - yield = string_cat(yield, s); + yield = string_fmt_append(yield, "%ld", n); continue; } @@ -6798,8 +7120,7 @@ while (*s != 0) "operator is \"%s\", which is not a decimal number", sub); goto EXPAND_FAILED; } - t = string_base62(n); - yield = string_cat(yield, t); + yield = string_cat(yield, string_base62(n)); continue; } @@ -6812,7 +7133,7 @@ while (*s != 0) while (*tt != 0) { uschar *t = Ustrchr(base62_chars, *tt++); - if (t == NULL) + if (!t) { expand_string_message = string_sprintf("argument for base62d " "operator is \"%s\", which is not a base %d number", sub, @@ -6825,10 +7146,24 @@ while (*s != 0) 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); - if (expanded == NULL) + if (!expanded) { expand_string_message = string_sprintf("internal expansion of \"%s\" failed: %s", sub, @@ -7027,7 +7362,7 @@ while (*s != 0) int sep = 0; uschar buffer[256]; - while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; + while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer))) cnt++; yield = string_fmt_append(yield, "%d", cnt); continue; } @@ -7046,7 +7381,7 @@ while (*s != 0) uschar buffer[256]; if (*sub == '+') sub++; - if (arg == NULL) /* no-argument version */ + if (!arg) /* no-argument version */ { if (!(t = tree_search(addresslist_anchor, sub)) && !(t = tree_search(domainlist_anchor, sub)) && @@ -7219,7 +7554,7 @@ while (*s != 0) { 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++; @@ -7250,7 +7585,7 @@ while (*s != 0) 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 (;;) @@ -7279,7 +7614,7 @@ while (*s != 0) /* 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; } @@ -7296,9 +7631,9 @@ while (*s != 0) case EOP_QUOTE: case EOP_QUOTE_LOCAL_PART: - if (arg == NULL) + 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) @@ -7344,20 +7679,20 @@ while (*s != 0) int n; uschar *opt = Ustrchr(arg, '_'); - if (opt != NULL) *opt++ = 0; + if (opt) *opt++ = 0; - n = search_findtype(arg, Ustrlen(arg)); - if (n < 0) + if ((n = search_findtype(arg, Ustrlen(arg))) < 0) { expand_string_message = search_error_message; goto EXPAND_FAILED; } - if (lookup_list[n]->quote != NULL) + if (lookup_list[n]->quote) sub = (lookup_list[n]->quote)(sub, opt); - else if (opt != NULL) sub = NULL; + else if (opt) + sub = NULL; - if (sub == NULL) + if (!sub) { expand_string_message = string_sprintf( "\"%s\" unrecognized after \"${quote_%s\"", @@ -7404,7 +7739,7 @@ while (*s != 0) uschar *error; uschar *decoded = rfc2047_decode(sub, check_rfc2047_length, headers_charset, '?', &len, &error); - if (error != NULL) + if (error) { expand_string_message = error; goto EXPAND_FAILED; @@ -7418,10 +7753,10 @@ while (*s != 0) 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; @@ -7430,7 +7765,7 @@ while (*s != 0) continue; } - /* replace illegal UTF-8 sequences by replacement character */ + /* replace illegal UTF-8 sequences by replacement character */ #define UTF8_REPLACEMENT_CHAR US"?" @@ -7442,7 +7777,17 @@ while (*s != 0) 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++; @@ -7468,7 +7813,7 @@ while (*s != 0) } 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; @@ -7513,9 +7858,8 @@ while (*s != 0) * 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; } @@ -7766,14 +8110,13 @@ while (*s != 0) /* Perform the required operation */ - ret = - (c == EOP_HASH || c == EOP_H)? - compute_hash(sub, value1, value2, &len) : - (c == EOP_NHASH || c == EOP_NH)? - compute_nhash(sub, value1, value2, &len) : - extract_substr(sub, value1, value2, &len); + ret = c == EOP_HASH || c == EOP_H + ? compute_hash(sub, value1, value2, &len) + : c == EOP_NHASH || c == EOP_NH + ? compute_nhash(sub, value1, value2, &len) + : extract_substr(sub, value1, value2, &len); + if (!ret) goto EXPAND_FAILED; - if (ret == NULL) goto EXPAND_FAILED; yield = string_catn(yield, ret, len); continue; } @@ -8135,7 +8478,7 @@ uschar *endptr; /* If expansion failed, expand_string_message will be set. */ -if (s == NULL) return -1; +if (!s) return -1; /* On an overflow, strtol() returns LONG_MAX or LONG_MIN, and sets errno to ERANGE. When there isn't an overflow, errno is not changed, at least on some @@ -8230,10 +8573,9 @@ exp_bool(address_item *addr, uschar *svalue, BOOL *rvalue) { uschar *expanded; -if (svalue == NULL) { *rvalue = bvalue; return OK; } +if (!svalue) { *rvalue = bvalue; return OK; } -expanded = expand_string(svalue); -if (expanded == NULL) +if (!(expanded = expand_string(svalue))) { if (f.expand_string_forcedfail) { @@ -8302,7 +8644,7 @@ expand_file_big_buffer(const uschar * filename) { 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);