Make sure all of the utilities carry the new and improved version of the GPL declarat...
[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/doku.php/faq:installation:authmodes\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 #if 0
211 // Debug loading of locales... Strace does a better job though.
212         printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
213         printf("Text domain: %s\n", textdomain("citadel-setup"));
214         printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
215         {
216                 int i;
217                 for (i = 0; i < eMaxQuestions; i++)
218                         printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
219                 exit(0);
220         }
221 #endif
222 }
223
224
225 void title(const char *text) {
226         printf("\n\033[1m\033[32m<\033[33m%s\033[32m>\033[0m\n", text);
227 }
228
229
230 int yesno(const char *question, int default_value) {
231         int answer = 0;
232         char buf[SIZ];
233
234         do {
235                 printf("\033[31m\033[32m%s\n%s [\033[33m%s\033[32m]\033[0m --> ", question, _("Yes/No"), ( default_value ? _("Yes") : _("No") ));
236                 if (fgets(buf, sizeof buf, stdin)) {
237                         answer = tolower(buf[0]);
238                         if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
239                                 answer = default_value;
240                         }
241                         else if (answer == 'y') {
242                                 answer = 1;
243                         }
244                         else if (answer == 'n') {
245                                 answer = 0;
246                         }
247                 }
248         } while ((answer < 0) || (answer > 1));
249         return (answer);
250 }
251
252
253 void important_message(const char *title, const char *msgtext) {
254         char buf[SIZ];
255
256         printf("\n%s\n%s\n\n", title, msgtext);
257         printf("%s", _("Press return to continue..."));
258         if (fgets(buf, sizeof buf, stdin)) {
259                 ;
260         }
261 }
262
263
264 void important_msgnum(int msgnum) {
265         important_message(_("Important Message"), setup_text[msgnum]);
266 }
267
268
269 void display_error(char *error_message_format, ...) {
270         StrBuf *Msg;
271         va_list arg_ptr;
272
273         Msg = NewStrBuf();
274         va_start(arg_ptr, error_message_format);
275         StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
276         va_end(arg_ptr);
277
278         important_message(_("Error"), ChrPtr(Msg));
279         FreeStrBuf(&Msg);
280 }
281
282
283 void progress(char *text, long int curr, long int cmax) {
284         long a = 0;
285         long i = 0;
286
287         if (curr == 0) {
288                 printf("%s\n", text);
289                 printf("\033[1m\033[33m[\033[32m............................................................................\033[33m]\033[0m\r");
290         }
291         else if (curr == cmax) {
292                 printf("\r%79s\n", "");
293         }
294         else {
295                 printf("\033[1m\033[33m[\033[32m");
296                 a = (curr * 100) / cmax;
297                 a = a * 76;
298                 a = a / 100;
299                 for (i=0; i<a; ++i) {
300                         printf("*");
301                 }
302                 printf("\033[0m\r");
303         }
304         fflush(stdout);
305 }
306
307
308 int uds_connectsock(char *sockpath) {
309         int s;
310         struct sockaddr_un addr;
311
312         memset(&addr, 0, sizeof(addr));
313         addr.sun_family = AF_UNIX;
314         strcpy(addr.sun_path, sockpath);
315
316         s = socket(AF_UNIX, SOCK_STREAM, 0);
317         if (s < 0) {
318                 return(-1);
319         }
320
321         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
322                 close(s);
323                 return(-1);
324         }
325
326         return s;
327 }
328
329
330 // input binary data from socket
331 void serv_read(char *buf, int bytes) {
332         int len, rlen;
333
334         len = 0;
335         while (len < bytes) {
336                 rlen = read(serv_sock, &buf[len], bytes - len);
337                 if (rlen < 1) {
338                         return;
339                 }
340                 len = len + rlen;
341         }
342 }
343
344
345 // send binary to server
346 void serv_write(char *buf, int nbytes) {
347         int bytes_written = 0;
348         int retval;
349         while (bytes_written < nbytes) {
350                 retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
351                 if (retval < 1) {
352                         return;
353                 }
354                 bytes_written = bytes_written + retval;
355         }
356 }
357
358
359 // input string from socket - implemented in terms of serv_read()
360 void serv_gets(char *buf) {
361         int i;
362
363         // Read one character at a time.
364         for (i = 0;; i++) {
365                 serv_read(&buf[i], 1);
366                 if (buf[i] == '\n' || i == (SIZ-1))
367                         break;
368         }
369
370         // If we got a long line, discard characters until the newline.
371         if (i == (SIZ-1)) {
372                 while (buf[i] != '\n') {
373                         serv_read(&buf[i], 1);
374                 }
375         }
376
377         // Strip all trailing nonprintables (crlf)
378         buf[i] = 0;
379 }
380
381
382 // send line to server - implemented in terms of serv_write()
383 void serv_puts(char *buf) {
384         serv_write(buf, strlen(buf));
385         serv_write("\n", 1);
386 }
387
388
389 // Convenience functions to get/set system configuration entries
390 void getconf_str(char *buf, char *key) {
391         char cmd[SIZ];
392         char ret[SIZ];
393
394         sprintf(cmd, "CONF GETVAL|%s", key);
395         serv_puts(cmd);
396         serv_gets(ret);
397         if (ret[0] == '2') {
398                 extract_token(buf, &ret[4], 0, '|', SIZ);
399         }
400         else {
401                 strcpy(buf, "");
402         }
403 }
404
405
406 int getconf_int(char *key) {
407         char buf[SIZ];
408         getconf_str(buf, key);
409         return atoi(buf);
410 }
411
412
413 void setconf_str(char *key, char *val) {
414         char buf[SIZ];
415
416         sprintf(buf, "CONF PUTVAL|%s|%s", key, val);
417         serv_puts(buf);
418         serv_gets(buf);
419 }
420
421
422 void setconf_int(char *key, int val) {
423         char buf[SIZ];
424
425         sprintf(buf, "CONF PUTVAL|%s|%d", key, val);
426         serv_puts(buf);
427         serv_gets(buf);
428 }
429
430
431 // On systems which use xinetd, see if we can offer to install Citadel as
432 // the default telnet target.
433 void check_xinetd_entry(void) {
434         char *filename = "/etc/xinetd.d/telnet";
435         FILE *fp;
436         char buf[SIZ];
437         int already_citadel = 0;
438         int rv;
439
440         fp = fopen(filename, "r+");
441         if (fp == NULL) return;         // Not there.  Oh well...
442
443         while (fgets(buf, sizeof buf, fp) != NULL) {
444                 if (strstr(buf, "/citadel") != NULL) {
445                         already_citadel = 1;
446                 }
447         }
448         fclose(fp);
449         if (already_citadel) return;    // Already set up this way.
450
451         // Otherwise, prompt the user to create an entry.
452         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
453                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
454                         return;
455                 }
456         }
457         else {
458                 snprintf(buf, sizeof buf,
459                          _("Setup can configure the \"xinetd\" service to automatically\n"
460                            "connect incoming telnet sessions to Citadel, bypassing the\n"
461                            "host system login: prompt.  Would you like to do this?\n"
462                          )
463                 );
464                 if (yesno(buf, 1) == 0) {
465                         return;
466                 }
467         }
468
469         fp = fopen(filename, "w");
470         fprintf(fp,
471                 "# description: telnet service for Citadel users\n"
472                 "service telnet\n"
473                 "{\n"
474                 "       disable = no\n"
475                 "       flags           = REUSE\n"
476                 "       socket_type     = stream\n"
477                 "       wait            = no\n"
478                 "       user            = root\n"
479                 "       server          = /usr/sbin/in.telnetd\n"
480                 "       server_args     = -h -L %s/citadel\n"
481                 "       log_on_failure  += USERID\n"
482                 "}\n",
483                 ctdl_bin_dir
484         );
485         fclose(fp);
486
487         // Now try to restart the service.  This will not have the intended effect on Solaris, but who uses Solaris anymore?
488         rv = system("systemctl restart xinetd >/dev/null 2>&1");
489         if (rv != 0) {
490                 rv = system("service xinetd restart >/dev/null 2>&1");
491         }
492         if (rv != 0) {
493                 display_error(_("failed to restart xinetd.\n"));
494         }
495 }
496
497
498 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue) {
499         char buf[SIZ] = "";
500         char setupmsg[SIZ];
501
502         strcpy(setupmsg, "");
503
504         title(prompt_title);
505         printf("\n%s\n", prompt_text);
506         printf("%s\n%s\n", _("This is currently set to:"), Target);
507         printf("%s\n", _("Enter new value or press return to leave unchanged:"));
508         if (fgets(buf, sizeof buf, stdin)) {
509                 buf[strlen(buf) - 1] = 0;
510         }
511         if (!IsEmptyStr(buf)) {
512                 strcpy(Target, buf);
513         }
514 }
515
516
517 void set_bool_val(int msgpos, int *ip, char *DefValue) {
518         title(setup_titles[msgpos]);
519         *ip = yesno(setup_text[msgpos], *ip);
520 }
521
522
523 void set_str_val(int msgpos, char *Target, char *DefValue) {
524         strprompt(setup_titles[msgpos], 
525                   setup_text[msgpos], 
526                   Target, 
527                   DefValue
528         );
529 }
530
531
532 // like set_str_val() but for numeric values
533 void set_int_val(int msgpos, int *target, char *default_value) {
534         char buf[32];
535         sprintf(buf, "%d", *target);
536         do {
537                 set_str_val(msgpos, buf, default_value);
538         } while ( (strcmp(buf, "0")) && (atoi(buf) == 0) );
539         *target = atoi(buf);
540 }
541
542
543 void edit_value(int curr) {
544         struct passwd *pw = NULL;
545         char ctdluidname[256];
546         char buf[SIZ];
547         char *default_value = NULL;
548         int ctdluid = 0;
549         int portnum = 0;
550         int auth = 0;
551         int lportnum = 0;
552
553         if (default_value == NULL) {
554                 default_value = "";
555         }
556
557         switch (curr) {
558
559         case eSysAdminName:
560                 getconf_str(admin_name, "c_sysadm");
561                 set_str_val(curr, admin_name, default_value);
562                 setconf_str("c_sysadm", admin_name);
563                 break;
564
565         case eSysAdminPW:
566                 set_str_val(curr, admin_pass, default_value);
567                 break;
568         
569         case eUID:
570                 ctdluid = getconf_int("c_ctdluid");
571                 pw = getpwuid(ctdluid);
572                 if (pw == NULL) {
573                         set_int_val(curr, &ctdluid, default_value);
574                 }
575                 else {
576                         strcpy(ctdluidname, pw->pw_name);
577                         set_str_val(curr, ctdluidname, default_value);
578                         pw = getpwnam(ctdluidname);
579                         if (pw != NULL) {
580                                 ctdluid = pw->pw_uid;
581                         }
582                         else if (atoi(ctdluidname) > 0) {
583                                 ctdluid = atoi(ctdluidname);
584                         }
585                 }
586                 setconf_int("c_ctdluid", ctdluid);
587                 break;
588
589         case eIP_ADDR:
590                 getconf_str(buf, "c_ip_addr");
591                 set_str_val(curr, buf, default_value);
592                 setconf_str("c_ip_addr", buf);
593                 break;
594
595         case eCTDL_Port:
596                 portnum = getconf_int("c_port_number");
597                 set_int_val(curr, &portnum, default_value);
598                 setconf_int("c_port_number", portnum);
599                 break;
600
601         case eAuthType:
602                 auth = getconf_int("c_auth_mode");
603                 set_int_val(curr, &auth, default_value);
604                 setconf_int("c_auth_mode", auth);
605                 break;
606
607         case eLDAP_Host:
608                 getconf_str(buf, "c_ldap_host");
609                 if (IsEmptyStr(buf)) {
610                         strcpy(buf, "localhost");
611                 }
612                 set_str_val(curr, buf, default_value);
613                 setconf_str("c_ldap_host", buf);
614                 break;
615
616         case eLDAP_Port:
617                 lportnum = getconf_int("c_ldap_port");
618                 if (lportnum == 0) {
619                         lportnum = 389;
620                 }
621                 set_int_val(curr, &lportnum, default_value);
622                 setconf_int("c_ldap_port", lportnum);
623                 break;
624
625         case eLDAP_Base_DN:
626                 getconf_str(buf, "c_ldap_base_dn");
627                 set_str_val(curr, buf, default_value);
628                 setconf_str("c_ldap_base_dn", buf);
629                 break;
630
631         case eLDAP_Bind_DN:
632                 getconf_str(buf, "c_ldap_bind_dn");
633                 set_str_val(curr, buf, default_value);
634                 setconf_str("c_ldap_bind_dn", buf);
635                 break;
636
637         case eLDAP_Bind_PW:
638                 getconf_str(buf, "c_ldap_bind_pw");
639                 set_str_val(curr, buf, default_value);
640                 setconf_str("c_ldap_bind_pw", buf);
641                 break;
642         }
643 }
644
645
646 // Messages that are no longer in use.
647 // We keep them here so we don't lose the translations if we need them later.
648 #if 0
649 important_message(_("Setup finished"),
650 _("Setup of the Citadel server is complete.\n"
651 "If you will be using WebCit, please run its\n"
652 "setup program now; otherwise, run './citadel'\n"
653 "to log in.\n"));
654 important_message(_("Setup failed"),
655 _("Setup is finished, but the Citadel server failed to start.\n"
656 "Go back and check your configuration.\n");
657 important_message(_("Setup finished"),
658 _("Setup is finished.  You may now start the server."));
659 #endif
660
661
662 int main(int argc, char *argv[]) {
663         int a, i;
664         int curr;
665         char buf[1024]; 
666         char aaa[128];
667         char ctdldir[PATH_MAX]=CTDLDIR;
668         struct passwd *pw;
669         gid_t gid;
670         char *activity = NULL;
671         
672         // Keep a mild groove on
673         program_title = _("Citadel setup program");
674
675         // set an invalid setup type
676         setup_type = (-1);
677
678         // parse command line args
679         for (a = 0; a < argc; ++a) {
680                 if (!strncmp(argv[a], "-u", 2)) {
681                         strcpy(aaa, argv[a]);
682                         strcpy(aaa, &aaa[2]);
683                         setup_type = atoi(aaa);
684                 }
685                 else if (!strncmp(argv[a], "-h", 2)) {
686                         safestrncpy(ctdldir, &argv[a][2], sizeof ctdldir);
687                 }
688         }
689
690         if (chdir(ctdldir) != 0) {
691                 fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
692                 exit(errno);
693         }
694
695         SetTitles();
696
697         // Connect to the running Citadel server.
698         char *connectingmsg = _("Connecting to Citadel server");
699         for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) {          // wait for server to start up
700                 progress(connectingmsg, i, 30);
701                 serv_sock = uds_connectsock(file_citadel_admin_socket);
702                 sleep(1);
703         }
704         progress(connectingmsg, 30, 30);
705
706         if (serv_sock < 0) { 
707                 display_error(
708                         "%s: %s %s\n", 
709                         _("Setup could not connect to a running Citadel server."),
710                         strerror(errno), file_citadel_admin_socket
711                 );
712                 exit(1);
713         }
714
715         // read the server greeting
716         serv_gets(buf);
717         if (buf[0] != '2') {
718                 display_error("%s\n", buf);
719                 exit(2);
720         }
721
722         // Are we connected to the correct Citadel server?
723         serv_puts("INFO");
724         serv_gets(buf);
725         if (buf[0] != '1') {
726                 display_error("%s\n", buf);
727                 exit(3);
728         }
729         a = 0;
730         while (serv_gets(buf), strcmp(buf, "000")) {
731                 if (a == 5) {
732                         if (atoi(buf) != REV_LEVEL) {
733                                 display_error("%s\n",
734                                 _("Your setup program and Citadel server are from different versions.")
735                                 );
736                                 exit(4);
737                         }
738                 }
739                 ++a;
740         }
741
742         printf("\n\n\n         *** %s ***\n\n", program_title);
743
744         // Go through a series of dialogs prompting for config info
745         for (curr = 1; curr < eMaxQuestions; ++curr) {
746                 edit_value(curr);
747
748                 if (    (curr == eAuthType)
749                         && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
750                         && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
751                 ) {
752                         curr += 5;      // skip LDAP questions if we're not authenticating against LDAP
753                 }
754
755                 if (curr == eSysAdminName) {
756                         if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) { // for native auth mode, fetch the admin's existing pw
757                                 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
758                                 serv_puts(buf);
759                                 serv_gets(buf);
760                                 if (buf[0] == '2') {
761                                         extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
762                                 }
763                         }
764                         else {
765                                 ++curr;         // skip the password question for non-native auth modes
766                         }
767                 }
768         }
769
770         if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
771                 gid = getgid();
772         }
773         else {
774                 gid = pw->pw_gid;
775         }
776
777         // setup now must be run after Citadel Server is already running, so we don't need this anymore.
778         //if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
779                 //display_error("%s\n", _("failed to create directories"));
780         //}
781                 
782         activity = _("Reconfiguring Citadel server");
783         progress(activity, 0, 5);
784         sleep(1);                                       // Let the message appear briefly
785
786         // Create the administrator account.  It's ok if the command fails if this user already exists.
787         if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
788                 progress(activity, 1, 5);
789                 snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
790                 serv_puts(buf);
791                 progress(activity, 2, 5);
792                 serv_gets(buf);
793         }
794         progress(activity, 3, 5);
795
796         // Assign the desired password and access level to the administrator account.
797         if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
798                 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
799                 serv_puts(buf);
800                 progress(activity, 4, 5);
801                 serv_gets(buf);
802                 if (buf[0] == '2') {
803                         int admin_flags = extract_int(&buf[4], 2);
804                         int admin_times_called = extract_int(&buf[4], 3);
805                         int admin_msgs_posted = extract_int(&buf[4], 4);
806                         snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
807                                 admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
808                         );
809                         serv_puts(buf);
810                         serv_gets(buf);
811                 }
812         }
813         progress(activity, 5, 5);
814
815         check_xinetd_entry();   // Check /etc/xinetd.d/telnet
816
817         // Restart citserver
818         activity = _("Restarting Citadel server to apply changes");
819         progress(activity, 0, 51);
820
821         serv_puts("TIME");
822         serv_gets(buf);
823         long original_start_time = extract_long(&buf[4], 3);
824
825         progress(activity, 1, 51);
826         serv_puts("DOWN 1");
827         progress(activity, 2, 51);
828         serv_gets(buf);
829         if (buf[0] != '2') {
830                 display_error("%s\n", buf);
831                 exit(6);
832         }
833
834         close(serv_sock);
835         serv_sock = (-1);
836
837         for (i=3; i<=6; ++i) {                                  // wait for server to shut down
838                 progress(activity, i, 51);
839                 sleep(1);
840         }
841
842         for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) {         // wait for server to start up
843                 progress(activity, i, 51);
844                 serv_sock = uds_connectsock(file_citadel_admin_socket);
845                 sleep(1);
846         }
847
848         progress(activity, 49, 51);
849         serv_gets(buf);
850
851         progress(activity, 50, 51);
852         serv_puts("TIME");
853         serv_gets(buf);
854         long new_start_time = extract_long(&buf[4], 3);
855
856         close(serv_sock);
857         progress(activity, 51, 51);
858
859         if ((original_start_time == new_start_time) || (new_start_time <= 0)) {
860                 display_error("%s\n", _("Setup failed to restart Citadel server.  Please restart it manually."));
861                 exit(7);
862         }
863
864         exit(0);
865         return 0;
866 }