* Shortened the authmode question in setup because it was breaking the 'dialog' program
[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 "Specify which authentication mode you wish to use.\n"
141 "\n"
142 " 0. Self contained authentication\n"
143 " 1. Host system integrated authentication\n"
144 " 2. External LDAP - RFC 2307 compliant directory\n"
145 " 3. External LDAP - nonstandard MS Active Directory\n"
146 "\n"
147 "WARNING: do not change this setting once your system is installed.\n"
148 "\n"
149 "Answer \"0\" unless you completely understand this option.\n",
150
151 "Please enter the host name or IP address of your LDAP server.\n",
152
153 "Please enter the port number of the LDAP service (usually 389).\n",
154
155 "Please enter the Base DN to search for authentication\n"
156 "(for example: dc=example,dc=com)\n",
157
158 "Please enter the DN of an account to use for binding to the LDAP server\n"
159 "for performing queries.  The account does not require any other\n"
160 "privileges.  If your LDAP server allows anonymous queries, you can.\n"
161 "leave this blank.\n",
162
163 "If you entered a Bind DN in the previous question, you must now enter\n"
164 "the password associated with that account.  Otherwise, you can leave this\n"
165 "blank.\n"
166
167 };
168
169 struct config config;
170 int direction;
171
172
173 void cleanup(int exitcode)
174 {
175 //      printf("Exitcode: %d\n", exitcode);
176 //      cit_backtrace();
177         exit(exitcode);
178 }
179
180
181
182 void title(char *text)
183 {
184         if (setup_type == UI_TEXT) {
185                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
186         }
187 }
188
189
190
191 int yesno(char *question, int default_value)
192 {
193         int i = 0;
194         int answer = 0;
195         char buf[SIZ];
196
197         switch (setup_type) {
198
199         case UI_TEXT:
200                 do {
201                         printf("%s\nYes/No [%s] --> ",
202                                 question,
203                                 ( default_value ? "Yes" : "No" )
204                         );
205                         if (fgets(buf, sizeof buf, stdin))
206                         {
207                                 answer = tolower(buf[0]);
208                                 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
209                                         answer = default_value;
210                                 else if (answer == 'y')
211                                         answer = 1;
212                                 else if (answer == 'n')
213                                         answer = 0;
214                         }
215                 } while ((answer < 0) || (answer > 1));
216                 break;
217
218         case UI_DIALOG:
219                 sprintf(buf, "exec %s %s --yesno '%s' 15 75",
220                         getenv("CTDL_DIALOG"),
221                         ( default_value ? "" : "--defaultno" ),
222                         question);
223                 i = system(buf);
224                 if (i == 0) {
225                         answer = 1;
226                 }
227                 else {
228                         answer = 0;
229                 }
230                 break;
231         case UI_SILENT:
232                 break;
233
234         }
235         return (answer);
236 }
237
238
239 void important_message(char *title, char *msgtext)
240 {
241         char buf[SIZ];
242
243         switch (setup_type) {
244
245         case UI_TEXT:
246                 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");
247                 printf("       %s \n\n%s\n\n", title, msgtext);
248                 printf("Press return to continue...");
249                 if (fgets(buf, sizeof buf, stdin));
250                 break;
251
252         case UI_DIALOG:
253                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
254                         getenv("CTDL_DIALOG"),
255                         msgtext);
256                 system(buf);
257                 break;
258         case UI_SILENT:
259                 fprintf(stderr, "%s\n", msgtext);
260                 break;
261         }
262 }
263
264 void important_msgnum(int msgnum)
265 {
266         important_message("Important Message", setup_text[msgnum]);
267 }
268
269 void display_error(char *error_message)
270 {
271         important_message("Error", error_message);
272 }
273
274 void progress(char *text, long int curr, long int cmax)
275 {
276         static long dots_printed = 0L;
277         long a = 0;
278         static FILE *fp = NULL;
279         char buf[SIZ];
280
281         switch (setup_type) {
282
283         case UI_TEXT:
284                 if (curr == 0) {
285                         printf("%s\n", text);
286                         printf("..........................");
287                         printf("..........................");
288                         printf("..........................\r");
289                         fflush(stdout);
290                         dots_printed = 0;
291                 } else if (curr == cmax) {
292                         printf("\r%79s\n", "");
293                 } else {
294                         a = (curr * 100) / cmax;
295                         a = a * 78;
296                         a = a / 100;
297                         while (dots_printed < a) {
298                                 printf("*");
299                                 ++dots_printed;
300                                 fflush(stdout);
301                         }
302                 }
303                 break;
304
305         case UI_DIALOG:
306                 if (curr == 0) {
307                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
308                                 getenv("CTDL_DIALOG"),
309                                 text);
310                         fp = popen(buf, "w");
311                         if (fp != NULL) {
312                                 fprintf(fp, "0\n");
313                                 fflush(fp);
314                         }
315                 } 
316                 else if (curr == cmax) {
317                         if (fp != NULL) {
318                                 fprintf(fp, "100\n");
319                                 pclose(fp);
320                                 fp = NULL;
321                         }
322                 }
323                 else {
324                         a = (curr * 100) / cmax;
325                         if (fp != NULL) {
326                                 fprintf(fp, "%ld\n", a);
327                                 fflush(fp);
328                         }
329                 }
330                 break;
331         case UI_SILENT:
332                 break;
333
334         }
335 }
336
337
338
339 /*
340  * check_services_entry()  -- Make sure "citadel" is in /etc/services
341  *
342  */
343 void check_services_entry(void)
344 {
345         int i;
346         FILE *sfp;
347         char errmsg[256];
348
349         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
350                 for (i=0; i<=2; ++i) {
351                         progress("Adding service entry...", i, 2);
352                         if (i == 0) {
353                                 sfp = fopen("/etc/services", "a");
354                                 if (sfp == NULL) {
355                                         sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno));
356                                         display_error(errmsg);
357                                 } else {
358                                         fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
359                                         fclose(sfp);
360                                 }
361                         }
362                 }
363         }
364 }
365
366
367
368
369 /*
370  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
371  *
372  */
373 void delete_inittab_entry(void)
374 {
375         FILE *infp;
376         FILE *outfp;
377         char looking_for[256];
378         char buf[1024];
379         char outfilename[32];
380         int changes_made = 0;
381
382         /* Determine the fully qualified path name of citserver */
383         snprintf(looking_for, 
384                  sizeof looking_for,
385                  "%s/citserver", 
386                  ctdl_sbin_dir
387                 );
388
389         /* Now tweak /etc/inittab */
390         infp = fopen("/etc/inittab", "r");
391         if (infp == NULL) {
392
393                 /* If /etc/inittab does not exist, return quietly.
394                  * Not all host platforms have it.
395                  */
396                 if (errno == ENOENT) {
397                         return;
398                 }
399
400                 /* Other errors might mean something really did go wrong.
401                  */
402                 sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
403                 display_error(buf);
404                 return;
405         }
406
407         strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
408         outfp = fdopen(mkstemp(outfilename), "w+");
409         if (outfp == NULL) {
410                 sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
411                 display_error(buf);
412                 fclose(infp);
413                 return;
414         }
415
416         while (fgets(buf, sizeof buf, infp) != NULL) {
417                 if (strstr(buf, looking_for) != NULL) {
418                         fwrite("#", 1, 1, outfp);
419                         ++changes_made;
420                 }
421                 fwrite(buf, strlen(buf), 1, outfp);
422         }
423
424         fclose(infp);
425         fclose(outfp);
426
427         if (changes_made) {
428                 sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
429                 system(buf);
430                 system("/sbin/init q 2>/dev/null");
431         }
432         else {
433                 unlink(outfilename);
434         }
435 }
436
437
438 /*
439  * install_init_scripts()  -- Try to configure to start Citadel at boot
440  *
441  */
442 void install_init_scripts(void)
443 {
444         struct stat etcinitd;
445         FILE *fp;
446         char *initfile = "/etc/init.d/citadel";
447         char command[SIZ];
448
449         if ((stat("/etc/init.d/", &etcinitd) == -1) && 
450             (errno == ENOENT))
451         {
452                 if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
453                     (errno == ENOENT))
454                         initfile = CTDLDIR"/citadel.init";
455                 else
456                         initfile = "/etc/rc.d/init.d/citadel";
457         }
458
459         fp = fopen(initfile, "r");
460         if (fp != NULL) {
461                 if (yesno("Citadel already appears to be configured to start at boot.\n"
462                           "Would you like to keep your boot configuration as is?\n", 1) == 1) {
463                         return;
464                 }
465                 fclose(fp);
466                 
467         }
468
469         if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
470                 return;
471         }
472
473         fp = fopen(initfile, "w");
474         if (fp == NULL) {
475                 display_error("Cannot create /etc/init.d/citadel");
476                 return;
477         }
478
479         fprintf(fp,     "#!/bin/sh\n"
480                 "#\n"
481                 "# Init file for Citadel\n"
482                 "#\n"
483                 "# chkconfig: - 79 30\n"
484                 "# description: Citadel service\n"
485                 "# processname: citserver\n"
486                 "# pidfile: %s/citadel.pid\n\n"
487                 "# uncomment this to create coredumps as described in\n"
488                 "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
489                 "# ulimit -c unlimited\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 -lmail -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                 if (fgets(buf, sizeof buf, stdin)){
714                         buf[strlen(buf) - 1] = 0;
715                 }
716                 if (!IsEmptyStr(buf))
717                         strcpy(str, buf);
718                 break;
719
720         case UI_DIALOG:
721                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
722                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
723                         getenv("CTDL_DIALOG"),
724                         prompt_text,
725                         str,
726                         dialog_result);
727                 system(buf);
728                 fp = fopen(dialog_result, "r");
729                 if (fp != NULL) {
730                         if (fgets(str, sizeof buf, fp)) {
731                                 if (str[strlen(str)-1] == 10) {
732                                         str[strlen(str)-1] = 0;
733                                 }
734                         }
735                         fclose(fp);
736                         unlink(dialog_result);
737                 }
738                 break;
739         case UI_SILENT:
740                 break;
741         }
742 }
743
744 void set_bool_val(int msgpos, int *ip) {
745         title(setup_titles[msgpos]);
746         *ip = yesno(setup_text[msgpos], *ip);
747 }
748
749 void set_str_val(int msgpos, char *str) {
750         strprompt(setup_titles[msgpos], setup_text[msgpos], str);
751 }
752
753 void set_int_val(int msgpos, int *ip)
754 {
755         char buf[16];
756         snprintf(buf, sizeof buf, "%d", (int) *ip);
757         set_str_val(msgpos, buf);
758         *ip = atoi(buf);
759 }
760
761
762 void set_char_val(int msgpos, char *ip)
763 {
764         char buf[16];
765         snprintf(buf, sizeof buf, "%d", (int) *ip);
766         set_str_val(msgpos, buf);
767         *ip = (char) atoi(buf);
768 }
769
770
771 void set_long_val(int msgpos, long int *ip)
772 {
773         char buf[16];
774         snprintf(buf, sizeof buf, "%ld", *ip);
775         set_str_val(msgpos, buf);
776         *ip = atol(buf);
777 }
778
779
780 void edit_value(int curr)
781 {
782         int i;
783         struct passwd *pw;
784         char ctdluidname[256];
785
786         switch (curr) {
787
788         case 1:
789                 if (setup_type == UI_SILENT)
790                 {
791                         if (getenv("SYSADMIN_NAME")) {
792                                 strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
793                         }
794                 }
795                 else {
796                         set_str_val(curr, config.c_sysadm);
797                 }
798                 break;
799
800         case 2:
801                 if (setup_type == UI_SILENT)
802                 {
803                         if (getenv("SYSADMIN_PW")) {
804                                 strcpy(admin_pass, getenv("SYSADMIN_PW"));
805                         }
806                 }
807                 else {
808                         set_str_val(curr, admin_pass);
809                 }
810                 break;
811         
812         case 3:
813                 if (setup_type == UI_SILENT)
814                 {               
815                         if (getenv("CITADEL_UID")) {
816                                 config.c_ctdluid = atoi(getenv("CITADEL_UID"));
817                         }                                       
818                 }
819                 else
820                 {
821 #ifdef __CYGWIN__
822                         config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
823 #else
824                         i = config.c_ctdluid;
825                         pw = getpwuid(i);
826                         if (pw == NULL) {
827                                 set_int_val(curr, &i);
828                                 config.c_ctdluid = i;
829                         }
830                         else {
831                                 strcpy(ctdluidname, pw->pw_name);
832                                 set_str_val(curr, ctdluidname);
833                                 pw = getpwnam(ctdluidname);
834                                 if (pw != NULL) {
835                                         config.c_ctdluid = pw->pw_uid;
836                                 }
837                                 else if (atoi(ctdluidname) > 0) {
838                                         config.c_ctdluid = atoi(ctdluidname);
839                                 }
840                         }
841 #endif
842                 }
843                 break;
844
845         case 4:
846                 if (setup_type == UI_SILENT)
847                 {
848                         if (getenv("IP_ADDR")) {
849                                 strcpy(config.c_ip_addr, getenv("IP_ADDR"));
850                         }
851                 }
852                 else {
853                         set_str_val(curr, config.c_ip_addr);
854                 }
855                 break;
856
857         case 5:
858                 if (setup_type == UI_SILENT)
859                 {
860                         if (getenv("CITADEL_PORT")) {
861                                 config.c_port_number = atoi(getenv("CITADEL_PORT"));
862                         }
863                 }
864                 else
865                 {
866                         set_int_val(curr, &config.c_port_number);
867                 }
868                 break;
869
870         case 6:
871                 if (setup_type == UI_SILENT)
872                 {
873                         if (getenv("ENABLE_UNIX_AUTH")) {
874                                 if (!strcasecmp(getenv("ENABLE_UNIX_AUTH"), "yes")) {
875                                         config.c_auth_mode = AUTHMODE_HOST;
876                                 }
877                                 else {
878                                         config.c_auth_mode = AUTHMODE_NATIVE;
879                                 }
880                         }
881                 }
882                 else {
883                         set_int_val(curr, &config.c_auth_mode);
884                 }
885                 break;
886
887         case 7:
888                 if (setup_type == UI_SILENT)
889                 {
890                         if (getenv("LDAP_HOST")) {
891                                 strcpy(config.c_ldap_host, getenv("LDAP_HOST"));
892                         }
893                 }
894                 else
895                 {
896                         set_str_val(curr, config.c_ldap_host);
897                 }
898                 break;
899
900         case 8:
901                 if (setup_type == UI_SILENT)
902                 {
903                         if (getenv("LDAP_PORT")) {
904                                 config.c_ldap_port = atoi(getenv("LDAP_PORT"));
905                         }
906                 }
907                 else
908                 {
909                         set_int_val(curr, &config.c_ldap_port);
910                 }
911                 break;
912
913         case 9:
914                 if (setup_type == UI_SILENT)
915                 {
916                         if (getenv("LDAP_BASE_DN")) {
917                                 strcpy(config.c_ldap_base_dn, getenv("LDAP_BASE_DN"));
918                         }
919                 }
920                 else
921                 {
922                         set_str_val(curr, config.c_ldap_base_dn);
923                 }
924                 break;
925
926         case 10:
927                 if (setup_type == UI_SILENT)
928                 {
929                         if (getenv("LDAP_BIND_DN")) {
930                                 strcpy(config.c_ldap_bind_dn, getenv("LDAP_BIND_DN"));
931                         }
932                 }
933                 else
934                 {
935                         set_str_val(curr, config.c_ldap_bind_dn);
936                 }
937                 break;
938
939         case 11:
940                 if (setup_type == UI_SILENT)
941                 {
942                         if (getenv("LDAP_BIND_PW")) {
943                                 strcpy(config.c_ldap_bind_pw, getenv("LDAP_BIND_PW"));
944                         }
945                 }
946                 else
947                 {
948                         set_str_val(curr, config.c_ldap_bind_pw);
949                 }
950                 break;
951
952         }
953
954 }
955
956 /*
957  * (re-)write the config data to disk
958  */
959 void write_config_to_disk(void)
960 {
961         FILE *fp;
962         int fd;
963
964         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
965                 display_error("setup: cannot open citadel.config");
966                 cleanup(1);
967         }
968         fp = fdopen(fd, "wb");
969         if (fp == NULL) {
970                 display_error("setup: cannot open citadel.config");
971                 cleanup(1);
972         }
973         fwrite((char *) &config, sizeof(struct config), 1, fp);
974         fclose(fp);
975 }
976
977
978
979
980 /*
981  * Figure out what type of user interface we're going to use
982  */
983 int discover_ui(void)
984 {
985
986         /* Use "dialog" if we have it */
987         if (getenv("CTDL_DIALOG") != NULL) {
988                 return UI_DIALOG;
989         }
990                 
991         return UI_TEXT;
992 }
993
994
995
996
997
998 /*
999  * Strip "db" entries out of /etc/nsswitch.conf
1000  */
1001 void fixnss(void) {
1002         FILE *fp_read;
1003         int fd_write;
1004         char buf[256];
1005         char buf_nc[256];
1006         char question[512];
1007         int i;
1008         int changed = 0;
1009         int file_changed = 0;
1010         char new_filename[64];
1011
1012         fp_read = fopen(NSSCONF, "r");
1013         if (fp_read == NULL) {
1014                 return;
1015         }
1016
1017         strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
1018         fd_write = mkstemp(new_filename);
1019         if (fd_write < 0) {
1020                 fclose(fp_read);
1021                 return;
1022         }
1023
1024         while (fgets(buf, sizeof buf, fp_read) != NULL) {
1025                 changed = 0;
1026                 strcpy(buf_nc, buf);
1027                 for (i=0; i<strlen(buf_nc); ++i) {
1028                         if (buf_nc[i] == '#') {
1029                                 buf_nc[i] = 0;
1030                         }
1031                 }
1032                 for (i=0; i<strlen(buf_nc); ++i) {
1033                         if (!strncasecmp(&buf_nc[i], "db", 2)) {
1034                                 if (i > 0) {
1035                                         if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
1036                                                 changed = 1;
1037                                                 file_changed = 1;
1038                                                 strcpy(&buf_nc[i], &buf_nc[i+2]);
1039                                                 strcpy(&buf[i], &buf[i+2]);
1040                                                 if (buf[i]==32) {
1041                                                         strcpy(&buf_nc[i], &buf_nc[i+1]);
1042                                                         strcpy(&buf[i], &buf[i+1]);
1043                                                 }
1044                                         }
1045                                 }
1046                         }
1047                 }
1048                 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
1049                         fclose(fp_read);
1050                         close(fd_write);
1051                         unlink(new_filename);
1052                         return;
1053                 }
1054         }
1055
1056         fclose(fp_read);
1057         
1058         if (!file_changed) {
1059                 unlink(new_filename);
1060                 return;
1061         }
1062
1063         snprintf(question, sizeof question,
1064                 "\n"
1065                 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
1066                 "one or more services.  This is not necessary on most systems,\n"
1067                 "and it is known to crash the Citadel server when delivering\n"
1068                 "mail to the Internet.\n"
1069                 "\n"
1070                 "Do you want this module to be automatically disabled?\n"
1071                 "\n"
1072         );
1073
1074         if (yesno(question, 1)) {
1075                 sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
1076                 system(buf);
1077                 chmod(NSSCONF, 0644);
1078         }
1079         unlink(new_filename);
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089 int main(int argc, char *argv[])
1090 {
1091         int a;
1092         int curr; 
1093         char aaa[128];
1094         FILE *fp;
1095         int old_setup_level = 0;
1096         int info_only = 0;
1097         struct utsname my_utsname;
1098         struct passwd *pw;
1099         struct hostent *he;
1100         gid_t gid;
1101         int relh=0;
1102         int home=0;
1103         char relhome[PATH_MAX]="";
1104         char ctdldir[PATH_MAX]=CTDLDIR;
1105         
1106         /* set an invalid setup type */
1107         setup_type = (-1);
1108
1109         /* Check to see if we're running the web installer */
1110         if (getenv("CITADEL_INSTALLER") != NULL) {
1111                 using_web_installer = 1;
1112         }
1113
1114         /* parse command line args */
1115         for (a = 0; a < argc; ++a) {
1116                 if (!strncmp(argv[a], "-u", 2)) {
1117                         strcpy(aaa, argv[a]);
1118                         strcpy(aaa, &aaa[2]);
1119                         setup_type = atoi(aaa);
1120                 }
1121                 else if (!strcmp(argv[a], "-i")) {
1122                         info_only = 1;
1123                 }
1124                 else if (!strcmp(argv[a], "-q")) {
1125                         setup_type = UI_SILENT;
1126                 }
1127                 else if (!strncmp(argv[a], "-h", 2)) {
1128                         relh=argv[a][2]!='/';
1129                         if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
1130                                                                    sizeof ctdl_home_directory);
1131                         else
1132                                 safestrncpy(relhome, &argv[a][2],
1133                                                         sizeof relhome);
1134                         home = 1;
1135                 }
1136
1137         }
1138
1139         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1140
1141         /* If a setup type was not specified, try to determine automatically
1142          * the best one to use out of all available types.
1143          */
1144         if (setup_type < 0) {
1145                 setup_type = discover_ui();
1146         }
1147         if (info_only == 1) {
1148                 important_message("Citadel Setup", CITADEL);
1149                 cleanup(0);
1150         }
1151
1152         /* Get started in a valid setup directory. */
1153         strcpy(setup_directory, ctdl_run_dir);
1154         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
1155                 strcpy(setup_directory, getenv("CITADEL"));
1156         }
1157         else {
1158                 set_str_val(0, setup_directory);
1159         }
1160
1161         enable_home = ( relh | home );
1162
1163         if (chdir(setup_directory) != 0) {
1164                 char errmsg[SIZ];
1165                 sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
1166                 
1167                 important_message("Citadel Setup", errmsg);
1168                 cleanup(errno);
1169         }
1170
1171         /* Determine our host name, in case we need to use it as a default */
1172         uname(&my_utsname);
1173
1174         /* Try to stop Citadel if we can */
1175         if (!access("/etc/init.d/citadel", X_OK)) {
1176                 system("/etc/init.d/citadel stop");
1177         }
1178
1179         /* Make sure Citadel is not running. */
1180         if (test_server(setup_directory, relhome, enable_home) == 0) {
1181                 important_message("Citadel Setup",
1182                         "The Citadel service is still running.\n"
1183                         "Please stop the service manually and run "
1184                         "setup again.");
1185                 cleanup(1);
1186         }
1187
1188         /* Now begin. */
1189         switch (setup_type) {
1190
1191         case UI_TEXT:
1192                 printf("\n\n\n"
1193                         "              *** Citadel setup program ***\n\n");
1194                 break;
1195
1196         }
1197
1198         /*
1199          * What we're going to try to do here is append a whole bunch of
1200          * nulls to the citadel.config file, so we can keep the old config
1201          * values if they exist, but if the file is missing or from an
1202          * earlier version with a shorter config structure, when setup tries
1203          * to read the old config parameters, they'll all come up zero.
1204          * The length of the config file will be set to what it's supposed
1205          * to be when we rewrite it, because we replace the old file with a
1206          * completely new copy.
1207          */
1208         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1209                       S_IRUSR | S_IWUSR)) == -1) {
1210                 display_error("setup: cannot append citadel.config");
1211                 cleanup(errno);
1212         }
1213         fp = fdopen(a, "ab");
1214         if (fp == NULL) {
1215                 display_error("setup: cannot append citadel.config");
1216                 cleanup(errno);
1217         }
1218         for (a = 0; a < sizeof(struct config); ++a)
1219                 putc(0, fp);
1220         fclose(fp);
1221
1222         /* now we re-open it, and read the old or blank configuration */
1223         fp = fopen(file_citadel_config, "rb");
1224         if (fp == NULL) {
1225                 display_error("setup: cannot open citadel.config");
1226                 cleanup(errno);
1227         }
1228         fread((char *) &config, sizeof(struct config), 1, fp);
1229         fclose(fp);
1230
1231         /* set some sample/default values in place of blanks... */
1232         if (IsEmptyStr(config.c_nodename))
1233                 safestrncpy(config.c_nodename, my_utsname.nodename,
1234                             sizeof config.c_nodename);
1235         strtok(config.c_nodename, ".");
1236         if (IsEmptyStr(config.c_fqdn) ) {
1237                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
1238                         safestrncpy(config.c_fqdn, he->h_name,
1239                                     sizeof config.c_fqdn);
1240                 else
1241                         safestrncpy(config.c_fqdn, my_utsname.nodename,
1242                                     sizeof config.c_fqdn);
1243         }
1244         if (IsEmptyStr(config.c_humannode))
1245                 strcpy(config.c_humannode, "My System");
1246         if (IsEmptyStr(config.c_phonenum))
1247                 strcpy(config.c_phonenum, "US 800 555 1212");
1248         if (config.c_initax == 0) {
1249                 config.c_initax = 4;
1250         }
1251         if (IsEmptyStr(config.c_moreprompt))
1252                 strcpy(config.c_moreprompt, "<more>");
1253         if (IsEmptyStr(config.c_twitroom))
1254                 strcpy(config.c_twitroom, "Trashcan");
1255         if (IsEmptyStr(config.c_baseroom))
1256                 strcpy(config.c_baseroom, BASEROOM);
1257         if (IsEmptyStr(config.c_aideroom))
1258                 strcpy(config.c_aideroom, "Aide");
1259         if (config.c_port_number == 0) {
1260                 config.c_port_number = 504;
1261         }
1262         if (config.c_sleeping == 0) {
1263                 config.c_sleeping = 900;
1264         }
1265         if (config.c_ctdluid == 0) {
1266                 pw = getpwnam("citadel");
1267                 if (pw != NULL)
1268                         config.c_ctdluid = pw->pw_uid;
1269         }
1270         if (config.c_ctdluid == 0) {
1271                 pw = getpwnam("bbs");
1272                 if (pw != NULL)
1273                         config.c_ctdluid = pw->pw_uid;
1274         }
1275         if (config.c_ctdluid == 0) {
1276                 pw = getpwnam("guest");
1277                 if (pw != NULL)
1278                         config.c_ctdluid = pw->pw_uid;
1279         }
1280         if (config.c_createax == 0) {
1281                 config.c_createax = 3;
1282         }
1283         /*
1284          * Negative values for maxsessions are not allowed.
1285          */
1286         if (config.c_maxsessions < 0) {
1287                 config.c_maxsessions = 0;
1288         }
1289         /* We need a system default message expiry policy, because this is
1290          * the top level and there's no 'higher' policy to fall back on.
1291          * By default, do not expire messages at all.
1292          */
1293         if (config.c_ep.expire_mode == 0) {
1294                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1295                 config.c_ep.expire_value = 0;
1296         }
1297
1298         /*
1299          * Default port numbers for various services
1300          */
1301         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1302         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1303         if (config.c_imap_port == 0) config.c_imap_port = 143;
1304         if (config.c_msa_port == 0) config.c_msa_port = 587;
1305         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1306         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1307         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1308         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1309         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1310         if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
1311         if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
1312
1313         /* Go through a series of dialogs prompting for config info */
1314         for (curr = 1; curr <= MAXSETUP; ++curr) {
1315                 edit_value(curr);
1316                 if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
1317                         curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
1318                 }
1319         }
1320
1321 /***** begin version update section ***** */
1322         /* take care of any updating that is necessary */
1323
1324         old_setup_level = config.c_setup_level;
1325
1326         if (old_setup_level == 0) {
1327                 goto NEW_INST;
1328         }
1329
1330         if (old_setup_level < 555) {
1331                 important_message("Citadel Setup",
1332                                   "This Citadel installation is too old "
1333                                   "to be upgraded.");
1334                 cleanup(1);
1335         }
1336         write_config_to_disk();
1337
1338         old_setup_level = config.c_setup_level;
1339
1340         /* end of version update section */
1341
1342 NEW_INST:
1343         config.c_setup_level = REV_LEVEL;
1344
1345 /******************************************/
1346
1347         write_config_to_disk();
1348
1349         mkdir(ctdl_info_dir, 0700);
1350         chmod(ctdl_info_dir, 0700);
1351         chown(ctdl_info_dir, config.c_ctdluid, -1);
1352
1353         mkdir(ctdl_bio_dir, 0700);
1354         chmod(ctdl_bio_dir, 0700);
1355         chown(ctdl_bio_dir, config.c_ctdluid, -1);
1356
1357         mkdir(ctdl_usrpic_dir, 0700);
1358         chmod(ctdl_usrpic_dir, 0700);
1359         chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
1360
1361         mkdir(ctdl_message_dir, 0700);
1362         chmod(ctdl_message_dir, 0700);
1363         chown(ctdl_message_dir, config.c_ctdluid, -1);
1364
1365         mkdir(ctdl_hlp_dir, 0700);
1366         chmod(ctdl_hlp_dir, 0700);
1367         chown(ctdl_hlp_dir, config.c_ctdluid, -1);
1368
1369         mkdir(ctdl_image_dir, 0700);
1370         chmod(ctdl_image_dir, 0700);
1371         chown(ctdl_image_dir, config.c_ctdluid, -1);
1372
1373         mkdir(ctdl_bb_dir, 0700);
1374         chmod(ctdl_bb_dir, 0700);
1375         chown(ctdl_bb_dir, config.c_ctdluid, -1);
1376
1377         mkdir(ctdl_file_dir, 0700);
1378         chmod(ctdl_file_dir, 0700);
1379         chown(ctdl_file_dir, config.c_ctdluid, -1);
1380
1381         mkdir(ctdl_netcfg_dir, 0700);
1382         chmod(ctdl_netcfg_dir, 0700);
1383         chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
1384
1385         /* Delete files and directories used by older Citadel versions */
1386         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1387         unlink("citadel.log");
1388         unlink("weekly");
1389
1390         if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
1391             (setup_type != UI_SILENT))
1392                 check_services_entry(); /* Check /etc/services */
1393 #ifndef __CYGWIN__
1394         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1395         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1396
1397         if ((getenv("ACT_AS_MTA") == NULL) || 
1398             (getenv("ACT_AS_MTA") &&
1399              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
1400                 /* Offer to disable other MTA's on the system. */
1401                 disable_other_mta("courier-authdaemon");
1402                 disable_other_mta("courier-imap");
1403                 disable_other_mta("courier-imap-ssl");
1404                 disable_other_mta("courier-pop");
1405                 disable_other_mta("courier-pop3");
1406                 disable_other_mta("courier-pop3d");
1407                 disable_other_mta("cyrmaster");
1408                 disable_other_mta("cyrus");
1409                 disable_other_mta("dovecot");
1410                 disable_other_mta("exim");
1411                 disable_other_mta("exim4");
1412                 disable_other_mta("imapd");
1413                 disable_other_mta("mta");
1414                 disable_other_mta("pop3d");
1415                 disable_other_mta("popd");
1416                 disable_other_mta("postfix");
1417                 disable_other_mta("qmail");
1418                 disable_other_mta("saslauthd");
1419                 disable_other_mta("sendmail");
1420                 disable_other_mta("vmailmgrd");
1421         }
1422 #endif
1423
1424         /* Check for the 'db' nss and offer to disable it */
1425         fixnss();
1426
1427         if ((pw = getpwuid(config.c_ctdluid)) == NULL)
1428                 gid = getgid();
1429         else
1430                 gid = pw->pw_gid;
1431
1432         progress("Setting file permissions", 0, 3);
1433         chown(ctdl_run_dir, config.c_ctdluid, gid);
1434         progress("Setting file permissions", 1, 3);
1435         chown(file_citadel_config, config.c_ctdluid, gid);
1436         progress("Setting file permissions", 2, 3);
1437         chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1438         progress("Setting file permissions", 3, 3);
1439
1440         /* 
1441          * If we're running on SysV, install init scripts.
1442          */
1443         if (!access("/var/run", W_OK)) {
1444
1445                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1446                         install_init_scripts();
1447                 }
1448
1449                 if (!access("/etc/init.d/citadel", X_OK)) {
1450                         system("/etc/init.d/citadel start");
1451                         sleep(3);
1452                 }
1453
1454                 if (test_server(setup_directory, relhome, enable_home) == 0) {
1455                         char buf[SIZ];
1456                         int found_it = 0;
1457
1458                         snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
1459                                   ctdl_sbin_dir, config.c_sysadm, admin_pass);
1460                         fp = popen(admin_cmd, "r");
1461                         if (fp != NULL) {
1462                                 while (fgets(buf, sizeof buf, fp) != NULL) 
1463                                 {
1464                                         if ((atol(buf) == 574) || (atol(buf) == 200))
1465                                                 ++found_it;
1466                                 }
1467                                 pclose(fp);
1468                         }
1469                 
1470                         if (found_it == 0)
1471                                 important_message("Error","Setup failed to create your admin user");
1472
1473                         if (setup_type != UI_SILENT)
1474                                 important_message("Setup finished",
1475                                                   "Setup of the Citadel server is complete.\n"
1476                                                   "If you will be using WebCit, please run its\n"
1477                                                   "setup program now; otherwise, run './citadel'\n"
1478                                                   "to log in.\n");
1479                 }
1480                 else {
1481                         important_message("Setup failed",
1482                                 "Setup is finished, but the Citadel server failed to start.\n"
1483                                 "Go back and check your configuration.\n"
1484                         );
1485                 }
1486
1487         }
1488
1489         else {
1490                 important_message("Setup finished",
1491                         "Setup is finished.  You may now start the server.");
1492         }
1493
1494         cleanup(0);
1495         return 0;
1496 }
1497
1498