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