tidying
[exim.git] / src / src / buildconfig.c
index 6dd0d51120ebcb49038a897cee2b01e060a80d3c..426714f9113dee0ea6e1c57d71aab8d39972b0a2 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/buildconfig.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
 /* This auxiliary program builds the file config.h by the following
 process:
 
-First it reads Makefile, looking for certain OS-specific definitions which it
-uses to define macros. Then it reads the defaults file config.h.defaults.
+First, it determines the size of off_t and time_t variables, and generates
+macro code to define OFF_T_FMT and TIME_T_FMT as suitable formats, if they are
+not already defined in the system-specific header file.
+
+Then it reads Makefile, looking for certain OS-specific definitions which it
+uses to define some specific macros. Finally, it reads the defaults file
+config.h.defaults.
 
 The defaults file contains normal C #define statements for various macros; if
 the name of a macro is found in the environment, the environment value replaces
@@ -33,20 +36,22 @@ normally called independently. */
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/time.h>
+#include <poll.h>
 #include <pwd.h>
 #include <grp.h>
 
 typedef struct {
-  char *name;
+  const char *name;
   int *flag;
 } have_item;
 
 typedef struct {
-  char *name;
+  const char *name;
   char *data;
 } save_item;
 
-static char *db_opts[] = { "", "USE_DB", "USE_GDBM", "USE_TDB" };
+static const char *db_opts[] = { "", "USE_DB", "USE_GDBM", "USE_TDB" };
 
 static int have_ipv6 = 0;
 static int have_iconv = 0;
