fix BATV prvs expiry calculation for one rollover case
[exim.git] / src / src / exim.c
index 5ed6e54e18ab27ca9bfa1db10b4c49c719101dff..22b0a8c8c75f4f7b733c17b4543513fa61cee72c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.51 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.60 2008/01/17 13:03:35 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -920,6 +920,12 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_DOMAINKEYS
   fprintf(f, " Experimental_DomainKeys");
 #endif
+#ifdef EXPERIMENTAL_DKIM
+  fprintf(f, " Experimental_DKIM");
+#endif
+#ifdef EXPERIMENTAL_DCC
+  fprintf(f, " Experimental_DCC");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Lookups:");
@@ -1232,6 +1238,43 @@ return yield;
 
 
 
+/*************************************************
+*    Output usage information for the program    *
+*************************************************/
+
+/* This function is called when there are no recipients
+   or a specific --help argument was added.
+
+Arguments:
+  progname      information on what name we were called by
+
+Returns:        DOES NOT RETURN
+*/
+
+static void
+exim_usage(uschar *progname)
+{
+
+/* Handle specific program invocation varients */
+if (Ustrcmp(progname, US"-mailq") == 0)
+  {
+  fprintf(stderr,
+    "mailq - list the contents of the mail queue\n\n",
+    "For a list of options, see the Exim documentation.\n");
+  exit(EXIT_FAILURE);
+  }
+
+/* Generic usage - we output this whatever happens */
+fprintf(stderr,
+  "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
+  "not directly from a shell command line. Options and/or arguments control\n"
+  "what it does when called. For a list of options, see the Exim documentation.\n");
+
+exit(EXIT_FAILURE);
+}
+
+
+
 /*************************************************
 *          Entry point and high-level code       *
 *************************************************/
@@ -1291,8 +1334,10 @@ BOOL one_msg_action = FALSE;
 BOOL queue_only_set = FALSE;
 BOOL receiving_message = TRUE;
 BOOL sender_ident_set = FALSE;
+BOOL session_local_queue_only;
 BOOL unprivileged;
 BOOL removed_privilege = FALSE;
+BOOL usage_wanted = FALSE;
 BOOL verify_address_mode = FALSE;
 BOOL verify_as_sender = FALSE;
 BOOL version_printed = FALSE;
@@ -1587,10 +1632,15 @@ running in an unprivileged state. */
 
 unprivileged = (real_uid != root_uid && original_euid != root_uid);
 
-/* If the first argument is --help, pretend there are no arguments. This will
-cause a brief message to be given. */
+/* If the first argument is --help, set usage_wanted and pretend there
+are no arguments. This will cause a brief message to be given.   We do
+the message generation downstream so we can pick up how we were invoked */
 
-if (argc > 1 && Ustrcmp(argv[1], "--help") == 0) argc = 1;
+if (argc > 1 && Ustrcmp(argv[1], "--help") == 0)
+  {
+  argc = 1;
+  usage_wanted = TRUE;
+  }
 
 /* Scan the program's arguments. Some can be dealt with right away; others are
 simply recorded for checking and handling afterwards. Do a high-level switch
@@ -2165,6 +2215,9 @@ for (i = 1; i < argc; i++)
 
     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");
@@ -2194,6 +2247,19 @@ for (i = 1; i < argc; i++)
         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;
       }
@@ -2267,6 +2333,7 @@ for (i = 1; i < argc; i++)
        -Mes  edit sender
        -Mset load a message for use with -be
        -Mvb  show body
+       -Mvc  show copy (of whole message, in RFC 2822 format)
        -Mvh  show header
        -Mvl  show log
     */
@@ -2314,6 +2381,11 @@ for (i = 1; i < argc; i++)
       msg_action = MSG_SHOW_BODY;
       one_msg_action = TRUE;
       }
+    else if (Ustrcmp(argrest, "vc") == 0)
+      {
+      msg_action = MSG_SHOW_COPY;
+      one_msg_action = TRUE;
+      }
     else if (Ustrcmp(argrest, "vh") == 0)
       {
       msg_action = MSG_SHOW_HEADER;
@@ -2903,9 +2975,11 @@ if ((deliver_selectstring != NULL || deliver_selectstring_sender != NULL) &&
   queue_interval < 0) queue_interval = 0;
 
 
-/* Arguments have been processed. Check for incompatibilities. */
-
 END_ARG:
+/* If usage_wanted is set we call the usage function - which never returns */
+if (usage_wanted) exim_usage(called_as);
+
+/* Arguments have been processed. Check for incompatibilities. */
 if ((
     (smtp_input || extract_recipients || recipients_arg < argc) &&
     (daemon_listen || queue_interval >= 0 || bi_option ||
@@ -3458,7 +3532,6 @@ if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid)
 else
   {
   int i, j;
-
   for (i = 0; i < group_count; i++)
     {
     if (group_list[i] == exim_gid) admin_user = TRUE;
@@ -3622,7 +3695,7 @@ if (receiving_message &&
         (is_inetd && smtp_load_reserve >= 0)
       ))
   {
-  load_average = os_getloadavg();
+  load_average = OS_GETLOADAVG();
   }
 #endif
 
@@ -4371,6 +4444,11 @@ if (host_checking)
     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);
@@ -4380,6 +4458,7 @@ if (host_checking)
       if (smtp_setup_msg() <= 0) break;
       if (!receive_msg(FALSE)) break;
       }
+    smtp_log_no_mail();
     }
   exim_exit(EXIT_SUCCESS);
   }
