"Fossies" - the Fresh Open Source Software Archive

Member "citadel/setup.c" (5 Jun 2021, 22349 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "setup.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Citadel setup utility
    3  *
    4  * Copyright (c) 1987-2021 by the citadel.org team
    5  *
    6  * This program is open source software; you can redistribute it and/or
    7  * modify it under the terms of the GNU General Public License version 3.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  */
   14 
   15 #define SHOW_ME_VAPPEND_PRINTF
   16 #include <stdlib.h>
   17 #include <unistd.h>
   18 #include <stdio.h>
   19 #include <string.h>
   20 #include <ctype.h>
   21 #include <fcntl.h>
   22 #include <sys/types.h>
   23 #include <sys/stat.h>
   24 #include <sys/wait.h>
   25 #include <signal.h>
   26 #include <netdb.h>
   27 #include <errno.h>
   28 #include <limits.h>
   29 #include <pwd.h>
   30 #include <time.h>
   31 #include <sys/socket.h>
   32 #include <sys/un.h>
   33 #include <assert.h>
   34 #include <libcitadel.h>
   35 #include "citadel.h"
   36 #include "axdefs.h"
   37 #include "sysdep.h"
   38 #include "citadel_dirs.h"
   39 
   40 #ifdef ENABLE_NLS
   41 #ifdef HAVE_XLOCALE_H
   42 #include <xlocale.h>
   43 #endif
   44 #include <libintl.h>
   45 #include <locale.h>
   46 #define _(string)   gettext(string)
   47 #else
   48 #define _(string)   (string)
   49 #endif
   50 
   51 #define SERVICE_NAME    "citadel"
   52 #define PROTO_NAME  "tcp"
   53 #define NSSCONF     "/etc/nsswitch.conf"
   54 
   55 typedef enum _SetupStep {
   56     eCitadelHomeDir = 0,
   57     eSysAdminName = 1,
   58     eSysAdminPW = 2,
   59     eUID = 3,
   60     eIP_ADDR = 4,
   61     eCTDL_Port = 5,
   62     eAuthType = 6,
   63     eLDAP_Host = 7,
   64     eLDAP_Port = 8,
   65     eLDAP_Base_DN = 9,
   66     eLDAP_Bind_DN = 10,
   67     eLDAP_Bind_PW = 11,
   68     eMaxQuestions = 12
   69 } eSetupStep;
   70 
   71 ///"CREATE_XINETD_ENTRY";
   72 /* Environment variables, don't translate! */
   73 const char *EnvNames [eMaxQuestions] = {
   74         "HOME_DIRECTORY",
   75     "SYSADMIN_NAME",
   76     "SYSADMIN_PW",
   77     "CITADEL_UID",
   78     "IP_ADDR",
   79     "CITADEL_PORT",
   80     "ENABLE_UNIX_AUTH",
   81     "LDAP_HOST",
   82     "LDAP_PORT",
   83     "LDAP_BASE_DN",
   84     "LDAP_BIND_DN",
   85     "LDAP_BIND_PW"
   86 };
   87 
   88 int setup_type = (-1);
   89 int enable_home = 1;
   90 char admin_name[SIZ];
   91 char admin_pass[SIZ];
   92 char admin_cmd[SIZ];
   93 int serv_sock = (-1) ;
   94 
   95 const char *setup_titles[eMaxQuestions];
   96 const char *setup_text[eMaxQuestions];
   97 
   98 char *program_title;
   99 
  100 void SetTitles(void)
  101 {
  102     int have_run_dir;
  103 #ifndef HAVE_RUN_DIR
  104     have_run_dir = 1;
  105 #else
  106     have_run_dir = 0;
  107 #endif
  108 
  109 #ifdef ENABLE_NLS
  110     setlocale(LC_MESSAGES, getenv("LANG"));
  111     bindtextdomain("citadel-setup", LOCALEDIR"/locale");
  112     textdomain("citadel-setup");
  113     bind_textdomain_codeset("citadel-setup","UTF8");
  114 #endif
  115 
  116     setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
  117     if (have_run_dir)
  118         setup_text[eCitadelHomeDir] = _(
  119 "Enter the full pathname of the directory in which the Citadel\n"
  120 "installation you are creating or updating resides.  If you\n"
  121 "specify a directory other than the default, you will need to\n"
  122 "specify the -h flag to the server when you start it up.\n");
  123     else
  124         setup_text[eCitadelHomeDir] = _(
  125 "Enter the subdirectory name for an alternate installation of "
  126 "Citadel. To do a default installation just leave it blank."
  127 "If you specify a directory other than the default, you will need to\n"
  128 "specify the -h flag to the server when you start it up.\n"
  129 "note that it may not have a leading /");
  130 
  131     setup_titles[eSysAdminName] = _("Citadel administrator username:");
  132     setup_text[eSysAdminName] = _(
  133 "Please enter the name of the Citadel user account that should be granted "
  134 "administrative privileges once created. If using internal authentication "
  135 "this user account will be created if it does not exist. For external "
  136 "authentication this user account has to exist.");
  137 
  138     setup_titles[eSysAdminPW] = _("Administrator password:");
  139     setup_text[eSysAdminPW] = _(
  140 "Enter a password for the system administrator. When setup\n"
  141 "completes it will attempt to create the administrator user\n"
  142 "and set the password specified here.\n");
  143 
  144     setup_titles[eUID] = _("Citadel User ID:");
  145     setup_text[eUID] = _(
  146 "Citadel needs to run under its own user ID.  This would\n"
  147 "typically be called \"citadel\", but if you are running Citadel\n"
  148 "as a public site, you might also call it \"bbs\" or \"guest\".\n"
  149 "The server will run under this user ID.  Please specify that\n"
  150 "user ID here.  You may specify either a user name or a numeric\n"
  151 "UID.\n");
  152 
  153     setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
  154     setup_text[eIP_ADDR] = _(
  155 "Please specify the IP address which the server should be listening to. "
  156 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
  157 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
  158 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
  159 "listen on all addresses. "
  160 "This can usually be left to the default unless multiple instances of Citadel "
  161 "are running on the same computer.");
  162 
  163     setup_titles[eCTDL_Port] = _("Server port number:");
  164     setup_text[eCTDL_Port] = _(
  165 "Specify the TCP port number on which your server will run.\n"
  166 "Normally, this will be port 504, which is the official port\n"
  167 "assigned by the IANA for Citadel servers.  You will only need\n"
  168 "to specify a different port number if you run multiple instances\n"
  169 "of Citadel on the same computer and there is something else\n"
  170 "already using port 504.\n");
  171 
  172     setup_titles[eAuthType] = _("Authentication method to use:");
  173     setup_text[eAuthType] = _(
  174 "Please choose the user authentication mode. By default Citadel will use its "
  175 "own internal user accounts database. If you choose Host, Citadel users will "
  176 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
  177 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
  178 "chooses the nonstandard MS Active Directory LDAP scheme."
  179 "\n"
  180 "Do not change this option unless you are sure it is required, since changing "
  181 "back requires a full reinstall of Citadel."
  182 "\n"
  183 " 0. Self contained authentication\n"
  184 " 1. Host system integrated authentication\n"
  185 " 2. External LDAP - RFC 2307 POSIX schema\n"
  186 " 3. External LDAP - MS Active Directory schema\n"
  187 "\n"
  188 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
  189 "\n"
  190 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
  191 
  192     setup_titles[eLDAP_Host] = _("LDAP host:");
  193     setup_text[eLDAP_Host] = _(
  194 "Please enter the host name or IP address of your LDAP server.\n");
  195 
  196     setup_titles[eLDAP_Port] = _("LDAP port number:");
  197     setup_text[eLDAP_Port] = _(
  198 "Please enter the port number of the LDAP service (usually 389).\n");
  199 
  200     setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
  201     setup_text[eLDAP_Base_DN] = _(
  202 "Please enter the Base DN to search for authentication\n"
  203 "(for example: dc=example,dc=com)\n");
  204 
  205     setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
  206     setup_text[eLDAP_Bind_DN] = _(
  207 "Please enter the DN of an account to use for binding to the LDAP server for "
  208 "performing queries. The account does not require any other privileges. If "
  209 "your LDAP server allows anonymous queries, you can leave this blank.\n");
  210 
  211     setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
  212     setup_text[eLDAP_Bind_PW] = _(
  213 "If you entered a Bind DN in the previous question, you must now enter\n"
  214 "the password associated with that account.  Otherwise, you can leave this\n"
  215 "blank.\n");
  216 
  217 #if 0
  218 // Debug loading of locales... Strace does a better job though.
  219     printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
  220     printf("Text domain: %s\n", textdomain("citadel-setup"));
  221     printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
  222     {
  223         int i;
  224         for (i = 0; i < eMaxQuestions; i++)
  225             printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
  226         exit(0);
  227     }
  228 #endif
  229 }
  230 
  231 
  232 void title(const char *text) {
  233     printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
  234 }
  235 
  236 
  237 int yesno(const char *question, int default_value) {
  238     int answer = 0;
  239     char buf[SIZ];
  240 
  241     do {
  242         printf("%s\n%s [%s] --> ", question, _("Yes/No"), ( default_value ? _("Yes") : _("No") ));
  243         if (fgets(buf, sizeof buf, stdin)) {
  244             answer = tolower(buf[0]);
  245             if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
  246                 answer = default_value;
  247             }
  248             else if (answer == 'y') {
  249                 answer = 1;
  250             }
  251             else if (answer == 'n') {
  252                 answer = 0;
  253             }
  254         }
  255     } while ((answer < 0) || (answer > 1));
  256     return (answer);
  257 }
  258 
  259 
  260 void important_message(const char *title, const char *msgtext) {
  261     char buf[SIZ];
  262 
  263     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");
  264     printf("       %s \n\n%s\n\n", title, msgtext);
  265     printf("%s", _("Press return to continue..."));
  266     if (fgets(buf, sizeof buf, stdin)) {
  267         ;
  268     }
  269 }
  270 
  271 
  272 void important_msgnum(int msgnum) {
  273     important_message(_("Important Message"), setup_text[msgnum]);
  274 }
  275 
  276 
  277 void display_error(char *error_message_format, ...) {
  278     StrBuf *Msg;
  279     va_list arg_ptr;
  280 
  281     Msg = NewStrBuf();
  282     va_start(arg_ptr, error_message_format);
  283     StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
  284     va_end(arg_ptr);
  285 
  286     important_message(_("Error"), ChrPtr(Msg));
  287     FreeStrBuf(&Msg);
  288 }
  289 
  290 
  291 void progress(char *text, long int curr, long int cmax) {
  292     static long dots_printed = 0L;
  293     long a = 0;
  294 
  295     if (curr == 0) {
  296         printf("%s\n", text);
  297         printf("....................................................");
  298         printf("..........................\r");
  299         dots_printed = 0;
  300     } else if (curr == cmax) {
  301         printf("\r%79s\n", "");
  302     } else {
  303         a = (curr * 100) / cmax;
  304         a = a * 78;
  305         a = a / 100;
  306         while (dots_printed < a) {
  307             printf("*");
  308             ++dots_printed;
  309         }
  310     }
  311     fflush(stdout);
  312 }
  313 
  314 
  315 int uds_connectsock(char *sockpath) {
  316     int s;
  317     struct sockaddr_un addr;
  318 
  319     memset(&addr, 0, sizeof(addr));
  320     addr.sun_family = AF_UNIX;
  321     strcpy(addr.sun_path, sockpath);
  322 
  323     s = socket(AF_UNIX, SOCK_STREAM, 0);
  324     if (s < 0) {
  325         return(-1);
  326     }
  327 
  328     if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
  329         close(s);
  330         return(-1);
  331     }
  332 
  333     return s;
  334 }
  335 
  336 
  337 /*
  338  * input binary data from socket
  339  */
  340 void serv_read(char *buf, int bytes) {
  341     int len, rlen;
  342 
  343     len = 0;
  344     while (len < bytes) {
  345         rlen = read(serv_sock, &buf[len], bytes - len);
  346         if (rlen < 1) {
  347             return;
  348         }
  349         len = len + rlen;
  350     }
  351 }
  352 
  353 
  354 /*
  355  * send binary to server
  356  */
  357 void serv_write(char *buf, int nbytes) {
  358     int bytes_written = 0;
  359     int retval;
  360     while (bytes_written < nbytes) {
  361         retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
  362         if (retval < 1) {
  363             return;
  364         }
  365         bytes_written = bytes_written + retval;
  366     }
  367 }
  368 
  369 
  370 /*
  371  * input string from socket - implemented in terms of serv_read()
  372  */
  373 void serv_gets(char *buf) {
  374     int i;
  375 
  376     /* Read one character at a time.
  377      */
  378     for (i = 0;; i++) {
  379         serv_read(&buf[i], 1);
  380         if (buf[i] == '\n' || i == (SIZ-1))
  381             break;
  382     }
  383 
  384     /* If we got a long line, discard characters until the newline.
  385      */
  386     if (i == (SIZ-1)) {
  387         while (buf[i] != '\n') {
  388             serv_read(&buf[i], 1);
  389         }
  390     }
  391 
  392     /* Strip all trailing nonprintables (crlf)
  393      */
  394     buf[i] = 0;
  395 }
  396 
  397 
  398 /*
  399  * send line to server - implemented in terms of serv_write()
  400  */
  401 void serv_puts(char *buf) {
  402     serv_write(buf, strlen(buf));
  403     serv_write("\n", 1);
  404 }
  405 
  406 
  407 /*
  408  * Convenience functions to get/set system configuration entries
  409  */
  410 void getconf_str(char *buf, char *key) {
  411     char cmd[SIZ];
  412     char ret[SIZ];
  413 
  414     sprintf(cmd, "CONF GETVAL|%s", key);
  415     serv_puts(cmd);
  416     serv_gets(ret);
  417     if (ret[0] == '2') {
  418         extract_token(buf, &ret[4], 0, '|', SIZ);
  419     }
  420     else {
  421         strcpy(buf, "");
  422     }
  423 }
  424 
  425 
  426 int getconf_int(char *key) {
  427     char buf[SIZ];
  428     getconf_str(buf, key);
  429     return atoi(buf);
  430 }
  431 
  432 
  433 void setconf_str(char *key, char *val) {
  434     char buf[SIZ];
  435 
  436     sprintf(buf, "CONF PUTVAL|%s|%s", key, val);
  437     serv_puts(buf);
  438     serv_gets(buf);
  439 }
  440 
  441 
  442 void setconf_int(char *key, int val) {
  443     char buf[SIZ];
  444 
  445     sprintf(buf, "CONF PUTVAL|%s|%d", key, val);
  446     serv_puts(buf);
  447     serv_gets(buf);
  448 }
  449 
  450 
  451 /*
  452  * On systems which use xinetd, see if we can offer to install Citadel as
  453  * the default telnet target.
  454  */
  455 void check_xinetd_entry(void)
  456 {
  457     char *filename = "/etc/xinetd.d/telnet";
  458     FILE *fp;
  459     char buf[SIZ];
  460     int already_citadel = 0;
  461     int rv;
  462 
  463     fp = fopen(filename, "r+");
  464     if (fp == NULL) return;     /* Not there.  Oh well... */
  465 
  466     while (fgets(buf, sizeof buf, fp) != NULL) {
  467         if (strstr(buf, "/citadel") != NULL) {
  468             already_citadel = 1;
  469         }
  470     }
  471     fclose(fp);
  472     if (already_citadel) return;    /* Already set up this way. */
  473 
  474     /* Otherwise, prompt the user to create an entry. */
  475     if (getenv("CREATE_XINETD_ENTRY") != NULL) {
  476         if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
  477             return;
  478         }
  479     }
  480     else {
  481         snprintf(buf, sizeof buf,
  482              _("Setup can configure the \"xinetd\" service to automatically\n"
  483                "connect incoming telnet sessions to Citadel, bypassing the\n"
  484                "host system login: prompt.  Would you like to do this?\n"
  485              )
  486         );
  487         if (yesno(buf, 1) == 0) {
  488             return;
  489         }
  490     }
  491 
  492     fp = fopen(filename, "w");
  493     fprintf(fp,
  494         "# description: telnet service for Citadel users\n"
  495         "service telnet\n"
  496         "{\n"
  497         "   disable = no\n"
  498         "   flags       = REUSE\n"
  499         "   socket_type = stream\n"
  500         "   wait        = no\n"
  501         "   user        = root\n"
  502         "   server      = /usr/sbin/in.telnetd\n"
  503         "   server_args = -h -L %s/citadel\n"
  504         "   log_on_failure  += USERID\n"
  505         "}\n",
  506         ctdl_bin_dir
  507     );
  508     fclose(fp);
  509 
  510     /* Now try to restart the service.  This will not have the intended effect on Solaris, but who uses Solaris anymore? */
  511     rv = system("systemctl restart xinetd >/dev/null 2>&1");
  512     if (rv != 0) {
  513         rv = system("service xinetd restart >/dev/null 2>&1");
  514     }
  515     if (rv != 0) {
  516         display_error(_("failed to restart xinetd.\n"));
  517     }
  518 }
  519 
  520 
  521 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
  522 {
  523     char buf[SIZ] = "";
  524     char setupmsg[SIZ];
  525 
  526     strcpy(setupmsg, "");
  527 
  528     title(prompt_title);
  529     printf("\n%s\n", prompt_text);
  530     printf("%s\n%s\n", _("This is currently set to:"), Target);
  531     printf("%s\n", _("Enter new value or press return to leave unchanged:"));
  532     if (fgets(buf, sizeof buf, stdin)) {
  533         buf[strlen(buf) - 1] = 0;
  534     }
  535     if (!IsEmptyStr(buf)) {
  536         strcpy(Target, buf);
  537     }
  538 }
  539 
  540 
  541 void set_bool_val(int msgpos, int *ip, char *DefValue) {
  542     title(setup_titles[msgpos]);
  543     *ip = yesno(setup_text[msgpos], *ip);
  544 }
  545 
  546 
  547 void set_str_val(int msgpos, char *Target, char *DefValue) 
  548 {
  549     strprompt(setup_titles[msgpos], 
  550           setup_text[msgpos], 
  551           Target, 
  552           DefValue
  553     );
  554 }
  555 
  556 
  557 /* like set_str_val() but for numeric values */
  558 void set_int_val(int msgpos, int *target, char *default_value)
  559 {
  560     char buf[32];
  561     sprintf(buf, "%d", *target);
  562     do {
  563         set_str_val(msgpos, buf, default_value);
  564     } while ( (strcmp(buf, "0")) && (atoi(buf) == 0) );
  565     *target = atoi(buf);
  566 }
  567 
  568 
  569 void edit_value(int curr)
  570 {
  571     struct passwd *pw = NULL;
  572     char ctdluidname[256];
  573     char buf[SIZ];
  574     char *default_value = NULL;
  575     int ctdluid = 0;
  576     int portnum = 0;
  577     int auth = 0;
  578     int lportnum = 0;
  579 
  580     if (default_value == NULL) {
  581         default_value = "";
  582     }
  583 
  584     switch (curr) {
  585 
  586     case eSysAdminName:
  587         getconf_str(admin_name, "c_sysadm");
  588         set_str_val(curr, admin_name, default_value);
  589         setconf_str("c_sysadm", admin_name);
  590         break;
  591 
  592     case eSysAdminPW:
  593         set_str_val(curr, admin_pass, default_value);
  594         break;
  595     
  596     case eUID:
  597         ctdluid = getconf_int("c_ctdluid");
  598         pw = getpwuid(ctdluid);
  599         if (pw == NULL) {
  600             set_int_val(curr, &ctdluid, default_value);
  601         }
  602         else {
  603             strcpy(ctdluidname, pw->pw_name);
  604             set_str_val(curr, ctdluidname, default_value);
  605             pw = getpwnam(ctdluidname);
  606             if (pw != NULL) {
  607                 ctdluid = pw->pw_uid;
  608             }
  609             else if (atoi(ctdluidname) > 0) {
  610                 ctdluid = atoi(ctdluidname);
  611             }
  612         }
  613         setconf_int("c_ctdluid", ctdluid);
  614         break;
  615 
  616     case eIP_ADDR:
  617         getconf_str(buf, "c_ip_addr");
  618         set_str_val(curr, buf, default_value);
  619         setconf_str("c_ip_addr", buf);
  620         break;
  621 
  622     case eCTDL_Port:
  623         portnum = getconf_int("c_port_number");
  624         set_int_val(curr, &portnum, default_value);
  625         setconf_int("c_port_number", portnum);
  626         break;
  627 
  628     case eAuthType:
  629         auth = getconf_int("c_auth_mode");
  630         set_int_val(curr, &auth, default_value);
  631         setconf_int("c_auth_mode", auth);
  632         break;
  633 
  634     case eLDAP_Host:
  635         getconf_str(buf, "c_ldap_host");
  636         if (IsEmptyStr(buf)) {
  637             strcpy(buf, "localhost");
  638         }
  639         set_str_val(curr, buf, default_value);
  640         setconf_str("c_ldap_host", buf);
  641         break;
  642 
  643     case eLDAP_Port:
  644         lportnum = getconf_int("c_ldap_port");
  645         if (lportnum == 0) {
  646             lportnum = 389;
  647         }
  648         set_int_val(curr, &lportnum, default_value);
  649         setconf_int("c_ldap_port", lportnum);
  650         break;
  651 
  652     case eLDAP_Base_DN:
  653         getconf_str(buf, "c_ldap_base_dn");
  654         set_str_val(curr, buf, default_value);
  655         setconf_str("c_ldap_base_dn", buf);
  656         break;
  657 
  658     case eLDAP_Bind_DN:
  659         getconf_str(buf, "c_ldap_bind_dn");
  660         set_str_val(curr, buf, default_value);
  661         setconf_str("c_ldap_bind_dn", buf);
  662         break;
  663 
  664     case eLDAP_Bind_PW:
  665         getconf_str(buf, "c_ldap_bind_pw");
  666         set_str_val(curr, buf, default_value);
  667         setconf_str("c_ldap_bind_pw", buf);
  668         break;
  669     }
  670 }
  671 
  672 
  673 /*
  674  * Messages that are no longer in use.
  675  * We keep them here so we don't lose the translations if we need them later.
  676  */
  677 #if 0
  678 important_message(_("Setup finished"),
  679 _("Setup of the Citadel server is complete.\n"
  680 "If you will be using WebCit, please run its\n"
  681 "setup program now; otherwise, run './citadel'\n"
  682 "to log in.\n"));
  683 important_message(_("Setup failed"),
  684 _("Setup is finished, but the Citadel server failed to start.\n"
  685 "Go back and check your configuration.\n");
  686 important_message(_("Setup finished"),
  687 _("Setup is finished.  You may now start the server."));
  688 #endif
  689 
  690 
  691 int main(int argc, char *argv[])
  692 {
  693     int a, i;
  694     int curr;
  695     char buf[1024]; 
  696     char aaa[128];
  697     char ctdldir[PATH_MAX]=CTDLDIR;
  698     struct passwd *pw;
  699     gid_t gid;
  700     char *activity = NULL;
  701     
  702     /* Keep a mild groove on */
  703     program_title = _("Citadel setup program");
  704 
  705     /* set an invalid setup type */
  706     setup_type = (-1);
  707 
  708     /* parse command line args */
  709     for (a = 0; a < argc; ++a) {
  710         if (!strncmp(argv[a], "-u", 2)) {
  711             strcpy(aaa, argv[a]);
  712             strcpy(aaa, &aaa[2]);
  713             setup_type = atoi(aaa);
  714         }
  715         else if (!strncmp(argv[a], "-h", 2)) {
  716             safestrncpy(ctdldir, &argv[a][2], sizeof ctdldir);
  717         }
  718     }
  719 
  720     if (chdir(ctdldir) != 0) {
  721         fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
  722         exit(errno);
  723     }
  724 
  725     SetTitles();
  726 
  727     /*
  728      * Connect to the running Citadel server.
  729      */
  730     char *connectingmsg = _("Connecting to Citadel server");
  731     for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) {      /* wait for server to start up */
  732         progress(connectingmsg, i, 30);
  733             serv_sock = uds_connectsock(file_citadel_admin_socket);
  734         sleep(1);
  735     }
  736     progress(connectingmsg, 30, 30);
  737 
  738     if (serv_sock < 0) { 
  739         display_error(
  740             "%s: %s %s\n", 
  741             _("Setup could not connect to a running Citadel server."),
  742             strerror(errno), file_citadel_admin_socket
  743         );
  744         exit(1);
  745     }
  746 
  747     /*
  748      * read the server greeting
  749      */
  750     serv_gets(buf);
  751     if (buf[0] != '2') {
  752         display_error("%s\n", buf);
  753         exit(2);
  754     }
  755 
  756     /*
  757      * Are we connected to the correct Citadel server?
  758      */
  759     serv_puts("INFO");
  760     serv_gets(buf);
  761     if (buf[0] != '1') {
  762         display_error("%s\n", buf);
  763         exit(3);
  764     }
  765     a = 0;
  766     while (serv_gets(buf), strcmp(buf, "000")) {
  767         if (a == 5) {
  768             if (atoi(buf) != REV_LEVEL) {
  769                 display_error("%s\n",
  770                 _("Your setup program and Citadel server are from different versions.")
  771                 );
  772                 exit(4);
  773             }
  774         }
  775         ++a;
  776     }
  777 
  778     printf("\n\n\n         *** %s ***\n\n", program_title);
  779 
  780     /* Go through a series of dialogs prompting for config info */
  781     for (curr = 1; curr < eMaxQuestions; ++curr) {
  782         edit_value(curr);
  783 
  784         if (    (curr == eAuthType)
  785             && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
  786             && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
  787         ) {
  788             curr += 5;  /* skip LDAP questions if we're not authenticating against LDAP */
  789         }
  790 
  791         if (curr == eSysAdminName) {
  792             if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
  793                         /* for native auth mode, fetch the admin's existing pw */
  794                 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
  795                 serv_puts(buf);
  796                 serv_gets(buf);
  797                 if (buf[0] == '2') {
  798                     extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
  799                 }
  800             }
  801             else {
  802                 ++curr;     /* skip the password question for non-native auth modes */
  803             }
  804         }
  805     }
  806 
  807     if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
  808         gid = getgid();
  809     } else {
  810         gid = pw->pw_gid;
  811     }
  812 
  813     if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
  814         display_error("%s\n", _("failed to create directories"));
  815     }
  816         
  817     activity = _("Reconfiguring Citadel server");
  818     progress(activity, 0, 5);
  819     sleep(1);                   /* Let the message appear briefly */
  820 
  821     /*
  822      * Create the administrator account.  It's ok if the command fails if this user already exists.
  823      */
  824     if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
  825         progress(activity, 1, 5);
  826         snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
  827         serv_puts(buf);
  828         progress(activity, 2, 5);
  829         serv_gets(buf);
  830     }
  831     progress(activity, 3, 5);
  832 
  833     /*
  834      * Assign the desired password and access level to the administrator account.
  835      */
  836     if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
  837         snprintf(buf, sizeof buf, "AGUP %s", admin_name);
  838         serv_puts(buf);
  839         progress(activity, 4, 5);
  840         serv_gets(buf);
  841         if (buf[0] == '2') {
  842             int admin_flags = extract_int(&buf[4], 2);
  843             int admin_times_called = extract_int(&buf[4], 3);
  844             int admin_msgs_posted = extract_int(&buf[4], 4);
  845             snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
  846                 admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
  847             );
  848             serv_puts(buf);
  849             serv_gets(buf);
  850         }
  851     }
  852     progress(activity, 5, 5);
  853 
  854     check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
  855 
  856     /*
  857      * Restart citserver
  858      */
  859     activity = _("Restarting Citadel server to apply changes");
  860     progress(activity, 0, 51);
  861 
  862     serv_puts("TIME");
  863     serv_gets(buf);
  864     long original_start_time = extract_long(&buf[4], 3);
  865 
  866     progress(activity, 1, 51);
  867     serv_puts("DOWN 1");
  868     progress(activity, 2, 51);
  869     serv_gets(buf);
  870     if (buf[0] != '2') {
  871         display_error("%s\n", buf);
  872         exit(6);
  873     }
  874 
  875     close(serv_sock);
  876     serv_sock = (-1);
  877 
  878     for (i=3; i<=6; ++i) {                  /* wait for server to shut down */
  879         progress(activity, i, 51);
  880         sleep(1);
  881     }
  882 
  883     for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) {     /* wait for server to start up */
  884         progress(activity, i, 51);
  885             serv_sock = uds_connectsock(file_citadel_admin_socket);
  886         sleep(1);
  887     }
  888 
  889     progress(activity, 49, 51);
  890     serv_gets(buf);
  891 
  892     progress(activity, 50, 51);
  893     serv_puts("TIME");
  894     serv_gets(buf);
  895     long new_start_time = extract_long(&buf[4], 3);
  896 
  897     close(serv_sock);
  898     progress(activity, 51, 51);
  899 
  900     if ((original_start_time == new_start_time) || (new_start_time <= 0)) {
  901         display_error("%s\n", _("Setup failed to restart Citadel server.  Please restart it manually."));
  902         exit(7);
  903     }
  904 
  905     exit(0);
  906     return 0;
  907 }