Copyright updates:
[exim.git] / src / src / buildconfig.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2022 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9
10 /*************************************************
11 *       Build configuration header for Exim      *
12 *************************************************/
13
14 /* This auxiliary program builds the file config.h by the following
15 process:
16
17 First, it determines the size of off_t and time_t variables, and generates
18 macro code to define OFF_T_FMT and TIME_T_FMT as suitable formats, if they are
19 not already defined in the system-specific header file.
20
21 Then it reads Makefile, looking for certain OS-specific definitions which it
22 uses to define some specific macros. Finally, it reads the defaults file
23 config.h.defaults.
24
25 The defaults file contains normal C #define statements for various macros; if
26 the name of a macro is found in the environment, the environment value replaces
27 the default. If the default #define does not contain any value, then that macro
28 is not copied to the created file unless there is some value in the
29 environment.
30
31 This program is compiled and run as part of the Make process and is not
32 normally called independently. */
33
34
35 #include <ctype.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/time.h>
41 #include <poll.h>
42 #include <pwd.h>
43 #include <grp.h>
44
45 typedef struct {
46   const char *name;
47   int *flag;
48 } have_item;
49
50 typedef struct {
51   const char *name;
52   char *data;
53 } save_item;
54
55 static const char *db_opts[] = { "", "USE_DB", "USE_GDBM", "USE_TDB", "USE_NDBM" };
56
57 static int have_ipv6 = 0;
58 static int have_iconv = 0;
59
60 static char errno_quota[256];
61 static char ostype[256];
62 static char cc[256];
63
64 /* If any entry is an initial substring of another, the longer one must
65 appear first. */
66
67 static have_item have_list[] = {
68   { "HAVE_IPV6",      &have_ipv6 },
69   { "HAVE_ICONV",     &have_iconv },
70   { NULL, NULL}
71 };
72
73 static save_item save_list[] = {
74   { "ERRNO_QUOTA",    errno_quota },
75   { "OSTYPE",         ostype },
76   { "CC",             cc },
77   { NULL, NULL}
78 };
79
80
81 /* Subroutine to check a string for precisely one instance of "%s". If not,
82 bomb out. */
83
84 void
85 check_percent_ess(char *value, char *name)
86 {
87 int OK = 0;
88 char *p = strstr(value, "%s");
89 if (p != NULL) OK = strstr(p+2, "%s") == NULL;
90 if (!OK)
91   {
92   printf("\n*** \"%s\" (%s) must contain precisely one occurrence of\n"
93     "*** \"%%s\". Please review your build-time configuration.\n\n/", value,
94     name);
95   exit(1);
96   }
97 }
98
99
100 /* Main program */
101
102 int
103 main(int argc, char **argv)
104 {
105 off_t test_off_t = 0;
106 time_t test_time_t = 0;
107 ino_t test_ino_t;
108 #if ! (__STDC_VERSION__ >= 199901L)
109 size_t test_size_t = 0;
110 ssize_t test_ssize_t = 0;
111 unsigned long test_ulong_t = 0L;
112 unsigned int test_uint_t = 0;
113 #endif
114 long test_long_t = 0;
115 long long test_longlong_t = 0;
116 int test_int_t = 0;
117 FILE *base;
118 FILE *new;
119 int last_initial = 'A';
120 int linecount = 0;
121 int have_auth = 0;
122 int in_local_makefile = 0;
123 int use_which_db = 0;
124 int use_which_db_in_local_makefile = 0;
125 int support_crypteq = 0;
126 char buffer[1024];
127
128 if (argc != 1)
129   {
130   printf("*** Buildconfig: called with incorrect arguments\n");
131   exit(1);
132   }
133
134 new = fopen("config.h", "wb");
135 if (new == NULL)
136   {
137   printf("*** Buildconfig: failed to open config.h for output\n");
138   exit(1);
139   }
140
141 printf("Building configuration file config.h\n");
142
143 fprintf(new, "/*************************************************\n");
144 fprintf(new, "*           Configuration header for Exim        *\n");
145 fprintf(new, "*************************************************/\n\n");
146
147 fprintf(new, "/* This file was automatically generated from Makefile and "
148   "config.h.defaults,\n");
149 fprintf(new, "using values specified in the configuration file Local/Makefile.\n");
150 fprintf(new, "Do not edit it. Instead, edit Local/Makefile and "
151   "rerun make. */\n\n");
152
153 /* First, deal with the printing format for off_t variables. We assume that if
154 the size of off_t is greater than 4, "%lld" will be available as a format for
155 printing long long variables, and there will be support for the long long type.
156 This assumption is known to be OK for the common operating systems. */
157
158 fprintf(new, "#ifndef OFF_T_FMT\n");
159 if (sizeof(test_off_t) > sizeof(test_long_t))
160   fprintf(new, "# define OFF_T_FMT  \"%%lld\"\n");
161 else
162   fprintf(new, "# define OFF_T_FMT  \"%%ld\"\n");
163 fprintf(new, "#endif\n\n");
164
165 fprintf(new, "#ifndef LONGLONG_T\n");
166 if (sizeof(test_longlong_t) > sizeof(test_long_t))
167   fprintf(new, "# define LONGLONG_T long long int\n");
168 else
169   fprintf(new, "# define LONGLONG_T long int\n");
170 fprintf(new, "#endif\n\n");
171
172 /* Now do the same thing for time_t variables. If the length is greater than
173 4, we want to assume long long support (even if off_t was less than 4). If the
174 length is 4 or less, we can leave LONGLONG_T to whatever was defined above for
175 off_t. */
176
177 fprintf(new, "#ifndef TIME_T_FMT\n");
178 if (sizeof(test_time_t) > sizeof(test_long_t))
179   {
180   fprintf(new, "# define TIME_T_FMT  \"%%lld\"\n");
181   fprintf(new, "# undef  LONGLONG_T\n");
182   fprintf(new, "# define LONGLONG_T long long int\n");
183   }
184 else
185   fprintf(new, "# define TIME_T_FMT  \"%%ld\"\n");
186 fprintf(new, "#endif\n\n");
187
188 fprintf(new, "#ifndef INO_T_FMT\n");
189 if (sizeof(test_ino_t) > sizeof(test_long_t))
190   fprintf(new, "# define INO_T_FMT  \"%%llu\"\n");
191 else
192   fprintf(new, "# define INO_T_FMT  \"%%lu\"\n");
193 fprintf(new, "#endif\n\n");
194
195 fprintf(new, "#ifndef PID_T_FMT\n");
196 fprintf(new, "# define PID_T_FMT  \"%%lu\"\n");
197 fprintf(new, "#endif\n\n");
198
199 /* And for sizeof() results, size_t, which should with C99 be just %zu, deal
200 with C99 not being ubiquitous yet.  Unfortunately.  Assume ssize_t is same
201 size as size_t on C99; if someone comes up with a version where it's not, fix
202 it then. */
203
204 #if __STDC_VERSION__ >= 199901L
205 fprintf(new, "#define SIZE_T_FMT  \"%%zu\"\n");
206 fprintf(new, "#define SSIZE_T_FMT  \"%%zd\"\n");
207 #else
208 if (sizeof(test_size_t) > sizeof (test_ulong_t))
209   fprintf(new, "#define SIZE_T_FMT  \"%%llu\"\n");
210 else if (sizeof(test_size_t) > sizeof (test_uint_t))
211   fprintf(new, "#define SIZE_T_FMT  \"%%lu\"\n");
212 else
213   fprintf(new, "#define SIZE_T_FMT  \"%%u\"\n");
214
215 if (sizeof(test_ssize_t) > sizeof(test_long_t))
216   fprintf(new, "#define SSIZE_T_FMT  \"%%lld\"\n");
217 else if (sizeof(test_ssize_t) > sizeof(test_int_t))
218   fprintf(new, "#define SSIZE_T_FMT  \"%%ld\"\n");
219 else
220   fprintf(new, "#define SSIZE_T_FMT  \"%%d\"\n");
221 #endif
222
223 /* Now search the makefile for certain settings */
224
225 if (!(base = fopen("Makefile", "rb")))
226   {
227   printf("*** Buildconfig: failed to open Makefile\n");
228   (void)fclose(new);
229   exit(1);
230   }
231
232 errno_quota[0] = 0;    /* no over-riding value set */
233 ostype[0] = 0;         /* just in case */
234 cc[0] = 0;
235
236 while (fgets(buffer, sizeof(buffer), base) != NULL)
237   {
238   int i;
239   have_item *h;
240   save_item *s;
241   char *p = buffer + (int)strlen(buffer);
242   linecount++;
243   while (p > buffer && isspace((unsigned char)p[-1])) p--;
244   *p = 0;
245   p = buffer;
246   while (isspace((unsigned char)*p)) p++;
247
248   /* Notice when we hit the user's makefile */
249
250   if (strcmp(p, "# From Local/Makefile") == 0)
251     {
252     in_local_makefile = 1;
253     continue;
254     }
255
256   /* Remember the last DB option setting. If we hit two in the user's
257   Makefile, complain. */
258
259   for (i = 1; i < sizeof(db_opts)/sizeof(char *); i++)
260     {
261     int len = (int)strlen(db_opts[i]);
262     if (strncmp(p, db_opts[i], len) == 0 && (p[len] == ' ' || p[len] == '='))
263       {
264       if (in_local_makefile)
265         {
266         if (use_which_db_in_local_makefile)
267           {
268           printf("*** Only one of USE_DB, USE_GDBM, or USE_TDB should be "
269             "defined in Local/Makefile\n");
270           exit(1);
271           }
272         use_which_db_in_local_makefile = 1;
273         }
274       use_which_db = i;
275       break;
276       }
277     }
278   if (i < sizeof(db_opts)/sizeof(char *)) continue;
279
280   /* Items where we just save a boolean */
281
282   for (h = have_list; h->name != NULL; h++)
283     {
284     int len = (int)strlen(h->name);
285     if (strncmp(p, h->name, len) == 0)
286       {
287       p += len;
288       while (isspace((unsigned char)*p)) p++;
289       if (*p++ != '=')
290         {
291         printf("*** Buildconfig: syntax error in Makefile line %d\n", linecount);
292         exit(1);
293         }
294       while (isspace((unsigned char)*p)) p++;
295       if (strcmp(p, "YES") == 0 || strcmp(p, "yes") == 0) *(h->flag) = 1;
296         else *(h->flag) = 0;   /* Must reset in case multiple instances */
297       break;
298       }
299     }
300
301   if (h->name != NULL) continue;
302
303   /* Items where we save the complete string */
304
305   for (s = save_list; s->name != NULL; s++)
306     {
307     int len = (int)strlen(s->name);
308     if (strncmp(p, s->name, len) == 0)
309       {
310       p += len;
311       while (isspace((unsigned char)*p)) p++;
312       if (*p++ != '=')
313         {
314         printf("*** Buildconfig: syntax error in Makefile line %d\n", linecount);
315         exit(1);
316         }
317       while (isspace((unsigned char)*p)) p++;
318       strcpy(s->data, p);
319       }
320     }
321   }
322
323 fprintf(new, "#define HAVE_IPV6             %s\n",
324   have_ipv6? "TRUE" : "FALSE");
325
326 fprintf(new, "#define HAVE_ICONV            %s\n",
327   have_iconv? "TRUE" : "FALSE");
328
329 if (errno_quota[0] != 0)
330   fprintf(new, "\n#define ERRNO_QUOTA           %s\n", errno_quota);
331
332 if (strcmp(cc, "gcc") == 0 &&
333     (strstr(ostype, "IRIX") != NULL || strstr(ostype, "AIX") != NULL))
334   {
335   fprintf(new, "\n/* This switch includes the code to fix the inet_ntoa() */");
336   fprintf(new, "\n/* bug when using gcc on an IRIX or AIX system. */");
337   fprintf(new, "\n#define USE_INET_NTOA_FIX");
338   }
339
340 fprintf(new, "\n");
341 (void)fclose(base);
342
343
344 /* Now handle the macros listed in the defaults */
345
346 base = fopen("../src/config.h.defaults", "rb");
347 if (base == NULL)
348   {
349   printf("*** Buildconfig: failed to open ../src/config.h.defaults\n");
350   (void)fclose(new);
351   exit(1);
352   }
353
354 while (fgets(buffer, sizeof(buffer), base) != NULL)
355   {
356   int i;
357   char name[256];
358   char *value;
359   char *p = buffer;
360   char *q = name;
361
362   while (*p == ' ' || *p == '\t') p++;
363
364   if (strncmp(p, "#ifdef ", 7) == 0
365    || strncmp(p, "#ifndef ", 8) == 0
366    || strncmp(p, "#if ", 4) == 0
367    || strncmp(p, "#endif", 6) == 0
368      )
369     {
370     fputs(buffer, new);
371     continue;
372     }
373
374   if (strncmp(p, "#define ", 8) != 0) continue;
375
376   p += 8;
377   while (*p == ' ' || *p == '\t') p++;
378
379   if (*p < last_initial) fprintf(new, "\n");
380   last_initial = *p;
381
382   while (*p && (isalnum((unsigned char)*p) || *p == '_')) *q++ = *p++;
383   *q = 0;
384
385   /* USE_DB, USE_GDBM, and USE_TDB are special cases. We want to have only
386   one of them set. The scan of the Makefile has saved which was the last one
387   encountered. */
388
389   for (i = 1; i < sizeof(db_opts)/sizeof(char *); i++)
390     if (strcmp(name, db_opts[i]) == 0)
391       {
392       if (use_which_db == i)
393         fprintf(new, "#define %s %.*syes\n", db_opts[i],
394           21 - (int)strlen(db_opts[i]), "                         ");
395       else
396         fprintf(new, "/* %s not set */\n", name);
397       break;
398       }
399   if (i < sizeof(db_opts)/sizeof(char *)) continue;
400
401   /* EXIM_USER is a special case. We look in the environment for EXIM_USER or
402   EXIM_UID (the latter for backward compatibility with Exim 3). If the value is
403   not numeric, we look up the user, and default the GID if found. Otherwise,
404   EXIM_GROUP or EXIM_GID must be in the environment. */
405
406   if (strcmp(name, "EXIM_UID") == 0)
407     {
408     uid_t uid = 0;
409     gid_t gid = 0;
410     int gid_set = 0;
411     int uid_not_set = 0;
412     char *username = NULL;
413     char *groupname = NULL;
414     char *s;
415     char *user = getenv("EXIM_USER");
416     char *group = getenv("EXIM_GROUP");
417
418     if (user == NULL) user = getenv("EXIM_UID");
419     if (group == NULL) group = getenv("EXIM_GID");
420
421     if (user == NULL)
422       {
423       printf("\n*** EXIM_USER has not been defined in any of the Makefiles in "
424         "the\n    \"Local\" directory. Please review your build-time "
425         "configuration.\n\n");
426       return 1;
427       }
428
429     while (isspace((unsigned char)(*user))) user++;
430     if (*user == 0)
431       {
432       printf("\n*** EXIM_USER is defined as an empty string in one of the "
433         "files\n    in the \"Local\" directory. Please review your build-time"
434         "\n    configuration.\n\n");
435       return 1;
436       }
437
438     for (s = user; *s != 0; s++)
439       {
440       if (iscntrl((unsigned char)(*s)))
441         {
442         printf("\n*** EXIM_USER contains the control character 0x%02X in one "
443           "of the files\n    in the \"Local\" directory. Please review your "
444           "build-time\n    configuration.\n\n", *s);
445         return 1;
446         }
447       }
448
449     /* Numeric uid given */
450
451     if (user[strspn(user, "0123456789")] == 0)
452       {
453       uid = (uid_t)atoi(user);
454       }
455
456     /* User name given. Normally, we look up the uid right away. However,
457     people building binary distributions sometimes want to retain the name till
458     runtime. This is supported if the name begins "ref:". */
459
460     else if (strncmp(user, "ref:", 4) == 0)
461       {
462       user += 4;
463       while (isspace(*user)) user++;
464       username = user;
465       gid_set = 1;
466       uid_not_set = 1;
467       }
468
469     else
470       {
471       struct passwd *pw = getpwnam(user);
472       if (pw == NULL)
473         {
474         printf("\n*** User \"%s\" (specified in one of the Makefiles) does not "
475           "exist.\n    Please review your build-time configuration.\n\n",
476           user);
477         return 1;
478         }
479
480       uid = pw->pw_uid;
481       gid = pw->pw_gid;
482       gid_set = 1;
483       }
484
485     /* Use explicit group if set. */
486
487     if (group != NULL)
488       {
489       while (isspace((unsigned char)(*group))) group++;
490       if (*group == 0)
491         {
492         printf("\n*** EXIM_GROUP is defined as an empty string in one of "
493           "the files in the\n    \"Local\" directory. ");
494         if (gid_set)
495           {
496           printf("If you want the Exim group to be taken from the\n    "
497             "password data for the Exim user, just remove the EXIM_GROUP "
498             "setting.\n    Otherwise, p");
499           }
500         else printf("EXIM_USER is defined numerically, so there is no"
501           "\n    default for EXIM_GROUP and you must set it explicitly.\n    P");
502         printf("lease review your build-time configuration.\n\n");
503         return 1;
504         }
505
506       for (s = group; *s != 0; s++)
507         {
508         if (iscntrl((unsigned char)(*s)))
509           {
510           printf("\n*** EXIM_GROUP contains the control character 0x%02X in one "
511             "of the files\n    in the \"Local\" directory. Please review your "
512             "build-time\n    configuration.\n\n", *s);
513           return 1;
514           }
515         }
516
517       /* Group name given. This may be by reference or to be looked up now,
518       as for user. */
519
520       if (strncmp(group, "ref:", 4) == 0)
521         {
522         group += 4;
523         while (isspace(*group)) group++;
524         groupname = group;
525         }
526
527       else if (username != NULL)
528         {
529         groupname = group;
530         }
531
532       else if (group[strspn(group, "0123456789")] == 0)
533         {
534         gid = (gid_t)atoi(group);
535         }
536
537       else
538         {
539         struct group *gr = getgrnam(group);
540         if (gr == NULL)
541           {
542           printf("\n*** Group \"%s\" (specified in one of the Makefiles) does "
543             "not exist.\n   Please review your build-time configuration.\n\n",
544             group);
545           return 1;
546           }
547         gid = gr->gr_gid;
548         }
549       }
550
551     /* Else trouble unless found in passwd file with user */
552
553     else if (!gid_set)
554       {
555       printf("\n*** No group set for Exim. Please review your build-time "
556         "configuration.\n\n");
557       return 1;
558       }
559
560     /* security sanity checks
561     if ref: is being used, we can never be sure, but we can take reasonable
562     steps to filter out the most obvious ones.  */
563
564     if ((!uid_not_set && uid == 0) ||
565         ((username != NULL) && (
566           (strcmp(username, "root") == 0) ||
567           (strcmp(username, "toor") == 0) )))
568       {
569       printf("\n*** Exim's internal user must not be root.\n\n");
570       return 1;
571       }
572
573     /* Output user and group names or uid/gid. When names are set, uid/gid
574     are set to zero but will be replaced at runtime. */
575
576     if (username != NULL)
577       fprintf(new, "#define EXIM_USERNAME         \"%s\"\n", username);
578     if (groupname != NULL)
579       fprintf(new, "#define EXIM_GROUPNAME        \"%s\"\n", groupname);
580
581     fprintf(new, "#define EXIM_UID              %d\n", (int)uid);
582     fprintf(new, "#define EXIM_GID              %d\n", (int)gid);
583     continue;
584     }
585
586   /* CONFIGURE_OWNER and CONFIGURE_GROUP are special cases. We look in the
587   environment for first. If the value is not numeric, we look up the user or
588   group. A lot of this code is similar to that for EXIM_USER, but it's easier
589   to keep it separate. */
590
591   if (strcmp(name, "CONFIGURE_OWNER") == 0 ||
592       strcmp(name, "CONFIGURE_GROUP") == 0)
593     {
594     int isgroup = name[10] == 'G';
595     uid_t uid = 0;
596     gid_t gid = 0;
597     const char *s;
598     const char *username = NULL;
599     const char *user = getenv(name);
600
601     if (user == NULL) user = "";
602     while (isspace((unsigned char)(*user))) user++;
603     if (*user == 0)
604       {
605       fprintf(new, "/* %s not set */\n", name);
606       continue;
607       }
608
609     for (s = user; *s != 0; s++)
610       {
611       if (iscntrl((unsigned char)(*s)))
612         {
613         printf("\n*** %s contains the control character 0x%02X in "
614           "one of the files\n    in the \"Local\" directory. Please review "
615           "your build-time\n    configuration.\n\n", name, *s);
616         return 1;
617         }
618       }
619
620     /* Numeric uid given */
621
622     if (user[strspn(user, "0123456789")] == 0)
623       {
624       if (isgroup)
625         gid = (gid_t)atoi(user);
626       else
627         uid = (uid_t)atoi(user);
628       }
629
630     /* Name given. Normally, we look up the uid or gid right away. However,
631     people building binary distributions sometimes want to retain the name till
632     runtime. This is supported if the name begins "ref:". */
633
634     else if (strncmp(user, "ref:", 4) == 0)
635       {
636       user += 4;
637       while (isspace(*user)) user++;
638       username = user;
639       }
640 else if (isgroup)
641       {
642       struct group *gr = getgrnam(user);
643       if (gr == NULL)
644         {
645         printf("\n*** Group \"%s\" (specified in one of the Makefiles) does not "
646           "exist.\n    Please review your build-time configuration.\n\n",
647           user);
648         return 1;
649         }
650       gid = gr->gr_gid;
651       }
652
653     else
654       {
655       struct passwd *pw = getpwnam(user);
656       if (pw == NULL)
657         {
658         printf("\n*** User \"%s\" (specified in one of the Makefiles) does not "
659           "exist.\n    Please review your build-time configuration.\n\n",
660           user);
661         return 1;
662         }
663       uid = pw->pw_uid;
664       }
665
666     /* Output user and group names or uid/gid. When names are set, uid/gid
667     are set to zero but will be replaced at runtime. */
668
669     if (username != NULL)
670       {
671       if (isgroup)
672         fprintf(new, "#define CONFIGURE_GROUPNAME         \"%s\"\n", username);
673       else
674         fprintf(new, "#define CONFIGURE_OWNERNAME         \"%s\"\n", username);
675       }
676
677     if (isgroup)
678       fprintf(new, "#define CONFIGURE_GROUP              %d\n", (int)gid);
679     else
680       fprintf(new, "#define CONFIGURE_OWNER              %d\n", (int)uid);
681     continue;
682     }
683
684   /* FIXED_NEVER_USERS is another special case. Look up the uid values and
685   create suitable initialization data for a vector. */
686
687   if (strcmp(name, "FIXED_NEVER_USERS") == 0)
688     {
689     char *list = getenv("FIXED_NEVER_USERS");
690     if (list == NULL)
691       {
692       fprintf(new, "#define FIXED_NEVER_USERS     0\n");
693       }
694     else
695       {
696       int count = 1;
697       int i, j;
698       uid_t *vector;
699       char *p = list;
700       while (*p != 0) if (*p++ == ':') count++;
701
702       vector = malloc((count+1) * sizeof(uid_t));
703       vector[0] = (uid_t)count;
704
705       for (i = 1, j = 0; i <= count; list++, i++)
706         {
707         char name[64];
708
709         p = list;
710         while (*list != 0 && *list != ':') list++;
711         strncpy(name, p, list-p);
712         name[list-p] = 0;
713
714         if (name[0] == 0)
715           {
716           continue;
717           }
718         else if (name[strspn(name, "0123456789")] == 0)
719           {
720           vector[j++] = (uid_t)atoi(name);
721           }
722         else
723           {
724           struct passwd *pw = getpwnam(name);
725           if (pw == NULL)
726             {
727             printf("\n*** User \"%s\" (specified for FIXED_NEVER_USERS in one of the Makefiles) does not "
728               "exist.\n    Please review your build-time configuration.\n\n",
729               name);
730             return 1;
731             }
732           vector[j++] = pw->pw_uid;
733           }
734         }
735       fprintf(new, "#define FIXED_NEVER_USERS     %d", j);
736       for (i = 0; i < j; i++) fprintf(new, ", %d", (unsigned int)vector[i]);
737       fprintf(new, "\n");
738       free(vector);
739       }
740     continue;
741     }
742
743   /* WITH_CONTENT_SCAN is another special case: it must be set if it or
744   EXPERIMENTAL_DCC is set. */
745
746   if (strcmp(name, "WITH_CONTENT_SCAN") == 0)
747     {
748     char *wcs = getenv("WITH_CONTENT_SCAN");
749     char *dcc = getenv("EXPERIMENTAL_DCC");
750     fprintf(new, wcs || dcc
751       ? "#define WITH_CONTENT_SCAN     yes\n"
752       : "/* WITH_CONTENT_SCAN not set */\n");
753     continue;
754     }
755
756   /* DISABLE_DKIM is special; must be forced if DISABLE_TLS */
757   if (strcmp(name, "DISABLE_DKIM") == 0)
758     {
759     char *d_dkim = getenv("DISABLE_DKIM");
760     char *notls = getenv("DISABLE_TLS");
761
762     if (d_dkim)
763       fprintf(new, "#define DISABLE_DKIM          yes\n");
764     else if (notls)
765       fprintf(new, "#define DISABLE_DKIM          yes /* forced by lack of TLS */\n");
766     else
767       fprintf(new, "/* DISABLE_DKIM not set */\n");
768     continue;
769     }
770
771   /* Otherwise, check whether a value exists in the environment. Remember if
772   it is an AUTH setting or SUPPORT_CRYPTEQ. */
773
774   if ((value = getenv(name)) != NULL)
775     {
776     int len;
777     len = 21 - (int)strlen(name);
778
779     if (strncmp(name, "AUTH_", 5) == 0) have_auth = 1;
780     if (strncmp(name, "SUPPORT_CRYPTEQ", 15) == 0) support_crypteq = 1;
781
782     /* The text value of LDAP_LIB_TYPE refers to a macro that gets set. */
783
784     if (strcmp(name, "LDAP_LIB_TYPE") == 0)
785       {
786       if (strcmp(value, "NETSCAPE") == 0 ||
787           strcmp(value, "UMICHIGAN") == 0 ||
788           strcmp(value, "OPENLDAP1") == 0 ||
789           strcmp(value, "OPENLDAP2") == 0 ||
790           strcmp(value, "SOLARIS") == 0 ||
791           strcmp(value, "SOLARIS7") == 0)              /* Compatibility */
792         {
793         fprintf(new, "#define LDAP_LIB_%s\n", value);
794         }
795       else
796         {
797         printf("\n*** LDAP_LIB_TYPE=%s is not a recognized LDAP library type."
798           "\n*** Please review your build-time configuration.\n\n", value);
799         return 1;
800         }
801       }
802
803     else if (strcmp(name, "RADIUS_LIB_TYPE") == 0)
804       {
805       if (strcmp(value, "RADIUSCLIENT") == 0 ||
806           strcmp(value, "RADIUSCLIENTNEW") == 0 ||
807           strcmp(value, "RADLIB") == 0)
808         {
809         fprintf(new, "#define RADIUS_LIB_%s\n", value);
810         }
811       else
812         {
813         printf("\n*** RADIUS_LIB_TYPE=%s is not a recognized RADIUS library type."
814           "\n*** Please review your build-time configuration.\n\n", value);
815         return 1;
816         }
817       }
818
819     /* Other macros get set to the environment value. */
820
821     else
822       {
823       fprintf(new, "#define %s ", name);
824       while(len-- > 0) fputc(' ', new);
825
826       /* LOG_FILE_PATH is now messy because it can be a path containing %s or
827       it can be "syslog" or ":syslog" or "syslog:path" or even "path:syslog". */
828
829       if (strcmp(name, "LOG_FILE_PATH") == 0)
830         {
831         char *ss = value;
832         for(;;)
833           {
834           char *pp;
835           char *sss = strchr(ss, ':');
836           if (sss != NULL)
837             {
838             strncpy(buffer, ss, sss-ss);
839             buffer[sss-ss] = 0;  /* For empty case */
840             }
841           else
842             {
843             strncpy(buffer, ss, sizeof(buffer));
844             buffer[sizeof(buffer)-1] = 0;
845             }
846           pp = buffer + (int)strlen(buffer);
847           while (pp > buffer && isspace((unsigned char)pp[-1])) pp--;
848           *pp = 0;
849           if (buffer[0] != 0 && strcmp(buffer, "syslog") != 0)
850             check_percent_ess(buffer, name);
851           if (sss == NULL) break;
852           ss = sss + 1;
853           while (isspace((unsigned char)*ss)) ss++;
854           }
855         fprintf(new, "\"%s\"\n", value);
856         }
857
858       /* Timezone values HEADERS_CHARSET, TCP_WRAPPERS_DAEMON_NAME and
859       WHITELIST_D_MACROS get quoted */
860
861       else if (strcmp(name, "TIMEZONE_DEFAULT") == 0||
862                strcmp(name, "TCP_WRAPPERS_DAEMON_NAME") == 0||
863                strcmp(name, "HEADERS_CHARSET") == 0||
864                strcmp(name, "WHITELIST_D_MACROS") == 0)
865         fprintf(new, "\"%s\"\n", value);
866
867       /* GnuTLS constants; first is for debugging, others are tuning */
868
869       /* less than 0 is not-active; 0-9 are normal, API suggests higher
870       taken without problems */
871       else if (strcmp(name, "EXIM_GNUTLS_LIBRARY_LOG_LEVEL") == 0)
872         {
873         long nv;
874         char *end;
875         nv = strtol(value, &end, 10);
876         if (end != value && *end == '\0' && nv >= -1 && nv <= 100)
877           {
878           fprintf(new, "%s\n", value);
879           }
880         else
881           {
882           printf("Value of %s should be -1..9\n", name);
883           return 1;
884           }
885         }
886
887       /* how many bits Exim, as a client, demands must be in D-H */
888       /* 1024 is a historical figure; some sites actually use lower, so we
889       permit the value to be lowered "dangerously" low, but not "insanely"
890       low.  Though actually, 1024 is becoming "dangerous". */
891       else if ((strcmp(name, "EXIM_CLIENT_DH_MIN_MIN_BITS") == 0) ||
892                (strcmp(name, "EXIM_CLIENT_DH_DEFAULT_MIN_BITS") == 0) ||
893                (strcmp(name, "EXIM_SERVER_DH_BITS_PRE2_12") == 0))
894         {
895         long nv;
896         char *end;
897         nv = strtol(value, &end, 10);
898         if (end != value && *end == '\0' && nv >= 512 && nv < 500000)
899           {
900           fprintf(new, "%s\n", value);
901           }
902         else
903           {
904           printf("Unreasonable value (%s) of \"%s\".\n", value, name);
905           return 1;
906           }
907         }
908
909       /* For others, quote any paths and don't quote anything else */
910
911       else
912         {
913         if (value[0] == '/') fprintf(new, "\"%s\"\n", value);
914           else fprintf(new, "%s\n", value);
915         }
916       }
917     }
918
919   /* Value not defined in the environment; use the default */
920
921   else
922     {
923     char *t = p;
924     while (*p == ' ' || *p == '\t') p++;
925     if (*p != '\n') fputs(buffer, new); else
926       {
927       *t = 0;
928       if (strcmp(name, "BIN_DIRECTORY")   == 0 ||
929           strcmp(name, "CONFIGURE_FILE")  == 0)
930         {
931         printf("\n*** %s has not been defined in any of the Makefiles in the\n"
932           "    \"Local\" directory. "
933           "Please review your build-time configuration.\n\n", name);
934         return 1;
935         }
936
937       if (strcmp(name, "TIMEZONE_DEFAULT") == 0)
938         {
939         char *tz = getenv("TZ");
940         fprintf(new, "#define TIMEZONE_DEFAULT      ");
941         if (tz == NULL) fprintf(new, "NULL\n"); else
942           fprintf(new, "\"%s\"\n", tz);
943         }
944
945       else fprintf(new, "/* %s not set */\n", name);
946       }
947     }
948   }
949
950 (void)fclose(base);
951
952 /* If any AUTH macros were defined, ensure that SUPPORT_CRYPTEQ is also
953 defined. */
954
955 if (have_auth)
956   if (!support_crypteq) fprintf(new, "/* Force SUPPORT_CRYPTEQ for AUTH */\n"
957     "#define SUPPORT_CRYPTEQ\n");
958
959 /* Check poll() for timer functionality.
960 Some OS' have released with it broken. */
961
962   {
963   struct timeval before, after;
964   size_t us;
965
966   gettimeofday(&before, NULL);
967   (void) poll(NULL, 0, 500);
968   gettimeofday(&after, NULL);
969
970   us = (after.tv_sec - before.tv_sec) * 1000000 +
971     (after.tv_usec - before.tv_usec);
972
973   if (us < 400000)
974     fprintf(new, "#define NO_POLL_H\n");
975   }
976
977 /* End off */
978
979 fprintf(new, "\n/* End of config.h */\n");
980 (void)fclose(new);
981 return 0;
982 }
983
984 /* End of buildconfig.c */