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