93068896b32d7bce9fe6dc527fccf69b1f770fb4
[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         int i = 0;
227 #endif
228         int answer = 0;
229         char buf[SIZ];
230
231         switch (setup_type) {
232
233         case UI_TEXT:
234                 do {
235                         printf("%s\nYes/No --> ", question);
236                         fgets(buf, sizeof buf, stdin);
237                         answer = tolower(buf[0]);
238                         if (answer == 'y')
239                                 answer = 1;
240                         else if (answer == 'n')
241                                 answer = 0;
242                 } while ((answer < 0) || (answer > 1));
243                 break;
244
245 #ifdef HAVE_NEWT
246         case UI_NEWT:
247                 newtCenteredWindow(76, 10, "Question");
248                 form = newtForm(NULL, NULL, 0);
249                 for (i=0; i<num_tokens(question, '\n'); ++i) {
250                         extract_token(buf, question, i, '\n');
251                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
252                 }
253                 yesbutton = newtButton(10, 5, "Yes");
254                 nobutton = newtButton(60, 5, "No");
255                 newtFormAddComponent(form, yesbutton);
256                 newtFormAddComponent(form, nobutton);
257                 if (newtRunForm(form) == yesbutton) {
258                         answer = 1;
259                 }
260                 else {
261                         answer = 0;
262                 }
263                 newtPopWindow();
264                 newtFormDestroy(form);  
265
266                 break;
267 #endif
268
269         }
270         return (answer);
271 }
272
273
274 void important_message(char *title, char *msgtext)
275 {
276 #ifdef HAVE_NEWT
277         newtComponent form = NULL;
278         int i = 0;
279 #endif
280         char buf[SIZ];
281
282         switch (setup_type) {
283
284         case UI_TEXT:
285                 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");
286                 printf("       %s \n\n%s\n\n", title, msgtext);
287                 printf("Press return to continue...");
288                 fgets(buf, sizeof buf, stdin);
289                 break;
290
291 #ifdef HAVE_NEWT
292         case UI_NEWT:
293                 newtCenteredWindow(76, 10, title);
294                 form = newtForm(NULL, NULL, 0);
295                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
296                         extract_token(buf, msgtext, i, '\n');
297                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
298                 }
299                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
300                 newtRunForm(form);
301                 newtPopWindow();
302                 newtFormDestroy(form);  
303                 break;
304 #endif
305
306         }
307 }
308
309 void important_msgnum(int msgnum)
310 {
311         important_message("Important Message", setup_text[msgnum]);
312 }
313
314 void display_error(char *error_message)
315 {
316         important_message("Error", error_message);
317 }
318
319 void progress(char *text, long int curr, long int cmax)
320 {
321 #ifdef HAVE_NEWT
322
323         /* These variables are static because progress() gets called
324          * multiple times during the course of whatever operation is
325          * being performed.  This makes setup non-threadsafe, but who
326          * cares?
327          */
328         static newtComponent form = NULL;
329         static newtComponent scale = NULL;
330 #endif
331         static long dots_printed = 0L;
332         long a = 0;
333
334         switch (setup_type) {
335
336         case UI_TEXT:
337                 if (curr == 0) {
338                         printf("%s\n", text);
339                         printf("..........................");
340                         printf("..........................");
341                         printf("..........................\r");
342                         fflush(stdout);
343                         dots_printed = 0;
344                 } else if (curr == cmax) {
345                         printf("\r%79s\n", "");
346                 } else {
347                         a = (curr * 100) / cmax;
348                         a = a * 78;
349                         a = a / 100;
350                         while (dots_printed < a) {
351                                 printf("*");
352                                 ++dots_printed;
353                                 fflush(stdout);
354                         }
355                 }
356                 break;
357
358 #ifdef HAVE_NEWT
359         case UI_NEWT:
360                 if (curr == 0) {
361                         newtCenteredWindow(76, 8, text);
362                         form = newtForm(NULL, NULL, 0);
363                         scale = newtScale(1, 3, 74, cmax);
364                         newtFormAddComponent(form, scale);
365                         newtDrawForm(form);
366                         newtRefresh();
367                 }
368                 if ((curr > 0) && (curr <= cmax)) {
369                         newtScaleSet(scale, curr);
370                         newtRefresh();
371                 }
372                 if (curr == cmax) {
373                         newtFormDestroy(form);  
374                         newtPopWindow();
375                         newtRefresh();
376                 }
377                 break;
378 #endif
379
380         }
381 }
382
383
384
385 /*
386  * check_services_entry()  -- Make sure "citadel" is in /etc/services
387  *
388  */
389 void check_services_entry(void)
390 {
391         int i;
392         FILE *sfp;
393
394         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
395                 for (i=0; i<3; ++i) {
396                         progress("Adding service entry...", i, 3);
397                         if (i == 0) {
398                                 sfp = fopen("/etc/services", "a");
399                                 if (sfp == NULL) {
400                                         display_error(strerror(errno));
401                                 } else {
402                                         fprintf(sfp, "%s                504/tcp\n",
403                                                 SERVICE_NAME);
404                                         fclose(sfp);
405                                 }
406                         }
407                         sleep(1);
408                 }
409         }
410 }
411
412
413 /*
414  * check_inittab_entry()  -- Make sure "citadel" is in /etc/inittab
415  *
416  */
417 void check_inittab_entry(void)
418 {
419         FILE *infp;
420         char buf[SIZ];
421         char looking_for[SIZ];
422         char question[128];
423         char entryname[5];
424
425         /* Determine the fully qualified path name of citserver */
426         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
427
428         /* If there's already an entry, then we have nothing left to do. */
429         if (strlen(init_entry) > 0) {
430                 return;
431         }
432
433         /* Otherwise, prompt the user to create an entry. */
434         snprintf(question, sizeof question,
435                 "There is no '%s' entry in /etc/inittab.\n"
436                 "Would you like to add one?",
437                 looking_for);
438         if (yesno(question) == 0)
439                 return;
440
441         /* Generate a unique entry name for /etc/inittab */
442         snprintf(entryname, sizeof entryname, "c0");
443         do {
444                 ++entryname[1];
445                 if (entryname[1] > '9') {
446                         entryname[1] = 0;
447                         ++entryname[0];
448                         if (entryname[0] > 'z') {
449                                 display_error(
450                                    "Can't generate a unique entry name");
451                                 return;
452                         }
453                 }
454                 snprintf(buf, sizeof buf,
455                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
456         } while (system(buf) == 0);
457
458         /* Now write it out to /etc/inittab */
459         infp = fopen("/etc/inittab", "a");
460         if (infp == NULL) {
461                 display_error(strerror(errno));
462         } else {
463                 fprintf(infp, "# Start the Citadel/UX server...\n");
464                 fprintf(infp, "%s:2345:respawn:%s -h%s\n",
465                         entryname, looking_for, setup_directory);
466                 fclose(infp);
467                 strcpy(init_entry, entryname);
468         }
469 }
470
471
472
473 void set_str_val(int msgpos, char str[])
474 {
475 #ifdef HAVE_NEWT
476         newtComponent form;
477         char *result;
478         int i;
479 #endif
480         char buf[SIZ];
481         char tempfile[PATH_MAX];
482         char setupmsg[SIZ];
483
484         strcpy(tempfile, tmpnam(NULL));
485         strcpy(setupmsg, "");
486
487         switch (setup_type) {
488         case UI_TEXT:
489                 title(setup_titles[msgpos]);
490                 printf("\n%s\n", setup_text[msgpos]);
491                 printf("This is currently set to:\n%s\n", str);
492                 printf("Enter new value or press return to leave unchanged:\n");
493                 fgets(buf, sizeof buf, stdin);
494                 buf[strlen(buf) - 1] = 0;
495                 if (strlen(buf) != 0)
496                         strcpy(str, buf);
497                 break;
498 #ifdef HAVE_NEWT
499         case UI_NEWT:
500
501                 newtCenteredWindow(76, 10, setup_titles[msgpos]);
502                 form = newtForm(NULL, NULL, 0);
503                 for (i=0; i<num_tokens(setup_text[msgpos], '\n'); ++i) {
504                         extract_token(buf, setup_text[msgpos], i, '\n');
505                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
506                 }
507                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
508                                         NEWT_FLAG_RETURNEXIT));
509                 newtRunForm(form);
510                 strcpy(str, result);
511
512                 newtPopWindow();
513                 newtFormDestroy(form);  
514
515 #endif
516         }
517 }
518
519 void set_int_val(int msgpos, int *ip)
520 {
521         char buf[16];
522         snprintf(buf, sizeof buf, "%d", (int) *ip);
523         set_str_val(msgpos, buf);
524         *ip = atoi(buf);
525 }
526
527
528 void set_char_val(int msgpos, char *ip)
529 {
530         char buf[16];
531         snprintf(buf, sizeof buf, "%d", (int) *ip);
532         set_str_val(msgpos, buf);
533         *ip = (char) atoi(buf);
534 }
535
536
537 void set_long_val(int msgpos, long int *ip)
538 {
539         char buf[16];
540         snprintf(buf, sizeof buf, "%ld", *ip);
541         set_str_val(msgpos, buf);
542         *ip = atol(buf);
543 }
544
545
546 void edit_value(int curr)
547 {
548         int i;
549         struct passwd *pw;
550         char bbsuidname[SIZ];
551
552         switch (curr) {
553
554         case 1:
555                 set_str_val(curr, config.c_sysadm);
556                 break;
557
558         case 2:
559 #ifdef __CYGWIN__
560                 config.c_bbsuid = 0;    /* XXX Windows hack, prob. insecure */
561 #else
562                 i = config.c_bbsuid;
563                 pw = getpwuid(i);
564                 if (pw == NULL) {
565                         set_int_val(curr, &i);
566                         config.c_bbsuid = i;
567                 }
568                 else {
569                         strcpy(bbsuidname, pw->pw_name);
570                         set_str_val(curr, bbsuidname);
571                         pw = getpwnam(bbsuidname);
572                         if (pw != NULL) {
573                                 config.c_bbsuid = pw->pw_uid;
574                         }
575                         else if (atoi(bbsuidname) > 0) {
576                                 config.c_bbsuid = atoi(bbsuidname);
577                         }
578                 }
579 #endif
580                 break;
581
582         case 3:
583                 set_int_val(curr, &config.c_port_number);
584                 break;
585
586
587         }
588 }
589
590 /*
591  * (re-)write the config data to disk
592  */
593 void write_config_to_disk(void)
594 {
595         FILE *fp;
596         int fd;
597
598         if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
599                 display_error("setup: cannot open citadel.config");
600                 cleanup(1);
601         }
602         fp = fdopen(fd, "wb");
603         if (fp == NULL) {
604                 display_error("setup: cannot open citadel.config");
605                 cleanup(1);
606         }
607         fwrite((char *) &config, sizeof(struct config), 1, fp);
608         fclose(fp);
609 }
610
611
612
613
614 /*
615  * Figure out what type of user interface we're going to use
616  */
617 int discover_ui(void)
618 {
619
620 #ifdef HAVE_NEWT
621         newtInit();
622         newtCls();
623         newtDrawRootText(0, 0, "Citadel/UX Setup");
624         return UI_NEWT;
625 #endif
626         return UI_TEXT;
627 }
628
629
630
631
632
633 int main(int argc, char *argv[])
634 {
635         int a;
636         int curr;
637         char aaa[128];
638         FILE *fp;
639         int old_setup_level = 0;
640         int info_only = 0;
641         struct utsname my_utsname;
642         struct passwd *pw;
643         struct hostent *he;
644         gid_t gid;
645
646         /* set an invalid setup type */
647         setup_type = (-1);
648
649         /* parse command line args */
650         for (a = 0; a < argc; ++a) {
651                 if (!strncmp(argv[a], "-u", 2)) {
652                         strcpy(aaa, argv[a]);
653                         strcpy(aaa, &aaa[2]);
654                         setup_type = atoi(aaa);
655                 }
656                 if (!strcmp(argv[a], "-i")) {
657                         info_only = 1;
658                 }
659                 if (!strcmp(argv[a], "-q")) {
660                         setup_type = UI_SILENT;
661                 }
662         }
663
664
665         /* If a setup type was not specified, try to determine automatically
666          * the best one to use out of all available types.
667          */
668         if (setup_type < 0) {
669                 setup_type = discover_ui();
670         }
671         if (info_only == 1) {
672                 important_message("Citadel/UX Setup", CITADEL);
673                 cleanup(0);
674         }
675
676         /* Get started in a valid setup directory. */
677         strcpy(setup_directory, BBSDIR);
678         set_str_val(0, setup_directory);
679         if (chdir(setup_directory) != 0) {
680                 important_message("Citadel/UX Setup",
681                           "The directory you specified does not exist.");
682                 cleanup(errno);
683         }
684
685         /* Determine our host name, in case we need to use it as a default */
686         uname(&my_utsname);
687
688         /* See if we need to shut down the Citadel service. */
689         for (a=0; a<=5; ++a) {
690                 progress("Shutting down the Citadel service...", a, 5);
691                 if (a == 0) shutdown_service();
692                 sleep(1);
693         }
694
695         /* Now begin. */
696         switch (setup_type) {
697
698         case UI_TEXT:
699                 printf("\n\n\n"
700                         "               *** Citadel/UX setup program ***\n\n");
701                 break;
702
703         }
704
705         /*
706          * What we're going to try to do here is append a whole bunch of
707          * nulls to the citadel.config file, so we can keep the old config
708          * values if they exist, but if the file is missing or from an
709          * earlier version with a shorter config structure, when setup tries
710          * to read the old config parameters, they'll all come up zero.
711          * The length of the config file will be set to what it's supposed
712          * to be when we rewrite it, because we replace the old file with a
713          * completely new copy.
714          */
715
716         if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
717                       S_IRUSR | S_IWUSR)) == -1) {
718                 display_error("setup: cannot append citadel.config");
719                 cleanup(errno);
720         }
721         fp = fdopen(a, "ab");
722         if (fp == NULL) {
723                 display_error("setup: cannot append citadel.config");
724                 cleanup(errno);
725         }
726         for (a = 0; a < sizeof(struct config); ++a)
727                 putc(0, fp);
728         fclose(fp);
729
730         /* now we re-open it, and read the old or blank configuration */
731         fp = fopen("citadel.config", "rb");
732         if (fp == NULL) {
733                 display_error("setup: cannot open citadel.config");
734                 cleanup(errno);
735         }
736         fread((char *) &config, sizeof(struct config), 1, fp);
737         fclose(fp);
738
739         /* set some sample/default values in place of blanks... */
740         if (strlen(config.c_nodename) == 0)
741                 safestrncpy(config.c_nodename, my_utsname.nodename,
742                             sizeof config.c_nodename);
743         strtok(config.c_nodename, ".");
744         if (strlen(config.c_fqdn) == 0) {
745                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
746                         safestrncpy(config.c_fqdn, he->h_name,
747                                     sizeof config.c_fqdn);
748                 else
749                         safestrncpy(config.c_fqdn, my_utsname.nodename,
750                                     sizeof config.c_fqdn);
751         }
752         if (strlen(config.c_humannode) == 0)
753                 strcpy(config.c_humannode, "My System");
754         if (strlen(config.c_phonenum) == 0)
755                 strcpy(config.c_phonenum, "US 800 555 1212");
756         if (config.c_initax == 0) {
757                 config.c_initax = 4;
758         }
759         if (strlen(config.c_moreprompt) == 0)
760                 strcpy(config.c_moreprompt, "<more>");
761         if (strlen(config.c_twitroom) == 0)
762                 strcpy(config.c_twitroom, "Trashcan");
763         if (strlen(config.c_bucket_dir) == 0)
764                 strcpy(config.c_bucket_dir, "bitbucket");
765         if (strlen(config.c_net_password) == 0)
766                 strcpy(config.c_net_password, "netpassword");
767         if (strlen(config.c_baseroom) == 0)
768                 strcpy(config.c_baseroom, "Lobby");
769         if (strlen(config.c_aideroom) == 0)
770                 strcpy(config.c_aideroom, "Aide");
771         if (config.c_port_number == 0) {
772                 config.c_port_number = 504;
773         }
774         if (config.c_ipgm_secret == 0) {
775                 srand(getpid());
776                 config.c_ipgm_secret = rand();
777         }
778         if (config.c_sleeping == 0) {
779                 config.c_sleeping = 900;
780         }
781         if (config.c_bbsuid == 0) {
782                 pw = getpwnam("citadel");
783                 if (pw != NULL)
784                         config.c_bbsuid = pw->pw_uid;
785         }
786         if (config.c_bbsuid == 0) {
787                 pw = getpwnam("bbs");
788                 if (pw != NULL)
789                         config.c_bbsuid = pw->pw_uid;
790         }
791         if (config.c_bbsuid == 0) {
792                 pw = getpwnam("guest");
793                 if (pw != NULL)
794                         config.c_bbsuid = pw->pw_uid;
795         }
796         if (config.c_createax == 0) {
797                 config.c_createax = 3;
798         }
799         /*
800          * Negative values for maxsessions are not allowed.
801          */
802         if (config.c_maxsessions < 0) {
803                 config.c_maxsessions = 0;
804         }
805         /* We need a system default message expiry policy, because this is
806          * the top level and there's no 'higher' policy to fall back on.
807          */
808         if (config.c_ep.expire_mode == 0) {
809                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
810                 config.c_ep.expire_value = 150;
811         }
812
813         /*
814          * Default port numbers for various services
815          */
816         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
817         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
818         if (config.c_imap_port == 0) config.c_imap_port = 143;
819
820         /* Go through a series of dialogs prompting for config info */
821         if (setup_type != UI_SILENT) {
822                 for (curr = 1; curr <= MAXSETUP; ++curr) {
823                         edit_value(curr);
824                 }
825         }
826
827         /*
828            if (setuid(config.c_bbsuid) != 0) {
829            important_message("Citadel/UX Setup",
830            "Failed to change the user ID to your Citadel user.");
831            cleanup(errno);
832            }
833          */
834
835 /***** begin version update section ***** */
836         /* take care of any updating that is necessary */
837
838         old_setup_level = config.c_setup_level;
839
840         if (old_setup_level == 0)
841                 goto NEW_INST;
842
843         if (old_setup_level < 323) {
844                 important_message("Citadel/UX Setup",
845                                   "This Citadel/UX installation is too old "
846                                   "to be upgraded.");
847                 cleanup(1);
848         }
849         write_config_to_disk();
850
851         if ((config.c_setup_level / 10) == 32) {
852                 important_msgnum(31);
853                 cleanup(0);
854         }
855         if (config.c_setup_level < 400) {
856                 config.c_setup_level = 400;
857         }
858         /* end of 3.23 -> 4.00 update section */
859
860         /* end of 4.00 -> 4.02 update section */
861
862         old_setup_level = config.c_setup_level;
863
864         /* end of version update section */
865
866 NEW_INST:
867         config.c_setup_level = REV_LEVEL;
868
869 /******************************************/
870
871         write_config_to_disk();
872
873         mkdir("info", 0700);
874         mkdir("bio", 0700);
875         mkdir("userpics", 0700);
876         mkdir("messages", 0700);
877         mkdir("help", 0700);
878         mkdir("images", 0700);
879         mkdir("netconfigs", 0700);
880         mkdir(config.c_bucket_dir, 0700);
881
882         /* Delete a bunch of old files from Citadel v4; don't need anymore */
883         system("rm -fr ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
884
885         /* Delete the old citadel.log file; this facility has been removed */
886         unlink("citadel.log");
887
888         check_services_entry(); /* Check /etc/services */
889 #ifndef __CYGWIN__
890         check_inittab_entry();  /* Check /etc/inittab */
891 #endif
892
893         if ((pw = getpwuid(config.c_bbsuid)) == NULL)
894                 gid = getgid();
895         else
896                 gid = pw->pw_gid;
897
898         progress("Setting file permissions", 0, 5);
899         chown(".", config.c_bbsuid, gid);
900         progress("Setting file permissions", 1, 5);
901         chown("citadel.config", config.c_bbsuid, gid);
902         progress("Setting file permissions", 2, 5);
903         snprintf(aaa, sizeof aaa,
904                 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
905                 (long)config.c_bbsuid, (long)gid);
906         system(aaa);
907         progress("Setting file permissions", 3, 5);
908         chmod("citadel.config", S_IRUSR | S_IWUSR);
909         progress("Setting file permissions", 4, 5);
910
911         /* See if we can start the Citadel service. */
912         if (strlen(init_entry) > 0) {
913                 for (a=0; a<=5; ++a) {
914                         progress("Starting the Citadel service...", a, 5);
915                         if (a == 0) start_the_service();
916                         sleep(1);
917                 }
918                 important_message("Setup finished",
919                         "Setup is finished.  You may now log in.");
920         }
921         else {
922                 important_message("Setup finished",
923                         "Setup is finished.  You may now start the server.");
924         }
925
926         cleanup(0);
927         return 0;
928 }