X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=beb72aa678470696e617dcc3fc874598c6ee3477;hb=06864c44d0dd88cbdd50af255501706c553eab8c;hp=83cd74962120a8e99548794da0ee66db3fdfb25d;hpb=184e88237dea64ce48076cdd0184612d057cbafd;p=exim.git diff --git a/src/src/expand.c b/src/src/expand.c index 83cd74962..beb72aa67 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.75 2007/01/08 10:50:18 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.92 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -106,17 +106,20 @@ alphabetical order. */ static uschar *item_table[] = { US"dlfunc", US"extract", + US"filter", US"hash", US"hmac", US"if", US"length", US"lookup", + US"map", US"nhash", US"perl", US"prvs", US"prvscheck", US"readfile", US"readsocket", + US"reduce", US"run", US"sg", US"substr", @@ -125,17 +128,20 @@ static uschar *item_table[] = { enum { EITEM_DLFUNC, EITEM_EXTRACT, + EITEM_FILTER, EITEM_HASH, EITEM_HMAC, EITEM_IF, EITEM_LENGTH, EITEM_LOOKUP, + EITEM_MAP, EITEM_NHASH, EITEM_PERL, EITEM_PRVS, EITEM_PRVSCHECK, EITEM_READFILE, EITEM_READSOCK, + EITEM_REDUCE, EITEM_RUN, EITEM_SG, EITEM_SUBSTR, @@ -162,6 +168,7 @@ enum { static uschar *op_table_main[] = { US"address", + US"addresses", US"base62", US"base62d", US"domain", @@ -181,6 +188,7 @@ static uschar *op_table_main[] = { US"nhash", US"quote", US"rfc2047", + US"rfc2047d", US"rxquote", US"s", US"sha1", @@ -192,6 +200,7 @@ static uschar *op_table_main[] = { enum { EOP_ADDRESS = sizeof(op_table_underscore)/sizeof(uschar *), + EOP_ADDRESSES, EOP_BASE62, EOP_BASE62D, EOP_DOMAIN, @@ -211,6 +220,7 @@ enum { EOP_NHASH, EOP_QUOTE, EOP_RFC2047, + EOP_RFC2047D, EOP_RXQUOTE, EOP_S, EOP_SHA1, @@ -238,6 +248,8 @@ static uschar *cond_table[] = { US"eqi", US"exists", US"first_delivery", + US"forall", + US"forany", US"ge", US"gei", US"gt", @@ -277,6 +289,8 @@ enum { ECOND_STR_EQI, ECOND_EXISTS, ECOND_FIRST_DELIVERY, + ECOND_FORALL, + ECOND_FORANY, ECOND_STR_GE, ECOND_STR_GEI, ECOND_STR_GT, @@ -335,7 +349,8 @@ enum { vtype_localpart, /* extract local part from string */ vtype_domain, /* extract domain from string */ vtype_recipients, /* extract recipients from recipients list */ - /* (enabled only during system filtering */ + /* (available only in system filters, ACLs, and */ + /* local_scan()) */ vtype_todbsdin, /* value not used; generate BSD inbox tod */ vtype_tode, /* value not used; generate tod in epoch format */ vtype_todf, /* value not used; generate full tod */ @@ -381,6 +396,10 @@ static var_entry var_table[] = { { "compile_date", vtype_stringptr, &version_date }, { "compile_number", vtype_stringptr, &version_cnumber }, { "csa_status", vtype_stringptr, &csa_status }, +#ifdef EXPERIMENTAL_DCC + { "dcc_header", vtype_stringptr, &dcc_header }, + { "dcc_result", vtype_stringptr, &dcc_result }, +#endif #ifdef WITH_OLD_DEMIME { "demime_errorlevel", vtype_int, &demime_errorlevel }, { "demime_reason", vtype_stringptr, &demime_reason }, @@ -397,8 +416,13 @@ static var_entry var_table[] = { { "dk_signsall", vtype_dk_verify, NULL }, { "dk_status", vtype_dk_verify, NULL }, { "dk_testing", vtype_dk_verify, NULL }, +#endif +#ifdef EXPERIMENTAL_DKIM + { "dkim_domain", vtype_stringptr, &dkim_signing_domain }, + { "dkim_selector", vtype_stringptr, &dkim_signing_selector }, #endif { "dnslist_domain", vtype_stringptr, &dnslist_domain }, + { "dnslist_matched", vtype_stringptr, &dnslist_matched }, { "dnslist_text", vtype_stringptr, &dnslist_text }, { "dnslist_value", vtype_stringptr, &dnslist_value }, { "domain", vtype_stringptr, &deliver_domain }, @@ -418,6 +442,7 @@ static var_entry var_table[] = { { "inode", vtype_ino, &deliver_inode }, { "interface_address", vtype_stringptr, &interface_address }, { "interface_port", vtype_int, &interface_port }, + { "item", vtype_stringptr, &iterate_item }, #ifdef LOOKUP_LDAP { "ldap_dn", vtype_stringptr, &eldap_dn }, #endif @@ -436,6 +461,7 @@ static var_entry var_table[] = { #ifdef WITH_CONTENT_SCAN { "malware_name", vtype_stringptr, &malware_name }, #endif + { "max_received_linelength", vtype_int, &max_received_linelength }, { "message_age", vtype_int, &message_age }, { "message_body", vtype_msgbody, &message_body }, { "message_body_end", vtype_msgbody_end, &message_body_end }, @@ -525,9 +551,13 @@ static var_entry var_table[] = { { "sender_rate_period", vtype_stringptr, &sender_rate_period }, { "sender_rcvhost", vtype_stringptr, &sender_rcvhost }, { "sender_verify_failure",vtype_stringptr, &sender_verify_failure }, + { "sending_ip_address", vtype_stringptr, &sending_ip_address }, + { "sending_port", vtype_int, &sending_port }, { "smtp_active_hostname", vtype_stringptr, &smtp_active_hostname }, { "smtp_command", vtype_stringptr, &smtp_cmd_buffer }, { "smtp_command_argument", vtype_stringptr, &smtp_cmd_argument }, + { "smtp_count_at_connection_start", vtype_int, &smtp_accept_count }, + { "smtp_notquit_reason", vtype_stringptr, &smtp_notquit_reason }, { "sn0", vtype_filter_int, &filter_sn[0] }, { "sn1", vtype_filter_int, &filter_sn[1] }, { "sn2", vtype_filter_int, &filter_sn[2] }, @@ -1425,7 +1455,7 @@ while (last > first) return var_buffer; case vtype_load_avg: - sprintf(CS var_buffer, "%d", os_getloadavg()); /* load_average */ + sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */ return var_buffer; case vtype_host_lookup: /* Lookup if not done so */ @@ -1484,9 +1514,15 @@ while (last > first) if (len > 0) { body[len] = 0; - while (len > 0) + if (message_body_newlines) /* Separate loops for efficiency */ + { + while (len > 0) + { if (body[--len] == 0) body[len] = ' '; } + } + else { - if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; + while (len > 0) + { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; } } } } @@ -1988,8 +2024,17 @@ switch(cond_type) if (!isalpha(name[0]) && yield != NULL) { - num[i] = expand_string_integer(sub[i], FALSE); - if (expand_string_message != NULL) return NULL; + if (sub[i][0] == 0) + { + num[i] = 0; + DEBUG(D_expand) + debug_printf("empty string cast to zero for numerical comparison\n"); + } + else + { + num[i] = expand_string_integer(sub[i], FALSE); + if (expand_string_message != NULL) return NULL; + } } } @@ -2335,6 +2380,68 @@ switch(cond_type) return ++s; + /* forall/forany: iterates a condition with different values */ + + case ECOND_FORALL: + case ECOND_FORANY: + { + int sep = 0; + uschar *save_iterate_item = iterate_item; + + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; + sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL)); + if (sub[0] == NULL) return NULL; + if (*s++ != '}') goto COND_FAILED_CURLY_END; + + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; + + sub[1] = s; + + /* Call eval_condition once, with result discarded (as if scanning a + "false" part). This allows us to find the end of the condition, because if + the list it empty, we won't actually evaluate the condition for real. */ + + s = eval_condition(sub[1], NULL); + if (s == NULL) + { + expand_string_message = string_sprintf("%s inside \"%s\" condition", + expand_string_message, name); + return NULL; + } + while (isspace(*s)) s++; + + if (*s++ != '}') + { + expand_string_message = string_sprintf("missing } at end of condition " + "inside \"%s\"", name); + return NULL; + } + + if (yield != NULL) *yield = !testfor; + while ((iterate_item = string_nextinlist(&sub[0], &sep, NULL, 0)) != NULL) + { + DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, iterate_item); + if (eval_condition(sub[1], &tempcond) == NULL) + { + expand_string_message = string_sprintf("%s inside \"%s\" condition", + expand_string_message, name); + iterate_item = save_iterate_item; + return NULL; + } + DEBUG(D_expand) debug_printf("%s: condition evaluated to %s\n", name, + tempcond? "true":"false"); + + if (yield != NULL) *yield = (tempcond == testfor); + if (tempcond == (cond_type == ECOND_FORANY)) break; + } + + iterate_item = save_iterate_item; + return s; + } + + /* Unknown condition */ default: @@ -3609,11 +3716,11 @@ while (*s != 0) *domain++ = '\0'; yield = string_cat(yield,&size,&ptr,US"prvs=",5); - string_cat(yield,&size,&ptr,sub_arg[0],Ustrlen(sub_arg[0])); - string_cat(yield,&size,&ptr,US"/",1); string_cat(yield,&size,&ptr,(sub_arg[2] != NULL) ? sub_arg[2] : US"0", 1); string_cat(yield,&size,&ptr,prvs_daystamp(7),3); string_cat(yield,&size,&ptr,p,6); + string_cat(yield,&size,&ptr,US"=",1); + string_cat(yield,&size,&ptr,sub_arg[0],Ustrlen(sub_arg[0])); string_cat(yield,&size,&ptr,US"@",1); string_cat(yield,&size,&ptr,domain,Ustrlen(domain)); @@ -3651,15 +3758,15 @@ while (*s != 0) case 3: goto EXPAND_FAILED; } - re = regex_must_compile(US"^prvs\\=(.+)\\/([0-9])([0-9]{3})([A-F0-9]{6})\\@(.+)$", + re = regex_must_compile(US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$", TRUE,FALSE); if (regex_match_and_setup(re,sub_arg[0],0,-1)) { - uschar *local_part = string_copyn(expand_nstring[1],expand_nlength[1]); - uschar *key_num = string_copyn(expand_nstring[2],expand_nlength[2]); - uschar *daystamp = string_copyn(expand_nstring[3],expand_nlength[3]); - uschar *hash = string_copyn(expand_nstring[4],expand_nlength[4]); + uschar *local_part = string_copyn(expand_nstring[4],expand_nlength[4]); + uschar *key_num = string_copyn(expand_nstring[1],expand_nlength[1]); + uschar *daystamp = string_copyn(expand_nstring[2],expand_nlength[2]); + uschar *hash = string_copyn(expand_nstring[3],expand_nlength[3]); uschar *domain = string_copyn(expand_nstring[5],expand_nlength[5]); DEBUG(D_expand) debug_printf("prvscheck localpart: %s\n", local_part); @@ -4601,6 +4708,184 @@ while (*s != 0) } + /* Handle list operations */ + + case EITEM_FILTER: + case EITEM_MAP: + case EITEM_REDUCE: + { + int sep = 0; + int save_ptr = ptr; + uschar outsep[2] = { '\0', '\0' }; + uschar *list, *expr, *temp; + uschar *save_iterate_item = iterate_item; + uschar *save_lookup_value = lookup_value; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + list = expand_string_internal(s, TRUE, &s, skipping); + if (list == NULL) goto EXPAND_FAILED; + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + if (item_type == EITEM_REDUCE) + { + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + temp = expand_string_internal(s, TRUE, &s, skipping); + if (temp == NULL) goto EXPAND_FAILED; + lookup_value = temp; + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + } + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + expr = s; + + /* For EITEM_FILTER, call eval_condition once, with result discarded (as + if scanning a "false" part). This allows us to find the end of the + condition, because if the list is empty, we won't actually evaluate the + condition for real. For EITEM_MAP and EITEM_REDUCE, do the same, using + the normal internal expansion function. */ + + if (item_type == EITEM_FILTER) + { + temp = eval_condition(expr, NULL); + if (temp != NULL) s = temp; + } + else + { + temp = expand_string_internal(s, TRUE, &s, TRUE); + } + + if (temp == NULL) + { + expand_string_message = string_sprintf("%s inside \"%s\" item", + expand_string_message, name); + goto EXPAND_FAILED; + } + + while (isspace(*s)) s++; + if (*s++ != '}') + { + expand_string_message = string_sprintf("missing } at end of condition " + "or expression inside \"%s\"", name); + goto EXPAND_FAILED; + } + + while (isspace(*s)) s++; + if (*s++ != '}') + { + expand_string_message = string_sprintf("missing } at end of \"%s\"", + name); + goto EXPAND_FAILED; + } + + /* If we are skipping, we can now just move on to the next item. When + processing for real, we perform the iteration. */ + + if (skipping) continue; + while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL) + { + *outsep = (uschar)sep; /* Separator as a string */ + + DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, iterate_item); + + if (item_type == EITEM_FILTER) + { + BOOL condresult; + if (eval_condition(expr, &condresult) == NULL) + { + iterate_item = save_iterate_item; + lookup_value = save_lookup_value; + expand_string_message = string_sprintf("%s inside \"%s\" condition", + expand_string_message, name); + goto EXPAND_FAILED; + } + DEBUG(D_expand) debug_printf("%s: condition is %s\n", name, + condresult? "true":"false"); + if (condresult) + temp = iterate_item; /* TRUE => include this item */ + else + continue; /* FALSE => skip this item */ + } + + /* EITEM_MAP and EITEM_REDUCE */ + + else + { + temp = expand_string_internal(expr, TRUE, NULL, skipping); + if (temp == NULL) + { + iterate_item = save_iterate_item; + expand_string_message = string_sprintf("%s inside \"%s\" item", + expand_string_message, name); + goto EXPAND_FAILED; + } + if (item_type == EITEM_REDUCE) + { + lookup_value = temp; /* Update the value of $value */ + continue; /* and continue the iteration */ + } + } + + /* We reach here for FILTER if the condition is true, always for MAP, + and never for REDUCE. The value in "temp" is to be added to the output + list that is being created, ensuring that any occurrences of the + separator character are doubled. Unless we are dealing with the first + item of the output list, add in a space if the new item begins with the + separator character, or is an empty string. */ + + if (ptr != save_ptr && (temp[0] == *outsep || temp[0] == 0)) + yield = string_cat(yield, &size, &ptr, US" ", 1); + + /* Add the string in "temp" to the output list that we are building, + This is done in chunks by searching for the separator character. */ + + for (;;) + { + size_t seglen = Ustrcspn(temp, outsep); + yield = string_cat(yield, &size, &ptr, temp, seglen + 1); + + /* If we got to the end of the string we output one character + too many; backup and end the loop. Otherwise arrange to double the + separator. */ + + if (temp[seglen] == '\0') { ptr--; break; } + yield = string_cat(yield, &size, &ptr, outsep, 1); + temp += seglen + 1; + } + + /* Output a separator after the string: we will remove the redundant + final one at the end. */ + + yield = string_cat(yield, &size, &ptr, outsep, 1); + } /* End of iteration over the list loop */ + + /* REDUCE has generated no output above: output the final value of + $value. */ + + if (item_type == EITEM_REDUCE) + { + yield = string_cat(yield, &size, &ptr, lookup_value, + Ustrlen(lookup_value)); + lookup_value = save_lookup_value; /* Restore $value */ + } + + /* FILTER and MAP generate lists: if they have generated anything, remove + the redundant final separator. Even though an empty item at the end of a + list does not count, this is tidier. */ + + else if (ptr != save_ptr) ptr--; + + /* Restore preserved $item */ + + iterate_item = save_iterate_item; + continue; + } + + /* If ${dlfunc support is configured, handle calling dynamically-loaded functions, unless locked out at this time. Syntax is ${dlfunc{file}{func}} or ${dlfunc{file}{func}{arg}} or ${dlfunc{file}{func}{arg1}{arg2}} or up to @@ -4961,6 +5246,69 @@ while (*s != 0) continue; } + case EOP_ADDRESSES: + { + uschar outsep[2] = { ':', '\0' }; + uschar *address, *error; + int save_ptr = ptr; + int start, end, domain; /* Not really used */ + + while (isspace(*sub)) sub++; + if (*sub == '>') { *outsep = *++sub; ++sub; } + parse_allow_group = TRUE; + + for (;;) + { + uschar *p = parse_find_address_end(sub, FALSE); + uschar saveend = *p; + *p = '\0'; + address = parse_extract_address(sub, &error, &start, &end, &domain, + FALSE); + *p = saveend; + + /* Add the address to the output list that we are building. This is + done in chunks by searching for the separator character. At the + start, unless we are dealing with the first address of the output + list, add in a space if the new address begins with the separator + character, or is an empty string. */ + + if (address != NULL) + { + if (ptr != save_ptr && address[0] == *outsep) + yield = string_cat(yield, &size, &ptr, US" ", 1); + + for (;;) + { + size_t seglen = Ustrcspn(address, outsep); + yield = string_cat(yield, &size, &ptr, address, seglen + 1); + + /* If we got to the end of the string we output one character + too many. */ + + if (address[seglen] == '\0') { ptr--; break; } + yield = string_cat(yield, &size, &ptr, outsep, 1); + address += seglen + 1; + } + + /* Output a separator after the string: we will remove the + redundant final one at the end. */ + + yield = string_cat(yield, &size, &ptr, outsep, 1); + } + + if (saveend == '\0') break; + sub = p + 1; + } + + /* If we have generated anything, remove the redundant final + separator. */ + + if (ptr != save_ptr) ptr--; + parse_allow_group = FALSE; + continue; + } + + /* quote puts a string in quotes if it is empty or contains anything other than alphamerics, underscore, dot, or hyphen. @@ -5072,6 +5420,23 @@ while (*s != 0) continue; } + /* RFC 2047 decode */ + + case EOP_RFC2047D: + { + int len; + uschar *error; + uschar *decoded = rfc2047_decode(sub, check_rfc2047_length, + headers_charset, '?', &len, &error); + if (error != NULL) + { + expand_string_message = error; + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, decoded, len); + continue; + } + /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into underscores */ @@ -5499,7 +5864,7 @@ systems, so we set it zero ourselves. */ errno = 0; expand_string_message = NULL; /* Indicates no error */ -value = strtol(CS s, CSS &endptr, 0); +value = strtol(CS s, CSS &endptr, 10); if (endptr == s) {