stringbuf.c: random idle style cleanup.
[citadel.git] / citadel / utils / setup.c
1 // Citadel setup utility
2 //
3 // Copyright (c) 1987-2024 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
8 #define SHOW_ME_VAPPEND_PRINTF
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <fcntl.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <netdb.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <pwd.h>
23 #include <time.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <assert.h>
27 #include <libcitadel.h>
28 #include "../server/citadel_defs.h"
29 #include "../server/server.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 only works on systemd; others will need to restart it manually.)
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 void unused_messages(void) {
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         );
653         important_message(_("Setup failed"),
654                 _("Setup is finished, but the Citadel server failed to start.\n"
655                 "Go back and check your configuration.\n")
656         );
657         important_message(_("Setup finished"),
658                 _("Setup is finished.  You may now start the server.")
659         );
660 }
661
662
663 int main(int argc, char *argv[]) {
664         int a, i;
665         int curr;
666         char buf[1024]; 
667         char aaa[128];
668         char ctdldir[PATH_MAX]=CTDLDIR;
669         struct passwd *pw;
670         gid_t gid;
671         char *activity = NULL;
672         
673         // Keep a mild groove on
674         program_title = _("Citadel Server setup");
675
676         // set an invalid setup type
677         setup_type = (-1);
678
679         // parse command line args
680         for (a = 0; a < argc; ++a) {
681                 if (!strncmp(argv[a], "-u", 2)) {
682                         strcpy(aaa, argv[a]);
683                         strcpy(aaa, &aaa[2]);
684                         setup_type = atoi(aaa);
685                 }
686                 else if (!strncmp(argv[a], "-h", 2)) {
687                         safestrncpy(ctdldir, &argv[a][2], sizeof ctdldir);
688                 }
689         }
690
691         if (chdir(ctdldir) != 0) {
692                 fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
693                 exit(errno);
694         }
695
696         SetTitles();
697
698         // Connect to the running Citadel server.
699         char *connectingmsg = _("Connecting to Citadel server");
700         for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) {          // wait for server to start up
701                 progress(connectingmsg, i, 30);
702                 serv_sock = uds_connectsock(file_citadel_admin_socket);
703                 sleep(1);
704         }
705         progress(connectingmsg, 30, 30);
706
707         if (serv_sock < 0) { 
708                 display_error(
709                         "%s: %s %s\n", 
710                         _("Setup could not connect to a running Citadel server."),
711                         strerror(errno), file_citadel_admin_socket
712                 );
713                 exit(1);
714         }
715
716         // read the server greeting
717         serv_gets(buf);
718         if (buf[0] != '2') {
719                 display_error("%s\n", buf);
720                 exit(2);
721         }
722
723         // Are we connected to the correct Citadel server?
724         serv_puts("INFO");
725         serv_gets(buf);
726         if (buf[0] != '1') {
727                 display_error("%s\n", buf);
728                 exit(3);
729         }
730         a = 0;
731         while (serv_gets(buf), strcmp(buf, "000")) {
732                 if (a == 5) {
733                         if (atoi(buf) != REV_LEVEL) {
734                                 display_error("%s\n", _("Your setup program and Citadel server are from different versions."));
735                                 exit(4);
736                         }
737                 }
738                 ++a;
739         }
740
741         printf("\n\n\n         *** %s ***\n\n", program_title);
742
743         // Go through a series of dialogs prompting for config info
744         for (curr = 1; curr < eMaxQuestions; ++curr) {
745                 edit_value(curr);
746
747                 if (    (curr == eAuthType)
748                         && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
749                         && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
750                 ) {
751                         curr += 5;      // skip LDAP questions if we're not authenticating against LDAP
752                 }
753
754                 if (curr == eSysAdminName) {
755                         if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) { // for native auth mode, fetch the admin's existing pw
756                                 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
757                                 serv_puts(buf);
758                                 serv_gets(buf);
759                                 if (buf[0] == '2') {
760                                         extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
761                                 }
762                         }
763                         else {
764                                 ++curr;         // skip the password question for non-native auth modes
765                         }
766                 }
767         }
768
769         if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
770                 gid = getgid();
771         }
772         else {
773                 gid = pw->pw_gid;
774         }
775
776         // setup now must be run after Citadel Server is already running, so we don't need this anymore.
777         //if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
778                 //display_error("%s\n", _("failed to create directories"));
779         //}
780                 
781         activity = _("Reconfiguring Citadel server");
782         progress(activity, 0, 5);
783         sleep(1);                                       // Let the message appear briefly
784
785         // Create the administrator account.  It's ok if the command fails if this user already exists.
786         if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
787                 progress(activity, 1, 5);
788                 snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
789                 serv_puts(buf);
790                 progress(activity, 2, 5);
791                 serv_gets(buf);
792         }
793         progress(activity, 3, 5);
794
795         // Assign the desired password and access level to the administrator account.
796         if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
797                 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
798                 serv_puts(buf);
799                 progress(activity, 4, 5);
800                 serv_gets(buf);
801                 if (buf[0] == '2') {
802                         int admin_flags = extract_int(&buf[4], 2);
803                         int admin_times_called = extract_int(&buf[4], 3);
804                         int admin_msgs_posted = extract_int(&buf[4], 4);
805                         snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
806                                 admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
807                         );
808                         serv_puts(buf);
809                         serv_gets(buf);
810                 }
811         }
812         progress(activity, 5, 5);
813
814         check_xinetd_entry();   // Check /etc/xinetd.d/telnet
815
816         // Restart citserver
817         activity = _("Restarting Citadel server to apply changes");
818         progress(activity, 0, 51);
819
820         serv_puts("TIME");
821         serv_gets(buf);
822         long original_start_time = extract_long(&buf[4], 3);
823
824         progress(activity, 1, 51);
825         serv_puts("DOWN");
826         progress(activity, 2, 51);
827         serv_gets(buf);
828         if (buf[0] != '2') {
829                 display_error("%s\n", buf);
830                 exit(6);
831         }
832
833         close(serv_sock);
834         serv_sock = (-1);
835
836         for (i=3; i<=6; ++i) {                                  // wait for server to shut down
837                 progress(activity, i, 51);
838                 sleep(1);
839         }
840
841         for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) {         // wait for server to start up
842                 progress(activity, i, 51);
843                 serv_sock = uds_connectsock(file_citadel_admin_socket);
844                 sleep(1);
845         }
846
847         progress(activity, 49, 51);
848         serv_gets(buf);
849
850         progress(activity, 50, 51);
851         serv_puts("TIME");
852         serv_gets(buf);
853         long new_start_time = extract_long(&buf[4], 3);
854
855         close(serv_sock);
856         progress(activity, 51, 51);
857
858         if ((original_start_time == new_start_time) || (new_start_time <= 0)) {
859                 display_error("%s\n", _("Setup failed to restart Citadel server.  Please restart it manually."));
860                 exit(7);
861         }
862
863         exit(0);
864         return 0;
865 }