X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=789f09907f074e27ff65984337a56ef6f8665fc4;hb=137ae145e066dda8f9d81cf6d2c9f76c15929605;hp=366cd737a0ee29d7f03036e28da36f08ac6f87ea;hpb=5b195d6b9592fcef09b0b3b31390e73226deffc9;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/expand.c b/src/src/expand.c index 366cd737a..789f09907 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -213,6 +213,7 @@ static uschar *op_table_main[] = { US"base62d", US"base64", US"base64d", + US"bless", US"domain", US"escape", US"escape8bit", @@ -260,6 +261,7 @@ enum { EOP_BASE62D, EOP_BASE64, EOP_BASE64D, + EOP_BLESS, EOP_DOMAIN, EOP_ESCAPE, EOP_ESCAPE8BIT, @@ -464,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. */ @@ -586,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 @@ -666,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 }, @@ -1167,7 +1174,7 @@ static uschar * expand_getkeyed(uschar * key, const uschar * s) { int length = Ustrlen(key); -while (isspace(*s)) s++; +Uskip_whitespace(&s); /* Loop to search for the key */ @@ -1179,14 +1186,13 @@ while (*s) 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; @@ -1627,8 +1633,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 @@ -1640,17 +1646,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 */ @@ -1660,7 +1661,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 @@ -1668,16 +1669,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); } } @@ -1746,6 +1743,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 * *************************************************/ @@ -1958,7 +2043,7 @@ switch (vp->type) 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 */ @@ -1969,8 +2054,8 @@ switch (vp->type) 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; } @@ -2058,7 +2143,7 @@ read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping, { const uschar *s = *sptr; -while (isspace(*s)) s++; +Uskip_whitespace(&s); for (int i = 0; i < n; i++) { if (*s != '{') @@ -2075,7 +2160,7 @@ for (int i = 0; i < n; i++) 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++ != '}') { @@ -2198,9 +2283,7 @@ uschar * p = 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++; @@ -2892,7 +2975,7 @@ switch(cond_type = identify_operator(&s, &opname)) 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 " @@ -3940,8 +4023,7 @@ int c; 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); @@ -3953,7 +4035,7 @@ if (isdigit((c = *s))) 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 == '(') { @@ -3975,7 +4057,7 @@ eval_op_unary(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; int_eximarith_t x; -while (isspace(*s)) s++; +Uskip_whitespace(&s); if (*s == '+' || *s == '-' || *s == '~') { int op = *s++; @@ -4693,7 +4775,7 @@ while (*s != 0) 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); @@ -4733,20 +4815,19 @@ while (*s != 0) 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; @@ -4806,16 +4887,13 @@ while (*s != 0) 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 @@ -4842,7 +4920,7 @@ while (*s != 0) 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 = @@ -5208,7 +5286,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]; @@ -5249,8 +5327,9 @@ while (*s != 0) 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; @@ -5441,7 +5520,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; } @@ -5538,7 +5617,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)); @@ -5608,7 +5688,7 @@ while (*s != 0) case EITEM_TR: { - int oldptr = yield->ptr; + int oldptr = gstring_length(yield); int o2m; uschar *sub[3]; @@ -5833,7 +5913,7 @@ while (*s != 0) /* 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 " @@ -6347,7 +6427,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; @@ -6494,7 +6574,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, @@ -6534,7 +6615,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 */ @@ -7058,6 +7139,20 @@ 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); @@ -7452,7 +7547,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++; @@ -7483,7 +7578,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 (;;) @@ -7512,7 +7607,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; } @@ -7531,7 +7626,7 @@ while (*s != 0) 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) @@ -7651,10 +7746,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; @@ -7663,7 +7758,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"?" @@ -7675,7 +7770,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++; @@ -7701,7 +7806,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; @@ -7746,9 +7851,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; } @@ -8533,7 +8637,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);