Moved some files around into sub dirs to clean up the base dir a litle.
[citadel.git] / citadel / utils / 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         int rv;
1116         
1117         /* set an invalid setup type */
1118         setup_type = (-1);
1119
1120         /* Check to see if we're running the web installer */
1121         if (getenv("CITADEL_INSTALLER") != NULL) {
1122                 using_web_installer = 1;
1123         }
1124
1125         /* parse command line args */
1126         for (a = 0; a < argc; ++a) {
1127                 if (!strncmp(argv[a], "-u", 2)) {
1128                         strcpy(aaa, argv[a]);
1129                         strcpy(aaa, &aaa[2]);
1130                         setup_type = atoi(aaa);
1131                 }
1132                 else if (!strcmp(argv[a], "-i")) {
1133                         info_only = 1;
1134                 }
1135                 else if (!strcmp(argv[a], "-q")) {
1136                         setup_type = UI_SILENT;
1137                 }
1138                 else if (!strncmp(argv[a], "-h", 2)) {
1139                         relh=argv[a][2]!='/';
1140                         if (!relh) {
1141                                 safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
1142                         } else {
1143                                 safestrncpy(relhome, &argv[a][2], sizeof relhome);
1144                         }
1145                         home = 1;
1146                 }
1147
1148         }
1149
1150         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1151
1152         /* If a setup type was not specified, try to determine automatically
1153          * the best one to use out of all available types.
1154          */
1155         if (setup_type < 0) {
1156                 setup_type = discover_ui();
1157         }
1158         if (info_only == 1) {
1159                 important_message("Citadel Setup", CITADEL);
1160                 cleanup(0);
1161         }
1162
1163         /* Get started in a valid setup directory. */
1164         strcpy(setup_directory, ctdl_run_dir);
1165         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
1166                 strcpy(setup_directory, getenv("CITADEL"));
1167         }
1168         else {
1169                 set_str_val(0, setup_directory);
1170         }
1171
1172         enable_home = ( relh | home );
1173
1174         if (chdir(setup_directory) != 0) {
1175                 char errmsg[SIZ];
1176                 sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
1177                 
1178                 important_message("Citadel Setup", errmsg);
1179                 cleanup(errno);
1180         }
1181
1182         /* Determine our host name, in case we need to use it as a default */
1183         uname(&my_utsname);
1184
1185         /* Try to stop Citadel if we can */
1186         if (!access("/etc/init.d/citadel", X_OK)) {
1187                 rv = system("/etc/init.d/citadel stop");
1188         }
1189
1190         /* Make sure Citadel is not running. */
1191         if (test_server(setup_directory, relhome, enable_home) == 0) {
1192                 important_message("Citadel Setup",
1193                         "The Citadel service is still running.\n"
1194                         "Please stop the service manually and run "
1195                         "setup again.");
1196                 cleanup(1);
1197         }
1198
1199         /* Now begin. */
1200         switch (setup_type) {
1201
1202         case UI_TEXT:
1203                 printf("\n\n\n"
1204                         "              *** Citadel setup program ***\n\n");
1205                 break;
1206
1207         }
1208
1209         /*
1210          * What we're going to try to do here is append a whole bunch of
1211          * nulls to the citadel.config file, so we can keep the old config
1212          * values if they exist, but if the file is missing or from an
1213          * earlier version with a shorter config structure, when setup tries
1214          * to read the old config parameters, they'll all come up zero.
1215          * The length of the config file will be set to what it's supposed
1216          * to be when we rewrite it, because we replace the old file with a
1217          * completely new copy.
1218          */
1219         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1220                       S_IRUSR | S_IWUSR)) == -1) {
1221                 display_error("setup: cannot append citadel.config");
1222                 cleanup(errno);
1223         }
1224         fp = fdopen(a, "ab");
1225         if (fp == NULL) {
1226                 display_error("setup: cannot append citadel.config");
1227                 cleanup(errno);
1228         }
1229         for (a = 0; a < sizeof(struct config); ++a) {
1230                 putc(0, fp);
1231         }
1232         fclose(fp);
1233
1234         /* now we re-open it, and read the old or blank configuration */
1235         fp = fopen(file_citadel_config, "rb");
1236         if (fp == NULL) {
1237                 display_error("setup: cannot open citadel.config");
1238                 cleanup(errno);
1239         }
1240         rv = fread((char *) &config, sizeof(struct config), 1, fp);
1241         fclose(fp);
1242
1243         /* set some sample/default values in place of blanks... */
1244         if (IsEmptyStr(config.c_nodename))
1245                 safestrncpy(config.c_nodename, my_utsname.nodename,
1246                             sizeof config.c_nodename);
1247         strtok(config.c_nodename, ".");
1248         if (IsEmptyStr(config.c_fqdn) ) {
1249                 if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
1250                         safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
1251                 } else {
1252                         safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
1253                 }
1254         }
1255         if (IsEmptyStr(config.c_humannode)) {
1256                 strcpy(config.c_humannode, "My System");
1257         }
1258         if (IsEmptyStr(config.c_phonenum)) {
1259                 strcpy(config.c_phonenum, "US 800 555 1212");
1260         }
1261         if (config.c_initax == 0) {
1262                 config.c_initax = 4;
1263         }
1264         if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, "<more>");
1265         if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan");
1266         if (IsEmptyStr(config.c_baseroom)) strcpy(config.c_baseroom, BASEROOM);
1267         if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide");
1268         if (config.c_port_number == 0) {
1269                 config.c_port_number = 504;
1270         }
1271         if (config.c_sleeping == 0) {
1272                 config.c_sleeping = 900;
1273         }
1274         if (config.c_ctdluid == 0) {
1275                 pw = getpwnam("citadel");
1276                 if (pw != NULL) {
1277                         config.c_ctdluid = pw->pw_uid;
1278                 }
1279         }
1280         if (config.c_ctdluid == 0) {
1281                 pw = getpwnam("bbs");
1282                 if (pw != NULL) {
1283                         config.c_ctdluid = pw->pw_uid;
1284                 }
1285         }
1286         if (config.c_ctdluid == 0) {
1287                 pw = getpwnam("guest");
1288                 if (pw != NULL) {
1289                         config.c_ctdluid = pw->pw_uid;
1290                 }
1291         }
1292         if (config.c_createax == 0) {
1293                 config.c_createax = 3;
1294         }
1295         /*
1296          * Negative values for maxsessions are not allowed.
1297          */
1298         if (config.c_maxsessions < 0) {
1299                 config.c_maxsessions = 0;
1300         }
1301         /* We need a system default message expiry policy, because this is
1302          * the top level and there's no 'higher' policy to fall back on.
1303          * By default, do not expire messages at all.
1304          */
1305         if (config.c_ep.expire_mode == 0) {
1306                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1307                 config.c_ep.expire_value = 0;
1308         }
1309
1310         /*
1311          * Default port numbers for various services
1312          */
1313         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1314         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1315         if (config.c_imap_port == 0) config.c_imap_port = 143;
1316         if (config.c_msa_port == 0) config.c_msa_port = 587;
1317         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1318         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1319         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1320         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1321         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1322         if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
1323         if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
1324
1325         /* Go through a series of dialogs prompting for config info */
1326         for (curr = 1; curr <= MAXSETUP; ++curr) {
1327                 edit_value(curr);
1328                 if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
1329                         curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
1330                 }
1331         }
1332
1333 /***** begin version update section ***** */
1334         /* take care of any updating that is necessary */
1335
1336         old_setup_level = config.c_setup_level;
1337
1338         if (old_setup_level == 0) {
1339                 goto NEW_INST;
1340         }
1341
1342         if (old_setup_level < 555) {
1343                 important_message("Citadel Setup",
1344                                   "This Citadel installation is too old "
1345                                   "to be upgraded.");
1346                 cleanup(1);
1347         }
1348         write_config_to_disk();
1349
1350         old_setup_level = config.c_setup_level;
1351
1352         /* end of version update section */
1353
1354 NEW_INST:
1355         config.c_setup_level = REV_LEVEL;
1356
1357 /******************************************/
1358
1359         write_config_to_disk();
1360
1361         rv = mkdir(ctdl_info_dir, 0700);
1362         rv = chmod(ctdl_info_dir, 0700);
1363         rv = chown(ctdl_info_dir, config.c_ctdluid, -1);
1364
1365         rv = mkdir(ctdl_bio_dir, 0700);
1366         rv = chmod(ctdl_bio_dir, 0700);
1367         rv = chown(ctdl_bio_dir, config.c_ctdluid, -1);
1368
1369         rv = mkdir(ctdl_usrpic_dir, 0700);
1370         rv = chmod(ctdl_usrpic_dir, 0700);
1371         rv = chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
1372
1373         rv = mkdir(ctdl_message_dir, 0700);
1374         rv = chmod(ctdl_message_dir, 0700);
1375         rv = chown(ctdl_message_dir, config.c_ctdluid, -1);
1376
1377         rv = mkdir(ctdl_hlp_dir, 0700);
1378         rv = chmod(ctdl_hlp_dir, 0700);
1379         rv = chown(ctdl_hlp_dir, config.c_ctdluid, -1);
1380
1381         rv = mkdir(ctdl_image_dir, 0700);
1382         rv = chmod(ctdl_image_dir, 0700);
1383         rv = chown(ctdl_image_dir, config.c_ctdluid, -1);
1384
1385         rv = mkdir(ctdl_bb_dir, 0700);
1386         rv = chmod(ctdl_bb_dir, 0700);
1387         rv = chown(ctdl_bb_dir, config.c_ctdluid, -1);
1388
1389         rv = mkdir(ctdl_file_dir, 0700);
1390         rv = chmod(ctdl_file_dir, 0700);
1391         rv = chown(ctdl_file_dir, config.c_ctdluid, -1);
1392
1393         rv = mkdir(ctdl_netcfg_dir, 0700);
1394         rv = chmod(ctdl_netcfg_dir, 0700);
1395         rv = chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
1396
1397         /* Delete files and directories used by older Citadel versions */
1398         rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1399         unlink("citadel.log");
1400         unlink("weekly");
1401
1402         if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
1403             (setup_type != UI_SILENT))
1404                 check_services_entry(); /* Check /etc/services */
1405 #ifndef __CYGWIN__
1406         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1407         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1408
1409         if ((getenv("ACT_AS_MTA") == NULL) || 
1410             (getenv("ACT_AS_MTA") &&
1411              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
1412                 /* Offer to disable other MTA's on the system. */
1413                 disable_other_mta("courier-authdaemon");
1414                 disable_other_mta("courier-imap");
1415                 disable_other_mta("courier-imap-ssl");
1416                 disable_other_mta("courier-pop");
1417                 disable_other_mta("courier-pop3");
1418                 disable_other_mta("courier-pop3d");
1419                 disable_other_mta("cyrmaster");
1420                 disable_other_mta("cyrus");
1421                 disable_other_mta("dovecot");
1422                 disable_other_mta("exim");
1423                 disable_other_mta("exim4");
1424                 disable_other_mta("imapd");
1425                 disable_other_mta("mta");
1426                 disable_other_mta("pop3d");
1427                 disable_other_mta("popd");
1428                 disable_other_mta("postfix");
1429                 disable_other_mta("qmail");
1430                 disable_other_mta("saslauthd");
1431                 disable_other_mta("sendmail");
1432                 disable_other_mta("vmailmgrd");
1433         }
1434 #endif
1435
1436         /* Check for the 'db' nss and offer to disable it */
1437         fixnss();
1438
1439         if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
1440                 gid = getgid();
1441         } else {
1442                 gid = pw->pw_gid;
1443         }
1444
1445         progress("Setting file permissions", 0, 3);
1446         rv = chown(ctdl_run_dir, config.c_ctdluid, gid);
1447         progress("Setting file permissions", 1, 3);
1448         rv = chown(file_citadel_config, config.c_ctdluid, gid);
1449         progress("Setting file permissions", 2, 3);
1450         rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1451         progress("Setting file permissions", 3, 3);
1452
1453         /* 
1454          * If we're running on SysV, install init scripts.
1455          */
1456         if (!access("/var/run", W_OK)) {
1457
1458                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1459                         install_init_scripts();
1460                 }
1461
1462                 if (!access("/etc/init.d/citadel", X_OK)) {
1463                         rv = system("/etc/init.d/citadel start");
1464                         sleep(3);
1465                 }
1466
1467                 if (test_server(setup_directory, relhome, enable_home) == 0) {
1468                         char buf[SIZ];
1469                         int found_it = 0;
1470
1471                         if (config.c_auth_mode == AUTHMODE_NATIVE) {
1472                                 snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
1473                                         ctdl_sbin_dir, config.c_sysadm, admin_pass);
1474                                 fp = popen(admin_cmd, "r");
1475                                 if (fp != NULL) {
1476                                         while (fgets(buf, sizeof buf, fp) != NULL) 
1477                                         {
1478                                                 if ((atol(buf) == 574) || (atol(buf) == 200))
1479                                                         ++found_it;
1480                                         }
1481                                         pclose(fp);
1482                                 }
1483                         
1484                                 if (found_it == 0) {
1485                                         important_message("Error","Setup failed to create your admin user");
1486                                 }
1487                         }
1488
1489                         if (setup_type != UI_SILENT)
1490                                 important_message("Setup finished",
1491                                                   "Setup of the Citadel server is complete.\n"
1492                                                   "If you will be using WebCit, please run its\n"
1493                                                   "setup program now; otherwise, run './citadel'\n"
1494                                                   "to log in.\n");
1495                 }
1496                 else {
1497                         important_message("Setup failed",
1498                                 "Setup is finished, but the Citadel server failed to start.\n"
1499                                 "Go back and check your configuration.\n"
1500                         );
1501                 }
1502
1503         }
1504
1505         else {
1506                 important_message("Setup finished",
1507                         "Setup is finished.  You may now start the server.");
1508         }
1509
1510         cleanup(0);
1511         return 0;
1512 }
1513
1514