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