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