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