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