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