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