Tue Aug 18 00:42:33 EDT 1998 Nathan Bryant <bryant@cs.usm.maine.edu>
[citadel.git] / citadel / setup.c
1 /*
2  * Citadel/UX setup program
3  * v4.1 / by Art Cancro
4  * see copyright.txt for copyright information
5  *
6  * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
7  */
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <fcntl.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <netdb.h>
18 #include <errno.h>
19 #include <limits.h>
20
21 #include "citadel.h"
22 #include "axdefs.h"
23 #include "sysdep.h"
24 #include "config.h"
25
26 #ifdef CURSES_INC
27 # ifdef OK
28 # undef OK
29 # endif
30 #include CURSES_INC
31 #endif
32
33 #define MAXSETUP 19
34
35 #define UI_TEXT         0       /* Default setup type -- text only */
36 #define UI_DIALOG       1       /* Use the 'dialog' program */
37 #define UI_CURSES       2       /* Use curses */
38
39 #define SERVICE_NAME    "citadel"
40 #define PROTO_NAME      "tcp"
41
42 int setup_type;
43 char setup_directory[128];
44 int need_init_q = 0;
45
46 char *setup_titles[] = {
47         "BBS Home Directory",
48         "Citadel node name",
49         "Fully Qualified Domain Name (FQDN)",
50         "Human-readable node name",
51         "Phone number",
52         "BBS City and State",
53         "System Administrator",
54         "BBS User ID",
55         "'Room Creator = Room Aide' flag",
56         "Server timeout period",
57         "Initial access level",
58         "Registration requirements",
59         "Twit Detect!",
60         "Twit Detect target room",
61         "Maximum concurrent sessions",
62         "Paginator prompt",
63         "Restrict Internet mail flag",
64         "Name of bit bucket subdirectory",
65         "System net password",
66         "Server port number",
67         };
68
69
70 char *setup_text[] = {
71
72 "0",
73 "Enter the full pathname of the directory in which the BBS you are",
74 "creating or updating resides.  If you specify a directory other than the",
75 "default, you will need to specify the -h flag to the server when you start",
76 "it up.",
77
78 "1",
79 "This is the name your system is known by on a Citadel/UX network.  It",
80 "should be 8 characters or less, and should generally be comprised only of",
81 "letters.  You can also use numbers and hyphens if necessary.",
82
83 "2",
84 "This is the name your system is known by on the Internet.",
85 "If you're not on the Internet, simply set this to your",
86 "node name followed by '.UUCP'.",
87
88 "3",
89 "This is a longer description of your system, readable by",
90 "us mere humans.  It can be up to 20 characters long and it",
91 "can have spaces in it.  Note that if you are part of a",
92 "Cit86Net, this is the name your system will be known by on",
93 "that network.",
94
95 "4",
96 "This is the main dialup number for your system.  If yours",
97 "can not be dialed into, then make one up!  It should be in",
98 "the format 'US 000 000 0000' - the US is your country code",
99 "(look it up if you're not in the United States) and the",
100 "rest is, of course, your area code and phone number.",
101 "This doesn't have any use in Citadel/UX, but gateways to",
102 "other networks may require it, and someday we may use this",
103 "to have the networker automatically build a BBS list.",
104
105 "5",
106 "Enter the geographical location of your system (city and",
107 "state/province/country etc.)",
108
109 "6",
110 "Enter the name of the system administrator (which is probably you).",
111 "When an account is created with this name, it will automatically be",
112 "assigned the highest access level.",
113
114 "7",
115 "You should create a user called 'bbs', 'guest', 'citadel', or something",
116 "similar, that will allow users a way into your BBS.  The server will run",
117 "under this user ID.  Please specify that (numeric) user ID here.",
118
119 "8",
120 "This is a boolean value.  If you set it to 1, anyone who",
121 "creates a class 3 (passworded) or class 4 (invitation",
122 "only) room will automatically become the Room Aide for",
123 "that room, allowing them to edit it, delete/move messages,",
124 "etc.  This is an administrative decision: it works well on",
125 "some systems, and not so well on others.  Set this to 0 to",
126 "disable this function.",
127
128 "9",
129 "This setting specifies how long a server session may sit idle before it is",
130 "automatically terminated.  The recommended value is 900 seconds (15",
131 "minutes).  Note that this has *nothing* to do with any watchdog timer that",
132 "is presented to the user.  The server's timeout is intended to kill idle or",
133 "zombie sessions running on a network, etc.  ",
134 "You MUST set this to a reasonable value.  Setting it to zero will cause",
135 "the server to malfunction.",
136
137 "10",
138 "This is the access level new users are assigned.",
139 "",
140 "The most common settings for this will be either 1, for",
141 "systems which require new user validation by the system",
142 "administrator ('sysop' is a word for people who run DOS",
143 "boards!), or 4, for systems which give instant access.",
144 "The current access levels available are:",
145
146 "11",
147 "'Registration' refers to the boring part of logging into a BBS for the first",
148 "time: typing your name, address, and telephone number.  Set this value to 1",
149 "to automatically do registration for new users, or 0 to not auto-register.",
150 "Optionally, you could set it to, say, 2, to auto-register on a user's second",
151 "call, but there really isn't much point to doing this.  The recommended",
152 "value is 1 if you've set your inital access level to 1, or 0 if you've set",
153 "your initial access level to something higher.",
154
155 "12",
156 "Every BBS has its share of problem users.  This is one",
157 "good way to deal with them: if you enable this option,",
158 "anyone you flag as a 'problem user' (access level 2) can",
159 "post anywhere they want, but their messages will all be",
160 "automatically moved to a room of your choosing.  Set this",
161 "value to 1 to enable Twit Detect, or 0 to disable it.",
162
163 "13",
164 "This is the name of the room that problem user messages",
165 "get moved to if you have Twit Detect enabled.",
166 "(Note: don't forget to *create* this room!)",
167
168 "14",
169 "This is the maximum number of concurrent Citadel sessions which may be",
170 "running at any given time.  Use this to keep very busy systems from being",
171 "overloaded.",
172 "  Set this value to 0 to allow an unlimited number of sessions.",
173
174 "15",
175 "This is the prompt that appears after each screenful of",
176 "text - for users that have chosen that option.  Usually",
177 "a simple '<more>' will do, but some folks like to be",
178 "creative...",
179
180 "16",
181 "If you have a gateway set up to allow Citadel users to",
182 "send Internet mail, with sendmail, qmail, or whatever, and",
183 "you wish to restrict this to only users to whom you have",
184 "given this privilege, set this flag to 1.  Otherwise, set",
185 "it to 0 to allow everyone to send Internet mail.",
186 "(Obviously, if your system doesn't have the ability to",
187 "send mail to the outside world, this is all irrelevant.)",
188
189 "17",
190 "Select the name of a subdirectory (relative to the main",
191 "Citadel directory - do not type an absolute pathname!) in",
192 "which to place arriving file transfers that otherwise",
193 "don't have a home.",
194
195 "18",
196 "If you use Citadel client/server sessions to transport network spool data",
197 "between systems, this is the password other systems will use to authenticate",
198 "themselves as network nodes rather than regular callers.",
199
200 "19",
201 "Specify the TCP port number on which your server will run.  Normally, this",
202 "will be port 504, which is the official port assigned by the IANA for",
203 "Citadel servers.  You'll only need to specify a different port number if",
204 "you run multiple BBS's on the same computer and there's something else",
205 "already using port 504.",
206
207 "20",
208 "21",
209 "22",
210 "23",
211 "24",
212 "25",
213 "26",
214 "27",
215 "28",
216 "29",
217 "30",
218
219 "31",
220 "Setup has detected that you currently have data files from a Citadel/UX",
221 "version 3.2x installation.  The program 'conv_32_40' can upgrade your",
222 "files to version 4.0x format.",
223 " Setup will now exit.  Please either run 'conv_32_40' or delete your data",
224 "files, and run setup again.",
225
226 "32",
227
228 };
229
230 struct config config;
231 int direction;
232
233 void cleanup(int exitcode) {
234 #ifdef CURSES_INC
235         if (setup_type == UI_CURSES) {
236                 clear();
237                 refresh();
238                 endwin();
239                 }
240 #endif
241
242         /* Do an 'init q' if we need to.  When we hit the right one, init
243          * will take over and setup won't come back because we didn't do a
244          * fork().  If init isn't found, we fall through the bottom of the
245          * loop and setup exits quietly.
246          */
247         if (need_init_q) {
248                 execlp("/sbin/init", "init", "q", NULL);
249                 execlp("/usr/sbin/init", "init", "q", NULL);
250                 execlp("/bin/init", "init", "q", NULL);
251                 execlp("/usr/bin/init", "init", "q", NULL);
252                 execlp("init", "init", "q", NULL);
253                 }
254
255         exit(exitcode);
256         }
257
258
259 #ifdef CURSES_INC
260 void getlin(int yp, int xp, char *string, int lim)      /* Gets a line from the terminal */
261                                 /* Where on the screen to start */
262                                 /* Pointer to string buffer */
263                                 /* Maximum length - if negative, no-show */
264 {
265 int a,b; char flag;
266
267         flag=0;
268         if (lim<0) { lim=(0-lim); flag=1; }
269         move(yp,xp);
270         standout();
271         for (a=0; a<lim; ++a) addch('-');
272         refresh();
273         move(yp,xp);
274         for (a=0; a<lim; ++a) addch(' ');
275         move(yp,xp);
276         printw("%s", string);
277 GLA:    move(yp,xp+strlen(string));
278         refresh();
279         a=getch();
280         if (a==127) a=8;
281         a=(a&127);
282         if (a==10) a=13;
283         if ((a==8)&&(strlen(string)==0)) goto GLA;
284         if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
285         if ((a==8)&&(string[0]!=0)) {
286                 string[strlen(string)-1]=0;
287                 move(yp,xp+strlen(string));
288                 addch(' ');
289                 goto GLA;
290                 }
291         if ((a==13)||(a==10)) {
292                 standend();
293                 move(yp,xp);
294                 for (a=0; a<lim; ++a) addch(' ');
295                 mvprintw(yp,xp,"%s",string);
296                 refresh();
297                 return;
298                 }
299         b=strlen(string);
300         string[b]=a;
301         string[b+1]=0;
302         if (flag==0) addch(a);
303         if (flag==1) addch('*');
304         goto GLA;
305 }
306 #endif
307
308
309
310 void title(char *text)
311 {
312         if (setup_type == UI_TEXT) {
313                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
314                 }
315         }
316
317
318 void hit_any_key(void) {
319         char junk[5];
320
321 #ifdef CURSES_INC
322         if (setup_type == UI_CURSES) {
323                 mvprintw(20, 0, "Press any key to continue... ");
324                 refresh();
325                 getch();
326                 return;
327                 }
328 #endif
329         printf("Press return to continue...");
330         fgets(junk, 5, stdin);
331         }
332
333 int yesno(char *question)
334 {
335         int answer = 0;
336         char buf[4096];
337
338         switch(setup_type) {
339
340                 case UI_TEXT:
341                         do {
342                                 printf("%s\nYes/No --> ",question);
343                                 fgets(buf, 4096, stdin);
344                                 answer=tolower(buf[0]);
345                                 if (answer=='y') answer=1;
346                                 else if (answer=='n') answer=0;
347                                 } while ((answer<0)||(answer>1));
348                         break;
349
350                 case UI_DIALOG:
351                         sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
352                         answer = ( (system(buf)==0) ? 1 : 0);
353                         break;
354 #ifdef CURSES_INC
355                 case UI_CURSES:
356                         do {
357                                 clear();
358                                 standout();
359                                 mvprintw(1, 20, "Question");
360                                 standend();
361                                 mvprintw(10, 0, "%-80s", question);
362                                 mvprintw(20, 0, "%80s", "");
363                                 mvprintw(20, 0, "Yes/No -> ");
364                                 refresh();
365                                 answer = getch();
366                                 answer=tolower(answer);
367                                 if (answer=='y') answer=1;
368                                 else if (answer=='n') answer=0;
369                                 } while ((answer<0)||(answer>1));
370                         break;
371 #endif
372
373                 }
374         return(answer);
375         }
376
377
378
379 void dump_access_levels(void) {
380         int a;
381         for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
382         }
383
384 void get_setup_msg(char *dispbuf, int msgnum) {
385         int a,b;
386
387         a=0;
388         b=0;
389         while (atol(setup_text[a]) != msgnum) ++a;
390         ++a;
391         strcpy(dispbuf, "");
392         do {
393                 strcat(dispbuf, setup_text[a++]);
394                 strcat(dispbuf, "\n");
395                 } while(atol(setup_text[a])!=(msgnum+1));
396         }
397
398 void print_setup(int msgnum) {
399         char dispbuf[4096];
400
401         get_setup_msg(dispbuf, msgnum);
402         printf("\n\n%s\n\n", dispbuf);
403         }
404
405
406 void important_message(char *title, char *msgtext) {
407         char buf[4096];
408
409         switch(setup_type) {
410                 
411                 case UI_TEXT:
412                         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");
413                         printf("       %s \n\n%s\n\n", title, msgtext);
414                         hit_any_key();
415                         break;
416
417                 case UI_DIALOG:
418                         sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
419                                 title, msgtext);
420                         system(buf);
421                         break;
422 #ifdef CURSES_INC
423                 case UI_CURSES:
424                         clear();
425                         move(1, 20);
426                         standout();
427                         printw("  Important Message  ");
428                         standend();
429                         move(3, 0);
430                         printw("%s", msgtext);
431                         refresh();
432                         hit_any_key();
433                         break;
434 #endif
435
436                 }
437         }
438
439 void important_msgnum(int msgnum) {
440         char dispbuf[4096];
441         
442         get_setup_msg(dispbuf, msgnum);
443         important_message("Important Message", dispbuf);
444         }
445
446 void display_error(char *error_message) {
447         important_message("Error", error_message);
448         }
449
450 void progress(char *text, long int curr, long int cmax)
451 {
452         static long dots_printed;
453         long a;
454         static long prev;
455         static FILE *gauge = NULL;
456         char gcmd[256];
457
458         switch(setup_type) {
459
460                 case UI_TEXT:
461                         if (curr==0) {
462                                 printf("%s\n",text);
463                                 printf("..........................");
464                                 printf("..........................");
465                                 printf("..........................\r");
466                                 fflush(stdout);
467                                 dots_printed = 0;
468                                 }
469                         else if (curr==cmax) {
470                                 printf("\r%79s\n","");
471                                 }
472                         else {
473                                 a=(curr * 100) / cmax;
474                                 a=a*78; a=a/100;
475                                 while (dots_printed < a) {
476                                         printf("*");
477                                         ++dots_printed;
478                                         fflush(stdout);
479                                         }
480                                 }
481                         break;
482
483 #ifdef CURSES_INC
484                 case UI_CURSES:
485                         if (curr==0) {
486                                 clear();
487                                 move(5, 20);
488                                 printw("%s\n",text);
489                                 move(10, 1);
490                                 printf("..........................");
491                                 printf("..........................");
492                                 printf("..........................\r");
493                                 refresh();
494                                 dots_printed = 0;
495                                 }
496                         else if (curr==cmax) {
497                                 clear();
498                                 refresh();
499                                 }
500                         else {
501                                 a=(curr * 100) / cmax;
502                                 a=a*78; a=a/100;
503                                 move(10,1);
504                                 dots_printed = 0;
505                                 while (dots_printed < a) {
506                                         printw("*");
507                                         ++dots_printed;
508                                         }
509                                 refresh();
510                                 }
511                         break;
512 #endif
513                         
514                 case UI_DIALOG:
515                         if ( (curr == 0) && (gauge == NULL) ) {
516                                 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
517                                         text);
518                                 gauge = (FILE *) popen(gcmd, "w");
519                                 prev = 0;
520                                 }
521                         else if (curr==cmax) {
522                                 fprintf(gauge, "100\n");
523                                 pclose(gauge);
524                                 gauge = NULL;
525                                 }
526                         else {
527                                 a=(curr * 100) / cmax;
528                                 if (a != prev) {
529                                         fprintf(gauge, "%ld\n", a);
530                                         fflush(gauge);
531                                         }
532                                 prev = a;
533                                 }
534                         break;
535                 }
536         }
537
538
539
540 /*
541  * check_services_entry()  -- Make sure "citadel" is in /etc/services
542  *
543  */
544 void check_services_entry(void) {
545         char question[128];
546         FILE *sfp;
547
548         sprintf(question,
549 "There is no '%s' entry in /etc/services.  Would you like to add one?",
550                 SERVICE_NAME);
551
552         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
553                 if (yesno(question)==1) {
554                         sfp = fopen("/etc/services", "a");
555                         if (sfp == NULL) {
556                                 display_error(strerror(errno));
557                                 }
558                         else {
559                                 fprintf(sfp, "%s                504/tcp\n",
560                                         SERVICE_NAME);
561                                 fclose(sfp);
562                                 }
563                         }
564                 }
565
566         }
567
568
569 /*
570  * check_inittab_entry()  -- Make sure "citadel" is in /etc/inittab
571  *
572  */
573 void check_inittab_entry(void) {
574         FILE *infp;
575         char buf[256];
576         char looking_for[256];
577         char question[128];
578         char *ptr;
579         int have_entry = 0;
580         char entryname[3];
581
582         /* Determine the fully qualified path name of citserver */
583         sprintf(looking_for, "%s/citserver ", BBSDIR);
584
585         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
586          * an entry is found which we believe starts citserver.
587          */
588         infp = fopen("/etc/inittab", "r");
589         if (infp == NULL) {
590                 display_error(strerror(errno));
591                 }
592         else {
593                 while (fgets(buf, 256, infp) != NULL) {
594                         buf[strlen(buf) - 1] = 0;
595                         ptr = strtok(buf, ":");
596                         ptr = strtok(NULL, ":");
597                         ptr = strtok(NULL, ":");
598                         ptr = strtok(NULL, ":");
599                         if (ptr != NULL) {
600                          if (!strncmp(ptr, looking_for, strlen(looking_for))) {
601                                         ++have_entry;
602                                         }
603                                 }
604                         }
605                 fclose(infp);
606                 }
607
608         /* If there's already an entry, then we have nothing left to do. */
609         if (have_entry > 0) return;
610
611         /* Otherwise, prompt the user to create an entry. */
612         sprintf(question,
613 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
614                 looking_for);
615         if (yesno(question)==0) return;
616
617         /* Generate a unique entry name for /etc/inittab */
618         sprintf(entryname, "c0");
619         do {
620                 ++entryname[1];
621                 if (entryname[1] > '9') {
622                         entryname[1] = 0;
623                         ++entryname[0];
624                         if (entryname[0] > 'z') {
625                                 display_error(
626                                         "Can't generate a unique entry name");
627                                 return;
628                                 }
629                         }
630                 sprintf(buf,
631                         "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
632                 } while(system(buf)==0);
633
634         /* Now write it out to /etc/inittab */
635         infp = fopen("/etc/inittab", "a");
636         if (infp == NULL) {
637                 display_error(strerror(errno));
638                 }
639         else {
640                 fprintf(infp, "# Start the Citadel/UX server...\n");
641                 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
642                         entryname, looking_for, setup_directory);
643                 fclose(infp);
644                 need_init_q = 1;
645                 }
646         }
647
648
649
650 void set_str_val(int msgpos, char str[]) {
651         char buf[4096];
652         char setupmsg[4096];
653         char tempfile[64];
654         FILE *fp;
655
656         sprintf(tempfile, "/tmp/setup.%d", getpid());
657
658         switch (setup_type) {
659                 case UI_TEXT:
660                         title(setup_titles[msgpos]);
661                         print_setup(msgpos);
662                         if (msgpos==11) dump_access_levels();
663                         printf("This is currently set to:\n%s\n",str);
664                         printf("Enter new value or press return to leave unchanged:\n");
665                         fgets(buf, 4096, stdin);
666                         buf[strlen(buf)-1] = 0;
667                         if (strlen(buf)!=0) strcpy(str,buf);
668                         break;
669                 case UI_DIALOG:
670                         get_setup_msg(setupmsg, msgpos);
671                         sprintf(buf,
672                                 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
673                                 setup_titles[msgpos],
674                                 setupmsg,
675                                 str, tempfile);
676                         if (system(buf)==0) {
677                                 fp = fopen(tempfile, "rb");
678                                 fgets(str, 4095, fp);
679                                 fclose(fp);
680                                 if (strlen(str)>0) 
681                                         if (str[strlen(str)-1]==10)
682                                                 str[strlen(str)-1]=0;
683                                 }
684                         break;
685 #ifdef CURSES_INC
686                 case UI_CURSES:
687                         clear();
688                         move(1, ((80-strlen(setup_titles[msgpos]))/2) );
689                         standout();
690                         printw("%s", setup_titles[msgpos]);
691                         standend();
692                         move(3, 0);
693                         get_setup_msg(setupmsg, msgpos);
694                         printw("%s", setupmsg);
695                         refresh();
696                         getlin(20, 0, str, 80);
697                         break;
698 #endif
699                 }
700         }
701
702 void set_int_val(int msgpos, int *ip)
703 {
704         char buf[16];
705         sprintf(buf,"%d",(int)*ip);
706         set_str_val(msgpos, buf);
707         *ip = atoi(buf);
708         }
709
710
711 void set_char_val(int msgpos, char *ip)
712 {
713         char buf[16];
714         sprintf(buf,"%d",(int)*ip);
715         set_str_val(msgpos, buf);
716         *ip = (char)atoi(buf);
717         }
718
719
720 void set_long_val(int msgpos, long int *ip)
721 {
722         char buf[16];
723         sprintf(buf,"%ld",*ip);
724         set_str_val(msgpos, buf);
725         *ip = atol(buf);
726         }
727
728
729 #if 0 /* FIXME: this isn't used, can we remove it? */
730 int yesno_s(char *question) {
731         int a;
732         char buf[4096];
733         char tempfile[64];
734         FILE *fp;
735
736         sprintf(tempfile, "/tmp/setup.%d", getpid());
737         switch (setup_type) {
738         
739                 case UI_TEXT:
740                         a=yesno(question);
741                         if (a==1) a=yesno("Are you SURE you want to reinitialize this file? ");
742                         return(a);
743                         break;
744
745                 case UI_CURSES:
746                         a=yesno(question);
747                         if (a==1) a=yesno("Are you SURE you want to reinitialize this file? ");
748                         return(a);
749                         break;
750
751                 case UI_DIALOG:
752                         a = yesno(question);
753                         if (a==0) return(a);
754                         sprintf(buf, "dialog --title \"Confirm file overwrite\" --menu \"\nAre you SURE you want to reinitialize this file?\n\" 13 80 2 NO \"No, don't overwrite\" YES \"Yes, overwrite the existing file\" 2>%s", tempfile);
755                         a = system(buf);
756                         if (a != 0) return(0);
757                         fp = fopen(tempfile, "rb");
758                         fgets(buf, 4095, fp);
759                         fclose(fp);
760                         if (strlen(buf)>0) 
761                                 if (buf[strlen(buf)-1]==10)
762                                         buf[strlen(buf)-1]=0;
763                         return( (!strcmp(buf, "YES")) ? 1 : 0 );
764                         break;
765
766                 }
767
768         return(0); /* just in case */
769         }
770 #endif
771
772
773 void edit_value(int curr)
774 {
775  int a;
776  
777  switch(curr) {
778
779 case 1:
780         set_str_val(curr, config.c_nodename);
781         break;
782
783 case 2:
784         set_str_val(curr, config.c_fqdn);
785         break;
786
787 case 3:
788         set_str_val(curr, config.c_humannode);
789         break;
790
791 case 4:
792         set_str_val(curr, config.c_phonenum);
793         break;
794
795 case 5:
796         set_str_val(curr, config.c_bbs_city);
797         break;
798
799 case 6:
800         set_str_val(curr, config.c_sysadm);
801         break;
802
803 case 7:
804         set_int_val(curr, &config.c_bbsuid);
805         break;
806
807 case 8:
808         set_char_val(curr, &config.c_creataide);
809         break;
810
811 case 9:
812         set_int_val(curr, &config.c_sleeping);
813         break;
814
815 case 10:
816         set_char_val(curr, &config.c_initax);
817         break;
818
819 case 11:
820         set_char_val(curr, &config.c_regiscall);
821         break;
822
823 case 12:
824         set_char_val(curr, &config.c_twitdetect);
825         break;
826
827 case 13:
828         set_str_val(curr, config.c_twitroom);
829         break;
830
831 case 14:
832         set_int_val(curr, &config.c_maxsessions);
833         break;
834
835 case 15:
836         set_str_val(curr, config.c_moreprompt);
837         break;
838
839 case 16:
840         set_char_val(curr, &config.c_restrict);
841         break;
842
843 case 17:
844         set_str_val(curr, config.c_bucket_dir);
845         config.c_bucket_dir[14] = 0;
846         for (a=0; a<strlen(config.c_bucket_dir); ++a)
847                 if (!isalpha(config.c_bucket_dir[a]))
848                         strcpy(&config.c_bucket_dir[a],
849                                 &config.c_bucket_dir[a+1]);
850         break;
851
852 case 18:
853         set_str_val(curr, config.c_net_password);
854         break;
855
856 case 19:
857         set_int_val(curr, &config.c_port_number);
858         break;
859
860
861  }
862 }
863
864 /*
865  * (re-)write the config data to disk
866  */
867 void write_config_to_disk(void) {
868         FILE *fp;
869
870         fp=fopen("citadel.config","wb");
871         if (fp==NULL) {
872                 display_error("setup: cannot open citadel.config");
873                 cleanup(1);
874                 }
875         fwrite((char *)&config,sizeof(struct config),1,fp);
876         fclose(fp);
877         }
878
879
880
881
882 /*
883  * Figure out what type of user interface we're going to use
884  */
885 int discover_ui(void) {
886
887 #ifdef CURSES_INC
888         return UI_CURSES;
889 #endif
890
891         if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
892                 return UI_DIALOG;
893                 }
894
895         return UI_TEXT;
896         }
897
898
899
900
901
902 void main(int argc, char *argv[]) {
903         int a;
904         int curr;
905         char aaa[128];
906         FILE *fp;
907         int old_setup_level = 0;
908         int info_only = 0;
909
910         /* set an invalid setup type */
911         setup_type = (-1);
912
913         /* parse command line args */
914         for (a=0; a<argc; ++a) {
915                 if (!strncmp(argv[a], "-u", 2)) {
916                         strcpy(aaa, argv[a]);
917                         strcpy(aaa, &aaa[2]);
918                         setup_type = atoi(aaa);
919                         }
920                 if (!strcmp(argv[a], "-i")) {
921                         info_only = 1;
922                         }
923                 }
924
925
926         /* If a setup type was not specified, try to determine automatically
927          * the best one to use out of all available types.
928          */
929         if (setup_type < 0) {
930                 setup_type = discover_ui();
931                 }
932
933 #ifdef CURSES_INC
934         if (setup_type == UI_CURSES) {
935                 initscr();
936                 raw();
937                 noecho();
938                 }
939 #endif
940
941         if (info_only == 1) {
942                 important_message("Citadel/UX Setup", CITADEL);
943                 cleanup(0);
944                 }
945
946         strcpy(setup_directory, BBSDIR);
947         set_str_val(0, setup_directory);
948         if (chdir(setup_directory) != 0) {
949                 important_message("Citadel/UX Setup",
950                         "The directory you specified does not exist.");
951                 cleanup(errno);
952                 }
953
954
955         switch(setup_type) {
956                 
957                 case UI_TEXT:
958                         printf("\n\n\n               *** Citadel/UX setup program ***\n\n");
959                         break;
960                 
961                 case UI_DIALOG:
962                         system("exec clear");
963                         break;
964                         
965                 }
966
967         /*
968          * What we're going to try to do here is append a whole bunch of
969          * nulls to the citadel.config file, so we can keep the old config
970          * values if they exist, but if the file is missing or from an
971          * earlier version with a shorter config structure, when setup tries
972          * to read the old config parameters, they'll all come up zero.
973          * The length of the config file will be set to what it's supposed
974          * to be when we rewrite it, because we replace the old file with a
975          * completely new copy.  (Neat, eh?)
976          */
977
978         fp=fopen("citadel.config","ab");
979         if (fp==NULL) {
980                 display_error("setup: cannot append citadel.config");
981                 cleanup(errno);
982                 }
983         for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
984         fclose(fp);
985
986         /* now we re-open it, and read the old or blank configuration */
987         fp=fopen("citadel.config","rb");
988         if (fp==NULL) {
989                 display_error("setup: cannot open citadel.config");
990                 cleanup(errno);
991                 }
992         fread((char *)&config,sizeof(struct config),1,fp);
993         fclose(fp);
994
995
996         /* set some sample/default values in place of blanks... */
997         if (strlen(config.c_nodename)==0)
998                 strcpy(config.c_nodename,"mysystem");
999         if (strlen(config.c_fqdn)==0)
1000                 sprintf(config.c_fqdn,"%s.UUCP",config.c_nodename);
1001         if (strlen(config.c_humannode)==0)
1002                 strcpy(config.c_humannode,"My System");
1003         if (strlen(config.c_phonenum)==0)
1004                 strcpy(config.c_phonenum,"US 800 555 1212");
1005         if (config.c_initax == 0)
1006                 config.c_initax = 1;
1007         if (strlen(config.c_moreprompt)==0)
1008                 strcpy(config.c_moreprompt,"<more>");
1009         if (strlen(config.c_twitroom)==0)
1010                 strcpy(config.c_twitroom,"Trashcan");
1011         if (strlen(config.c_bucket_dir)==0)
1012                 strcpy(config.c_bucket_dir,"bitbucket");
1013         if (strlen(config.c_net_password)==0)
1014                 strcpy(config.c_net_password,"netpassword");
1015         if (config.c_port_number == 0) {
1016                 config.c_port_number = 504;
1017                 }
1018         if (config.c_ipgm_secret == 0) {
1019                 srand(getpid());
1020                 config.c_ipgm_secret = rand();
1021                 }
1022         if (config.c_sleeping == 0) {
1023                 config.c_sleeping = 900;
1024                 }
1025
1026         /* Go through a series of dialogs prompting for config info */
1027         for (curr = 1; curr <= MAXSETUP; ++curr) {
1028                 edit_value(curr);
1029                 }
1030
1031         /*
1032         if (setuid(config.c_bbsuid) != 0) {
1033                 important_message("Citadel/UX Setup",
1034                         "Failed to change the user ID to your BBS user.");
1035                 cleanup(errno);
1036                 }
1037         */
1038
1039         /***** begin version update section ***** */
1040         /* take care of any updating that is necessary */
1041
1042         old_setup_level = config.c_setup_level;
1043
1044         if (old_setup_level == 0) goto NEW_INST;
1045         
1046         if (old_setup_level < 323) {
1047                 important_message("Citadel/UX Setup",
1048                         "This Citadel/UX installation is too old to be upgraded.");
1049                 cleanup(1);
1050                 }
1051
1052         write_config_to_disk();
1053
1054         if ((config.c_setup_level / 10) == 32) {
1055                 important_msgnum(31);
1056                 cleanup(0);
1057                 }
1058
1059         if (config.c_setup_level < 400) {
1060                 config.c_setup_level = 400;
1061                 }
1062
1063         /* end of 3.23 -> 4.00 update section */
1064
1065         /* end of 4.00 -> 4.02 update section */
1066
1067         old_setup_level = config.c_setup_level;
1068
1069         /* end of version update section */
1070
1071 NEW_INST:
1072         config.c_setup_level = REV_LEVEL;
1073         write_config_to_disk();
1074
1075         system("mkdir info 2>/dev/null");               /* Create these */
1076         system("mkdir bio 2>/dev/null");
1077         system("mkdir userpics 2>/dev/null");
1078         system("mkdir messages 2>/dev/null");
1079         system("mkdir help 2>/dev/null");
1080         system("mkdir images 2>/dev/null");
1081         sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1082         system(aaa);
1083
1084
1085         system("rm -fr ./chatpipes 2>/dev/null");       /* Don't need these */
1086         system("rm -fr ./expressmsgs 2>/dev/null");
1087         unlink("sessions");
1088
1089         check_services_entry();         /* Check /etc/services */
1090         check_inittab_entry();          /* Check /etc/inittab */
1091
1092         progress("Setting file permissions", 0, 3);
1093         chown(".", config.c_bbsuid, getgid());
1094         progress("Setting file permissions", 1, 3);
1095         chown("citadel.config", config.c_bbsuid, getgid());
1096         progress("Setting file permissions", 2, 3);
1097         sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1098                 config.c_bbsuid);
1099         system(aaa);
1100         progress("Setting file permissions", 3, 3);
1101
1102         important_message("Setup finished", 
1103                 "Setup is finished.  You may now start the Citadel server.");
1104
1105
1106         cleanup(0);
1107 }