]> code.citadel.org Git - citadel.git/blob - citadel/setup.c
11dfd1054fd54474c148087dda497e135d213529
[citadel.git] / citadel / setup.c
1 /*
2  * $Id$
3  *
4  * Citadel/UX setup utility
5  *
6  */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/utsname.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <netdb.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <pwd.h>
23
24 #include "citadel.h"
25 #include "axdefs.h"
26 #include "sysdep.h"
27 #include "config.h"
28 #include "tools.h"
29
30 #ifdef HAVE_NEWT
31 #include <newt.h>
32 #endif
33
34
35 #define MAXSETUP 3      /* How many setup questions to ask */
36
37 #define UI_TEXT         0       /* Default setup type -- text only */
38 #define UI_SILENT       3       /* Silent running, for use in scripts */
39 #define UI_NEWT         4       /* Use the "newt" window library */
40
41 #define SERVICE_NAME    "citadel"
42 #define PROTO_NAME      "tcp"
43
44 int setup_type;
45 char setup_directory[SIZ];
46 char init_entry[SIZ];
47
48 char *setup_titles[] =
49 {
50         "Citadel Home Directory",
51         "System Administrator",
52         "Citadel User ID",
53         "Server port number",
54 };
55
56
57 char *setup_text[] =
58 {
59 "Enter the full pathname of the directory in which the Citadel installation\n"
60 "you are creating or updating resides.  If you specify a directory other\n"
61 "than the default, you will need to specify the -h flag to the server when\n"
62 "you start it up.\n",
63
64 "Enter the name of the system administrator (which is probably you).\n"
65 "When an account is created with this name, it will automatically be\n"
66 "assigned the highest access level.\n",
67
68 "Citadel needs to run under its own user ID.  This would typically be\n"
69 "called \"citadel\", but if you are running Citadel as a public BBS, you\n"
70 "might also call it \"bbs\" or \"guest\".  The server will run under this\n"
71 "user ID.  Please specify that user ID here.  You may specify either a\n"
72 "user name or a numeric UID.\n",
73
74 "Specify the TCP port number on which your server will run.  Normally, this\n"
75 "will be port 504, which is the official port assigned by the IANA for\n"
76 "Citadel servers.  You'll only need to specify a different port number if\n"
77 "you run multiple instances of Citadel on the same computer and there's\n"
78 "something else already using port 504.\n",
79
80 "Setup has detected that you currently have data files from a Citadel/UX\n"
81 "version 3.2x installation.  The program 'conv_32_40' can upgrade your\n"
82 "files to version 4.0x format.\n"
83 " Setup will now exit.  Please either run 'conv_32_40' or delete your data\n"
84 "files, and run setup again.\n"
85
86 };
87
88 struct config config;
89 int direction;
90
91 /*
92  * Set an entry in inittab to the desired state
93  */
94 void set_init_entry(char *which_entry, char *new_state) {
95         char *inittab = NULL;
96         FILE *fp;
97         char buf[SIZ];
98         char entry[SIZ];
99         char levels[SIZ];
100         char state[SIZ];
101         char prog[SIZ];
102
103         inittab = strdup("");
104         if (inittab == NULL) return;
105
106         fp = fopen("/etc/inittab", "r");
107         if (fp == NULL) return;
108
109         while(fgets(buf, sizeof buf, fp) != NULL) {
110
111                 if (num_tokens(buf, ':') == 4) {
112                         extract_token(entry, buf, 0, ':');
113                         extract_token(levels, buf, 1, ':');
114                         extract_token(state, buf, 2, ':');
115                         extract_token(prog, buf, 3, ':'); /* includes 0x0a LF */
116
117                         if (!strcmp(entry, which_entry)) {
118                                 strcpy(state, new_state);
119                                 sprintf(buf, "%s:%s:%s:%s",
120                                         entry, levels, state, prog);
121                         }
122                 }
123
124                 inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
125                 if (inittab == NULL) {
126                         fclose(fp);
127                         return;
128                 }
129                 
130                 strcat(inittab, buf);
131         }
132         fclose(fp);
133         fp = fopen("/etc/inittab", "w");
134         if (fp != NULL) {
135                 fwrite(inittab, strlen(inittab), 1, fp);
136                 fclose(fp);
137                 kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
138         }
139         free(inittab);
140 }
141
142
143
144
145 /* 
146  * Shut down the Citadel service if necessary, during setup.
147  */
148 void shutdown_service(void) {
149         FILE *infp;
150         char buf[SIZ];
151         char looking_for[SIZ];
152         int have_entry = 0;
153         char entry[SIZ];
154         char prog[SIZ];
155
156         strcpy(init_entry, "");
157
158         /* Determine the fully qualified path name of citserver */
159         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
160
161         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
162          * an entry is found which we believe starts citserver.
163          */
164         infp = fopen("/etc/inittab", "r");
165         if (infp == NULL) {
166                 return;
167         } else {
168                 while (fgets(buf, sizeof buf, infp) != NULL) {
169                         buf[strlen(buf) - 1] = 0;
170                         extract_token(entry, buf, 0, ':');      
171                         extract_token(prog, buf, 3, ':');
172                         if (!strncasecmp(prog, looking_for,
173                            strlen(looking_for))) {
174                                 ++have_entry;
175                                 strcpy(init_entry, entry);
176                         }
177                 }
178                 fclose(infp);
179         }
180
181         /* Bail out if there's nothing to do. */
182         if (!have_entry) return;
183
184         set_init_entry(init_entry, "off");
185 }
186
187
188 /*
189  * Start the Citadel service.
190  */
191 void start_the_service(void) {
192         if (strlen(init_entry) > 0) {
193                 set_init_entry(init_entry, "respawn");
194         }
195 }
196
197
198
199 void cleanup(int exitcode)
200 {
201 #ifdef HAVE_NEWT
202         newtCls();
203         newtRefresh();
204         newtFinished();
205 #endif
206         exit(exitcode);
207 }
208
209
210
211 void title(char *text)
212 {
213         if (setup_type == UI_TEXT) {
214                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
215         }
216 }
217
218
219
220 int yesno(char *question)
221 {
222 #ifdef HAVE_NEWT
223         newtComponent form = NULL;
224         newtComponent yesbutton = NULL;
225         newtComponent nobutton = NULL;
226         int i = 0;
227 #endif
228         int answer = 0;
229         char buf[SIZ];
230
231         switch (setup_type) {
232
233         case UI_TEXT:
234                 do {
235                         printf("%s\nYes/No --> ", question);
236                         fgets(buf, sizeof buf, stdin);
237                         answer = tolower(buf[0]);
238                         if (answer == 'y')
239                                 answer = 1;
240                         else if (answer == 'n')
241                                 answer = 0;
242                 } while ((answer < 0) || (answer > 1));
243                 break;
244
245 #ifdef HAVE_NEWT
246         case UI_NEWT:
247                 newtCenteredWindow(76, 10, "Question");
248                 form = newtForm(NULL, NULL, 0);
249                 for (i=0; i<num_tokens(question, '\n'); ++i) {
250                         extract_token(buf, question, i, '\n');
251                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
252                 }
253                 yesbutton = newtButton(10, 5, "Yes");
254                 nobutton = newtButton(60, 5, "No");
255                 newtFormAddComponent(form, yesbutton);
256                 newtFormAddComponent(form, nobutton);
257                 if (newtRunForm(form) == yesbutton) {
258                         answer = 1;
259                 }
260                 else {
261                         answer = 0;
262                 }
263                 newtPopWindow();
264                 newtFormDestroy(form);  
265
266                 break;
267 #endif
268
269         }
270         return (answer);
271 }
272
273
274 void important_message(char *title, char *msgtext)
275 {
276 #ifdef HAVE_NEWT
277         newtComponent form = NULL;
278         int i = 0;
279 #endif
280         char buf[SIZ];
281
282         switch (setup_type) {
283
284         case UI_TEXT:
285                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
286                 printf("       %s \n\n%s\n\n", title, msgtext);
287                 printf("Press return to continue...");
288                 fgets(buf, sizeof buf, stdin);
289                 break;
290
291 #ifdef HAVE_NEWT
292         case UI_NEWT:
293                 newtCenteredWindow(76, 10, title);
294                 form = newtForm(NULL, NULL, 0);
295                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
296                         extract_token(buf, msgtext, i, '\n');
297                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
298                 }
299                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
300                 newtRunForm(form);
301                 newtPopWindow();
302                 newtFormDestroy(form);  
303                 break;
304 #endif
305
306         }
307 }
308
309 void important_msgnum(int msgnum)
310 {
311         important_message("Important Message", setup_text[msgnum]);
312 }
313
314 void display_error(char *error_message)
315 {
316         important_message("Error", error_message);
317 }
318
319 void progress(char *text, long int curr, long int cmax)
320 {
321 #ifdef HAVE_NEWT
322
323         /* These variables are static because progress() gets called
324          * multiple times during the course of whatever operation is
325          * being performed.  This makes setup non-threadsafe, but who
326          * cares?
327          */
328         static newtComponent form = NULL;
329         static newtComponent scale = NULL;
330 #endif
331         static long dots_printed = 0L;
332         long a = 0;
333
334         switch (setup_type) {
335
336         case UI_TEXT:
337                 if (curr == 0) {
338                         printf("%s\n", text);
339                         printf("..........................");
340                         printf("..........................");
341                         printf("..........................\r");
342                         fflush(stdout);
343                         dots_printed = 0;
344                 } else if (curr == cmax) {
345                         printf("\r%79s\n", "");
346                 } else {
347                         a = (curr * 100) / cmax;
348                         a = a * 78;
349                         a = a / 100;
350                         while (dots_printed < a) {
351                                 printf("*");
352                                 ++dots_printed;
353                                 fflush(stdout);
354                         }
355                 }
356                 break;
357
358 #ifdef HAVE_NEWT
359         case UI_NEWT:
360                 if (curr == 0) {
361                         newtCenteredWindow(76, 8, text);
362                         form = newtForm(NULL, NULL, 0);
363                         scale = newtScale(1, 3, 74, cmax);
364                         newtFormAddComponent(form, scale);
365                         newtDrawForm(form);
366                         newtRefresh();
367                 }
368                 if ((curr > 0) && (curr <= cmax)) {
369                         newtScaleSet(scale, curr);
370                         newtRefresh();
371                 }
372                 if (curr == cmax) {
373                         newtFormDestroy(form);  
374                         newtPopWindow();
375                         newtRefresh();
376                 }
377                 break;
378 #endif
379
380         }
381 }
382
383
384
385 /*
386  * check_services_entry()  -- Make sure "citadel" is in /etc/services
387  *
388  */
389 void check_services_entry(void)
390 {
391         int i;
392         FILE *sfp;
393
394         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
395                 for (i=0; i<3; ++i) {
396                         progress("Adding service entry...", i, 3);
397                         if (i == 0) {
398                                 sfp = fopen("/etc/services", "a");
399                                 if (sfp == NULL) {
400                                         display_error(strerror(errno));
401                                 } else {
402                                         fprintf(sfp, "%s                504/tcp\n",
403                                                 SERVICE_NAME);
404                                         fclose(sfp);
405                                 }
406                         }
407                         sleep(1);
408                 }
409         }
410 }
411
412
413 /*
414  * check_inittab_entry()  -- Make sure "citadel" is in /etc/inittab
415  *
416  */
417 void check_inittab_entry(void)
418 {
419         FILE *infp;
420         char buf[SIZ];
421         char looking_for[SIZ];
422         char question[128];
423         char entryname[5];
424
425         /* Determine the fully qualified path name of citserver */
426         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
427
428         /* If there's already an entry, then we have nothing left to do. */
429         if (strlen(init_entry) > 0) {
430                 return;
431         }
432
433         /* Otherwise, prompt the user to create an entry. */
434         snprintf(question, sizeof question,
435                 "There is no '%s' entry in /etc/inittab.\n"
436                 "Would you like to add one?",
437                 looking_for);
438         if (yesno(question) == 0)
439                 return;
440
441         /* Generate a unique entry name for /etc/inittab */
442         snprintf(entryname, sizeof entryname, "c0");
443         do {
444                 ++entryname[1];
445                 if (entryname[1] > '9') {
446                         entryname[1] = 0;
447                         ++entryname[0];
448                         if (entryname[0] > 'z') {
449                                 display_error(
450                                    "Can't generate a unique entry name");
451                                 return;
452                         }
453                 }
454                 snprintf(buf, sizeof buf,
455                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
456         } while (system(buf) == 0);
457
458         /* Now write it out to /etc/inittab */
459         infp = fopen("/etc/inittab", "a");
460         if (infp == NULL) {
461                 display_error(strerror(errno));
462         } else {
463                 fprintf(infp, "# Start the Citadel/UX server...\n");
464                 fprintf(infp, "%s:2345:respawn:%s -h%s\n",
465                         entryname, looking_for, setup_directory);
466                 fclose(infp);
467                 strcpy(init_entry, entryname);
468         }
469 }
470
471
472 /*
473  * On systems which use xinetd, see if we can offer to install Citadel as
474  * the default telnet target.
475  */
476 void check_xinetd_entry(void) {
477         char *filename = "/etc/xinetd.d/telnet";
478         FILE *fp;
479         char buf[SIZ];
480         int already_citadel = 0;
481
482         fp = fopen(filename, "r+");
483         if (fp == NULL) return;         /* Not there.  Oh well... */
484
485         while (fgets(buf, sizeof buf, fp) != NULL) {
486                 if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
487         }
488         fclose(fp);
489         if (already_citadel) return;    /* Already set up this way. */
490
491         /* Otherwise, prompt the user to create an entry. */
492         snprintf(buf, sizeof buf,
493                 "Setup can configure the 'xinetd' service to automatically\n"
494                 "connect incoming telnet sessions to Citadel, bypassing the\n"
495                 "host system's login prompt.  Would you like to do this?\n"
496         );
497         if (yesno(buf) == 0)
498                 return;
499
500         fp = fopen(filename, "w");
501         fprintf(fp,
502                 "# description: telnet service for Citadel users\n"
503                 "service telnet\n"
504                 "{\n"
505                 "       disable = no\n"
506                 "       flags           = REUSE\n"
507                 "       socket_type     = stream\n"
508                 "       wait            = no\n"
509                 "       user            = root\n"
510                 "       server          = /usr/sbin/in.telnetd\n"
511                 "       server_args     = -h -L %s/citadel\n"
512                 "       log_on_failure  += USERID\n"
513                 "}\n",
514                 setup_directory
515         );
516         fclose(fp);
517
518         /* Now try to restart the service */
519         system("/etc/init.d/xinetd restart >/dev/null 2>&1");
520 }
521
522
523
524 /*
525  * Offer to disable other MTA's
526  */
527 void disable_other_mta(char *mta) {
528         char buf[SIZ];
529         FILE *fp;
530         int lines = 0;
531
532         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null", mta);
533         fp = popen(buf, "r");
534         if (fp == NULL) return;
535
536         while (fgets(buf, sizeof buf, fp) != NULL) {
537                 ++lines;
538         }
539         fclose(fp);
540         if (lines == 0) return;         /* Nothing to do. */
541
542         /* Offer to replace other MTA with the vastly superior Citadel :)  */
543         snprintf(buf, sizeof buf,
544                 "You appear to have the '%s' mail transport agent\n"
545                 "running on your system.  Would you like to disable it,\n"
546                 "allowing Citadel to handle your system's Internet mail\n"
547                 "instead?\n",
548                 mta
549         );
550         if (yesno(buf) == 0)
551                 return;
552
553         sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
554         system(buf);
555         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
556         system(buf);
557 }
558
559
560
561
562 void set_str_val(int msgpos, char str[])
563 {
564 #ifdef HAVE_NEWT
565         newtComponent form;
566         char *result;
567         int i;
568 #endif
569         char buf[SIZ];
570         char tempfile[PATH_MAX];
571         char setupmsg[SIZ];
572
573         strcpy(tempfile, tmpnam(NULL));
574         strcpy(setupmsg, "");
575
576         switch (setup_type) {
577         case UI_TEXT:
578                 title(setup_titles[msgpos]);
579                 printf("\n%s\n", setup_text[msgpos]);
580                 printf("This is currently set to:\n%s\n", str);
581                 printf("Enter new value or press return to leave unchanged:\n");
582                 fgets(buf, sizeof buf, stdin);
583                 buf[strlen(buf) - 1] = 0;
584                 if (strlen(buf) != 0)
585                         strcpy(str, buf);
586                 break;
587 #ifdef HAVE_NEWT
588         case UI_NEWT:
589
590                 newtCenteredWindow(76, 10, setup_titles[msgpos]);
591                 form = newtForm(NULL, NULL, 0);
592                 for (i=0; i<num_tokens(setup_text[msgpos], '\n'); ++i) {
593                         extract_token(buf, setup_text[msgpos], i, '\n');
594                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
595                 }
596                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
597                                         NEWT_FLAG_RETURNEXIT));
598                 newtRunForm(form);
599                 strcpy(str, result);
600
601                 newtPopWindow();
602                 newtFormDestroy(form);  
603
604 #endif
605         }
606 }
607
608 void set_int_val(int msgpos, int *ip)
609 {
610         char buf[16];
611         snprintf(buf, sizeof buf, "%d", (int) *ip);
612         set_str_val(msgpos, buf);
613         *ip = atoi(buf);
614 }
615
616
617 void set_char_val(int msgpos, char *ip)
618 {
619         char buf[16];
620         snprintf(buf, sizeof buf, "%d", (int) *ip);
621         set_str_val(msgpos, buf);
622         *ip = (char) atoi(buf);
623 }
624
625
626 void set_long_val(int msgpos, long int *ip)
627 {
628         char buf[16];
629         snprintf(buf, sizeof buf, "%ld", *ip);
630         set_str_val(msgpos, buf);
631         *ip = atol(buf);
632 }
633
634
635 void edit_value(int curr)
636 {
637         int i;
638         struct passwd *pw;
639         char bbsuidname[SIZ];
640
641         switch (curr) {
642
643         case 1:
644                 set_str_val(curr, config.c_sysadm);
645                 break;
646
647         case 2:
648 #ifdef __CYGWIN__
649                 config.c_bbsuid = 0;    /* XXX Windows hack, prob. insecure */
650 #else
651                 i = config.c_bbsuid;
652                 pw = getpwuid(i);
653                 if (pw == NULL) {
654                         set_int_val(curr, &i);
655                         config.c_bbsuid = i;
656                 }
657                 else {
658                         strcpy(bbsuidname, pw->pw_name);
659                         set_str_val(curr, bbsuidname);
660                         pw = getpwnam(bbsuidname);
661                         if (pw != NULL) {
662                                 config.c_bbsuid = pw->pw_uid;
663                         }
664                         else if (atoi(bbsuidname) > 0) {
665                                 config.c_bbsuid = atoi(bbsuidname);
666                         }
667                 }
668 #endif
669                 break;
670
671         case 3:
672                 set_int_val(curr, &config.c_port_number);
673                 break;
674
675
676         }
677 }
678
679 /*
680  * (re-)write the config data to disk
681  */
682 void write_config_to_disk(void)
683 {
684         FILE *fp;
685         int fd;
686
687         if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
688                 display_error("setup: cannot open citadel.config");
689                 cleanup(1);
690         }
691         fp = fdopen(fd, "wb");
692         if (fp == NULL) {
693                 display_error("setup: cannot open citadel.config");
694                 cleanup(1);
695         }
696         fwrite((char *) &config, sizeof(struct config), 1, fp);
697         fclose(fp);
698 }
699
700
701
702
703 /*
704  * Figure out what type of user interface we're going to use
705  */
706 int discover_ui(void)
707 {
708
709 #ifdef HAVE_NEWT
710         newtInit();
711         newtCls();
712         newtDrawRootText(0, 0, "Citadel/UX Setup");
713         return UI_NEWT;
714 #endif
715         return UI_TEXT;
716 }
717
718
719
720
721
722 int main(int argc, char *argv[])
723 {
724         int a;
725         int curr;
726         char aaa[128];
727         FILE *fp;
728         int old_setup_level = 0;
729         int info_only = 0;
730         struct utsname my_utsname;
731         struct passwd *pw;
732         struct hostent *he;
733         gid_t gid;
734
735         /* set an invalid setup type */
736         setup_type = (-1);
737
738         /* parse command line args */
739         for (a = 0; a < argc; ++a) {
740                 if (!strncmp(argv[a], "-u", 2)) {
741                         strcpy(aaa, argv[a]);
742                         strcpy(aaa, &aaa[2]);
743                         setup_type = atoi(aaa);
744                 }
745                 if (!strcmp(argv[a], "-i")) {
746                         info_only = 1;
747                 }
748                 if (!strcmp(argv[a], "-q")) {
749                         setup_type = UI_SILENT;
750                 }
751         }
752
753
754         /* If a setup type was not specified, try to determine automatically
755          * the best one to use out of all available types.
756          */
757         if (setup_type < 0) {
758                 setup_type = discover_ui();
759         }
760         if (info_only == 1) {
761                 important_message("Citadel/UX Setup", CITADEL);
762                 cleanup(0);
763         }
764
765         /* Get started in a valid setup directory. */
766         strcpy(setup_directory, BBSDIR);
767         set_str_val(0, setup_directory);
768         if (chdir(setup_directory) != 0) {
769                 important_message("Citadel/UX Setup",
770                           "The directory you specified does not exist.");
771                 cleanup(errno);
772         }
773
774         /* Determine our host name, in case we need to use it as a default */
775         uname(&my_utsname);
776
777         /* See if we need to shut down the Citadel service. */
778         for (a=0; a<=3; ++a) {
779                 progress("Shutting down the Citadel service...", a, 3);
780                 if (a == 0) shutdown_service();
781                 sleep(1);
782         }
783
784         /* Now begin. */
785         switch (setup_type) {
786
787         case UI_TEXT:
788                 printf("\n\n\n"
789                         "               *** Citadel/UX setup program ***\n\n");
790                 break;
791
792         }
793
794         /*
795          * What we're going to try to do here is append a whole bunch of
796          * nulls to the citadel.config file, so we can keep the old config
797          * values if they exist, but if the file is missing or from an
798          * earlier version with a shorter config structure, when setup tries
799          * to read the old config parameters, they'll all come up zero.
800          * The length of the config file will be set to what it's supposed
801          * to be when we rewrite it, because we replace the old file with a
802          * completely new copy.
803          */
804
805         if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
806                       S_IRUSR | S_IWUSR)) == -1) {
807                 display_error("setup: cannot append citadel.config");
808                 cleanup(errno);
809         }
810         fp = fdopen(a, "ab");
811         if (fp == NULL) {
812                 display_error("setup: cannot append citadel.config");
813                 cleanup(errno);
814         }
815         for (a = 0; a < sizeof(struct config); ++a)
816                 putc(0, fp);
817         fclose(fp);
818
819         /* now we re-open it, and read the old or blank configuration */
820         fp = fopen("citadel.config", "rb");
821         if (fp == NULL) {
822                 display_error("setup: cannot open citadel.config");
823                 cleanup(errno);
824         }
825         fread((char *) &config, sizeof(struct config), 1, fp);
826         fclose(fp);
827
828         /* set some sample/default values in place of blanks... */
829         if (strlen(config.c_nodename) == 0)
830                 safestrncpy(config.c_nodename, my_utsname.nodename,
831                             sizeof config.c_nodename);
832         strtok(config.c_nodename, ".");
833         if (strlen(config.c_fqdn) == 0) {
834                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
835                         safestrncpy(config.c_fqdn, he->h_name,
836                                     sizeof config.c_fqdn);
837                 else
838                         safestrncpy(config.c_fqdn, my_utsname.nodename,
839                                     sizeof config.c_fqdn);
840         }
841         if (strlen(config.c_humannode) == 0)
842                 strcpy(config.c_humannode, "My System");
843         if (strlen(config.c_phonenum) == 0)
844                 strcpy(config.c_phonenum, "US 800 555 1212");
845         if (config.c_initax == 0) {
846                 config.c_initax = 4;
847         }
848         if (strlen(config.c_moreprompt) == 0)
849                 strcpy(config.c_moreprompt, "<more>");
850         if (strlen(config.c_twitroom) == 0)
851                 strcpy(config.c_twitroom, "Trashcan");
852         if (strlen(config.c_net_password) == 0)
853                 strcpy(config.c_net_password, "netpassword");
854         if (strlen(config.c_baseroom) == 0)
855                 strcpy(config.c_baseroom, "Lobby");
856         if (strlen(config.c_aideroom) == 0)
857                 strcpy(config.c_aideroom, "Aide");
858         if (config.c_port_number == 0) {
859                 config.c_port_number = 504;
860         }
861         if (config.c_sleeping == 0) {
862                 config.c_sleeping = 900;
863         }
864         if (config.c_bbsuid == 0) {
865                 pw = getpwnam("citadel");
866                 if (pw != NULL)
867                         config.c_bbsuid = pw->pw_uid;
868         }
869         if (config.c_bbsuid == 0) {
870                 pw = getpwnam("bbs");
871                 if (pw != NULL)
872                         config.c_bbsuid = pw->pw_uid;
873         }
874         if (config.c_bbsuid == 0) {
875                 pw = getpwnam("guest");
876                 if (pw != NULL)
877                         config.c_bbsuid = pw->pw_uid;
878         }
879         if (config.c_createax == 0) {
880                 config.c_createax = 3;
881         }
882         /*
883          * Negative values for maxsessions are not allowed.
884          */
885         if (config.c_maxsessions < 0) {
886                 config.c_maxsessions = 0;
887         }
888         /* We need a system default message expiry policy, because this is
889          * the top level and there's no 'higher' policy to fall back on.
890          */
891         if (config.c_ep.expire_mode == 0) {
892                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
893                 config.c_ep.expire_value = 150;
894         }
895
896         /*
897          * Default port numbers for various services
898          */
899         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
900         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
901         if (config.c_imap_port == 0) config.c_imap_port = 143;
902
903         /* Go through a series of dialogs prompting for config info */
904         if (setup_type != UI_SILENT) {
905                 for (curr = 1; curr <= MAXSETUP; ++curr) {
906                         edit_value(curr);
907                 }
908         }
909
910         /*
911            if (setuid(config.c_bbsuid) != 0) {
912            important_message("Citadel/UX Setup",
913            "Failed to change the user ID to your Citadel user.");
914            cleanup(errno);
915            }
916          */
917
918 /***** begin version update section ***** */
919         /* take care of any updating that is necessary */
920
921         old_setup_level = config.c_setup_level;
922
923         if (old_setup_level == 0) {
924                 goto NEW_INST;
925         }
926
927         if (old_setup_level < 555) {
928                 important_message("Citadel/UX Setup",
929                                   "This Citadel/UX installation is too old "
930                                   "to be upgraded.");
931                 cleanup(1);
932         }
933         write_config_to_disk();
934
935         old_setup_level = config.c_setup_level;
936
937         /* end of version update section */
938
939 NEW_INST:
940         config.c_setup_level = REV_LEVEL;
941
942 /******************************************/
943
944         write_config_to_disk();
945
946         mkdir("info", 0700);
947         mkdir("bio", 0700);
948         mkdir("userpics", 0700);
949         mkdir("messages", 0700);
950         mkdir("help", 0700);
951         mkdir("images", 0700);
952         mkdir("netconfigs", 0700);
953
954         /* Delete files and directories used by older Citadel versions */
955         system("rm -fr ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
956         unlink("citadel.log");
957         unlink("weekly");
958
959         check_services_entry(); /* Check /etc/services */
960 #ifndef __CYGWIN__
961         check_inittab_entry();  /* Check /etc/inittab */
962         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
963
964         /* Offer to disable other MTA's on the system. */
965         disable_other_mta("sendmail");
966         disable_other_mta("postfix");
967         disable_other_mta("qmail");
968 #endif
969
970         if ((pw = getpwuid(config.c_bbsuid)) == NULL)
971                 gid = getgid();
972         else
973                 gid = pw->pw_gid;
974
975         progress("Setting file permissions", 0, 4);
976         chown(".", config.c_bbsuid, gid);
977         progress("Setting file permissions", 1, 4);
978         chown("citadel.config", config.c_bbsuid, gid);
979         progress("Setting file permissions", 2, 4);
980         snprintf(aaa, sizeof aaa,
981                 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
982                 (long)config.c_bbsuid, (long)gid);
983         system(aaa);
984         progress("Setting file permissions", 3, 4);
985         chmod("citadel.config", S_IRUSR | S_IWUSR);
986         progress("Setting file permissions", 4, 4);
987
988         /* See if we can start the Citadel service. */
989         if (strlen(init_entry) > 0) {
990                 for (a=0; a<=3; ++a) {
991                         progress("Starting the Citadel service...", a, 3);
992                         if (a == 0) start_the_service();
993                         sleep(1);
994                 }
995                 important_message("Setup finished",
996                         "Setup is finished.  You may now log in.");
997         }
998         else {
999                 important_message("Setup finished",
1000                         "Setup is finished.  You may now start the server.");
1001         }
1002
1003         cleanup(0);
1004         return 0;
1005 }