Added our standard 'designed to piss off Richard Stallman' license declaration to...
[citadel.git] / citadel / utils / setup.c
index 5af5c4bf46d2ec9aa9b6f03460d2db483cb6bcd0..bf6ea963429b907ca9665d7b955a48d254ddadd8 100644 (file)
@@ -1,13 +1,19 @@
 /*
- * $Id$
- *
  * Citadel setup utility
  *
+ * Copyright (c) 1987-2012 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  */
 
+#define SHOW_ME_VAPPEND_PRINTF
 #include "ctdl_module.h"
-
-
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <execinfo.h>
 #endif
 
+#ifdef ENABLE_NLS
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h>
+#endif
+#include <libintl.h>
+#include <locale.h>
+#define _(string)      gettext(string)
+#else
+#define _(string)      (string)
+#endif
+
 
 #define MAXSETUP 11    /* How many setup questions to ask */
 
 #define PROTO_NAME     "tcp"
 #define NSSCONF                "/etc/nsswitch.conf"
 
+
+typedef enum _SetupStep {
+       eCitadelHomeDir = 0,
+       eSysAdminName = 1,
+       eSysAdminPW = 2,
+       eUID = 3,
+       eIP_ADDR = 4,
+       eCTDL_Port = 5,
+       eAuthType = 6,
+       eLDAP_Host = 7,
+       eLDAP_Port = 8,
+       eLDAP_Base_DN = 9,
+       eLDAP_Bind_DN = 10,
+       eLDAP_Bind_PW = 11,
+       eMaxQuestions = 12
+} eSteupStep;
+
+///"CREATE_XINETD_ENTRY";
+/* Environment variables, don't translate! */
+const char *EnvNames [eMaxQuestions] = {
+        "HOME_DIRECTORY",
+       "SYSADMIN_NAME",
+       "SYSADMIN_PW",
+       "CITADEL_UID",
+       "IP_ADDR",
+       "CITADEL_PORT",
+       "ENABLE_UNIX_AUTH",
+       "LDAP_HOST",
+       "LDAP_PORT",
+       "LDAP_BASE_DN",
+       "LDAP_BIND_DN",
+       "LDAP_BIND_PW"
+};
+
 int setup_type;
-char setup_directory[PATH_MAX];
 int using_web_installer = 0;
 int enable_home = 1;
 char admin_pass[SIZ];
 char admin_cmd[SIZ];
 
-char *setup_titles[] =
-{
-       "Citadel Home Directory",
-       "System Administrator",
-       "Administrator Password",
-       "Citadel User ID",
-       "Server IP address",
-       "Server port number",
-       "Authentication mode",
-       "LDAP host",
-       "LDAP port number",
-       "LDAP base DN",
-       "LDAP bind DN",
-       "LDAP bind password"
-};
+const char *setup_titles[eMaxQuestions];
+const char *setup_text[eMaxQuestions];
 
