* move stuff cleaning up old, now unneeded files into its own function
[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(const 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 const char *other_mtas[] = {
709         "courier-authdaemon",
710         "courier-imap",
711         "courier-imap-ssl",
712         "courier-pop",
713         "courier-pop3",
714         "courier-pop3d",
715         "cyrmaster",
716         "cyrus",
717         "dovecot",
718         "exim",
719         "exim4",
720         "imapd",
721         "mta",
722         "pop3d",
723         "popd",
724         "postfix",
725         "qmail",
726         "saslauthd",
727         "sendmail",
728         "vmailmgrd",
729         ""
730 };
731
732 void disable_other_mtas(void)
733 {
734         int i = 0;
735         if ((getenv("ACT_AS_MTA") == NULL) || 
736             (getenv("ACT_AS_MTA") &&
737              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
738                 /* Offer to disable other MTA's on the system. */
739                 while (!IsEmptyStr(other_mtas[i]))
740                 {
741                         disable_other_mta(other_mtas[i]);
742                         i++;
743                 }
744         }
745 }
746
747 /* 
748  * Check to see if our server really works.  Returns 0 on success.
749  */
750 int test_server(char *setup_directory, char *relhomestr, int relhome) {
751         char cmd[256];
752         char cookie[256];
753         FILE *fp;
754         char buf[4096];
755         int found_it = 0;
756
757         /* Generate a silly little cookie.  We're going to write it out
758          * to the server and try to get it back.  The cookie does not
759          * have to be secret ... just unique.
760          */
761         sprintf(cookie, "--test--%d--", getpid());
762
763         if (relhome)
764                 sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
765                         ctdl_sbin_dir,
766                         relhomestr,
767                         cookie);
768         else
769                 sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
770                         ctdl_sbin_dir,
771                         cookie);
772
773         fp = popen(cmd, "r");
774         if (fp == NULL) return(errno);
775
776         while (fgets(buf, sizeof buf, fp) != NULL) {
777                 if ( (buf[0]=='2')
778                      && (strstr(buf, cookie) != NULL) ) {
779                         ++found_it;
780                 }
781         }
782         pclose(fp);
783
784         if (found_it) {
785                 return(0);
786         }
787         return(-1);
788 }
789
790 void strprompt(const char *prompt_title, char *prompt_text, char *Target, char *DefValue)
791 {
792         char buf[SIZ] = "";
793         char setupmsg[SIZ];
794         char dialog_result[PATH_MAX];
795         FILE *fp = NULL;
796         int rv;
797
798         strcpy(setupmsg, "");
799
800         switch (setup_type) {
801         case UI_TEXT:
802                 title(prompt_title);
803                 printf("\n%s\n", prompt_text);
804                 printf("This is currently set to:\n%s\n", Target);
805                 printf("Enter new value or press return to leave unchanged:\n");
806                 if (fgets(buf, sizeof buf, stdin)){
807                         buf[strlen(buf) - 1] = 0;
808                 }
809                 if (!IsEmptyStr(buf))
810                         strcpy(Target, buf);
811                 break;
812
813         case UI_DIALOG:
814                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
815                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
816                         getenv("CTDL_DIALOG"),
817                         prompt_text,
818                         Target,
819                         dialog_result);
820                 rv = system(buf);
821                 fp = fopen(dialog_result, "r");
822                 if (fp != NULL) {
823                         if (fgets(Target, sizeof buf, fp)) {
824                                 if (Target[strlen(Target)-1] == 10) {
825                                         Target[strlen(Target)-1] = 0;
826                                 }
827                         }
828                         fclose(fp);
829                         unlink(dialog_result);
830                 }
831                 break;
832         case UI_SILENT:
833                 strcpy(Target, DefValue);
834                 break;
835         }
836 }
837
838 void set_bool_val(int msgpos, int *ip, char *DefValue) {
839         title(setup_titles[msgpos]);
840         *ip = yesno(setup_text[msgpos], *ip);
841 }
842
843 void set_str_val(int msgpos, char *Target, char *DefValue) {
844         strprompt(setup_titles[msgpos], 
845                   setup_text[msgpos], 
846                   Target, 
847                   DefValue);
848 }
849
850 void set_int_val(int msgpos, int *ip, char *DefValue)
851 {
852         char buf[16];
853         snprintf(buf, sizeof buf, "%d", (int) *ip);
854         set_str_val(msgpos, buf, DefValue);
855         *ip = atoi(buf);
856 }
857
858
859 void set_char_val(int msgpos, char *ip, char *DefValue)
860 {
861         char buf[16];
862         snprintf(buf, sizeof buf, "%d", (int) *ip);
863         set_str_val(msgpos, buf, DefValue);
864         *ip = (char) atoi(buf);
865 }
866
867
868 void set_long_val(int msgpos, long int *ip, char *DefValue)
869 {
870         char buf[16];
871         snprintf(buf, sizeof buf, "%ld", *ip);
872         set_str_val(msgpos, buf, DefValue);
873         *ip = atol(buf);
874 }
875
876
877 void edit_value(int curr)
878 {
879         int i;
880         struct passwd *pw;
881         char ctdluidname[256];
882         char *Value = NULL;
883
884         if (setup_type == UI_SILENT)
885         {
886                 Value = getenv(EnvNames[curr]);
887         }
888
889
890         switch (curr) {
891
892         case eSysAdminName:
893                 set_str_val(curr, config.c_sysadm, Value);
894                 break;
895
896         case eSysAdminPW:
897                 set_str_val(curr, admin_pass, Value);
898                 break;
899         
900         case eUID:
901                 if (setup_type == UI_SILENT)
902                 {               
903                         if (Value) {
904                                 config.c_ctdluid = atoi(Value);
905                         }                                       
906                 }
907                 else
908                 {
909 #ifdef __CYGWIN__
910                         config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
911 #else
912                         i = config.c_ctdluid;
913                         pw = getpwuid(i);
914                         if (pw == NULL) {
915                                 set_int_val(curr, &i, Value);
916                                 config.c_ctdluid = i;
917                         }
918                         else {
919                                 strcpy(ctdluidname, pw->pw_name);
920                                 set_str_val(curr, ctdluidname, Value);
921                                 pw = getpwnam(ctdluidname);
922                                 if (pw != NULL) {
923                                         config.c_ctdluid = pw->pw_uid;
924                                 }
925                                 else if (atoi(ctdluidname) > 0) {
926                                         config.c_ctdluid = atoi(ctdluidname);
927                                 }
928                         }
929 #endif
930                 }
931                 break;
932
933         case eIP_ADDR:
934                 set_str_val(curr, config.c_ip_addr, Value);
935                 break;
936
937         case eCTDL_Port:
938                 set_int_val(curr, &config.c_port_number, Value);
939                 break;
940
941         case eAuthType:
942                 if (setup_type == UI_SILENT)
943                 {
944                         const char *auth;
945                         config.c_auth_mode = AUTHMODE_NATIVE;
946                         auth = Value;
947                         if (auth != NULL)
948                         {
949                                 if ((strcasecmp(auth, "yes") == 0) ||
950                                     (strcasecmp(auth, "host") == 0))
951                                 {
952                                         config.c_auth_mode = AUTHMODE_HOST;
953                                 }
954                                 else if (strcasecmp(auth, "ldap") == 0){
955                                         config.c_auth_mode = AUTHMODE_LDAP;
956                                 }
957                                 else if ((strcasecmp(auth, "ldap_ad") == 0) ||
958                                          (strcasecmp(auth, "active directory") == 0)){
959                                         config.c_auth_mode = AUTHMODE_LDAP_AD;
960                                 }
961                         }
962                 }
963                 else {
964                         set_int_val(curr, &config.c_auth_mode, Value);
965                 }
966                 break;
967
968         case eLDAP_Host:
969                 set_str_val(curr, config.c_ldap_host, Value);
970                 break;
971
972         case eLDAP_Port:
973                 if (config.c_ldap_port == 0) {
974                         config.c_ldap_port = 389;
975                 }
976                 set_int_val(curr, &config.c_ldap_port, Value);
977                 break;
978
979         case eLDAP_Base_DN:
980                 set_str_val(curr, config.c_ldap_base_dn, Value);
981                 break;
982
983         case eLDAP_Bind_DN:
984                 set_str_val(curr, config.c_ldap_bind_dn, Value);
985                 break;
986
987         case eLDAP_Bind_PW:
988                 set_str_val(curr, config.c_ldap_bind_pw, Value);
989                 break;
990
991         }
992 }
993
994 /*
995  * (re-)write the config data to disk
996  */
997 void write_config_to_disk(void)
998 {
999         FILE *fp;
1000         int fd;
1001         int rv;
1002
1003         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
1004                 display_error("setup: cannot open citadel.config");
1005                 cleanup(1);
1006         }
1007         fp = fdopen(fd, "wb");
1008         if (fp == NULL) {
1009                 display_error("setup: cannot open citadel.config");
1010                 cleanup(1);
1011         }
1012         rv = fwrite((char *) &config, sizeof(struct config), 1, fp);
1013         fclose(fp);
1014 }
1015
1016
1017
1018
1019 /*
1020  * Figure out what type of user interface we're going to use
1021  */
1022 int discover_ui(void)
1023 {
1024
1025         /* Use "dialog" if we have it */
1026         if (getenv("CTDL_DIALOG") != NULL) {
1027                 return UI_DIALOG;
1028         }
1029                 
1030         return UI_TEXT;
1031 }
1032
1033
1034
1035 void migrate_old_installs(void)
1036 {
1037         int rv;
1038         rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1039         unlink("citadel.log");
1040         unlink("weekly");
1041 }
1042
1043
1044 /*
1045  * Strip "db" entries out of /etc/nsswitch.conf
1046  */
1047 void fixnss(void) {
1048         FILE *fp_read;
1049         int fd_write;
1050         char buf[256];
1051         char buf_nc[256];
1052         char question[512];
1053         int i;
1054         int changed = 0;
1055         int file_changed = 0;
1056         char new_filename[64];
1057         int rv;
1058
1059         fp_read = fopen(NSSCONF, "r");
1060         if (fp_read == NULL) {
1061                 return;
1062         }
1063
1064         strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
1065         fd_write = mkstemp(new_filename);
1066         if (fd_write < 0) {
1067                 fclose(fp_read);
1068                 return;
1069         }
1070
1071         while (fgets(buf, sizeof buf, fp_read) != NULL) {
1072                 changed = 0;
1073                 strcpy(buf_nc, buf);
1074                 for (i=0; i<strlen(buf_nc); ++i) {
1075                         if (buf_nc[i] == '#') {
1076                                 buf_nc[i] = 0;
1077                         }
1078                 }
1079                 for (i=0; i<strlen(buf_nc); ++i) {
1080                         if (!strncasecmp(&buf_nc[i], "db", 2)) {
1081                                 if (i > 0) {
1082                                         if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
1083                                                 changed = 1;
1084                                                 file_changed = 1;
1085                                                 strcpy(&buf_nc[i], &buf_nc[i+2]);
1086                                                 strcpy(&buf[i], &buf[i+2]);
1087                                                 if (buf[i]==32) {
1088                                                         strcpy(&buf_nc[i], &buf_nc[i+1]);
1089                                                         strcpy(&buf[i], &buf[i+1]);
1090                                                 }
1091                                         }
1092                                 }
1093                         }
1094                 }
1095                 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
1096                         fclose(fp_read);
1097                         close(fd_write);
1098                         unlink(new_filename);
1099                         return;
1100                 }
1101         }
1102
1103         fclose(fp_read);
1104         
1105         if (!file_changed) {
1106                 unlink(new_filename);
1107                 return;
1108         }
1109
1110         snprintf(question, sizeof question,
1111                 "\n"
1112                 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
1113                 "one or more services.  This is not necessary on most systems,\n"
1114                 "and it is known to crash the Citadel server when delivering\n"
1115                 "mail to the Internet.\n"
1116                 "\n"
1117                 "Do you want this module to be automatically disabled?\n"
1118                 "\n"
1119         );
1120
1121         if (yesno(question, 1)) {
1122                 sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
1123                 rv = system(buf);
1124                 chmod(NSSCONF, 0644);
1125         }
1126         unlink(new_filename);
1127 }
1128
1129
1130
1131 void set_default_values(void)
1132 {
1133         struct passwd *pw;
1134         struct utsname my_utsname;
1135         struct hostent *he;
1136
1137         /* Determine our host name, in case we need to use it as a default */
1138         uname(&my_utsname);
1139
1140         /* set some sample/default values in place of blanks... */
1141         if (IsEmptyStr(config.c_nodename))
1142                 safestrncpy(config.c_nodename, my_utsname.nodename,
1143                             sizeof config.c_nodename);
1144         strtok(config.c_nodename, ".");
1145         if (IsEmptyStr(config.c_fqdn) ) {
1146                 if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
1147                         safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
1148                 } else {
1149                         safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
1150                 }
1151         }
1152         if (IsEmptyStr(config.c_humannode)) {
1153                 strcpy(config.c_humannode, "My System");
1154         }
1155         if (IsEmptyStr(config.c_phonenum)) {
1156                 strcpy(config.c_phonenum, "US 800 555 1212");
1157         }
1158         if (config.c_initax == 0) {
1159                 config.c_initax = 4;
1160         }
1161         if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, "<more>");
1162         if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan");
1163         if (IsEmptyStr(config.c_baseroom)) strcpy(config.c_baseroom, BASEROOM);
1164         if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide");
1165         if (config.c_port_number == 0) {
1166                 config.c_port_number = 504;
1167         }
1168         if (config.c_sleeping == 0) {
1169                 config.c_sleeping = 900;
1170         }
1171         if (config.c_ctdluid == 0) {
1172                 pw = getpwnam("citadel");
1173                 if (pw != NULL) {
1174                         config.c_ctdluid = pw->pw_uid;
1175                 }
1176         }
1177         if (config.c_ctdluid == 0) {
1178                 pw = getpwnam("bbs");
1179                 if (pw != NULL) {
1180                         config.c_ctdluid = pw->pw_uid;
1181                 }
1182         }
1183         if (config.c_ctdluid == 0) {
1184                 pw = getpwnam("guest");
1185                 if (pw != NULL) {
1186                         config.c_ctdluid = pw->pw_uid;
1187                 }
1188         }
1189         if (config.c_createax == 0) {
1190                 config.c_createax = 3;
1191         }
1192         /*
1193          * Negative values for maxsessions are not allowed.
1194          */
1195         if (config.c_maxsessions < 0) {
1196                 config.c_maxsessions = 0;
1197         }
1198         /* We need a system default message expiry policy, because this is
1199          * the top level and there's no 'higher' policy to fall back on.
1200          * By default, do not expire messages at all.
1201          */
1202         if (config.c_ep.expire_mode == 0) {
1203                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1204                 config.c_ep.expire_value = 0;
1205         }
1206
1207         /*
1208          * Default port numbers for various services
1209          */
1210         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1211         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1212         if (config.c_imap_port == 0) config.c_imap_port = 143;
1213         if (config.c_msa_port == 0) config.c_msa_port = 587;
1214         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1215         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1216         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1217         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1218         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1219         if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
1220         if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
1221 }
1222
1223
1224
1225
1226 int main(int argc, char *argv[])
1227 {
1228         int a;
1229         int curr; 
1230         char aaa[128];
1231         FILE *fp;
1232         int old_setup_level = 0;
1233         int info_only = 0;
1234         int relh=0;
1235         int home=0;
1236         char relhome[PATH_MAX]="";
1237         char ctdldir[PATH_MAX]=CTDLDIR;
1238         char DefValue[PATH_MAX];
1239         int rv;
1240         struct passwd *pw;
1241         gid_t gid;
1242         
1243         /* set an invalid setup type */
1244         setup_type = (-1);
1245
1246         /* Check to see if we're running the web installer */
1247         if (getenv("CITADEL_INSTALLER") != NULL) {
1248                 using_web_installer = 1;
1249         }
1250
1251         /* parse command line args */
1252         for (a = 0; a < argc; ++a) {
1253                 if (!strncmp(argv[a], "-u", 2)) {
1254                         strcpy(aaa, argv[a]);
1255                         strcpy(aaa, &aaa[2]);
1256                         setup_type = atoi(aaa);
1257                 }
1258                 else if (!strcmp(argv[a], "-i")) {
1259                         info_only = 1;
1260                 }
1261                 else if (!strcmp(argv[a], "-q")) {
1262                         setup_type = UI_SILENT;
1263                 }
1264                 else if (!strncmp(argv[a], "-h", 2)) {
1265                         relh=argv[a][2]!='/';
1266                         if (!relh) {
1267                                 safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
1268                         } else {
1269                                 safestrncpy(relhome, &argv[a][2], sizeof relhome);
1270                         }
1271                         home = 1;
1272                 }
1273
1274         }
1275
1276         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1277
1278         /* If a setup type was not specified, try to determine automatically
1279          * the best one to use out of all available types.
1280          */
1281         if (setup_type < 0) {
1282                 setup_type = discover_ui();
1283         }
1284         if (info_only == 1) {
1285                 important_message("Citadel Setup", CITADEL);
1286                 cleanup(0);
1287         }
1288
1289         /* Get started in a valid setup directory. */
1290         strcpy(setup_directory, ctdl_run_dir);
1291         strcpy(DefValue, ctdl_run_dir);
1292         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
1293                 strcpy(setup_directory, getenv("CITADEL"));
1294         }
1295         else {
1296                 set_str_val(0, setup_directory, DefValue);
1297         }
1298
1299         enable_home = ( relh | home );
1300
1301         if (chdir(setup_directory) != 0) {
1302                 char errmsg[SIZ];
1303                 sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
1304                 
1305                 important_message("Citadel Setup", errmsg);
1306                 cleanup(errno);
1307         }
1308
1309
1310         /* Try to stop Citadel if we can */
1311         if (!access("/etc/init.d/citadel", X_OK)) {
1312                 rv = system("/etc/init.d/citadel stop");
1313         }
1314
1315         /* Make sure Citadel is not running. */
1316         if (test_server(setup_directory, relhome, enable_home) == 0) {
1317                 important_message("Citadel Setup",
1318                         "The Citadel service is still running.\n"
1319                         "Please stop the service manually and run "
1320                         "setup again.");
1321                 cleanup(1);
1322         }
1323
1324         /* Now begin. */
1325         switch (setup_type) {
1326
1327         case UI_TEXT:
1328                 printf("\n\n\n"
1329                         "              *** Citadel setup program ***\n\n");
1330                 break;
1331
1332         }
1333
1334         /*
1335          * What we're going to try to do here is append a whole bunch of
1336          * nulls to the citadel.config file, so we can keep the old config
1337          * values if they exist, but if the file is missing or from an
1338          * earlier version with a shorter config structure, when setup tries
1339          * to read the old config parameters, they'll all come up zero.
1340          * The length of the config file will be set to what it's supposed
1341          * to be when we rewrite it, because we replace the old file with a
1342          * completely new copy.
1343          */
1344         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1345                       S_IRUSR | S_IWUSR)) == -1) {
1346                 display_error("setup: cannot append citadel.config");
1347                 cleanup(errno);
1348         }
1349         fp = fdopen(a, "ab");
1350         if (fp == NULL) {
1351                 display_error("setup: cannot append citadel.config");
1352                 cleanup(errno);
1353         }
1354         for (a = 0; a < sizeof(struct config); ++a) {
1355                 putc(0, fp);
1356         }
1357         fclose(fp);
1358
1359         /* now we re-open it, and read the old or blank configuration */
1360         fp = fopen(file_citadel_config, "rb");
1361         if (fp == NULL) {
1362                 display_error("setup: cannot open citadel.config");
1363                 cleanup(errno);
1364         }
1365         rv = fread((char *) &config, sizeof(struct config), 1, fp);
1366         fclose(fp);
1367
1368         set_default_values();
1369
1370         /* Go through a series of dialogs prompting for config info */
1371         for (curr = 1; curr <= MAXSETUP; ++curr) {
1372                 edit_value(curr);
1373                 if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
1374                         curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
1375                 }
1376         }
1377
1378 /***** begin version update section ***** */
1379         /* take care of any updating that is necessary */
1380
1381         old_setup_level = config.c_setup_level;
1382
1383         if (old_setup_level == 0) {
1384                 goto NEW_INST;
1385         }
1386
1387         if (old_setup_level < 555) {
1388                 important_message("Citadel Setup",
1389                                   "This Citadel installation is too old "
1390                                   "to be upgraded.");
1391                 cleanup(1);
1392         }
1393         write_config_to_disk();
1394
1395         old_setup_level = config.c_setup_level;
1396
1397         /* end of version update section */
1398
1399 NEW_INST:
1400         config.c_setup_level = REV_LEVEL;
1401
1402 /******************************************/
1403         if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
1404                 gid = getgid();
1405         } else {
1406                 gid = pw->pw_gid;
1407         }
1408
1409         create_run_directories(config.c_ctdluid, gid);
1410
1411         write_config_to_disk();
1412
1413         migrate_old_installs(); /* Delete files and directories used by older Citadel versions */
1414
1415         if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
1416             (setup_type != UI_SILENT))
1417                 check_services_entry(); /* Check /etc/services */
1418 #ifndef __CYGWIN__
1419         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1420         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1421         disable_other_mtas();   /* Offer to disable other MTAs */
1422
1423 #endif
1424
1425         /* Check for the 'db' nss and offer to disable it */
1426         fixnss();
1427
1428         progress("Setting file permissions", 1, 3);
1429         rv = chown(file_citadel_config, config.c_ctdluid, gid);
1430         progress("Setting file permissions", 2, 3);
1431         rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1432         progress("Setting file permissions", 3, 3);
1433
1434         /* 
1435          * If we're running on SysV, install init scripts.
1436          */
1437         if (!access("/var/run", W_OK)) {
1438
1439                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1440                         install_init_scripts();
1441                 }
1442
1443                 if (!access("/etc/init.d/citadel", X_OK)) {
1444                         rv = system("/etc/init.d/citadel start");
1445                         sleep(3);
1446                 }
1447
1448                 if (test_server(setup_directory, relhome, enable_home) == 0) {
1449                         char buf[SIZ];
1450                         int found_it = 0;
1451
1452                         if (config.c_auth_mode == AUTHMODE_NATIVE) {
1453                                 snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
1454                                         ctdl_sbin_dir, config.c_sysadm, admin_pass);
1455                                 fp = popen(admin_cmd, "r");
1456                                 if (fp != NULL) {
1457                                         while (fgets(buf, sizeof buf, fp) != NULL) 
1458                                         {
1459                                                 if ((atol(buf) == 574) || (atol(buf) == 200))
1460                                                         ++found_it;
1461                                         }
1462                                         pclose(fp);
1463                                 }
1464                         
1465                                 if (found_it == 0) {
1466                                         important_message("Error","Setup failed to create your admin user");
1467                                 }
1468                         }
1469
1470                         if (setup_type != UI_SILENT)
1471                                 important_message("Setup finished",
1472                                                   "Setup of the Citadel server is complete.\n"
1473                                                   "If you will be using WebCit, please run its\n"
1474                                                   "setup program now; otherwise, run './citadel'\n"
1475                                                   "to log in.\n");
1476                 }
1477                 else {
1478                         important_message("Setup failed",
1479                                 "Setup is finished, but the Citadel server failed to start.\n"
1480                                 "Go back and check your configuration.\n"
1481                         );
1482                 }
1483
1484         }
1485
1486         else {
1487                 important_message("Setup finished",
1488                         "Setup is finished.  You may now start the server.");
1489         }
1490
1491         cleanup(0);
1492         return 0;
1493 }
1494
1495