oops
[citadel.git] / citadel / utils / setup.c
index 61e415ddb3e6ac578ef422dbbd3ab115d27696cf..df16c0ea704921d18a73ab0488f66412e9e5f413 100644 (file)
@@ -1,13 +1,18 @@
 /*
- * $Id$
- *
  * Citadel setup utility
  *
+ * Copyright (c) 1987-2019 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.
  */
 
-#include "ctdl_module.h"
-
-
+#define SHOW_ME_VAPPEND_PRINTF
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -16,7 +21,6 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/utsname.h>
 #include <sys/wait.h>
 #include <signal.h>
 #include <netdb.h>
 #include <limits.h>
 #include <pwd.h>
 #include <time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
 #include <libcitadel.h>
 #include "citadel.h"
 #include "axdefs.h"
 #include "sysdep.h"
-#include "config.h"
 #include "citadel_dirs.h"
-#if HAVE_BACKTRACE
-#include <execinfo.h>
-#endif
-
 
-#define MAXSETUP 11    /* How many setup questions to ask */
+#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 UI_TEXT                0       /* Default setup type -- text only */
-#define UI_DIALOG      2       /* Use the 'dialog' program */
+#define UI_DIALOG      2       /* Use the 'whiptail' or 'dialog' program */
 #define UI_SILENT      3       /* Silent running, for use in scripts */
 
 #define SERVICE_NAME   "citadel"
 #define PROTO_NAME     "tcp"
 #define NSSCONF                "/etc/nsswitch.conf"
 
-int setup_type;
-char setup_directory[PATH_MAX];
-int using_web_installer = 0;
+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
+} eSetupStep;
+
+///"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 = (-1);
 int enable_home = 1;
+char admin_name[SIZ];
 char admin_pass[SIZ];
 char admin_cmd[SIZ];
+int serv_sock = (-1) ;
 
-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"
-};
-
-/**
- * \brief print the actual stack frame.
- */
-void cit_backtrace(void)
-{
-#ifdef HAVE_BACKTRACE
-       void *stack_frames[50];
-       size_t size, i;
-       char **strings;
+const char *setup_titles[eMaxQuestions];
+const char *setup_text[eMaxQuestions];
 
+char *program_title;
 
-       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 /");
 
-"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[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.");
+
+
+       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"
+"as a public site, 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"
-" 2. External LDAP - RFC 2307 compliant directory\n"
-" 3. External LDAP - nonstandard MS Active Directory\n"
+" 2. External LDAP - RFC 2307 POSIX schema\n"
+" 3. External LDAP - MS Active Directory schema\n"
 "\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");
 
-"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",
+       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.\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");
 
-};
-
-struct config config;
-int direction;
-
-
-void cleanup(int exitcode)
-{
-//     printf("Exitcode: %d\n", exitcode);
-//     cit_backtrace();
-       exit(exitcode);
+#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
 }
 
 
 
-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 +246,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,26 +256,31 @@ 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))
                        {
                                answer = tolower(buf[0]);
-                               if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
+                               if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
                                        answer = default_value;
-                               else if (answer == 'y')
+                               }
+                               else if (answer == 'y') {
                                        answer = 1;
-                               else if (answer == 'n')
+                               }
+                               else if (answer == 'n') {
                                        answer = 0;
+                               }
                        }
                } while ((answer < 0) || (answer > 1));
                break;
 
        case UI_DIALOG:
-               sprintf(buf, "exec %s %s --yesno '%s' 15 75",
+               snprintf(buf, sizeof buf, "exec %s --backtitle '%s' %s --yesno '%s' 15 75",
                        getenv("CTDL_DIALOG"),
+                       program_title,
                        ( default_value ? "" : "--defaultno" ),
                        question);
                i = system(buf);
@@ -233,31 +293,34 @@ int yesno(char *question, int default_value)
                break;
        case UI_SILENT:
                break;
-
        }
        return (answer);
 }
 
 
