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