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