@@ -4396,14 +4475,9 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input)
     printf("Configuration file is %s\n", config_main_filename);
     return EXIT_SUCCESS;
     }
+
   if (filter_test == FTEST_NONE)
-    {
-    fprintf(stderr,
-"Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
-"not directly from a shell command line. Options and/or arguments control\n"
-"what it does when called. For a list of options, see the Exim documentation.\n");
-    return EXIT_FAILURE;
-    }
+    exim_usage(called_as);
   }
 
 
@@ -4481,16 +4555,17 @@ but fd 1 will not be set. This also happens for passed SMTP channels. */
 
 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
   {
@@ -4500,11 +4575,11 @@ else
     sender_address);
   }
 
-/* Initialize the local_queue-only flag (this will be ignored if mua_wrapper is
-set) */
+/* Initialize the session_local_queue-only flag (this will be ignored if
+mua_wrapper is set) */
 
 queue_check_only();
-local_queue_only = queue_only;
+session_local_queue_only = queue_only;
 
 /* For non-SMTP and for batched SMTP input, check that there is enough space on
 the spool if so configured. On failure, we must not attempt to send an error
@@ -4517,8 +4592,13 @@ if ((!smtp_input || smtp_batched_input) && !receive_check_fs(0))
   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)
   {
@@ -4606,20 +4686,13 @@ while (more)
   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)
     {
@@ -4632,14 +4705,36 @@ while (more)
         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
@@ -4843,27 +4938,36 @@ while (more)
     }
 
   /* Else act on the result of message reception. We should not get here unless
-  message_id[0] is non-zero. If queue_only is set, local_queue_only will be
-  TRUE. If it is not, check on the number of messages received in this
-  connection. If that's OK and queue_only_load is set, check that the load
-  average is below it. If it is not, set local_queue_only TRUE. Note that it
-  then remains this way for any subsequent messages on the same SMTP connection.
-  This is a deliberate choice; even though the load average may fall, it
-  doesn't seem right to deliver later messages on the same call when not
-  delivering earlier ones. */
-
-  if (!local_queue_only)
+  message_id[0] is non-zero. If queue_only is set, session_local_queue_only
+  will be TRUE. If it is not, check on the number of messages received in this
+  connection. */
+
+  if (!session_local_queue_only &&
+      smtp_accept_queue_per_connection > 0 &&
+      receive_messagecount > smtp_accept_queue_per_connection)
     {
-    if (smtp_accept_queue_per_connection > 0 &&
-        receive_messagecount > smtp_accept_queue_per_connection)
-      {
-      local_queue_only = TRUE;
-      queue_only_reason = 2;
-      }
-    else if (queue_only_load >= 0)
+    session_local_queue_only = TRUE;
+    queue_only_reason = 2;
+    }
+
+  /* Initialize local_queue_only from session_local_queue_only. If it is false,
+  and queue_only_load is set, check that the load average is below it. If it is
+  not, set local_queue_only TRUE. If queue_only_load_latch is true (the
+  default), we put the whole session into queue_only mode. It then remains this
+  way for any subsequent messages on the same SMTP connection. This is a
+  deliberate choice; even though the load average may fall, it doesn't seem
+  right to deliver later messages on the same call when not delivering earlier
+  ones. However, there are odd cases where this is not wanted, so this can be
+  changed by setting queue_only_load_latch false. */
+
+  local_queue_only = session_local_queue_only;
+  if (!local_queue_only && queue_only_load >= 0)
+    {
+    local_queue_only = (load_average = OS_GETLOADAVG()) > queue_only_load;
+    if (local_queue_only)
       {
-      local_queue_only = (load_average = os_getloadavg()) > queue_only_load;
-      if (local_queue_only) queue_only_reason = 3;
+      queue_only_reason = 3;
+      if (queue_only_load_latch) session_local_queue_only = TRUE;
       }
     }