-void important_message(char *title, char *msgtext)
+void important_message(const char *title, const char *msgtext)
 {
        char buf[SIZ];
-       int rv;
 
        switch (setup_type) {
 
        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...");
-               if (fgets(buf, sizeof buf, stdin));
+               printf("%s", _("Press return to continue..."));
+               if (fgets(buf, sizeof buf, stdin))
+               {;}
                break;
 
        case UI_DIALOG:
-               sprintf(buf, "exec %s --msgbox '%s' 19 72",
+               snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --msgbox '%s' 19 72",
                        getenv("CTDL_DIALOG"),
+                       program_title,
                        msgtext);
-               rv = system(buf);
+               int 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 +330,21 @@ 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,10 +359,8 @@ 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;
                } else if (curr == cmax) {
                        printf("\r%79s\n", "");
@@ -301,15 +371,16 @@ void progress(char *text, long int curr, long int cmax)
                        while (dots_printed < a) {
                                printf("*");
                                ++dots_printed;
-                               fflush(stdout);
                        }
                }
+               fflush(stdout);
                break;
 
        case UI_DIALOG:
                if (curr == 0) {
-                       sprintf(buf, "exec %s --gauge '%s' 7 72 0",
+                       snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --gauge '%s' 7 72 0",
                                getenv("CTDL_DIALOG"),
+                               program_title,
                                text);
                        fp = popen(buf, "w");
                        if (fp != NULL) {
@@ -335,228 +406,165 @@ void progress(char *text, long int curr, long int cmax)
        case UI_SILENT:
                break;
 
+       default:
+               assert(1==0);   /* If we got here then the developer is a moron */
        }
 }
 
 
-
-/*
- * check_services_entry()  -- Make sure "citadel" is in /etc/services
- *
- */
-void check_services_entry(void)
+int uds_connectsock(char *sockpath)
 {
-       int i;
-       FILE *sfp;
-       char errmsg[256];
-
-       if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
-               for (i=0; i<=2; ++i) {
-                       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));
-                                       display_error(errmsg);
-                               } else {
-                                       fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
-                                       fclose(sfp);
-                               }
-                       }
-               }
+       int s;
+       struct sockaddr_un addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               return(-1);
        }
-}
 
+       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(s);
+               return(-1);
+       }
 
+       return s;
+}
 
 
 /*
- * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
- *
+ * input binary data from socket
  */
-void delete_inittab_entry(void)
+void serv_read(char *buf, int bytes)
 {
-       FILE *infp;
-       FILE *outfp;
-       char looking_for[256];
-       char buf[1024];
-       char outfilename[32];
-       int changes_made = 0;
-       int rv;
+       int len, rlen;
 
-       /* Determine the fully qualified path name of citserver */
-       snprintf(looking_for, 
-                sizeof looking_for,
-                "%s/citserver", 
-                ctdl_sbin_dir
-               );
+       len = 0;
+       while (len < bytes) {
+               rlen = read(serv_sock, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       return;
+               }
+               len = len + rlen;
+       }
+}
 
