-/* $Cambridge: exim/src/src/expand.c,v 1.89 2007/08/22 10:10:23 ph10 Exp $ */
+/* $Cambridge: exim/src/src/expand.c,v 1.101 2009/10/14 14:48:41 nm4 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
US">",
US">=",
US"and",
+ US"bool",
US"crypteq",
US"def",
US"eq",
ECOND_NUM_G,
ECOND_NUM_GE,
ECOND_AND,
+ ECOND_BOOL,
ECOND_CRYPTEQ,
ECOND_DEF,
ECOND_STR_EQ,
vtype_load_avg, /* value not used; result is int from os_getloadavg */
vtype_pspace, /* partition space; value is T/F for spool/log */
vtype_pinodes /* partition inodes; value is T/F for spool/log */
-#ifdef EXPERIMENTAL_DOMAINKEYS
- ,vtype_dk_verify /* Serve request out of DomainKeys verification structure */
-#endif
+ #ifndef DISABLE_DKIM
+ ,vtype_dkim /* Lookup of value in DKIM signature */
+ #endif
};
/* This table must be kept in alphabetical order. */
{ "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 },
#endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
- { "dk_domain", vtype_stringptr, &dk_signing_domain },
- { "dk_is_signed", vtype_dk_verify, NULL },
- { "dk_result", vtype_dk_verify, NULL },
- { "dk_selector", vtype_stringptr, &dk_signing_selector },
- { "dk_sender", vtype_dk_verify, NULL },
- { "dk_sender_domain", vtype_dk_verify, NULL },
- { "dk_sender_local_part",vtype_dk_verify, NULL },
- { "dk_sender_source", vtype_dk_verify, NULL },
- { "dk_signsall", vtype_dk_verify, NULL },
- { "dk_status", vtype_dk_verify, NULL },
- { "dk_testing", vtype_dk_verify, NULL },
+#ifndef DISABLE_DKIM
+ { "dkim_algo", vtype_dkim, (void *)DKIM_ALGO },
+ { "dkim_bodylength", vtype_dkim, (void *)DKIM_BODYLENGTH },
+ { "dkim_canon_body", vtype_dkim, (void *)DKIM_CANON_BODY },
+ { "dkim_canon_headers", vtype_dkim, (void *)DKIM_CANON_HEADERS },
+ { "dkim_copiedheaders", vtype_dkim, (void *)DKIM_COPIEDHEADERS },
+ { "dkim_created", vtype_dkim, (void *)DKIM_CREATED },
+ { "dkim_domain", vtype_stringptr, &dkim_signing_domain },
+ { "dkim_expires", vtype_dkim, (void *)DKIM_EXPIRES },
+ { "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES },
+ { "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY },
+ { "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY },
+ { "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS },
+ { "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES },
+ { "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE },
+ { "dkim_key_testing", vtype_dkim, (void *)DKIM_KEY_TESTING },
+ { "dkim_selector", vtype_stringptr, &dkim_signing_selector },
+ { "dkim_signing_domains",vtype_stringptr, &dkim_signing_domains },
+ { "dkim_verify_reason", vtype_dkim, (void *)DKIM_VERIFY_REASON },
+ { "dkim_verify_status", vtype_dkim, (void *)DKIM_VERIFY_STATUS},
#endif
{ "dnslist_domain", vtype_stringptr, &dnslist_domain },
{ "dnslist_matched", vtype_stringptr, &dnslist_matched },
{ "spam_score_int", vtype_stringptr, &spam_score_int },
#endif
#ifdef EXPERIMENTAL_SPF
+ { "spf_guess", vtype_stringptr, &spf_guess },
{ "spf_header_comment", vtype_stringptr, &spf_header_comment },
{ "spf_received", vtype_stringptr, &spf_received },
{ "spf_result", vtype_stringptr, &spf_result },
switch (var_table[middle].type)
{
-#ifdef EXPERIMENTAL_DOMAINKEYS
-
- case vtype_dk_verify:
- if (dk_verify_block == NULL) return US"";
- s = NULL;
- if (Ustrcmp(var_table[middle].name, "dk_result") == 0)
- s = dk_verify_block->result_string;
- if (Ustrcmp(var_table[middle].name, "dk_sender") == 0)
- s = dk_verify_block->address;
- if (Ustrcmp(var_table[middle].name, "dk_sender_domain") == 0)
- s = dk_verify_block->domain;
- if (Ustrcmp(var_table[middle].name, "dk_sender_local_part") == 0)
- s = dk_verify_block->local_part;
-
- if (Ustrcmp(var_table[middle].name, "dk_sender_source") == 0)
- switch(dk_verify_block->address_source) {
- 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 = 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)? US"1" : US"0";
-
- if (Ustrcmp(var_table[middle].name, "dk_testing") == 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)? US"1" : US"0";
-
- return (s == NULL)? US"" : s;
-#endif
-
case vtype_filter_int:
if (!filter_running) return NULL;
/* Fall through */
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] = ' '; }
}
}
}
sprintf(CS var_buffer, "%d", inodes);
}
return var_buffer;
+
+ #ifndef DISABLE_DKIM
+ case vtype_dkim:
+ return dkim_exim_expand_query((int)(long)var_table[middle].value);
+ #endif
+
}
}
}
+ /* The bool{} expansion condition maps a string to boolean.
+ The values supported should match those supported by the ACL condition
+ (acl.c, ACLC_CONDITION) so that we keep to a minimum the different ideas
+ of true/false. Note that Router "condition" rules have a different
+ interpretation, where general data can be used and only a few values
+ map to FALSE.
+ Note that readconf.c boolean matching, for boolean configuration options,
+ only matches true/yes/false/no. */
+ case ECOND_BOOL:
+ {
+ uschar *sub_arg[1];
+ uschar *t;
+ size_t len;
+ BOOL boolvalue = FALSE;
+ while (isspace(*s)) s++;
+ if (*s != '{') goto COND_FAILED_CURLY_START;
+ switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, US"bool"))
+ {
+ case 1: expand_string_message = US"too few arguments or bracketing "
+ "error for bool";
+ /*FALLTHROUGH*/
+ case 2:
+ case 3: return NULL;
+ }
+ t = sub_arg[0];
+ while (isspace(*t)) t++;
+ len = Ustrlen(t);
+ DEBUG(D_expand)
+ debug_printf("considering bool: %s\n", len ? t : US"<empty>");
+ if (len == 0)
+ boolvalue = FALSE;
+ else if (Ustrspn(t, "0123456789") == len)
+ boolvalue = (Uatoi(t) == 0) ? FALSE : TRUE;
+ else if (strcmpic(t, US"true") == 0 || strcmpic(t, US"yes") == 0)
+ boolvalue = TRUE;
+ else if (strcmpic(t, US"false") == 0 || strcmpic(t, US"no") == 0)
+ boolvalue = FALSE;
+ else
+ {
+ expand_string_message = string_sprintf("unrecognised boolean "
+ "value \"%s\"", t);
+ return NULL;
+ }
+ if (yield != NULL) *yield = (boolvalue != 0);
+ return s;
+ }
+
/* Unknown condition */
default:
use that without copying. This is helpful for expanding strings like
$message_headers which can get very long.
+There's a problem if a ${dlfunc item has side-effects that cause allocation,
+since resetting the store at the end of the expansion will free store that was
+allocated by the plugin code as well as the slop after the expanded string. So
+we skip any resets if ${dlfunc has been used. This is an unfortunate
+consequence of string expansion becoming too powerful.
+
Arguments:
string the string to be expanded
ket_ends true if expansion is to stop at }
uschar *s = string;
uschar *save_expand_nstring[EXPAND_MAXN+1];
int save_expand_nlength[EXPAND_MAXN+1];
+BOOL resetok = TRUE;
expand_string_forcedfail = FALSE;
expand_string_message = US"";
if (ptr == 0 && yield != NULL)
{
- store_reset(yield);
+ if (resetok) store_reset(yield);
yield = NULL;
size = 0;
}
Adjust "inow" accordingly. */
if ( (iexpire < 7) && (inow >= 993) ) inow = 0;
- if (iexpire > inow)
+ if (iexpire >= inow)
{
prvscheck_result = US"1";
DEBUG(D_expand) debug_printf("prvscheck: success, $pvrs_result set to 1\n");
while (len > 0 && isspace(p[len-1])) len--;
p[len] = 0;
- if (*p == 0)
+ if (*p == 0 && !skipping)
{
expand_string_message = US"first argument of \"extract\" must "
"not be empty";
returns OK, we have a replacement string; if it returns DEFER then
expansion has failed in a non-forced manner; if it returns FAIL then
failure was forced; if it returns ERROR or any other value there's a
- problem, so panic slightly. */
+ problem, so panic slightly. In any case, assume that the function has
+ side-effects on the store that must be preserved. */
+ resetok = FALSE;
result = NULL;
for (argc = 0; argv[argc] != NULL; argc++);
status = func(&result, argc - 2, &argv[2]);
int newsize = 0;
if (ptr == 0)
{
- store_reset(yield);
+ if (resetok) store_reset(yield);
yield = NULL;
size = 0;
}
In many cases the final string will be the first one that was got and so there
will be optimal store usage. */
-store_reset(yield + ptr + 1);
+if (resetok) store_reset(yield + ptr + 1);
DEBUG(D_expand)
{
debug_printf("expanding: %.*s\n result: %s\n", (int)(s - string), string,
errno = 0;
expand_string_message = NULL; /* Indicates no error */
+
+/* Before Exim 4.64, strings consisting entirely of whitespace compared
+equal to 0. Unfortunately, people actually relied upon that, so preserve
+the behaviour explicitly. Stripping leading whitespace is a harmless
+noop change since strtol skips it anyway (provided that there is a number
+to find at all). */
+if (isspace(*s))
+ {
+ while (isspace(*s)) ++s;
+ if (*s == '\0')
+ {
+ DEBUG(D_expand)
+ debug_printf("treating blank string as number 0\n");
+ return 0;
+ }
+ }
+
value = strtol(CS s, CSS &endptr, 10);
if (endptr == s)