* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
/* See the file NOTICE for conditions of use and distribution. */
*/
void
-exim_exit(int rc)
+exim_exit(int rc, const uschar * process)
{
search_tidyup();
DEBUG(D_any)
- debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d terminating with rc=%d "
- ">>>>>>>>>>>>>>>>\n", (int)getpid(), rc);
+ debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d "
+ ">>>>>>>>>>>>>>>>\n", (int)getpid(),
+ process ? "(" : "", process, process ? ") " : "", rc);
exit(rc);
}
fprintf(f, " SOCKS");
#endif
#ifdef TCP_FASTOPEN
- fprintf(f, " TCP_Fast_Open");
+ deliver_init();
+ if (tcp_fastopen_ok) fprintf(f, " TCP_Fast_Open");
#endif
#ifdef EXPERIMENTAL_LMDB
fprintf(f, " Experimental_LMDB");
"If the string is not recognised, you'll get this help (on stderr).\n"
"\n"
" exim -bI:help this information\n"
-" exim -bI:dscp dscp value keywords known\n"
-" exim -bI:sieve list of supported sieve extensions, one per line.\n"
+" exim -bI:dscp list of known dscp value keywords\n"
+" exim -bI:sieve list of supported sieve extensions\n"
);
return;
case CMDINFO_SIEVE:
local_part_quote(uschar *lpart)
{
BOOL needs_quote = FALSE;
-int size, ptr;
-uschar *yield;
+gstring * g;
uschar *t;
for (t = lpart; !needs_quote && *t != 0; t++)
if (!needs_quote) return lpart;
-size = ptr = 0;
-yield = string_catn(NULL, &size, &ptr, US"\"", 1);
+g = string_catn(NULL, US"\"", 1);
for (;;)
{
uschar *nq = US Ustrpbrk(lpart, "\\\"");
if (nq == NULL)
{
- yield = string_cat(yield, &size, &ptr, lpart);
+ g = string_cat(g, lpart);
break;
}
- yield = string_catn(yield, &size, &ptr, lpart, nq - lpart);
- yield = string_catn(yield, &size, &ptr, US"\\", 1);
- yield = string_catn(yield, &size, &ptr, nq, 1);
+ g = string_catn(g, lpart, nq - lpart);
+ g = string_catn(g, US"\\", 1);
+ g = string_catn(g, nq, 1);
lpart = nq + 1;
}
-yield = string_catn(yield, &size, &ptr, US"\"", 1);
-yield[ptr] = 0;
-return yield;
+g = string_catn(g, US"\"", 1);
+return string_from_gstring(g);
}
get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *))
{
int i;
-int size = 0;
-int ptr = 0;
-uschar *yield = NULL;
+gstring * g = NULL;
-if (fn_readline == NULL) { printf("> "); fflush(stdout); }
+if (!fn_readline) { printf("> "); fflush(stdout); }
for (i = 0;; i++)
{
while (p < ss && isspace(*p)) p++; /* leading space after cont */
}
- yield = string_catn(yield, &size, &ptr, p, ss - p);
+ g = string_catn(g, p, ss - p);
#ifdef USE_READLINE
- if (fn_readline != NULL) free(readline_line);
+ if (fn_readline) free(readline_line);
#endif
- /* yield can only be NULL if ss==p */
- if (ss == p || yield[ptr-1] != '\\')
- {
- if (yield) yield[ptr] = 0;
+ /* g can only be NULL if ss==p */
+ if (ss == p || g->s[g->ptr-1] != '\\')
break;
- }
- yield[--ptr] = 0;
+
+ --g->ptr;
+ (void) string_from_gstring(g);
}
-if (yield == NULL) printf("\n");
-return yield;
+if (!g) printf("\n");
+return string_from_gstring(g);
}
}
+/*************************************************
+* Expansion testing *
+*************************************************/
+
+/* Expand and print one item, doing macro-processing.
+
+Arguments:
+ item line for expansion
+*/
+
+static void
+expansion_test_line(uschar * line)
+{
+int len;
+BOOL dummy_macexp;
+
+Ustrncpy(big_buffer, line, big_buffer_size);
+big_buffer[big_buffer_size-1] = '\0';
+len = Ustrlen(big_buffer);
+
+(void) macros_expand(0, &len, &dummy_macexp);
+
+if (isupper(big_buffer[0]))
+ {
+ if (macro_read_assignment(big_buffer))
+ printf("Defined macro '%s'\n", mlast->name);
+ }
+else
+ if ((line = expand_string(big_buffer))) printf("%s\n", CS line);
+ else printf("Failed: %s\n", expand_string_message);
+}
+
+
/*************************************************
* Entry point and high-level code *
*************************************************/
which sets the host protocol and host name */
if (*argrest == 0)
- {
- if (i+1 < argc) argrest = argv[++i]; else
+ if (i+1 < argc)
+ argrest = argv[++i];
+ else
{ badarg = TRUE; break; }
- }
if (*argrest != 0)
{
hn = Ustrchr(argrest, ':');
if (hn == NULL)
- {
received_protocol = argrest;
- }
else
{
int old_pool = store_pool;
directory to "/"! Later we change to $spool_directory. We do it there, because
during readconf_main() some expansion takes place already. */
-/* Store the initial cwd before we change directories */
-if ((initial_cwd = os_getcwd(NULL, 0)) == NULL)
- {
- perror("exim: can't get the current working directory");
- exit(EXIT_FAILURE);
- }
+/* Store the initial cwd before we change directories. Can be NULL if the
+dir has already been unlinked. */
+initial_cwd = os_getcwd(NULL, 0);
/* checking:
-be[m] expansion test -
}
/* At this point, we know if the user is privileged and some command-line
-options become possibly imperssible, depending upon the configuration file. */
+options become possibly impermissible, depending upon the configuration file. */
if (checking && commandline_checks_require_admin && !admin_user) {
fprintf(stderr, "exim: those command-line flags are set to require admin\n");
if (test_retry_arg >= argc)
{
printf("-brt needs a domain or address argument\n");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
s1 = argv[test_retry_arg++];
s2 = NULL;
printf("\n");
}
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
/* Handle a request to list one or more configuration options */
}
else readconf_print(argv[i], NULL, flag_n);
}
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
if (list_config)
{
set_process_info("listing config");
readconf_print(US"config", NULL, flag_n);
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
if (prod_requires_admin && !admin_user)
{
fprintf(stderr, "exim: Permission denied\n");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
set_process_info("delivering specified messages");
if (deliver_give_up) forced_delivery = deliver_force_thaw = TRUE;
{
fprintf(stderr, "failed to fork delivery process for %s: %s\n", argv[i],
strerror(errno));
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
else wait(&status);
}
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
else
set_process_info("running the queue (single queue run)");
queue_run(start_queue_run_id, stop_queue_run_id, FALSE);
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
/* If user name has not been set by -F, set it from the passwd entry
unless -f has been used to set the sender address by a trusted user. */
- if (originator_name == NULL)
+ if (!originator_name)
{
- if (sender_address == NULL ||
- (!trusted_caller && filter_test == FTEST_NONE))
+ if (!sender_address || (!trusted_caller && filter_test == FTEST_NONE))
{
uschar *name = US pw->pw_gecos;
uschar *amp = Ustrchr(name, '&');
replaced by a copy of the login name, and some even specify that
the first character should be upper cased, so that's what we do. */
- if (amp != NULL)
+ if (amp)
{
int loffset;
string_format(buffer, sizeof(buffer), "%.*s%n%s%s",
- amp - name, name, &loffset, originator_login, amp + 1);
+ (int)(amp - name), name, &loffset, originator_login, amp + 1);
buffer[loffset] = toupper(buffer[loffset]);
name = buffer;
}
/* If a pattern for matching the gecos field was supplied, apply
it and then expand the name string. */
- if (gecos_pattern != NULL && gecos_name != NULL)
+ if (gecos_pattern && gecos_name)
{
const pcre *re;
re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
{
uschar *new_name = expand_string(gecos_name);
expand_nmax = -1;
- if (new_name != NULL)
+ if (new_name)
{
DEBUG(D_receive) debug_printf("user name \"%s\" extracted from "
"gecos field \"%s\"\n", new_name, name);
if (test_rewrite_arg >= argc)
{
printf("-brw needs an address argument\n");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
rewrite_test(argv[test_rewrite_arg]);
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
/* A locally-supplied message is considered to be coming from a local user
}
route_tidyup();
- exim_exit(exit_value);
+ exim_exit(exit_value, US"main");
}
/* Handle expansion checking. Either expand items on the command line, or read
/* Read a test message from a file. We fudge it up to be on stdin, saving
stdin itself for later reading of expansion strings. */
- else if (expansion_test_message != NULL)
+ else if (expansion_test_message)
{
int save_stdin = dup(0);
int fd = Uopen(expansion_test_message, O_RDONLY, 0);
clearerr(stdin); /* Required by Darwin */
}
+ /* Only admin users may see config-file macros this way */
+
+ if (!admin_user) macros = mlast = NULL;
+
/* Allow $recipients for this testing */
enable_dollar_recipients = TRUE;
/* Expand command line items */
if (recipients_arg < argc)
- {
while (recipients_arg < argc)
- {
- uschar *s = argv[recipients_arg++];
- uschar *ss = expand_string(s);
- if (ss == NULL) printf ("Failed: %s\n", expand_string_message);
- else printf("%s\n", CS ss);
- }
- }
+ expansion_test_line(argv[recipients_arg++]);
/* Read stdin */
{
char *(*fn_readline)(const char *) = NULL;
void (*fn_addhist)(const char *) = NULL;
+ uschar * s;
- #ifdef USE_READLINE
+#ifdef USE_READLINE
void *dlhandle = set_readline(&fn_readline, &fn_addhist);
- #endif
+#endif
- for (;;)
- {
- uschar *ss;
- uschar *source = get_stdinput(fn_readline, fn_addhist);
- if (source == NULL) break;
- ss = expand_string(source);
- if (ss == NULL)
- printf ("Failed: %s\n", expand_string_message);
- else printf("%s\n", CS ss);
- }
+ while (s = get_stdinput(fn_readline, fn_addhist))
+ expansion_test_line(s);
- #ifdef USE_READLINE
- if (dlhandle != NULL) dlclose(dlhandle);
- #endif
+#ifdef USE_READLINE
+ if (dlhandle) dlclose(dlhandle);
+#endif
}
/* The data file will be open after -Mset */
deliver_datafile = -1;
}
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main: expansion test");
}
}
smtp_log_no_mail();
}
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
if (!smtp_start_session())
{
mac_smtp_fflush();
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"smtp_start toplevel");
}
}
cancel_cutthrough_connection(TRUE, US"receive dropped");
if (more) goto moreloop;
smtp_log_no_mail(); /* Log no mail if configured */
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"receive toplevel");
}
}
else
{
cancel_cutthrough_connection(TRUE, US"message setup dropped");
smtp_log_no_mail(); /* Log no mail if configured */
- exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
+ exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS, US"msg setup toplevel");
}
}
if (error_handling == ERRORS_STDERR)
{
fprintf(stderr, "exim: too many recipients\n");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
else
- {
return
moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)?
errors_sender_rc : EXIT_FAILURE;
- }
#ifdef SUPPORT_I18N
{
{
fprintf(stderr, "exim: bad recipient address \"%s\": %s\n",
string_printing(list[i]), errmess);
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
else
{
if (!receive_timeout)
{
- struct timeval t = { 30*60, 0 }; /* 30 minutes */
+ struct timeval t = { .tv_sec = 30*60, .tv_usec = 0 }; /* 30 minutes */
fd_set r;
FD_ZERO(&r); FD_SET(0, &r);
for real; when reading the headers of a message for filter testing,
it is TRUE if the headers were terminated by '.' and FALSE otherwise. */
- if (message_id[0] == 0) exim_exit(EXIT_FAILURE);
+ if (message_id[0] == 0) exim_exit(EXIT_FAILURE, US"main");
} /* Non-SMTP message reception */
/* If this is a filter testing run, there are headers in store, but
if (chdir("/")) /* Get away from wherever the user is running this from */
{
DEBUG(D_receive) debug_printf("chdir(\"/\") failed\n");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, US"main");
}
/* Now we run either a system filter test, or a user filter test, or both.
explicitly. */
if ((filter_test & FTEST_SYSTEM) != 0)
- {
if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more))
- exim_exit(EXIT_FAILURE);
- }
+ exim_exit(EXIT_FAILURE, US"main");
memcpy(filter_sn, filter_n, sizeof(filter_sn));
if ((filter_test & FTEST_USER) != 0)
- {
if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more))
- exim_exit(EXIT_FAILURE);
- }
+ exim_exit(EXIT_FAILURE, US"main");
- exim_exit(EXIT_SUCCESS);
+ exim_exit(EXIT_SUCCESS, US"main");
}
/* Else act on the result of message reception. We should not get here unless
log_write(0, LOG_MAIN|LOG_PANIC,
"process %d crashed with signal %d while delivering %s",
(int)pid, status & 0x00ff, message_id);
- if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
+ if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"main");
}
}
}
store_reset(reset_point);
}
-exim_exit(EXIT_SUCCESS); /* Never returns */
+exim_exit(EXIT_SUCCESS, US"main"); /* Never returns */
return 0; /* To stop compiler warning */
}
+
/* End of exim.c */