X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=2366eb8828f55f896e99ca093beaecdda28176c6;hp=366cd737a0ee29d7f03036e28da36f08ac6f87ea;hb=298849d8ea217fd104d167f5233bd11240b3ddae;hpb=5b195d6b9592fcef09b0b3b31390e73226deffc9 diff --git a/src/src/expand.c b/src/src/expand.c index 366cd737a..2366eb882 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 }, @@ -1627,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 @@ -1640,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 */ @@ -1660,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 @@ -1668,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); } } @@ -1746,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 * *************************************************/ @@ -2892,7 +2978,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 " @@ -4806,16 +4892,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 @@ -5208,7 +5291,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 +5332,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 +5525,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 +5622,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 +5693,7 @@ while (*s != 0) case EITEM_TR: { - int oldptr = yield->ptr; + int oldptr = gstring_length(yield); int o2m; uschar *sub[3]; @@ -5833,7 +5918,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 +6432,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 +6579,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 +6620,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 +7144,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 +7552,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 +7583,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 +7612,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 +7631,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 +7751,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 +7763,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 +7775,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 +7811,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 +7856,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 +8642,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);