@@ -96,6 +101,17 @@ if (!OK)
 int
 main(int argc, char **argv)
 {
+off_t test_off_t = 0;
+time_t test_time_t = 0;
+ino_t test_ino_t;
+#if ! (__STDC_VERSION__ >= 199901L)
+size_t test_size_t = 0;
+ssize_t test_ssize_t = 0;
+unsigned long test_ulong_t = 0L;
+unsigned int test_uint_t = 0;
+#endif
+long test_long_t = 0;
+int test_int_t = 0;
 FILE *base;
 FILE *new;
 int last_initial = 'A';
@@ -132,13 +148,82 @@ fprintf(new, "using values specified in the configuration file Local/Makefile.\n
 fprintf(new, "Do not edit it. Instead, edit Local/Makefile and "
   "rerun make. */\n\n");
 
-/* First, search the makefile for certain settings */
+/* First, deal with the printing format for off_t variables. We assume that if
+the size of off_t is greater than 4, "%lld" will be available as a format for
+printing long long variables, and there will be support for the long long type.
+This assumption is known to be OK for the common operating systems. */
+
+fprintf(new, "#ifndef OFF_T_FMT\n");
+if (sizeof(test_off_t) > sizeof(test_long_t))
+  {
+  fprintf(new, "# define OFF_T_FMT  \"%%lld\"\n");
+  fprintf(new, "# define LONGLONG_T long long int\n");
+  }
+else
+  {
+  fprintf(new, "# define OFF_T_FMT  \"%%ld\"\n");
+  fprintf(new, "# define LONGLONG_T long int\n");
+  }
+fprintf(new, "#endif\n\n");
+
+/* Now do the same thing for time_t variables. If the length is greater than
+4, we want to assume long long support (even if off_t was less than 4). If the
+length is 4 or less, we can leave LONGLONG_T to whatever was defined above for
+off_t. */
+
+fprintf(new, "#ifndef TIME_T_FMT\n");
+if (sizeof(test_time_t) > sizeof(test_long_t))
+  {
+  fprintf(new, "# define TIME_T_FMT  \"%%lld\"\n");
+  fprintf(new, "# undef  LONGLONG_T\n");
+  fprintf(new, "# define LONGLONG_T long long int\n");
+  }
+else
+  fprintf(new, "# define TIME_T_FMT  \"%%ld\"\n");
+fprintf(new, "#endif\n\n");
+
+fprintf(new, "#ifndef INO_T_FMT\n");
+if (sizeof(test_ino_t) > sizeof(test_long_t))
+  fprintf(new, "# define INO_T_FMT  \"%%llu\"\n");
+else
+  fprintf(new, "# define INO_T_FMT  \"%%lu\"\n");
+fprintf(new, "#endif\n\n");
+
+fprintf(new, "#ifndef PID_T_FMT\n");
+fprintf(new, "# define PID_T_FMT  \"%%lu\"\n");
+fprintf(new, "#endif\n\n");
+
+/* And for sizeof() results, size_t, which should with C99 be just %zu, deal
+with C99 not being ubiquitous yet.  Unfortunately.  Assume ssize_t is same
+size as size_t on C99; if someone comes up with a version where it's not, fix
+it then. */
+
+#if __STDC_VERSION__ >= 199901L
+fprintf(new, "#define SIZE_T_FMT  \"%%zu\"\n");
+fprintf(new, "#define SSIZE_T_FMT  \"%%zd\"\n");
+#else
+if (sizeof(test_size_t) > sizeof (test_ulong_t))
+  fprintf(new, "#define SIZE_T_FMT  \"%%llu\"\n");
+else if (sizeof(test_size_t) > sizeof (test_uint_t))
+  fprintf(new, "#define SIZE_T_FMT  \"%%lu\"\n");
+else
+  fprintf(new, "#define SIZE_T_FMT  \"%%u\"\n");
+
+if (sizeof(test_ssize_t) > sizeof(test_long_t))
+  fprintf(new, "#define SSIZE_T_FMT  \"%%lld\"\n");
+else if (sizeof(test_ssize_t) > sizeof(test_int_t))
+  fprintf(new, "#define SSIZE_T_FMT  \"%%ld\"\n");
+else
+  fprintf(new, "#define SSIZE_T_FMT  \"%%d\"\n");
+#endif
+
+/* Now search the makefile for certain settings */
 
 base = fopen("Makefile", "rb");
 if (base == NULL)
   {
   printf("*** Buildconfig: failed to open Makefile\n");
-  fclose(new);
+  (void)fclose(new);
   exit(1);
   }
 
@@ -242,15 +327,16 @@ fprintf(new, "#define HAVE_ICONV            %s\n",
 if (errno_quota[0] != 0)
   fprintf(new, "\n#define ERRNO_QUOTA           %s\n", errno_quota);
 
-if (strcmp(cc, "gcc") == 0 && strstr(ostype, "IRIX") != NULL)
+if (strcmp(cc, "gcc") == 0 &&
+    (strstr(ostype, "IRIX") != NULL || strstr(ostype, "AIX") != NULL))
   {
   fprintf(new, "\n/* This switch includes the code to fix the inet_ntoa() */");
-  fprintf(new, "\n/* bug when using gcc on an IRIX system. */");
+  fprintf(new, "\n/* bug when using gcc on an IRIX or AIX system. */");
   fprintf(new, "\n#define USE_INET_NTOA_FIX");
   }
 
 fprintf(new, "\n");
-fclose(base);
+(void)fclose(base);
 
 
 /* Now handle the macros listed in the defaults */
@@ -259,7 +345,7 @@ base = fopen("../src/config.h.defaults", "rb");
 if (base == NULL)
   {
   printf("*** Buildconfig: failed to open ../src/config.h.defaults\n");
-  fclose(new);
+  (void)fclose(new);
   exit(1);
   }
 
@@ -273,6 +359,16 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
 
   while (*p == ' ' || *p == '\t') p++;
 
+  if (strncmp(p, "#ifdef ", 7) == 0
+   || strncmp(p, "#ifndef ", 8) == 0
+   || strncmp(p, "#if ", 4) == 0
+   || strncmp(p, "#endif", 6) == 0
+     )
+    {
+    fputs(buffer, new);
+    continue;
+    }
+
   if (strncmp(p, "#define ", 8) != 0) continue;
 
   p += 8;
@@ -312,6 +408,7 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
     uid_t uid = 0;
     gid_t gid = 0;
     int gid_set = 0;
+    int uid_not_set = 0;
     char *username = NULL;
     char *groupname = NULL;
     char *s;
@@ -366,6 +463,7 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
       while (isspace(*user)) user++;
       username = user;
       gid_set = 1;
+      uid_not_set = 1;
       }
 
     else
@@ -459,6 +557,19 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
       return 1;
       }
 
+    /* security sanity checks
+    if ref: is being used, we can never be sure, but we can take reasonable
+    steps to filter out the most obvious ones.  */
+
+    if ((!uid_not_set && uid == 0) ||
+        ((username != NULL) && (
+          (strcmp(username, "root") == 0) ||
+          (strcmp(username, "toor") == 0) )))
+      {
+      printf("\n*** Exim's internal user must not be root.\n\n");
+      return 1;
+      }
+
     /* Output user and group names or uid/gid. When names are set, uid/gid
     are set to zero but will be replaced at runtime. */
 
@@ -472,17 +583,20 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
     continue;
     }
 
