-/* $Cambridge: exim/src/src/exim.c,v 1.45 2006/10/23 13:24:21 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.55 2007/01/30 15:10:59 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
/* See the file NOTICE for conditions of use and distribution. */
int ptr = 0;
uschar *yield = NULL;
-if (fn_readline == NULL) printf("> ");
+if (fn_readline == NULL) { printf("> "); fflush(stdout); }
for (i = 0;; i++)
{
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_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. */
+code, possibly extended, at the start of an error message. Note that the
+terminating whitespace character is included. */
regex_smtp_code =
regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?",
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 */
if (Ustrcmp(argrest, "C") == 0)
{
+ union sockaddr_46 interface_sock;
+ EXIM_SOCKLEN_T size = sizeof(interface_sock);
+
if (argc != i + 6)
{
fprintf(stderr, "exim: too many or too few arguments after -MC\n");
return EXIT_FAILURE;
}
+ /* Set up $sending_ip_address and $sending_port */
+
+ if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock),
+ &size) == 0)
+ sending_ip_address = host_ntoa(-1, &interface_sock, NULL,
+ &sending_port);
+ else
+ {
+ fprintf(stderr, "exim: getsockname() failed after -MC option: %s\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
if (running_in_test_harness) millisleep(500);
break;
}
case 'q':
receiving_message = FALSE;
+ if (queue_interval >= 0)
+ {
+ fprintf(stderr, "exim: -q specified more than once\n");
+ exit(EXIT_FAILURE);
+ }
/* -qq...: Do queue runs in a 2-stage manner */
}
}
else deliver_selectstring = argrest;
- if (queue_interval < 0) queue_interval = 0;
break;
}
}
else deliver_selectstring_sender = argrest;
- if (queue_interval < 0) queue_interval = 0;
break;
/* -Tqt is an option that is exclusively for use by the testing suite.
}
+/* If -R or -S have been specified without -q, assume a single queue run. */
+
+if ((deliver_selectstring != NULL || deliver_selectstring_sender != NULL) &&
+ queue_interval < 0) queue_interval = 0;
+
+
/* Arguments have been processed. Check for incompatibilities. */
END_ARG:
) ||
(
deliver_selectstring != NULL && queue_interval < 0
+ ) ||
+ (
+ msg_action == MSG_LOAD &&
+ (!expansion_test || expansion_test_message != NULL)
)
)
{
/* 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. */
+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)
{
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));
+ message_linecount += body_linecount;
+ (void)dup2(save_stdin, 0);
+ (void)close(save_stdin);
+ clearerr(stdin); /* Required by Darwin */
+ }
+
+ /* Allow $recipients for this testing */
+
+ enable_dollar_recipients = TRUE;
+
/* Expand command line items */
if (recipients_arg < argc)
log_write_selector &= ~L_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,
+ because a log line has already been written for all its failure exists
+ (usually "connection refused: <reason>") and writing another one is
+ unnecessary clutter. */
+
if (smtp_start_session())
{
reset_point = store_get(0);
if (smtp_setup_msg() <= 0) break;
if (!receive_msg(FALSE)) break;
}
+ smtp_log_no_mail();
}
exim_exit(EXIT_SUCCESS);
}
if (fstat(1, &statbuf) < 0) (void)dup2(0, 1);
-/* Set up the incoming protocol name and the state of the program. Root
-is allowed to force received protocol via the -oMr option above, and if we are
-in a non-local SMTP state it means we have come via inetd and the process info
-has already been set up. We don't set received_protocol here for smtp input,
-as it varies according to batch/HELO/EHLO/AUTH/TLS. */
+/* Set up the incoming protocol name and the state of the program. Root is
+allowed to force received protocol via the -oMr option above. If we have come
+via inetd, the process info has already been set up. We don't set
+received_protocol here for smtp input, as it varies according to
+batch/HELO/EHLO/AUTH/TLS. */
if (smtp_input)
{
- if (sender_local) set_process_info("accepting a local SMTP message from <%s>",
- sender_address);
+ if (!is_inetd) set_process_info("accepting a local %sSMTP message from <%s>",
+ smtp_batched_input? "batched " : "",
+ (sender_address!= NULL)? sender_address : originator_login);
}
else
{
return EXIT_FAILURE;
}
-/* If this is smtp input of any kind, handle the start of the SMTP
-session. */
+/* If this is smtp input of any kind, real or batched, handle the start of the
+SMTP session.
+
+NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
+because a log line has already been written for all its failure exists
+(usually "connection refused: <reason>") and writing another one is
+unnecessary clutter. */
if (smtp_input)
{
store_reset(reset_point);
message_id[0] = 0;
- /* In the SMTP case, we have to handle the initial SMTP input and build the
- recipients list, before calling receive_msg() to read the message proper.
- Whatever sender address is actually given in the SMTP transaction is
- actually ignored for local senders - we use the actual sender, which is
- normally either the underlying user running this process or a -f argument
- provided by a trusted caller. It is saved in real_sender_address.
-
- However, if this value is NULL, we are dealing with a trusted caller when
- -f was not used; in this case, the SMTP sender is allowed to stand.
-
- Also, if untrusted_set_sender is set, we permit sender addresses that match
- anything in its list.
-
- The variable raw_sender_address holds the sender address before rewriting. */
+ /* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP
+ input and build the recipients list, before calling receive_msg() to read the
+ message proper. Whatever sender address is given in the SMTP transaction is
+ often ignored for local senders - we use the actual sender, which is normally
+ either the underlying user running this process or a -f argument provided by
+ a trusted caller. It is saved in real_sender_address. The test for whether to
+ accept the SMTP sender is encapsulated in receive_check_set_sender(). */
if (smtp_input)
{
sender_address = raw_sender = real_sender_address;
sender_address_unrewritten = NULL;
}
+
+ /* For batched SMTP, we have to run the acl_not_smtp_start ACL, since it
+ isn't really SMTP, so no other ACL will run until the acl_not_smtp one at
+ the very end. The result of the ACL is ignored (as for other non-SMTP
+ messages). It is run for its potential side effects. */
+
+ if (smtp_batched_input && 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;
+ }
+
+ /* Now get the data for the message */
+
more = receive_msg(extract_recipients);
if (message_id[0] == 0)
{
if (more) continue;
+ smtp_log_no_mail(); /* Log no mail if configured */
exim_exit(EXIT_FAILURE);
}
}
- else exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
+ else
+ {
+ smtp_log_no_mail(); /* Log no mail if configured */
+ exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
+ }
}
/* In the non-SMTP case, we have all the information from the command