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