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