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