setup.c: don't offer to tweak /etc/inittab on host systems
[citadel.git] / citadel / setup.c
1 /*
2  * $Id$
3  *
4  * Citadel setup utility
5  *
6  */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/utsname.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <netdb.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <pwd.h>
23 #include <time.h>
24
25 #include "citadel.h"
26 #include "axdefs.h"
27 #include "sysdep.h"
28 #include "config.h"
29 #include "tools.h"
30 #include "citadel_dirs.h"
31
32 #ifdef HAVE_NEWT
33 #include <newt.h>
34 #endif
35
36
37 #define MAXSETUP 4      /* How many setup questions to ask */
38
39 #define UI_TEXT         0       /* Default setup type -- text only */
40 #define UI_DIALOG       2       /* Use the 'dialog' program */
41 #define UI_SILENT       3       /* Silent running, for use in scripts */
42 #define UI_NEWT         4       /* Use the "newt" window library */
43
44 #define SERVICE_NAME    "citadel"
45 #define PROTO_NAME      "tcp"
46
47 int setup_type;
48 char setup_directory[SIZ];
49 char citserver_init_entry[SIZ];
50 int using_web_installer = 0;
51 int enable_home = 1;
52
53 /**
54  *      We will set have_sysv_init to nonzero if the host operating system
55  *      has a System V style init driven by /etc/inittab.  This will cause
56  *      setup to offer to automatically start and stop the Citadel service.
57  */
58 int have_sysv_init = 0;
59
60 #ifdef HAVE_LDAP
61 void contemplate_ldap(void);
62 #endif
63
64 char *setup_titles[] =
65 {
66         "Citadel Home Directory",
67         "System Administrator",
68         "Citadel User ID",
69         "Server IP address",
70         "Server port number",
71 };
72
73
74 struct config config;
75
76         /* calculate all our path on a central place */
77     /* where to keep our config */
78         
79
80 char *setup_text[] = {
81 #ifndef HAVE_RUN_DIR
82 "Enter the full pathname of the directory in which the Citadel\n"
83 "installation you are creating or updating resides.  If you\n"
84 "specify a directory other than the default, you will need to\n"
85 "specify the -h flag to the server when you start it up.\n",
86 #else
87 "Enter the subdirectoryname for an alternating installation of "
88 "Citadel. To do a default installation just leave it blank."
89 "If you specify a directory other than the default, you will need to\n"
90 "specify the -h flag to the server when you start it up.\n"
91 "note that it may not have a leading /",
92 #endif
93
94 "Enter the name of the system administrator (which is probably\n"
95 "you).  When an account is created with this name, it will\n"
96 "automatically be given administrator-level access.\n",
97
98 "Citadel needs to run under its own user ID.  This would\n"
99 "typically be called \"citadel\", but if you are running Citadel\n"
100 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
101 "The server will run under this user ID.  Please specify that\n"
102 "user ID here.  You may specify either a user name or a numeric\n"
103 "UID.\n",
104
105 "Specify the IP address on which your server will run.  If you\n"
106 "leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
107 "on all addresses.  You can usually skip this unless you are\n"
108 "running multiple instances of Citadel on the same computer.\n",
109
110 "Specify the TCP port number on which your server will run.\n"
111 "Normally, this will be port 504, which is the official port\n"
112 "assigned by the IANA for Citadel servers.  You will only need\n"
113 "to specify a different port number if you run multiple instances\n"
114 "of Citadel on the same computer and there is something else\n"
115 "already using port 504.\n",
116
117 };
118
119 struct config config;
120 int direction;
121
122 /*
123  * Set an entry in inittab to the desired state
124  */
125 void set_init_entry(char *which_entry, char *new_state) {
126         char *inittab = NULL;
127         FILE *fp;
128         char buf[SIZ];
129         char entry[SIZ];
130         char levels[SIZ];
131         char state[SIZ];
132         char prog[SIZ];
133
134         if (which_entry == NULL) return;
135         if (strlen(which_entry) == 0) return;
136
137         inittab = strdup("");
138         if (inittab == NULL) return;
139
140         fp = fopen("/etc/inittab", "r");
141         if (fp == NULL) return;
142
143         while(fgets(buf, sizeof buf, fp) != NULL) {
144
145                 if (num_tokens(buf, ':') == 4) {
146                         extract_token(entry, buf, 0, ':', sizeof entry);
147                         extract_token(levels, buf, 1, ':', sizeof levels);
148                         extract_token(state, buf, 2, ':', sizeof state);
149                         extract_token(prog, buf, 3, ':', sizeof prog); /* includes 0x0a LF */
150
151                         if (!strcmp(entry, which_entry)) {
152                                 strcpy(state, new_state);
153                                 sprintf(buf, "%s:%s:%s:%s",
154                                         entry, levels, state, prog);
155                         }
156                 }
157
158                 inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
159                 if (inittab == NULL) {
160                         fclose(fp);
161                         return;
162                 }
163                 
164                 strcat(inittab, buf);
165         }
166         fclose(fp);
167         fp = fopen("/etc/inittab", "w");
168         if (fp != NULL) {
169                 fwrite(inittab, strlen(inittab), 1, fp);
170                 fclose(fp);
171                 kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
172         }
173         free(inittab);
174 }
175
176
177 /*
178  * Locate the name of an inittab entry for a specific program
179  */
180 void locate_init_entry(char *init_entry, char *looking_for) {
181
182         FILE *infp;
183         char buf[SIZ];
184         int have_entry = 0;
185         char entry[SIZ];
186         char prog[SIZ];
187
188         strcpy(init_entry, "");
189
190         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
191          * an entry is found which we believe starts the specified program.
192          */
193         infp = fopen("/etc/inittab", "r");
194         if (infp == NULL) {
195                 return;
196         } else {
197                 while (fgets(buf, sizeof buf, infp) != NULL) {
198                         buf[strlen(buf) - 1] = 0;
199                         extract_token(entry, buf, 0, ':', sizeof entry);
200                         extract_token(prog, buf, 3, ':', sizeof prog);
201                         if (!strncasecmp(prog, looking_for,
202                            strlen(looking_for))) {
203                                 ++have_entry;
204                                 strcpy(init_entry, entry);
205                         }
206                 }
207                 fclose(infp);
208         }
209
210 }
211
212
213 /* 
214  * Shut down the Citadel service if necessary, during setup.
215  */
216 void shutdown_citserver(void) {
217         char looking_for[SIZ];
218
219         snprintf(looking_for, 
220                          sizeof looking_for,
221                          "%s/citserver", 
222 #ifndef HAVE_RUN_DIR
223                          setup_directory
224 #else
225                          CTDLDIR
226 #endif
227                          );
228         locate_init_entry(citserver_init_entry, looking_for);
229         if (strlen(citserver_init_entry) > 0) {
230                 set_init_entry(citserver_init_entry, "off");
231         }
232 }
233
234
235 /*
236  * Start the Citadel service.
237  */
238 void start_citserver(void) {
239         if (strlen(citserver_init_entry) > 0) {
240                 set_init_entry(citserver_init_entry, "respawn");
241         }
242 }
243
244
245
246 void cleanup(int exitcode)
247 {
248 #ifdef HAVE_NEWT
249         newtCls();
250         newtRefresh();
251         newtFinished();
252 #endif
253         exit(exitcode);
254 }
255
256
257
258 void title(char *text)
259 {
260         if (setup_type == UI_TEXT) {
261                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
262         }
263 }
264
265
266
267 int yesno(char *question)
268 {
269 #ifdef HAVE_NEWT
270         newtComponent form = NULL;
271         newtComponent yesbutton = NULL;
272         newtComponent nobutton = NULL;
273         int prompt_window_height = 0;
274 #endif
275         int i = 0;
276         int answer = 0;
277         char buf[SIZ];
278
279         switch (setup_type) {
280
281         case UI_TEXT:
282                 do {
283                         printf("%s\nYes/No --> ", question);
284                         fgets(buf, sizeof buf, stdin);
285                         answer = tolower(buf[0]);
286                         if (answer == 'y')
287                                 answer = 1;
288                         else if (answer == 'n')
289                                 answer = 0;
290                 } while ((answer < 0) || (answer > 1));
291                 break;
292
293         case UI_DIALOG:
294                 sprintf(buf, "exec %s --yesno '%s' 10 72",
295                         getenv("CTDL_DIALOG"),
296                         question);
297                 i = system(buf);
298                 if (i == 0) {
299                         answer = 1;
300                 }
301                 else {
302                         answer = 0;
303                 }
304                 break;
305
306 #ifdef HAVE_NEWT
307         case UI_NEWT:
308                 prompt_window_height = num_tokens(question, '\n') + 5;
309                 newtCenteredWindow(76, prompt_window_height, "Question");
310                 form = newtForm(NULL, NULL, 0);
311                 for (i=0; i<num_tokens(question, '\n'); ++i) {
312                         extract_token(buf, question, i, '\n', sizeof buf);
313                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
314                 }
315                 yesbutton = newtButton(10, (prompt_window_height - 4), "Yes");
316                 nobutton = newtButton(60, (prompt_window_height - 4), "No");
317                 newtFormAddComponent(form, yesbutton);
318                 newtFormAddComponent(form, nobutton);
319                 if (newtRunForm(form) == yesbutton) {
320                         answer = 1;
321                 }
322                 else {
323                         answer = 0;
324                 }
325                 newtPopWindow();
326                 newtFormDestroy(form);  
327
328                 break;
329 #endif
330
331         }
332         return (answer);
333 }
334
335
336 void important_message(char *title, char *msgtext)
337 {
338 #ifdef HAVE_NEWT
339         newtComponent form = NULL;
340         int i = 0;
341 #endif
342         char buf[SIZ];
343
344         switch (setup_type) {
345
346         case UI_TEXT:
347                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
348                 printf("       %s \n\n%s\n\n", title, msgtext);
349                 printf("Press return to continue...");
350                 fgets(buf, sizeof buf, stdin);
351                 break;
352
353         case UI_DIALOG:
354                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
355                         getenv("CTDL_DIALOG"),
356                         msgtext);
357                 system(buf);
358                 break;
359
360 #ifdef HAVE_NEWT
361         case UI_NEWT:
362                 newtCenteredWindow(76, 10, title);
363                 form = newtForm(NULL, NULL, 0);
364                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
365                         extract_token(buf, msgtext, i, '\n', sizeof buf);
366                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
367                 }
368                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
369                 newtRunForm(form);
370                 newtPopWindow();
371                 newtFormDestroy(form);  
372                 break;
373 #endif
374
375         }
376 }
377
378 void important_msgnum(int msgnum)
379 {
380         important_message("Important Message", setup_text[msgnum]);
381 }
382
383 void display_error(char *error_message)
384 {
385         important_message("Error", error_message);
386 }
387
388 void progress(char *text, long int curr, long int cmax)
389 {
390 #ifdef HAVE_NEWT
391
392         /* These variables are static because progress() gets called
393          * multiple times during the course of whatever operation is
394          * being performed.  This makes setup non-threadsafe, but who
395          * cares?
396          */
397         static newtComponent form = NULL;
398         static newtComponent scale = NULL;
399 #endif
400         static long dots_printed = 0L;
401         long a = 0;
402         static FILE *fp = NULL;
403         char buf[SIZ];
404
405         switch (setup_type) {
406
407         case UI_TEXT:
408                 if (curr == 0) {
409                         printf("%s\n", text);
410                         printf("..........................");
411                         printf("..........................");
412                         printf("..........................\r");
413                         fflush(stdout);
414                         dots_printed = 0;
415                 } else if (curr == cmax) {
416                         printf("\r%79s\n", "");
417                 } else {
418                         a = (curr * 100) / cmax;
419                         a = a * 78;
420                         a = a / 100;
421                         while (dots_printed < a) {
422                                 printf("*");
423                                 ++dots_printed;
424                                 fflush(stdout);
425                         }
426                 }
427                 break;
428
429         case UI_DIALOG:
430                 if (curr == 0) {
431                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
432                                 getenv("CTDL_DIALOG"),
433                                 text);
434                         fp = popen(buf, "w");
435                         if (fp != NULL) {
436                                 fprintf(fp, "0\n");
437                                 fflush(fp);
438                         }
439                 } 
440                 else if (curr == cmax) {
441                         if (fp != NULL) {
442                                 fprintf(fp, "100\n");
443                                 pclose(fp);
444                                 fp = NULL;
445                         }
446                 }
447                 else {
448                         a = (curr * 100) / cmax;
449                         if (fp != NULL) {
450                                 fprintf(fp, "%ld\n", a);
451                                 fflush(fp);
452                         }
453                 }
454                 break;
455
456 #ifdef HAVE_NEWT
457         case UI_NEWT:
458                 if (curr == 0) {
459                         newtCenteredWindow(76, 8, text);
460                         form = newtForm(NULL, NULL, 0);
461                         scale = newtScale(1, 3, 74, cmax);
462                         newtFormAddComponent(form, scale);
463                         newtDrawForm(form);
464                         newtRefresh();
465                 }
466                 if ((curr > 0) && (curr <= cmax)) {
467                         newtScaleSet(scale, curr);
468                         newtRefresh();
469                 }
470                 if (curr == cmax) {
471                         newtFormDestroy(form);  
472                         newtPopWindow();
473                         newtRefresh();
474                 }
475                 break;
476 #endif
477
478         }
479 }
480
481
482
483 /*
484  * check_services_entry()  -- Make sure "citadel" is in /etc/services
485  *
486  */
487 void check_services_entry(void)
488 {
489         int i;
490         FILE *sfp;
491
492         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
493                 for (i=0; i<=3; ++i) {
494                         progress("Adding service entry...", i, 3);
495                         if (i == 0) {
496                                 sfp = fopen("/etc/services", "a");
497                                 if (sfp == NULL) {
498                                         display_error(strerror(errno));
499                                 } else {
500                                         fprintf(sfp, "%s                504/tcp\n",
501                                                 SERVICE_NAME);
502                                         fclose(sfp);
503                                 }
504                         }
505                         sleep(1);
506                 }
507         }
508 }
509
510
511 /*
512  * Generate a unique entry name for a new inittab entry
513  */
514 void generate_entry_name(char *entryname) {
515         char buf[SIZ];
516
517         snprintf(entryname, sizeof entryname, "c0");
518         do {
519                 ++entryname[1];
520                 if (entryname[1] > '9') {
521                         entryname[1] = 0;
522                         ++entryname[0];
523                         if (entryname[0] > 'z') {
524                                 display_error(
525                                    "Can't generate a unique entry name");
526                                 return;
527                         }
528                 }
529                 snprintf(buf, sizeof buf,
530                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
531         } while (system(buf) == 0);
532 }
533
534
535
536 /*
537  * check_inittab_entry()  -- Make sure "citadel" is in /etc/inittab
538  *
539  */
540 void check_inittab_entry(void)
541 {
542         FILE *infp;
543         char looking_for[SIZ];
544         char question[SIZ];
545         char entryname[5];
546
547         /* Determine the fully qualified path name of citserver */
548         snprintf(looking_for, 
549                          sizeof looking_for,
550                          "%s/citserver", 
551 #ifndef HAVE_RUN_DIR
552                          setup_directory
553 #else
554                          CTDLDIR
555 #endif
556                          );
557         locate_init_entry(citserver_init_entry, looking_for);
558
559         /* If there's already an entry, then we have nothing left to do. */
560         if (strlen(citserver_init_entry) > 0) {
561                 return;
562         }
563
564         /* Otherwise, prompt the user to create an entry. */
565         if (getenv("CREATE_INITTAB_ENTRY") != NULL) {
566                 if (strcasecmp(getenv("CREATE_INITTAB_ENTRY"), "yes")) {
567                         return;
568                 }
569         }
570         else {
571                 snprintf(question, sizeof question,
572                         "Do you want this computer configured to start the Citadel\n"
573                         "service automatically?  (If you answer yes, an entry in\n"
574                         "/etc/inittab pointing to %s will be added.)\n",
575                         looking_for);
576                 if (yesno(question) == 0) {
577                         return;
578                 }
579         }
580
581         /* Generate a unique entry name for /etc/inittab */
582         generate_entry_name(entryname);
583
584         /* Now write it out to /etc/inittab */
585         infp = fopen("/etc/inittab", "a");
586         if (infp == NULL) {
587                 display_error(strerror(errno));
588         } else {
589                 fprintf(infp, "# Start the Citadel server...\n");
590                 fprintf(infp, "%s:2345:respawn:%s %s%s -llocal4\n",
591                                 entryname, 
592                                 looking_for, 
593                                 (enable_home)?"-h":"", 
594                                 (enable_home)?setup_directory:"");
595                 fclose(infp);
596                 strcpy(citserver_init_entry, entryname);
597         }
598 }
599
600
601 /*
602  * On systems which use xinetd, see if we can offer to install Citadel as
603  * the default telnet target.
604  */
605 void check_xinetd_entry(void) {
606         char *filename = "/etc/xinetd.d/telnet";
607         FILE *fp;
608         char buf[SIZ];
609         int already_citadel = 0;
610
611         fp = fopen(filename, "r+");
612         if (fp == NULL) return;         /* Not there.  Oh well... */
613
614         while (fgets(buf, sizeof buf, fp) != NULL) {
615                 if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
616         }
617         fclose(fp);
618         if (already_citadel) return;    /* Already set up this way. */
619
620         /* Otherwise, prompt the user to create an entry. */
621         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
622                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
623                         return;
624                 }
625         }
626         else {
627                 snprintf(buf, sizeof buf,
628                         "Setup can configure the \"xinetd\" service to automatically\n"
629                         "connect incoming telnet sessions to Citadel, bypassing the\n"
630                         "host system login: prompt.  Would you like to do this?\n"
631                 );
632                 if (yesno(buf) == 0) {
633                         return;
634                 }
635         }
636
637         fp = fopen(filename, "w");
638         fprintf(fp,
639                 "# description: telnet service for Citadel users\n"
640                 "service telnet\n"
641                 "{\n"
642                 "       disable = no\n"
643                 "       flags           = REUSE\n"
644                 "       socket_type     = stream\n"
645                 "       wait            = no\n"
646                 "       user            = root\n"
647                 "       server          = /usr/sbin/in.telnetd\n"
648                 "       server_args     = -h -L %s/citadel\n"
649                 "       log_on_failure  += USERID\n"
650                 "}\n",
651 #ifndef HAVE_RUN_DIR
652                         setup_directory
653 #else
654                         RUN_DIR
655 #endif
656                         );
657         fclose(fp);
658
659         /* Now try to restart the service */
660         system("/etc/init.d/xinetd restart >/dev/null 2>&1");
661 }
662
663
664
665 /*
666  * Offer to disable other MTA's
667  */
668 void disable_other_mta(char *mta) {
669         char buf[SIZ];
670         FILE *fp;
671         int lines = 0;
672
673         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
674                 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
675                 mta, mta);
676         fp = popen(buf, "r");
677         if (fp == NULL) return;
678
679         while (fgets(buf, sizeof buf, fp) != NULL) {
680                 ++lines;
681         }
682         fclose(fp);
683         if (lines == 0) return;         /* Nothing to do. */
684
685
686         /* Offer to replace other MTA with the vastly superior Citadel :)  */
687
688         if (getenv("ACT_AS_MTA")) {
689                 if (strcasecmp(getenv("ACT_AS_MTA"), "yes")) {
690                         return;
691                 }
692         }
693         else {
694                 snprintf(buf, sizeof buf,
695                         "You appear to have the \"%s\" email program\n"
696                         "running on your system.  If you want Citadel mail\n"
697                         "connected with %s, you will have to manually integrate\n"
698                         "them.  It is preferable to disable %s, and use Citadel's\n"
699                         "SMTP, POP3, and IMAP services.\n\n"
700                         "May we disable %s so that Citadel has access to ports\n"
701                         "25, 110, and 143?\n",
702                         mta, mta, mta, mta
703                 );
704                 if (yesno(buf) == 0) {
705                         return;
706                 }
707         }
708
709         sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
710         system(buf);
711         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
712         system(buf);
713 }
714
715
716
717
718 /* 
719  * Check to see if our server really works.  Returns 0 on success.
720  */
721 int test_server(void) {
722         char cmd[256];
723         char cookie[256];
724         FILE *fp;
725         char buf[4096];
726         int found_it = 0;
727
728         /* Generate a silly little cookie.  We're going to write it out
729          * to the server and try to get it back.  The cookie does not
730          * have to be secret ... just unique.
731          */
732         sprintf(cookie, "--test--%d--", getpid());
733
734         sprintf(cmd, "%s/sendcommand %s%s ECHO %s 2>&1",
735 #ifndef HAVE_RUN_DIR
736                         setup_directory,
737 #else
738                         CTDLDIR,
739 #endif
740                         (enable_home)?"-h":"", 
741                         (enable_home)?setup_directory:"",
742                         cookie);
743
744         fp = popen(cmd, "r");
745         if (fp == NULL) return(errno);
746
747         while (fgets(buf, sizeof buf, fp) != NULL) {
748                 if ( (buf[0]=='2')
749                    && (strstr(buf, cookie) != NULL) ) {
750                         ++found_it;
751                 }
752         }
753         pclose(fp);
754
755         if (found_it) {
756                 return(0);
757         }
758         return(-1);
759 }
760
761 void strprompt(char *prompt_title, char *prompt_text, char *str)
762 {
763 #ifdef HAVE_NEWT
764         newtComponent form;
765         char *result;
766         int i;
767         int prompt_window_height = 0;
768 #endif
769         char buf[SIZ];
770         char setupmsg[SIZ];
771         char dialog_result[PATH_MAX];
772         FILE *fp = NULL;
773
774         strcpy(setupmsg, "");
775
776         switch (setup_type) {
777         case UI_TEXT:
778                 title(prompt_title);
779                 printf("\n%s\n", prompt_text);
780                 printf("This is currently set to:\n%s\n", str);
781                 printf("Enter new value or press return to leave unchanged:\n");
782                 fgets(buf, sizeof buf, stdin);
783                 buf[strlen(buf) - 1] = 0;
784                 if (strlen(buf) != 0)
785                         strcpy(str, buf);
786                 break;
787
788         case UI_DIALOG:
789                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
790                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
791                         getenv("CTDL_DIALOG"),
792                         prompt_text,
793                         str,
794                         dialog_result);
795                 system(buf);
796                 fp = fopen(dialog_result, "r");
797                 if (fp != NULL) {
798                         fgets(str, sizeof buf, fp);
799                         if (str[strlen(str)-1] == 10) {
800                                 str[strlen(str)-1] = 0;
801                         }
802                         fclose(fp);
803                         unlink(dialog_result);
804                 }
805                 break;
806
807 #ifdef HAVE_NEWT
808         case UI_NEWT:
809
810                 prompt_window_height = num_tokens(prompt_text, '\n') + 5 ;
811                 newtCenteredWindow(76,
812                                 prompt_window_height,
813                                 prompt_title);
814                 form = newtForm(NULL, NULL, 0);
815                 for (i=0; i<num_tokens(prompt_text, '\n'); ++i) {
816                         extract_token(buf, prompt_text, i, '\n', sizeof buf);
817                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
818                 }
819                 newtFormAddComponent(form,
820                         newtEntry(1,
821                                 (prompt_window_height - 2),
822                                 str,
823                                 74,
824                                 (const char **) &result,
825                                 NEWT_FLAG_RETURNEXIT)
826                 );
827                 newtRunForm(form);
828                 strcpy(str, result);
829
830                 newtPopWindow();
831                 newtFormDestroy(form);  
832
833 #endif
834         }
835 }
836
837 void set_str_val(int msgpos, char *str) {
838         strprompt(setup_titles[msgpos], setup_text[msgpos], str);
839 }
840
841
842
843 void set_int_val(int msgpos, int *ip)
844 {
845         char buf[16];
846         snprintf(buf, sizeof buf, "%d", (int) *ip);
847         set_str_val(msgpos, buf);
848         *ip = atoi(buf);
849 }
850
851
852 void set_char_val(int msgpos, char *ip)
853 {
854         char buf[16];
855         snprintf(buf, sizeof buf, "%d", (int) *ip);
856         set_str_val(msgpos, buf);
857         *ip = (char) atoi(buf);
858 }
859
860
861 void set_long_val(int msgpos, long int *ip)
862 {
863         char buf[16];
864         snprintf(buf, sizeof buf, "%ld", *ip);
865         set_str_val(msgpos, buf);
866         *ip = atol(buf);
867 }
868
869
870 void edit_value(int curr)
871 {
872         int i;
873         struct passwd *pw;
874         char ctdluidname[SIZ];
875
876         switch (curr) {
877
878         case 1:
879                 if (getenv("SYSADMIN_NAME")) {
880                         strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
881                 }
882                 else {
883                         set_str_val(curr, config.c_sysadm);
884                 }
885                 break;
886
887         case 2:
888 #ifdef __CYGWIN__
889                 config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
890 #else
891                 i = config.c_ctdluid;
892                 pw = getpwuid(i);
893                 if (pw == NULL) {
894                         set_int_val(curr, &i);
895                         config.c_ctdluid = i;
896                 }
897                 else {
898                         strcpy(ctdluidname, pw->pw_name);
899                         set_str_val(curr, ctdluidname);
900                         pw = getpwnam(ctdluidname);
901                         if (pw != NULL) {
902                                 config.c_ctdluid = pw->pw_uid;
903                         }
904                         else if (atoi(ctdluidname) > 0) {
905                                 config.c_ctdluid = atoi(ctdluidname);
906                         }
907                 }
908 #endif
909                 break;
910
911         case 3:
912                 set_str_val(curr, config.c_ip_addr);
913                 break;
914
915         case 4:
916                 set_int_val(curr, &config.c_port_number);
917                 break;
918
919
920         }
921 }
922
923 /*
924  * (re-)write the config data to disk
925  */
926 void write_config_to_disk(void)
927 {
928         FILE *fp;
929         int fd;
930
931         if ((fd = creat(
932 #ifndef HAVE_ETC_DIR
933                                         "."
934 #else
935                                         ETC_DIR
936 #endif
937                                         "/citadel.config", S_IRUSR | S_IWUSR)) == -1) {
938                 display_error("setup: cannot open citadel.config");
939                 cleanup(1);
940         }
941         fp = fdopen(fd, "wb");
942         if (fp == NULL) {
943                 display_error("setup: cannot open citadel.config");
944                 cleanup(1);
945         }
946         fwrite((char *) &config, sizeof(struct config), 1, fp);
947         fclose(fp);
948 }
949
950
951
952
953 /*
954  * Figure out what type of user interface we're going to use
955  */
956 int discover_ui(void)
957 {
958
959         /* Use "dialog" if we have it */
960         if (getenv("CTDL_DIALOG") != NULL) {
961                 return UI_DIALOG;
962         }
963                 
964
965 #ifdef HAVE_NEWT
966         newtInit();
967         newtCls();
968         newtDrawRootText(0, 0, "Citadel Setup");
969         return UI_NEWT;
970 #endif
971         return UI_TEXT;
972 }
973
974
975
976
977
978 int main(int argc, char *argv[])
979 {
980         int a;
981         int curr; 
982         char aaa[128];
983         FILE *fp;
984         int old_setup_level = 0;
985         int info_only = 0;
986         struct utsname my_utsname;
987         struct passwd *pw;
988         struct hostent *he;
989         gid_t gid;
990         int relh=0;
991         int home=0;
992         char relhome[PATH_MAX]="";
993         char ctdldir[PATH_MAX]=CTDLDIR;
994         
995         /* set an invalid setup type */
996         setup_type = (-1);
997
998         /* Learn whether to skip the auto-service-start questions */
999         fp = fopen("/etc/inittab", "r");
1000         if (fp != NULL) {
1001                 have_sysv_init = 1;
1002                 fclose(fp);
1003         }
1004
1005         /* Check to see if we're running the web installer */
1006         if (getenv("CITADEL_INSTALLER") != NULL) {
1007                 using_web_installer = 1;
1008         }
1009
1010         /* parse command line args */
1011         for (a = 0; a < argc; ++a) {
1012                 if (!strncmp(argv[a], "-u", 2)) {
1013                         strcpy(aaa, argv[a]);
1014                         strcpy(aaa, &aaa[2]);
1015                         setup_type = atoi(aaa);
1016                 }
1017                 if (!strcmp(argv[a], "-i")) {
1018                         info_only = 1;
1019                 }
1020                 if (!strcmp(argv[a], "-q")) {
1021                         setup_type = UI_SILENT;
1022                 }
1023         }
1024
1025
1026         /* If a setup type was not specified, try to determine automatically
1027          * the best one to use out of all available types.
1028          */
1029         if (setup_type < 0) {
1030                 setup_type = discover_ui();
1031         }
1032         if (info_only == 1) {
1033                 important_message("Citadel Setup", CITADEL);
1034                 cleanup(0);
1035         }
1036
1037         /* Get started in a valid setup directory. */
1038         strcpy(setup_directory, 
1039 #ifdef HAVE_RUN_DIR
1040                    ""
1041 #else
1042                    CTDLDIR
1043 #endif
1044                    );
1045         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
1046                 strcpy(setup_directory, getenv("CITADEL"));
1047         }
1048         else {
1049                 set_str_val(0, setup_directory);
1050         }
1051
1052         home=(setup_directory[1]!='\0');
1053         relh=home&(setup_directory[1]!='/');
1054         if (!relh) safestrncpy(ctdl_home_directory, setup_directory,
1055                                                                    sizeof ctdl_home_directory);
1056         else
1057                 safestrncpy(relhome, ctdl_home_directory,
1058                                         sizeof relhome);
1059
1060         calc_dirs_n_files(relh, home, relhome, ctdldir);
1061         
1062         enable_home=(relh|home);
1063
1064         if ((home) && (chdir(setup_directory) != 0)) {
1065                 important_message("Citadel Setup",
1066                           "The directory you specified does not exist.");
1067                 cleanup(errno);
1068         }
1069
1070         /* Determine our host name, in case we need to use it as a default */
1071         uname(&my_utsname);
1072
1073         if (have_sysv_init) {
1074                 /* See if we need to shut down the Citadel service. */
1075                 for (a=0; a<=3; ++a) {
1076                         progress("Shutting down the Citadel service...", a, 3);
1077                         if (a == 0) shutdown_citserver();
1078                         sleep(1);
1079                 }
1080
1081                 /* Make sure it's stopped. */
1082                 if (test_server() == 0) {
1083                         important_message("Citadel Setup",
1084                                 "The Citadel service is still running.\n"
1085                                 "Please stop the service manually and run "
1086                                 "setup again.");
1087                         cleanup(1);
1088                 }
1089         }
1090
1091         /* Now begin. */
1092         switch (setup_type) {
1093
1094         case UI_TEXT:
1095                 printf("\n\n\n"
1096                         "               *** Citadel setup program ***\n\n");
1097                 break;
1098
1099         }
1100
1101         /*
1102          * What we're going to try to do here is append a whole bunch of
1103          * nulls to the citadel.config file, so we can keep the old config
1104          * values if they exist, but if the file is missing or from an
1105          * earlier version with a shorter config structure, when setup tries
1106          * to read the old config parameters, they'll all come up zero.
1107          * The length of the config file will be set to what it's supposed
1108          * to be when we rewrite it, because we replace the old file with a
1109          * completely new copy.
1110          */
1111         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1112                       S_IRUSR | S_IWUSR)) == -1) {
1113                 display_error("setup: cannot append citadel.config");
1114                 cleanup(errno);
1115         }
1116         fp = fdopen(a, "ab");
1117         if (fp == NULL) {
1118                 display_error("setup: cannot append citadel.config");
1119                 cleanup(errno);
1120         }
1121         for (a = 0; a < sizeof(struct config); ++a)
1122                 putc(0, fp);
1123         fclose(fp);
1124
1125         /* now we re-open it, and read the old or blank configuration */
1126         fp = fopen(file_citadel_config, "rb");
1127         if (fp == NULL) {
1128                 display_error("setup: cannot open citadel.config");
1129                 cleanup(errno);
1130         }
1131         fread((char *) &config, sizeof(struct config), 1, fp);
1132         fclose(fp);
1133
1134         /* set some sample/default values in place of blanks... */
1135         if (strlen(config.c_nodename) == 0)
1136                 safestrncpy(config.c_nodename, my_utsname.nodename,
1137                             sizeof config.c_nodename);
1138         strtok(config.c_nodename, ".");
1139         if (strlen(config.c_fqdn) == 0) {
1140                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
1141                         safestrncpy(config.c_fqdn, he->h_name,
1142                                     sizeof config.c_fqdn);
1143                 else
1144                         safestrncpy(config.c_fqdn, my_utsname.nodename,
1145                                     sizeof config.c_fqdn);
1146         }
1147         if (strlen(config.c_humannode) == 0)
1148                 strcpy(config.c_humannode, "My System");
1149         if (strlen(config.c_phonenum) == 0)
1150                 strcpy(config.c_phonenum, "US 800 555 1212");
1151         if (config.c_initax == 0) {
1152                 config.c_initax = 4;
1153         }
1154         if (strlen(config.c_moreprompt) == 0)
1155                 strcpy(config.c_moreprompt, "<more>");
1156         if (strlen(config.c_twitroom) == 0)
1157                 strcpy(config.c_twitroom, "Trashcan");
1158         if (strlen(config.c_baseroom) == 0)
1159                 strcpy(config.c_baseroom, BASEROOM);
1160         if (strlen(config.c_aideroom) == 0)
1161                 strcpy(config.c_aideroom, "Aide");
1162         if (config.c_port_number == 0) {
1163                 config.c_port_number = 504;
1164         }
1165         if (config.c_sleeping == 0) {
1166                 config.c_sleeping = 900;
1167         }
1168         if (config.c_ctdluid == 0) {
1169                 pw = getpwnam("citadel");
1170                 if (pw != NULL)
1171                         config.c_ctdluid = pw->pw_uid;
1172         }
1173         if (config.c_ctdluid == 0) {
1174                 pw = getpwnam("bbs");
1175                 if (pw != NULL)
1176                         config.c_ctdluid = pw->pw_uid;
1177         }
1178         if (config.c_ctdluid == 0) {
1179                 pw = getpwnam("guest");
1180                 if (pw != NULL)
1181                         config.c_ctdluid = pw->pw_uid;
1182         }
1183         if (config.c_createax == 0) {
1184                 config.c_createax = 3;
1185         }
1186         /*
1187          * Negative values for maxsessions are not allowed.
1188          */
1189         if (config.c_maxsessions < 0) {
1190                 config.c_maxsessions = 0;
1191         }
1192         /* We need a system default message expiry policy, because this is
1193          * the top level and there's no 'higher' policy to fall back on.
1194          * By default, do not expire messages at all.
1195          */
1196         if (config.c_ep.expire_mode == 0) {
1197                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1198                 config.c_ep.expire_value = 0;
1199         }
1200
1201         /*
1202          * Default port numbers for various services
1203          */
1204         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1205         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1206         if (config.c_imap_port == 0) config.c_imap_port = 143;
1207         if (config.c_msa_port == 0) config.c_msa_port = 587;
1208         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1209         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1210         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1211
1212         /* Go through a series of dialogs prompting for config info */
1213         if (setup_type != UI_SILENT) {
1214                 for (curr = 1; curr <= MAXSETUP; ++curr) {
1215                         edit_value(curr);
1216                 }
1217         }
1218
1219 /***** begin version update section ***** */
1220         /* take care of any updating that is necessary */
1221
1222         old_setup_level = config.c_setup_level;
1223
1224         if (old_setup_level == 0) {
1225                 goto NEW_INST;
1226         }
1227
1228         if (old_setup_level < 555) {
1229                 important_message("Citadel Setup",
1230                                   "This Citadel installation is too old "
1231                                   "to be upgraded.");
1232                 cleanup(1);
1233         }
1234         write_config_to_disk();
1235
1236         old_setup_level = config.c_setup_level;
1237
1238         /* end of version update section */
1239
1240 NEW_INST:
1241         config.c_setup_level = REV_LEVEL;
1242
1243 /******************************************/
1244
1245         write_config_to_disk();
1246
1247         mkdir(ctdl_info_dir, 0700);
1248         chmod(ctdl_info_dir, 0700);
1249         chown(ctdl_info_dir, config.c_ctdluid, -1);
1250
1251         mkdir(ctdl_bio_dir, 0700);
1252         chmod(ctdl_bio_dir, 0700);
1253         chown(ctdl_bio_dir, config.c_ctdluid, -1);
1254
1255         mkdir(ctdl_usrpic_dir, 0700);
1256         chmod(ctdl_usrpic_dir, 0700);
1257         chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
1258
1259         mkdir(ctdl_message_dir, 0700);
1260         chmod(ctdl_message_dir, 0700);
1261         chown(ctdl_message_dir, config.c_ctdluid, -1);
1262
1263         mkdir(ctdl_hlp_dir, 0700);
1264         chmod(ctdl_hlp_dir, 0700);
1265         chown(ctdl_hlp_dir, config.c_ctdluid, -1);
1266
1267         mkdir(ctdl_image_dir, 0700);
1268         chmod(ctdl_image_dir, 0700);
1269         chown(ctdl_image_dir, config.c_ctdluid, -1);
1270
1271         /* TODO: where to put this? */
1272         mkdir("netconfigs", 0700);
1273         chmod("netconfigs", 0700);
1274         chown("netconfigs", config.c_ctdluid, -1);
1275
1276         /* Delete files and directories used by older Citadel versions */
1277         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1278         unlink("citadel.log");
1279         unlink("weekly");
1280
1281         check_services_entry(); /* Check /etc/services */
1282 #ifndef __CYGWIN__
1283         if (have_sysv_init) {
1284                 check_inittab_entry();  /* Check /etc/inittab */
1285         }
1286         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1287
1288         /* Offer to disable other MTA's on the system. */
1289         disable_other_mta("courier-authdaemon");
1290         disable_other_mta("courier-imap");
1291         disable_other_mta("courier-imap-ssl");
1292         disable_other_mta("courier-pop");
1293         disable_other_mta("courier-pop3");
1294         disable_other_mta("courier-pop3d");
1295         disable_other_mta("cyrmaster");
1296         disable_other_mta("cyrus");
1297         disable_other_mta("dovecot");
1298         disable_other_mta("exim");
1299         disable_other_mta("exim4");
1300         disable_other_mta("hula");
1301         disable_other_mta("imapd");
1302         disable_other_mta("mta");
1303         disable_other_mta("pop3d");
1304         disable_other_mta("popd");
1305         disable_other_mta("postfix");
1306         disable_other_mta("qmail");
1307         disable_other_mta("saslauthd");
1308         disable_other_mta("sendmail");
1309         disable_other_mta("vmailmgrd");
1310         disable_other_mta("zimbra");
1311 #endif
1312
1313         if ((pw = getpwuid(config.c_ctdluid)) == NULL)
1314                 gid = getgid();
1315         else
1316                 gid = pw->pw_gid;
1317
1318         progress("Setting file permissions", 0, 4);
1319         chown(".", config.c_ctdluid, gid);
1320         sleep(1);
1321         progress("Setting file permissions", 1, 4);
1322         chown(file_citadel_config, config.c_ctdluid, gid);
1323         sleep(1);
1324         progress("Setting file permissions", 2, 4);
1325
1326         snprintf(aaa, sizeof aaa,
1327                          "%schkpwd",
1328                          ctdl_sbin_dir);
1329         chown(aaa,0,0); /*  config.c_ctdluid, gid); chkpwd needs to be root owned*/
1330         sleep(1);
1331         progress("Setting file permissions", 3, 4);
1332         chmod(aaa, 04755); 
1333
1334         sleep(1);
1335         progress("Setting file permissions", 3, 4);
1336         chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1337         sleep(1);
1338         progress("Setting file permissions", 4, 4);
1339
1340 #ifdef HAVE_LDAP
1341         /* Contemplate the possibility of auto-configuring OpenLDAP */
1342         contemplate_ldap();
1343 #endif
1344
1345         /* See if we can start the Citadel service. */
1346         if ( (have_sysv_init) && (strlen(citserver_init_entry) > 0) ) {
1347                 for (a=0; a<=3; ++a) {
1348                         progress("Starting the Citadel service...", a, 3);
1349                         if (a == 0) start_citserver();
1350                         sleep(1);
1351                 }
1352                 if (test_server() == 0) {
1353                         important_message("Setup finished",
1354                                 "Setup of the Citadel server is complete.\n"
1355                                 "If you will be using WebCit, please run its\n"
1356                                 "setup program now; otherwise, run './citadel'\n"
1357                                 "to log in.\n");
1358                 }
1359                 else {
1360                         important_message("Setup finished",
1361                                 "Setup is finished, but the Citadel service "
1362                                 "failed to start.\n"
1363                                 "Go back and check your configuration.");
1364                 }
1365         }
1366         else {
1367                 important_message("Setup finished",
1368                         "Setup is finished.  You may now start the server.");
1369         }
1370
1371         cleanup(0);
1372         return 0;
1373 }
1374
1375
1376 #ifdef HAVE_LDAP
1377 /*
1378  * If we're in the middle of an Easy Install, we might just be able to
1379  * auto-configure a standalone OpenLDAP server.
1380  */
1381 void contemplate_ldap(void) {
1382         char question[SIZ];
1383         char slapd_init_entry[SIZ];
1384         FILE *fp;
1385
1386         /* If conditions are not ideal, give up on this idea... */
1387         if (!have_sysv_init) return;
1388         if (using_web_installer == 0) return;
1389         if (getenv("LDAP_CONFIG") == NULL) return;
1390         if (getenv("SUPPORT") == NULL) return;
1391         if (getenv("SLAPD_BINARY") == NULL) return;
1392         if (getenv("CITADEL") == NULL) return;
1393
1394         /* And if inittab is already starting slapd, bail out... */
1395         locate_init_entry(slapd_init_entry, getenv("SLAPD_BINARY"));
1396         if (strlen(slapd_init_entry) > 0) {
1397                 important_message("Citadel Setup",
1398                         "You appear to already have a standalone LDAP "
1399                         "service\nconfigured for use with Citadel.  No "
1400                         "changes will be made.\n");
1401                 /* set_init_entry(slapd_init_entry, "off"); */
1402                 return;
1403         }
1404
1405         /* Generate a unique entry name for slapd if we don't have one. */
1406         else {
1407                 generate_entry_name(slapd_init_entry);
1408         }
1409
1410         /* Ask the user if it's ok to set up slapd automatically. */
1411         snprintf(question, sizeof question,
1412                 "\n"
1413                 "Do you want this computer configured to start a standalone\n"
1414                 "LDAP service automatically?  (If you answer yes, a new\n"
1415                 "slapd.conf will be written, and an /etc/inittab entry\n"
1416                 "pointing to %s will be added.)\n"
1417                 "\n",
1418                 getenv("SLAPD_BINARY")
1419         );
1420         if (yesno(question) == 0)
1421                 return;
1422
1423         strcpy(config.c_ldap_base_dn, "dc=example,dc=com");
1424         strprompt("Base DN",
1425                 "\n"
1426                 "Please enter the Base DN for your directory.  This will\n"
1427                 "generally be something based on the primary DNS domain in\n"
1428                 "which you receive mail, but it does not have to be.  Your\n"
1429                 "LDAP tree will be built using this Distinguished Name.\n"
1430                 "\n",
1431                 config.c_ldap_base_dn
1432         );
1433
1434         strcpy(config.c_ldap_host, "localhost");
1435         config.c_ldap_port = 389;
1436         sprintf(config.c_ldap_bind_dn, "cn=manager,%s", config.c_ldap_base_dn);
1437
1438         /*
1439          * Generate a bind password.  If you're some grey hat hacker who
1440          * is just dying to get some street cred on Bugtraq, and you think
1441          * this password generation scheme is too weak, please submit a patch
1442          * instead of just whining about it, ok?
1443          */
1444         sprintf(config.c_ldap_bind_pw, "%d%ld", getpid(), (long)time(NULL));
1445
1446         write_config_to_disk();
1447
1448         fp = fopen(getenv("LDAP_CONFIG"), "w");
1449         if (fp == NULL) {
1450                 sprintf(question, "\nCannot create %s:\n%s\n\n"
1451                                 "Citadel will still function, but you will "
1452                                 "not have an LDAP service.\n\n",
1453                                 getenv("LDAP_CONFIG"),
1454                                 strerror(errno)
1455                 );
1456                 important_message("Error", question);
1457                 return;
1458         }
1459
1460         fprintf(fp, "include    %s/citadel-openldap.schema\n",
1461                 getenv("CITADEL"));
1462         fprintf(fp, "pidfile    %s/openldap-data/slapd.pid\n",
1463                 getenv("CITADEL"));
1464         fprintf(fp, "argsfile   %s/openldap-data/slapd.args\n",
1465                 getenv("CITADEL"));
1466         fprintf(fp,     "allow          bind_v2\n"
1467                         "database       bdb\n"
1468                         "schemacheck    off\n"
1469         );
1470         fprintf(fp,     "suffix         \"%s\"\n", config.c_ldap_base_dn);
1471         fprintf(fp,     "rootdn         \"%s\"\n", config.c_ldap_bind_dn);
1472         fprintf(fp,     "rootpw         %s\n", config.c_ldap_bind_pw);
1473         fprintf(fp,     "directory      %s/openldap-data\n",
1474                 getenv("CITADEL"));
1475         fprintf(fp,     "index          objectClass     eq\n");
1476
1477         fclose(fp);
1478
1479         /* This is where our OpenLDAP server will keep its data. */
1480         mkdir("openldap-data", 0700);
1481
1482         /* Now write it out to /etc/inittab.
1483          * FIXME make it run as some non-root user.
1484          * The "-d 0" seems superfluous, but it's actually a way to make
1485          * slapd run in the foreground without spewing messages to the console.
1486          */
1487         fp = fopen("/etc/inittab", "a");
1488         if (fp == NULL) {
1489                 display_error(strerror(errno));
1490         } else {
1491                 fprintf(fp, "# Start the OpenLDAP server for Citadel...\n");
1492                 fprintf(fp, "%s:2345:respawn:%s -d 0 -f %s\n",
1493                         slapd_init_entry,
1494                         getenv("SLAPD_BINARY"),
1495                         getenv("LDAP_CONFIG")
1496                 );
1497                 fclose(fp);
1498         }
1499
1500 }
1501 #endif  /* HAVE_LDAP */