* setup.c: offer to hack /etc/xinetd.d/telnet
[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?"
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
526
527 void set_str_val(int msgpos, char str[])
528 {
529 #ifdef HAVE_NEWT
530         newtComponent form;
531         char *result;
532         int i;
533 #endif
534         char buf[SIZ];
535         char tempfile[PATH_MAX];
536         char setupmsg[SIZ];
537
538         strcpy(tempfile, tmpnam(NULL));
539         strcpy(setupmsg, "");
540
541         switch (setup_type) {
542         case UI_TEXT:
543                 title(setup_titles[msgpos]);
544                 printf("\n%s\n", setup_text[msgpos]);
545                 printf("This is currently set to:\n%s\n", str);
546                 printf("Enter new value or press return to leave unchanged:\n");
547                 fgets(buf, sizeof buf, stdin);
548                 buf[strlen(buf) - 1] = 0;
549                 if (strlen(buf) != 0)
550                         strcpy(str, buf);
551                 break;
552 #ifdef HAVE_NEWT
553         case UI_NEWT:
554
555                 newtCenteredWindow(76, 10, setup_titles[msgpos]);
556                 form = newtForm(NULL, NULL, 0);
557                 for (i=0; i<num_tokens(setup_text[msgpos], '\n'); ++i) {
558                         extract_token(buf, setup_text[msgpos], i, '\n');
559                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
560                 }
561                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
562                                         NEWT_FLAG_RETURNEXIT));
563                 newtRunForm(form);
564                 strcpy(str, result);
565
566                 newtPopWindow();
567                 newtFormDestroy(form);  
568
569 #endif
570         }
571 }
572
573 void set_int_val(int msgpos, int *ip)
574 {
575         char buf[16];
576         snprintf(buf, sizeof buf, "%d", (int) *ip);
577         set_str_val(msgpos, buf);
578         *ip = atoi(buf);
579 }
580
581
582 void set_char_val(int msgpos, char *ip)
583 {
584         char buf[16];
585         snprintf(buf, sizeof buf, "%d", (int) *ip);
586         set_str_val(msgpos, buf);
587         *ip = (char) atoi(buf);
588 }
589
590
591 void set_long_val(int msgpos, long int *ip)
592 {
593         char buf[16];
594         snprintf(buf, sizeof buf, "%ld", *ip);
595         set_str_val(msgpos, buf);
596         *ip = atol(buf);
597 }
598
599
600 void edit_value(int curr)
601 {
602         int i;
603         struct passwd *pw;
604         char bbsuidname[SIZ];
605
606         switch (curr) {
607
608         case 1:
609                 set_str_val(curr, config.c_sysadm);
610                 break;
611
612         case 2:
613 #ifdef __CYGWIN__
614                 config.c_bbsuid = 0;    /* XXX Windows hack, prob. insecure */
615 #else
616                 i = config.c_bbsuid;
617                 pw = getpwuid(i);
618                 if (pw == NULL) {
619                         set_int_val(curr, &i);
620                         config.c_bbsuid = i;
621                 }
622                 else {
623                         strcpy(bbsuidname, pw->pw_name);
624                         set_str_val(curr, bbsuidname);
625                         pw = getpwnam(bbsuidname);
626                         if (pw != NULL) {
627                                 config.c_bbsuid = pw->pw_uid;
628                         }
629                         else if (atoi(bbsuidname) > 0) {
630                                 config.c_bbsuid = atoi(bbsuidname);
631                         }
632                 }
633 #endif
634                 break;
635
636         case 3:
637                 set_int_val(curr, &config.c_port_number);
638                 break;
639
640
641         }
642 }
643
644 /*
645  * (re-)write the config data to disk
646  */
647 void write_config_to_disk(void)
648 {
649         FILE *fp;
650         int fd;
651
652         if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
653                 display_error("setup: cannot open citadel.config");
654                 cleanup(1);
655         }
656         fp = fdopen(fd, "wb");
657         if (fp == NULL) {
658                 display_error("setup: cannot open citadel.config");
659                 cleanup(1);
660         }
661         fwrite((char *) &config, sizeof(struct config), 1, fp);
662         fclose(fp);
663 }
664
665
666
667
668 /*
669  * Figure out what type of user interface we're going to use
670  */
671 int discover_ui(void)
672 {
673
674 #ifdef HAVE_NEWT
675         newtInit();
676         newtCls();
677         newtDrawRootText(0, 0, "Citadel/UX Setup");
678         return UI_NEWT;
679 #endif
680         return UI_TEXT;
681 }
682
683
684
685
686
687 int main(int argc, char *argv[])
688 {
689         int a;
690         int curr;
691         char aaa[128];
692         FILE *fp;
693         int old_setup_level = 0;
694         int info_only = 0;
695         struct utsname my_utsname;
696         struct passwd *pw;
697         struct hostent *he;
698         gid_t gid;
699
700         /* set an invalid setup type */
701         setup_type = (-1);
702
703         /* parse command line args */
704         for (a = 0; a < argc; ++a) {
705                 if (!strncmp(argv[a], "-u", 2)) {
706                         strcpy(aaa, argv[a]);
707                         strcpy(aaa, &aaa[2]);
708                         setup_type = atoi(aaa);
709                 }
710                 if (!strcmp(argv[a], "-i")) {
711                         info_only = 1;
712                 }
713                 if (!strcmp(argv[a], "-q")) {
714                         setup_type = UI_SILENT;
715                 }
716         }
717
718
719         /* If a setup type was not specified, try to determine automatically
720          * the best one to use out of all available types.
721          */
722         if (setup_type < 0) {
723                 setup_type = discover_ui();
724         }
725         if (info_only == 1) {
726                 important_message("Citadel/UX Setup", CITADEL);
727                 cleanup(0);
728         }
729
730         /* Get started in a valid setup directory. */
731         strcpy(setup_directory, BBSDIR);
732         set_str_val(0, setup_directory);
733         if (chdir(setup_directory) != 0) {
734                 important_message("Citadel/UX Setup",
735                           "The directory you specified does not exist.");
736                 cleanup(errno);
737         }
738
739         /* Determine our host name, in case we need to use it as a default */
740         uname(&my_utsname);
741
742         /* See if we need to shut down the Citadel service. */
743         for (a=0; a<=3; ++a) {
744                 progress("Shutting down the Citadel service...", a, 3);
745                 if (a == 0) shutdown_service();
746                 sleep(1);
747         }
748
749         /* Now begin. */
750         switch (setup_type) {
751
752         case UI_TEXT:
753                 printf("\n\n\n"
754                         "               *** Citadel/UX setup program ***\n\n");
755                 break;
756
757         }
758
759         /*
760          * What we're going to try to do here is append a whole bunch of
761          * nulls to the citadel.config file, so we can keep the old config
762          * values if they exist, but if the file is missing or from an
763          * earlier version with a shorter config structure, when setup tries
764          * to read the old config parameters, they'll all come up zero.
765          * The length of the config file will be set to what it's supposed
766          * to be when we rewrite it, because we replace the old file with a
767          * completely new copy.
768          */
769
770         if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
771                       S_IRUSR | S_IWUSR)) == -1) {
772                 display_error("setup: cannot append citadel.config");
773                 cleanup(errno);
774         }
775         fp = fdopen(a, "ab");
776         if (fp == NULL) {
777                 display_error("setup: cannot append citadel.config");
778                 cleanup(errno);
779         }
780         for (a = 0; a < sizeof(struct config); ++a)
781                 putc(0, fp);
782         fclose(fp);
783
784         /* now we re-open it, and read the old or blank configuration */
785         fp = fopen("citadel.config", "rb");
786         if (fp == NULL) {
787                 display_error("setup: cannot open citadel.config");
788                 cleanup(errno);
789         }
790         fread((char *) &config, sizeof(struct config), 1, fp);
791         fclose(fp);
792
793         /* set some sample/default values in place of blanks... */
794         if (strlen(config.c_nodename) == 0)
795                 safestrncpy(config.c_nodename, my_utsname.nodename,
796                             sizeof config.c_nodename);
797         strtok(config.c_nodename, ".");
798         if (strlen(config.c_fqdn) == 0) {
799                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
800                         safestrncpy(config.c_fqdn, he->h_name,
801                                     sizeof config.c_fqdn);
802                 else
803                         safestrncpy(config.c_fqdn, my_utsname.nodename,
804                                     sizeof config.c_fqdn);
805         }
806         if (strlen(config.c_humannode) == 0)
807                 strcpy(config.c_humannode, "My System");
808         if (strlen(config.c_phonenum) == 0)
809                 strcpy(config.c_phonenum, "US 800 555 1212");
810         if (config.c_initax == 0) {
811                 config.c_initax = 4;
812         }
813         if (strlen(config.c_moreprompt) == 0)
814                 strcpy(config.c_moreprompt, "<more>");
815         if (strlen(config.c_twitroom) == 0)
816                 strcpy(config.c_twitroom, "Trashcan");
817         if (strlen(config.c_net_password) == 0)
818                 strcpy(config.c_net_password, "netpassword");
819         if (strlen(config.c_baseroom) == 0)
820                 strcpy(config.c_baseroom, "Lobby");
821         if (strlen(config.c_aideroom) == 0)
822                 strcpy(config.c_aideroom, "Aide");
823         if (config.c_port_number == 0) {
824                 config.c_port_number = 504;
825         }
826         if (config.c_sleeping == 0) {
827                 config.c_sleeping = 900;
828         }
829         if (config.c_bbsuid == 0) {
830                 pw = getpwnam("citadel");
831                 if (pw != NULL)
832                         config.c_bbsuid = pw->pw_uid;
833         }
834         if (config.c_bbsuid == 0) {
835                 pw = getpwnam("bbs");
836                 if (pw != NULL)
837                         config.c_bbsuid = pw->pw_uid;
838         }
839         if (config.c_bbsuid == 0) {
840                 pw = getpwnam("guest");
841                 if (pw != NULL)
842                         config.c_bbsuid = pw->pw_uid;
843         }
844         if (config.c_createax == 0) {
845                 config.c_createax = 3;
846         }
847         /*
848          * Negative values for maxsessions are not allowed.
849          */
850         if (config.c_maxsessions < 0) {
851                 config.c_maxsessions = 0;
852         }
853         /* We need a system default message expiry policy, because this is
854          * the top level and there's no 'higher' policy to fall back on.
855          */
856         if (config.c_ep.expire_mode == 0) {
857                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
858                 config.c_ep.expire_value = 150;
859         }
860
861         /*
862          * Default port numbers for various services
863          */
864         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
865         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
866         if (config.c_imap_port == 0) config.c_imap_port = 143;
867
868         /* Go through a series of dialogs prompting for config info */
869         if (setup_type != UI_SILENT) {
870                 for (curr = 1; curr <= MAXSETUP; ++curr) {
871                         edit_value(curr);
872                 }
873         }
874
875         /*
876            if (setuid(config.c_bbsuid) != 0) {
877            important_message("Citadel/UX Setup",
878            "Failed to change the user ID to your Citadel user.");
879            cleanup(errno);
880            }
881          */
882
883 /***** begin version update section ***** */
884         /* take care of any updating that is necessary */
885
886         old_setup_level = config.c_setup_level;
887
888         if (old_setup_level == 0)
889                 goto NEW_INST;
890
891         if (old_setup_level < 323) {
892                 important_message("Citadel/UX Setup",
893                                   "This Citadel/UX installation is too old "
894                                   "to be upgraded.");
895                 cleanup(1);
896         }
897         write_config_to_disk();
898
899         if ((config.c_setup_level / 10) == 32) {
900                 important_msgnum(31);
901                 cleanup(0);
902         }
903         if (config.c_setup_level < 400) {
904                 config.c_setup_level = 400;
905         }
906         /* end of 3.23 -> 4.00 update section */
907
908         /* end of 4.00 -> 4.02 update section */
909
910         old_setup_level = config.c_setup_level;
911
912         /* end of version update section */
913
914 NEW_INST:
915         config.c_setup_level = REV_LEVEL;
916
917 /******************************************/
918
919         write_config_to_disk();
920
921         mkdir("info", 0700);
922         mkdir("bio", 0700);
923         mkdir("userpics", 0700);
924         mkdir("messages", 0700);
925         mkdir("help", 0700);
926         mkdir("images", 0700);
927         mkdir("netconfigs", 0700);
928
929         /* Delete a bunch of old files from Citadel v4; don't need anymore */
930         system("rm -fr ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
931
932         /* Delete the old citadel.log file; this facility has been removed */
933         unlink("citadel.log");
934
935         check_services_entry(); /* Check /etc/services */
936 #ifndef __CYGWIN__
937         check_inittab_entry();  /* Check /etc/inittab */
938         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
939 #endif
940
941         if ((pw = getpwuid(config.c_bbsuid)) == NULL)
942                 gid = getgid();
943         else
944                 gid = pw->pw_gid;
945
946         progress("Setting file permissions", 0, 4);
947         chown(".", config.c_bbsuid, gid);
948         progress("Setting file permissions", 1, 4);
949         chown("citadel.config", config.c_bbsuid, gid);
950         progress("Setting file permissions", 2, 4);
951         snprintf(aaa, sizeof aaa,
952                 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
953                 (long)config.c_bbsuid, (long)gid);
954         system(aaa);
955         progress("Setting file permissions", 3, 4);
956         chmod("citadel.config", S_IRUSR | S_IWUSR);
957         progress("Setting file permissions", 4, 4);
958
959         /* See if we can start the Citadel service. */
960         if (strlen(init_entry) > 0) {
961                 for (a=0; a<=3; ++a) {
962                         progress("Starting the Citadel service...", a, 3);
963                         if (a == 0) start_the_service();
964                         sleep(1);
965                 }
966                 important_message("Setup finished",
967                         "Setup is finished.  You may now log in.");
968         }
969         else {
970                 important_message("Setup finished",
971                         "Setup is finished.  You may now start the server.");
972         }
973
974         cleanup(0);
975         return 0;
976 }