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