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