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