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