X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/b1c749bb7f147e7f9215fe6067c848cf02938b92..f78eb7c6264c5f1a4ec2fb24c39060e0686f7714:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index 7ecfc09d4..1d82a150c 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.26 2005/06/16 14:10:13 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.54 2006/02/10 14:25:43 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2006 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -57,6 +57,8 @@ static uschar *item_table[] = { US"lookup", US"nhash", US"perl", + US"prvs", + US"prvscheck", US"readfile", US"readsocket", US"run", @@ -74,6 +76,8 @@ enum { EITEM_LOOKUP, EITEM_NHASH, EITEM_PERL, + EITEM_PRVS, + EITEM_PRVSCHECK, EITEM_READFILE, EITEM_READSOCK, EITEM_RUN, @@ -191,6 +195,7 @@ static uschar *cond_table[] = { US"match", US"match_address", US"match_domain", + US"match_ip", US"match_local_part", US"or", US"pam", @@ -229,6 +234,7 @@ enum { ECOND_MATCH, ECOND_MATCH_ADDRESS, ECOND_MATCH_DOMAIN, + ECOND_MATCH_IP, ECOND_MATCH_LOCAL_PART, ECOND_OR, ECOND_PAM, @@ -292,26 +298,6 @@ enum { /* This table must be kept in alphabetical order. */ static var_entry var_table[] = { - { "acl_c0", vtype_stringptr, &acl_var[0] }, - { "acl_c1", vtype_stringptr, &acl_var[1] }, - { "acl_c2", vtype_stringptr, &acl_var[2] }, - { "acl_c3", vtype_stringptr, &acl_var[3] }, - { "acl_c4", vtype_stringptr, &acl_var[4] }, - { "acl_c5", vtype_stringptr, &acl_var[5] }, - { "acl_c6", vtype_stringptr, &acl_var[6] }, - { "acl_c7", vtype_stringptr, &acl_var[7] }, - { "acl_c8", vtype_stringptr, &acl_var[8] }, - { "acl_c9", vtype_stringptr, &acl_var[9] }, - { "acl_m0", vtype_stringptr, &acl_var[10] }, - { "acl_m1", vtype_stringptr, &acl_var[11] }, - { "acl_m2", vtype_stringptr, &acl_var[12] }, - { "acl_m3", vtype_stringptr, &acl_var[13] }, - { "acl_m4", vtype_stringptr, &acl_var[14] }, - { "acl_m5", vtype_stringptr, &acl_var[15] }, - { "acl_m6", vtype_stringptr, &acl_var[16] }, - { "acl_m7", vtype_stringptr, &acl_var[17] }, - { "acl_m8", vtype_stringptr, &acl_var[18] }, - { "acl_m9", vtype_stringptr, &acl_var[19] }, { "acl_verify_message", vtype_stringptr, &acl_verify_message }, { "address_data", vtype_stringptr, &deliver_address_data }, { "address_file", vtype_stringptr, &address_file }, @@ -393,6 +379,7 @@ static var_entry var_table[] = { { "message_body", vtype_msgbody, &message_body }, { "message_body_end", vtype_msgbody_end, &message_body_end }, { "message_body_size", vtype_int, &message_body_size }, + { "message_exim_id", vtype_stringptr, &message_id }, { "message_headers", vtype_msgheaders, NULL }, { "message_id", vtype_stringptr, &message_id }, { "message_linecount", vtype_int, &message_linecount }, @@ -433,6 +420,9 @@ static var_entry var_table[] = { { "parent_local_part", vtype_stringptr, &deliver_localpart_parent }, { "pid", vtype_pid, NULL }, { "primary_hostname", vtype_stringptr, &primary_hostname }, + { "prvscheck_address", vtype_stringptr, &prvscheck_address }, + { "prvscheck_keynum", vtype_stringptr, &prvscheck_keynum }, + { "prvscheck_result", vtype_stringptr, &prvscheck_result }, { "qualify_domain", vtype_stringptr, &qualify_domain_sender }, { "qualify_recipient", vtype_stringptr, &qualify_domain_recipient }, { "rcpt_count", vtype_int, &rcpt_count }, @@ -472,7 +462,8 @@ static var_entry var_table[] = { { "sender_rcvhost", vtype_stringptr, &sender_rcvhost }, { "sender_verify_failure",vtype_stringptr, &sender_verify_failure }, { "smtp_active_hostname", vtype_stringptr, &smtp_active_hostname }, - { "smtp_command_argument", vtype_stringptr, &smtp_command_argument }, + { "smtp_command", vtype_stringptr, &smtp_cmd_buffer }, + { "smtp_command_argument", vtype_stringptr, &smtp_cmd_argument }, { "sn0", vtype_filter_int, &filter_sn[0] }, { "sn1", vtype_filter_int, &filter_sn[1] }, { "sn2", vtype_filter_int, &filter_sn[2] }, @@ -1194,7 +1185,8 @@ else uschar *decoded, *error; while (ptr > yield && isspace(ptr[-1])) ptr--; *ptr = 0; - decoded = rfc2047_decode2(yield, TRUE, charset, '?', NULL, newsize, &error); + decoded = rfc2047_decode2(yield, check_rfc2047_length, charset, '?', NULL, + newsize, &error); if (error != NULL) { DEBUG(D_any) debug_printf("*** error in RFC 2047 decoding: %s\n" @@ -1237,6 +1229,48 @@ find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize) int first = 0; int last = var_table_size; +/* Handle ACL variables, which are not in the table because their number may +vary depending on a build-time setting. If the variable's name is not of the +form acl_mddd or acl_cddd, where the d's are digits, fall through to look for +other names that start with acl_. */ + +if (Ustrncmp(name, "acl_", 4) == 0) + { + uschar *endptr; + int offset = -1; + int max = 0; + + if (name[4] == 'm') + { + offset = ACL_CVARS; + max = ACL_MVARS; + } + else if (name[4] == 'c') + { + offset = 0; + max = ACL_CVARS; + } + + if (offset >= 0) + { + int n = Ustrtoul(name + 5, &endptr, 10); + if (*endptr == 0 && n < max) + return (acl_var[offset + n] == NULL)? US"" : acl_var[offset + n]; + } + } + +/* Similarly for $auth variables. */ + +if (Ustrncmp(name, "auth", 4) == 0) + { + uschar *endptr; + int n = Ustrtoul(name + 4, &endptr, 10); + if (*endptr == 0 && n != 0 && n <= AUTH_VARS) + return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1]; + } + +/* For all other variables, search the table */ + while (last > first) { uschar *s, *domain; @@ -1248,16 +1282,12 @@ while (last > first) if (c < 0) { last = middle; continue; } /* Found an existing variable. If in skipping state, the value isn't needed, - and we want to avoid processing (such as looking up up the host name). */ + and we want to avoid processing (such as looking up the host name). */ if (skipping) return US""; switch (var_table[middle].type) { - case vtype_filter_int: - if (!filter_running) return NULL; - /* Fall through */ - #ifdef EXPERIMENTAL_DOMAINKEYS case vtype_dk_verify: @@ -1274,35 +1304,39 @@ while (last > first) if (Ustrcmp(var_table[middle].name, "dk_sender_source") == 0) switch(dk_verify_block->address_source) { - case DK_EXIM_ADDRESS_NONE: s = "0"; break; - case DK_EXIM_ADDRESS_FROM_FROM: s = "from"; break; - case DK_EXIM_ADDRESS_FROM_SENDER: s = "sender"; break; + case DK_EXIM_ADDRESS_NONE: s = US"0"; break; + case DK_EXIM_ADDRESS_FROM_FROM: s = US"from"; break; + case DK_EXIM_ADDRESS_FROM_SENDER: s = US"sender"; break; } if (Ustrcmp(var_table[middle].name, "dk_status") == 0) switch(dk_verify_block->result) { - case DK_EXIM_RESULT_ERR: s = "error"; break; - case DK_EXIM_RESULT_BAD_FORMAT: s = "bad format"; break; - case DK_EXIM_RESULT_NO_KEY: s = "no key"; break; - case DK_EXIM_RESULT_NO_SIGNATURE: s = "no signature"; break; - case DK_EXIM_RESULT_REVOKED: s = "revoked"; break; - case DK_EXIM_RESULT_NON_PARTICIPANT: s = "non-participant"; break; - case DK_EXIM_RESULT_GOOD: s = "good"; break; - case DK_EXIM_RESULT_BAD: s = "bad"; break; + case DK_EXIM_RESULT_ERR: s = US"error"; break; + case DK_EXIM_RESULT_BAD_FORMAT: s = US"bad format"; break; + case DK_EXIM_RESULT_NO_KEY: s = US"no key"; break; + case DK_EXIM_RESULT_NO_SIGNATURE: s = US"no signature"; break; + case DK_EXIM_RESULT_REVOKED: s = US"revoked"; break; + case DK_EXIM_RESULT_NON_PARTICIPANT: s = US"non-participant"; break; + case DK_EXIM_RESULT_GOOD: s = US"good"; break; + case DK_EXIM_RESULT_BAD: s = US"bad"; break; } if (Ustrcmp(var_table[middle].name, "dk_signsall") == 0) - s = (dk_verify_block->signsall)? "1" : "0"; + s = (dk_verify_block->signsall)? US"1" : US"0"; if (Ustrcmp(var_table[middle].name, "dk_testing") == 0) - s = (dk_verify_block->testing)? "1" : "0"; + s = (dk_verify_block->testing)? US"1" : US"0"; if (Ustrcmp(var_table[middle].name, "dk_is_signed") == 0) - s = (dk_verify_block->is_signed)? "1" : "0"; + s = (dk_verify_block->is_signed)? US"1" : US"0"; return (s == NULL)? US"" : s; #endif + case vtype_filter_int: + if (!filter_running) return NULL; + /* Fall through */ + /* VVVVVVVVVVVV */ case vtype_int: sprintf(CS var_buffer, "%d", *(int *)(var_table[middle].value)); /* Integer */ return var_buffer; @@ -1414,10 +1448,22 @@ while (last > first) return tod_stamp(tod_log_datestamp); case vtype_reply: /* Get reply address */ - s = find_header(US"reply-to:", exists_only, newsize, FALSE, + s = find_header(US"reply-to:", exists_only, newsize, TRUE, headers_charset); + if (s != NULL) while (isspace(*s)) s++; if (s == NULL || *s == 0) - s = find_header(US"from:", exists_only, newsize, FALSE, headers_charset); + { + *newsize = 0; /* For the *s==0 case */ + s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset); + } + if (s != NULL) + { + uschar *t; + while (isspace(*s)) s++; + for (t = s; *t != 0; t++) if (*t == '\n') *t = ' '; + while (t > s && isspace(t[-1])) t--; + *t = 0; + } return (s == NULL)? US"" : s; /* A recipients list is available only during system message filtering, @@ -1698,7 +1744,7 @@ switch(cond_type) case ECOND_ISIP4: case ECOND_ISIP6: rc = string_is_ip_address(sub[0], NULL); - *yield = ((cond_type == ECOND_ISIP)? (rc > 0) : + *yield = ((cond_type == ECOND_ISIP)? (rc != 0) : (cond_type == ECOND_ISIP4)? (rc == 4) : (rc == 6)) == testfor; break; @@ -1794,6 +1840,7 @@ switch(cond_type) variables if it succeeds match_address: matches in an address list match_domain: matches in a domain list + match_ip: matches a host list that is restricted to IP addresses match_local_part: matches in a local part list crypteq: encrypts plaintext and compares against an encrypted text, using crypt(), crypt16(), MD5 or SHA-1 @@ -1802,6 +1849,7 @@ switch(cond_type) case ECOND_MATCH: case ECOND_MATCH_ADDRESS: case ECOND_MATCH_DOMAIN: + case ECOND_MATCH_IP: case ECOND_MATCH_LOCAL_PART: case ECOND_CRYPTEQ: @@ -1955,11 +2003,46 @@ switch(cond_type) MCL_DOMAIN + MCL_NOEXPAND, TRUE, NULL); goto MATCHED_SOMETHING; + case ECOND_MATCH_IP: /* Match IP address in a host list */ + if (sub[0][0] != 0 && string_is_ip_address(sub[0], NULL) == 0) + { + expand_string_message = string_sprintf("\"%s\" is not an IP address", + sub[0]); + return NULL; + } + else + { + unsigned int *nullcache = NULL; + check_host_block cb; + + cb.host_name = US""; + cb.host_address = sub[0]; + + /* If the host address starts off ::ffff: it is an IPv6 address in + IPv4-compatible mode. Find the IPv4 part for checking against IPv4 + addresses. */ + + cb.host_ipv4 = (Ustrncmp(cb.host_address, "::ffff:", 7) == 0)? + cb.host_address + 7 : cb.host_address; + + rc = match_check_list( + &sub[1], /* the list */ + 0, /* separator character */ + &hostlist_anchor, /* anchor pointer */ + &nullcache, /* cache pointer */ + check_host, /* function for testing */ + &cb, /* argument for function */ + MCL_HOST, /* type of check */ + sub[0], /* text for debugging */ + NULL); /* where to pass back data */ + } + goto MATCHED_SOMETHING; + case ECOND_MATCH_LOCAL_PART: rc = match_isinlist(sub[0], &(sub[1]), 0, &localpartlist_anchor, NULL, MCL_LOCALPART + MCL_NOEXPAND, TRUE, NULL); /* Fall through */ - + /* VVVVVVVVVVVV */ MATCHED_SOMETHING: switch(rc) { @@ -2418,8 +2501,6 @@ return rc; - - /************************************************* * Handle MD5 or SHA-1 computation for HMAC * *************************************************/ @@ -2465,6 +2546,110 @@ else +/******************************************************** +* prvs: Get last three digits of days since Jan 1, 1970 * +********************************************************/ + +/* This is needed to implement the "prvs" BATV reverse + path signing scheme + +Argument: integer "days" offset to add or substract to + or from the current number of days. + +Returns: pointer to string containing the last three + digits of the number of days since Jan 1, 1970, + modified by the offset argument, NULL if there + was an error in the conversion. + +*/ + +static uschar * +prvs_daystamp(int day_offset) +{ +uschar *days = store_get(16); +(void)string_format(days, 16, TIME_T_FMT, + (time(NULL) + day_offset*86400)/86400); +return (Ustrlen(days) >= 3) ? &days[Ustrlen(days)-3] : US"100"; +} + + + +/******************************************************** +* prvs: perform HMAC-SHA1 computation of prvs bits * +********************************************************/ + +/* This is needed to implement the "prvs" BATV reverse + path signing scheme + +Arguments: + address RFC2821 Address to use + key The key to use (must be less than 64 characters + in size) + key_num Single-digit key number to use. Defaults to + '0' when NULL. + +Returns: pointer to string containing the first three + bytes of the final hash in hex format, NULL if + there was an error in the process. +*/ + +static uschar * +prvs_hmac_sha1(uschar *address, uschar *key, uschar *key_num, uschar *daystamp) +{ +uschar *hash_source, *p; +int size = 0,offset = 0,i; +sha1 sha1_base; +void *use_base = &sha1_base; +uschar innerhash[20]; +uschar finalhash[20]; +uschar innerkey[64]; +uschar outerkey[64]; +uschar *finalhash_hex = store_get(40); + +if (key_num == NULL) + key_num = US"0"; + +if (Ustrlen(key) > 64) + return NULL; + +hash_source = string_cat(NULL,&size,&offset,key_num,1); +string_cat(hash_source,&size,&offset,daystamp,3); +string_cat(hash_source,&size,&offset,address,Ustrlen(address)); +hash_source[offset] = '\0'; + +DEBUG(D_expand) debug_printf("prvs: hash source is '%s'\n", hash_source); + +memset(innerkey, 0x36, 64); +memset(outerkey, 0x5c, 64); + +for (i = 0; i < Ustrlen(key); i++) + { + innerkey[i] ^= key[i]; + outerkey[i] ^= key[i]; + } + +chash_start(HMAC_SHA1, use_base); +chash_mid(HMAC_SHA1, use_base, innerkey); +chash_end(HMAC_SHA1, use_base, hash_source, offset, innerhash); + +chash_start(HMAC_SHA1, use_base); +chash_mid(HMAC_SHA1, use_base, outerkey); +chash_end(HMAC_SHA1, use_base, innerhash, 20, finalhash); + +p = finalhash_hex; +for (i = 0; i < 3; i++) + { + *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; + *p++ = hex_digits[finalhash[i] & 0x0f]; + } +*p = '\0'; + +return finalhash_hex; +} + + + + /************************************************* * Join a file onto the output string * *************************************************/ @@ -2597,12 +2782,14 @@ uschar *s = *sptr; int x = eval_term(&s, decimal, error); if (*error == NULL) { - while (*s == '*' || *s == '/') + while (*s == '*' || *s == '/' || *s == '%') { int op = *s++; int y = eval_term(&s, decimal, error); if (*error != NULL) break; - if (op == '*') x *= y; else x /= y; + if (op == '*') x *= y; + else if (op == '/') x /= y; + else x %= y; } } *sptr = s; @@ -2977,7 +3164,7 @@ while (*s != 0) /* Check that a key was provided for those lookup types that need it, and was not supplied for those that use the query style. */ - if (!mac_islookup(stype, lookup_querystyle)) + if (!mac_islookup(stype, lookup_querystyle|lookup_absfilequery)) { if (key == NULL) { @@ -2997,7 +3184,9 @@ while (*s != 0) } /* Get the next string in brackets and expand it. It is the file name for - single-key+file lookups, and the whole query otherwise. */ + single-key+file lookups, and the whole query otherwise. In the case of + queries that also require a file name (e.g. sqlite), the file name comes + first. */ if (*s != '{') goto EXPAND_FAILED_CURLY; filename = expand_string_internal(s+1, TRUE, &s, skipping); @@ -3006,12 +3195,30 @@ while (*s != 0) while (isspace(*s)) s++; /* If this isn't a single-key+file lookup, re-arrange the variables - to be appropriate for the search_ functions. */ + to be appropriate for the search_ functions. For query-style lookups, + there is just a "key", and no file name. For the special query-style + + file types, the query (i.e. "key") starts with a file name. */ if (key == NULL) { + while (isspace(*filename)) filename++; key = filename; - filename = NULL; + + 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 skipping, don't do the next bit - just lookup_value == NULL, as if @@ -3163,6 +3370,209 @@ while (*s != 0) } #endif /* EXIM_PERL */ + /* Transform email address to "prvs" scheme to use + as BATV-signed return path */ + + case EITEM_PRVS: + { + uschar *sub_arg[3]; + uschar *p,*domain; + + switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, US"prvs")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + /* If skipping, we don't actually do anything */ + if (skipping) continue; + + /* sub_arg[0] is the address */ + domain = Ustrrchr(sub_arg[0],'@'); + if ( (domain == NULL) || (domain == sub_arg[0]) || (Ustrlen(domain) == 1) ) + { + expand_string_message = US"prvs first argument must be a qualified email address"; + goto EXPAND_FAILED; + } + + /* Calculate the hash. The second argument must be a single-digit + key number, or unset. */ + + if (sub_arg[2] != NULL && + (!isdigit(sub_arg[2][0]) || sub_arg[2][1] != 0)) + { + expand_string_message = US"prvs second argument must be a single digit"; + goto EXPAND_FAILED; + } + + p = prvs_hmac_sha1(sub_arg[0],sub_arg[1],sub_arg[2],prvs_daystamp(7)); + if (p == NULL) + { + expand_string_message = US"prvs hmac-sha1 conversion failed"; + goto EXPAND_FAILED; + } + + /* Now separate the domain from the local part */ + *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,domain,Ustrlen(domain)); + + continue; + } + + /* Check a prvs-encoded address for validity */ + + case EITEM_PRVSCHECK: + { + uschar *sub_arg[3]; + int mysize = 0, myptr = 0; + const pcre *re; + uschar *p; + + /* TF: Ugliness: We want to expand parameter 1 first, then set + up expansion variables that are used in the expansion of + parameter 2. So we clone the string for the first + expansion, where we only expand parameter 1. + + PH: Actually, that isn't necessary. The read_subs() function is + designed to work this way for the ${if and ${lookup expansions. I've + tidied the code. + */ + + /* Reset expansion variables */ + prvscheck_result = NULL; + prvscheck_address = NULL; + prvscheck_keynum = NULL; + + switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, US"prvs")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + 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 *domain = string_copyn(expand_nstring[5],expand_nlength[5]); + + DEBUG(D_expand) debug_printf("prvscheck localpart: %s\n", local_part); + DEBUG(D_expand) debug_printf("prvscheck key number: %s\n", key_num); + DEBUG(D_expand) debug_printf("prvscheck daystamp: %s\n", daystamp); + DEBUG(D_expand) debug_printf("prvscheck hash: %s\n", hash); + DEBUG(D_expand) debug_printf("prvscheck domain: %s\n", domain); + + /* Set up expansion variables */ + prvscheck_address = string_cat(NULL, &mysize, &myptr, local_part, Ustrlen(local_part)); + string_cat(prvscheck_address,&mysize,&myptr,US"@",1); + string_cat(prvscheck_address,&mysize,&myptr,domain,Ustrlen(domain)); + prvscheck_address[myptr] = '\0'; + prvscheck_keynum = string_copy(key_num); + + /* Now expand the second argument */ + switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, US"prvs")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + /* Now we have the key and can check the address. */ + + p = prvs_hmac_sha1(prvscheck_address, sub_arg[0], prvscheck_keynum, + daystamp); + + if (p == NULL) + { + expand_string_message = US"hmac-sha1 conversion failed"; + goto EXPAND_FAILED; + } + + DEBUG(D_expand) debug_printf("prvscheck: received hash is %s\n", hash); + DEBUG(D_expand) debug_printf("prvscheck: own hash is %s\n", p); + + if (Ustrcmp(p,hash) == 0) + { + /* Success, valid BATV address. Now check the expiry date. */ + uschar *now = prvs_daystamp(0); + unsigned int inow = 0,iexpire = 1; + + (void)sscanf(CS now,"%u",&inow); + (void)sscanf(CS daystamp,"%u",&iexpire); + + /* When "iexpire" is < 7, a "flip" has occured. + Adjust "inow" accordingly. */ + if ( (iexpire < 7) && (inow >= 993) ) inow = 0; + + if (iexpire > inow) + { + prvscheck_result = US"1"; + DEBUG(D_expand) debug_printf("prvscheck: success, $pvrs_result set to 1\n"); + } + else + { + prvscheck_result = NULL; + DEBUG(D_expand) debug_printf("prvscheck: signature expired, $pvrs_result unset\n"); + } + } + else + { + prvscheck_result = NULL; + DEBUG(D_expand) debug_printf("prvscheck: hash failure, $pvrs_result unset\n"); + } + + /* Now expand the final argument. We leave this till now so that + it can include $prvscheck_result. */ + + switch(read_subs(sub_arg, 1, 0, &s, skipping, TRUE, US"prvs")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + if (sub_arg[0] == NULL || *sub_arg[0] == '\0') + yield = string_cat(yield,&size,&ptr,prvscheck_address,Ustrlen(prvscheck_address)); + else + yield = string_cat(yield,&size,&ptr,sub_arg[0],Ustrlen(sub_arg[0])); + + /* Reset the "internal" variables afterwards, because they are in + dynamic store that will be reclaimed if the expansion succeeded. */ + + prvscheck_address = NULL; + prvscheck_keynum = NULL; + } + else + { + /* Does not look like a prvs encoded address, return the empty string. + We need to make sure all subs are expanded first, so as to skip over + the entire item. */ + + switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, US"prvs")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + } + + continue; + } + /* Handle "readfile" to insert an entire file */ case EITEM_READFILE: @@ -3197,7 +3607,7 @@ while (*s != 0) } yield = cat_file(f, yield, &size, &ptr, sub_arg[1]); - fclose(f); + (void)fclose(f); continue; } @@ -3290,7 +3700,7 @@ while (*s != 0) alarm(timeout); yield = cat_file(f, yield, &size, &ptr, sub_arg[3]); alarm(0); - fclose(f); + (void)fclose(f); /* After a timeout, we restore the pointer in the result, that is, make sure we add nothing from the socket. */ @@ -3387,7 +3797,7 @@ while (*s != 0) /* Nothing is written to the standard input. */ - close(fd_in); + (void)close(fd_in); /* Wait for the process to finish, applying the timeout, and inspect its return code for serious disasters. Simple non-zero returns are passed on. @@ -3418,7 +3828,7 @@ while (*s != 0) f = fdopen(fd_out, "rb"); lookup_value = NULL; lookup_value = cat_file(f, lookup_value, &lsize, &lptr, NULL); - fclose(f); + (void)fclose(f); } /* Process the yes/no strings; $value may be useful in both cases */ @@ -4017,6 +4427,8 @@ while (*s != 0) continue; } + /* Note that for Darwin and Cygwin, BASE_62 actually has the value 36 */ + case EOP_BASE62D: { uschar buf[16]; @@ -4028,10 +4440,11 @@ while (*s != 0) if (t == NULL) { expand_string_message = string_sprintf("argument for base62d " - "operator is \"%s\", which is not a base 62 number", sub); + "operator is \"%s\", which is not a base %d number", sub, + BASE_62); goto EXPAND_FAILED; } - n = n * 62 + (t - base62_chars); + n = n * BASE_62 + (t - base62_chars); } (void)sprintf(CS buf, "%ld", n); yield = string_cat(yield, &size, &ptr, buf, Ustrlen(buf)); @@ -4511,6 +4924,12 @@ while (*s != 0) mode_t mode; struct stat st; + if ((expand_forbid & RDO_EXISTS) != 0) + { + expand_string_message = US"Use of the stat() expansion is not permitted"; + goto EXPAND_FAILED; + } + if (stat(CS sub, &st) < 0) { expand_string_message = string_sprintf("stat(%s) failed: %s", @@ -4769,7 +5188,6 @@ return -2; } - /************************************************* ************************************************** * Stand-alone test program *