Clean up compiler warnings from { gcc -Wall }, many of which I introduced with
[exim.git] / src / src / exim.c
index 12f61a20d4dd828dcb87fd5a0c349b3af45906bd..50950faa500b5031ee19b8e5535f9671e45029b7 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.57 2007/06/27 11:01:51 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.71 2010/06/07 00:12:42 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -681,161 +681,12 @@ else
 
 
 
-/*************************************************
-*         Decode bit settings for log/debug      *
-*************************************************/
-
-/* This function decodes a string containing bit settings in the form of +name
-and/or -name sequences, and sets/unsets bits in a bit string accordingly. It
-also recognizes a numeric setting of the form =<number>, but this is not
-intended for user use. It's an easy way for Exim to pass the debug settings
-when it is re-exec'ed.
-
-The log options are held in two unsigned ints (because there became too many
-for one). The top bit in the table means "put in 2nd selector". This does not
-yet apply to debug options, so the "=" facility sets only the first selector.
-
-The "all" selector, which must be equal to 0xffffffff, is recognized specially.
-It sets all the bits in both selectors. However, there is a facility for then
-unsetting certain bits, because we want to turn off "memory" in the debug case.
-
-A bad value for a debug setting is treated as an unknown option - error message
-to stderr and die. For log settings, which come from the configuration file,
-we write to the log on the way out...
-
-Arguments:
-  selector1      address of the first bit string
-  selector2      address of the second bit string, or NULL
-  notall1        bits to exclude from "all" for selector1
-  notall2        bits to exclude from "all" for selector2
-  string         the configured string
-  options        the table of option names
-  count          size of table
-  which          "log" or "debug"
-
-Returns:         nothing on success - bomb out on failure
-*/
-
-static void
-decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
-  int notall2, uschar *string, bit_table *options, int count, uschar *which)
-{
-uschar *errmsg;
-if (string == NULL) return;
-
-if (*string == '=')
-  {
-  char *end;    /* Not uschar */
-  *selector1 = strtoul(CS string+1, &end, 0);
-  if (*end == 0) return;
-  errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
-    string);
-  goto ERROR_RETURN;
-  }
-
-/* Handle symbolic setting */
-
-else for(;;)
-  {
-  BOOL adding;
-  uschar *s;
-  int len;
-  bit_table *start, *end;
-
-  while (isspace(*string)) string++;
-  if (*string == 0) return;
-
-  if (*string != '+' && *string != '-')
-    {
-    errmsg = string_sprintf("malformed %s_selector setting: "
-      "+ or - expected but found \"%s\"", which, string);
-    goto ERROR_RETURN;
-    }
-
-  adding = *string++ == '+';
-  s = string;
-  while (isalnum(*string) || *string == '_') string++;
-  len = string - s;
-
-  start = options;
-  end = options + count;
-
-  while (start < end)
-    {
-    bit_table *middle = start + (end - start)/2;
-    int c = Ustrncmp(s, middle->name, len);
-    if (c == 0)
-      {
-      if (middle->name[len] != 0) c = -1; else
-        {
-        unsigned int bit = middle->bit;
-        unsigned int *selector;
-
-        /* The value with all bits set means "force all bits in both selectors"
-        in the case where two are being handled. However, the top bit in the
-        second selector is never set. When setting, some bits can be excluded.
-        */
-
-        if (bit == 0xffffffff)
-          {
-          if (adding)
-            {
-            *selector1 = 0xffffffff ^ notall1;
-            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
-            }
-          else
-            {
-            *selector1 = 0;
-            if (selector2 != NULL) *selector2 = 0;
-            }
-          }
-
-        /* Otherwise, the 0x80000000 bit means "this value, without the top
-        bit, belongs in the second selector". */
-
-        else
-          {
-          if ((bit & 0x80000000) != 0)
-            {
-            selector = selector2;
-            bit &= 0x7fffffff;
-            }
-          else selector = selector1;
-          if (adding) *selector |= bit; else *selector &= ~bit;
-          }
-        break;  /* Out of loop to match selector name */
-        }
-      }
-    if (c < 0) end = middle; else start = middle + 1;
-    }  /* Loop to match selector name */
-
-  if (start >= end)
-    {
-    errmsg = string_sprintf("unknown %s_selector setting: %c%.*s", which,
-      adding? '+' : '-', len, s);
-    goto ERROR_RETURN;
-    }
-  }    /* Loop for selector names */
-
-/* Handle disasters */
-
-ERROR_RETURN:
-if (Ustrcmp(which, "debug") == 0)
-  {
-  fprintf(stderr, "exim: %s\n", errmsg);
-  exit(EXIT_FAILURE);
-  }
-else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg);
-}
-
-
-
 /*************************************************
 *          Show supported features               *
 *************************************************/
 
-/* This function is called for -bV and for -d to output the optional features
-of the current Exim binary.
+/* This function is called for -bV/--version and for -d to output the optional
+features of the current Exim binary.
 
 Arguments:  a FILE for printing
 Returns:    nothing
@@ -905,6 +756,9 @@ fprintf(f, "Support for:");
 #ifdef WITH_CONTENT_SCAN
   fprintf(f, " Content_Scanning");
 #endif
+#ifndef DISABLE_DKIM
+  fprintf(f, " DKIM");
+#endif
 #ifdef WITH_OLD_DEMIME
   fprintf(f, " Old_Demime");
 #endif
@@ -917,8 +771,8 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   fprintf(f, " Experimental_Brightmail");
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  fprintf(f, " Experimental_DomainKeys");
+#ifdef EXPERIMENTAL_DCC
+  fprintf(f, " Experimental_DCC");
 #endif
 fprintf(f, "\n");
 
@@ -1051,7 +905,15 @@ if (fixed_never_users[0] > 0)
   fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]);
   }
 
-fprintf(f, "Size of off_t: %d\n", sizeof(off_t));
+fprintf(f, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
+
+/* This runtime check is to help diagnose library linkage mismatches which
+result in segfaults and the like; as such, it's left until the end,
+just in case.  There will still be a "Configuration file is" line still to
+come. */
+#ifdef SUPPORT_TLS
+tls_version_report(f);
+#endif
 }
 
 