-  /* CONFIGURE_OWNER is a special case. We look in the environment for
-  CONFIGURE_OWNER. If the value is not numeric, we look up the user. A lot of
-  this code is similar to that for EXIM_USER, but we aren't interested in a gid
-  here, and it's all optional, so just keep it separate. */
+  /* CONFIGURE_OWNER and CONFIGURE_GROUP are special cases. We look in the
+  environment for first. If the value is not numeric, we look up the user or
+  group. A lot of this code is similar to that for EXIM_USER, but it's easier
+  to keep it separate. */
 
-  if (strcmp(name, "CONFIGURE_OWNER") == 0)
+  if (strcmp(name, "CONFIGURE_OWNER") == 0 ||
+      strcmp(name, "CONFIGURE_GROUP") == 0)
     {
+    int isgroup = name[10] == 'G';
     uid_t uid = 0;
-    char *s;
-    char *username = NULL;
-    char *user = getenv("CONFIGURE_OWNER");
+    gid_t gid = 0;
+    const char *s;
+    const char *username = NULL;
+    const char *user = getenv(name);
 
     if (user == NULL) user = "";
     while (isspace((unsigned char)(*user))) user++;
@@ -496,9 +610,9 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
       {
       if (iscntrl((unsigned char)(*s)))
         {
-        printf("\n*** CONFIGURE_OWNER contains the control character 0x%02X in "
+        printf("\n*** %s contains the control character 0x%02X in "
           "one of the files\n    in the \"Local\" directory. Please review "
-          "your build-time\n    configuration.\n\n", *s);
+          "your build-time\n    configuration.\n\n", name, *s);
         return 1;
         }
       }
@@ -507,10 +621,13 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
 
     if (user[strspn(user, "0123456789")] == 0)
       {
-      uid = (uid_t)atoi(user);
+      if (isgroup)
+        gid = (gid_t)atoi(user);
+      else
+        uid = (uid_t)atoi(user);
       }
 
-    /* User name given. Normally, we look up the uid right away. However,
+    /* Name given. Normally, we look up the uid or gid right away. However,
     people building binary distributions sometimes want to retain the name till
     runtime. This is supported if the name begins "ref:". */
 
@@ -520,6 +637,18 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
       while (isspace(*user)) user++;
       username = user;
       }
+else if (isgroup)
+      {
+      struct group *gr = getgrnam(user);
+      if (gr == NULL)
+        {
+        printf("\n*** Group \"%s\" (specified in one of the Makefiles) does not "
+          "exist.\n    Please review your build-time configuration.\n\n",
+          user);
+        return 1;
+        }
+      gid = gr->gr_gid;
+      }
 
     else
       {
@@ -531,7 +660,6 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
           user);
         return 1;
         }
-
       uid = pw->pw_uid;
       }
 
@@ -539,8 +667,17 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
     are set to zero but will be replaced at runtime. */
 
     if (username != NULL)