-       /* Now tweak /etc/inittab */
-       infp = fopen("/etc/inittab", "r");
-       if (infp == NULL) {
 
-               /* If /etc/inittab does not exist, return quietly.
-                * Not all host platforms have it.
-                */
-               if (errno == ENOENT) {
+/*
+ * send binary to server
+ */
+void serv_write(char *buf, int nbytes)
+{
+       int bytes_written = 0;
+       int retval;
+       while (bytes_written < nbytes) {
+               retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
+               if (retval < 1) {
                        return;
                }
-
-               /* Other errors might mean something really did go wrong.
-                */
-               sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
-               display_error(buf);
-               return;
+               bytes_written = bytes_written + retval;
        }
+}
 
-       strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
-       outfp = fdopen(mkstemp(outfilename), "w+");
-       if (outfp == NULL) {
-               sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
-               display_error(buf);
-               fclose(infp);
-               return;
+
+
+/*
+ * input string from socket - implemented in terms of serv_read()
+ */
+void serv_gets(char *buf)
+{
+       int i;
+
+       /* Read one character at a time.
+        */
+       for (i = 0;; i++) {
+               serv_read(&buf[i], 1);
+               if (buf[i] == '\n' || i == (SIZ-1))
+                       break;
        }
 
-       while (fgets(buf, sizeof buf, infp) != NULL) {
-               if (strstr(buf, looking_for) != NULL) {
-                       rv = fwrite("#", 1, 1, outfp);
-                       ++changes_made;
+       /* If we got a long line, discard characters until the newline.
+        */
+       if (i == (SIZ-1)) {
+               while (buf[i] != '\n') {
+                       serv_read(&buf[i], 1);
                }
-               rv = fwrite(buf, strlen(buf), 1, outfp);
        }
 
-       fclose(infp);
-       fclose(outfp);
+       /* Strip all trailing nonprintables (crlf)
+        */
+       buf[i] = 0;
+}
 
-       if (changes_made) {
-               sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
-               rv = system(buf);
-               rv = system("/sbin/init q 2>/dev/null");
-       }
-       else {
-               unlink(outfilename);
-       }
+
+/*
+ * send line to server - implemented in terms of serv_write()
+ */
+void serv_puts(char *buf)
+{
+       serv_write(buf, strlen(buf));
+       serv_write("\n", 1);
 }
 
 
 /*
- * install_init_scripts()  -- Try to configure to start Citadel at boot
- *
+ * Convenience functions to get/set system configuration entries
  */
-void install_init_scripts(void)
+void getconf_str(char *buf, char *key)
 {
-       struct stat etcinitd;
-       FILE *fp;
-       char *initfile = "/etc/init.d/citadel";
-       char command[SIZ];
-       int rv;
+       char cmd[SIZ];
+       char ret[SIZ];
 
-       if ((stat("/etc/init.d/", &etcinitd) == -1) && 
-           (errno == ENOENT))
-       {
-               if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
-                   (errno == ENOENT))
-                       initfile = CTDLDIR"/citadel.init";
-               else
-                       initfile = "/etc/rc.d/init.d/citadel";
+       sprintf(cmd, "CONF GETVAL|%s", key);
+       serv_puts(cmd);
+       serv_gets(ret);
+       if (ret[0] == '2') {
+               extract_token(buf, &ret[4], 0, '|', SIZ);
        }
-
-       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) {
-                       return;
-               }
-               fclose(fp);
-               
+       else {
+               strcpy(buf, "");
        }
+}
 
-       if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
-               return;
-       }
+int getconf_int(char *key)
+{
+       char buf[SIZ];
+       getconf_str(buf, key);
+       return atoi(buf);
+}
 
-       fp = fopen(initfile, "w");
-       if (fp == NULL) {
-               display_error("Cannot create /etc/init.d/citadel");
-               return;
-       }
+void setconf_str(char *key, char *val)
+{
+       char buf[SIZ];
 
-       fprintf(fp,     "#!/bin/sh\n"
-               "#\n"
-               "# Init file for Citadel\n"
-               "#\n"
-               "# chkconfig: - 79 30\n"
-               "# description: Citadel service\n"
-               "# processname: citserver\n"
-               "# pidfile: %s/citadel.pid\n\n"
-               "# uncomment this to create coredumps as described in\n"
-               "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
-               "# ulimit -c unlimited\n"
-               "\n"
-               "CITADEL_DIR=%s\n"
-               ,
-               setup_directory,
-               setup_directory
-               );
-       fprintf(fp,     "\n"
-               "test -d /var/run || exit 0\n"
-               "\n"
-               "case \"$1\" in\n"
-               "\n"
-               "start)         echo -n \"Starting Citadel... \"\n"
-               "               if $CITADEL_DIR/citserver -lmail -d -h$CITADEL_DIR\n"
-               "               then\n"
-               "                       echo \"ok\"\n"
-               "               else\n"
-               "                       echo \"failed\"\n"
-               "               fi\n");
-       fprintf(fp,     "               ;;\n"
-               "stop)          echo -n \"Stopping Citadel... \"\n"
-               "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
-               "                       echo \"ok\"\n"
-               "               else\n"
-               "                       echo \"failed\"\n"
-               "               fi\n"
-               "               rm -f %s/citadel.pid 2>/dev/null\n"
-               ,
-               setup_directory
-               );
-       fprintf(fp,     "               ;;\n"
-               "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
-               "                       echo \"ok\"\n"
-               "               else\n"
-               "                       echo \"failed\"\n"
-               "               fi\n"
-               "               ;;\n"
-               "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
-               "               exit 1\n"
-               "               ;;\n"
-               "esac\n"
-               );
+       sprintf(buf, "CONF PUTVAL|%s|%s", key, val);
+       serv_puts(buf);
+       serv_gets(buf);
+}
 
-       fclose(fp);
-       chmod(initfile, 0755);
 
-       /* Set up the run levels. */
-       rv = system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
-       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);
-       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);
+void setconf_int(char *key, int val)
+{
+       char buf[SIZ];
 
+       sprintf(buf, "CONF PUTVAL|%s|%d", key, val);
+       serv_puts(buf);
+       serv_gets(buf);
 }
 
 
 
 
 
-
 /*
  * On systems which use xinetd, see if we can offer to install Citadel as
  * the default telnet target.
  */
-void check_xinetd_entry(void) {
+void check_xinetd_entry(void)
+{
        char *filename = "/etc/xinetd.d/telnet";
        FILE *fp;
        char buf[SIZ];
@@ -567,7 +575,9 @@ 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 +590,11 @@ 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;
                }
@@ -603,107 +614,31 @@ void check_xinetd_entry(void) {
                "       server_args     = -h -L %s/citadel\n"
                "       log_on_failure  += USERID\n"
                "}\n",
-               ctdl_bin_dir);
+               ctdl_bin_dir
+       );
        fclose(fp);
 
-       /* Now try to restart the service */
-       rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
-}
-
-
-
-/*
- * Offer to disable other MTA's
- */
-void disable_other_mta(char *mta) {
-       char buf[SIZ];
-       FILE *fp;
-       int lines = 0;
-       int rv;
-
-       sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
-               "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
-               mta, mta);
-       fp = popen(buf, "r");
-       if (fp == NULL) return;
-
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               ++lines;
+       /* Now try to restart the service.  This will not have the intended effect on Solaris, but who the hell uses Solaris anymore? */
+       rv = system("systemctl restart xinetd >/dev/null 2>&1");
+       if (rv != 0) {
+               rv = system("service xinetd restart >/dev/null 2>&1");
        }