@@ -1232,6 +1094,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       *
 *************************************************/
@@ -1294,6 +1193,7 @@ 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;
@@ -1306,6 +1206,7 @@ uschar *ftest_domain = NULL;
 uschar *ftest_localpart = NULL;
 uschar *ftest_prefix = NULL;
 uschar *ftest_suffix = NULL;
+uschar *malware_test_file = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
 void *reset_point;
@@ -1333,6 +1234,12 @@ This is a feature to make the lives of binary distributors easier. */
 #ifdef EXIM_USERNAME
 if (route_finduser(US EXIM_USERNAME, &pw, &exim_uid))
   {
+  if (exim_uid == 0)
+    {
+    fprintf(stderr, "exim: refusing to run with uid 0 for \"%s\"\n",
+      EXIM_USERNAME);
+    exit(EXIT_FAILURE);
+    }
   exim_gid = pw->pw_gid;
   }
 else
@@ -1588,11 +1495,6 @@ 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 (argc > 1 && Ustrcmp(argv[1], "--help") == 0) argc = 1;
-
 /* 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
 on the second character (the one after '-'), to save some effort. */
@@ -1657,6 +1559,21 @@ for (i = 1; i < argc; i++)
     argrest++;
     }
 
+  /* deal with --option_aliases */
+  else if (switchchar == '-')
+    {
+    if (Ustrcmp(argrest, "help") == 0)
+      {
+      usage_wanted = TRUE;
+      break;
+      }
+    else if (Ustrcmp(argrest, "version") == 0)
+      {
+      switchchar = 'b';
+      argrest = US"V";
+      }
+    }
+
   /* High-level switch on active initial letter */
 
   switch(switchchar)
@@ -1767,6 +1684,14 @@ for (i = 1; i < argc; i++)
 
     else if (Ustrcmp(argrest, "m") == 0) receiving_message = TRUE;
 
+    /* -bmalware: test the filename given for malware */
+
+    else if (Ustrcmp(argrest, "malware") == 0)
+      {
+      if (++i >= argc) { badarg = TRUE; break; }
+      malware_test_file = argv[i];
+      }
+
     /* -bnq: For locally originating messages, do not qualify unqualified
     addresses. In the envelope, this causes errors; in header lines they
     just get left. */
@@ -2017,7 +1942,7 @@ for (i = 1; i < argc; i++)
         }
       if (*argrest != 0)
         decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
-          debug_options_count, US"debug");
+          debug_options_count, US"debug", 0);
       debug_selector = selector;
       }
     break;
@@ -2926,9 +2851,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 ||
@@ -3184,8 +3111,8 @@ readconf_main();
 
 /* Handle the decoding of logging options. */
 
-decode_bits(&log_write_selector, &log_extra_selector, 0, 0, log_selector_string,
-  log_options, log_options_count, US"log");
+decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
+  log_selector_string, log_options, log_options_count, US"log", 0);
 
 DEBUG(D_any)
   {
@@ -3536,12 +3463,13 @@ configuration, but the queue run restriction can be relaxed. Only an admin
 user may request that a message be returned to its sender forthwith. Only an
 admin user may specify a debug level greater than D_v (because it might show
 passwords, etc. in lookup queries). Only an admin user may request a queue
-count. */
+count. Only an admin user can use the test interface to scan for email
+(because Exim will be in the spool dir and able to look at mails). */
 
 if (!admin_user)
   {
   BOOL debugset = (debug_selector & ~D_v) != 0;
-  if (deliver_give_up || daemon_listen ||
+  if (deliver_give_up || daemon_listen || malware_test_file ||
      (count_queue && queue_list_requires_admin) ||
      (list_queue && queue_list_requires_admin) ||
      (queue_interval >= 0 && prod_requires_admin) ||
@@ -3692,6 +3620,33 @@ if (!unprivileged &&                      /* originally had root AND */
 
 else setgid(exim_gid);
 
+/* Handle a request to scan a file for malware */
+if (malware_test_file)
+  {
+#ifdef WITH_CONTENT_SCAN
+  int result;
+  set_process_info("scanning file for malware");
+  result = malware_in_file(malware_test_file);
+  if (result == FAIL)
+    {
+    printf("No malware found.\n");
+    exit(EXIT_SUCCESS);
+    }
+  if (result != OK)
+    {
+    printf("Malware lookup returned non-okay/fail: %d\n", result);
+    exit(EXIT_FAILURE);
+    }
+  if (malware_name)
+    printf("Malware found: %s\n", malware_name);
+  else
+    printf("Malware scan detected malware of unknown name.\n");
+#else
+  printf("Malware scanning not enabled at compile time.\n");
+#endif
+  exit(EXIT_FAILURE);
+  }
+
 /* Handle a request to list the delivery queue */
 
 if (list_queue)
@@ -3883,7 +3838,8 @@ if (list_options)
       if (i < argc - 1 &&
           (Ustrcmp(argv[i], "router") == 0 ||
            Ustrcmp(argv[i], "transport") == 0 ||
-           Ustrcmp(argv[i], "authenticator") == 0))
+           Ustrcmp(argv[i], "authenticator") == 0 ||
+           Ustrcmp(argv[i], "macro") == 0))
         {
         readconf_print(argv[i+1], argv[i]);
         i++;
@@ -4424,14 +4380,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);
   }