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