-       fclose(fp);
-       if (lines == 0) return;         /* Nothing to do. */
-
-
-       /* 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
-               );
-       if (yesno(buf, 1) == 0) {
-               return;
+       if (rv != 0) {
+               display_error(_("failed to restart xinetd.\n"));
        }
-       
-
-       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);
-       sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
-       rv = system(buf);
 }
 
 
-
-
-/* 
- * Check to see if our server really works.  Returns 0 on success.
- */
-int test_server(char *setup_directory, char *relhomestr, int relhome) {
-       char cmd[256];
-       char cookie[256];
-       FILE *fp;
-       char buf[4096];
-       int found_it = 0;
-
-       /* Generate a silly little cookie.  We're going to write it out
-        * to the server and try to get it back.  The cookie does not
-        * have to be secret ... just unique.
-        */
-       sprintf(cookie, "--test--%d--", getpid());
-
-       if (relhome)
-               sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
-                       ctdl_sbin_dir,
-                       relhomestr,
-                       cookie);
-       else
-               sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
-                       ctdl_sbin_dir,
-                       cookie);
-
-       fp = popen(cmd, "r");
-       if (fp == NULL) return(errno);
-
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               if ( (buf[0]=='2')
-                    && (strstr(buf, cookie) != NULL) ) {
-                       ++found_it;
-               }
-       }
-       pclose(fp);
-
-       if (found_it) {
-               return(0);
+void disable_other_mtas(void)
+{
+       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. */
+               /* FIXME this has to be rewritten to work in the new systemd-based world. */
        }
-       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,28 +652,33 @@ 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");
-               if (fgets(buf, sizeof buf, stdin)){
+               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:
                CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
-               sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
+               snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --nocancel --inputbox '%s' 19 72 '%s' 2>%s",
                        getenv("CTDL_DIALOG"),
+                       program_title,
                        prompt_text,
-                       str,
+                       Target,
                        dialog_result);
                rv = system(buf);
+               if (rv != 0) {
+                       fprintf(stderr, "failed to run whiptail or 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,258 +686,166 @@ 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_int_val(int msgpos, int *ip)
-{
-       char buf[16];
-       snprintf(buf, sizeof buf, "%d", (int) *ip);
-       set_str_val(msgpos, buf);
-       *ip = atoi(buf);
-}
-
-
-void set_char_val(int msgpos, char *ip)
+void set_str_val(int msgpos, char *Target, char *DefValue) 
 {
-       char buf[16];
-       snprintf(buf, sizeof buf, "%d", (int) *ip);
-       set_str_val(msgpos, buf);
-       *ip = (char) atoi(buf);
+       strprompt(setup_titles[msgpos], 
+                 setup_text[msgpos], 
+                 Target, 
+                 DefValue
+       );
 }
 
-
-void set_long_val(int msgpos, long int *ip)
+/* like set_str_val() but for numeric values */
+void set_int_val(int msgpos, int *target, char *default_value)
 {
-       char buf[16];
-       snprintf(buf, sizeof buf, "%ld", *ip);
-       set_str_val(msgpos, buf);
-       *ip = atol(buf);
+       char buf[32];
+       sprintf(buf, "%d", *target);
+       do {
+               set_str_val(msgpos, buf, default_value);
+       } while ( (strcmp(buf, "0")) && (atoi(buf) == 0) );
+       *target = atoi(buf);
 }
 
 
 void edit_value(int curr)
 {
-       int i;
-       struct passwd *pw;
+       struct passwd *pw = NULL;
        char ctdluidname[256];
+       char buf[SIZ];
+       char *default_value = NULL;
+       int ctdluid = 0;
+       int portnum = 0;
+       int auth = 0;
+       int lportnum = 0;
+
+       if (setup_type == UI_SILENT)
+       {
+               default_value = getenv(EnvNames[curr]);
+       }
+       if (default_value == NULL) {
+               default_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:
+               getconf_str(admin_name, "c_sysadm");
+               set_str_val(curr, admin_name, default_value);
+               setconf_str("c_sysadm", admin_name);
                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, default_value);
                break;
        
-       case 3:
+       case eUID:
+               ctdluid = getconf_int("c_ctdluid");
                if (setup_type == UI_SILENT)
                {               
-                       if (getenv("CITADEL_UID")) {
-                               config.c_ctdluid = atoi(getenv("CITADEL_UID"));
+                       if (default_value) {
+                               ctdluid = atoi(default_value);
                        }                                       
                }
                else
                {
-#ifdef __CYGWIN__
-                       config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
-#else
-                       i = config.c_ctdluid;
-                       pw = getpwuid(i);
+                       pw = getpwuid(ctdluid);
                        if (pw == NULL) {
-                               set_int_val(curr, &i);
-                               config.c_ctdluid = i;
+                               set_int_val(curr, &ctdluid, default_value);
                        }
                        else {
                                strcpy(ctdluidname, pw->pw_name);
-                               set_str_val(curr, ctdluidname);
+                               set_str_val(curr, ctdluidname, default_value);
                                pw = getpwnam(ctdluidname);
                                if (pw != NULL) {
-                                       config.c_ctdluid = pw->pw_uid;
+                                       ctdluid = pw->pw_uid;
                                }
                                else if (atoi(ctdluidname) > 0) {
-                                       config.c_ctdluid = atoi(ctdluidname);
+                                       ctdluid = atoi(ctdluidname);
                                }
                        }
 #endif
                }
+               setconf_int("c_ctdluid", ctdluid);
                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:
+               getconf_str(buf, "c_ip_addr");
+               set_str_val(curr, buf, default_value);
+               setconf_str("c_ip_addr", buf);
                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:
+               portnum = getconf_int("c_port_number");
+               set_int_val(curr, &portnum, default_value);
+               setconf_int("c_port_number", portnum);
                break;
 
-       case 6:
+       case eAuthType:
+               auth = getconf_int("c_auth_mode");
                if (setup_type == UI_SILENT)
                {
-                       const char *auth;
-                       config.c_auth_mode = AUTHMODE_NATIVE;
-                       auth = getenv("ENABLE_UNIX_AUTH");
-                       if (auth != NULL)
-                       {
-                               if ((strcasecmp(auth, "yes") == 0) ||
-                                   (strcasecmp(auth, "host") == 0))
-                               {
-                                       config.c_auth_mode = AUTHMODE_HOST;
-                               }
-                               else if (strcasecmp(auth, "ldap") == 0){
-                                       config.c_auth_mode = AUTHMODE_LDAP;
-                               }
-                               else if ((strcasecmp(auth, "ldap_ad") == 0) ||
-                                        (strcasecmp(auth, "active directory") == 0)){
-                                       config.c_auth_mode = AUTHMODE_LDAP_AD;
-                               }
-                       }
+                       if ( (default_value) && (!strcasecmp(default_value, "yes")) ) auth = AUTHMODE_HOST;
+                       if ( (default_value) && (!strcasecmp(default_value, "host")) ) auth = AUTHMODE_HOST;
+                       if ( (default_value) && (!strcasecmp(default_value, "ldap")) ) auth = AUTHMODE_LDAP;
+                       if ( (default_value) && (!strcasecmp(default_value, "ldap_ad")) ) auth = AUTHMODE_LDAP_AD;
+                       if ( (default_value) && (!strcasecmp(default_value, "active directory")) ) auth = AUTHMODE_LDAP_AD;
                }
                else {
-                       set_int_val(curr, &config.c_auth_mode);
+                       set_int_val(curr, &auth, default_value);
                }
+               setconf_int("c_auth_mode", auth);
                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:
+               getconf_str(buf, "c_ldap_host");
+               if (IsEmptyStr(buf)) {
+                       strcpy(buf, "localhost");
                }
+               set_str_val(curr, buf, default_value);
+               setconf_str("c_ldap_host", buf);
                break;
 
-       case 8:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_PORT")) {
-                               config.c_ldap_port = atoi(getenv("LDAP_PORT"));
-                       }
-               }
-               else
-               {
-                       if (config.c_ldap_port == 0) {
-                               config.c_ldap_port = 389;
-                       }
-                       set_int_val(curr, &config.c_ldap_port);
+       case eLDAP_Port:
+               lportnum = getconf_int("c_ldap_port");
+               if (lportnum == 0) {
+                       lportnum = 389;
                }
+               set_int_val(curr, &lportnum, default_value);
+               setconf_int("c_ldap_port", lportnum);
                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:
+               getconf_str(buf, "c_ldap_base_dn");
+               set_str_val(curr, buf, default_value);
+               setconf_str("c_ldap_base_dn", buf);
                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:
+               getconf_str(buf, "c_ldap_bind_dn");
+               set_str_val(curr, buf, default_value);
+               setconf_str("c_ldap_bind_dn", buf);
                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:
+               getconf_str(buf, "c_ldap_bind_pw");
+               set_str_val(curr, buf, default_value);
+               setconf_str("c_ldap_bind_pw", buf);
                break;
-
        }
-
 }
 
-/*
- * (re-)write the config data to disk
- */
-void write_config_to_disk(void)
-{
-       FILE *fp;
-       int fd;
-       int rv;
-
-       if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
-               display_error("setup: cannot open citadel.config");
-               cleanup(1);
-       }
-       fp = fdopen(fd, "wb");
-       if (fp == NULL) {
-               display_error("setup: cannot open citadel.config");
-               cleanup(1);
-       }
-       rv = fwrite((char *) &config, sizeof(struct config), 1, fp);
-       fclose(fp);
-}
-
-
 
 
 /*
@@ -1006,7 +854,7 @@ void write_config_to_disk(void)
 int discover_ui(void)
 {
 
-       /* Use "dialog" if we have it */
+       /* Use "whiptail" or "dialog" if we have it */
        if (getenv("CTDL_DIALOG") != NULL) {
                return UI_DIALOG;
        }
@@ -1016,8 +864,6 @@ int discover_ui(void)
 
 
 
-
-
 /*
  * Strip "db" entries out of /etc/nsswitch.conf
  */
@@ -1028,7 +874,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;
@@ -1046,18 +891,17 @@ 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) {
+               for (i=0; buf_nc[i]; ++i) {
                        if (buf_nc[i] == '#') {
                                buf_nc[i] = 0;
+                               break;
                        }
                }
                for (i=0; i<strlen(buf_nc); ++i) {
                        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]);
