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