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