@@ -1069,7 +913,8 @@ void fixnss(void) {
                                }
                        }
                }
-               if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
+               long buflen = strlen(buf);
+               if (write(fd_write, buf, buflen) != buflen) {
                        fclose(fp_read);
                        close(fd_write);
                        unlink(new_filename);
@@ -1085,57 +930,68 @@ 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);
+               snprintf(buf, sizeof 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);
 }
 
 
-
-
-
-
+/*
+ * Messages that are no longer in use.
+ * We keep them here so we don't lose the translations if we need them later.
+ */
+void niu_messages() {
+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"));
+important_message(_("Setup failed"),
+_("Setup is finished, but the Citadel server failed to start.\n"
+"Go back and check your configuration.\n");
+important_message(_("Setup finished"),
+_("Setup is finished.  You may now start the server."));
+}
 
 
 int main(int argc, char *argv[])
 {
-       int a;
-       int curr; 
+       int a, i;
+       int curr;
+       char buf[1024]; 
        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;
+       int relh = 0;
+       int home = 0;
        char relhome[PATH_MAX]="";
        char ctdldir[PATH_MAX]=CTDLDIR;
-       int rv;
+       struct passwd *pw;
+       gid_t gid;
+       char *activity = NULL;
        
+       /* Keep a mild groove on */
+       program_title = _("Citadel setup program");
+
        /* 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;
-       }
-
        /* parse command line args */
        for (a = 0; a < argc; ++a) {
                if (!strncmp(argv[a], "-u", 2)) {
@@ -1143,9 +999,6 @@ int main(int argc, char *argv[])
                        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;
                }
@@ -1162,6 +1015,7 @@ int main(int argc, char *argv[])
        }
 
        calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+       SetTitles();
 
        /* If a setup type was not specified, try to determine automatically
         * the best one to use out of all available types.
@@ -1169,360 +1023,210 @@ int main(int argc, char *argv[])
        if (setup_type < 0) {
                setup_type = discover_ui();
        }
-       if (info_only == 1) {
-               important_message("Citadel Setup", CITADEL);
-               cleanup(0);
-       }
-
-       /* 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);
-       }
 
        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);
-       }
-
-       /* 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");
+       if (chdir(ctdl_run_dir) != 0) {
+               display_error("%s: [%s]\n", _("The directory you specified does not exist"), ctdl_run_dir);
+               exit(errno);
        }
 
-       /* 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.
+        * Connect to the running Citadel server.
         */
-       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);
+       char *connectingmsg = _("Connecting to Citadel server");
+       for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) {          /* wait for server to start up */
+               progress(connectingmsg, i, 30);
+               serv_sock = uds_connectsock(file_citadel_admin_socket);
+               sleep(1);
+       }
+       progress(connectingmsg, 30, 30);
+
+       if (serv_sock < 0) { 
+               display_error(
+                       "%s: %s %s\n", 
+                       _("Setup could not connect to a running Citadel server."),
+                       strerror(errno), file_citadel_admin_socket
+               );
+               exit(1);
        }
