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