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