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