-      fprintf(new, "#define CONFIGURE_OWNERNAME         \"%s\"\n", username);
-    fprintf(new, "#define CONFIGURE_OWNER              %d\n", (int)uid);
+      {
+      if (isgroup)
+        fprintf(new, "#define CONFIGURE_GROUPNAME         \"%s\"\n", username);
+      else
+        fprintf(new, "#define CONFIGURE_OWNERNAME         \"%s\"\n", username);
+      }
+
+    if (isgroup)
+      fprintf(new, "#define CONFIGURE_GROUP              %d\n", (int)gid);
+    else
+      fprintf(new, "#define CONFIGURE_OWNER              %d\n", (int)uid);
     continue;
     }
 
@@ -557,7 +694,7 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
     else
       {
       int count = 1;
-      int i;
+      int i, j;
       uid_t *vector;
       char *p = list;
       while (*p != 0) if (*p++ == ':') count++;
@@ -565,17 +702,22 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
       vector = malloc((count+1) * sizeof(uid_t));
       vector[0] = (uid_t)count;
 
-      for (i = 1; i <= count; list++, i++)
+      for (i = 1, j = 0; i <= count; list++, i++)
         {
         char name[64];
+
         p = list;
         while (*list != 0 && *list != ':') list++;
         strncpy(name, p, list-p);
         name[list-p] = 0;
 
-        if (name[strspn(name, "0123456789")] == 0)
+        if (name[0] == 0)
           {
-          vector[i] = (uid_t)atoi(name);
+          continue;
+          }
+        else if (name[strspn(name, "0123456789")] == 0)
+          {
+          vector[j++] = (uid_t)atoi(name);
           }
         else
           {
@@ -587,17 +729,45 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
               name);
             return 1;
             }
-          vector[i] = pw->pw_uid;
+          vector[j++] = pw->pw_uid;
           }
         }
-      fprintf(new, "#define FIXED_NEVER_USERS     %d, ", count);
-      for (i = 1; i <= count - 1; i++)
-        fprintf(new, "%d, ", (unsigned int)vector[i]);
-      fprintf(new, "%d\n", (unsigned int)vector[i]);
+      fprintf(new, "#define FIXED_NEVER_USERS     %d", j);
+      for (i = 0; i < j; i++) fprintf(new, ", %d", (unsigned int)vector[i]);
+      fprintf(new, "\n");
+      free(vector);
       }
     continue;
     }
 
+  /* WITH_CONTENT_SCAN is another special case: it must be set if it or
+  EXPERIMENTAL_DCC is set. */
+
+  if (strcmp(name, "WITH_CONTENT_SCAN") == 0)
+    {
+    char *wcs = getenv("WITH_CONTENT_SCAN");
+    char *dcc = getenv("EXPERIMENTAL_DCC");
+    fprintf(new, wcs || dcc
+      ? "#define WITH_CONTENT_SCAN     yes\n"
+      : "/* WITH_CONTENT_SCAN not set */\n");
+    continue;
+    }
+
+  /* DISABLE_DKIM is special; must be forced if no SUPPORT_TLS */
+  if (strcmp(name, "DISABLE_DKIM") == 0)
+    {
+    char *d_dkim = getenv("DISABLE_DKIM");
+    char *tls = getenv("SUPPORT_TLS");
+
+    if (d_dkim)
+      fprintf(new, "#define DISABLE_DKIM          yes\n");
+    else if (!tls)
+      fprintf(new, "#define DISABLE_DKIM          yes /* forced by lack of TLS */\n");
+    else
+      fprintf(new, "/* DISABLE_DKIM not set */\n");
+    continue;
+    }
+
   /* Otherwise, check whether a value exists in the environment. Remember if
   it is an AUTH setting or SUPPORT_CRYPTEQ. */
 
@@ -633,6 +803,7 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
     else if (strcmp(name, "RADIUS_LIB_TYPE") == 0)
       {
       if (strcmp(value, "RADIUSCLIENT") == 0 ||
+          strcmp(value, "RADIUSCLIENTNEW") == 0 ||
           strcmp(value, "RADLIB") == 0)
         {
         fprintf(new, "#define RADIUS_LIB_%s\n", value);
@@ -667,7 +838,11 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
             strncpy(buffer, ss, sss-ss);
             buffer[sss-ss] = 0;  /* For empty case */
             }
-          else strcpy(buffer, ss);
+          else
+           {
+                   strncpy(buffer, ss, sizeof(buffer));
+           buffer[sizeof(buffer)-1] = 0;
+           }
           pp = buffer + (int)strlen(buffer);
           while (pp > buffer && isspace((unsigned char)pp[-1])) pp--;
           *pp = 0;
@@ -680,12 +855,57 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
         fprintf(new, "\"%s\"\n", value);
         }
 
