Back to index

citadel  8.12
setup.c
Go to the documentation of this file.
00001 /*
00002  * Citadel setup utility
00003  *
00004  * Copyright (c) 1987-2012 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #define SHOW_ME_VAPPEND_PRINTF
00016 #include <stdlib.h>
00017 #include <unistd.h>
00018 #include <stdio.h>
00019 #include <string.h>
00020 #include <ctype.h>
00021 #include <fcntl.h>
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <sys/wait.h>
00025 #include <signal.h>
00026 #include <netdb.h>
00027 #include <errno.h>
00028 #include <limits.h>
00029 #include <pwd.h>
00030 #include <time.h>
00031 #include <sys/socket.h>
00032 #include <sys/un.h>
00033 #include <assert.h>
00034 #include <libcitadel.h>
00035 #include "citadel.h"
00036 #include "axdefs.h"
00037 #include "sysdep.h"
00038 #include "citadel_dirs.h"
00039 #if HAVE_BACKTRACE
00040 #include <execinfo.h>
00041 #endif
00042 
00043 #ifdef ENABLE_NLS
00044 #ifdef HAVE_XLOCALE_H
00045 #include <xlocale.h>
00046 #endif
00047 #include <libintl.h>
00048 #include <locale.h>
00049 #define _(string)    gettext(string)
00050 #else
00051 #define _(string)    (string)
00052 #endif
00053 
00054 #define UI_TEXT             0      /* Default setup type -- text only */
00055 #define UI_DIALOG    2      /* Use the 'whiptail' or 'dialog' program */
00056 #define UI_SILENT    3      /* Silent running, for use in scripts */
00057 
00058 #define SERVICE_NAME "citadel"
00059 #define PROTO_NAME   "tcp"
00060 #define NSSCONF             "/etc/nsswitch.conf"
00061 
00062 typedef enum _SetupStep {
00063        eCitadelHomeDir = 0,
00064        eSysAdminName = 1,
00065        eSysAdminPW = 2,
00066        eUID = 3,
00067        eIP_ADDR = 4,
00068        eCTDL_Port = 5,
00069        eAuthType = 6,
00070        eLDAP_Host = 7,
00071        eLDAP_Port = 8,
00072        eLDAP_Base_DN = 9,
00073        eLDAP_Bind_DN = 10,
00074        eLDAP_Bind_PW = 11,
00075        eMaxQuestions = 12
00076 } eSteupStep;
00077 
00079 /* Environment variables, don't translate! */
00080 const char *EnvNames [eMaxQuestions] = {
00081         "HOME_DIRECTORY",
00082        "SYSADMIN_NAME",
00083        "SYSADMIN_PW",
00084        "CITADEL_UID",
00085        "IP_ADDR",
00086        "CITADEL_PORT",
00087        "ENABLE_UNIX_AUTH",
00088        "LDAP_HOST",
00089        "LDAP_PORT",
00090        "LDAP_BASE_DN",
00091        "LDAP_BIND_DN",
00092        "LDAP_BIND_PW"
00093 };
00094 
00095 int setup_type = (-1);
00096 int using_web_installer = 0;
00097 int enable_home = 1;
00098 char admin_pass[SIZ];
00099 char admin_cmd[SIZ];
00100 int serv_sock = (-1) ;
00101 char configs[NUM_CONFIGS][1024];
00102 
00103 const char *setup_titles[eMaxQuestions];
00104 const char *setup_text[eMaxQuestions];
00105 
00106 char *program_title;
00107 
00108 void SetTitles(void)
00109 {
00110        int have_run_dir;
00111 #ifndef HAVE_RUN_DIR
00112        have_run_dir = 1;
00113 #else
00114        have_run_dir = 0;
00115 #endif
00116 
00117 #ifdef ENABLE_NLS
00118        setlocale(LC_MESSAGES, getenv("LANG"));
00119 
00120        bindtextdomain("citadel-setup", LOCALEDIR"/locale");
00121        textdomain("citadel-setup");
00122        bind_textdomain_codeset("citadel-setup","UTF8");
00123 #endif
00124 
00125        setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
00126        if (have_run_dir)
00127               setup_text[eCitadelHomeDir] = _(
00128 "Enter the full pathname of the directory in which the Citadel\n"
00129 "installation you are creating or updating resides.  If you\n"
00130 "specify a directory other than the default, you will need to\n"
00131 "specify the -h flag to the server when you start it up.\n");
00132        else
00133               setup_text[eCitadelHomeDir] = _(
00134 "Enter the subdirectory name for an alternate installation of "
00135 "Citadel. To do a default installation just leave it blank."
00136 "If you specify a directory other than the default, you will need to\n"
00137 "specify the -h flag to the server when you start it up.\n"
00138 "note that it may not have a leading /");
00139 
00140 
00141        setup_titles[eSysAdminName] = _("Citadel administrator username:");
00142        setup_text[eSysAdminName] = _(
00143 "Please enter the name of the Citadel user account that should be granted "
00144 "administrative privileges once created. If using internal authentication "
00145 "this user account will be created if it does not exist. For external "
00146 "authentication this user account has to exist.");
00147 
00148 
00149        setup_titles[eSysAdminPW] = _("Administrator password:");
00150        setup_text[eSysAdminPW] = _(
00151 "Enter a password for the system administrator. When setup\n"
00152 "completes it will attempt to create the administrator user\n"
00153 "and set the password specified here.\n");
00154 
00155        setup_titles[eUID] = _("Citadel User ID:");
00156        setup_text[eUID] = _(
00157 "Citadel needs to run under its own user ID.  This would\n"
00158 "typically be called \"citadel\", but if you are running Citadel\n"
00159 "as a public site, you might also call it \"bbs\" or \"guest\".\n"
00160 "The server will run under this user ID.  Please specify that\n"
00161 "user ID here.  You may specify either a user name or a numeric\n"
00162 "UID.\n");
00163 
00164        setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
00165        setup_text[eIP_ADDR] = _(
00166 "Please specify the IP address which the server should be listening to. "
00167 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
00168 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
00169 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
00170 "listen on all addresses. "
00171 "This can usually be left to the default unless multiple instances of Citadel "
00172 "are running on the same computer.");
00173 
00174        setup_titles[eCTDL_Port] = _("Server port number:");
00175        setup_text[eCTDL_Port] = _(
00176 "Specify the TCP port number on which your server will run.\n"
00177 "Normally, this will be port 504, which is the official port\n"
00178 "assigned by the IANA for Citadel servers.  You will only need\n"
00179 "to specify a different port number if you run multiple instances\n"
00180 "of Citadel on the same computer and there is something else\n"
00181 "already using port 504.\n");
00182 
00183        setup_titles[eAuthType] = _("Authentication method to use:");
00184        setup_text[eAuthType] = _(
00185 "Please choose the user authentication mode. By default Citadel will use its "
00186 "own internal user accounts database. If you choose Host, Citadel users will "
00187 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
00188 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
00189 "chooses the nonstandard MS Active Directory LDAP scheme."
00190 "\n"
00191 "Do not change this option unless you are sure it is required, since changing "
00192 "back requires a full reinstall of Citadel."
00193 "\n"
00194 " 0. Self contained authentication\n"
00195 " 1. Host system integrated authentication\n"
00196 " 2. External LDAP - RFC 2307 compliant directory\n"
00197 " 3. External LDAP - nonstandard MS Active Directory\n"
00198 "\n"
00199 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
00200 "\n"
00201 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
00202 
00203        setup_titles[eLDAP_Host] = _("LDAP host:");
00204        setup_text[eLDAP_Host] = _(
00205 "Please enter the host name or IP address of your LDAP server.\n");
00206 
00207        setup_titles[eLDAP_Port] = _("LDAP port number:");
00208        setup_text[eLDAP_Port] = _(
00209 "Please enter the port number of the LDAP service (usually 389).\n");
00210 
00211        setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
00212        setup_text[eLDAP_Base_DN] = _(
00213 "Please enter the Base DN to search for authentication\n"
00214 "(for example: dc=example,dc=com)\n");
00215 
00216        setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
00217        setup_text[eLDAP_Bind_DN] = _(
00218 "Please enter the DN of an account to use for binding to the LDAP server for "
00219 "performing queries. The account does not require any other privileges. If "
00220 "your LDAP server allows anonymous queries, you can leave this blank.\n");
00221 
00222        setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
00223        setup_text[eLDAP_Bind_PW] = _(
00224 "If you entered a Bind DN in the previous question, you must now enter\n"
00225 "the password associated with that account.  Otherwise, you can leave this\n"
00226 "blank.\n");
00227 
00228 #if 0
00229 // Debug loading of locales... Strace does a better job though.
00230        printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
00231        printf("Text domain: %s\n", textdomain("citadel-setup"));
00232        printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
00233        {
00234               int i;
00235               for (i = 0; i < eMaxQuestions; i++)
00236                      printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
00237               exit(0);
00238        }
00239 #endif
00240 }
00241 
00242 /*
00243  * Print the stack frame for a backtrace
00244  */
00245 void cit_backtrace(void)
00246 {
00247 #ifdef HAVE_BACKTRACE
00248        void *stack_frames[50];
00249        size_t size, i;
00250        char **strings;
00251 
00252        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00253        strings = backtrace_symbols(stack_frames, size);
00254        for (i = 0; i < size; i++) {
00255               if (strings != NULL)
00256                      fprintf(stderr, "%s\n", strings[i]);
00257               else
00258                      fprintf(stderr, "%p\n", stack_frames[i]);
00259        }
00260        free(strings);
00261 #endif
00262 }
00263 
00264 
00265 
00266 void title(const char *text)
00267 {
00268        if (setup_type == UI_TEXT) {
00269               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
00270        }
00271 }
00272 
00273 
00274 
00275 int yesno(const char *question, int default_value)
00276 {
00277        int i = 0;
00278        int answer = 0;
00279        char buf[SIZ];
00280 
00281        switch (setup_type) {
00282 
00283        case UI_TEXT:
00284               do {
00285                      printf("%s\n%s [%s] --> ",
00286                             question,
00287                             _("Yes/No"),
00288                             ( default_value ? _("Yes") : _("No") )
00289                      );
00290                      if (fgets(buf, sizeof buf, stdin))
00291                      {
00292                             answer = tolower(buf[0]);
00293                             if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
00294                                    answer = default_value;
00295                             }
00296                             else if (answer == 'y') {
00297                                    answer = 1;
00298                             }
00299                             else if (answer == 'n') {
00300                                    answer = 0;
00301                             }
00302                      }
00303               } while ((answer < 0) || (answer > 1));
00304               break;
00305 
00306        case UI_DIALOG:
00307               snprintf(buf, sizeof buf, "exec %s --backtitle '%s' %s --yesno '%s' 15 75",
00308                      getenv("CTDL_DIALOG"),
00309                      program_title,
00310                      ( default_value ? "" : "--defaultno" ),
00311                      question);
00312               i = system(buf);
00313               if (i == 0) {
00314                      answer = 1;
00315               }
00316               else {
00317                      answer = 0;
00318               }
00319               break;
00320        case UI_SILENT:
00321               break;
00322        }
00323        return (answer);
00324 }
00325 
00326 
00327 void important_message(const char *title, const char *msgtext)
00328 {
00329        char buf[SIZ];
00330 
00331        switch (setup_type) {
00332 
00333        case UI_TEXT:
00334               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");
00335               printf("       %s \n\n%s\n\n", title, msgtext);
00336               printf("%s", _("Press return to continue..."));
00337               if (fgets(buf, sizeof buf, stdin));
00338               break;
00339 
00340        case UI_DIALOG:
00341               snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --msgbox '%s' 19 72",
00342                      getenv("CTDL_DIALOG"),
00343                      program_title,
00344                      msgtext);
00345               int rv = system(buf);
00346               if (rv != 0) {
00347                      fprintf(stderr, _("failed to run the dialog command\n"));
00348               }
00349               break;
00350        case UI_SILENT:
00351               fprintf(stderr, "%s\n", msgtext);
00352               break;
00353        }
00354 }
00355 
00356 void important_msgnum(int msgnum)
00357 {
00358        important_message(_("Important Message"), setup_text[msgnum]);
00359 }
00360 
00361 void display_error(char *error_message_format, ...)
00362 {
00363        StrBuf *Msg;
00364        va_list arg_ptr;
00365 
00366        Msg = NewStrBuf();
00367        va_start(arg_ptr, error_message_format);
00368        StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
00369        va_end(arg_ptr);
00370 
00371        important_message(_("Error"), ChrPtr(Msg));
00372        FreeStrBuf(&Msg);
00373 }
00374 
00375 void progress(char *text, long int curr, long int cmax)
00376 {
00377        static long dots_printed = 0L;
00378        long a = 0;
00379        static FILE *fp = NULL;
00380        char buf[SIZ];
00381 
00382        switch (setup_type) {
00383 
00384        case UI_TEXT:
00385               if (curr == 0) {
00386                      printf("%s\n", text);
00387                      printf("....................................................");
00388                      printf("..........................\r");
00389                      dots_printed = 0;
00390               } else if (curr == cmax) {
00391                      printf("\r%79s\n", "");
00392               } else {
00393                      a = (curr * 100) / cmax;
00394                      a = a * 78;
00395                      a = a / 100;
00396                      while (dots_printed < a) {
00397                             printf("*");
00398                             ++dots_printed;
00399                      }
00400               }
00401               fflush(stdout);
00402               break;
00403 
00404        case UI_DIALOG:
00405               if (curr == 0) {
00406                      snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --gauge '%s' 7 72 0",
00407                             getenv("CTDL_DIALOG"),
00408                             program_title,
00409                             text);
00410                      fp = popen(buf, "w");
00411                      if (fp != NULL) {
00412                             fprintf(fp, "0\n");
00413                             fflush(fp);
00414                      }
00415               } 
00416               else if (curr == cmax) {
00417                      if (fp != NULL) {
00418                             fprintf(fp, "100\n");
00419                             pclose(fp);
00420                             fp = NULL;
00421                      }
00422               }
00423               else {
00424                      a = (curr * 100) / cmax;
00425                      if (fp != NULL) {
00426                             fprintf(fp, "%ld\n", a);
00427                             fflush(fp);
00428                      }
00429               }
00430               break;
00431        case UI_SILENT:
00432               break;
00433 
00434        default:
00435               assert(1==0); /* If we got here then the developer is a moron */
00436        }
00437 }
00438 
00439 
00440 
00441 int uds_connectsock(char *sockpath)
00442 {
00443        int s;
00444        struct sockaddr_un addr;
00445 
00446        memset(&addr, 0, sizeof(addr));
00447        addr.sun_family = AF_UNIX;
00448        strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
00449 
00450        s = socket(AF_UNIX, SOCK_STREAM, 0);
00451        if (s < 0) {
00452               return(-1);
00453        }
00454 
00455        if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00456               close(s);
00457               return(-1);
00458        }
00459 
00460        return s;
00461 }
00462 
00463 
00464 /*
00465  * input binary data from socket
00466  */
00467 void serv_read(char *buf, int bytes)
00468 {
00469        int len, rlen;
00470 
00471        len = 0;
00472        while (len < bytes) {
00473               rlen = read(serv_sock, &buf[len], bytes - len);
00474               if (rlen < 1) {
00475                      return;
00476               }
00477               len = len + rlen;
00478        }
00479 }
00480 
00481 
00482 /*
00483  * send binary to server
00484  */
00485 void serv_write(char *buf, int nbytes)
00486 {
00487        int bytes_written = 0;
00488        int retval;
00489        while (bytes_written < nbytes) {
00490               retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
00491               if (retval < 1) {
00492                      return;
00493               }
00494               bytes_written = bytes_written + retval;
00495        }
00496 }
00497 
00498 
00499 
00500 /*
00501  * input string from socket - implemented in terms of serv_read()
00502  */
00503 void serv_gets(char *buf)
00504 {
00505        int i;
00506 
00507        /* Read one character at a time.
00508         */
00509        for (i = 0;; i++) {
00510               serv_read(&buf[i], 1);
00511               if (buf[i] == '\n' || i == (SIZ-1))
00512                      break;
00513        }
00514 
00515        /* If we got a long line, discard characters until the newline.
00516         */
00517        if (i == (SIZ-1)) {
00518               while (buf[i] != '\n') {
00519                      serv_read(&buf[i], 1);
00520               }
00521        }
00522 
00523        /* Strip all trailing nonprintables (crlf)
00524         */
00525        buf[i] = 0;
00526 }
00527 
00528 
00529 /*
00530  * send line to server - implemented in terms of serv_write()
00531  */
00532 void serv_puts(char *buf)
00533 {
00534        serv_write(buf, strlen(buf));
00535        serv_write("\n", 1);
00536 }
00537 
00538 
00539 /*
00540  * On systems which use xinetd, see if we can offer to install Citadel as
00541  * the default telnet target.
00542  */
00543 void check_xinetd_entry(void) {
00544        char *filename = "/etc/xinetd.d/telnet";
00545        FILE *fp;
00546        char buf[SIZ];
00547        int already_citadel = 0;
00548        int rv;
00549 
00550        fp = fopen(filename, "r+");
00551        if (fp == NULL) return;            /* Not there.  Oh well... */
00552 
00553        while (fgets(buf, sizeof buf, fp) != NULL) {
00554               if (strstr(buf, "/citadel") != NULL) {
00555                      already_citadel = 1;
00556               }
00557        }
00558        fclose(fp);
00559        if (already_citadel) return;       /* Already set up this way. */
00560 
00561        /* Otherwise, prompt the user to create an entry. */
00562        if (getenv("CREATE_XINETD_ENTRY") != NULL) {
00563               if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
00564                      return;
00565               }
00566        }
00567        else {
00568               snprintf(buf, sizeof buf,
00569                       _("Setup can configure the \"xinetd\" service to automatically\n"
00570                         "connect incoming telnet sessions to Citadel, bypassing the\n"
00571                         "host system login: prompt.  Would you like to do this?\n"
00572                       )
00573               );
00574               if (yesno(buf, 1) == 0) {
00575                      return;
00576               }
00577        }
00578 
00579        fp = fopen(filename, "w");
00580        fprintf(fp,
00581               "# description: telnet service for Citadel users\n"
00582               "service telnet\n"
00583               "{\n"
00584               "      disable       = no\n"
00585               "      flags         = REUSE\n"
00586               "      socket_type   = stream\n"
00587               "      wait          = no\n"
00588               "      user          = root\n"
00589               "      server        = /usr/sbin/in.telnetd\n"
00590               "      server_args   = -h -L %s/citadel\n"
00591               "      log_on_failure       += USERID\n"
00592               "}\n",
00593               ctdl_bin_dir);
00594        fclose(fp);
00595 
00596        /* Now try to restart the service */
00597        rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
00598        if (rv != 0) {
00599               display_error(_("failed to restart xinetd.\n"));
00600        }
00601 }
00602 
00603 
00604 
00605 /*
00606  * Offer to disable other MTA's
00607  */
00608 void disable_other_mta(const char *mta) {
00609        char buf[SIZ];
00610        FILE *fp;
00611        int lines = 0;
00612        int rv;
00613 
00614        snprintf(buf, sizeof buf,
00615               "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
00616               "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
00617               mta, mta
00618        );
00619        fp = popen(buf, "r");
00620        if (fp == NULL) return;
00621 
00622        while (fgets(buf, sizeof buf, fp) != NULL) {
00623               ++lines;
00624        }
00625        fclose(fp);
00626        if (lines == 0) return;            /* Nothing to do. */
00627 
00628        /* Offer to replace other MTA with the vastly superior Citadel :)  */
00629 
00630        snprintf(buf, sizeof buf,
00631                "%s \"%s\" %s%s%s%s%s%s%s", 
00632                _("You appear to have the "), 
00633                mta, 
00634                _(" email program\n"
00635                  "running on your system.  If you want Citadel mail\n"
00636                  "connected with "), 
00637                mta,
00638                _(" you will have to manually integrate\n"
00639                  "them.  It is preferable to disable "), 
00640                mta,
00641                _(", and use Citadel's\n"
00642                  "SMTP, POP3, and IMAP services.\n\n"
00643                  "May we disable "), 
00644                mta, 
00645                _("so that Citadel has access to ports\n"
00646                  "25, 110, and 143?\n")
00647               );
00648        if (yesno(buf, 1) == 0) {
00649               return;
00650        }
00651        
00652 
00653        snprintf(buf, sizeof 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);
00654        rv = system(buf);
00655        if (rv != 0)
00656               display_error("%s %s.\n", _("failed to disable other mta"), mta);
00657 
00658        snprintf(buf, sizeof buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
00659        rv = system(buf);
00660        if (rv != 0)
00661               display_error(" %s.\n", _("failed to disable other mta"), mta);
00662 }
00663 
00664 const char *other_mtas[] = {
00665        "courier-authdaemon",
00666        "courier-imap",
00667        "courier-imap-ssl",
00668        "courier-pop",
00669        "courier-pop3",
00670        "courier-pop3d",
00671        "cyrmaster",
00672        "cyrus",
00673        "dovecot",
00674        "exim",
00675        "exim4",
00676        "imapd",
00677        "mta",
00678        "pop3d",
00679        "popd",
00680        "postfix",
00681        "qmail",
00682        "saslauthd",
00683        "sendmail",
00684        "vmailmgrd",
00685        ""
00686 };
00687 
00688 void disable_other_mtas(void)
00689 {
00690        int i = 0;
00691        if ((getenv("ACT_AS_MTA") == NULL) || 
00692            (getenv("ACT_AS_MTA") &&
00693             strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
00694               /* Offer to disable other MTA's on the system. */
00695               while (!IsEmptyStr(other_mtas[i]))
00696               {
00697                      disable_other_mta(other_mtas[i]);
00698                      i++;
00699               }
00700        }
00701 }
00702 
00703 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
00704 {
00705        char buf[SIZ] = "";
00706        char setupmsg[SIZ];
00707        char dialog_result[PATH_MAX];
00708        FILE *fp = NULL;
00709        int rv;
00710 
00711        strcpy(setupmsg, "");
00712 
00713        switch (setup_type) {
00714        case UI_TEXT:
00715               title(prompt_title);
00716               printf("\n%s\n", prompt_text);
00717               printf("%s\n%s\n", _("This is currently set to:"), Target);
00718               printf("%s\n", _("Enter new value or press return to leave unchanged:"));
00719               if (fgets(buf, sizeof buf, stdin)){
00720                      buf[strlen(buf) - 1] = 0;
00721               }
00722               if (!IsEmptyStr(buf))
00723                      strcpy(Target, buf);
00724               break;
00725 
00726        case UI_DIALOG:
00727               CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
00728               snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --nocancel --inputbox '%s' 19 72 '%s' 2>%s",
00729                      getenv("CTDL_DIALOG"),
00730                      program_title,
00731                      prompt_text,
00732                      Target,
00733                      dialog_result);
00734               rv = system(buf);
00735               if (rv != 0) {
00736                      fprintf(stderr, "failed to run whiptail or dialog\n");
00737               }
00738               
00739               fp = fopen(dialog_result, "r");
00740               if (fp != NULL) {
00741                      if (fgets(Target, sizeof buf, fp)) {
00742                             if (Target[strlen(Target)-1] == 10) {
00743                                    Target[strlen(Target)-1] = 0;
00744                             }
00745                      }
00746                      fclose(fp);
00747                      unlink(dialog_result);
00748               }
00749               break;
00750        case UI_SILENT:
00751               if (*DefValue != '\0')
00752                      strcpy(Target, DefValue);
00753               break;
00754        }
00755 }
00756 
00757 void set_bool_val(int msgpos, int *ip, char *DefValue) 
00758 {
00759        title(setup_titles[msgpos]);
00760        *ip = yesno(setup_text[msgpos], *ip);
00761 }
00762 
00763 void set_str_val(int msgpos, char *Target, char *DefValue) 
00764 {
00765        strprompt(setup_titles[msgpos], 
00766                 setup_text[msgpos], 
00767                 Target, 
00768                 DefValue
00769        );
00770 }
00771 
00772 /* like set_str_val() but make sure we ended up with a numeric value */
00773 void set_int_val(int msgpos, char *target, char *DefValue)
00774 {
00775        while(1) {
00776               set_str_val(msgpos, target, DefValue);
00777               if (!strcmp(target, "0")) return;
00778               if (atoi(target) != 0) return;
00779        }
00780 }
00781 
00782 
00783 void edit_value(int curr)
00784 {
00785        int i;
00786        struct passwd *pw;
00787        char ctdluidname[256];
00788        char *Value = NULL;
00789 
00790        if (setup_type == UI_SILENT)
00791        {
00792               Value = getenv(EnvNames[curr]);
00793        }
00794        if (Value == NULL) {
00795               Value = "";
00796        }
00797 
00798        switch (curr) {
00799 
00800        case eSysAdminName:
00801               set_str_val(curr, configs[13], Value);
00802               break;
00803 
00804        case eSysAdminPW:
00805               set_str_val(curr, admin_pass, Value);
00806               break;
00807        
00808        case eUID:
00809               if (setup_type == UI_SILENT)
00810               {             
00811                      if (Value) {
00812                             sprintf(configs[69], "%d", atoi(Value));
00813                      }                                  
00814               }
00815               else
00816               {
00817 #ifdef __CYGWIN__
00818                      strcpy(configs[69], "0");   /* work-around for Windows */
00819 #else
00820                      i = atoi(configs[69]);
00821                      pw = getpwuid(i);
00822                      if (pw == NULL) {
00823                             set_int_val(curr, configs[69], Value);
00824                             sprintf(configs[69], "%d", i);
00825                      }
00826                      else {
00827                             strcpy(ctdluidname, pw->pw_name);
00828                             set_str_val(curr, ctdluidname, Value);
00829                             pw = getpwnam(ctdluidname);
00830                             if (pw != NULL) {
00831                                    sprintf(configs[69], "%d", pw->pw_uid);
00832                             }
00833                             else if (atoi(ctdluidname) > 0) {
00834                                    sprintf(configs[69], "%d", atoi(ctdluidname));
00835                             }
00836                      }
00837 #endif
00838               }
00839               break;
00840 
00841        case eIP_ADDR:
00842               set_str_val(curr, configs[37], Value);
00843               break;
00844 
00845        case eCTDL_Port:
00846               set_int_val(curr, configs[68], Value);
00847               break;
00848 
00849        case eAuthType:
00850               if (setup_type == UI_SILENT)
00851               {
00852                      const char *auth;
00853                      //config.c_auth_mode = AUTHMODE_NATIVE;
00854                      auth = Value;
00855                      if (auth != NULL)
00856                      {
00857                             if ((strcasecmp(auth, "yes") == 0) ||
00858                                 (strcasecmp(auth, "host") == 0))
00859                             {
00860                                    //config.c_auth_mode = AUTHMODE_HOST;
00861                             }
00862                             else if (strcasecmp(auth, "ldap") == 0){
00863                                    //config.c_auth_mode = AUTHMODE_LDAP;
00864                             }
00865                             else if ((strcasecmp(auth, "ldap_ad") == 0) ||
00866                                     (strcasecmp(auth, "active directory") == 0)){
00867                                    //config.c_auth_mode = AUTHMODE_LDAP_AD;
00868                             }
00869                      }
00870               }
00871               else {
00872                      set_int_val(curr, configs[52], Value);
00873               }
00874               break;
00875 
00876        case eLDAP_Host:
00877               if (IsEmptyStr(configs[32])) {
00878                      strcpy(configs[32], "localhost");
00879               }
00880               set_str_val(curr, configs[32], Value);
00881               break;
00882 
00883        case eLDAP_Port:
00884               if (atoi(configs[33]) == 0) {
00885                      strcpy(configs[33], "389");
00886               }
00887               set_int_val(curr, configs[33], Value);
00888               break;
00889 
00890        case eLDAP_Base_DN:
00891               set_str_val(curr, configs[34], Value);
00892               break;
00893 
00894        case eLDAP_Bind_DN:
00895               set_str_val(curr, configs[35], Value);
00896               break;
00897 
00898        case eLDAP_Bind_PW:
00899               set_str_val(curr, configs[36], Value);
00900               break;
00901 
00902        }
00903 }
00904 
00905 
00906 
00907 /*
00908  * Figure out what type of user interface we're going to use
00909  */
00910 int discover_ui(void)
00911 {
00912 
00913        /* Use "whiptail" or "dialog" if we have it */
00914        if (getenv("CTDL_DIALOG") != NULL) {
00915               return UI_DIALOG;
00916        }
00917               
00918        return UI_TEXT;
00919 }
00920 
00921 
00922 
00923 /*
00924  * Strip "db" entries out of /etc/nsswitch.conf
00925  */
00926 void fixnss(void) {
00927        FILE *fp_read;
00928        int fd_write;
00929        char buf[256];
00930        char buf_nc[256];
00931        char question[512];
00932        int i;
00933        int file_changed = 0;
00934        char new_filename[64];
00935        int rv;
00936 
00937        fp_read = fopen(NSSCONF, "r");
00938        if (fp_read == NULL) {
00939               return;
00940        }
00941 
00942        strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
00943        fd_write = mkstemp(new_filename);
00944        if (fd_write < 0) {
00945               fclose(fp_read);
00946               return;
00947        }
00948 
00949        while (fgets(buf, sizeof buf, fp_read) != NULL) {
00950               strcpy(buf_nc, buf);
00951               for (i=0; i<strlen(buf_nc); ++i) {
00952                      if (buf_nc[i] == '#') {
00953                             buf_nc[i] = 0;
00954                      }
00955               }
00956               for (i=0; i<strlen(buf_nc); ++i) {
00957                      if (!strncasecmp(&buf_nc[i], "db", 2)) {
00958                             if (i > 0) {
00959                                    if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
00960                                           file_changed = 1;
00961                                           strcpy(&buf_nc[i], &buf_nc[i+2]);
00962                                           strcpy(&buf[i], &buf[i+2]);
00963                                           if (buf[i]==32) {
00964                                                  strcpy(&buf_nc[i], &buf_nc[i+1]);
00965                                                  strcpy(&buf[i], &buf[i+1]);
00966                                           }
00967                                    }
00968                             }
00969                      }
00970               }
00971               if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
00972                      fclose(fp_read);
00973                      close(fd_write);
00974                      unlink(new_filename);
00975                      return;
00976               }
00977        }
00978 
00979        fclose(fp_read);
00980        
00981        if (!file_changed) {
00982               unlink(new_filename);
00983               return;
00984        }
00985 
00986        snprintf(question, sizeof question,
00987                _(
00988                       "\n"
00989                       "/etc/nsswitch.conf is configured to use the 'db' module for\n"
00990                       "one or more services.  This is not necessary on most systems,\n"
00991                       "and it is known to crash the Citadel server when delivering\n"
00992                       "mail to the Internet.\n"
00993                       "\n"
00994                       "Do you want this module to be automatically disabled?\n"
00995                       "\n"
00996                       )
00997        );
00998 
00999        if (yesno(question, 1)) {
01000               snprintf(buf, sizeof buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
01001               rv = system(buf);
01002               if (rv != 0) {
01003                      fprintf(stderr, "failed to edit %s.\n", NSSCONF);
01004               }
01005               chmod(NSSCONF, 0644);
01006        }
01007        unlink(new_filename);
01008 }
01009 
01010 
01011 
01012 #if 0
01013                             important_message(_("Setup finished"),
01014                                             _("Setup of the Citadel server is complete.\n"
01015                                               "If you will be using WebCit, please run its\n"
01016                                               "setup program now; otherwise, run './citadel'\n"
01017                                               "to log in.\n"));
01018                      important_message(_("Setup failed"),
01019                                      _("Setup is finished, but the Citadel server failed to start.\n"
01020                                        "Go back and check your configuration.\n")
01021               important_message(_("Setup finished"),
01022                               _("Setup is finished.  You may now start the server."));
01023 #endif
01024 
01025 
01026 
01027 #define GetDefaultVALINT(CFGNAME, DEFL) GetDefaultValInt(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
01028 void GetDefaultValInt(int *WhereTo, const char *VarName, int DefVal)
01029 {
01030        const char *ch;
01031        if (*WhereTo == 0) *WhereTo = DefVal;
01032 
01033        if ((setup_type == UI_SILENT) &&
01034            (ch = getenv(VarName), ch != NULL))
01035        {
01036               *WhereTo = atoi(ch);
01037        }
01038 }
01039 #define GetDefaultVALCHAR(CFGNAME, DEFL) GetDefaultValChar(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
01040 void GetDefaultValChar(char *WhereTo, const char *VarName, char DefVal)
01041 {
01042        const char *ch;
01043        if (*WhereTo == 0) *WhereTo = DefVal;
01044 
01045        if ((setup_type == UI_SILENT) &&
01046            (ch = getenv(VarName), ch != NULL))
01047        {
01048               *WhereTo = atoi(ch);
01049        }
01050 }
01051 #define GetDefaultVALSTR(CFGNAME, DEFL) GetDefaultValStr(&config.CFGNAME[0], sizeof(config.CFGNAME), "CITADEL_"#CFGNAME, DEFL)
01052 void GetDefaultValStr(char *WhereTo, size_t nMax, const char *VarName, const char *DefVal)
01053 {
01054        const char *ch;
01055        if (*WhereTo == '\0') 
01056               safestrncpy(WhereTo, DefVal, nMax);
01057 
01058        if ((setup_type == UI_SILENT) &&
01059            (ch = getenv(VarName), ch != NULL))
01060        {
01061               safestrncpy(WhereTo, ch, nMax);
01062        }
01063 }
01064 
01065 
01066 void set_default_values(void)
01067 {
01068 #if 0
01069        struct passwd *pw;
01070        struct utsname my_utsname;
01071        struct hostent *he;
01072 
01073        /* Determine our host name, in case we need to use it as a default */
01074        uname(&my_utsname);
01075 
01076        /* set some sample/default values in place of blanks... */
01077        GetDefaultVALSTR(c_nodename, my_utsname.nodename);
01078        strtok(config.c_nodename, ".");
01079        if (IsEmptyStr(config.c_fqdn) ) {
01080               if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
01081                      safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
01082               } else {
01083                      safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
01084               }
01085        }
01086        GetDefaultVALSTR(c_humannode, _("My System"));
01087        GetDefaultVALSTR(c_phonenum, _("US 800 555 1212"));
01088 
01089        GetDefaultVALCHAR(c_initax, 4);
01090 
01091        GetDefaultVALSTR(c_moreprompt, "<more>");
01092        GetDefaultVALSTR(c_twitroom, "Trashcan");
01093        GetDefaultVALSTR(c_baseroom, BASEROOM);
01094        GetDefaultVALSTR(c_aideroom, "Aide");
01095        GetDefaultVALINT(c_port_number, 504);
01096        
01097        GetDefaultVALINT(c_sleeping, 900);
01098 
01099        if (config.c_ctdluid == 0) {
01100               pw = getpwnam("citadel");
01101               if (pw != NULL) {
01102                      config.c_ctdluid = pw->pw_uid;
01103               }
01104        }
01105        if (config.c_ctdluid == 0) {
01106               pw = getpwnam("bbs");
01107               if (pw != NULL) {
01108                      config.c_ctdluid = pw->pw_uid;
01109               }
01110        }
01111        if (config.c_ctdluid == 0) {
01112               pw = getpwnam("guest");
01113               if (pw != NULL) {
01114                      config.c_ctdluid = pw->pw_uid;
01115               }
01116        }
01117        if (config.c_createax == 0) {
01118               config.c_createax = 3;
01119        }
01120        /*
01121         * Negative values for maxsessions are not allowed.
01122         */
01123        if (config.c_maxsessions < 0) {
01124               config.c_maxsessions = 0;
01125        }
01126        /* We need a system default message expiry policy, because this is
01127         * the top level and there's no 'higher' policy to fall back on.
01128         * By default, do not expire messages at all.
01129         */
01130        if (config.c_ep.expire_mode == 0) {
01131               config.c_ep.expire_mode = EXPIRE_MANUAL;
01132               config.c_ep.expire_value = 0;
01133        }
01134 
01135        /*
01136         * Default port numbers for various services
01137         */
01138        GetDefaultVALINT(c_smtp_port, 25);
01139        GetDefaultVALINT(c_pop3_port, 110);
01140        GetDefaultVALINT(c_imap_port, 143);
01141        GetDefaultVALINT(c_msa_port, 587);
01142        GetDefaultVALINT(c_smtps_port, 465);
01143        GetDefaultVALINT(c_pop3s_port, 995);
01144        GetDefaultVALINT(c_imaps_port, 993);
01145        GetDefaultVALINT(c_pftcpdict_port, -1);
01146        GetDefaultVALINT(c_managesieve_port, 2020);
01147        GetDefaultVALINT(c_xmpp_c2s_port, 5222);
01148        GetDefaultVALINT(c_xmpp_s2s_port, 5269);
01149 #endif
01150 }
01151 
01152 
01153 
01154 int main(int argc, char *argv[])
01155 {
01156        int a, i;
01157        int curr;
01158        char buf[1024]; 
01159        char aaa[128];
01160        int info_only = 0;
01161        int relh = 0;
01162        int home = 0;
01163        int nRetries = 0;
01164        char relhome[PATH_MAX]="";
01165        char ctdldir[PATH_MAX]=CTDLDIR;
01166        struct passwd *pw;
01167        gid_t gid;
01168        char *activity = NULL;
01169        
01170        /* Keep a mild groove on */
01171        program_title = _("Citadel setup program");
01172 
01173        /* set an invalid setup type */
01174        setup_type = (-1);
01175 
01176        /* Check to see if we're running the web installer */
01177        if (getenv("CITADEL_INSTALLER") != NULL) {
01178               using_web_installer = 1;
01179        }
01180 
01181        /* parse command line args */
01182        for (a = 0; a < argc; ++a) {
01183               if (!strncmp(argv[a], "-u", 2)) {
01184                      strcpy(aaa, argv[a]);
01185                      strcpy(aaa, &aaa[2]);
01186                      setup_type = atoi(aaa);
01187               }
01188               else if (!strcmp(argv[a], "-i")) {
01189                      info_only = 1;
01190               }
01191               else if (!strcmp(argv[a], "-q")) {
01192                      setup_type = UI_SILENT;
01193               }
01194               else if (!strncmp(argv[a], "-h", 2)) {
01195                      relh=argv[a][2]!='/';
01196                      if (!relh) {
01197                             safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
01198                      } else {
01199                             safestrncpy(relhome, &argv[a][2], sizeof relhome);
01200                      }
01201                      home = 1;
01202               }
01203 
01204        }
01205 
01206        calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
01207        SetTitles();
01208 
01209        /* If a setup type was not specified, try to determine automatically
01210         * the best one to use out of all available types.
01211         */
01212        if (setup_type < 0) {
01213               setup_type = discover_ui();
01214        }
01215        if (info_only == 1) {
01216               important_message(_("Citadel Setup"), CITADEL);
01217               exit(0);
01218        }
01219 
01220        enable_home = ( relh | home );
01221 
01222        if (chdir(ctdl_run_dir) != 0) {
01223               display_error(_("Citadel Setup"), 
01224                            "%s: [%s]\n", 
01225                            _("The directory you specified does not exist"), 
01226                            ctdl_run_dir);
01227               exit(errno);
01228        }
01229 
01230 
01231        /*
01232         * Connect to the running Citadel server.
01233         */
01234        while ((serv_sock < 0) && (nRetries < 10)) {
01235               serv_sock = uds_connectsock(file_citadel_admin_socket);
01236               nRetries ++;
01237               if (serv_sock < 0)
01238                      sleep(1);
01239        }
01240        if (serv_sock < 0) { 
01241               display_error(
01242                      "%s: %s %s\n", 
01243                      _("Setup could not connect to a running Citadel server."),
01244                      strerror(errno), file_citadel_admin_socket
01245               );
01246               exit(1);
01247        }
01248 
01249        /*
01250         * read the server greeting
01251         */
01252        serv_gets(buf);
01253        if (buf[0] != '2') {
01254               display_error("%s\n", buf);
01255               exit(2);
01256        }
01257 
01258        /*
01259         * Are we connected to the correct Citadel server?
01260         */
01261        serv_puts("INFO");
01262        serv_gets(buf);
01263        if (buf[0] != '1') {
01264               display_error("%s\n", buf);
01265               exit(3);
01266        }
01267        a = 0;
01268        while (serv_gets(buf), strcmp(buf, "000")) {
01269               if (a == 5) {
01270                      if (atoi(buf) != REV_LEVEL) {
01271                             display_error("%s\n",
01272                             _("Your setup program and Citadel server are from different versions.")
01273                             );
01274                             exit(4);
01275                      }
01276               }
01277               ++a;
01278        }
01279 
01280        /*
01281         * Load the server's configuration
01282         */
01283        serv_puts("CONF GET");
01284        serv_gets(buf);
01285        if (buf[0] != '1') {
01286               display_error("%s\n", buf);
01287               exit(5);
01288        }
01289        memset(configs, 0, sizeof configs);
01290        a = 0;
01291        while (serv_gets(buf), strcmp(buf, "000")) {
01292               if (a < NUM_CONFIGS) {
01293                      safestrncpy(configs[a], buf, sizeof(configs[a]));
01294               }
01295               ++a;
01296        }
01297 
01298        /*
01299         * Now begin.
01300         */
01301 
01302        /* _("Citadel Setup"),  */
01303 
01304        if (setup_type == UI_TEXT) {
01305               printf("\n\n\n              *** %s ***\n\n", program_title);
01306        }
01307 
01308        if (setup_type == UI_DIALOG) {
01309               system("clear 2>/dev/null");
01310        }
01311 
01312        set_default_values();
01313 
01314        /* Go through a series of dialogs prompting for config info */
01315        for (curr = 1; curr < eMaxQuestions; ++curr) {
01316               edit_value(curr);
01317 
01318               if (   (curr == 6)
01319                      && (atoi(configs[52]) != AUTHMODE_LDAP)
01320                      && (atoi(configs[52]) != AUTHMODE_LDAP_AD)
01321               ) {
01322                      curr += 5;    /* skip LDAP questions if we're not authenticating against LDAP */
01323               }
01324 
01325               if (curr == eSysAdminName) {
01326                      if (atoi(configs[52]) == AUTHMODE_NATIVE) {
01327                                           /* for native auth mode, fetch the admin's existing pw */
01328                             snprintf(buf, sizeof buf, "AGUP %s", configs[13]);
01329                             serv_puts(buf);
01330                             serv_gets(buf);
01331                             if (buf[0] == '2') {
01332                                    extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
01333                             }
01334                      }
01335                      else {
01336                             ++curr;              /* skip the password question for non-native auth modes */
01337                      }
01338               }
01339        }
01340 
01341        if ((pw = getpwuid(atoi(configs[69]))) == NULL) {
01342               gid = getgid();
01343        } else {
01344               gid = pw->pw_gid;
01345        }
01346 
01347        create_run_directories(atoi(configs[69]), gid);
01348 
01349        activity = _("Reconfiguring Citadel server");
01350        progress(activity, 0, NUM_CONFIGS+3);
01351        sleep(1);                                 /* Let the message appear briefly */
01352        serv_puts("CONF SET");
01353        serv_gets(buf);
01354        if (buf[0] == '4') {
01355               for (i=0; i<NUM_CONFIGS; ++i) {
01356                      progress(activity, i+1, NUM_CONFIGS+3);
01357                      serv_puts(configs[i]);
01358               }
01359               serv_puts("000");
01360        }
01361        sleep(1);                                 /* Let the message appear briefly */
01362 
01363        /*
01364         * Create the administrator account.  It's ok if the command fails if this user already exists.
01365         */
01366        progress(activity, NUM_CONFIGS+1, NUM_CONFIGS+3);
01367        snprintf(buf, sizeof buf, "CREU %s|%s", configs[13], admin_pass);
01368        serv_puts(buf);
01369        progress(activity, NUM_CONFIGS+2, NUM_CONFIGS+3);
01370        serv_gets(buf);
01371        progress(activity, NUM_CONFIGS+3, NUM_CONFIGS+3);
01372 
01373        /*
01374         * Assign the desired password and access level to the administrator account.
01375         */
01376        snprintf(buf, sizeof buf, "AGUP %s", configs[13]);
01377        serv_puts(buf);
01378        serv_gets(buf);
01379        if (buf[0] == '2') {
01380               int admin_flags = extract_int(&buf[4], 2);
01381               int admin_times_called = extract_int(&buf[4], 3);
01382               int admin_msgs_posted = extract_int(&buf[4], 4);
01383               snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
01384                      configs[13], admin_pass, admin_flags, admin_times_called, admin_msgs_posted
01385               );
01386               serv_puts(buf);
01387               serv_gets(buf);
01388        }
01389 
01390 #ifndef __CYGWIN__
01391        check_xinetd_entry();       /* Check /etc/xinetd.d/telnet */
01392        disable_other_mtas();   /* Offer to disable other MTAs */
01393        fixnss();            /* Check for the 'db' nss and offer to disable it */
01394 #endif
01395 
01396        activity = _("Setting file permissions");
01397        progress(activity, 0, 2);
01398        //chown(file_citadel_config, config.c_ctdluid, gid);
01399        progress(activity, 1, 2);
01400        chmod(file_citadel_config, S_IRUSR | S_IWUSR);
01401        progress(activity, 2, 2);
01402 
01403        /*
01404         * Restart citserver
01405         */
01406        activity = _("Restarting Citadel server to apply changes");
01407        progress(activity, 0, 41);
01408 
01409        serv_puts("TIME");
01410        serv_gets(buf);
01411        long original_start_time = extract_long(&buf[4], 3);
01412 
01413        progress(activity, 1, 41);
01414        serv_puts("DOWN 1");
01415        progress(activity, 2, 41);
01416        serv_gets(buf);
01417        if (buf[0] != '2') {
01418               display_error("%s\n", buf);
01419               exit(6);
01420        }
01421 
01422        close(serv_sock);
01423        serv_sock = (-1);
01424 
01425        for (i=3; i<=6; ++i) {                                  /* wait for server to shut down */
01426               progress(activity, i, 41);
01427               sleep(1);
01428        }
01429 
01430        for (i=7; ((i<=38) && (serv_sock < 0)) ; ++i) {         /* wait for server to start up */
01431               progress(activity, i, 41);
01432               serv_sock = uds_connectsock(file_citadel_admin_socket);
01433               sleep(1);
01434        }
01435 
01436        progress(activity, 39, 41);
01437        serv_gets(buf);
01438 
01439        progress(activity, 40, 41);
01440        serv_puts("TIME");
01441        serv_gets(buf);
01442        long new_start_time = extract_long(&buf[4], 3);
01443 
01444        close(serv_sock);
01445        progress(activity, 41, 41);
01446 
01447        if (   (original_start_time == new_start_time)
01448               || (new_start_time <= 0)
01449        ) {
01450               display_error("%s\n",
01451                      _("Setup failed to restart Citadel server.  Please restart it manually.")
01452               );
01453               exit(7);
01454        }
01455 
01456        exit(0);
01457        return 0;
01458 }