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