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