-       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);
-       strtok(config.c_nodename, ".");
-       if (IsEmptyStr(config.c_fqdn) ) {
-               if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
-                       safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
-               } else {
-                       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;
-       }
-       if (config.c_ctdluid == 0) {
-               pw = getpwnam("citadel");
-               if (pw != NULL) {
-                       config.c_ctdluid = pw->pw_uid;
-               }
-       }
-       if (config.c_ctdluid == 0) {
-               pw = getpwnam("bbs");
-               if (pw != NULL) {
-                       config.c_ctdluid = pw->pw_uid;
-               }
-       }
-       if (config.c_ctdluid == 0) {
-               pw = getpwnam("guest");
-               if (pw != NULL) {
-                       config.c_ctdluid = pw->pw_uid;
-               }
-       }
-       if (config.c_createax == 0) {
-               config.c_createax = 3;
-       }
        /*
-        * Negative values for maxsessions are not allowed.
-        */
-       if (config.c_maxsessions < 0) {
-               config.c_maxsessions = 0;
-       }
-       /* We need a system default message expiry policy, because this is
-        * the top level and there's no 'higher' policy to fall back on.
-        * By default, do not expire messages at all.
+        * read the server greeting
         */
-       if (config.c_ep.expire_mode == 0) {
-               config.c_ep.expire_mode = EXPIRE_MANUAL;
-               config.c_ep.expire_value = 0;
+       serv_gets(buf);
+       if (buf[0] != '2') {
+               display_error("%s\n", buf);
+               exit(2);
        }
 
        /*
-        * Default port numbers for various services
+        * Are we connected to the correct Citadel server?
         */
-       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;
-
-       /* 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 */
+       serv_puts("INFO");
+       serv_gets(buf);
+       if (buf[0] != '1') {
+               display_error("%s\n", buf);
+               exit(3);
+       }
+       a = 0;
+       while (serv_gets(buf), strcmp(buf, "000")) {
+               if (a == 5) {
+                       if (atoi(buf) != REV_LEVEL) {
+                               display_error("%s\n",
+                               _("Your setup program and Citadel server are from different versions.")
+                               );
+                               exit(4);
+                       }
                }
+               ++a;
        }
 
-/***** begin version update section ***** */
-       /* take care of any updating that is necessary */
+       /*
+        * Now begin.
+        */
 
-       old_setup_level = config.c_setup_level;
 
-       if (old_setup_level == 0) {
-               goto NEW_INST;
+       if (setup_type == UI_TEXT) {
+               printf("\n\n\n         *** %s ***\n\n", program_title);
        }
 
-       if (old_setup_level < 555) {
-               important_message("Citadel Setup",
-                                 "This Citadel installation is too old "
-                                 "to be upgraded.");
-               cleanup(1);
+       if (setup_type == UI_DIALOG) {
+               system("clear 2>/dev/null");
        }
-       write_config_to_disk();
-
-       old_setup_level = config.c_setup_level;
-
-       /* end of version update section */
-
-NEW_INST:
-       config.c_setup_level = REV_LEVEL;
-
-/******************************************/
 
-       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);
-
-       rv = mkdir(ctdl_bio_dir, 0700);
-       rv = chmod(ctdl_bio_dir, 0700);
-       rv = chown(ctdl_bio_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_usrpic_dir, 0700);
-       rv = chmod(ctdl_usrpic_dir, 0700);
-       rv = chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_message_dir, 0700);
-       rv = chmod(ctdl_message_dir, 0700);
-       rv = chown(ctdl_message_dir, config.c_ctdluid, -1);
+       /* Go through a series of dialogs prompting for config info */
+       for (curr = 1; curr < eMaxQuestions; ++curr) {
+               edit_value(curr);
 
-       rv = mkdir(ctdl_hlp_dir, 0700);
-       rv = chmod(ctdl_hlp_dir, 0700);
-       rv = chown(ctdl_hlp_dir, config.c_ctdluid, -1);
+               if (    (curr == eAuthType)
+                       && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
+                       && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
+               ) {
+                       curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
+               }
 
-       rv = mkdir(ctdl_image_dir, 0700);
-       rv = chmod(ctdl_image_dir, 0700);
-       rv = chown(ctdl_image_dir, config.c_ctdluid, -1);
+               if (curr == eSysAdminName) {
+                       if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
+                                               /* for native auth mode, fetch the admin's existing pw */
+                               snprintf(buf, sizeof buf, "AGUP %s", admin_name);
+                               serv_puts(buf);
+                               serv_gets(buf);
+                               if (buf[0] == '2') {
+                                       extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
+                               }
+                       }
+                       else {
+                               ++curr;         /* skip the password question for non-native auth modes */
+                       }
+               }
+       }
 
