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