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