-/* $Cambridge: exim/src/src/exim.c,v 1.39 2006/05/22 18:42:34 fanf2 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.46 2006/10/24 12:56:06 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
#ifdef AUTH_CYRUS_SASL
fprintf(f, " cyrus_sasl");
#endif
+#ifdef AUTH_DOVECOT
+ fprintf(f, " dovecot");
+#endif
#ifdef AUTH_PLAINTEXT
fprintf(f, " plaintext");
#endif
int ptr = 0;
uschar *yield = NULL;
-if (fn_readline == NULL) printf("> ");
+if (fn_readline == NULL) { printf("> "); fflush(stdout); }
for (i = 0;; i++)
{
BOOL one_msg_action = FALSE;
BOOL queue_only_set = FALSE;
BOOL receiving_message = TRUE;
+BOOL sender_ident_set = FALSE;
BOOL unprivileged;
BOOL removed_privilege = FALSE;
BOOL verify_address_mode = FALSE;
uschar *called_as = US"";
uschar *start_queue_run_id = NULL;
uschar *stop_queue_run_id = NULL;
+uschar *expansion_test_message = NULL;
uschar *ftest_domain = NULL;
uschar *ftest_localpart = NULL;
uschar *ftest_prefix = NULL;
regex_ismsgid =
regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE);
+/* Precompile the regular expression that is used for matching an SMTP error
+code, possibly extended, at the start of an error message. */
+
+regex_smtp_code =
+ regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?",
+ FALSE, TRUE);
+
/* 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
links called "mailq" in standard OS configurations. */
else if (*argrest != 0) { badarg = TRUE; break; }
}
- /* -be: Run in expansion test mode */
+ /* -be: Run in expansion test mode
+ -bem: Ditto, but read a message from a file first
+ */
else if (*argrest == 'e')
+ {
expansion_test = checking = TRUE;
+ if (argrest[1] == 'm')
+ {
+ if (++i >= argc) { badarg = TRUE; break; }
+ expansion_test_message = argv[i];
+ argrest++;
+ }
+ if (argrest[1] != 0) { badarg = TRUE; break; }
+ }
/* -bF: Run system filter test */
-Mmad mark all recipients delivered
-Mmd mark recipients(s) delivered
-Mes edit sender
+ -Mset load a message for use with -be
-Mvb show body
-Mvh show header
-Mvl show log
one_msg_action = TRUE;
}
else if (Ustrcmp(argrest, "rm") == 0) msg_action = MSG_REMOVE;
+ else if (Ustrcmp(argrest, "set") == 0)
+ {
+ msg_action = MSG_LOAD;
+ one_msg_action = TRUE;
+ }
else if (Ustrcmp(argrest, "t") == 0) msg_action = MSG_THAW;
else if (Ustrcmp(argrest, "vb") == 0)
{
/* -oMt: Set sender ident */
- else if (Ustrcmp(argrest, "Mt") == 0) sender_ident = argv[++i];
+ else if (Ustrcmp(argrest, "Mt") == 0)
+ {
+ sender_ident_set = TRUE;
+ sender_ident = argv[++i];
+ }
/* Else a bad argument */
) ||
(
msg_action_arg > 0 &&
- (daemon_listen || queue_interval >= 0 || list_options || checking ||
- bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0)
+ (daemon_listen || queue_interval >= 0 || list_options ||
+ (checking && msg_action != MSG_LOAD) ||
+ bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0)
) ||
(
(daemon_listen || queue_interval >= 0) &&
(sender_address != NULL || list_options || list_queue || checking ||
- bi_option)
+ bi_option)
) ||
(
daemon_listen && queue_interval == 0
) ||
(
deliver_selectstring != NULL && queue_interval < 0
+ ) ||
+ (
+ msg_action == MSG_LOAD &&
+ (!expansion_test || expansion_test_message != NULL)
)
)
{
exit(EXIT_SUCCESS);
}
-/* Handle actions on specific messages, except for the force delivery action,
-which is done below. Some actions take a whole list of message ids, which
-are known to continue up to the end of the arguments. Others take a single
-message id and then operate on the recipients list. */
+/* Handle actions on specific messages, except for the force delivery and
+message load actions, which are done below. Some actions take a whole list of
+message ids, which are known to continue up to the end of the arguments. Others
+take a single message id and then operate on the recipients list. */
-if (msg_action_arg > 0 && msg_action != MSG_DELIVER)
+if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
{
int yield = EXIT_SUCCESS;
set_process_info("acting on specified messages");
/* Handle a request to deliver one or more messages that are already on the
-queue. Values of msg_action other than MSG_DELIVER are dealt with above. This
-is typically used for a small number when prodding by hand (when the option
-forced_delivery will be set) or when re-execing to regain root privilege.
-Each message delivery must happen in a separate process, so we fork a process
-for each one, and run them sequentially so that debugging output doesn't get
-intertwined, and to avoid spawning too many processes if a long list is given.
-However, don't fork for the last one; this saves a process in the common case
-when Exim is called to deliver just one message. */
-
-if (msg_action_arg > 0)
+queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
+above. MSG_LOAD is handled with -be (which is the only time it applies) below.
+
+Delivery of specific messages is typically used for a small number when
+prodding by hand (when the option forced_delivery will be set) or when
+re-execing to regain root privilege. Each message delivery must happen in a
+separate process, so we fork a process for each one, and run them sequentially
+so that debugging output doesn't get intertwined, and to avoid spawning too
+many processes if a long list is given. However, don't fork for the last one;
+this saves a process in the common case when Exim is called to deliver just one
+message. */
+
+if (msg_action_arg > 0 && msg_action != MSG_LOAD)
{
if (prod_requires_admin && !admin_user)
{
sender_local = TRUE;
/* A trusted caller can supply authenticated_sender and authenticated_id
- via -oMas and -oMai and if so, they will already be set. */
+ via -oMas and -oMai and if so, they will already be set. Otherwise, force
+ defaults except when host checking. */
- if (authenticated_sender == NULL)
+ if (authenticated_sender == NULL && !host_checking)
authenticated_sender = string_sprintf("%s@%s", originator_login,
qualify_domain_sender);
- if (authenticated_id == NULL) authenticated_id = originator_login;
+ if (authenticated_id == NULL && !host_checking)
+ authenticated_id = originator_login;
}
/* Trusted callers are always permitted to specify the sender address.
exim_exit(exit_value);
}
-/* Handle expansion checking */
+/* Handle expansion checking. Either expand items on the command line, or read
+from stdin if there aren't any. If -Mset was specified, load the message so
+that its variables can be used, but restrict this facility to admin users.
+Otherwise, if -bem was used, read a message from stdin. */
if (expansion_test)
{
+ if (msg_action_arg > 0 && msg_action == MSG_LOAD)
+ {
+ uschar spoolname[256]; /* Not big_buffer; used in spool_read_header() */
+ if (!admin_user)
+ {
+ fprintf(stderr, "exim: permission denied\n");
+ exit(EXIT_FAILURE);
+ }
+ message_id = argv[msg_action_arg];
+ (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id);
+ if (!spool_open_datafile(message_id))
+ printf ("Failed to load message datafile %s\n", message_id);
+ if (spool_read_header(spoolname, TRUE, FALSE) != spool_read_OK)
+ printf ("Failed to load message %s\n", message_id);
+ }
+
+ /* 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)
+ {
+ int save_stdin = dup(0);
+ int fd = Uopen(expansion_test_message, O_RDONLY, 0);
+ if (fd < 0)
+ {
+ fprintf(stderr, "exim: failed to open %s: %s\n", expansion_test_message,
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ (void) dup2(fd, 0);
+ filter_test = FTEST_USER; /* Fudge to make it look like filter test */
+ message_ended = END_NOTENDED;
+ read_message_body(receive_msg(extract_recipients));
+ (void)dup2(save_stdin, 0);
+ (void)close(save_stdin);
+ }
+
+ /* 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);
+ if (ss == NULL) printf ("Failed: %s\n", expand_string_message);
else printf("%s\n", CS ss);
}
}
#endif
}
+ /* The data file will be open after -Mset */
+
+ if (deliver_datafile >= 0)
+ {
+ (void)close(deliver_datafile);
+ deliver_datafile = -1;
+ }
+
exim_exit(EXIT_SUCCESS);
}
}
/* Handle host checking: this facility mocks up an incoming SMTP call from a
-given IP address so that the blocking and relay configuration can be tested. An
-RFC 1413 call is made only if we are running in the test harness and an
-incoming interface and both ports are specified, because there is no TCP/IP
-call to find the ident for. */
+given IP address so that the blocking and relay configuration can be tested.
+Unless a sender_ident was set by -oMt, we discard it (the default is the
+caller's login name). An RFC 1413 call is made only if we are running in the
+test harness and an incoming interface and both ports are specified, because
+there is no TCP/IP call to find the ident for. */
if (host_checking)
{
int x[4];
int size;
- sender_ident = NULL;
- if (running_in_test_harness && sender_host_port != 0 &&
- interface_address != NULL && interface_port != 0)
- verify_get_ident(1413);
+ if (!sender_ident_set)
+ {
+ sender_ident = NULL;
+ if (running_in_test_harness && sender_host_port != 0 &&
+ interface_address != NULL && interface_port != 0)
+ verify_get_ident(1413);
+ }
/* In case the given address is a non-canonical IPv6 address, canonicize
it. The code works for both IPv4 and IPv6, as it happens. */
}
}
-/* Otherwise, set up the input size limit here */
+/* Otherwise, set up the input size limit here. */
else
{
- thismessage_size_limit = expand_string_integer(message_size_limit);
- if (thismessage_size_limit < 0)
+ thismessage_size_limit = expand_string_integer(message_size_limit, TRUE);
+ if (expand_string_message != NULL)
{
if (thismessage_size_limit == -1)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand "
}
}
+ /* Run the acl_not_smtp_start ACL if required. The result of the ACL is
+ ignored; rejecting here would just add complication, and it can just as
+ well be done later. Allow $recipients to be visible in the ACL. */
+
+ if (acl_not_smtp_start != NULL)
+ {
+ uschar *user_msg, *log_msg;
+ enable_dollar_recipients = TRUE;
+ (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start,
+ &user_msg, &log_msg);
+ enable_dollar_recipients = FALSE;
+ }
+
/* Read the data for the message. If filter_test is not FTEST_NONE, this
will just read the headers for the message, and not write anything onto the
spool. */