-       rv = mkdir(ctdl_bb_dir, 0700);
-       rv = chmod(ctdl_bb_dir, 0700);
-       rv = chown(ctdl_bb_dir, config.c_ctdluid, -1);
+       if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
+               gid = getgid();
+       } else {
+               gid = pw->pw_gid;
+       }
 
-       rv = mkdir(ctdl_file_dir, 0700);
-       rv = chmod(ctdl_file_dir, 0700);
-       rv = chown(ctdl_file_dir, config.c_ctdluid, -1);
+       if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
+               display_error("%s\n", _("failed to create directories"));
+       }
+               
+       activity = _("Reconfiguring Citadel server");
+       progress(activity, 0, 5);
+       sleep(1);                                       /* Let the message appear briefly */
 
-       rv = mkdir(ctdl_netcfg_dir, 0700);
-       rv = chmod(ctdl_netcfg_dir, 0700);
-       rv = chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
+       /*
+        * Create the administrator account.  It's ok if the command fails if this user already exists.
+        */
+       if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
+               progress(activity, 1, 5);
+               snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
+               serv_puts(buf);
+               progress(activity, 2, 5);
+               serv_gets(buf);
+       }
+       progress(activity, 3, 5);
 
-       /* 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");
+       /*
+        * Assign the desired password and access level to the administrator account.
+        */
+       if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
+               snprintf(buf, sizeof buf, "AGUP %s", admin_name);
+               serv_puts(buf);
+               progress(activity, 4, 5);
+               serv_gets(buf);
+               if (buf[0] == '2') {
+                       int admin_flags = extract_int(&buf[4], 2);
+                       int admin_times_called = extract_int(&buf[4], 3);
+                       int admin_msgs_posted = extract_int(&buf[4], 4);
+                       snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
+                               admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
+                       );
+                       serv_puts(buf);
+                       serv_gets(buf);
+               }
+       }
+       progress(activity, 5, 5);
 
