* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2012 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
#include "exim.h"
+#ifdef USE_GNUTLS
+# include <gnutls/gnutls.h>
+# if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP)
+# define DISABLE_OCSP
+# endif
+#endif
+
extern void init_lookup_list(void);
*/
const pcre *
-regex_must_compile(uschar *pattern, BOOL caseless, BOOL use_malloc)
+regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc)
{
int offset;
int options = PCRE_COPT;
pcre_free = function_store_free;
}
if (caseless) options |= PCRE_CASELESS;
-yield = pcre_compile(CS pattern, options, (const char **)&error, &offset, NULL);
+yield = pcre_compile(CCS pattern, options, (const char **)&error, &offset, NULL);
pcre_malloc = function_store_get;
pcre_free = function_dummy_free;
if (yield == NULL)
*/
BOOL
-regex_match_and_setup(const pcre *re, uschar *subject, int options, int setup)
+regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup)
{
int ovector[3*(EXPAND_MAXN+1)];
-int n = pcre_exec(re, NULL, CS subject, Ustrlen(subject), 0,
+uschar * s = string_copy(subject); /* de-constifying */
+int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
PCRE_EOPT | options, ovector, sizeof(ovector)/sizeof(int));
BOOL yield = n >= 0;
if (n == 0) n = EXPAND_MAXN + 1;
expand_nmax = (setup < 0)? 0 : setup + 1;
for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2)
{
- expand_nstring[expand_nmax] = subject + ovector[nn];
+ expand_nstring[expand_nmax] = s + ovector[nn];
expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
}
expand_nmax--;
if (fd < 0) return;
-{int dummy = write(fd, process_info, process_info_len); dummy = dummy; }
+(void)write(fd, process_info, process_info_len);
(void)close(fd);
}
when a bug in a function that calls milliwait() caused it to pass invalid data.
That's when I added the check. :-)
+We assume it to be not worth sleeping for under 100us; this value will
+require revisiting as hardware advances. This avoids the issue of
+a zero-valued timer setting meaning "never fire".
+
Argument: an itimerval structure containing the interval
Returns: nothing
*/
{
sigset_t sigmask;
sigset_t old_sigmask;
+
+if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
+ return;
(void)sigemptyset(&sigmask); /* Empty mask */
(void)sigaddset(&sigmask, SIGALRM); /* Add SIGALRM */
(void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask); /* Block SIGALRM */
{
if (!running_in_test_harness)
{
- debug_printf("tick check: %lu.%06lu %lu.%06lu\n",
- then_tv->tv_sec, then_tv->tv_usec, now_tv.tv_sec, now_tv.tv_usec);
- debug_printf("waiting %lu.%06lu\n", itval.it_value.tv_sec,
- itval.it_value.tv_usec);
+ debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
+ then_tv->tv_sec, (long) then_tv->tv_usec,
+ now_tv.tv_sec, (long) now_tv.tv_usec);
+ debug_printf("waiting " TIME_T_FMT ".%06lu\n",
+ itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
}
}
if (smtp_input)
{
#ifdef SUPPORT_TLS
- tls_close(FALSE, FALSE); /* Shut down the TLS library */
+ tls_close(TRUE, FALSE); /* Shut down the TLS library */
#endif
(void)close(fileno(smtp_in));
(void)close(fileno(smtp_out));
#ifdef WITH_CONTENT_SCAN
fprintf(f, " Content_Scanning");
#endif
+#ifdef WITH_OLD_DEMIME
+ fprintf(f, " Old_Demime");
+#endif
#ifndef DISABLE_DKIM
fprintf(f, " DKIM");
#endif
-#ifdef WITH_OLD_DEMIME
- fprintf(f, " Old_Demime");
+#ifndef DISABLE_DNSSEC
+ fprintf(f, " DNSSEC");
+#endif
+#ifndef DISABLE_EVENT
+ fprintf(f, " Event");
+#endif
+#ifdef SUPPORT_I18N
+ fprintf(f, " I18N");
+#endif
+#ifndef DISABLE_OCSP
+ fprintf(f, " OCSP");
+#endif
+#ifndef DISABLE_PRDR
+ fprintf(f, " PRDR");
+#endif
+#ifdef SUPPORT_PROXY
+ fprintf(f, " PROXY");
+#endif
+#ifdef SUPPORT_SOCKS
+ fprintf(f, " SOCKS");
#endif
#ifdef EXPERIMENTAL_SPF
fprintf(f, " Experimental_SPF");
#ifdef EXPERIMENTAL_BRIGHTMAIL
fprintf(f, " Experimental_Brightmail");
#endif
+#ifdef EXPERIMENTAL_DANE
+ fprintf(f, " Experimental_DANE");
+#endif
#ifdef EXPERIMENTAL_DCC
fprintf(f, " Experimental_DCC");
#endif
#ifdef EXPERIMENTAL_DMARC
fprintf(f, " Experimental_DMARC");
#endif
-#ifdef EXPERIMENTAL_OCSP
- fprintf(f, " Experimental_OCSP");
-#endif
-#ifdef EXPERIMENTAL_PRDR
- fprintf(f, " Experimental_PRDR");
-#endif
-#ifdef EXPERIMENTAL_TPDA
- fprintf(f, " Experimental_TPDA");
+#ifdef EXPERIMENTAL_DSN_INFO
+ fprintf(f, " Experimental_DSN_info");
#endif
fprintf(f, "\n");
#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
fprintf(f, " pgsql");
#endif
+#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
+ fprintf(f, " redis");
+#endif
#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
fprintf(f, " sqlite");
#endif
#ifdef AUTH_SPA
fprintf(f, " spa");
#endif
+#ifdef AUTH_TLS
+ fprintf(f, " tls");
+#endif
fprintf(f, "\n");
fprintf(f, "Routers:");
#ifdef SUPPORT_TLS
tls_version_report(f);
#endif
+#ifdef SUPPORT_I18N
+ utf8_version_report(f);
+#endif
- for (authi = auths_available; *authi->driver_name != '\0'; ++authi) {
- if (authi->version_report) {
+ for (authi = auths_available; *authi->driver_name != '\0'; ++authi)
+ if (authi->version_report)
(*authi->version_report)(f);
- }
- }
/* PCRE_PRERELEASE is either defined and empty or a bare sequence of
characters; unless it's an ancient version of PCRE in which case it
init_lookup_list();
for (i = 0; i < lookup_list_count; i++)
- {
if (lookup_list[i]->version_report)
lookup_list[i]->version_report(f);
- }
#ifdef WHITELIST_D_MACROS
fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
BOOL deliver_give_up = FALSE;
BOOL list_queue = FALSE;
BOOL list_options = FALSE;
+BOOL list_config = FALSE;
BOOL local_queue_only;
BOOL more = TRUE;
BOOL one_msg_action = FALSE;
uschar *ftest_localpart = NULL;
uschar *ftest_prefix = NULL;
uschar *ftest_suffix = NULL;
+uschar *log_oneline = NULL;
uschar *malware_test_file = NULL;
uschar *real_sender_address;
uschar *originator_home = US"/";
}
#endif
-/* In the Cygwin environment, some initialization needs doing. It is fudged
-in by means of this macro. */
+/* In the Cygwin environment, some initialization used to need doing.
+It was fudged in by means of this macro; now no longer but we'll leave
+it in case of others. */
#ifdef OS_INIT
OS_INIT
exit(EXIT_FAILURE);
}
+/* Initialize the default log options. */
+
+bits_set(log_selector, log_selector_size, log_default);
+
/* Set log_stderr to stderr, provided that stderr exists. This gets reset to
NULL when the daemon is run and the file is closed. We have to use this
indirection, because some systems don't allow writing to the variable "stderr".
regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
#endif
+for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
+
/* If the program is called as "mailq" treat it as equivalent to "exim -bp";
this seems to be a generally accepted convention, since one finds symbolic
else if (Ustrcmp(argrest, "P") == 0)
{
- list_options = TRUE;
- debug_selector |= D_v;
- debug_file = stderr;
+ /* -bP config: we need to setup here, because later,
+ * when list_options is checked, the config is read already */
+ if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
+ {
+ list_config = TRUE;
+ readconf_save_config(version_string);
+ }
+ else
+ {
+ list_options = TRUE;
+ debug_selector |= D_v;
+ debug_file = stderr;
+ }
}
/* -brt: Test retry configuration lookup */
if (nr_configs)
{
int sep = 0;
- uschar *list = argrest;
+ const uschar *list = argrest;
uschar *filename;
while (trusted_config && (filename = string_nextinlist(&list,
&sep, big_buffer, big_buffer_size)) != NULL)
argrest++;
}
if (*argrest != 0)
- decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
- debug_options_count, US"debug", 0);
+ decode_bits(&selector, 1, debug_notall, argrest,
+ debug_options, debug_options_count, US"debug", 0);
debug_selector = selector;
}
break;
case 'f':
{
- int start, end;
+ int dummy_start, dummy_end;
uschar *errmess;
if (*argrest == 0)
{
{ badarg = TRUE; break; }
}
if (*argrest == 0)
- {
sender_address = string_sprintf(""); /* Ensure writeable memory */
- }
else
{
uschar *temp = argrest + Ustrlen(argrest) - 1;
if (temp >= argrest && *temp == '.') f_end_dot = TRUE;
allow_domain_literals = TRUE;
strip_trailing_dot = TRUE;
- sender_address = parse_extract_address(argrest, &errmess, &start, &end,
- &sender_address_domain, TRUE);
+#ifdef SUPPORT_I18N
+ allow_utf8_domains = TRUE;
+#endif
+ sender_address = parse_extract_address(argrest, &errmess,
+ &dummy_start, &dummy_end, &sender_address_domain, TRUE);
+#ifdef SUPPORT_I18N
+ message_smtputf8 = string_is_utf8(sender_address);
+ allow_utf8_domains = FALSE;
+#endif
allow_domain_literals = FALSE;
strip_trailing_dot = FALSE;
if (sender_address == NULL)
break;
}
+ /* -MCD: set the smtp_use_dsn flag; this indicates that the host
+ that exim is connected to supports the esmtp extension DSN */
+ else if (Ustrcmp(argrest, "CD") == 0)
+ {
+ smtp_use_dsn = TRUE;
+ break;
+ }
+
/* -MCP: set the smtp_use_pipelining flag; this is useful only when
it preceded -MC (see above) */
else if (Ustrcmp(argrest, "Mi") == 0) interface_address = argv[++i];
+ /* -oMm: Message reference */
+
+ else if (Ustrcmp(argrest, "Mm") == 0)
+ {
+ if (!mac_ismsgid(argv[i+1]))
+ {
+ fprintf(stderr,"-oMm must be a valid message ID\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!trusted_config)
+ {
+ fprintf(stderr,"-oMm must be called by a trusted user/config\n");
+ exit(EXIT_FAILURE);
+ }
+ message_reference = argv[++i];
+ }
+
/* -oMr: Received protocol */
else if (Ustrcmp(argrest, "Mr") == 0) received_protocol = argv[++i];
case 'X':
if (*argrest == '\0')
- {
if (++i >= argc)
{
fprintf(stderr, "exim: string expected after -X\n");
exit(EXIT_FAILURE);
}
- }
+ break;
+
+ case 'z':
+ if (*argrest == '\0')
+ if (++i < argc) log_oneline = argv[i]; else
+ {
+ fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
+ exit(EXIT_FAILURE);
+ }
break;
/* All other initial characters are errors */
This needs to happen before we read the main configuration. */
init_lookup_list();
+#ifdef SUPPORT_I18N
+if (running_in_test_harness) smtputf8_advertise_hosts = NULL;
+#endif
+
/* Read the main runtime configuration data; this gives up if there
is a failure. It leaves the configuration file open so that the subsequent
configuration data for delivery can be read if needed. */
/* Handle the decoding of logging options. */
-decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
+decode_bits(log_selector, log_selector_size, log_notall,
log_selector_string, log_options, log_options_count, US"log", 0);
DEBUG(D_any)
{
+ int i;
debug_printf("configuration file is %s\n", config_main_filename);
- debug_printf("log selectors = %08x %08x\n", log_write_selector,
- log_extra_selector);
+ debug_printf("log selectors =");
+ for (i = 0; i < log_selector_size; i++)
+ debug_printf(" %08x", log_selector[i]);
+ debug_printf("\n");
}
/* If domain literals are not allowed, check the sender address that was
log_write(0, LOG_MAIN|LOG_PANIC_DIE,
"syslog_processname is longer than 32 chars: aborting");
+if (log_oneline)
+ {
+ if (admin_user)
+ {
+ log_write(0, LOG_MAIN, "%s", log_oneline);
+ return EXIT_SUCCESS;
+ }
+ else
+ return EXIT_FAILURE;
+ }
+
/* In some operating systems, the environment variable TMPDIR controls where
temporary files are created; Exim doesn't use these (apart from when delivering
to MBX mailboxes), but called libraries such as DBM libraries may require them.
Don't attempt it if logging is disabled, or if listing variables or if
verifying/testing addresses or expansions. */
-if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
+if (((debug_selector & D_any) != 0 || LOGGING(arguments))
&& really_exim && !list_options && !checking)
{
int i;
for (i = 0; i < argc; i++)
{
int len = Ustrlen(argv[i]);
- uschar *printing;
+ const uschar *printing;
uschar *quote;
if (p + len + 8 >= big_buffer + big_buffer_size)
{
printing = string_printing(argv[i]);
if (printing[0] == 0) quote = US"\""; else
{
- uschar *pp = printing;
+ const uschar *pp = printing;
quote = US"";
while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; }
}
while (*p) p++;
}
- if ((log_extra_selector & LX_arguments) != 0)
+ if (LOGGING(arguments))
log_write(0, LOG_MAIN, "%s", big_buffer);
else
debug_printf("%s\n", big_buffer);
exim_exit(EXIT_SUCCESS);
}
+if (list_config)
+ {
+ set_process_info("listing config");
+ readconf_print(US"config", NULL, FALSE);
+ exim_exit(EXIT_SUCCESS);
+ }
+
/* Handle a request to deliver one or more messages that are already on the
queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
"**** This is not for real!\n\n",
sender_host_address);
+ memset(sender_host_cache, 0, sizeof(sender_host_cache));
if (verify_check_host(&hosts_connection_nolog) == OK)
- log_write_selector &= ~L_smtp_connection;
+ BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
/* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
deliver_drop_privilege = TRUE;
queue_smtp = FALSE;
queue_smtp_domains = NULL;
+#ifdef SUPPORT_I18N
+ message_utf8_downconvert = -1; /* convert-if-needed */
+#endif
}
{
smtp_in = stdin;
smtp_out = stdout;
+ memset(sender_host_cache, 0, sizeof(sender_host_cache));
if (verify_check_host(&hosts_connection_nolog) == OK)
- log_write_selector &= ~L_smtp_connection;
+ BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
if (!smtp_start_session())
{
if (recipients_max > 0 && ++rcount > recipients_max &&
!extract_recipients)
- {
if (error_handling == ERRORS_STDERR)
{
fprintf(stderr, "exim: too many recipients\n");
moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)?
errors_sender_rc : EXIT_FAILURE;
}
- }
+#ifdef SUPPORT_I18N
+ {
+ BOOL b = allow_utf8_domains;
+ allow_utf8_domains = TRUE;
+#endif
recipient =
parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
+#ifdef SUPPORT_I18N
+ if (string_is_utf8(recipient))
+ message_smtputf8 = TRUE;
+ else
+ allow_utf8_domains = b;
+ }
+#endif
if (domain == 0 && !allow_unqualified_recipient)
{
recipient = NULL;
return_path = string_copy(sender_address);
}
else
- {
printf("Return-path = %s\n", (return_path[0] == 0)? US"<>" : return_path);
- }
printf("Sender = %s\n", (sender_address[0] == 0)? US"<>" : sender_address);
receive_add_recipient(