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