]> code.citadel.org Git - citadel.git/blob - citadel/setup.c
* Addition of ENABLE_UNIX_AUTH environment variable
[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                 if (getenv("ENABLE_UNIX_AUTH")) {
735                         if (!strcasecmp(getenv("ENABLE_UNIX_AUTH"), "yes")) {
736                                 config.c_auth_mode = 1;
737                         }
738                         else {
739                                 config.c_auth_mode = 0;
740                         }
741                 }
742                 else {
743                         set_bool_val(curr, &config.c_auth_mode);
744                 }
745                 break;
746
747         }
748 }
749
750 /*
751  * (re-)write the config data to disk
752  */
753 void write_config_to_disk(void)
754 {
755         FILE *fp;
756         int fd;
757
758         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
759                 display_error("setup: cannot open citadel.config");
760                 cleanup(1);
761         }
762         fp = fdopen(fd, "wb");
763         if (fp == NULL) {
764                 display_error("setup: cannot open citadel.config");
765                 cleanup(1);
766         }
767         fwrite((char *) &config, sizeof(struct config), 1, fp);
768         fclose(fp);
769 }
770
771
772
773
774 /*
775  * Figure out what type of user interface we're going to use
776  */
777 int discover_ui(void)
778 {
779
780         /* Use "dialog" if we have it */
781         if (getenv("CTDL_DIALOG") != NULL) {
782                 return UI_DIALOG;
783         }
784                 
785         return UI_TEXT;
786 }
787
788
789
790
791
792 /*
793  * Strip "db" entries out of /etc/nsswitch.conf
794  */
795 void fixnss(void) {
796         FILE *fp_read;
797         int fd_write;
798         char buf[256];
799         char buf_nc[256];
800         char question[512];
801         int i;
802         int changed = 0;
803         int file_changed = 0;
804         char new_filename[64];
805
806         fp_read = fopen(NSSCONF, "r");
807         if (fp_read == NULL) {
808                 return;
809         }
810
811         strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
812         fd_write = mkstemp(new_filename);
813         if (fd_write < 0) {
814                 fclose(fp_read);
815                 return;
816         }
817
818         while (fgets(buf, sizeof buf, fp_read) != NULL) {
819                 changed = 0;
820                 strcpy(buf_nc, buf);
821                 for (i=0; i<strlen(buf_nc); ++i) {
822                         if (buf_nc[i] == '#') {
823                                 buf_nc[i] = 0;
824                         }
825                 }
826                 for (i=0; i<strlen(buf_nc); ++i) {
827                         if (!strncasecmp(&buf_nc[i], "db", 2)) {
828                                 if (i > 0) {
829                                         if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
830                                                 changed = 1;
831                                                 file_changed = 1;
832                                                 strcpy(&buf_nc[i], &buf_nc[i+2]);
833                                                 strcpy(&buf[i], &buf[i+2]);
834                                                 if (buf[i]==32) {
835                                                         strcpy(&buf_nc[i], &buf_nc[i+1]);
836                                                         strcpy(&buf[i], &buf[i+1]);
837                                                 }
838                                         }
839                                 }
840                         }
841                 }
842                 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
843                         fclose(fp_read);
844                         close(fd_write);
845                         unlink(new_filename);
846                         return;
847                 }
848         }
849
850         fclose(fp_read);
851         
852         if (!file_changed) {
853                 unlink(new_filename);
854                 return;
855         }
856
857         snprintf(question, sizeof question,
858                 "\n"
859                 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
860                 "one or more services.  This is not necessary on most systems,\n"
861                 "and it is known to crash the Citadel server when delivering\n"
862                 "mail to the Internet.\n"
863                 "\n"
864                 "Do you want this module to be automatically disabled?\n"
865                 "\n"
866         );
867
868         if (yesno(question, 1)) {
869                 sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
870                 system(buf);
871         }
872         unlink(new_filename);
873 }
874
875
876
877
878
879
880
881
882 int main(int argc, char *argv[])
883 {
884         int a;
885         int curr; 
886         char aaa[128];
887         FILE *fp;
888         int old_setup_level = 0;
889         int info_only = 0;
890         struct utsname my_utsname;
891         struct passwd *pw;
892         struct hostent *he;
893         gid_t gid;
894         int relh=0;
895         int home=0;
896         char relhome[PATH_MAX]="";
897         char ctdldir[PATH_MAX]=CTDLDIR;
898         
899         /* set an invalid setup type */
900         setup_type = (-1);
901
902         /* Check to see if we're running the web installer */
903         if (getenv("CITADEL_INSTALLER") != NULL) {
904                 using_web_installer = 1;
905         }
906
907         /* parse command line args */
908         for (a = 0; a < argc; ++a) {
909                 if (!strncmp(argv[a], "-u", 2)) {
910                         strcpy(aaa, argv[a]);
911                         strcpy(aaa, &aaa[2]);
912                         setup_type = atoi(aaa);
913                 }
914                 if (!strcmp(argv[a], "-i")) {
915                         info_only = 1;
916                 }
917                 if (!strcmp(argv[a], "-q")) {
918                         setup_type = UI_SILENT;
919                 }
920         }
921
922
923         /* If a setup type was not specified, try to determine automatically
924          * the best one to use out of all available types.
925          */
926         if (setup_type < 0) {
927                 setup_type = discover_ui();
928         }
929         if (info_only == 1) {
930                 important_message("Citadel Setup", CITADEL);
931                 cleanup(0);
932         }
933
934         /* Get started in a valid setup directory. */
935         strcpy(setup_directory, 
936 #ifdef HAVE_RUN_DIR
937                    ""
938 #else
939                    CTDLDIR
940 #endif
941                    );
942         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
943                 strcpy(setup_directory, getenv("CITADEL"));
944         }
945         else {
946                 set_str_val(0, setup_directory);
947         }
948
949         home=(setup_directory[1]!='\0');
950         relh=home&(setup_directory[1]!='/');
951         if (!relh) {
952                 safestrncpy(ctdl_home_directory, setup_directory, sizeof ctdl_home_directory);
953         }
954         else {
955                 safestrncpy(relhome, ctdl_home_directory, sizeof relhome);
956         }
957
958         calc_dirs_n_files(relh, home, relhome, ctdldir);
959         
960         enable_home=(relh|home);
961
962         if (home) {
963                 if (chdir(setup_directory) == 0) {
964                         strcpy(file_citadel_config, "./citadel.config");
965                 }
966                 else {
967                         important_message("Citadel Setup",
968                                 "The directory you specified does not exist.");
969                         cleanup(errno);
970                 }
971         }
972
973         /* Determine our host name, in case we need to use it as a default */
974         uname(&my_utsname);
975
976         /* Try to stop Citadel if we can */
977         if (!access("/etc/init.d/citadel", X_OK)) {
978                 system("/etc/init.d/citadel stop");
979         }
980
981         /* Make sure Citadel is not running. */
982         if (test_server() == 0) {
983                 important_message("Citadel Setup",
984                         "The Citadel service is still running.\n"
985                         "Please stop the service manually and run "
986                         "setup again.");
987                 cleanup(1);
988         }
989
990         /* Now begin. */
991         switch (setup_type) {
992
993         case UI_TEXT:
994                 printf("\n\n\n"
995                         "              *** Citadel setup program ***\n\n");
996                 break;
997
998         }
999
1000         /*
1001          * What we're going to try to do here is append a whole bunch of
1002          * nulls to the citadel.config file, so we can keep the old config
1003          * values if they exist, but if the file is missing or from an
1004          * earlier version with a shorter config structure, when setup tries
1005          * to read the old config parameters, they'll all come up zero.
1006          * The length of the config file will be set to what it's supposed
1007          * to be when we rewrite it, because we replace the old file with a
1008          * completely new copy.
1009          */
1010         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1011                       S_IRUSR | S_IWUSR)) == -1) {
1012                 display_error("setup: cannot append citadel.config");
1013                 cleanup(errno);
1014         }
1015         fp = fdopen(a, "ab");
1016         if (fp == NULL) {
1017                 display_error("setup: cannot append citadel.config");
1018                 cleanup(errno);
1019         }
1020         for (a = 0; a < sizeof(struct config); ++a)
1021                 putc(0, fp);
1022         fclose(fp);
1023
1024         /* now we re-open it, and read the old or blank configuration */
1025         fp = fopen(file_citadel_config, "rb");
1026         if (fp == NULL) {
1027                 display_error("setup: cannot open citadel.config");
1028                 cleanup(errno);
1029         }
1030         fread((char *) &config, sizeof(struct config), 1, fp);
1031         fclose(fp);
1032
1033         /* set some sample/default values in place of blanks... */
1034         if (strlen(config.c_nodename) == 0)
1035                 safestrncpy(config.c_nodename, my_utsname.nodename,
1036                             sizeof config.c_nodename);
1037         strtok(config.c_nodename, ".");
1038         if (strlen(config.c_fqdn) == 0) {
1039                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
1040                         safestrncpy(config.c_fqdn, he->h_name,
1041                                     sizeof config.c_fqdn);
1042                 else
1043                         safestrncpy(config.c_fqdn, my_utsname.nodename,
1044                                     sizeof config.c_fqdn);
1045         }
1046         if (strlen(config.c_humannode) == 0)
1047                 strcpy(config.c_humannode, "My System");
1048         if (strlen(config.c_phonenum) == 0)
1049                 strcpy(config.c_phonenum, "US 800 555 1212");
1050         if (config.c_initax == 0) {
1051                 config.c_initax = 4;
1052         }
1053         if (strlen(config.c_moreprompt) == 0)
1054                 strcpy(config.c_moreprompt, "<more>");
1055         if (strlen(config.c_twitroom) == 0)
1056                 strcpy(config.c_twitroom, "Trashcan");
1057         if (strlen(config.c_baseroom) == 0)
1058                 strcpy(config.c_baseroom, BASEROOM);
1059         if (strlen(config.c_aideroom) == 0)
1060                 strcpy(config.c_aideroom, "Aide");
1061         if (config.c_port_number == 0) {
1062                 config.c_port_number = 504;
1063         }
1064         if (config.c_sleeping == 0) {
1065                 config.c_sleeping = 900;
1066         }
1067         if (config.c_ctdluid == 0) {
1068                 pw = getpwnam("citadel");
1069                 if (pw != NULL)
1070                         config.c_ctdluid = pw->pw_uid;
1071         }
1072         if (config.c_ctdluid == 0) {
1073                 pw = getpwnam("bbs");
1074                 if (pw != NULL)
1075                         config.c_ctdluid = pw->pw_uid;
1076         }
1077         if (config.c_ctdluid == 0) {
1078                 pw = getpwnam("guest");
1079                 if (pw != NULL)
1080                         config.c_ctdluid = pw->pw_uid;
1081         }
1082         if (config.c_createax == 0) {
1083                 config.c_createax = 3;
1084         }
1085         /*
1086          * Negative values for maxsessions are not allowed.
1087          */
1088         if (config.c_maxsessions < 0) {
1089                 config.c_maxsessions = 0;
1090         }
1091         /* We need a system default message expiry policy, because this is
1092          * the top level and there's no 'higher' policy to fall back on.
1093          * By default, do not expire messages at all.
1094          */
1095         if (config.c_ep.expire_mode == 0) {
1096                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1097                 config.c_ep.expire_value = 0;
1098         }
1099
1100         /*
1101          * Default port numbers for various services
1102          */
1103         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1104         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1105         if (config.c_imap_port == 0) config.c_imap_port = 143;
1106         if (config.c_msa_port == 0) config.c_msa_port = 587;
1107         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1108         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1109         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1110         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1111         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1112
1113         /* Go through a series of dialogs prompting for config info */
1114         if (setup_type != UI_SILENT) {
1115                 for (curr = 1; curr <= MAXSETUP; ++curr) {
1116                         edit_value(curr);
1117                 }
1118         }
1119
1120 /***** begin version update section ***** */
1121         /* take care of any updating that is necessary */
1122
1123         old_setup_level = config.c_setup_level;
1124
1125         if (old_setup_level == 0) {
1126                 goto NEW_INST;
1127         }
1128
1129         if (old_setup_level < 555) {
1130                 important_message("Citadel Setup",
1131                                   "This Citadel installation is too old "
1132                                   "to be upgraded.");
1133                 cleanup(1);
1134         }
1135         write_config_to_disk();
1136
1137         old_setup_level = config.c_setup_level;
1138
1139         /* end of version update section */
1140
1141 NEW_INST:
1142         config.c_setup_level = REV_LEVEL;
1143
1144 /******************************************/
1145
1146         write_config_to_disk();
1147
1148         mkdir(ctdl_info_dir, 0700);
1149         chmod(ctdl_info_dir, 0700);
1150         chown(ctdl_info_dir, config.c_ctdluid, -1);
1151
1152         mkdir(ctdl_bio_dir, 0700);
1153         chmod(ctdl_bio_dir, 0700);
1154         chown(ctdl_bio_dir, config.c_ctdluid, -1);
1155
1156         mkdir(ctdl_usrpic_dir, 0700);
1157         chmod(ctdl_usrpic_dir, 0700);
1158         chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
1159
1160         mkdir(ctdl_message_dir, 0700);
1161         chmod(ctdl_message_dir, 0700);
1162         chown(ctdl_message_dir, config.c_ctdluid, -1);
1163
1164         mkdir(ctdl_hlp_dir, 0700);
1165         chmod(ctdl_hlp_dir, 0700);
1166         chown(ctdl_hlp_dir, config.c_ctdluid, -1);
1167
1168         mkdir(ctdl_image_dir, 0700);
1169         chmod(ctdl_image_dir, 0700);
1170         chown(ctdl_image_dir, config.c_ctdluid, -1);
1171
1172         mkdir(ctdl_bb_dir, 0700);
1173         chmod(ctdl_bb_dir, 0700);
1174         chown(ctdl_bb_dir, config.c_ctdluid, -1);
1175
1176         mkdir(ctdl_file_dir, 0700);
1177         chmod(ctdl_file_dir, 0700);
1178         chown(ctdl_file_dir, config.c_ctdluid, -1);
1179
1180         mkdir(ctdl_netcfg_dir, 0700);
1181         chmod(ctdl_netcfg_dir, 0700);
1182         chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
1183
1184         /* TODO: where to put this? */
1185         mkdir("netconfigs", 0700);
1186         chmod("netconfigs", 0700);
1187         chown("netconfigs", config.c_ctdluid, -1);
1188
1189         /* Delete files and directories used by older Citadel versions */
1190         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1191         unlink("citadel.log");
1192         unlink("weekly");
1193
1194         check_services_entry(); /* Check /etc/services */
1195 #ifndef __CYGWIN__
1196         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1197         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1198
1199         /* Offer to disable other MTA's on the system. */
1200         disable_other_mta("courier-authdaemon");
1201         disable_other_mta("courier-imap");
1202         disable_other_mta("courier-imap-ssl");
1203         disable_other_mta("courier-pop");
1204         disable_other_mta("courier-pop3");
1205         disable_other_mta("courier-pop3d");
1206         disable_other_mta("cyrmaster");
1207         disable_other_mta("cyrus");
1208         disable_other_mta("dovecot");
1209         disable_other_mta("exim");
1210         disable_other_mta("exim4");
1211         disable_other_mta("hula");
1212         disable_other_mta("imapd");
1213         disable_other_mta("mta");
1214         disable_other_mta("pop3d");
1215         disable_other_mta("popd");
1216         disable_other_mta("postfix");
1217         disable_other_mta("qmail");
1218         disable_other_mta("saslauthd");
1219         disable_other_mta("sendmail");
1220         disable_other_mta("vmailmgrd");
1221         disable_other_mta("zimbra");
1222 #endif
1223
1224         /* Check for the 'db' nss and offer to disable it */
1225         fixnss();
1226
1227         if ((pw = getpwuid(config.c_ctdluid)) == NULL)
1228                 gid = getgid();
1229         else
1230                 gid = pw->pw_gid;
1231
1232         progress("Setting file permissions", 0, 4);
1233         chown(".", config.c_ctdluid, gid);
1234         sleep(1);
1235         progress("Setting file permissions", 1, 4);
1236         chown(file_citadel_config, config.c_ctdluid, gid);
1237         sleep(1);
1238         progress("Setting file permissions", 2, 4);
1239
1240         snprintf(aaa, sizeof aaa,
1241                          "%schkpwd",
1242                          ctdl_sbin_dir);
1243         chown(aaa,0,0); /*  config.c_ctdluid, gid); chkpwd needs to be root owned*/
1244         sleep(1);
1245         progress("Setting file permissions", 3, 4);
1246         chmod(aaa, 04755); 
1247
1248         sleep(1);
1249         progress("Setting file permissions", 3, 4);
1250         chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1251         sleep(1);
1252         progress("Setting file permissions", 4, 4);
1253
1254         /* 
1255          * If we're running on SysV, install init scripts.
1256          */
1257         if (!access("/var/run", W_OK)) {
1258
1259                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1260                         install_init_scripts();
1261                 }
1262
1263                 if (!access("/etc/init.d/citadel", X_OK)) {
1264                         system("/etc/init.d/citadel start");
1265                         sleep(3);
1266                 }
1267
1268                 if (test_server() == 0) {
1269                         important_message("Setup finished",
1270                                 "Setup of the Citadel server is complete.\n"
1271                                 "If you will be using WebCit, please run its\n"
1272                                 "setup program now; otherwise, run './citadel'\n"
1273                                 "to log in.\n");
1274                 }
1275                 else {
1276                         important_message("Setup failed",
1277                                 "Setup is finished, but the Citadel server failed to start.\n"
1278                                 "Go back and check your configuration.\n"
1279                         );
1280                 }
1281
1282         }
1283
1284         else {
1285                 important_message("Setup finished",
1286                         "Setup is finished.  You may now start the server.");
1287         }
1288
1289         cleanup(0);
1290         return 0;
1291 }
1292
1293