* setup.c: offer to disable "exim" if found
[citadel.git] / citadel / setup.c
1 /*
2  * $Id$
3  *
4  * Citadel/UX 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
24 #include "citadel.h"
25 #include "axdefs.h"
26 #include "sysdep.h"
27 #include "config.h"
28 #include "tools.h"
29
30 #ifdef HAVE_NEWT
31 #include <newt.h>
32 #endif
33
34
35 #define MAXSETUP 3      /* How many setup questions to ask */
36
37 #define UI_TEXT         0       /* Default setup type -- text only */
38 #define UI_SILENT       3       /* Silent running, for use in scripts */
39 #define UI_NEWT         4       /* Use the "newt" window library */
40
41 #define SERVICE_NAME    "citadel"
42 #define PROTO_NAME      "tcp"
43
44 int setup_type;
45 char setup_directory[SIZ];
46 char init_entry[SIZ];
47
48 char *setup_titles[] =
49 {
50         "Citadel Home Directory",
51         "System Administrator",
52         "Citadel User ID",
53         "Server port number",
54 };
55
56
57 char *setup_text[] =
58 {
59 "Enter the full pathname of the directory in which the Citadel installation\n"
60 "you are creating or updating resides.  If you specify a directory other\n"
61 "than the default, you will need to specify the -h flag to the server when\n"
62 "you start it up.\n",
63
64 "Enter the name of the system administrator (which is probably you).\n"
65 "When an account is created with this name, it will automatically be\n"
66 "assigned the highest access level.\n",
67
68 "Citadel needs to run under its own user ID.  This would typically be\n"
69 "called \"citadel\", but if you are running Citadel as a public BBS, you\n"
70 "might also call it \"bbs\" or \"guest\".  The server will run under this\n"
71 "user ID.  Please specify that user ID here.  You may specify either a\n"
72 "user name or a numeric UID.\n",
73
74 "Specify the TCP port number on which your server will run.  Normally, this\n"
75 "will be port 504, which is the official port assigned by the IANA for\n"
76 "Citadel servers.  You'll only need to specify a different port number if\n"
77 "you run multiple instances of Citadel on the same computer and there's\n"
78 "something else already using port 504.\n",
79
80 "Setup has detected that you currently have data files from a Citadel/UX\n"
81 "version 3.2x installation.  The program 'conv_32_40' can upgrade your\n"
82 "files to version 4.0x format.\n"
83 " Setup will now exit.  Please either run 'conv_32_40' or delete your data\n"
84 "files, and run setup again.\n"
85
86 };
87
88 struct config config;
89 int direction;
90
91 /*
92  * Set an entry in inittab to the desired state
93  */
94 void set_init_entry(char *which_entry, char *new_state) {
95         char *inittab = NULL;
96         FILE *fp;
97         char buf[SIZ];
98         char entry[SIZ];
99         char levels[SIZ];
100         char state[SIZ];
101         char prog[SIZ];
102
103         inittab = strdup("");
104         if (inittab == NULL) return;
105
106         fp = fopen("/etc/inittab", "r");
107         if (fp == NULL) return;
108
109         while(fgets(buf, sizeof buf, fp) != NULL) {
110
111                 if (num_tokens(buf, ':') == 4) {
112                         extract_token(entry, buf, 0, ':');
113                         extract_token(levels, buf, 1, ':');
114                         extract_token(state, buf, 2, ':');
115                         extract_token(prog, buf, 3, ':'); /* includes 0x0a LF */
116
117                         if (!strcmp(entry, which_entry)) {
118                                 strcpy(state, new_state);
119                                 sprintf(buf, "%s:%s:%s:%s",
120                                         entry, levels, state, prog);
121                         }
122                 }
123
124                 inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
125                 if (inittab == NULL) {
126                         fclose(fp);
127                         return;
128                 }
129                 
130                 strcat(inittab, buf);
131         }
132         fclose(fp);
133         fp = fopen("/etc/inittab", "w");
134         if (fp != NULL) {
135                 fwrite(inittab, strlen(inittab), 1, fp);
136                 fclose(fp);
137                 kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
138         }
139         free(inittab);
140 }
141
142
143
144
145 /* 
146  * Shut down the Citadel service if necessary, during setup.
147  */
148 void shutdown_service(void) {
149         FILE *infp;
150         char buf[SIZ];
151         char looking_for[SIZ];
152         int have_entry = 0;
153         char entry[SIZ];
154         char prog[SIZ];
155
156         strcpy(init_entry, "");
157
158         /* Determine the fully qualified path name of citserver */
159         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
160
161         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
162          * an entry is found which we believe starts citserver.
163          */
164         infp = fopen("/etc/inittab", "r");
165         if (infp == NULL) {
166                 return;
167         } else {
168                 while (fgets(buf, sizeof buf, infp) != NULL) {
169                         buf[strlen(buf) - 1] = 0;
170                         extract_token(entry, buf, 0, ':');      
171                         extract_token(prog, buf, 3, ':');
172                         if (!strncasecmp(prog, looking_for,
173                            strlen(looking_for))) {
174                                 ++have_entry;
175                                 strcpy(init_entry, entry);
176                         }
177                 }
178                 fclose(infp);
179         }
180
181         /* Bail out if there's nothing to do. */
182         if (!have_entry) return;
183
184         set_init_entry(init_entry, "off");
185 }
186
187
188 /*
189  * Start the Citadel service.
190  */
191 void start_the_service(void) {
192         if (strlen(init_entry) > 0) {
193                 set_init_entry(init_entry, "respawn");
194         }
195 }
196
197
198
199 void cleanup(int exitcode)
200 {
201 #ifdef HAVE_NEWT
202         newtCls();
203         newtRefresh();
204         newtFinished();
205 #endif
206         exit(exitcode);
207 }
208
209
210
211 void title(char *text)
212 {
213         if (setup_type == UI_TEXT) {
214                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
215         }
216 }
217
218
219
220 int yesno(char *question)
221 {
222 #ifdef HAVE_NEWT
223         newtComponent form = NULL;
224         newtComponent yesbutton = NULL;
225         newtComponent nobutton = NULL;
226         int i = 0;
227 #endif
228         int answer = 0;
229         char buf[SIZ];
230
231         switch (setup_type) {
232
233         case UI_TEXT:
234                 do {
235                         printf("%s\nYes/No --> ", question);
236                         fgets(buf, sizeof buf, stdin);
237                         answer = tolower(buf[0]);
238                         if (answer == 'y')
239                                 answer = 1;
240                         else if (answer == 'n')
241                                 answer = 0;
242                 } while ((answer < 0) || (answer > 1));
243                 break;
244
245 #ifdef HAVE_NEWT
246         case UI_NEWT:
247                 newtCenteredWindow(76, 10, "Question");
248                 form = newtForm(NULL, NULL, 0);
249                 for (i=0; i<num_tokens(question, '\n'); ++i) {
250                         extract_token(buf, question, i, '\n');
251                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
252                 }
253                 yesbutton = newtButton(10, 5, "Yes");
254                 nobutton = newtButton(60, 5, "No");
255                 newtFormAddComponent(form, yesbutton);
256                 newtFormAddComponent(form, nobutton);
257                 if (newtRunForm(form) == yesbutton) {
258                         answer = 1;
259                 }
260                 else {
261                         answer = 0;
262                 }
263                 newtPopWindow();
264                 newtFormDestroy(form);  
265
266                 break;
267 #endif
268
269         }
270         return (answer);
271 }
272
273
274 void important_message(char *title, char *msgtext)
275 {
276 #ifdef HAVE_NEWT
277         newtComponent form = NULL;
278         int i = 0;
279 #endif
280         char buf[SIZ];
281
282         switch (setup_type) {
283
284         case UI_TEXT:
285                 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");
286                 printf("       %s \n\n%s\n\n", title, msgtext);
287                 printf("Press return to continue...");
288                 fgets(buf, sizeof buf, stdin);
289                 break;
290
291 #ifdef HAVE_NEWT
292         case UI_NEWT:
293                 newtCenteredWindow(76, 10, title);
294                 form = newtForm(NULL, NULL, 0);
295                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
296                         extract_token(buf, msgtext, i, '\n');
297                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
298                 }
299                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
300                 newtRunForm(form);
301                 newtPopWindow();
302                 newtFormDestroy(form);  
303                 break;
304 #endif
305
306         }
307 }
308
309 void important_msgnum(int msgnum)
310 {
311         important_message("Important Message", setup_text[msgnum]);
312 }
313
314 void display_error(char *error_message)
315 {
316         important_message("Error", error_message);
317 }
318
319 void progress(char *text, long int curr, long int cmax)
320 {
321 #ifdef HAVE_NEWT
322
323         /* These variables are static because progress() gets called
324          * multiple times during the course of whatever operation is
325          * being performed.  This makes setup non-threadsafe, but who
326          * cares?
327          */
328         static newtComponent form = NULL;
329         static newtComponent scale = NULL;
330 #endif
331         static long dots_printed = 0L;
332         long a = 0;
333
334         switch (setup_type) {
335
336         case UI_TEXT:
337                 if (curr == 0) {
338                         printf("%s\n", text);
339                         printf("..........................");
340                         printf("..........................");
341                         printf("..........................\r");
342                         fflush(stdout);
343                         dots_printed = 0;
344                 } else if (curr == cmax) {
345                         printf("\r%79s\n", "");
346                 } else {
347                         a = (curr * 100) / cmax;
348                         a = a * 78;
349                         a = a / 100;
350                         while (dots_printed < a) {
351                                 printf("*");
352                                 ++dots_printed;
353                                 fflush(stdout);
354                         }
355                 }
356                 break;
357
358 #ifdef HAVE_NEWT
359         case UI_NEWT:
360                 if (curr == 0) {
361                         newtCenteredWindow(76, 8, text);
362                         form = newtForm(NULL, NULL, 0);
363                         scale = newtScale(1, 3, 74, cmax);
364                         newtFormAddComponent(form, scale);
365                         newtDrawForm(form);
366                         newtRefresh();
367                 }
368                 if ((curr > 0) && (curr <= cmax)) {
369                         newtScaleSet(scale, curr);
370                         newtRefresh();
371                 }
372                 if (curr == cmax) {
373                         newtFormDestroy(form);  
374                         newtPopWindow();
375                         newtRefresh();
376                 }
377                 break;
378 #endif
379
380         }
381 }
382
383
384
385 /*
386  * check_services_entry()  -- Make sure "citadel" is in /etc/services
387  *
388  */
389 void check_services_entry(void)
390 {
391         int i;
392         FILE *sfp;
393
394         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
395                 for (i=0; i<3; ++i) {
396                         progress("Adding service entry...", i, 3);
397                         if (i == 0) {
398                                 sfp = fopen("/etc/services", "a");
399                                 if (sfp == NULL) {
400                                         display_error(strerror(errno));
401                                 } else {
402                                         fprintf(sfp, "%s                504/tcp\n",
403                                                 SERVICE_NAME);
404                                         fclose(sfp);
405                                 }
406                         }
407                         sleep(1);
408                 }
409         }
410 }
411
412
413 /*
414  * check_inittab_entry()  -- Make sure "citadel" is in /etc/inittab
415  *
416  */
417 void check_inittab_entry(void)
418 {
419         FILE *infp;
420         char buf[SIZ];
421         char looking_for[SIZ];
422         char question[128];
423         char entryname[5];
424
425         /* Determine the fully qualified path name of citserver */
426         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
427
428         /* If there's already an entry, then we have nothing left to do. */
429         if (strlen(init_entry) > 0) {
430                 return;
431         }
432
433         /* Otherwise, prompt the user to create an entry. */
434         snprintf(question, sizeof question,
435                 "There is no '%s' entry in /etc/inittab.\n"
436                 "Would you like to add one?",
437                 looking_for);
438         if (yesno(question) == 0)
439                 return;
440
441         /* Generate a unique entry name for /etc/inittab */
442         snprintf(entryname, sizeof entryname, "c0");
443         do {
444                 ++entryname[1];
445                 if (entryname[1] > '9') {
446                         entryname[1] = 0;
447                         ++entryname[0];
448                         if (entryname[0] > 'z') {
449                                 display_error(
450                                    "Can't generate a unique entry name");
451                                 return;
452                         }
453                 }
454                 snprintf(buf, sizeof buf,
455                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
456         } while (system(buf) == 0);
457
458         /* Now write it out to /etc/inittab */
459         infp = fopen("/etc/inittab", "a");
460         if (infp == NULL) {
461                 display_error(strerror(errno));
462         } else {
463                 fprintf(infp, "# Start the Citadel/UX server...\n");
464                 fprintf(infp, "%s:2345:respawn:%s -h%s\n",
465                         entryname, looking_for, setup_directory);
466                 fclose(infp);
467                 strcpy(init_entry, entryname);
468         }
469 }
470
471
472 /*
473  * On systems which use xinetd, see if we can offer to install Citadel as
474  * the default telnet target.
475  */
476 void check_xinetd_entry(void) {
477         char *filename = "/etc/xinetd.d/telnet";
478         FILE *fp;
479         char buf[SIZ];
480         int already_citadel = 0;
481
482         fp = fopen(filename, "r+");
483         if (fp == NULL) return;         /* Not there.  Oh well... */
484
485         while (fgets(buf, sizeof buf, fp) != NULL) {
486                 if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
487         }
488         fclose(fp);
489         if (already_citadel) return;    /* Already set up this way. */
490
491         /* Otherwise, prompt the user to create an entry. */
492         snprintf(buf, sizeof buf,
493                 "Setup can configure the 'xinetd' service to automatically\n"
494                 "connect incoming telnet sessions to Citadel, bypassing the\n"
495                 "host system's login prompt.  Would you like to do this?\n"
496         );
497         if (yesno(buf) == 0)
498                 return;
499
500         fp = fopen(filename, "w");
501         fprintf(fp,
502                 "# description: telnet service for Citadel users\n"
503                 "service telnet\n"
504                 "{\n"
505                 "       disable = no\n"
506                 "       flags           = REUSE\n"
507                 "       socket_type     = stream\n"
508                 "       wait            = no\n"
509                 "       user            = root\n"
510                 "       server          = /usr/sbin/in.telnetd\n"
511                 "       server_args     = -h -L %s/citadel\n"
512                 "       log_on_failure  += USERID\n"
513                 "}\n",
514                 setup_directory
515         );
516         fclose(fp);
517
518         /* Now try to restart the service */
519         system("/etc/init.d/xinetd restart >/dev/null 2>&1");
520 }
521
522
523
524 /*
525  * Offer to disable other MTA's
526  */
527 void disable_other_mta(char *mta) {
528         char buf[SIZ];
529         FILE *fp;
530         int lines = 0;
531
532         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null", mta);
533         fp = popen(buf, "r");
534         if (fp == NULL) return;
535
536         while (fgets(buf, sizeof buf, fp) != NULL) {
537                 ++lines;
538         }
539         fclose(fp);
540         if (lines == 0) return;         /* Nothing to do. */
541
542         /* Offer to replace other MTA with the vastly superior Citadel :)  */
543         snprintf(buf, sizeof buf,
544                 "You appear to have the '%s' email program\n"
545                 "running on your system.  Would you like to disable it,\n"
546                 "allowing Citadel to handle your system's Internet mail\n"
547                 "instead?\n",
548                 mta
549         );
550         if (yesno(buf) == 0)
551                 return;
552
553         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);
554         system(buf);
555         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
556         system(buf);
557 }
558
559
560
561
562 /* 
563  * Check to see if our server really works.  Returns 0 on success.
564  */
565 int test_server(void) {
566         char cmd[256];
567         char cookie[256];
568         FILE *fp;
569         char buf[4096];
570         int found_it = 0;
571
572         /* Generate a silly little cookie.  We're going to write it out
573          * to the server and try to get it back.  The cookie does not
574          * have to be secret ... just unique.
575          */
576         sprintf(cookie, "%ld.%d", time(NULL), getpid());
577
578         sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
579                 setup_directory,
580                 setup_directory,
581                 cookie);
582
583         fp = popen(cmd, "r");
584         if (fp == NULL) return(errno);
585
586         while (fgets(buf, sizeof buf, fp) != NULL) {
587                 if ( (buf[0]=='2')
588                    && (strstr(buf, cookie) != NULL) ) {
589                         ++found_it;
590                 }
591         }
592         pclose(fp);
593
594         if (found_it) {
595                 return(0);
596         }
597         return(-1);
598 }
599
600
601
602
603
604
605 void set_str_val(int msgpos, char str[])
606 {
607 #ifdef HAVE_NEWT
608         newtComponent form;
609         char *result;
610         int i;
611 #endif
612         char buf[SIZ];
613         char tempfile[PATH_MAX];
614         char setupmsg[SIZ];
615
616         strcpy(tempfile, tmpnam(NULL));
617         strcpy(setupmsg, "");
618
619         switch (setup_type) {
620         case UI_TEXT:
621                 title(setup_titles[msgpos]);
622                 printf("\n%s\n", setup_text[msgpos]);
623                 printf("This is currently set to:\n%s\n", str);
624                 printf("Enter new value or press return to leave unchanged:\n");
625                 fgets(buf, sizeof buf, stdin);
626                 buf[strlen(buf) - 1] = 0;
627                 if (strlen(buf) != 0)
628                         strcpy(str, buf);
629                 break;
630 #ifdef HAVE_NEWT
631         case UI_NEWT:
632
633                 newtCenteredWindow(76, 10, setup_titles[msgpos]);
634                 form = newtForm(NULL, NULL, 0);
635                 for (i=0; i<num_tokens(setup_text[msgpos], '\n'); ++i) {
636                         extract_token(buf, setup_text[msgpos], i, '\n');
637                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
638                 }
639                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
640                                         NEWT_FLAG_RETURNEXIT));
641                 newtRunForm(form);
642                 strcpy(str, result);
643
644                 newtPopWindow();
645                 newtFormDestroy(form);  
646
647 #endif
648         }
649 }
650
651 void set_int_val(int msgpos, int *ip)
652 {
653         char buf[16];
654         snprintf(buf, sizeof buf, "%d", (int) *ip);
655         set_str_val(msgpos, buf);
656         *ip = atoi(buf);
657 }
658
659
660 void set_char_val(int msgpos, char *ip)
661 {
662         char buf[16];
663         snprintf(buf, sizeof buf, "%d", (int) *ip);
664         set_str_val(msgpos, buf);
665         *ip = (char) atoi(buf);
666 }
667
668
669 void set_long_val(int msgpos, long int *ip)
670 {
671         char buf[16];
672         snprintf(buf, sizeof buf, "%ld", *ip);
673         set_str_val(msgpos, buf);
674         *ip = atol(buf);
675 }
676
677
678 void edit_value(int curr)
679 {
680         int i;
681         struct passwd *pw;
682         char bbsuidname[SIZ];
683
684         switch (curr) {
685
686         case 1:
687                 set_str_val(curr, config.c_sysadm);
688                 break;
689
690         case 2:
691 #ifdef __CYGWIN__
692                 config.c_bbsuid = 0;    /* XXX Windows hack, prob. insecure */
693 #else
694                 i = config.c_bbsuid;
695                 pw = getpwuid(i);
696                 if (pw == NULL) {
697                         set_int_val(curr, &i);
698                         config.c_bbsuid = i;
699                 }
700                 else {
701                         strcpy(bbsuidname, pw->pw_name);
702                         set_str_val(curr, bbsuidname);
703                         pw = getpwnam(bbsuidname);
704                         if (pw != NULL) {
705                                 config.c_bbsuid = pw->pw_uid;
706                         }
707                         else if (atoi(bbsuidname) > 0) {
708                                 config.c_bbsuid = atoi(bbsuidname);
709                         }
710                 }
711 #endif
712                 break;
713
714         case 3:
715                 set_int_val(curr, &config.c_port_number);
716                 break;
717
718
719         }
720 }
721
722 /*
723  * (re-)write the config data to disk
724  */
725 void write_config_to_disk(void)
726 {
727         FILE *fp;
728         int fd;
729
730         if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
731                 display_error("setup: cannot open citadel.config");
732                 cleanup(1);
733         }
734         fp = fdopen(fd, "wb");
735         if (fp == NULL) {
736                 display_error("setup: cannot open citadel.config");
737                 cleanup(1);
738         }
739         fwrite((char *) &config, sizeof(struct config), 1, fp);
740         fclose(fp);
741 }
742
743
744
745
746 /*
747  * Figure out what type of user interface we're going to use
748  */
749 int discover_ui(void)
750 {
751
752 #ifdef HAVE_NEWT
753         newtInit();
754         newtCls();
755         newtDrawRootText(0, 0, "Citadel/UX Setup");
756         return UI_NEWT;
757 #endif
758         return UI_TEXT;
759 }
760
761
762
763
764
765 int main(int argc, char *argv[])
766 {
767         int a;
768         int curr;
769         char aaa[128];
770         FILE *fp;
771         int old_setup_level = 0;
772         int info_only = 0;
773         struct utsname my_utsname;
774         struct passwd *pw;
775         struct hostent *he;
776         gid_t gid;
777
778         /* set an invalid setup type */
779         setup_type = (-1);
780
781         /* parse command line args */
782         for (a = 0; a < argc; ++a) {
783                 if (!strncmp(argv[a], "-u", 2)) {
784                         strcpy(aaa, argv[a]);
785                         strcpy(aaa, &aaa[2]);
786                         setup_type = atoi(aaa);
787                 }
788                 if (!strcmp(argv[a], "-i")) {
789                         info_only = 1;
790                 }
791                 if (!strcmp(argv[a], "-q")) {
792                         setup_type = UI_SILENT;
793                 }
794         }
795
796
797         /* If a setup type was not specified, try to determine automatically
798          * the best one to use out of all available types.
799          */
800         if (setup_type < 0) {
801                 setup_type = discover_ui();
802         }
803         if (info_only == 1) {
804                 important_message("Citadel/UX Setup", CITADEL);
805                 cleanup(0);
806         }
807
808         /* Get started in a valid setup directory. */
809         strcpy(setup_directory, BBSDIR);
810         set_str_val(0, setup_directory);
811         if (chdir(setup_directory) != 0) {
812                 important_message("Citadel/UX Setup",
813                           "The directory you specified does not exist.");
814                 cleanup(errno);
815         }
816
817         /* Determine our host name, in case we need to use it as a default */
818         uname(&my_utsname);
819
820         /* See if we need to shut down the Citadel service. */
821         for (a=0; a<=3; ++a) {
822                 progress("Shutting down the Citadel service...", a, 3);
823                 if (a == 0) shutdown_service();
824                 sleep(1);
825         }
826
827         /* Make sure it's stopped. */
828         if (test_server() == 0) {
829                 important_message("Citadel/UX Setup",
830                         "The Citadel service is still running.\n"
831                         "Please stop the service manually and run "
832                         "setup again.");
833                 cleanup(1);
834         }
835
836         /* Now begin. */
837         switch (setup_type) {
838
839         case UI_TEXT:
840                 printf("\n\n\n"
841                         "               *** Citadel/UX setup program ***\n\n");
842                 break;
843
844         }
845
846         /*
847          * What we're going to try to do here is append a whole bunch of
848          * nulls to the citadel.config file, so we can keep the old config
849          * values if they exist, but if the file is missing or from an
850          * earlier version with a shorter config structure, when setup tries
851          * to read the old config parameters, they'll all come up zero.
852          * The length of the config file will be set to what it's supposed
853          * to be when we rewrite it, because we replace the old file with a
854          * completely new copy.
855          */
856
857         if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
858                       S_IRUSR | S_IWUSR)) == -1) {
859                 display_error("setup: cannot append citadel.config");
860                 cleanup(errno);
861         }
862         fp = fdopen(a, "ab");
863         if (fp == NULL) {
864                 display_error("setup: cannot append citadel.config");
865                 cleanup(errno);
866         }
867         for (a = 0; a < sizeof(struct config); ++a)
868                 putc(0, fp);
869         fclose(fp);
870
871         /* now we re-open it, and read the old or blank configuration */
872         fp = fopen("citadel.config", "rb");
873         if (fp == NULL) {
874                 display_error("setup: cannot open citadel.config");
875                 cleanup(errno);
876         }
877         fread((char *) &config, sizeof(struct config), 1, fp);
878         fclose(fp);
879
880         /* set some sample/default values in place of blanks... */
881         if (strlen(config.c_nodename) == 0)
882                 safestrncpy(config.c_nodename, my_utsname.nodename,
883                             sizeof config.c_nodename);
884         strtok(config.c_nodename, ".");
885         if (strlen(config.c_fqdn) == 0) {
886                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
887                         safestrncpy(config.c_fqdn, he->h_name,
888                                     sizeof config.c_fqdn);
889                 else
890                         safestrncpy(config.c_fqdn, my_utsname.nodename,
891                                     sizeof config.c_fqdn);
892         }
893         if (strlen(config.c_humannode) == 0)
894                 strcpy(config.c_humannode, "My System");
895         if (strlen(config.c_phonenum) == 0)
896                 strcpy(config.c_phonenum, "US 800 555 1212");
897         if (config.c_initax == 0) {
898                 config.c_initax = 4;
899         }
900         if (strlen(config.c_moreprompt) == 0)
901                 strcpy(config.c_moreprompt, "<more>");
902         if (strlen(config.c_twitroom) == 0)
903                 strcpy(config.c_twitroom, "Trashcan");
904         if (strlen(config.c_baseroom) == 0)
905                 strcpy(config.c_baseroom, "Lobby");
906         if (strlen(config.c_aideroom) == 0)
907                 strcpy(config.c_aideroom, "Aide");
908         if (config.c_port_number == 0) {
909                 config.c_port_number = 504;
910         }
911         if (config.c_sleeping == 0) {
912                 config.c_sleeping = 900;
913         }
914         if (config.c_bbsuid == 0) {
915                 pw = getpwnam("citadel");
916                 if (pw != NULL)
917                         config.c_bbsuid = pw->pw_uid;
918         }
919         if (config.c_bbsuid == 0) {
920                 pw = getpwnam("bbs");
921                 if (pw != NULL)
922                         config.c_bbsuid = pw->pw_uid;
923         }
924         if (config.c_bbsuid == 0) {
925                 pw = getpwnam("guest");
926                 if (pw != NULL)
927                         config.c_bbsuid = pw->pw_uid;
928         }
929         if (config.c_createax == 0) {
930                 config.c_createax = 3;
931         }
932         /*
933          * Negative values for maxsessions are not allowed.
934          */
935         if (config.c_maxsessions < 0) {
936                 config.c_maxsessions = 0;
937         }
938         /* We need a system default message expiry policy, because this is
939          * the top level and there's no 'higher' policy to fall back on.
940          */
941         if (config.c_ep.expire_mode == 0) {
942                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
943                 config.c_ep.expire_value = 150;
944         }
945
946         /*
947          * Default port numbers for various services
948          */
949         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
950         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
951         if (config.c_imap_port == 0) config.c_imap_port = 143;
952
953         /* Go through a series of dialogs prompting for config info */
954         if (setup_type != UI_SILENT) {
955                 for (curr = 1; curr <= MAXSETUP; ++curr) {
956                         edit_value(curr);
957                 }
958         }
959
960         /*
961            if (setuid(config.c_bbsuid) != 0) {
962            important_message("Citadel/UX Setup",
963            "Failed to change the user ID to your Citadel user.");
964            cleanup(errno);
965            }
966          */
967
968 /***** begin version update section ***** */
969         /* take care of any updating that is necessary */
970
971         old_setup_level = config.c_setup_level;
972
973         if (old_setup_level == 0) {
974                 goto NEW_INST;
975         }
976
977         if (old_setup_level < 555) {
978                 important_message("Citadel/UX Setup",
979                                   "This Citadel/UX installation is too old "
980                                   "to be upgraded.");
981                 cleanup(1);
982         }
983         write_config_to_disk();
984
985         old_setup_level = config.c_setup_level;
986
987         /* end of version update section */
988
989 NEW_INST:
990         config.c_setup_level = REV_LEVEL;
991
992 /******************************************/
993
994         write_config_to_disk();
995
996         mkdir("info", 0700);
997         mkdir("bio", 0700);
998         mkdir("userpics", 0700);
999         mkdir("messages", 0700);
1000         mkdir("help", 0700);
1001         mkdir("images", 0700);
1002         mkdir("netconfigs", 0700);
1003
1004         /* Delete files and directories used by older Citadel versions */
1005         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1006         unlink("citadel.log");
1007         unlink("weekly");
1008
1009         check_services_entry(); /* Check /etc/services */
1010 #ifndef __CYGWIN__
1011         check_inittab_entry();  /* Check /etc/inittab */
1012         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1013
1014         /* Offer to disable other MTA's on the system. */
1015         disable_other_mta("sendmail");
1016         disable_other_mta("postfix");
1017         disable_other_mta("qmail");
1018         disable_other_mta("cyrus");
1019         disable_other_mta("cyrmaster");
1020         disable_other_mta("saslauthd");
1021         disable_other_mta("mta");
1022         disable_other_mta("courier-imap");
1023         disable_other_mta("courier-imap-ssl");
1024         disable_other_mta("courier-authdaemon");
1025         disable_other_mta("courier-pop3");
1026         disable_other_mta("courier-pop3d");
1027         disable_other_mta("courier-pop");
1028         disable_other_mta("vmailmgrd");
1029         disable_other_mta("imapd");
1030         disable_other_mta("popd");
1031         disable_other_mta("pop3d");
1032         disable_other_mta("exim");
1033 #endif
1034
1035         if ((pw = getpwuid(config.c_bbsuid)) == NULL)
1036                 gid = getgid();
1037         else
1038                 gid = pw->pw_gid;
1039
1040         progress("Setting file permissions", 0, 4);
1041         chown(".", config.c_bbsuid, gid);
1042         progress("Setting file permissions", 1, 4);
1043         chown("citadel.config", config.c_bbsuid, gid);
1044         progress("Setting file permissions", 2, 4);
1045         snprintf(aaa, sizeof aaa,
1046                 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
1047                 (long)config.c_bbsuid, (long)gid);
1048         system(aaa);
1049         progress("Setting file permissions", 3, 4);
1050         chmod("citadel.config", S_IRUSR | S_IWUSR);
1051         progress("Setting file permissions", 4, 4);
1052
1053         /* See if we can start the Citadel service. */
1054         if (strlen(init_entry) > 0) {
1055                 for (a=0; a<=3; ++a) {
1056                         progress("Starting the Citadel service...", a, 3);
1057                         if (a == 0) start_the_service();
1058                         sleep(1);
1059                 }
1060                 if (test_server() == 0) {
1061                         important_message("Setup finished",
1062                                 "Setup is finished.  You may now log in.");
1063                 }
1064                 else {
1065                         important_message("Setup finished",
1066                                 "Setup is finished, but the Citadel service "
1067                                 "failed to start.\n"
1068                                 "Go back and check your configuration.");
1069                 }
1070         }
1071         else {
1072                 important_message("Setup finished",
1073                         "Setup is finished.  You may now start the server.");
1074         }
1075
1076         cleanup(0);
1077         return 0;
1078 }