* setup.c: removed an unused temp file name variable
[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 -x3 -llocal4\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 setupmsg[SIZ];
614
615         strcpy(setupmsg, "");
616
617         switch (setup_type) {
618         case UI_TEXT:
619                 title(setup_titles[msgpos]);
620                 printf("\n%s\n", setup_text[msgpos]);
621                 printf("This is currently set to:\n%s\n", str);
622                 printf("Enter new value or press return to leave unchanged:\n");
623                 fgets(buf, sizeof buf, stdin);
624                 buf[strlen(buf) - 1] = 0;
625                 if (strlen(buf) != 0)
626                         strcpy(str, buf);
627                 break;
628 #ifdef HAVE_NEWT
629         case UI_NEWT:
630
631                 newtCenteredWindow(76, 10, setup_titles[msgpos]);
632                 form = newtForm(NULL, NULL, 0);
633                 for (i=0; i<num_tokens(setup_text[msgpos], '\n'); ++i) {
634                         extract_token(buf, setup_text[msgpos], i, '\n');
635                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
636                 }
637                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
638                                         NEWT_FLAG_RETURNEXIT));
639                 newtRunForm(form);
640                 strcpy(str, result);
641
642                 newtPopWindow();
643                 newtFormDestroy(form);  
644
645 #endif
646         }
647 }
648
649 void set_int_val(int msgpos, int *ip)
650 {
651         char buf[16];
652         snprintf(buf, sizeof buf, "%d", (int) *ip);
653         set_str_val(msgpos, buf);
654         *ip = atoi(buf);
655 }
656
657
658 void set_char_val(int msgpos, char *ip)
659 {
660         char buf[16];
661         snprintf(buf, sizeof buf, "%d", (int) *ip);
662         set_str_val(msgpos, buf);
663         *ip = (char) atoi(buf);
664 }
665
666
667 void set_long_val(int msgpos, long int *ip)
668 {
669         char buf[16];
670         snprintf(buf, sizeof buf, "%ld", *ip);
671         set_str_val(msgpos, buf);
672         *ip = atol(buf);
673 }
674
675
676 void edit_value(int curr)
677 {
678         int i;
679         struct passwd *pw;
680         char bbsuidname[SIZ];
681
682         switch (curr) {
683
684         case 1:
685                 set_str_val(curr, config.c_sysadm);
686                 break;
687
688         case 2:
689 #ifdef __CYGWIN__
690                 config.c_bbsuid = 0;    /* XXX Windows hack, prob. insecure */
691 #else
692                 i = config.c_bbsuid;
693                 pw = getpwuid(i);
694                 if (pw == NULL) {
695                         set_int_val(curr, &i);
696                         config.c_bbsuid = i;
697                 }
698                 else {
699                         strcpy(bbsuidname, pw->pw_name);
700                         set_str_val(curr, bbsuidname);
701                         pw = getpwnam(bbsuidname);
702                         if (pw != NULL) {
703                                 config.c_bbsuid = pw->pw_uid;
704                         }
705                         else if (atoi(bbsuidname) > 0) {
706                                 config.c_bbsuid = atoi(bbsuidname);
707                         }
708                 }
709 #endif
710                 break;
711
712         case 3:
713                 set_int_val(curr, &config.c_port_number);
714                 break;
715
716
717         }
718 }
719
720 /*
721  * (re-)write the config data to disk
722  */
723 void write_config_to_disk(void)
724 {
725         FILE *fp;
726         int fd;
727
728         if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
729                 display_error("setup: cannot open citadel.config");
730                 cleanup(1);
731         }
732         fp = fdopen(fd, "wb");
733         if (fp == NULL) {
734                 display_error("setup: cannot open citadel.config");
735                 cleanup(1);
736         }
737         fwrite((char *) &config, sizeof(struct config), 1, fp);
738         fclose(fp);
739 }
740
741
742
743
744 /*
745  * Figure out what type of user interface we're going to use
746  */
747 int discover_ui(void)
748 {
749
750 #ifdef HAVE_NEWT
751         newtInit();
752         newtCls();
753         newtDrawRootText(0, 0, "Citadel/UX Setup");
754         return UI_NEWT;
755 #endif
756         return UI_TEXT;
757 }
758
759
760
761
762
763 int main(int argc, char *argv[])
764 {
765         int a;
766         int curr;
767         char aaa[128];
768         FILE *fp;
769         int old_setup_level = 0;
770         int info_only = 0;
771         struct utsname my_utsname;
772         struct passwd *pw;
773         struct hostent *he;
774         gid_t gid;
775
776         /* set an invalid setup type */
777         setup_type = (-1);
778
779         /* parse command line args */
780         for (a = 0; a < argc; ++a) {
781                 if (!strncmp(argv[a], "-u", 2)) {
782                         strcpy(aaa, argv[a]);
783                         strcpy(aaa, &aaa[2]);
784                         setup_type = atoi(aaa);
785                 }
786                 if (!strcmp(argv[a], "-i")) {
787                         info_only = 1;
788                 }
789                 if (!strcmp(argv[a], "-q")) {
790                         setup_type = UI_SILENT;
791                 }
792         }
793
794
795         /* If a setup type was not specified, try to determine automatically
796          * the best one to use out of all available types.
797          */
798         if (setup_type < 0) {
799                 setup_type = discover_ui();
800         }
801         if (info_only == 1) {
802                 important_message("Citadel/UX Setup", CITADEL);
803                 cleanup(0);
804         }
805
806         /* Get started in a valid setup directory. */
807         strcpy(setup_directory, BBSDIR);
808         set_str_val(0, setup_directory);
809         if (chdir(setup_directory) != 0) {
810                 important_message("Citadel/UX Setup",
811                           "The directory you specified does not exist.");
812                 cleanup(errno);
813         }
814
815         /* Determine our host name, in case we need to use it as a default */
816         uname(&my_utsname);
817
818         /* See if we need to shut down the Citadel service. */
819         for (a=0; a<=3; ++a) {
820                 progress("Shutting down the Citadel service...", a, 3);
821                 if (a == 0) shutdown_service();
822                 sleep(1);
823         }
824
825         /* Make sure it's stopped. */
826         if (test_server() == 0) {
827                 important_message("Citadel/UX Setup",
828                         "The Citadel service is still running.\n"
829                         "Please stop the service manually and run "
830                         "setup again.");
831                 cleanup(1);
832         }
833
834         /* Now begin. */
835         switch (setup_type) {
836
837         case UI_TEXT:
838                 printf("\n\n\n"
839                         "               *** Citadel/UX setup program ***\n\n");
840                 break;
841
842         }
843
844         /*
845          * What we're going to try to do here is append a whole bunch of
846          * nulls to the citadel.config file, so we can keep the old config
847          * values if they exist, but if the file is missing or from an
848          * earlier version with a shorter config structure, when setup tries
849          * to read the old config parameters, they'll all come up zero.
850          * The length of the config file will be set to what it's supposed
851          * to be when we rewrite it, because we replace the old file with a
852          * completely new copy.
853          */
854
855         if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
856                       S_IRUSR | S_IWUSR)) == -1) {
857                 display_error("setup: cannot append citadel.config");
858                 cleanup(errno);
859         }
860         fp = fdopen(a, "ab");
861         if (fp == NULL) {
862                 display_error("setup: cannot append citadel.config");
863                 cleanup(errno);
864         }
865         for (a = 0; a < sizeof(struct config); ++a)
866                 putc(0, fp);
867         fclose(fp);
868
869         /* now we re-open it, and read the old or blank configuration */
870         fp = fopen("citadel.config", "rb");
871         if (fp == NULL) {
872                 display_error("setup: cannot open citadel.config");
873                 cleanup(errno);
874         }
875         fread((char *) &config, sizeof(struct config), 1, fp);
876         fclose(fp);
877
878         /* set some sample/default values in place of blanks... */
879         if (strlen(config.c_nodename) == 0)
880                 safestrncpy(config.c_nodename, my_utsname.nodename,
881                             sizeof config.c_nodename);
882         strtok(config.c_nodename, ".");
883         if (strlen(config.c_fqdn) == 0) {
884                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
885                         safestrncpy(config.c_fqdn, he->h_name,
886                                     sizeof config.c_fqdn);
887                 else
888                         safestrncpy(config.c_fqdn, my_utsname.nodename,
889                                     sizeof config.c_fqdn);
890         }
891         if (strlen(config.c_humannode) == 0)
892                 strcpy(config.c_humannode, "My System");
893         if (strlen(config.c_phonenum) == 0)
894                 strcpy(config.c_phonenum, "US 800 555 1212");
895         if (config.c_initax == 0) {
896                 config.c_initax = 4;
897         }
898         if (strlen(config.c_moreprompt) == 0)
899                 strcpy(config.c_moreprompt, "<more>");
900         if (strlen(config.c_twitroom) == 0)
901                 strcpy(config.c_twitroom, "Trashcan");
902         if (strlen(config.c_baseroom) == 0)
903                 strcpy(config.c_baseroom, "Lobby");
904         if (strlen(config.c_aideroom) == 0)
905                 strcpy(config.c_aideroom, "Aide");
906         if (config.c_port_number == 0) {
907                 config.c_port_number = 504;
908         }
909         if (config.c_sleeping == 0) {
910                 config.c_sleeping = 900;
911         }
912         if (config.c_bbsuid == 0) {
913                 pw = getpwnam("citadel");
914                 if (pw != NULL)
915                         config.c_bbsuid = pw->pw_uid;
916         }
917         if (config.c_bbsuid == 0) {
918                 pw = getpwnam("bbs");
919                 if (pw != NULL)
920                         config.c_bbsuid = pw->pw_uid;
921         }
922         if (config.c_bbsuid == 0) {
923                 pw = getpwnam("guest");
924                 if (pw != NULL)
925                         config.c_bbsuid = pw->pw_uid;
926         }
927         if (config.c_createax == 0) {
928                 config.c_createax = 3;
929         }
930         /*
931          * Negative values for maxsessions are not allowed.
932          */
933         if (config.c_maxsessions < 0) {
934                 config.c_maxsessions = 0;
935         }
936         /* We need a system default message expiry policy, because this is
937          * the top level and there's no 'higher' policy to fall back on.
938          */
939         if (config.c_ep.expire_mode == 0) {
940                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
941                 config.c_ep.expire_value = 150;
942         }
943
944         /*
945          * Default port numbers for various services
946          */
947         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
948         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
949         if (config.c_imap_port == 0) config.c_imap_port = 143;
950
951         /* Go through a series of dialogs prompting for config info */
952         if (setup_type != UI_SILENT) {
953                 for (curr = 1; curr <= MAXSETUP; ++curr) {
954                         edit_value(curr);
955                 }
956         }
957
958         /*
959            if (setuid(config.c_bbsuid) != 0) {
960            important_message("Citadel/UX Setup",
961            "Failed to change the user ID to your Citadel user.");
962            cleanup(errno);
963            }
964          */
965
966 /***** begin version update section ***** */
967         /* take care of any updating that is necessary */
968
969         old_setup_level = config.c_setup_level;
970
971         if (old_setup_level == 0) {
972                 goto NEW_INST;
973         }
974
975         if (old_setup_level < 555) {
976                 important_message("Citadel/UX Setup",
977                                   "This Citadel/UX installation is too old "
978                                   "to be upgraded.");
979                 cleanup(1);
980         }
981         write_config_to_disk();
982
983         old_setup_level = config.c_setup_level;
984
985         /* end of version update section */
986
987 NEW_INST:
988         config.c_setup_level = REV_LEVEL;
989
990 /******************************************/
991
992         write_config_to_disk();
993
994         mkdir("info", 0700);
995         mkdir("bio", 0700);
996         mkdir("userpics", 0700);
997         mkdir("messages", 0700);
998         mkdir("help", 0700);
999         mkdir("images", 0700);
1000         mkdir("netconfigs", 0700);
1001
1002         /* Delete files and directories used by older Citadel versions */
1003         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1004         unlink("citadel.log");
1005         unlink("weekly");
1006
1007         check_services_entry(); /* Check /etc/services */
1008 #ifndef __CYGWIN__
1009         check_inittab_entry();  /* Check /etc/inittab */
1010         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1011
1012         /* Offer to disable other MTA's on the system. */
1013         disable_other_mta("sendmail");
1014         disable_other_mta("postfix");
1015         disable_other_mta("qmail");
1016         disable_other_mta("cyrus");
1017         disable_other_mta("cyrmaster");
1018         disable_other_mta("saslauthd");
1019         disable_other_mta("mta");
1020         disable_other_mta("courier-imap");
1021         disable_other_mta("courier-imap-ssl");
1022         disable_other_mta("courier-authdaemon");
1023         disable_other_mta("courier-pop3");
1024         disable_other_mta("courier-pop3d");
1025         disable_other_mta("courier-pop");
1026         disable_other_mta("vmailmgrd");
1027         disable_other_mta("imapd");
1028         disable_other_mta("popd");
1029         disable_other_mta("pop3d");
1030         disable_other_mta("exim");
1031 #endif
1032
1033         if ((pw = getpwuid(config.c_bbsuid)) == NULL)
1034                 gid = getgid();
1035         else
1036                 gid = pw->pw_gid;
1037
1038         progress("Setting file permissions", 0, 4);
1039         chown(".", config.c_bbsuid, gid);
1040         progress("Setting file permissions", 1, 4);
1041         chown("citadel.config", config.c_bbsuid, gid);
1042         progress("Setting file permissions", 2, 4);
1043         snprintf(aaa, sizeof aaa,
1044                 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
1045                 (long)config.c_bbsuid, (long)gid);
1046         system(aaa);
1047         progress("Setting file permissions", 3, 4);
1048         chmod("citadel.config", S_IRUSR | S_IWUSR);
1049         progress("Setting file permissions", 4, 4);
1050
1051         /* See if we can start the Citadel service. */
1052         if (strlen(init_entry) > 0) {
1053                 for (a=0; a<=3; ++a) {
1054                         progress("Starting the Citadel service...", a, 3);
1055                         if (a == 0) start_the_service();
1056                         sleep(1);
1057                 }
1058                 if (test_server() == 0) {
1059                         important_message("Setup finished",
1060                                 "Setup is finished.  You may now log in.");
1061                 }
1062                 else {
1063                         important_message("Setup finished",
1064                                 "Setup is finished, but the Citadel service "
1065                                 "failed to start.\n"
1066                                 "Go back and check your configuration.");
1067                 }
1068         }
1069         else {
1070                 important_message("Setup finished",
1071                         "Setup is finished.  You may now start the server.");
1072         }
1073
1074         cleanup(0);
1075         return 0;
1076 }