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