-/**
- * \brief print the actual stack frame.
- */
-void cit_backtrace(void)
-{
-#ifdef HAVE_BACKTRACE
-       void *stack_frames[50];
-       size_t size, i;
-       char **strings;
+/* calculate all our path on a central place */
+/* where to keep our config */
+       
 
 
-       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
-       strings = backtrace_symbols(stack_frames, size);
-       for (i = 0; i < size; i++) {
-               if (strings != NULL)
-                       fprintf(stderr, "%s\n", strings[i]);
-               else
-                       fprintf(stderr, "%p\n", stack_frames[i]);
-       }
-       free(strings);
+void SetTitles(void)
+{
+       int have_run_dir;
+#ifndef HAVE_RUN_DIR
+       have_run_dir = 1;
+#else
+       have_run_dir = 0;
 #endif
-}
 
-struct config config;
+#ifdef ENABLE_NLS
+       setlocale(LC_MESSAGES, getenv("LANG"));
 
-       /* calculate all our path on a central place */
-    /* where to keep our config */
-       
+       bindtextdomain("citadel-setup", LOCALEDIR"/locale");
+       textdomain("citadel-setup");
+       bind_textdomain_codeset("citadel-setup","UTF8");
+#endif
 
-char *setup_text[] = {
-#ifndef HAVE_RUN_DIR
+       setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
+       if (have_run_dir)
+               setup_text[eCitadelHomeDir] = _(
 "Enter the full pathname of the directory in which the Citadel\n"
 "installation you are creating or updating resides.  If you\n"
 "specify a directory other than the default, you will need to\n"
-"specify the -h flag to the server when you start it up.\n",
-#else
+"specify the -h flag to the server when you start it up.\n");
+       else
+               setup_text[eCitadelHomeDir] = _(
 "Enter the subdirectory name for an alternate installation of "
 "Citadel. To do a default installation just leave it blank."
 "If you specify a directory other than the default, you will need to\n"
 "specify the -h flag to the server when you start it up.\n"
-"note that it may not have a leading /",
-#endif
+"note that it may not have a leading /");
+
+
+       setup_titles[eSysAdminName] = _("Citadel administrator username:");
+       setup_text[eSysAdminName] = _(
+"Please enter the name of the Citadel user account that should be granted "
+"administrative privileges once created. If using internal authentication "
+"this user account will be created if it does not exist. For external "
+"authentication this user account has to exist.");
 
-"Enter the name of the system administrator (which is probably\n"
-"you).  When an account is created with this name, it will\n"
-"automatically be given administrator-level access.\n",
 
+       setup_titles[eSysAdminPW] = _("Administrator password:");
+       setup_text[eSysAdminPW] = _(
 "Enter a password for the system administrator. When setup\n"
 "completes it will attempt to create the administrator user\n"
-"and set the password specified here.\n",
+"and set the password specified here.\n");
 
+       setup_titles[eUID] = _("Citadel User ID:");
+       setup_text[eUID] = _(
 "Citadel needs to run under its own user ID.  This would\n"
 "typically be called \"citadel\", but if you are running Citadel\n"
 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
 "The server will run under this user ID.  Please specify that\n"
 "user ID here.  You may specify either a user name or a numeric\n"
-"UID.\n",
-
-"Specify the IP address on which your server will run.  If you\n"
-"leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
-"on all addresses.  You can usually skip this unless you are\n"
-"running multiple instances of Citadel on the same computer.\n",
-
+"UID.\n");
+
+       setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
+       setup_text[eIP_ADDR] = _(
+"Please specify the IP address which the server should be listening to. "
+"You can name a specific IPv4 or IPv6 address, or you can specify\n"
+"\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
+"for \"any IPv4 address\". If you leave this blank, Citadel will\n"
+"listen on all addresses. "
+"This can usually be left to the default unless multiple instances of Citadel "
+"are running on the same computer.");
+
+       setup_titles[eCTDL_Port] = _("Server port number:");
+       setup_text[eCTDL_Port] = _(
 "Specify the TCP port number on which your server will run.\n"
 "Normally, this will be port 504, which is the official port\n"
 "assigned by the IANA for Citadel servers.  You will only need\n"
 "to specify a different port number if you run multiple instances\n"
 "of Citadel on the same computer and there is something else\n"
-"already using port 504.\n",
-
-
-
-"Specify which authentication mode you wish to use.\n"
+"already using port 504.\n");
+
+       setup_titles[eAuthType] = _("Authentication method to use:");
+       setup_text[eAuthType] = _(
+"Please choose the user authentication mode. By default Citadel will use its "
+"own internal user accounts database. If you choose Host, Citadel users will "
+"have accounts on the host system, authenticated via /etc/passwd or a PAM "
+"source. LDAP chooses an RFC 2307 compliant directory server, the last option "
+"chooses the nonstandard MS Active Directory LDAP scheme."
+"\n"
+"Do not change this option unless you are sure it is required, since changing "
+"back requires a full reinstall of Citadel."
 "\n"
 " 0. Self contained authentication\n"
 " 1. Host system integrated authentication\n"
@@ -149,27 +203,75 @@ char *setup_text[] = {
 "\n"
 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
 "\n"
-"ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n",
+"ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
 
-"Please enter the host name or IP address of your LDAP server.\n",
+       setup_titles[eLDAP_Host] = _("LDAP host:");
+       setup_text[eLDAP_Host] = _(
+"Please enter the host name or IP address of your LDAP server.\n");
 
-"Please enter the port number of the LDAP service (usually 389).\n",
+       setup_titles[eLDAP_Port] = _("LDAP port number:");
+       setup_text[eLDAP_Port] = _(
+"Please enter the port number of the LDAP service (usually 389).\n");
 
+       setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
+       setup_text[eLDAP_Base_DN] = _(
 "Please enter the Base DN to search for authentication\n"
-"(for example: dc=example,dc=com)\n",
+"(for example: dc=example,dc=com)\n");
 
+       setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
+       setup_text[eLDAP_Bind_DN] = _(
+"Please enter the DN of an account to use for binding to the LDAP server for "
+"performing queries. The account does not require any other privileges. If "
+"your LDAP server allows anonymous queries, you can leave this blank."
 "Please enter the DN of an account to use for binding to the LDAP server\n"
 "for performing queries.  The account does not require any other\n"
-"privileges.  If your LDAP server allows anonymous queries, you can.\n"
-"leave this blank.\n",
+"privileges.  If your LDAP server allows anonymous queries, you can\n"
+"leave this blank.\n");
 
+       setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
+       setup_text[eLDAP_Bind_PW] = _(
 "If you entered a Bind DN in the previous question, you must now enter\n"
 "the password associated with that account.  Otherwise, you can leave this\n"
-"blank.\n"
+"blank.\n");
 
-};
+#if 0
+// Debug loading of locales... Strace does a better job though.
+       printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
+       printf("Text domain: %s\n", textdomain("citadel-setup"));
+       printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
+       {
+               int i;
+               for (i = 0; i < eMaxQuestions; i++)
+                       printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
+               exit(0);
+       }
+#endif
+}
+
+/*
+ * print the actual stack frame.
+ */
+void cit_backtrace(void)
+{
+#ifdef HAVE_BACKTRACE
+       void *stack_frames[50];
+       size_t size, i;
+       char **strings;
+
+       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
+       strings = backtrace_symbols(stack_frames, size);
+       for (i = 0; i < size; i++) {
+               if (strings != NULL)
+                       fprintf(stderr, "%s\n", strings[i]);
+               else
+                       fprintf(stderr, "%p\n", stack_frames[i]);
+       }
+       free(strings);
+#endif
+}
 
 struct config config;
+
 int direction;
 
 
@@ -182,7 +284,7 @@ void cleanup(int exitcode)
 
 
 
-void title(char *text)
+void title(const char *text)
 {
        if (setup_type == UI_TEXT) {
                printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
@@ -191,7 +293,7 @@ void title(char *text)
 
 
 
-int yesno(char *question, int default_value)
+int yesno(const char *question, int default_value)
 {
        int i = 0;
        int answer = 0;
@@ -201,9 +303,10 @@ int yesno(char *question, int default_value)
 
        case UI_TEXT:
                do {
-                       printf("%s\nYes/No [%s] --> ",
-                               question,
-                               ( default_value ? "Yes" : "No" )
+                       printf("%s\n%s [%s] --> ",
+                              question,
+                              _("Yes/No"),
+                              ( default_value ? _("Yes") : _("No") )
                        );
                        if (fgets(buf, sizeof buf, stdin))
                        {
@@ -239,7 +342,7 @@ int yesno(char *question, int default_value)
 }
 
 
-void important_message(char *title, char *msgtext)
+void important_message(const char *title, const char *msgtext)
 {
        char buf[SIZ];
        int rv;
@@ -249,7 +352,7 @@ void important_message(char *title, char *msgtext)
        case UI_TEXT:
                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");
                printf("       %s \n\n%s\n\n", title, msgtext);
-               printf("Press return to continue...");
+               printf("%s", _("Press return to continue..."));
                if (fgets(buf, sizeof buf, stdin));
                break;
 
@@ -258,6 +361,8 @@ void important_message(char *title, char *msgtext)
                        getenv("CTDL_DIALOG"),
                        msgtext);
                rv = system(buf);
+               if (rv != 0)
+                       fprintf(stderr, _("failed to run the dialog command\n"));
                break;
        case UI_SILENT:
                fprintf(stderr, "%s\n", msgtext);
@@ -267,12 +372,23 @@ void important_message(char *title, char *msgtext)
 
 void important_msgnum(int msgnum)
 {
-       important_message("Important Message", setup_text[msgnum]);
+       important_message(_("Important Message"), setup_text[msgnum]);
 }
 
-void display_error(char *error_message)
+void display_error(char *error_message_format, ...)
 {
-       important_message("Error", error_message);
+       StrBuf *Msg;
+       va_list arg_ptr;
+
+       Msg = NewStrBuf();
+       va_start(arg_ptr, error_message_format);
+       StrBufVAppendPrintf(Msg, 
+                           error_message_format, 
+                           arg_ptr);
+       va_end(arg_ptr);
+
+       important_message(_("Error"), ChrPtr(Msg));
+       FreeStrBuf(&Msg);
 }
 
 void progress(char *text, long int curr, long int cmax)
@@ -287,8 +403,7 @@ void progress(char *text, long int curr, long int cmax)
        case UI_TEXT:
                if (curr == 0) {
                        printf("%s\n", text);
-                       printf("..........................");
-                       printf("..........................");
+                       printf("....................................................");
                        printf("..........................\r");
                        fflush(stdout);
                        dots_printed = 0;
@@ -352,11 +467,11 @@ void check_services_entry(void)
 
        if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
                for (i=0; i<=2; ++i) {
-                       progress("Adding service entry...", i, 2);
+                       progress(_("Adding service entry..."), i, 2);
                        if (i == 0) {
                                sfp = fopen("/etc/services", "a");
                                if (sfp == NULL) {
-                                       sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno));
+                                       sprintf(errmsg, "%s /etc/services: %s", _("Cannot open"), strerror(errno));
                                        display_error(errmsg);
                                } else {
                                        fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
@@ -372,7 +487,6 @@ void check_services_entry(void)
 
 /*
  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
- *
  */
 void delete_inittab_entry(void)
 {
@@ -404,7 +518,7 @@ void delete_inittab_entry(void)
 
                /* Other errors might mean something really did go wrong.
                 */
-               sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
+               sprintf(buf, "%s /etc/inittab: %s", _("Cannot open"), strerror(errno));
                display_error(buf);
                return;
        }
@@ -412,7 +526,7 @@ void delete_inittab_entry(void)
        strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
        outfp = fdopen(mkstemp(outfilename), "w+");
        if (outfp == NULL) {
-               sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
+               sprintf(buf, "%s %s: %s", _("Cannot open"), outfilename, strerror(errno));
                display_error(buf);
                fclose(infp);
                return;
@@ -421,9 +535,21 @@ void delete_inittab_entry(void)
        while (fgets(buf, sizeof buf, infp) != NULL) {
                if (strstr(buf, looking_for) != NULL) {
                        rv = fwrite("#", 1, 1, outfp);
+                       if (rv == -1)
+                       {
+                               display_error("%s %s\n",
+                                             _("failed to modify inittab"), 
+                                             strerror(errno));
+                       }
                        ++changes_made;
                }
                rv = fwrite(buf, strlen(buf), 1, outfp);
+               if (rv == -1)
+               {
+                       display_error("%s %s\n",
+                                     _("failed to modify inittab"), 
+                                     strerror(errno));
+               }
        }
 
        fclose(infp);
@@ -442,7 +568,6 @@ void delete_inittab_entry(void)
 
 /*
  * install_init_scripts()  -- Try to configure to start Citadel at boot
- *
  */
 void install_init_scripts(void)
 {
@@ -464,21 +589,21 @@ void install_init_scripts(void)
 
        fp = fopen(initfile, "r");
        if (fp != NULL) {
-               if (yesno("Citadel already appears to be configured to start at boot.\n"
-                         "Would you like to keep your boot configuration as is?\n", 1) == 1) {
+               if (yesno(_("Citadel already appears to be configured to start at boot.\n"
+                           "Would you like to keep your boot configuration as is?\n"), 1) == 1) {
                        return;
                }
                fclose(fp);
                
        }
 
-       if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
+       if (yesno(_("Would you like to automatically start Citadel at boot?\n"), 1) == 0) {
                return;
        }
 
        fp = fopen(initfile, "w");
        if (fp == NULL) {
-               display_error("Cannot create /etc/init.d/citadel");
+               display_error("%s /etc/init.d/citadel", _("Cannot create"));
                return;
        }
 
@@ -496,8 +621,8 @@ void install_init_scripts(void)
                "\n"
                "CITADEL_DIR=%s\n"
                ,
-               setup_directory,
-               setup_directory
+               ctdl_run_dir,
+               ctdl_sbin_dir
                );
        fprintf(fp,     "\n"
                "test -d /var/run || exit 0\n"
@@ -505,7 +630,7 @@ void install_init_scripts(void)
                "case \"$1\" in\n"
                "\n"
                "start)         echo -n \"Starting Citadel... \"\n"
-               "               if $CITADEL_DIR/citserver -lmail -d -h$CITADEL_DIR\n"
+               "               if $CITADEL_DIR/citserver -lmail -d\n"
                "               then\n"
                "                       echo \"ok\"\n"
                "               else\n"
@@ -520,7 +645,7 @@ void install_init_scripts(void)
                "               fi\n"
                "               rm -f %s/citadel.pid 2>/dev/null\n"
                ,
-               setup_directory
+               ctdl_run_dir
                );
        fprintf(fp,     "               ;;\n"
                "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
@@ -540,18 +665,22 @@ void install_init_scripts(void)
 
        /* Set up the run levels. */
        rv = system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
+       if (rv != 0)
+               display_error(_("failed to remove system V init links \n"));
+
        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);
        rv = system(command);
+       if (rv != 0)
+               display_error(_("failed to set system V init links \n"));
+
        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);
        rv = system(command);
-
+       if (rv != 0)
+               display_error(_("failed to set system V init links \n"));
 }
 
 
 
-
-
-
 /*
  * On systems which use xinetd, see if we can offer to install Citadel as
  * the default telnet target.
@@ -567,7 +696,7 @@ void check_xinetd_entry(void) {
        if (fp == NULL) return;         /* Not there.  Oh well... */
 
        while (fgets(buf, sizeof buf, fp) != NULL) {
-               if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
+               if (strstr(buf, "/citadel") != NULL) already_citadel = 1;
        }
        fclose(fp);
        if (already_citadel) return;    /* Already set up this way. */
@@ -580,10 +709,10 @@ void check_xinetd_entry(void) {
        }
        else {
                snprintf(buf, sizeof buf,
-                        "Setup can configure the \"xinetd\" service to automatically\n"
-                        "connect incoming telnet sessions to Citadel, bypassing the\n"
-                        "host system login: prompt.  Would you like to do this?\n"
-                       );
+                        _("Setup can configure the \"xinetd\" service to automatically\n"
+                          "connect incoming telnet sessions to Citadel, bypassing the\n"
+                          "host system login: prompt.  Would you like to do this?\n"
+                                ));
                if (yesno(buf, 1) == 0) {
                        return;
                }
@@ -608,6 +737,8 @@ void check_xinetd_entry(void) {
 
        /* Now try to restart the service */
        rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
+       if (rv != 0)
+               display_error(_("failed to restart xinetd.\n"));
 }
 
 
@@ -615,7 +746,7 @@ void check_xinetd_entry(void) {
 /*
  * Offer to disable other MTA's
  */
-void disable_other_mta(char *mta) {
+void disable_other_mta(const char *mta) {
        char buf[SIZ];
        FILE *fp;
        int lines = 0;
@@ -637,14 +768,22 @@ void disable_other_mta(char *mta) {
        /* Offer to replace other MTA with the vastly superior Citadel :)  */
 
        snprintf(buf, sizeof buf,
-                "You appear to have the \"%s\" email program\n"
-                "running on your system.  If you want Citadel mail\n"
-                "connected with %s, you will have to manually integrate\n"
-                "them.  It is preferable to disable %s, and use Citadel's\n"
-                "SMTP, POP3, and IMAP services.\n\n"
-                "May we disable %s so that Citadel has access to ports\n"
-                "25, 110, and 143?\n",
-                mta, mta, mta, mta
+                "%s \"%s\" %s%s%s%s%s%s%s", 
+                _("You appear to have the "), 
+                mta, 
+                _(" email program\n"
+                  "running on your system.  If you want Citadel mail\n"
+                  "connected with "), 
+                mta,
+                _(" you will have to manually integrate\n"
+                  "them.  It is preferable to disable "), 
+                mta,
+                _(", and use Citadel's\n"
+                  "SMTP, POP3, and IMAP services.\n\n"
+                  "May we disable "), 
+                mta, 
+                _("so that Citadel has access to ports\n"
+                  "25, 110, and 143?\n")
                );
        if (yesno(buf, 1) == 0) {
                return;
@@ -653,17 +792,58 @@ void disable_other_mta(char *mta) {
 
        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);
        rv = system(buf);
+       if (rv != 0)
+               display_error("%s %s.\n", _("failed to disable other mta"), mta);
+
        sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
        rv = system(buf);
+       if (rv != 0)
+               display_error(" %s.\n", _("failed to disable other mta"), mta);
 }
 
+const char *other_mtas[] = {
+       "courier-authdaemon",
+       "courier-imap",
+       "courier-imap-ssl",
+       "courier-pop",
+       "courier-pop3",
+       "courier-pop3d",
+       "cyrmaster",
+       "cyrus",
+       "dovecot",
+       "exim",
+       "exim4",
+       "imapd",
+       "mta",
+       "pop3d",
+       "popd",
+       "postfix",
+       "qmail",
+       "saslauthd",
+       "sendmail",
+       "vmailmgrd",
+       ""
+};
 
-
+void disable_other_mtas(void)
+{
+       int i = 0;
+       if ((getenv("ACT_AS_MTA") == NULL) || 
+           (getenv("ACT_AS_MTA") &&
+            strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
+               /* Offer to disable other MTA's on the system. */
+               while (!IsEmptyStr(other_mtas[i]))
+               {
+                       disable_other_mta(other_mtas[i]);
+                       i++;
+               }
+       }
+}
 
 /* 
  * Check to see if our server really works.  Returns 0 on success.
  */
-int test_server(char *setup_directory, char *relhomestr, int relhome) {
+int test_server(char *relhomestr, int relhome) {
        char cmd[256];
        char cookie[256];
        FILE *fp;
@@ -703,7 +883,7 @@ int test_server(char *setup_directory, char *relhomestr, int relhome) {
        return(-1);
 }
 
-void strprompt(char *prompt_title, char *prompt_text, char *str)
+void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
 {
        char buf[SIZ] = "";
        char setupmsg[SIZ];
@@ -717,13 +897,13 @@ void strprompt(char *prompt_title, char *prompt_text, char *str)
        case UI_TEXT:
                title(prompt_title);
                printf("\n%s\n", prompt_text);
-               printf("This is currently set to:\n%s\n", str);
-               printf("Enter new value or press return to leave unchanged:\n");
+               printf("%s\n%s\n", _("This is currently set to:"), Target);
+               printf("%s\n", _("Enter new value or press return to leave unchanged:"));
                if (fgets(buf, sizeof buf, stdin)){
                        buf[strlen(buf) - 1] = 0;
                }
                if (!IsEmptyStr(buf))
-                       strcpy(str, buf);
+                       strcpy(Target, buf);
                break;
 
        case UI_DIALOG:
@@ -731,14 +911,17 @@ void strprompt(char *prompt_title, char *prompt_text, char *str)
                sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
                        getenv("CTDL_DIALOG"),
                        prompt_text,
-                       str,
+                       Target,
                        dialog_result);
                rv = system(buf);
+               if (rv != 0)
+                       fprintf(stderr, "failed to run Dialog.\n");
+               
                fp = fopen(dialog_result, "r");
                if (fp != NULL) {
-                       if (fgets(str, sizeof buf, fp)) {
-                               if (str[strlen(str)-1] == 10) {
-                                       str[strlen(str)-1] = 0;
+                       if (fgets(Target, sizeof buf, fp)) {
+                               if (Target[strlen(Target)-1] == 10) {
+                                       Target[strlen(Target)-1] = 0;
                                }
                        }
                        fclose(fp);
@@ -746,42 +929,49 @@ void strprompt(char *prompt_title, char *prompt_text, char *str)
                }
                break;
        case UI_SILENT:
+               if (*DefValue != '\0')
+                       strcpy(Target, DefValue);
                break;
        }
 }
 
-void set_bool_val(int msgpos, int *ip) {
+void set_bool_val(int msgpos, int *ip, char *DefValue) 
+{
        title(setup_titles[msgpos]);
        *ip = yesno(setup_text[msgpos], *ip);
 }
 
-void set_str_val(int msgpos, char *str) {
-       strprompt(setup_titles[msgpos], setup_text[msgpos], str);
+void set_str_val(int msgpos, char *Target, char *DefValue) 
+{
+       strprompt(setup_titles[msgpos], 
+                 setup_text[msgpos], 
+                 Target, 
+                 DefValue);
 }
 
-void set_int_val(int msgpos, int *ip)
+void set_int_val(int msgpos, int *ip, char *DefValue)
 {
        char buf[16];
        snprintf(buf, sizeof buf, "%d", (int) *ip);
-       set_str_val(msgpos, buf);
+       set_str_val(msgpos, buf, DefValue);
        *ip = atoi(buf);
 }
 
 
-void set_char_val(int msgpos, char *ip)
+void set_char_val(int msgpos, char *ip, char *DefValue)
 {
        char buf[16];
        snprintf(buf, sizeof buf, "%d", (int) *ip);
-       set_str_val(msgpos, buf);
+       set_str_val(msgpos, buf, DefValue);
        *ip = (char) atoi(buf);
 }
 
 
-void set_long_val(int msgpos, long int *ip)
+void set_long_val(int msgpos, long int *ip, char *DefValue)
 {
        char buf[16];
        snprintf(buf, sizeof buf, "%ld", *ip);
-       set_str_val(msgpos, buf);
+       set_str_val(msgpos, buf, DefValue);
        *ip = atol(buf);
 }
 
@@ -791,38 +981,31 @@ void edit_value(int curr)
        int i;
        struct passwd *pw;
        char ctdluidname[256];
+       char *Value = NULL;
+
+       if (setup_type == UI_SILENT)
+       {
+               Value = getenv(EnvNames[curr]);
+       }
+       if (Value == NULL)
+               Value = "";
+
 
        switch (curr) {
 
-       case 1:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("SYSADMIN_NAME")) {
-                               strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
-                       }
-               }
-               else {
-                       set_str_val(curr, config.c_sysadm);
-               }
+       case eSysAdminName:
+               set_str_val(curr, config.c_sysadm, Value);
                break;
 
-       case 2:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("SYSADMIN_PW")) {
-                               strcpy(admin_pass, getenv("SYSADMIN_PW"));
-                       }
-               }
-               else {
-                       set_str_val(curr, admin_pass);
-               }
+       case eSysAdminPW:
+               set_str_val(curr, admin_pass, Value);
                break;
        
-       case 3:
+       case eUID:
                if (setup_type == UI_SILENT)
                {               
-                       if (getenv("CITADEL_UID")) {
-                               config.c_ctdluid = atoi(getenv("CITADEL_UID"));
+                       if (Value) {
+                               config.c_ctdluid = atoi(Value);
                        }                                       
                }
                else
@@ -833,12 +1016,12 @@ void edit_value(int curr)
                        i = config.c_ctdluid;
                        pw = getpwuid(i);
                        if (pw == NULL) {
-                               set_int_val(curr, &i);
+                               set_int_val(curr, &i, Value);
                                config.c_ctdluid = i;
                        }
                        else {
                                strcpy(ctdluidname, pw->pw_name);
-                               set_str_val(curr, ctdluidname);
+                               set_str_val(curr, ctdluidname, Value);
                                pw = getpwnam(ctdluidname);
                                if (pw != NULL) {
                                        config.c_ctdluid = pw->pw_uid;
@@ -851,37 +1034,20 @@ void edit_value(int curr)
                }
                break;
 
-       case 4:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("IP_ADDR")) {
-                               strcpy(config.c_ip_addr, getenv("IP_ADDR"));
-                       }
-               }
-               else {
-                       set_str_val(curr, config.c_ip_addr);
-               }
+       case eIP_ADDR:
+               set_str_val(curr, config.c_ip_addr, Value);
                break;
 
-       case 5:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("CITADEL_PORT")) {
-                               config.c_port_number = atoi(getenv("CITADEL_PORT"));
-                       }
-               }
-               else
-               {
-                       set_int_val(curr, &config.c_port_number);
-               }
+       case eCTDL_Port:
+               set_int_val(curr, &config.c_port_number, Value);
                break;
 
-       case 6:
+       case eAuthType:
                if (setup_type == UI_SILENT)
                {
                        const char *auth;
                        config.c_auth_mode = AUTHMODE_NATIVE;
-                       auth = getenv("ENABLE_UNIX_AUTH");
+                       auth = Value;
                        if (auth != NULL)
                        {
                                if ((strcasecmp(auth, "yes") == 0) ||
@@ -899,77 +1065,34 @@ void edit_value(int curr)
                        }
                }
                else {
-                       set_int_val(curr, &config.c_auth_mode);
+                       set_int_val(curr, &config.c_auth_mode, Value);
                }
                break;
 
-       case 7:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_HOST")) {
-                               strcpy(config.c_ldap_host, getenv("LDAP_HOST"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_host);
-               }
+       case eLDAP_Host:
+               set_str_val(curr, config.c_ldap_host, Value);
                break;
 
-       case 8:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_PORT")) {
-                               config.c_ldap_port = atoi(getenv("LDAP_PORT"));
-                       }
-               }
-               else
-               {
-                       set_int_val(curr, &config.c_ldap_port);
+       case eLDAP_Port:
+               if (config.c_ldap_port == 0) {
+                       config.c_ldap_port = 389;
                }
+               set_int_val(curr, &config.c_ldap_port, Value);
                break;
 
-       case 9:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_BASE_DN")) {
-                               strcpy(config.c_ldap_base_dn, getenv("LDAP_BASE_DN"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_base_dn);
-               }
+       case eLDAP_Base_DN:
+               set_str_val(curr, config.c_ldap_base_dn, Value);
                break;
 
-       case 10:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_BIND_DN")) {
-                               strcpy(config.c_ldap_bind_dn, getenv("LDAP_BIND_DN"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_bind_dn);
-               }
+       case eLDAP_Bind_DN:
+               set_str_val(curr, config.c_ldap_bind_dn, Value);
                break;
 
-       case 11:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_BIND_PW")) {
-                               strcpy(config.c_ldap_bind_pw, getenv("LDAP_BIND_PW"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_bind_pw);
-               }
+       case eLDAP_Bind_PW:
+               set_str_val(curr, config.c_ldap_bind_pw, Value);
                break;
 
        }
-
 }
 
 /*
@@ -982,15 +1105,20 @@ void write_config_to_disk(void)
        int rv;
 
        if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
-               display_error("setup: cannot open citadel.config");
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot open"), file_citadel_config, strerror(errno));
                cleanup(1);
        }
        fp = fdopen(fd, "wb");
        if (fp == NULL) {
-               display_error("setup: cannot open citadel.config");
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot open"), file_citadel_config, strerror(errno));
                cleanup(1);
+               return;
        }
        rv = fwrite((char *) &config, sizeof(struct config), 1, fp);
+
+       if (rv == -1)
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot write"), file_citadel_config, strerror(errno));
+
        fclose(fp);
 }
 
@@ -1013,8 +1141,6 @@ int discover_ui(void)
 
 
 
-
-
 /*
  * Strip "db" entries out of /etc/nsswitch.conf
  */
@@ -1025,7 +1151,6 @@ void fixnss(void) {
        char buf_nc[256];
        char question[512];
        int i;
-       int changed = 0;
        int file_changed = 0;
        char new_filename[64];
        int rv;
@@ -1043,7 +1168,6 @@ void fixnss(void) {
        }
 
        while (fgets(buf, sizeof buf, fp_read) != NULL) {
-               changed = 0;
                strcpy(buf_nc, buf);
                for (i=0; i<strlen(buf_nc); ++i) {
                        if (buf_nc[i] == '#') {
@@ -1054,7 +1178,6 @@ void fixnss(void) {
                        if (!strncasecmp(&buf_nc[i], "db", 2)) {
                                if (i > 0) {
                                        if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
-                                               changed = 1;
                                                file_changed = 1;
                                                strcpy(&buf_nc[i], &buf_nc[i+2]);
                                                strcpy(&buf[i], &buf[i+2]);
@@ -1082,179 +1205,146 @@ void fixnss(void) {
        }
 
        snprintf(question, sizeof question,
-               "\n"
-               "/etc/nsswitch.conf is configured to use the 'db' module for\n"
-               "one or more services.  This is not necessary on most systems,\n"
-               "and it is known to crash the Citadel server when delivering\n"
-               "mail to the Internet.\n"
-               "\n"
-               "Do you want this module to be automatically disabled?\n"
-               "\n"
+                _(
+                        "\n"
+                        "/etc/nsswitch.conf is configured to use the 'db' module for\n"
+                        "one or more services.  This is not necessary on most systems,\n"
+                        "and it is known to crash the Citadel server when delivering\n"
+                        "mail to the Internet.\n"
+                        "\n"
+                        "Do you want this module to be automatically disabled?\n"
+                        "\n"
+                        )
        );
 
        if (yesno(question, 1)) {
                sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
                rv = system(buf);
+               if (rv != 0)
+                       fprintf(stderr, "failed to edit %s.\n", NSSCONF);
+
                chmod(NSSCONF, 0644);
        }
        unlink(new_filename);
 }
 
+void check_init_script (char *relhome)
+{
+       int rv;
+       FILE *fp;
+
+       /* 
+        * If we're running on SysV, install init scripts.
+        */
+       if (!access("/var/run", W_OK)) {
+
+               if (getenv("NO_INIT_SCRIPTS") == NULL) {
+                       install_init_scripts();
+               }
+
+               if (!access("/etc/init.d/citadel", X_OK)) {
+                       rv = system("/etc/init.d/citadel start");
+                       if (rv != 0)
+                               fprintf(stderr, "failed to call our initscript.");
+                       sleep(3);
+               }
 
+               if (test_server(relhome, enable_home) == 0) {
+                       char buf[SIZ];
+                       int found_it = 0;
 
+                       if (config.c_auth_mode == AUTHMODE_NATIVE) {
+                               snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
+                                       ctdl_sbin_dir, config.c_sysadm, admin_pass);
+                               fp = popen(admin_cmd, "r");
+                               if (fp != NULL) {
+                                       while (fgets(buf, sizeof buf, fp) != NULL) 
+                                       {
+                                               if ((atol(buf) == 574) || (atol(buf) == 200))
+                                                       ++found_it;
+                                       }
+                                       pclose(fp);
+                               }
+                       
+                               if (found_it == 0) {
+                                       important_message("Error","Setup failed to create your admin user");
+                               }
+                       }
 
+                       if (setup_type != UI_SILENT)
+                               important_message(_("Setup finished"),
+                                                 _("Setup of the Citadel server is complete.\n"
+                                                   "If you will be using WebCit, please run its\n"
+                                                   "setup program now; otherwise, run './citadel'\n"
+                                                   "to log in.\n"));
+               }
+               else {
+                       important_message(_("Setup failed"),
+                                         _("Setup is finished, but the Citadel server failed to start.\n"
+                                           "Go back and check your configuration.\n")
+                               );
+               }
 
+       }
 
+       else {
+               important_message(_("Setup finished"),
+                                 _("Setup is finished.  You may now start the server."));
+       }
+}
 
 
-int main(int argc, char *argv[])
+
+#define GetDefaultVALINT(CFGNAME, DEFL) GetDefaultValInt(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
+void GetDefaultValInt(int *WhereTo, const char *VarName, int DefVal)
 {
-       int a;
-       int curr; 
-       char aaa[128];
-       FILE *fp;
-       int old_setup_level = 0;
-       int info_only = 0;
-       struct utsname my_utsname;
-       struct passwd *pw;
-       struct hostent *he;
-       gid_t gid;
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       int rv;
-       
-       /* set an invalid setup type */
-       setup_type = (-1);
+       const char *ch;
+       if (*WhereTo == 0) *WhereTo = DefVal;
 
-       /* Check to see if we're running the web installer */
-       if (getenv("CITADEL_INSTALLER") != NULL) {
-               using_web_installer = 1;
+       if ((setup_type == UI_SILENT) &&
+           (ch = getenv(VarName), ch != NULL))
+       {
+               *WhereTo = atoi(ch);
        }
+}
+#define GetDefaultVALCHAR(CFGNAME, DEFL) GetDefaultValChar(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
+void GetDefaultValChar(char *WhereTo, const char *VarName, char DefVal)
+{
+       const char *ch;
+       if (*WhereTo == 0) *WhereTo = DefVal;
 
-       /* parse command line args */
-       for (a = 0; a < argc; ++a) {
-               if (!strncmp(argv[a], "-u", 2)) {
-                       strcpy(aaa, argv[a]);
-                       strcpy(aaa, &aaa[2]);
-                       setup_type = atoi(aaa);
-               }
-               else if (!strcmp(argv[a], "-i")) {
-                       info_only = 1;
-               }
-               else if (!strcmp(argv[a], "-q")) {
-                       setup_type = UI_SILENT;
-               }
-               else if (!strncmp(argv[a], "-h", 2)) {
-                       relh=argv[a][2]!='/';
-                       if (!relh) {
-                               safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
-                       } else {
-                               safestrncpy(relhome, &argv[a][2], sizeof relhome);
-                       }
-                       home = 1;
-               }
-
-       }
-
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-       /* If a setup type was not specified, try to determine automatically
-        * the best one to use out of all available types.
-        */
-       if (setup_type < 0) {
-               setup_type = discover_ui();
-       }
-       if (info_only == 1) {
-               important_message("Citadel Setup", CITADEL);
-               cleanup(0);
+       if ((setup_type == UI_SILENT) &&
+           (ch = getenv(VarName), ch != NULL))
+       {
+               *WhereTo = atoi(ch);
        }
+}
+#define GetDefaultVALSTR(CFGNAME, DEFL) GetDefaultValStr(&config.CFGNAME[0], sizeof(config.CFGNAME), "CITADEL_"#CFGNAME, DEFL)
+void GetDefaultValStr(char *WhereTo, size_t nMax, const char *VarName, const char *DefVal)
+{
+       const char *ch;
+       if (*WhereTo == '\0') 
+               safestrncpy(WhereTo, DefVal, nMax);
 
-       /* Get started in a valid setup directory. */
-       strcpy(setup_directory, ctdl_run_dir);
-       if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
-               strcpy(setup_directory, getenv("CITADEL"));
-       }
-       else {
-               set_str_val(0, setup_directory);
+       if ((setup_type == UI_SILENT) &&
+           (ch = getenv(VarName), ch != NULL))
+       {
+               safestrncpy(WhereTo, ch, nMax);
        }
+}
 
-       enable_home = ( relh | home );
 
-       if (chdir(setup_directory) != 0) {
-               char errmsg[SIZ];
-               sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
-               
-               important_message("Citadel Setup", errmsg);
-               cleanup(errno);
-       }
+void set_default_values(void)
+{
+       struct passwd *pw;
+       struct utsname my_utsname;
+       struct hostent *he;
 
        /* Determine our host name, in case we need to use it as a default */
        uname(&my_utsname);
 
-       /* Try to stop Citadel if we can */
-       if (!access("/etc/init.d/citadel", X_OK)) {
-               rv = system("/etc/init.d/citadel stop");
-       }
-
-       /* Make sure Citadel is not running. */
-       if (test_server(setup_directory, relhome, enable_home) == 0) {
-               important_message("Citadel Setup",
-                       "The Citadel service is still running.\n"
-                       "Please stop the service manually and run "
-                       "setup again.");
-               cleanup(1);
-       }
-
-       /* Now begin. */
-       switch (setup_type) {
-
-       case UI_TEXT:
-               printf("\n\n\n"
-                       "              *** Citadel setup program ***\n\n");
-               break;
-
-       }
-
-       /*
-        * What we're going to try to do here is append a whole bunch of
-        * nulls to the citadel.config file, so we can keep the old config
-        * values if they exist, but if the file is missing or from an
-        * earlier version with a shorter config structure, when setup tries
-        * to read the old config parameters, they'll all come up zero.
-        * The length of the config file will be set to what it's supposed
-        * to be when we rewrite it, because we replace the old file with a
-        * completely new copy.
-        */
-       if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
-                     S_IRUSR | S_IWUSR)) == -1) {
-               display_error("setup: cannot append citadel.config");
-               cleanup(errno);
-       }
-       fp = fdopen(a, "ab");
-       if (fp == NULL) {
-               display_error("setup: cannot append citadel.config");
-               cleanup(errno);
-       }
-       for (a = 0; a < sizeof(struct config); ++a) {
-               putc(0, fp);
-       }
-       fclose(fp);
-
-       /* now we re-open it, and read the old or blank configuration */
-       fp = fopen(file_citadel_config, "rb");
-       if (fp == NULL) {
-               display_error("setup: cannot open citadel.config");
-               cleanup(errno);
-       }
-       rv = fread((char *) &config, sizeof(struct config), 1, fp);
-       fclose(fp);
-
        /* set some sample/default values in place of blanks... */
-       if (IsEmptyStr(config.c_nodename))
-               safestrncpy(config.c_nodename, my_utsname.nodename,
-                           sizeof config.c_nodename);
+       GetDefaultVALSTR(c_nodename, my_utsname.nodename);
        strtok(config.c_nodename, ".");
        if (IsEmptyStr(config.c_fqdn) ) {
                if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
@@ -1263,25 +1353,19 @@ int main(int argc, char *argv[])
                        safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
                }
        }
-       if (IsEmptyStr(config.c_humannode)) {
-               strcpy(config.c_humannode, "My System");
-       }
-       if (IsEmptyStr(config.c_phonenum)) {
-               strcpy(config.c_phonenum, "US 800 555 1212");
-       }
-       if (config.c_initax == 0) {
-               config.c_initax = 4;
-       }
-       if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, "<more>");
-       if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan");
-       if (IsEmptyStr(config.c_baseroom)) strcpy(config.c_baseroom, BASEROOM);
-       if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide");
-       if (config.c_port_number == 0) {
-               config.c_port_number = 504;
-       }
-       if (config.c_sleeping == 0) {
-               config.c_sleeping = 900;
-       }
+       GetDefaultVALSTR(c_humannode, _("My System"));
+       GetDefaultVALSTR(c_phonenum, _("US 800 555 1212"));
+
+       GetDefaultVALCHAR(c_initax, 4);
+
+       GetDefaultVALSTR(c_moreprompt, "<more>");
+       GetDefaultVALSTR(c_twitroom, "Trashcan");
+       GetDefaultVALSTR(c_baseroom, BASEROOM);
+       GetDefaultVALSTR(c_aideroom, "Aide");
+       GetDefaultVALINT(c_port_number, 504);
+       
+       GetDefaultVALINT(c_sleeping, 900);
+
        if (config.c_ctdluid == 0) {
                pw = getpwnam("citadel");
                if (pw != NULL) {
@@ -1321,131 +1405,200 @@ int main(int argc, char *argv[])
        /*
         * Default port numbers for various services
         */
-       if (config.c_smtp_port == 0) config.c_smtp_port = 25;
-       if (config.c_pop3_port == 0) config.c_pop3_port = 110;
-       if (config.c_imap_port == 0) config.c_imap_port = 143;
-       if (config.c_msa_port == 0) config.c_msa_port = 587;
-       if (config.c_smtps_port == 0) config.c_smtps_port = 465;
-       if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
-       if (config.c_imaps_port == 0) config.c_imaps_port = 993;
-       if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
-       if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
-       if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
-       if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
+       GetDefaultVALINT(c_smtp_port, 25);
+       GetDefaultVALINT(c_pop3_port, 110);
+       GetDefaultVALINT(c_imap_port, 143);
+       GetDefaultVALINT(c_msa_port, 587);
+       GetDefaultVALINT(c_smtps_port, 465);
+       GetDefaultVALINT(c_pop3s_port, 995);
+       GetDefaultVALINT(c_imaps_port, 993);
+       GetDefaultVALINT(c_pftcpdict_port, -1);
+       GetDefaultVALINT(c_managesieve_port, 2020);
+       GetDefaultVALINT(c_xmpp_c2s_port, 5222);
+       GetDefaultVALINT(c_xmpp_s2s_port, 5269);
+}
 
-       /* Go through a series of dialogs prompting for config info */
-       for (curr = 1; curr <= MAXSETUP; ++curr) {
-               edit_value(curr);
-               if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
-                       curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
-               }
+
+void get_config (void)
+{
+       int a;
+       int rv;
+       FILE *fp;
+
+       /*
+        * What we're going to try to do here is append a whole bunch of
+        * nulls to the citadel.config file, so we can keep the old config
+        * values if they exist, but if the file is missing or from an
+        * earlier version with a shorter config structure, when setup tries
+        * to read the old config parameters, they'll all come up zero.
+        * The length of the config file will be set to what it's supposed
+        * to be when we rewrite it, because we replace the old file with a
+        * completely new copy.
+        */
+       if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
+                     S_IRUSR | S_IWUSR)) == -1) {
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot append"), file_citadel_config, strerror(errno));
+               cleanup(errno);
+       }
+       fp = fdopen(a, "ab");
+       if (fp == NULL) {
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot append"), file_citadel_config, strerror(errno));
+               cleanup(errno);
+       }
+       for (a = 0; a < sizeof(struct config); ++a) {
+               putc(0, fp);
        }
+       fclose(fp);
 
-/***** begin version update section ***** */
-       /* take care of any updating that is necessary */
+       /* now we re-open it, and read the old or blank configuration */
+       fp = fopen(file_citadel_config, "rb");
+       if (fp == NULL) {
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot open"), file_citadel_config, strerror(errno));
+               cleanup(errno);
+               return;
+       }
+       rv = fread((char *) &config, sizeof(struct config), 1, fp);
+       if (rv == -1)
+               display_error("%s citadel.config [%s][%s]\n", _("setup: cannot write"), file_citadel_config, strerror(errno));
+       fclose(fp);
 
-       old_setup_level = config.c_setup_level;
+}
 
-       if (old_setup_level == 0) {
-               goto NEW_INST;
+int main(int argc, char *argv[])
+{
+       int a;
+       int curr; 
+       char aaa[128];
+       int old_setup_level = 0;
+       int info_only = 0;
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       int rv;
+       struct passwd *pw;
+       gid_t gid;
+       
+       /* set an invalid setup type */
+       setup_type = (-1);
+
+       /* Check to see if we're running the web installer */
+       if (getenv("CITADEL_INSTALLER") != NULL) {
+               using_web_installer = 1;
        }
 
-       if (old_setup_level < 555) {
-               important_message("Citadel Setup",
-                                 "This Citadel installation is too old "
-                                 "to be upgraded.");
-               cleanup(1);
+       /* parse command line args */
+       for (a = 0; a < argc; ++a) {
+               if (!strncmp(argv[a], "-u", 2)) {
+                       strcpy(aaa, argv[a]);
+                       strcpy(aaa, &aaa[2]);
+                       setup_type = atoi(aaa);
+               }
+               else if (!strcmp(argv[a], "-i")) {
+                       info_only = 1;
+               }
+               else if (!strcmp(argv[a], "-q")) {
+                       setup_type = UI_SILENT;
+               }
+               else if (!strncmp(argv[a], "-h", 2)) {
+                       relh=argv[a][2]!='/';
+                       if (!relh) {
+                               safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
+                       } else {
+                               safestrncpy(relhome, &argv[a][2], sizeof relhome);
+                       }
+                       home = 1;
+               }
+
        }
-       write_config_to_disk();
 
-       old_setup_level = config.c_setup_level;
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+       SetTitles();
 
-       /* end of version update section */
+       /* If a setup type was not specified, try to determine automatically
+        * the best one to use out of all available types.
+        */
+       if (setup_type < 0) {
+               setup_type = discover_ui();
+       }
+       if (info_only == 1) {
+               important_message(_("Citadel Setup"), CITADEL);
+               cleanup(0);
+       }
 
-NEW_INST:
-       config.c_setup_level = REV_LEVEL;
+       enable_home = ( relh | home );
 
-/******************************************/
+       if (chdir(ctdl_run_dir) != 0) {
+               display_error(_("Citadel Setup"), 
+                             "%s: [%s]\n", 
+                             _("The directory you specified does not exist"), 
+                             ctdl_run_dir);
+               cleanup(errno);
+       }
 
-       write_config_to_disk();
 
-       rv = mkdir(ctdl_info_dir, 0700);
-       rv = chmod(ctdl_info_dir, 0700);
-       rv = chown(ctdl_info_dir, config.c_ctdluid, -1);
+       /* Try to stop Citadel if we can */
+       if (!access("/etc/init.d/citadel", X_OK)) {
+               rv = system("/etc/init.d/citadel stop");
+               if (rv != 0)
+                       fprintf(stderr, _("failed to stop us using the initscript.\n"));
+       }
 
-       rv = mkdir(ctdl_bio_dir, 0700);
-       rv = chmod(ctdl_bio_dir, 0700);
-       rv = chown(ctdl_bio_dir, config.c_ctdluid, -1);
+       /* Make sure Citadel is not running. */
+       if (test_server(relhome, enable_home) == 0) {
+               important_message(_("Citadel Setup"),
+                                 _("The Citadel service is still running.\n"
+                                   "Please stop the service manually and run "
+                                   "setup again."));
+               cleanup(1);
+       }
 
-       rv = mkdir(ctdl_usrpic_dir, 0700);
-       rv = chmod(ctdl_usrpic_dir, 0700);
-       rv = chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
+       /* Now begin. */
+       switch (setup_type) {
 
-       rv = mkdir(ctdl_message_dir, 0700);
-       rv = chmod(ctdl_message_dir, 0700);
-       rv = chown(ctdl_message_dir, config.c_ctdluid, -1);
+       case UI_TEXT:
+               printf("\n\n\n"
+                      "               *** %s ***\n\n",
+                      _("Citadel setup program"));
+               break;
 
-       rv = mkdir(ctdl_hlp_dir, 0700);
-       rv = chmod(ctdl_hlp_dir, 0700);
-       rv = chown(ctdl_hlp_dir, config.c_ctdluid, -1);
+       }
 
-       rv = mkdir(ctdl_image_dir, 0700);
-       rv = chmod(ctdl_image_dir, 0700);
-       rv = chown(ctdl_image_dir, config.c_ctdluid, -1);
+       get_config ();
 
-       rv = mkdir(ctdl_bb_dir, 0700);
-       rv = chmod(ctdl_bb_dir, 0700);
-       rv = chown(ctdl_bb_dir, config.c_ctdluid, -1);
+       set_default_values();
 
-       rv = mkdir(ctdl_file_dir, 0700);
-       rv = chmod(ctdl_file_dir, 0700);
-       rv = chown(ctdl_file_dir, config.c_ctdluid, -1);
+       /* Go through a series of dialogs prompting for config info */
+       for (curr = 1; curr <= MAXSETUP; ++curr) {
+               edit_value(curr);
+               if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
+                       curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
+               }
+       }
 
-       rv = mkdir(ctdl_netcfg_dir, 0700);
-       rv = chmod(ctdl_netcfg_dir, 0700);
-       rv = chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
+       /***** begin version update section *****/
 
-       /* Delete files and directories used by older Citadel versions */
-       rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
-       unlink("citadel.log");
-       unlink("weekly");
+       old_setup_level = config.c_setup_level;
 
-       if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
-           (setup_type != UI_SILENT))
-               check_services_entry(); /* Check /etc/services */
-#ifndef __CYGWIN__
-       delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
-       check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
+       if (old_setup_level == 0) {
+               goto NEW_INST;
+       }
 
-       if ((getenv("ACT_AS_MTA") == NULL) || 
-           (getenv("ACT_AS_MTA") &&
-            strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
-               /* Offer to disable other MTA's on the system. */
-               disable_other_mta("courier-authdaemon");
-               disable_other_mta("courier-imap");
-               disable_other_mta("courier-imap-ssl");
-               disable_other_mta("courier-pop");
-               disable_other_mta("courier-pop3");
-               disable_other_mta("courier-pop3d");
-               disable_other_mta("cyrmaster");
-               disable_other_mta("cyrus");
-               disable_other_mta("dovecot");
-               disable_other_mta("exim");
-               disable_other_mta("exim4");
-               disable_other_mta("imapd");
-               disable_other_mta("mta");
-               disable_other_mta("pop3d");
-               disable_other_mta("popd");
-               disable_other_mta("postfix");
-               disable_other_mta("qmail");
-               disable_other_mta("saslauthd");
-               disable_other_mta("sendmail");
-               disable_other_mta("vmailmgrd");
+       if (old_setup_level < 555) {
+               important_message(
+                       _("Citadel Setup"),
+                       _("This Citadel installation is too old to be upgraded.")
+               );
+               cleanup(1);
        }
-#endif
+       write_config_to_disk();
+
+       old_setup_level = config.c_setup_level;
 
-       /* Check for the 'db' nss and offer to disable it */
-       fixnss();
+       /***** end of version update section *****/
+
+NEW_INST:
+       config.c_setup_level = REV_LEVEL;
 
        if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
                gid = getgid();
@@ -1453,71 +1606,31 @@ NEW_INST:
                gid = pw->pw_gid;
        }
 
-       progress("Setting file permissions", 0, 3);
-       rv = chown(ctdl_run_dir, config.c_ctdluid, gid);
-       progress("Setting file permissions", 1, 3);
-       rv = chown(file_citadel_config, config.c_ctdluid, gid);
-       progress("Setting file permissions", 2, 3);
-       rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
-       progress("Setting file permissions", 3, 3);
-
-       /* 
-        * If we're running on SysV, install init scripts.
-        */
-       if (!access("/var/run", W_OK)) {
-
-               if (getenv("NO_INIT_SCRIPTS") == NULL) {
-                       install_init_scripts();
-               }
-
-               if (!access("/etc/init.d/citadel", X_OK)) {
-                       rv = system("/etc/init.d/citadel start");
-                       sleep(3);
-               }
-
-               if (test_server(setup_directory, relhome, enable_home) == 0) {
-                       char buf[SIZ];
-                       int found_it = 0;
-
-                       if (config.c_auth_mode == AUTHMODE_NATIVE) {
-                               snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
-                                       ctdl_sbin_dir, config.c_sysadm, admin_pass);
-                               fp = popen(admin_cmd, "r");
-                               if (fp != NULL) {
-                                       while (fgets(buf, sizeof buf, fp) != NULL) 
-                                       {
-                                               if ((atol(buf) == 574) || (atol(buf) == 200))
-                                                       ++found_it;
-                                       }
-                                       pclose(fp);
-                               }
-                       
-                               if (found_it == 0) {
-                                       important_message("Error","Setup failed to create your admin user");
-                               }
-                       }
+       create_run_directories(config.c_ctdluid, gid);
 
-                       if (setup_type != UI_SILENT)
-                               important_message("Setup finished",
-                                                 "Setup of the Citadel server is complete.\n"
-                                                 "If you will be using WebCit, please run its\n"
-                                                 "setup program now; otherwise, run './citadel'\n"
-                                                 "to log in.\n");
-               }
-               else {
-                       important_message("Setup failed",
-                               "Setup is finished, but the Citadel server failed to start.\n"
-                               "Go back and check your configuration.\n"
-                       );
-               }
+       write_config_to_disk();
 
+       if (    ((setup_type == UI_SILENT)
+               && (getenv("ALTER_ETC_SERVICES")!=NULL))
+               || (setup_type != UI_SILENT)
+       ) {
+               check_services_entry(); /* Check /etc/services */
        }
 
-       else {
-               important_message("Setup finished",
-                       "Setup is finished.  You may now start the server.");
-       }
+#ifndef __CYGWIN__
+       delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
+       check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
+       disable_other_mtas();   /* Offer to disable other MTAs */
+       fixnss();               /* Check for the 'db' nss and offer to disable it */
+#endif
+
+       progress(_("Setting file permissions"), 1, 3);
+       rv = chown(file_citadel_config, config.c_ctdluid, gid);
+       progress(_("Setting file permissions"), 2, 3);
+       rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
+       progress(_("Setting file permissions"), 3, 3);
 
+       check_init_script(relhome);
        cleanup(0);
        return 0;
 }