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