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