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