-/* $Cambridge: exim/src/src/expand.c,v 1.5 2004/11/17 15:21:10 ph10 Exp $ */
+/* $Cambridge: exim/src/src/expand.c,v 1.18 2005/03/22 16:52:06 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2005 */
/* See the file NOTICE for conditions of use and distribution. */
alphabetical order. */
static uschar *item_table[] = {
+ US"dlfunc",
US"extract",
US"hash",
US"hmac",
US"length",
US"lookup",
US"nhash",
- #ifdef EXIM_PERL
- US"perl",
- #endif
+ US"perl",
US"readfile",
US"readsocket",
US"run",
US"tr" };
enum {
+ EITEM_DLFUNC,
EITEM_EXTRACT,
EITEM_HASH,
EITEM_HMAC,
EITEM_LENGTH,
EITEM_LOOKUP,
EITEM_NHASH,
- #ifdef EXIM_PERL
- EITEM_PERL,
- #endif
+ EITEM_PERL,
EITEM_READFILE,
EITEM_READSOCK,
EITEM_RUN,
vtype_host_lookup, /* value not used; get host name */
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 */
+ 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
};
/* This table must be kept in alphabetical order. */
{ "authenticated_id", vtype_stringptr, &authenticated_id },
{ "authenticated_sender",vtype_stringptr, &authenticated_sender },
{ "authentication_failed",vtype_int, &authentication_failed },
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+ { "bmi_alt_location", vtype_stringptr, &bmi_alt_location },
+ { "bmi_base64_tracker_verdict", vtype_stringptr, &bmi_base64_tracker_verdict },
+ { "bmi_base64_verdict", vtype_stringptr, &bmi_base64_verdict },
+ { "bmi_deliver", vtype_int, &bmi_deliver },
+#endif
{ "body_linecount", vtype_int, &body_linecount },
{ "body_zerocount", vtype_int, &body_zerocount },
{ "bounce_recipient", vtype_stringptr, &bounce_recipient },
{ "caller_uid", vtype_uid, &real_uid },
{ "compile_date", vtype_stringptr, &version_date },
{ "compile_number", vtype_stringptr, &version_cnumber },
+#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 },
+#endif
{ "dnslist_domain", vtype_stringptr, &dnslist_domain },
{ "dnslist_text", vtype_stringptr, &dnslist_text },
{ "dnslist_value", vtype_stringptr, &dnslist_value },
{ "exim_gid", vtype_gid, &exim_gid },
{ "exim_path", vtype_stringptr, &exim_path },
{ "exim_uid", vtype_uid, &exim_uid },
+#ifdef WITH_OLD_DEMIME
+ { "found_extension", vtype_stringptr, &found_extension },
+#endif
{ "home", vtype_stringptr, &deliver_home },
{ "host", vtype_stringptr, &deliver_host },
{ "host_address", vtype_stringptr, &deliver_host_address },
{ "host_data", vtype_stringptr, &host_data },
+ { "host_lookup_deferred",vtype_int, &host_lookup_deferred },
{ "host_lookup_failed", vtype_int, &host_lookup_failed },
{ "inode", vtype_ino, &deliver_inode },
{ "interface_address", vtype_stringptr, &interface_address },
{ "local_user_uid", vtype_uid, &local_user_uid },
{ "localhost_number", vtype_int, &host_number },
{ "log_inodes", vtype_pinodes, (void *)FALSE },
- { "log_space", vtype_pspace, (void *)FALSE },
+ { "log_space", vtype_pspace, (void *)FALSE },
{ "mailstore_basename", vtype_stringptr, &mailstore_basename },
+#ifdef WITH_CONTENT_SCAN
+ { "malware_name", vtype_stringptr, &malware_name },
+#endif
{ "message_age", vtype_int, &message_age },
{ "message_body", vtype_msgbody, &message_body },
{ "message_body_end", vtype_msgbody_end, &message_body_end },
{ "message_headers", vtype_msgheaders, NULL },
{ "message_id", vtype_stringptr, &message_id },
{ "message_size", vtype_int, &message_size },
+#ifdef WITH_CONTENT_SCAN
+ { "mime_anomaly_level", vtype_int, &mime_anomaly_level },
+ { "mime_anomaly_text", vtype_stringptr, &mime_anomaly_text },
+ { "mime_boundary", vtype_stringptr, &mime_boundary },
+ { "mime_charset", vtype_stringptr, &mime_charset },
+ { "mime_content_description", vtype_stringptr, &mime_content_description },
+ { "mime_content_disposition", vtype_stringptr, &mime_content_disposition },
+ { "mime_content_id", vtype_stringptr, &mime_content_id },
+ { "mime_content_size", vtype_int, &mime_content_size },
+ { "mime_content_transfer_encoding",vtype_stringptr, &mime_content_transfer_encoding },
+ { "mime_content_type", vtype_stringptr, &mime_content_type },
+ { "mime_decoded_filename", vtype_stringptr, &mime_decoded_filename },
+ { "mime_filename", vtype_stringptr, &mime_filename },
+ { "mime_is_coverletter", vtype_int, &mime_is_coverletter },
+ { "mime_is_multipart", vtype_int, &mime_is_multipart },
+ { "mime_is_rfc822", vtype_int, &mime_is_rfc822 },
+ { "mime_part_count", vtype_int, &mime_part_count },
+#endif
{ "n0", vtype_filter_int, &filter_n[0] },
{ "n1", vtype_filter_int, &filter_n[1] },
{ "n2", vtype_filter_int, &filter_n[2] },
{ "received_count", vtype_int, &received_count },
{ "received_for", vtype_stringptr, &received_for },
{ "received_protocol", vtype_stringptr, &received_protocol },
+ { "received_time", vtype_int, &received_time },
{ "recipient_data", vtype_stringptr, &recipient_data },
- { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure },
+ { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure },
{ "recipients", vtype_recipients, NULL },
{ "recipients_count", vtype_int, &recipients_count },
+#ifdef WITH_CONTENT_SCAN
+ { "regex_match_string", vtype_stringptr, ®ex_match_string },
+#endif
{ "reply_address", vtype_reply, NULL },
{ "return_path", vtype_stringptr, &return_path },
{ "return_size_limit", vtype_int, &bounce_return_size_limit },
{ "sender_host_port", vtype_int, &sender_host_port },
{ "sender_ident", vtype_stringptr, &sender_ident },
{ "sender_rcvhost", vtype_stringptr, &sender_rcvhost },
- { "sender_verify_failure",vtype_stringptr, &sender_verify_failure },
+ { "sender_verify_failure",vtype_stringptr, &sender_verify_failure },
+ { "smtp_active_hostname", vtype_stringptr, &smtp_active_hostname },
{ "smtp_command_argument", vtype_stringptr, &smtp_command_argument },
{ "sn0", vtype_filter_int, &filter_sn[0] },
{ "sn1", vtype_filter_int, &filter_sn[1] },
{ "sn7", vtype_filter_int, &filter_sn[7] },
{ "sn8", vtype_filter_int, &filter_sn[8] },
{ "sn9", vtype_filter_int, &filter_sn[9] },
+#ifdef WITH_CONTENT_SCAN
+ { "spam_bar", vtype_stringptr, &spam_bar },
+ { "spam_report", vtype_stringptr, &spam_report },
+ { "spam_score", vtype_stringptr, &spam_score },
+ { "spam_score_int", vtype_stringptr, &spam_score_int },
+#endif
+#ifdef EXPERIMENTAL_SPF
+ { "spf_header_comment", vtype_stringptr, &spf_header_comment },
+ { "spf_received", vtype_stringptr, &spf_received },
+ { "spf_result", vtype_stringptr, &spf_result },
+ { "spf_smtp_comment", vtype_stringptr, &spf_smtp_comment },
+#endif
{ "spool_directory", vtype_stringptr, &spool_directory },
{ "spool_inodes", vtype_pinodes, (void *)TRUE },
- { "spool_space", vtype_pspace, (void *)TRUE },
+ { "spool_space", vtype_pspace, (void *)TRUE },
+#ifdef EXPERIMENTAL_SRS
+ { "srs_db_address", vtype_stringptr, &srs_db_address },
+ { "srs_db_key", vtype_stringptr, &srs_db_key },
+ { "srs_orig_recipient", vtype_stringptr, &srs_orig_recipient },
+ { "srs_orig_sender", vtype_stringptr, &srs_orig_sender },
+ { "srs_recipient", vtype_stringptr, &srs_recipient },
+ { "srs_status", vtype_stringptr, &srs_status },
+#endif
{ "thisaddress", vtype_stringptr, &filter_thisaddress },
{ "tls_certificate_verified", vtype_int, &tls_certificate_verified },
{ "tls_cipher", vtype_stringptr, &tls_cipher },
if (!filter_running) return NULL;
/* Fall through */
+#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 = "0"; break;
+ case DK_EXIM_ADDRESS_FROM_FROM: s = "from"; break;
+ case DK_EXIM_ADDRESS_FROM_SENDER: s = "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;
+ }
+
+ if (Ustrcmp(var_table[middle].name, "dk_signsall") == 0)
+ s = (dk_verify_block->signsall)? "1" : "0";
+
+ if (Ustrcmp(var_table[middle].name, "dk_testing") == 0)
+ s = (dk_verify_block->testing)? "1" : "0";
+
+ if (Ustrcmp(var_table[middle].name, "dk_is_signed") == 0)
+ s = (dk_verify_block->is_signed)? "1" : "0";
+
+ return (s == NULL)? US"" : s;
+#endif
+
case vtype_int:
sprintf(CS var_buffer, "%d", *(int *)(var_table[middle].value)); /* Integer */
return var_buffer;
s[ptr] = 0; /* string_cat() leaves room */
}
return s;
-
+
case vtype_pspace:
{
int inodes;
- sprintf(CS var_buffer, "%d",
- receive_statvfs((BOOL)(var_table[middle].value), &inodes));
+ sprintf(CS var_buffer, "%d",
+ receive_statvfs(var_table[middle].value == (void *)TRUE, &inodes));
}
return var_buffer;
-
+
case vtype_pinodes:
{
int inodes;
- (void) receive_statvfs((BOOL)(var_table[middle].value), &inodes);
+ (void) receive_statvfs(var_table[middle].value == (void *)TRUE, &inodes);
sprintf(CS var_buffer, "%d", inodes);
}
return var_buffer;
uschar *sub1, *sub2;
/* If there are no following strings, we substitute the contents of $value for
-lookups and for extractions in the success case. In the fail case, nothing is
-substituted. In the case of "if", lack of following strings is an error. */
+lookups and for extractions in the success case. For the ${if item, the string
+"true" is substituted. In the fail case, nothing is substituted for all three
+items. */
while (isspace(*s)) s++;
if (*s == '}')
{
- if (type[0] == 'i') goto FAILED_CURLY;
- if (yes && lookup_value != NULL)
- *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, lookup_value,
- Ustrlen(lookup_value));
- lookup_value = save_lookup;
+ if (type[0] == 'i')
+ {
+ if (yes) *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, US"true", 4);
+ }
+ else
+ {
+ if (yes && lookup_value != NULL)
+ *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, lookup_value,
+ Ustrlen(lookup_value));
+ lookup_value = save_lookup;
+ }
s++;
goto RETURN;
}
or ${perl{sub}{arg1}{arg2}} or up to a maximum of EXIM_PERL_MAX_ARGS
arguments (defined below). */
- #ifdef EXIM_PERL
#define EXIM_PERL_MAX_ARGS 8
case EITEM_PERL:
+ #ifndef EXIM_PERL
+ expand_string_message = US"\"${perl\" encountered, but this facility "
+ "is not included in this binary";
+ goto EXPAND_FAILED;
+
+ #else /* EXIM_PERL */
{
uschar *sub_arg[EXIM_PERL_MAX_ARGS + 2];
uschar *new_yield;
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
+ a maximum of EXPAND_DLFUNC_MAX_ARGS arguments (defined below). */
+
+ #define EXPAND_DLFUNC_MAX_ARGS 8
+
+ case EITEM_DLFUNC:
+ #ifndef EXPAND_DLFUNC
+ expand_string_message = US"\"${dlfunc\" encountered, but this facility "
+ "is not included in this binary";
+ goto EXPAND_FAILED;
+
+ #else /* EXPAND_DLFUNC */
+ {
+ tree_node *t;
+ exim_dlfunc_t *func;
+ uschar *result;
+ int status, argc;
+ uschar *argv[EXPAND_DLFUNC_MAX_ARGS + 3];
+
+ if ((expand_forbid & RDO_DLFUNC) != 0)
+ {
+ expand_string_message =
+ US"dynamically-loaded functions are not permitted";
+ goto EXPAND_FAILED;
+ }
+
+ switch(read_subs(argv, EXPAND_DLFUNC_MAX_ARGS + 2, 2, &s, skipping,
+ TRUE, US"dlfunc"))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+
+ /* If skipping, we don't actually do anything */
+
+ if (skipping) continue;
+
+ /* Look up the dynamically loaded object handle in the tree. If it isn't
+ found, dlopen() the file and put the handle in the tree for next time. */
+
+ t = tree_search(dlobj_anchor, argv[0]);
+ if (t == NULL)
+ {
+ void *handle = dlopen(CS argv[0], RTLD_LAZY);
+ if (handle == NULL)
+ {
+ expand_string_message = string_sprintf("dlopen \"%s\" failed: %s",
+ argv[0], dlerror());
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message);
+ goto EXPAND_FAILED;
+ }
+ t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]));
+ Ustrcpy(t->name, argv[0]);
+ t->data.ptr = handle;
+ (void)tree_insertnode(&dlobj_anchor, t);
+ }
+
+ /* Having obtained the dynamically loaded object handle, look up the
+ function pointer. */
+
+ func = (exim_dlfunc_t *)dlsym(t->data.ptr, CS argv[1]);
+ if (func == NULL)
+ {
+ expand_string_message = string_sprintf("dlsym \"%s\" in \"%s\" failed: "
+ "%s", argv[1], argv[0], dlerror());
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message);
+ goto EXPAND_FAILED;
+ }
+
+ /* Call the function and work out what to do with the result. If it
+ 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. */
+
+ result = NULL;
+ for (argc = 0; argv[argc] != NULL; argc++);
+ status = func(&result, argc - 2, &argv[2]);
+ if(status == OK)
+ {
+ if (result == NULL) result = US"";
+ yield = string_cat(yield, &size, &ptr, result, Ustrlen(result));
+ continue;
+ }
+ else
+ {
+ expand_string_message = result == NULL ? US"(no message)" : result;
+ if(status == FAIL_FORCED) expand_string_forcedfail = TRUE;
+ else if(status != FAIL)
+ log_write(0, LOG_MAIN|LOG_PANIC, "dlfunc{%s}{%s} failed (%d): %s",
+ argv[0], argv[1], status, expand_string_message);
+ goto EXPAND_FAILED;
+ }
+ }
+ #endif /* EXPAND_DLFUNC */
}
/* Control reaches here if the name is not recognized as one of the more
/* Convert to masked textual format and add to output. */
yield = string_cat(yield, &size, &ptr, buffer,
- host_nmtoa(count, binary, mask, buffer));
+ host_nmtoa(count, binary, mask, buffer, '.'));
continue;
}