Eximon: fix string-handling. Bug 2500
[users/heiko/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" };
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 base = fopen("Makefile", "rb");
225 if (base == NULL)
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     {
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     }
401   if (i < sizeof(db_opts)/sizeof(char *)) continue;
402
403   /* EXIM_USER is a special case. We look in the environment for EXIM_USER or
404   EXIM_UID (the latter for backward compatibility with Exim 3). If the value is
405   not numeric, we look up the user, and default the GID if found. Otherwise,
406   EXIM_GROUP or EXIM_GID must be in the environment. */
407
408   if (strcmp(name, "EXIM_UID") == 0)
409     {
410     uid_t uid = 0;
411     gid_t gid = 0;
412     int gid_set = 0;
413     int uid_not_set = 0;
414     char *username = NULL;
415     char *groupname = NULL;
416     char *s;
417     char *user = getenv("EXIM_USER");
418     char *group = getenv("EXIM_GROUP");
419
420     if (user == NULL) user = getenv("EXIM_UID");
421     if (group == NULL) group = getenv("EXIM_GID");
422
423     if (user == NULL)
424       {
425       printf("\n*** EXIM_USER has not been defined in any of the Makefiles in "
426         "the\n    \"Local\" directory. Please review your build-time "
427         "configuration.\n\n");
428       return 1;
429       }
430
431     while (isspace((unsigned char)(*user))) user++;
432     if (*user == 0)
433       {
434       printf("\n*** EXIM_USER is defined as an empty string in one of the "
435         "files\n    in the \"Local\" directory. Please review your build-time"
436         "\n    configuration.\n\n");
437       return 1;
438       }
439
440     for (s = user; *s != 0; s++)
441       {
442       if (iscntrl((unsigned char)(*s)))
443         {
444         printf("\n*** EXIM_USER contains the control character 0x%02X in one "
445           "of the files\n    in the \"Local\" directory. Please review your "
446           "build-time\n    configuration.\n\n", *s);
447         return 1;
448         }
449       }
450
451     /* Numeric uid given */
452
453     if (user[strspn(user, "0123456789")] == 0)
454       {
455       uid = (uid_t)atoi(user);
456       }
457
458     /* User name given. Normally, we look up the uid right away. However,
459     people building binary distributions sometimes want to retain the name till
460     runtime. This is supported if the name begins "ref:". */
461
462     else if (strncmp(user, "ref:", 4) == 0)
463       {
464       user += 4;
465       while (isspace(*user)) user++;
466       username = user;
467       gid_set = 1;
468       uid_not_set = 1;
469       }
470
471     else
472       {
473       struct passwd *pw = getpwnam(user);
474       if (pw == NULL)
475         {
476         printf("\n*** User \"%s\" (specified in one of the Makefiles) does not "
477           "exist.\n    Please review your build-time configuration.\n\n",
478           user);
479         return 1;
480         }
481
482       uid = pw->pw_uid;
483       gid = pw->pw_gid;
484       gid_set = 1;
485       }
486
487     /* Use explicit group if set. */
488
489     if (group != NULL)
490       {
491       while (isspace((unsigned char)(*group))) group++;
492       if (*group == 0)
493         {
494         printf("\n*** EXIM_GROUP is defined as an empty string in one of "
495           "the files in the\n    \"Local\" directory. ");
496         if (gid_set)
497           {
498           printf("If you want the Exim group to be taken from the\n    "
499             "password data for the Exim user, just remove the EXIM_GROUP "
500             "setting.\n    Otherwise, p");
501           }
502         else printf("EXIM_USER is defined numerically, so there is no"
503           "\n    default for EXIM_GROUP and you must set it explicitly.\n    P");
504         printf("lease review your build-time configuration.\n\n");
505         return 1;
506         }
507
508       for (s = group; *s != 0; s++)
509         {
510         if (iscntrl((unsigned char)(*s)))
511           {
512           printf("\n*** EXIM_GROUP contains the control character 0x%02X in one "
513             "of the files\n    in the \"Local\" directory. Please review your "
514             "build-time\n    configuration.\n\n", *s);
515           return 1;
516           }
517         }
518
519       /* Group name given. This may be by reference or to be looked up now,
520       as for user. */
521
522       if (strncmp(group, "ref:", 4) == 0)
523         {
524         group += 4;
525         while (isspace(*group)) group++;
526         groupname = group;
527         }
528
529       else if (username != NULL)
530         {
531         groupname = group;
532         }
533
534       else if (group[strspn(group, "0123456789")] == 0)
535         {
536         gid = (gid_t)atoi(group);
537         }
538
539       else
540         {
541         struct group *gr = getgrnam(group);
542         if (gr == NULL)
543           {
544           printf("\n*** Group \"%s\" (specified in one of the Makefiles) does "
545             "not exist.\n   Please review your build-time configuration.\n\n",
546             group);
547           return 1;
548           }
549         gid = gr->gr_gid;
550         }
551       }
552
553     /* Else trouble unless found in passwd file with user */
554
555     else if (!gid_set)
556       {
557       printf("\n*** No group set for Exim. Please review your build-time "
558         "configuration.\n\n");
559       return 1;
560       }
561
562     /* security sanity checks
563     if ref: is being used, we can never be sure, but we can take reasonable
564     steps to filter out the most obvious ones.  */
565
566     if ((!uid_not_set && uid == 0) ||
567         ((username != NULL) && (
568           (strcmp(username, "root") == 0) ||
569           (strcmp(username, "toor") == 0) )))
570       {
571       printf("\n*** Exim's internal user must not be root.\n\n");
572       return 1;
573       }
574
575     /* Output user and group names or uid/gid. When names are set, uid/gid
576     are set to zero but will be replaced at runtime. */
577
578     if (username != NULL)
579       fprintf(new, "#define EXIM_USERNAME         \"%s\"\n", username);
580     if (groupname != NULL)
581       fprintf(new, "#define EXIM_GROUPNAME        \"%s\"\n", groupname);
582
583     fprintf(new, "#define EXIM_UID              %d\n", (int)uid);
584     fprintf(new, "#define EXIM_GID              %d\n", (int)gid);
585     continue;
586     }
587
588   /* CONFIGURE_OWNER and CONFIGURE_GROUP are special cases. We look in the
589   environment for first. If the value is not numeric, we look up the user or
590   group. A lot of this code is similar to that for EXIM_USER, but it's easier
591   to keep it separate. */
592
593   if (strcmp(name, "CONFIGURE_OWNER") == 0 ||
594       strcmp(name, "CONFIGURE_GROUP") == 0)
595     {
596     int isgroup = name[10] == 'G';
597     uid_t uid = 0;
598     gid_t gid = 0;
599     const char *s;
600     const char *username = NULL;
601     const char *user = getenv(name);
602
603     if (user == NULL) user = "";
604     while (isspace((unsigned char)(*user))) user++;
605     if (*user == 0)
606       {
607       fprintf(new, "/* %s not set */\n", name);
608       continue;
609       }
610
611     for (s = user; *s != 0; s++)
612       {
613       if (iscntrl((unsigned char)(*s)))
614         {
615         printf("\n*** %s contains the control character 0x%02X in "
616           "one of the files\n    in the \"Local\" directory. Please review "
617           "your build-time\n    configuration.\n\n", name, *s);
618         return 1;
619         }
620       }
621
622     /* Numeric uid given */
623
624     if (user[strspn(user, "0123456789")] == 0)
625       {
626       if (isgroup)
627         gid = (gid_t)atoi(user);
628       else
629         uid = (uid_t)atoi(user);
630       }
631
632     /* Name given. Normally, we look up the uid or gid right away. However,
633     people building binary distributions sometimes want to retain the name till
634     runtime. This is supported if the name begins "ref:". */
635
636     else if (strncmp(user, "ref:", 4) == 0)
637       {
638       user += 4;
639       while (isspace(*user)) user++;
640       username = user;
641       }
642 else if (isgroup)
643       {
644       struct group *gr = getgrnam(user);
645       if (gr == NULL)
646         {
647         printf("\n*** Group \"%s\" (specified in one of the Makefiles) does not "
648           "exist.\n    Please review your build-time configuration.\n\n",
649           user);
650         return 1;
651         }
652       gid = gr->gr_gid;
653       }
654
655     else
656       {
657       struct passwd *pw = getpwnam(user);
658       if (pw == NULL)
659         {
660         printf("\n*** User \"%s\" (specified in one of the Makefiles) does not "
661           "exist.\n    Please review your build-time configuration.\n\n",
662           user);
663         return 1;
664         }
665       uid = pw->pw_uid;
666       }
667
668     /* Output user and group names or uid/gid. When names are set, uid/gid
669     are set to zero but will be replaced at runtime. */
670
671     if (username != NULL)
672       {
673       if (isgroup)
674         fprintf(new, "#define CONFIGURE_GROUPNAME         \"%s\"\n", username);
675       else
676         fprintf(new, "#define CONFIGURE_OWNERNAME         \"%s\"\n", username);
677       }
678
679     if (isgroup)
680       fprintf(new, "#define CONFIGURE_GROUP              %d\n", (int)gid);
681     else
682       fprintf(new, "#define CONFIGURE_OWNER              %d\n", (int)uid);
683     continue;
684     }
685
686   /* FIXED_NEVER_USERS is another special case. Look up the uid values and
687   create suitable initialization data for a vector. */
688
689   if (strcmp(name, "FIXED_NEVER_USERS") == 0)
690     {
691     char *list = getenv("FIXED_NEVER_USERS");
692     if (list == NULL)
693       {
694       fprintf(new, "#define FIXED_NEVER_USERS     0\n");
695       }
696     else
697       {
698       int count = 1;
699       int i, j;
700       uid_t *vector;
701       char *p = list;
702       while (*p != 0) if (*p++ == ':') count++;
703
704       vector = malloc((count+1) * sizeof(uid_t));
705       vector[0] = (uid_t)count;
706
707       for (i = 1, j = 0; i <= count; list++, i++)
708         {
709         char name[64];
710
711         p = list;
712         while (*list != 0 && *list != ':') list++;
713         strncpy(name, p, list-p);
714         name[list-p] = 0;
715
716         if (name[0] == 0)
717           {
718           continue;
719           }
720         else if (name[strspn(name, "0123456789")] == 0)
721           {
722           vector[j++] = (uid_t)atoi(name);
723           }
724         else
725           {
726           struct passwd *pw = getpwnam(name);
727           if (pw == NULL)
728             {
729             printf("\n*** User \"%s\" (specified for FIXED_NEVER_USERS in one of the Makefiles) does not "
730               "exist.\n    Please review your build-time configuration.\n\n",
731               name);
732             return 1;
733             }
734           vector[j++] = pw->pw_uid;
735           }
736         }
737       fprintf(new, "#define FIXED_NEVER_USERS     %d", j);
738       for (i = 0; i < j; i++) fprintf(new, ", %d", (unsigned int)vector[i]);
739       fprintf(new, "\n");
740       free(vector);
741       }
742     continue;
743     }
744
745   /* WITH_CONTENT_SCAN is another special case: it must be set if it or
746   EXPERIMENTAL_DCC is set. */
747
748   if (strcmp(name, "WITH_CONTENT_SCAN") == 0)
749     {
750     char *wcs = getenv("WITH_CONTENT_SCAN");
751     char *dcc = getenv("EXPERIMENTAL_DCC");
752     fprintf(new, wcs || dcc
753       ? "#define WITH_CONTENT_SCAN     yes\n"
754       : "/* WITH_CONTENT_SCAN not set */\n");
755     continue;
756     }
757
758   /* DISABLE_DKIM is special; must be forced if DISABLE_TLS */
759   if (strcmp(name, "DISABLE_DKIM") == 0)
760     {
761     char *d_dkim = getenv("DISABLE_DKIM");
762     char *notls = getenv("DISABLE_TLS");
763
764     if (d_dkim)
765       fprintf(new, "#define DISABLE_DKIM          yes\n");
766     else if (notls)
767       fprintf(new, "#define DISABLE_DKIM          yes /* forced by lack of TLS */\n");
768     else
769       fprintf(new, "/* DISABLE_DKIM not set */\n");
770     continue;
771     }
772
773   /* Otherwise, check whether a value exists in the environment. Remember if
774   it is an AUTH setting or SUPPORT_CRYPTEQ. */
775
776   if ((value = getenv(name)) != NULL)
777     {
778     int len;
779     len = 21 - (int)strlen(name);
780
781     if (strncmp(name, "AUTH_", 5) == 0) have_auth = 1;
782     if (strncmp(name, "SUPPORT_CRYPTEQ", 15) == 0) support_crypteq = 1;
783
784     /* The text value of LDAP_LIB_TYPE refers to a macro that gets set. */
785
786     if (strcmp(name, "LDAP_LIB_TYPE") == 0)
787       {
788       if (strcmp(value, "NETSCAPE") == 0 ||
789           strcmp(value, "UMICHIGAN") == 0 ||
790           strcmp(value, "OPENLDAP1") == 0 ||
791           strcmp(value, "OPENLDAP2") == 0 ||
792           strcmp(value, "SOLARIS") == 0 ||
793           strcmp(value, "SOLARIS7") == 0)              /* Compatibility */
794         {
795         fprintf(new, "#define LDAP_LIB_%s\n", value);
796         }
797       else
798         {
799         printf("\n*** LDAP_LIB_TYPE=%s is not a recognized LDAP library type."
800           "\n*** Please review your build-time configuration.\n\n", value);
801         return 1;
802         }
803       }
804
805     else if (strcmp(name, "RADIUS_LIB_TYPE") == 0)
806       {
807       if (strcmp(value, "RADIUSCLIENT") == 0 ||
808           strcmp(value, "RADIUSCLIENTNEW") == 0 ||
809           strcmp(value, "RADLIB") == 0)
810         {
811         fprintf(new, "#define RADIUS_LIB_%s\n", value);
812         }
813       else
814         {
815         printf("\n*** RADIUS_LIB_TYPE=%s is not a recognized RADIUS library type."
816           "\n*** Please review your build-time configuration.\n\n", value);
817         return 1;
818         }
819       }
820
821     /* Other macros get set to the environment value. */
822
823     else
824       {
825       fprintf(new, "#define %s ", name);
826       while(len-- > 0) fputc(' ', new);
827
828       /* LOG_FILE_PATH is now messy because it can be a path containing %s or
829       it can be "syslog" or ":syslog" or "syslog:path" or even "path:syslog". */
830
831       if (strcmp(name, "LOG_FILE_PATH") == 0)
832         {
833         char *ss = value;
834         for(;;)
835           {
836           char *pp;
837           char *sss = strchr(ss, ':');
838           if (sss != NULL)
839             {
840             strncpy(buffer, ss, sss-ss);
841             buffer[sss-ss] = 0;  /* For empty case */
842             }
843           else
844             {
845             strncpy(buffer, ss, sizeof(buffer));
846             buffer[sizeof(buffer)-1] = 0;
847             }
848           pp = buffer + (int)strlen(buffer);
849           while (pp > buffer && isspace((unsigned char)pp[-1])) pp--;
850           *pp = 0;
851           if (buffer[0] != 0 && strcmp(buffer, "syslog") != 0)
852             check_percent_ess(buffer, name);
853           if (sss == NULL) break;
854           ss = sss + 1;
855           while (isspace((unsigned char)*ss)) ss++;
856           }
857         fprintf(new, "\"%s\"\n", value);
858         }
859
860       /* Timezone values HEADERS_CHARSET, TCP_WRAPPERS_DAEMON_NAME and
861       WHITELIST_D_MACROS get quoted */
862
863       else if (strcmp(name, "TIMEZONE_DEFAULT") == 0||
864                strcmp(name, "TCP_WRAPPERS_DAEMON_NAME") == 0||
865                strcmp(name, "HEADERS_CHARSET") == 0||
866                strcmp(name, "WHITELIST_D_MACROS") == 0)
867         fprintf(new, "\"%s\"\n", value);
868
869       /* GnuTLS constants; first is for debugging, others are tuning */
870
871       /* less than 0 is not-active; 0-9 are normal, API suggests higher
872       taken without problems */
873       else if (strcmp(name, "EXIM_GNUTLS_LIBRARY_LOG_LEVEL") == 0)
874         {
875         long nv;
876         char *end;
877         nv = strtol(value, &end, 10);
878         if (end != value && *end == '\0' && nv >= -1 && nv <= 100)
879           {
880           fprintf(new, "%s\n", value);
881           }
882         else
883           {
884           printf("Value of %s should be -1..9\n", name);
885           return 1;
886           }
887         }
888
889       /* how many bits Exim, as a client, demands must be in D-H */
890       /* 1024 is a historical figure; some sites actually use lower, so we
891       permit the value to be lowered "dangerously" low, but not "insanely"
892       low.  Though actually, 1024 is becoming "dangerous". */
893       else if ((strcmp(name, "EXIM_CLIENT_DH_MIN_MIN_BITS") == 0) ||
894                (strcmp(name, "EXIM_CLIENT_DH_DEFAULT_MIN_BITS") == 0) ||
895                (strcmp(name, "EXIM_SERVER_DH_BITS_PRE2_12") == 0))
896         {
897         long nv;
898         char *end;
899         nv = strtol(value, &end, 10);
900         if (end != value && *end == '\0' && nv >= 512 && nv < 500000)
901           {
902           fprintf(new, "%s\n", value);
903           }
904         else
905           {
906           printf("Unreasonable value (%s) of \"%s\".\n", value, name);
907           return 1;
908           }
909         }
910
911       /* For others, quote any paths and don't quote anything else */
912
913       else
914         {
915         if (value[0] == '/') fprintf(new, "\"%s\"\n", value);
916           else fprintf(new, "%s\n", value);
917         }
918       }
919     }
920
921   /* Value not defined in the environment; use the default */
922
923   else
924     {
925     char *t = p;
926     while (*p == ' ' || *p == '\t') p++;
927     if (*p != '\n') fputs(buffer, new); else
928       {
929       *t = 0;
930       if (strcmp(name, "BIN_DIRECTORY")   == 0 ||
931           strcmp(name, "CONFIGURE_FILE")  == 0)
932         {
933         printf("\n*** %s has not been defined in any of the Makefiles in the\n"
934           "    \"Local\" directory. "
935           "Please review your build-time configuration.\n\n", name);
936         return 1;
937         }
938
939       if (strcmp(name, "TIMEZONE_DEFAULT") == 0)
940         {
941         char *tz = getenv("TZ");
942         fprintf(new, "#define TIMEZONE_DEFAULT      ");
943         if (tz == NULL) fprintf(new, "NULL\n"); else
944           fprintf(new, "\"%s\"\n", tz);
945         }
946
947       else fprintf(new, "/* %s not set */\n", name);
948       }
949     }
950   }
951
952 (void)fclose(base);
953
954 /* If any AUTH macros were defined, ensure that SUPPORT_CRYPTEQ is also
955 defined. */
956
957 if (have_auth)
958   if (!support_crypteq) fprintf(new, "/* Force SUPPORT_CRYPTEQ for AUTH */\n"
959     "#define SUPPORT_CRYPTEQ\n");
960
961 /* Check poll() for timer functionality.
962 Some OS' have released with it broken. */
963
964   {
965   struct timeval before, after;
966   size_t us;
967
968   gettimeofday(&before, NULL);
969   (void) poll(NULL, 0, 500);
970   gettimeofday(&after, NULL);
971
972   us = (after.tv_sec - before.tv_sec) * 1000000 +
973     (after.tv_usec - before.tv_usec);
974
975   if (us < 400000)
976     fprintf(new, "#define NO_POLL_H\n");
977   }
978
979 /* End off */
980
981 fprintf(new, "\n/* End of config.h */\n");
982 (void)fclose(new);
983 return 0;
984 }
985
986 /* End of buildconfig.c */