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