* added RCS Id keyword strings to sources
[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
27 #ifdef HAVE_CURSES_H
28 # ifdef OK
29 # undef OK
30 # endif
31 #include <curses.h>
32 #endif
33
34 #define MAXSETUP 19
35
36 #define UI_TEXT         0       /* Default setup type -- text only */
37 #define UI_DIALOG       1       /* Use the 'dialog' program */
38 #define UI_CURSES       2       /* Use curses */
39
40 #define SERVICE_NAME    "citadel"
41 #define PROTO_NAME      "tcp"
42
43 int setup_type;
44 char setup_directory[128];
45 int need_init_q = 0;
46
47 char *setup_titles[] = {
48         "BBS Home Directory",
49         "Citadel node name",
50         "Fully Qualified Domain Name (FQDN)",
51         "Human-readable node name",
52         "Phone number",
53         "BBS City and State",
54         "System Administrator",
55         "BBS User ID",
56         "'Room Creator = Room Aide' flag",
57         "Server timeout period",
58         "Initial access level",
59         "Registration requirements",
60         "Twit Detect!",
61         "Twit Detect target room",
62         "Maximum concurrent sessions",
63         "Paginator prompt",
64         "Restrict Internet mail flag",
65         "Name of bit bucket subdirectory",
66         "System net password",
67         "Server port number",
68         };
69
70
71 char *setup_text[] = {
72
73 "0",
74 "Enter the full pathname of the directory in which the BBS you are",
75 "creating or updating resides.  If you specify a directory other than the",
76 "default, you will need to specify the -h flag to the server when you start",
77 "it up.",
78
79 "1",
80 "This is the name your system is known by on a Citadel/UX network.  It",
81 "should be 8 characters or less, and should generally be comprised only of",
82 "letters.  You can also use numbers and hyphens if necessary.",
83
84 "2",
85 "This is the name your system is known by on the Internet.",
86 "If you're not on the Internet, simply set this to your",
87 "node name followed by '.local' or something similar.",
88
89 "3",
90 "This is a longer description of your system, readable by",
91 "us mere humans.  It can be up to 20 characters long and it",
92 "can have spaces in it.  Note that if you are part of a",
93 "Cit86Net, this is the name your system will be known by on",
94 "that network.",
95
96 "4",
97 "This is the main dialup number for your system.  If yours",
98 "can not be dialed into, then make one up!  It should be in",
99 "the format 'US 000 000 0000' - the US is your country code",
100 "(look it up if you're not in the United States) and the",
101 "rest is, of course, your area code and phone number.",
102 "This doesn't have any use in Citadel/UX, but gateways to",
103 "other networks may require it, and someday we may use this",
104 "to have the networker automatically build a BBS list.",
105
106 "5",
107 "Enter the geographical location of your system (city and",
108 "state/province/country etc.)",
109
110 "6",
111 "Enter the name of the system administrator (which is probably you).",
112 "When an account is created with this name, it will automatically be",
113 "assigned the highest access level.",
114
115 "7",
116 "You should create a user called 'bbs', 'guest', 'citadel', or something",
117 "similar, that will allow users a way into your BBS.  The server will run",
118 "under this user ID.  Please specify that (numeric) user ID here.",
119
120 "8",
121 "This is a boolean value.  If you set it to 1, anyone who",
122 "creates a class 3 (passworded) or class 4 (invitation",
123 "only) room will automatically become the Room Aide for",
124 "that room, allowing them to edit it, delete/move messages,",
125 "etc.  This is an administrative decision: it works well on",
126 "some systems, and not so well on others.  Set this to 0 to",
127 "disable this function.",
128
129 "9",
130 "This setting specifies how long a server session may sit idle before it is",
131 "automatically terminated.  The recommended value is 900 seconds (15",
132 "minutes).  Note that this has *nothing* to do with any watchdog timer that",
133 "is presented to the user.  The server's timeout is intended to kill idle or",
134 "zombie sessions running on a network, etc.  ",
135 "You MUST set this to a reasonable value.  Setting it to zero will cause",
136 "the server to malfunction.",
137
138 "10",
139 "This is the access level new users are assigned.",
140 "",
141 "The most common settings for this will be either 1, for",
142 "systems which require new user validation by the system",
143 "administrator, 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 HAVE_CURSES_H
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 HAVE_CURSES_H
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 HAVE_CURSES_H
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 HAVE_CURSES_H
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 HAVE_CURSES_H
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 HAVE_CURSES_H
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 HAVE_CURSES_H
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 void edit_value(int curr)
730 {
731  int a;
732  
733  switch(curr) {
734
735 case 1:
736         set_str_val(curr, config.c_nodename);
737         break;
738
739 case 2:
740         set_str_val(curr, config.c_fqdn);
741         break;
742
743 case 3:
744         set_str_val(curr, config.c_humannode);
745         break;
746
747 case 4:
748         set_str_val(curr, config.c_phonenum);
749         break;
750
751 case 5:
752         set_str_val(curr, config.c_bbs_city);
753         break;
754
755 case 6:
756         set_str_val(curr, config.c_sysadm);
757         break;
758
759 case 7:
760         set_int_val(curr, &config.c_bbsuid);
761         break;
762
763 case 8:
764         set_char_val(curr, &config.c_creataide);
765         break;
766
767 case 9:
768         set_int_val(curr, &config.c_sleeping);
769         break;
770
771 case 10:
772         set_char_val(curr, &config.c_initax);
773         break;
774
775 case 11:
776         set_char_val(curr, &config.c_regiscall);
777         break;
778
779 case 12:
780         set_char_val(curr, &config.c_twitdetect);
781         break;
782
783 case 13:
784         set_str_val(curr, config.c_twitroom);
785         break;
786
787 case 14:
788         set_int_val(curr, &config.c_maxsessions);
789         break;
790
791 case 15:
792         set_str_val(curr, config.c_moreprompt);
793         break;
794
795 case 16:
796         set_char_val(curr, &config.c_restrict);
797         break;
798
799 case 17:
800         set_str_val(curr, config.c_bucket_dir);
801         config.c_bucket_dir[14] = 0;
802         for (a=0; a<strlen(config.c_bucket_dir); ++a)
803                 if (!isalpha(config.c_bucket_dir[a]))
804                         strcpy(&config.c_bucket_dir[a],
805                                 &config.c_bucket_dir[a+1]);
806         break;
807
808 case 18:
809         set_str_val(curr, config.c_net_password);
810         break;
811
812 case 19:
813         set_int_val(curr, &config.c_port_number);
814         break;
815
816
817  }
818 }
819
820 /*
821  * (re-)write the config data to disk
822  */
823 void write_config_to_disk(void) {
824         FILE *fp;
825
826         fp=fopen("citadel.config","wb");
827         if (fp==NULL) {
828                 display_error("setup: cannot open citadel.config");
829                 cleanup(1);
830                 }
831         fwrite((char *)&config,sizeof(struct config),1,fp);
832         fclose(fp);
833         }
834
835
836
837
838 /*
839  * Figure out what type of user interface we're going to use
840  */
841 int discover_ui(void) {
842
843 #ifdef HAVE_CURSES_H
844         return UI_CURSES;
845 #endif
846
847         if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
848                 return UI_DIALOG;
849                 }
850
851         return UI_TEXT;
852         }
853
854
855
856
857
858 int main(int argc, char *argv[]) {
859         int a;
860         int curr;
861         char aaa[128];
862         FILE *fp;
863         int old_setup_level = 0;
864         int info_only = 0;
865         struct utsname my_utsname;
866         struct passwd *pw;
867
868         /* set an invalid setup type */
869         setup_type = (-1);
870
871         /* parse command line args */
872         for (a=0; a<argc; ++a) {
873                 if (!strncmp(argv[a], "-u", 2)) {
874                         strcpy(aaa, argv[a]);
875                         strcpy(aaa, &aaa[2]);
876                         setup_type = atoi(aaa);
877                         }
878                 if (!strcmp(argv[a], "-i")) {
879                         info_only = 1;
880                         }
881                 }
882
883
884         /* If a setup type was not specified, try to determine automatically
885          * the best one to use out of all available types.
886          */
887         if (setup_type < 0) {
888                 setup_type = discover_ui();
889                 }
890
891 #ifdef HAVE_CURSES_H
892         if (setup_type == UI_CURSES) {
893                 initscr();
894                 raw();
895                 noecho();
896                 }
897 #endif
898
899         if (info_only == 1) {
900                 important_message("Citadel/UX Setup", CITADEL);
901                 cleanup(0);
902                 }
903
904         /* Get started in a valid setup directory. */
905         strcpy(setup_directory, BBSDIR);
906         set_str_val(0, setup_directory);
907         if (chdir(setup_directory) != 0) {
908                 important_message("Citadel/UX Setup",
909                         "The directory you specified does not exist.");
910                 cleanup(errno);
911                 }
912
913         /* Determine our host name, in case we need to use it as a default */
914         uname(&my_utsname);
915
916         /* Now begin. */
917         switch(setup_type) {
918                 
919                 case UI_TEXT:
920                         printf("\n\n\n               *** Citadel/UX setup program ***\n\n");
921                         break;
922                 
923                 case UI_DIALOG:
924                         system("exec clear");
925                         break;
926                         
927                 }
928
929         /*
930          * What we're going to try to do here is append a whole bunch of
931          * nulls to the citadel.config file, so we can keep the old config
932          * values if they exist, but if the file is missing or from an
933          * earlier version with a shorter config structure, when setup tries
934          * to read the old config parameters, they'll all come up zero.
935          * The length of the config file will be set to what it's supposed
936          * to be when we rewrite it, because we replace the old file with a
937          * completely new copy.  (Neat, eh?)
938          */
939
940         fp=fopen("citadel.config","ab");
941         if (fp==NULL) {
942                 display_error("setup: cannot append citadel.config");
943                 cleanup(errno);
944                 }
945         for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
946         fclose(fp);
947
948         /* now we re-open it, and read the old or blank configuration */
949         fp=fopen("citadel.config","rb");
950         if (fp==NULL) {
951                 display_error("setup: cannot open citadel.config");
952                 cleanup(errno);
953                 }
954         fread((char *)&config,sizeof(struct config),1,fp);
955         fclose(fp);
956
957
958         /* set some sample/default values in place of blanks... */
959         if (strlen(config.c_nodename)==0)
960                 strcpy(config.c_nodename, my_utsname.nodename);
961         if (strlen(config.c_fqdn)==0)
962                 sprintf(config.c_fqdn, "%s.local", my_utsname.nodename);
963         if (strlen(config.c_humannode)==0)
964                 strcpy(config.c_humannode,"My System");
965         if (strlen(config.c_phonenum)==0)
966                 strcpy(config.c_phonenum,"US 800 555 1212");
967         if (config.c_initax == 0)
968                 config.c_initax = 1;
969         if (strlen(config.c_moreprompt)==0)
970                 strcpy(config.c_moreprompt,"<more>");
971         if (strlen(config.c_twitroom)==0)
972                 strcpy(config.c_twitroom,"Trashcan");
973         if (strlen(config.c_bucket_dir)==0)
974                 strcpy(config.c_bucket_dir,"bitbucket");
975         if (strlen(config.c_net_password)==0)
976                 strcpy(config.c_net_password,"netpassword");
977         if (config.c_port_number == 0) {
978                 config.c_port_number = 504;
979                 }
980         if (config.c_ipgm_secret == 0) {
981                 srand(getpid());
982                 config.c_ipgm_secret = rand();
983                 }
984         if (config.c_sleeping == 0) {
985                 config.c_sleeping = 900;
986                 }
987         if (config.c_bbsuid == 0) {
988                 pw = getpwnam("citadel");
989                 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
990                 }
991         if (config.c_bbsuid == 0) {
992                 pw = getpwnam("bbs");
993                 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
994                 }
995         if (config.c_bbsuid == 0) {
996                 pw = getpwnam("guest");
997                 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
998                 }
999
1000         /* We need a system default message expiry policy, because this is
1001          * the top level and there's no 'higher' policy to fall back on.
1002          */
1003         if (config.c_ep.expire_mode == 0) {
1004                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
1005                 config.c_ep.expire_value = 150;
1006                 }
1007
1008         /* Go through a series of dialogs prompting for config info */
1009         for (curr = 1; curr <= MAXSETUP; ++curr) {
1010                 edit_value(curr);
1011                 }
1012
1013         /*
1014         if (setuid(config.c_bbsuid) != 0) {
1015                 important_message("Citadel/UX Setup",
1016                         "Failed to change the user ID to your BBS user.");
1017                 cleanup(errno);
1018                 }
1019         */
1020
1021         /***** begin version update section ***** */
1022         /* take care of any updating that is necessary */
1023
1024         old_setup_level = config.c_setup_level;
1025
1026         if (old_setup_level == 0) goto NEW_INST;
1027         
1028         if (old_setup_level < 323) {
1029                 important_message("Citadel/UX Setup",
1030                         "This Citadel/UX installation is too old to be upgraded.");
1031                 cleanup(1);
1032                 }
1033
1034         write_config_to_disk();
1035
1036         if ((config.c_setup_level / 10) == 32) {
1037                 important_msgnum(31);
1038                 cleanup(0);
1039                 }
1040
1041         if (config.c_setup_level < 400) {
1042                 config.c_setup_level = 400;
1043                 }
1044
1045         /* end of 3.23 -> 4.00 update section */
1046
1047         /* end of 4.00 -> 4.02 update section */
1048
1049         old_setup_level = config.c_setup_level;
1050
1051         /* end of version update section */
1052
1053 NEW_INST:
1054         config.c_setup_level = REV_LEVEL;
1055
1056 /******************************************/
1057
1058         write_config_to_disk();
1059
1060         system("mkdir info 2>/dev/null");               /* Create these */
1061         system("mkdir bio 2>/dev/null");
1062         system("mkdir userpics 2>/dev/null");
1063         system("mkdir messages 2>/dev/null");
1064         system("mkdir help 2>/dev/null");
1065         system("mkdir images 2>/dev/null");
1066         sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1067         system(aaa);
1068
1069         /* Delete a bunch of old files from Citadel v4; don't need anymore */
1070         system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
1071
1072         check_services_entry();         /* Check /etc/services */
1073         check_inittab_entry();          /* Check /etc/inittab */
1074
1075         progress("Setting file permissions", 0, 3);
1076         chown(".", config.c_bbsuid, getgid());
1077         progress("Setting file permissions", 1, 3);
1078         chown("citadel.config", config.c_bbsuid, getgid());
1079         progress("Setting file permissions", 2, 3);
1080         sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1081                 config.c_bbsuid);
1082         system(aaa);
1083         progress("Setting file permissions", 3, 3);
1084
1085         important_message("Setup finished", 
1086                 "Setup is finished.  You may now start the Citadel server.");
1087
1088
1089         cleanup(0);
1090         return 0;
1091 }