-      /* Timezone values and HEADERS_CHARSET get quoted */
+      /* Timezone values HEADERS_CHARSET, TCP_WRAPPERS_DAEMON_NAME and
+      WHITELIST_D_MACROS get quoted */
 
       else if (strcmp(name, "TIMEZONE_DEFAULT") == 0||
-               strcmp(name, "HEADERS_CHARSET") == 0)
+               strcmp(name, "TCP_WRAPPERS_DAEMON_NAME") == 0||
+               strcmp(name, "HEADERS_CHARSET") == 0||
+               strcmp(name, "WHITELIST_D_MACROS") == 0)
         fprintf(new, "\"%s\"\n", value);
 
+      /* GnuTLS constants; first is for debugging, others are tuning */
+
+      /* less than 0 is not-active; 0-9 are normal, API suggests higher
+      taken without problems */
+      else if (strcmp(name, "EXIM_GNUTLS_LIBRARY_LOG_LEVEL") == 0)
+        {
+        long nv;
+        char *end;
+        nv = strtol(value, &end, 10);
+        if (end != value && *end == '\0' && nv >= -1 && nv <= 100)
+          {
+          fprintf(new, "%s\n", value);
+          }
+        else
+          {
+          printf("Value of %s should be -1..9\n", name);
+          return 1;
+          }
+        }
+
+      /* how many bits Exim, as a client, demands must be in D-H */
+      /* 1024 is a historical figure; some sites actually use lower, so we
+      permit the value to be lowered "dangerously" low, but not "insanely"
+      low.  Though actually, 1024 is becoming "dangerous". */
+      else if ((strcmp(name, "EXIM_CLIENT_DH_MIN_MIN_BITS") == 0) ||
+               (strcmp(name, "EXIM_CLIENT_DH_DEFAULT_MIN_BITS") == 0) ||
+               (strcmp(name, "EXIM_SERVER_DH_BITS_PRE2_12") == 0))
+        {
+        long nv;
+        char *end;
+        nv = strtol(value, &end, 10);
+        if (end != value && *end == '\0' && nv >= 512 && nv < 500000)
+          {
+          fprintf(new, "%s\n", value);
+          }
+        else
+          {
+          printf("Unreasonable value (%s) of \"%s\".\n", value, name);
+          return 1;
+          }
+        }
+
       /* For others, quote any paths and don't quote anything else */
 
       else
@@ -727,21 +947,37 @@ while (fgets(buffer, sizeof(buffer), base) != NULL)
     }
   }
 
-fclose(base);
+(void)fclose(base);
 
 /* If any AUTH macros were defined, ensure that SUPPORT_CRYPTEQ is also
 defined. */
 
 if (have_auth)
-  {
   if (!support_crypteq) fprintf(new, "/* Force SUPPORT_CRYPTEQ for AUTH */\n"
     "#define SUPPORT_CRYPTEQ\n");
+
+/* Check poll() for timer functionality.
+Some OS' have released with it broken. */
+
+  {
+  struct timeval before, after;
+  size_t us;
+
+  gettimeofday(&before, NULL);
+  (void) poll(NULL, 0, 500);
+  gettimeofday(&after, NULL);
+
+  us = (after.tv_sec - before.tv_sec) * 1000000 +
+    (after.tv_usec - before.tv_usec);
+
+  if (us < 400000)
+    fprintf(new, "#define NO_POLL_H\n");
   }
 
 /* End off */
 
 fprintf(new, "\n/* End of config.h */\n");
-fclose(new);
+(void)fclose(new);
 return 0;
 }