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