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