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