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