]> code.citadel.org Git - citadel.git/blob - citadel/setup.c
* Modified setup.c to allow ldap auth enable
[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 #include <time.h>
24 #include <libcitadel.h>
25 #include "citadel.h"
26 #include "axdefs.h"
27 #include "sysdep.h"
28 #include "config.h"
29 #include "citadel_dirs.h"
30 #if HAVE_BACKTRACE
31 #include <execinfo.h>
32 #endif
33
34
35 #define MAXSETUP 6      /* How many setup questions to ask */
36
37 #define UI_TEXT         0       /* Default setup type -- text only */
38 #define UI_DIALOG       2       /* Use the 'dialog' program */
39 #define UI_SILENT       3       /* Silent running, for use in scripts */
40
41 #define SERVICE_NAME    "citadel"
42 #define PROTO_NAME      "tcp"
43 #define NSSCONF         "/etc/nsswitch.conf"
44
45 int setup_type;
46 char setup_directory[PATH_MAX];
47 int using_web_installer = 0;
48 int enable_home = 1;
49 char admin_pass[SIZ];
50 char admin_cmd[SIZ];
51
52 char *setup_titles[] =
53 {
54         "Citadel Home Directory",
55         "System Administrator",
56         "Administrator Password",
57         "Citadel User ID",
58         "Server IP address",
59         "Server port number",
60         "Authentication mode"
61 };
62
63 /**
64  * \brief print the actual stack frame.
65  */
66 void cit_backtrace(void)
67 {
68 #ifdef HAVE_BACKTRACE
69         void *stack_frames[50];
70         size_t size, i;
71         char **strings;
72
73
74         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
75         strings = backtrace_symbols(stack_frames, size);
76         for (i = 0; i < size; i++) {
77                 if (strings != NULL)
78                         fprintf(stderr, "%s\n", strings[i]);
79                 else
80                         fprintf(stderr, "%p\n", stack_frames[i]);
81         }
82         free(strings);
83 #endif
84 }
85
86 struct config config;
87
88         /* calculate all our path on a central place */
89     /* where to keep our config */
90         
91
92 char *setup_text[] = {
93 #ifndef HAVE_RUN_DIR
94 "Enter the full pathname of the directory in which the Citadel\n"
95 "installation you are creating or updating resides.  If you\n"
96 "specify a directory other than the default, you will need to\n"
97 "specify the -h flag to the server when you start it up.\n",
98 #else
99 "Enter the subdirectory name for an alternate installation of "
100 "Citadel. To do a default installation just leave it blank."
101 "If you specify a directory other than the default, you will need to\n"
102 "specify the -h flag to the server when you start it up.\n"
103 "note that it may not have a leading /",
104 #endif
105
106 "Enter the name of the system administrator (which is probably\n"
107 "you).  When an account is created with this name, it will\n"
108 "automatically be given administrator-level access.\n",
109
110 "Enter a password for the system administrator. When setup\n"
111 "completes it will attempt to create the administrator user\n"
112 "and set the password specified here.\n",
113
114 "Citadel needs to run under its own user ID.  This would\n"
115 "typically be called \"citadel\", but if you are running Citadel\n"
116 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
117 "The server will run under this user ID.  Please specify that\n"
118 "user ID here.  You may specify either a user name or a numeric\n"
119 "UID.\n",
120
121 "Specify the IP address on which your server will run.  If you\n"
122 "leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
123 "on all addresses.  You can usually skip this unless you are\n"
124 "running multiple instances of Citadel on the same computer.\n",
125
126 "Specify the TCP port number on which your server will run.\n"
127 "Normally, this will be port 504, which is the official port\n"
128 "assigned by the IANA for Citadel servers.  You will only need\n"
129 "to specify a different port number if you run multiple instances\n"
130 "of Citadel on the same computer and there is something else\n"
131 "already using port 504.\n",
132
133
134
135 "How would you like to authenticate users on your Citadel system?\n"
136 "\n"
137 " 0. Self contained authentication - Citadel maintains its own user database\n"
138 "    (This mode is *strongly* recommended)\n"
139 "\n"
140 " 1. Authenticate users against the host system (unix or linux accounts)\n"
141 "\n"
142 " 2. Authenticate users against an external LDAP directory\n"
143 "\n"
144 "WARNING: do *not* change this setting once your system is installed.\n"
145 "\n"
146 "(Answer \"0\" unless you completely understand this option)\n"
147 "Which authentication mode do you want to use?\n"
148
149 };
150
151 struct config config;
152 int direction;
153
154
155 void cleanup(int exitcode)
156 {
157 //      printf("Exitcode: %d\n", exitcode);
158 //      cit_backtrace();
159         exit(exitcode);
160 }
161
162
163
164 void title(char *text)
165 {
166         if (setup_type == UI_TEXT) {
167                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
168         }
169 }
170
171
172
173 int yesno(char *question, int default_value)
174 {
175         int i = 0;
176         int answer = 0;
177         char buf[SIZ];
178
179         switch (setup_type) {
180
181         case UI_TEXT:
182                 do {
183                         printf("%s\nYes/No [%s] --> ",
184                                 question,
185                                 ( default_value ? "Yes" : "No" )
186                         );
187                         fgets(buf, sizeof buf, stdin);
188                         answer = tolower(buf[0]);
189                         if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
190                                 answer = default_value;
191                         else if (answer == 'y')
192                                 answer = 1;
193                         else if (answer == 'n')
194                                 answer = 0;
195                 } while ((answer < 0) || (answer > 1));
196                 break;
197
198         case UI_DIALOG:
199                 sprintf(buf, "exec %s %s --yesno '%s' 15 75",
200                         getenv("CTDL_DIALOG"),
201                         ( default_value ? "" : "--defaultno" ),
202                         question);
203                 i = system(buf);
204                 if (i == 0) {
205                         answer = 1;
206                 }
207                 else {
208                         answer = 0;
209                 }
210                 break;
211         case UI_SILENT:
212                 break;
213
214         }
215         return (answer);
216 }
217
218
219 void important_message(char *title, char *msgtext)
220 {
221         char buf[SIZ];
222
223         switch (setup_type) {
224
225         case UI_TEXT:
226                 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");
227                 printf("       %s \n\n%s\n\n", title, msgtext);
228                 printf("Press return to continue...");
229                 fgets(buf, sizeof buf, stdin);
230                 break;
231
232         case UI_DIALOG:
233                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
234                         getenv("CTDL_DIALOG"),
235                         msgtext);
236                 system(buf);
237                 break;
238         case UI_SILENT:
239                 fprintf(stderr, "%s\n", msgtext);
240                 break;
241         }
242 }
243
244 void important_msgnum(int msgnum)
245 {
246         important_message("Important Message", setup_text[msgnum]);
247 }
248
249 void display_error(char *error_message)
250 {
251         important_message("Error", error_message);
252 }
253
254 void progress(char *text, long int curr, long int cmax)
255 {
256         static long dots_printed = 0L;
257         long a = 0;
258         static FILE *fp = NULL;
259         char buf[SIZ];
260
261         switch (setup_type) {
262
263         case UI_TEXT:
264                 if (curr == 0) {
265                         printf("%s\n", text);
266                         printf("..........................");
267                         printf("..........................");
268                         printf("..........................\r");
269                         fflush(stdout);
270                         dots_printed = 0;
271                 } else if (curr == cmax) {
272                         printf("\r%79s\n", "");
273                 } else {
274                         a = (curr * 100) / cmax;
275                         a = a * 78;
276                         a = a / 100;
277                         while (dots_printed < a) {
278                                 printf("*");
279                                 ++dots_printed;
280                                 fflush(stdout);
281                         }
282                 }
283                 break;
284
285         case UI_DIALOG:
286                 if (curr == 0) {
287                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
288                                 getenv("CTDL_DIALOG"),
289                                 text);
290                         fp = popen(buf, "w");
291                         if (fp != NULL) {
292                                 fprintf(fp, "0\n");
293                                 fflush(fp);
294                         }
295                 } 
296                 else if (curr == cmax) {
297                         if (fp != NULL) {
298                                 fprintf(fp, "100\n");
299                                 pclose(fp);
300                                 fp = NULL;
301                         }
302                 }
303                 else {
304                         a = (curr * 100) / cmax;
305                         if (fp != NULL) {
306                                 fprintf(fp, "%ld\n", a);
307                                 fflush(fp);
308                         }
309                 }
310                 break;
311         case UI_SILENT:
312                 break;
313
314         }
315 }
316
317
318
319 /*
320  * check_services_entry()  -- Make sure "citadel" is in /etc/services
321  *
322  */
323 void check_services_entry(void)
324 {
325         int i;
326         FILE *sfp;
327         char errmsg[256];
328
329         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
330                 for (i=0; i<=2; ++i) {
331                         progress("Adding service entry...", i, 2);
332                         if (i == 0) {
333                                 sfp = fopen("/etc/services", "a");
334                                 if (sfp == NULL) {
335                                         sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno));
336                                         display_error(errmsg);
337                                 } else {
338                                         fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
339                                         fclose(sfp);
340                                 }
341                         }
342                 }
343         }
344 }
345
346
347
348
349 /*
350  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
351  *
352  */
353 void delete_inittab_entry(void)
354 {
355         FILE *infp;
356         FILE *outfp;
357         char looking_for[256];
358         char buf[1024];
359         char outfilename[32];
360         int changes_made = 0;
361
362         /* Determine the fully qualified path name of citserver */
363         snprintf(looking_for, 
364                  sizeof looking_for,
365                  "%s/citserver", 
366                  ctdl_sbin_dir
367                  );
368
369         /* Now tweak /etc/inittab */
370         infp = fopen("/etc/inittab", "r");
371         if (infp == NULL) {
372
373                 /* If /etc/inittab does not exist, return quietly.
374                  * Not all host platforms have it.
375                  */
376                 if (errno == ENOENT) {
377                         return;
378                 }
379
380                 /* Other errors might mean something really did go wrong.
381                  */
382                 sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
383                 display_error(buf);
384                 return;
385         }
386
387         strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
388         outfp = fdopen(mkstemp(outfilename), "w+");
389         if (outfp == NULL) {
390                 sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
391                 display_error(buf);
392                 fclose(infp);
393                 return;
394         }
395
396         while (fgets(buf, sizeof buf, infp) != NULL) {
397                 if (strstr(buf, looking_for) != NULL) {
398                         fwrite("#", 1, 1, outfp);
399                         ++changes_made;
400                 }
401                 fwrite(buf, strlen(buf), 1, outfp);
402         }
403
404         fclose(infp);
405         fclose(outfp);
406
407         if (changes_made) {
408                 sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
409                 system(buf);
410                 system("/sbin/init q 2>/dev/null");
411         }
412         else {
413                 unlink(outfilename);
414         }
415 }
416
417
418 /*
419  * install_init_scripts()  -- Try to configure to start Citadel at boot
420  *
421  */
422 void install_init_scripts(void)
423 {
424         struct stat etcinitd;
425         FILE *fp;
426         char *initfile = "/etc/init.d/citadel";
427         char command[SIZ];
428
429         if ((stat("/etc/init.d/", &etcinitd) == -1) && 
430             (errno == ENOENT))
431         {
432                 if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
433                     (errno == ENOENT))
434                         initfile = CTDLDIR"/citadel.init";
435                 else
436                         initfile = "/etc/rc.d/init.d/citadel";
437         }
438
439         fp = fopen(initfile, "r");
440         if (fp != NULL) {
441                 if (yesno("Citadel already appears to be configured to start at boot.\n"
442                    "Would you like to keep your boot configuration as is?\n", 1) == 1) {
443                         return;
444                 }
445                 fclose(fp);
446                 
447         }
448
449         if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
450                 return;
451         }
452
453         fp = fopen(initfile, "w");
454         if (fp == NULL) {
455                 display_error("Cannot create /etc/init.d/citadel");
456                 return;
457         }
458
459         fprintf(fp,     "#!/bin/sh\n"
460                         "#\n"
461                         "# Init file for Citadel\n"
462                         "#\n"
463                         "# chkconfig: - 79 30\n"
464                         "# description: Citadel service\n"
465                         "# processname: citserver\n"
466                         "# pidfile: %s/citadel.pid\n"
467                         "\n"
468                         "CITADEL_DIR=%s\n"
469                         ,
470                                 setup_directory,
471                                 setup_directory
472                         );
473         fprintf(fp,     "\n"
474                         "test -d /var/run || exit 0\n"
475                         "\n"
476                         "case \"$1\" in\n"
477                         "\n"
478                         "start)         echo -n \"Starting Citadel... \"\n"
479                         "               if $CITADEL_DIR/citserver -d -h$CITADEL_DIR\n"
480                         "               then\n"
481                         "                       echo \"ok\"\n"
482                         "               else\n"
483                         "                       echo \"failed\"\n"
484                         "               fi\n");
485         fprintf(fp,     "               ;;\n"
486                         "stop)          echo -n \"Stopping Citadel... \"\n"
487                         "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
488                         "                       echo \"ok\"\n"
489                         "               else\n"
490                         "                       echo \"failed\"\n"
491                         "               fi\n"
492                         "               rm -f %s/citadel.pid 2>/dev/null\n"
493                         ,
494                                 setup_directory
495                         );
496         fprintf(fp,     "               ;;\n"
497                         "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
498                         "                       echo \"ok\"\n"
499                         "               else\n"
500                         "                       echo \"failed\"\n"
501                         "               fi\n"
502                         "               ;;\n"
503                         "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
504                         "               exit 1\n"
505                         "               ;;\n"
506                         "esac\n"
507         );
508
509         fclose(fp);
510         chmod(initfile, 0755);
511
512         /* Set up the run levels. */
513         system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
514         snprintf(command, sizeof(command), "for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/S79citadel ; done 2>/dev/null", initfile);
515         system(command);
516         snprintf(command, sizeof(command),"for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/K30citadel ; done 2>/dev/null", initfile);
517         system(command);
518
519 }
520
521
522
523
524
525
526 /*
527  * On systems which use xinetd, see if we can offer to install Citadel as
528  * the default telnet target.
529  */
530 void check_xinetd_entry(void) {
531         char *filename = "/etc/xinetd.d/telnet";
532         FILE *fp;
533         char buf[SIZ];
534         int already_citadel = 0;
535
536         fp = fopen(filename, "r+");
537         if (fp == NULL) return;         /* Not there.  Oh well... */
538
539         while (fgets(buf, sizeof buf, fp) != NULL) {
540                 if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
541         }
542         fclose(fp);
543         if (already_citadel) return;    /* Already set up this way. */
544
545         /* Otherwise, prompt the user to create an entry. */
546         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
547                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
548                         return;
549                 }
550         }
551         else {
552                 snprintf(buf, sizeof buf,
553                         "Setup can configure the \"xinetd\" service to automatically\n"
554                         "connect incoming telnet sessions to Citadel, bypassing the\n"
555                         "host system login: prompt.  Would you like to do this?\n"
556                 );
557                 if (yesno(buf, 1) == 0) {
558                         return;
559                 }
560         }
561
562         fp = fopen(filename, "w");
563         fprintf(fp,
564                 "# description: telnet service for Citadel users\n"
565                 "service telnet\n"
566                 "{\n"
567                 "       disable = no\n"
568                 "       flags           = REUSE\n"
569                 "       socket_type     = stream\n"
570                 "       wait            = no\n"
571                 "       user            = root\n"
572                 "       server          = /usr/sbin/in.telnetd\n"
573                 "       server_args     = -h -L %s/citadel\n"
574                 "       log_on_failure  += USERID\n"
575                 "}\n",
576                 ctdl_bin_dir);
577         fclose(fp);
578
579         /* Now try to restart the service */
580         system("/etc/init.d/xinetd restart >/dev/null 2>&1");
581 }
582
583
584
585 /*
586  * Offer to disable other MTA's
587  */
588 void disable_other_mta(char *mta) {
589         char buf[SIZ];
590         FILE *fp;
591         int lines = 0;
592
593         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
594                 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
595                 mta, mta);
596         fp = popen(buf, "r");
597         if (fp == NULL) return;
598
599         while (fgets(buf, sizeof buf, fp) != NULL) {
600                 ++lines;
601         }
602         fclose(fp);
603         if (lines == 0) return;         /* Nothing to do. */
604
605
606         /* Offer to replace other MTA with the vastly superior Citadel :)  */
607
608         snprintf(buf, sizeof buf,
609                  "You appear to have the \"%s\" email program\n"
610                  "running on your system.  If you want Citadel mail\n"
611                  "connected with %s, you will have to manually integrate\n"
612                  "them.  It is preferable to disable %s, and use Citadel's\n"
613                  "SMTP, POP3, and IMAP services.\n\n"
614                  "May we disable %s so that Citadel has access to ports\n"
615                  "25, 110, and 143?\n",
616                  mta, mta, mta, mta
617                 );
618         if (yesno(buf, 1) == 0) {
619                 return;
620         }
621         
622
623         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);
624         system(buf);
625         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
626         system(buf);
627 }
628
629
630
631
632 /* 
633  * Check to see if our server really works.  Returns 0 on success.
634  */
635 int test_server(char *setup_directory, char *relhomestr, int relhome) {
636         char cmd[256];
637         char cookie[256];
638         FILE *fp;
639         char buf[4096];
640         int found_it = 0;
641
642         /* Generate a silly little cookie.  We're going to write it out
643          * to the server and try to get it back.  The cookie does not
644          * have to be secret ... just unique.
645          */
646         sprintf(cookie, "--test--%d--", getpid());
647
648         if (relhome)
649                 sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
650                         ctdl_sbin_dir,
651                         relhomestr,
652                         cookie);
653         else
654                 sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
655                         ctdl_sbin_dir,
656                         cookie);
657
658         fp = popen(cmd, "r");
659         if (fp == NULL) return(errno);
660
661         while (fgets(buf, sizeof buf, fp) != NULL) {
662                 if ( (buf[0]=='2')
663                    && (strstr(buf, cookie) != NULL) ) {
664                         ++found_it;
665                 }
666         }
667         pclose(fp);
668
669         if (found_it) {
670                 return(0);
671         }
672         return(-1);
673 }
674
675 void strprompt(char *prompt_title, char *prompt_text, char *str)
676 {
677         char buf[SIZ];
678         char setupmsg[SIZ];
679         char dialog_result[PATH_MAX];
680         FILE *fp = NULL;
681
682         strcpy(setupmsg, "");
683
684         switch (setup_type) {
685         case UI_TEXT:
686                 title(prompt_title);
687                 printf("\n%s\n", prompt_text);
688                 printf("This is currently set to:\n%s\n", str);
689                 printf("Enter new value or press return to leave unchanged:\n");
690                 fgets(buf, sizeof buf, stdin);
691                 buf[strlen(buf) - 1] = 0;
692                 if (!IsEmptyStr(buf))
693                         strcpy(str, buf);
694                 break;
695
696         case UI_DIALOG:
697                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
698                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
699                         getenv("CTDL_DIALOG"),
700                         prompt_text,
701                         str,
702                         dialog_result);
703                 system(buf);
704                 fp = fopen(dialog_result, "r");
705                 if (fp != NULL) {
706                         fgets(str, sizeof buf, fp);
707                         if (str[strlen(str)-1] == 10) {
708                                 str[strlen(str)-1] = 0;
709                         }
710                         fclose(fp);
711                         unlink(dialog_result);
712                 }
713                 break;
714         case UI_SILENT:
715                 break;
716         }
717 }
718
719 void set_bool_val(int msgpos, int *ip) {
720         title(setup_titles[msgpos]);
721         *ip = yesno(setup_text[msgpos], *ip);
722 }
723
724 void set_str_val(int msgpos, char *str) {
725         strprompt(setup_titles[msgpos], setup_text[msgpos], str);
726 }
727
728 void set_int_val(int msgpos, int *ip)
729 {
730         char buf[16];
731         snprintf(buf, sizeof buf, "%d", (int) *ip);
732         set_str_val(msgpos, buf);
733         *ip = atoi(buf);
734 }
735
736
737 void set_char_val(int msgpos, char *ip)
738 {
739         char buf[16];
740         snprintf(buf, sizeof buf, "%d", (int) *ip);
741         set_str_val(msgpos, buf);
742         *ip = (char) atoi(buf);
743 }
744
745
746 void set_long_val(int msgpos, long int *ip)
747 {
748         char buf[16];
749         snprintf(buf, sizeof buf, "%ld", *ip);
750         set_str_val(msgpos, buf);
751         *ip = atol(buf);
752 }
753
754
755 void edit_value(int curr)
756 {
757         int i;
758         struct passwd *pw;
759         char ctdluidname[256];
760
761         switch (curr) {
762
763         case 1:
764                 if (setup_type == UI_SILENT)
765                 {
766                         if (getenv("SYSADMIN_NAME")) {
767                                 strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
768                         }
769                 }
770                 else {
771                         set_str_val(curr, config.c_sysadm);
772                 }
773                 break;
774
775         case 2:
776                 if (setup_type == UI_SILENT)
777                 {
778                         if (getenv("SYSADMIN_PW")) {
779                                 strcpy(admin_pass, getenv("SYSADMIN_PW"));
780                         }
781                 }
782                 else {
783                         set_str_val(curr, admin_pass);
784                 }
785                 break;
786         
787         case 3:
788                 if (setup_type == UI_SILENT)
789                 {               
790                         if (getenv("CITADEL_UID")) {
791                                 config.c_ctdluid = atoi(getenv("CITADEL_UID"));
792                         }                                       
793                 }
794                 else
795                 {
796 #ifdef __CYGWIN__
797                         config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
798 #else
799                         i = config.c_ctdluid;
800                         pw = getpwuid(i);
801                         if (pw == NULL) {
802                                 set_int_val(curr, &i);
803                                 config.c_ctdluid = i;
804                         }
805                         else {
806                                 strcpy(ctdluidname, pw->pw_name);
807                                 set_str_val(curr, ctdluidname);
808                                 pw = getpwnam(ctdluidname);
809                                 if (pw != NULL) {
810                                         config.c_ctdluid = pw->pw_uid;
811                                 }
812                                 else if (atoi(ctdluidname) > 0) {
813                                         config.c_ctdluid = atoi(ctdluidname);
814                                 }
815                         }
816 #endif
817                 }
818                 break;
819
820         case 4:
821                 if (setup_type == UI_SILENT)
822                 {
823                         if (getenv("IP_ADDR")) {
824                                 strcpy(config.c_ip_addr, getenv("IP_ADDR"));
825                         }
826                 }
827                 else {
828                         set_str_val(curr, config.c_ip_addr);
829                 }
830                 break;
831
832         case 5:
833                 if (setup_type == UI_SILENT)
834                 {
835                         if (getenv("CITADEL_PORT")) {
836                                 config.c_port_number = atoi(getenv("CITADEL_PORT"));
837                         }
838                 }
839                 else
840                 {
841                         set_int_val(curr, &config.c_port_number);
842                 }
843                 break;
844
845         case 6:
846                 if (setup_type == UI_SILENT)
847                 {
848                         if (getenv("ENABLE_UNIX_AUTH")) {
849                                 if (!strcasecmp(getenv("ENABLE_UNIX_AUTH"), "yes")) {
850                                         config.c_auth_mode = AUTHMODE_HOST;
851                                 }
852                                 else {
853                                         config.c_auth_mode = AUTHMODE_NATIVE;
854                                 }
855                         }
856                 }
857                 else {
858                         set_int_val(curr, &config.c_auth_mode);
859                 }
860                 break;
861
862         }
863 }
864
865 /*
866  * (re-)write the config data to disk
867  */
868 void write_config_to_disk(void)
869 {
870         FILE *fp;
871         int fd;
872
873         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
874                 display_error("setup: cannot open citadel.config");
875                 cleanup(1);
876         }
877         fp = fdopen(fd, "wb");
878         if (fp == NULL) {
879                 display_error("setup: cannot open citadel.config");
880                 cleanup(1);
881         }
882         fwrite((char *) &config, sizeof(struct config), 1, fp);
883         fclose(fp);
884 }
885
886
887
888
889 /*
890  * Figure out what type of user interface we're going to use
891  */
892 int discover_ui(void)
893 {
894
895         /* Use "dialog" if we have it */
896         if (getenv("CTDL_DIALOG") != NULL) {
897                 return UI_DIALOG;
898         }
899                 
900         return UI_TEXT;
901 }
902
903
904
905
906
907 /*
908  * Strip "db" entries out of /etc/nsswitch.conf
909  */
910 void fixnss(void) {
911         FILE *fp_read;
912         int fd_write;
913         char buf[256];
914         char buf_nc[256];
915         char question[512];
916         int i;
917         int changed = 0;
918         int file_changed = 0;
919         char new_filename[64];
920
921         fp_read = fopen(NSSCONF, "r");
922         if (fp_read == NULL) {
923                 return;
924         }
925
926         strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
927         fd_write = mkstemp(new_filename);
928         if (fd_write < 0) {
929                 fclose(fp_read);
930                 return;
931         }
932
933         while (fgets(buf, sizeof buf, fp_read) != NULL) {
934                 changed = 0;
935                 strcpy(buf_nc, buf);
936                 for (i=0; i<strlen(buf_nc); ++i) {
937                         if (buf_nc[i] == '#') {
938                                 buf_nc[i] = 0;
939                         }
940                 }
941                 for (i=0; i<strlen(buf_nc); ++i) {
942                         if (!strncasecmp(&buf_nc[i], "db", 2)) {
943                                 if (i > 0) {
944                                         if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
945                                                 changed = 1;
946                                                 file_changed = 1;
947                                                 strcpy(&buf_nc[i], &buf_nc[i+2]);
948                                                 strcpy(&buf[i], &buf[i+2]);
949                                                 if (buf[i]==32) {
950                                                         strcpy(&buf_nc[i], &buf_nc[i+1]);
951                                                         strcpy(&buf[i], &buf[i+1]);
952                                                 }
953                                         }
954                                 }
955                         }
956                 }
957                 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
958                         fclose(fp_read);
959                         close(fd_write);
960                         unlink(new_filename);
961                         return;
962                 }
963         }
964
965         fclose(fp_read);
966         
967         if (!file_changed) {
968                 unlink(new_filename);
969                 return;
970         }
971
972         snprintf(question, sizeof question,
973                 "\n"
974                 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
975                 "one or more services.  This is not necessary on most systems,\n"
976                 "and it is known to crash the Citadel server when delivering\n"
977                 "mail to the Internet.\n"
978                 "\n"
979                 "Do you want this module to be automatically disabled?\n"
980                 "\n"
981         );
982
983         if (yesno(question, 1)) {
984                 sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
985                 system(buf);
986                 chmod(NSSCONF, 0644);
987         }
988         unlink(new_filename);
989 }
990
991
992
993
994
995
996
997
998 int main(int argc, char *argv[])
999 {
1000         int a;
1001         int curr; 
1002         char aaa[128];
1003         FILE *fp;
1004         int old_setup_level = 0;
1005         int info_only = 0;
1006         struct utsname my_utsname;
1007         struct passwd *pw;
1008         struct hostent *he;
1009         gid_t gid;
1010         int relh=0;
1011         int home=0;
1012         char relhome[PATH_MAX]="";
1013         char ctdldir[PATH_MAX]=CTDLDIR;
1014         
1015         /* set an invalid setup type */
1016         setup_type = (-1);
1017
1018         /* Check to see if we're running the web installer */
1019         if (getenv("CITADEL_INSTALLER") != NULL) {
1020                 using_web_installer = 1;
1021         }
1022
1023         /* parse command line args */
1024         for (a = 0; a < argc; ++a) {
1025                 if (!strncmp(argv[a], "-u", 2)) {
1026                         strcpy(aaa, argv[a]);
1027                         strcpy(aaa, &aaa[2]);
1028                         setup_type = atoi(aaa);
1029                 }
1030                 else if (!strcmp(argv[a], "-i")) {
1031                         info_only = 1;
1032                 }
1033                 else if (!strcmp(argv[a], "-q")) {
1034                         setup_type = UI_SILENT;
1035                 }
1036                 else if (!strncmp(argv[a], "-h", 2)) {
1037                         relh=argv[a][2]!='/';
1038                         if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
1039                                                                    sizeof ctdl_home_directory);
1040                         else
1041                                 safestrncpy(relhome, &argv[a][2],
1042                                                         sizeof relhome);
1043                         home = 1;
1044                 }
1045
1046         }
1047
1048         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1049
1050         /* If a setup type was not specified, try to determine automatically
1051          * the best one to use out of all available types.
1052          */
1053         if (setup_type < 0) {
1054                 setup_type = discover_ui();
1055         }
1056         if (info_only == 1) {
1057                 important_message("Citadel Setup", CITADEL);
1058                 cleanup(0);
1059         }
1060
1061         /* Get started in a valid setup directory. */
1062         strcpy(setup_directory, ctdl_run_dir);
1063         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
1064                 strcpy(setup_directory, getenv("CITADEL"));
1065         }
1066         else {
1067                 set_str_val(0, setup_directory);
1068         }
1069
1070         enable_home = ( relh | home );
1071
1072         if (chdir(setup_directory) != 0) {
1073                 char errmsg[SIZ];
1074                 sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
1075                 
1076                 important_message("Citadel Setup", errmsg);
1077                 cleanup(errno);
1078         }
1079
1080         /* Determine our host name, in case we need to use it as a default */
1081         uname(&my_utsname);
1082
1083         /* Try to stop Citadel if we can */
1084         if (!access("/etc/init.d/citadel", X_OK)) {
1085                 system("/etc/init.d/citadel stop");
1086         }
1087
1088         /* Make sure Citadel is not running. */
1089         if (test_server(setup_directory, relhome, enable_home) == 0) {
1090                 important_message("Citadel Setup",
1091                         "The Citadel service is still running.\n"
1092                         "Please stop the service manually and run "
1093                         "setup again.");
1094                 cleanup(1);
1095         }
1096
1097         /* Now begin. */
1098         switch (setup_type) {
1099
1100         case UI_TEXT:
1101                 printf("\n\n\n"
1102                         "              *** Citadel setup program ***\n\n");
1103                 break;
1104
1105         }
1106
1107         /*
1108          * What we're going to try to do here is append a whole bunch of
1109          * nulls to the citadel.config file, so we can keep the old config
1110          * values if they exist, but if the file is missing or from an
1111          * earlier version with a shorter config structure, when setup tries
1112          * to read the old config parameters, they'll all come up zero.
1113          * The length of the config file will be set to what it's supposed
1114          * to be when we rewrite it, because we replace the old file with a
1115          * completely new copy.
1116          */
1117         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1118                       S_IRUSR | S_IWUSR)) == -1) {
1119                 display_error("setup: cannot append citadel.config");
1120                 cleanup(errno);
1121         }
1122         fp = fdopen(a, "ab");
1123         if (fp == NULL) {
1124                 display_error("setup: cannot append citadel.config");
1125                 cleanup(errno);
1126         }
1127         for (a = 0; a < sizeof(struct config); ++a)
1128                 putc(0, fp);
1129         fclose(fp);
1130
1131         /* now we re-open it, and read the old or blank configuration */
1132         fp = fopen(file_citadel_config, "rb");
1133         if (fp == NULL) {
1134                 display_error("setup: cannot open citadel.config");
1135                 cleanup(errno);
1136         }
1137         fread((char *) &config, sizeof(struct config), 1, fp);
1138         fclose(fp);
1139
1140         /* set some sample/default values in place of blanks... */
1141         if (IsEmptyStr(config.c_nodename))
1142                 safestrncpy(config.c_nodename, my_utsname.nodename,
1143                             sizeof config.c_nodename);
1144         strtok(config.c_nodename, ".");
1145         if (IsEmptyStr(config.c_fqdn) ) {
1146                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
1147                         safestrncpy(config.c_fqdn, he->h_name,
1148                                     sizeof config.c_fqdn);
1149                 else
1150                         safestrncpy(config.c_fqdn, my_utsname.nodename,
1151                                     sizeof config.c_fqdn);
1152         }
1153         if (IsEmptyStr(config.c_humannode))
1154                 strcpy(config.c_humannode, "My System");
1155         if (IsEmptyStr(config.c_phonenum))
1156                 strcpy(config.c_phonenum, "US 800 555 1212");
1157         if (config.c_initax == 0) {
1158                 config.c_initax = 4;
1159         }
1160         if (IsEmptyStr(config.c_moreprompt))
1161                 strcpy(config.c_moreprompt, "<more>");
1162         if (IsEmptyStr(config.c_twitroom))
1163                 strcpy(config.c_twitroom, "Trashcan");
1164         if (IsEmptyStr(config.c_baseroom))
1165                 strcpy(config.c_baseroom, BASEROOM);
1166         if (IsEmptyStr(config.c_aideroom))
1167                 strcpy(config.c_aideroom, "Aide");
1168         if (config.c_port_number == 0) {
1169                 config.c_port_number = 504;
1170         }
1171         if (config.c_sleeping == 0) {
1172                 config.c_sleeping = 900;
1173         }
1174         if (config.c_ctdluid == 0) {
1175                 pw = getpwnam("citadel");
1176                 if (pw != NULL)
1177                         config.c_ctdluid = pw->pw_uid;
1178         }
1179         if (config.c_ctdluid == 0) {
1180                 pw = getpwnam("bbs");
1181                 if (pw != NULL)
1182                         config.c_ctdluid = pw->pw_uid;
1183         }
1184         if (config.c_ctdluid == 0) {
1185                 pw = getpwnam("guest");
1186                 if (pw != NULL)
1187                         config.c_ctdluid = pw->pw_uid;
1188         }
1189         if (config.c_createax == 0) {
1190                 config.c_createax = 3;
1191         }
1192         /*
1193          * Negative values for maxsessions are not allowed.
1194          */
1195         if (config.c_maxsessions < 0) {
1196                 config.c_maxsessions = 0;
1197         }
1198         /* We need a system default message expiry policy, because this is
1199          * the top level and there's no 'higher' policy to fall back on.
1200          * By default, do not expire messages at all.
1201          */
1202         if (config.c_ep.expire_mode == 0) {
1203                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1204                 config.c_ep.expire_value = 0;
1205         }
1206
1207         /*
1208          * Default port numbers for various services
1209          */
1210         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1211         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1212         if (config.c_imap_port == 0) config.c_imap_port = 143;
1213         if (config.c_msa_port == 0) config.c_msa_port = 587;
1214         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1215         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1216         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1217         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1218         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1219         if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
1220         if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
1221
1222         /* Go through a series of dialogs prompting for config info */
1223         for (curr = 1; curr <= MAXSETUP; ++curr) {
1224                 edit_value(curr);
1225         }
1226
1227 /***** begin version update section ***** */
1228         /* take care of any updating that is necessary */
1229
1230         old_setup_level = config.c_setup_level;
1231
1232         if (old_setup_level == 0) {
1233                 goto NEW_INST;
1234         }
1235
1236         if (old_setup_level < 555) {
1237                 important_message("Citadel Setup",
1238                                   "This Citadel installation is too old "
1239                                   "to be upgraded.");
1240                 cleanup(1);
1241         }
1242         write_config_to_disk();
1243
1244         old_setup_level = config.c_setup_level;
1245
1246         /* end of version update section */
1247
1248 NEW_INST:
1249         config.c_setup_level = REV_LEVEL;
1250
1251 /******************************************/
1252
1253         write_config_to_disk();
1254
1255         mkdir(ctdl_info_dir, 0700);
1256         chmod(ctdl_info_dir, 0700);
1257         chown(ctdl_info_dir, config.c_ctdluid, -1);
1258
1259         mkdir(ctdl_bio_dir, 0700);
1260         chmod(ctdl_bio_dir, 0700);
1261         chown(ctdl_bio_dir, config.c_ctdluid, -1);
1262
1263         mkdir(ctdl_usrpic_dir, 0700);
1264         chmod(ctdl_usrpic_dir, 0700);
1265         chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
1266
1267         mkdir(ctdl_message_dir, 0700);
1268         chmod(ctdl_message_dir, 0700);
1269         chown(ctdl_message_dir, config.c_ctdluid, -1);
1270
1271         mkdir(ctdl_hlp_dir, 0700);
1272         chmod(ctdl_hlp_dir, 0700);
1273         chown(ctdl_hlp_dir, config.c_ctdluid, -1);
1274
1275         mkdir(ctdl_image_dir, 0700);
1276         chmod(ctdl_image_dir, 0700);
1277         chown(ctdl_image_dir, config.c_ctdluid, -1);
1278
1279         mkdir(ctdl_bb_dir, 0700);
1280         chmod(ctdl_bb_dir, 0700);
1281         chown(ctdl_bb_dir, config.c_ctdluid, -1);
1282
1283         mkdir(ctdl_file_dir, 0700);
1284         chmod(ctdl_file_dir, 0700);
1285         chown(ctdl_file_dir, config.c_ctdluid, -1);
1286
1287         mkdir(ctdl_netcfg_dir, 0700);
1288         chmod(ctdl_netcfg_dir, 0700);
1289         chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
1290
1291         /* Delete files and directories used by older Citadel versions */
1292         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1293         unlink("citadel.log");
1294         unlink("weekly");
1295
1296         if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
1297             (setup_type != UI_SILENT))
1298                 check_services_entry(); /* Check /etc/services */
1299 #ifndef __CYGWIN__
1300         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1301         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1302
1303         if ((getenv("ACT_AS_MTA") == NULL) || 
1304             (getenv("ACT_AS_MTA") &&
1305              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
1306                 /* Offer to disable other MTA's on the system. */
1307                 disable_other_mta("courier-authdaemon");
1308                 disable_other_mta("courier-imap");
1309                 disable_other_mta("courier-imap-ssl");
1310                 disable_other_mta("courier-pop");
1311                 disable_other_mta("courier-pop3");
1312                 disable_other_mta("courier-pop3d");
1313                 disable_other_mta("cyrmaster");
1314                 disable_other_mta("cyrus");
1315                 disable_other_mta("dovecot");
1316                 disable_other_mta("exim");
1317                 disable_other_mta("exim4");
1318                 disable_other_mta("imapd");
1319                 disable_other_mta("mta");
1320                 disable_other_mta("pop3d");
1321                 disable_other_mta("popd");
1322                 disable_other_mta("postfix");
1323                 disable_other_mta("qmail");
1324                 disable_other_mta("saslauthd");
1325                 disable_other_mta("sendmail");
1326                 disable_other_mta("vmailmgrd");
1327         }
1328 #endif
1329
1330         /* Check for the 'db' nss and offer to disable it */
1331         fixnss();
1332
1333         if ((pw = getpwuid(config.c_ctdluid)) == NULL)
1334                 gid = getgid();
1335         else
1336                 gid = pw->pw_gid;
1337
1338         progress("Setting file permissions", 0, 3);
1339         chown(ctdl_run_dir, config.c_ctdluid, gid);
1340         progress("Setting file permissions", 1, 3);
1341         chown(file_citadel_config, config.c_ctdluid, gid);
1342         progress("Setting file permissions", 2, 3);
1343         chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1344         progress("Setting file permissions", 3, 3);
1345
1346         /* 
1347          * If we're running on SysV, install init scripts.
1348          */
1349         if (!access("/var/run", W_OK)) {
1350
1351                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1352                         install_init_scripts();
1353                 }
1354
1355                 if (!access("/etc/init.d/citadel", X_OK)) {
1356                         system("/etc/init.d/citadel start");
1357                         sleep(3);
1358                 }
1359
1360                 if (test_server(setup_directory, relhome, enable_home) == 0) {
1361                         char buf[SIZ];
1362                         int found_it = 0;
1363
1364                         snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
1365                                   ctdl_sbin_dir, config.c_sysadm, admin_pass);
1366                         fp = popen(admin_cmd, "r");
1367                         if (fp != NULL) {
1368                                 while (fgets(buf, sizeof buf, fp) != NULL) 
1369                                 {
1370                                         if ((atol(buf) == 574) || (atol(buf) == 200))
1371                                                 ++found_it;
1372                                 }
1373                                 pclose(fp);
1374                         }
1375                 
1376                         if (found_it == 0)
1377                                 important_message("Error","Setup failed to create your admin user");
1378
1379                         if (setup_type != UI_SILENT)
1380                                 important_message("Setup finished",
1381                                                   "Setup of the Citadel server is complete.\n"
1382                                                   "If you will be using WebCit, please run its\n"
1383                                                   "setup program now; otherwise, run './citadel'\n"
1384                                                   "to log in.\n");
1385                 }
1386                 else {
1387                         important_message("Setup failed",
1388                                 "Setup is finished, but the Citadel server failed to start.\n"
1389                                 "Go back and check your configuration.\n"
1390                         );
1391                 }
1392
1393         }
1394
1395         else {
1396                 important_message("Setup finished",
1397                         "Setup is finished.  You may now start the server.");
1398         }
1399
1400         cleanup(0);
1401         return 0;
1402 }
1403
1404