-       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 ((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");
-       }
+       disable_other_mtas();   /* Offer to disable other MTAs */
+       fixnss();               /* Check for the 'db' nss and offer to disable it */
 #endif
 
-       /* Check for the 'db' nss and offer to disable it */
-       fixnss();
+       /*
+        * Restart citserver
+        */
+       activity = _("Restarting Citadel server to apply changes");
+       progress(activity, 0, 51);
 
-       if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
-               gid = getgid();
-       } else {
-               gid = pw->pw_gid;
-       }
+       serv_puts("TIME");
+       serv_gets(buf);
+       long original_start_time = extract_long(&buf[4], 3);
 
-       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);
+       progress(activity, 1, 51);
+       serv_puts("DOWN 1");
+       progress(activity, 2, 51);
+       serv_gets(buf);
+       if (buf[0] != '2') {
+               display_error("%s\n", buf);
+               exit(6);
+       }
 
-       /* 
-        * If we're running on SysV, install init scripts.
-        */
-       if (!access("/var/run", W_OK)) {
+       close(serv_sock);
+       serv_sock = (-1);
 
-               if (getenv("NO_INIT_SCRIPTS") == NULL) {
-                       install_init_scripts();
-               }
+       for (i=3; i<=6; ++i) {                                  /* wait for server to shut down */
+               progress(activity, i, 51);
+               sleep(1);
+       }
 
-               if (!access("/etc/init.d/citadel", X_OK)) {
-                       rv = system("/etc/init.d/citadel start");
-                       sleep(3);
-               }
+       for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) {         /* wait for server to start up */
+               progress(activity, i, 51);
+               serv_sock = uds_connectsock(file_citadel_admin_socket);
+               sleep(1);
+       }
 
-               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");
-                               }
-                       }
+       progress(activity, 49, 51);
+       serv_gets(buf);
 
-                       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"
-                       );
-               }
+       progress(activity, 50, 51);
+       serv_puts("TIME");
+       serv_gets(buf);
+       long new_start_time = extract_long(&buf[4], 3);
 
-       }
+       close(serv_sock);
+       progress(activity, 51, 51);
 
-       else {
-               important_message("Setup finished",
-                       "Setup is finished.  You may now start the server.");
+       if (    (original_start_time == new_start_time)
+               || (new_start_time <= 0)
+       ) {
+               display_error("%s\n", _("Setup failed to restart Citadel server.  Please restart it manually."));
+               exit(7);
        }
 
-       cleanup(0);
+       exit(0);
        return 0;
 }
-
-