2 * Citadel setup utility
4 * Copyright (c) 1987-2012 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #define SHOW_ME_VAPPEND_PRINTF
22 #include <sys/types.h>
31 #include <sys/socket.h>
34 #include <libcitadel.h>
38 #include "citadel_dirs.h"
49 #define _(string) gettext(string)
51 #define _(string) (string)
54 #define UI_TEXT 0 /* Default setup type -- text only */
55 #define UI_DIALOG 2 /* Use the 'whiptail' or 'dialog' program */
56 #define UI_SILENT 3 /* Silent running, for use in scripts */
58 #define SERVICE_NAME "citadel"
59 #define PROTO_NAME "tcp"
60 #define NSSCONF "/etc/nsswitch.conf"
62 typedef enum _SetupStep {
78 ///"CREATE_XINETD_ENTRY";
79 /* Environment variables, don't translate! */
80 const char *EnvNames [eMaxQuestions] = {
95 int setup_type = (-1);
96 int using_web_installer = 0;
100 int serv_sock = (-1) ;
101 char configs[NUM_CONFIGS][1024];
103 const char *setup_titles[eMaxQuestions];
104 const char *setup_text[eMaxQuestions];
118 setlocale(LC_MESSAGES, getenv("LANG"));
120 bindtextdomain("citadel-setup", LOCALEDIR"/locale");
121 textdomain("citadel-setup");
122 bind_textdomain_codeset("citadel-setup","UTF8");
125 setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
127 setup_text[eCitadelHomeDir] = _(
128 "Enter the full pathname of the directory in which the Citadel\n"
129 "installation you are creating or updating resides. If you\n"
130 "specify a directory other than the default, you will need to\n"
131 "specify the -h flag to the server when you start it up.\n");
133 setup_text[eCitadelHomeDir] = _(
134 "Enter the subdirectory name for an alternate installation of "
135 "Citadel. To do a default installation just leave it blank."
136 "If you specify a directory other than the default, you will need to\n"
137 "specify the -h flag to the server when you start it up.\n"
138 "note that it may not have a leading /");
141 setup_titles[eSysAdminName] = _("Citadel administrator username:");
142 setup_text[eSysAdminName] = _(
143 "Please enter the name of the Citadel user account that should be granted "
144 "administrative privileges once created. If using internal authentication "
145 "this user account will be created if it does not exist. For external "
146 "authentication this user account has to exist.");
149 setup_titles[eSysAdminPW] = _("Administrator password:");
150 setup_text[eSysAdminPW] = _(
151 "Enter a password for the system administrator. When setup\n"
152 "completes it will attempt to create the administrator user\n"
153 "and set the password specified here.\n");
155 setup_titles[eUID] = _("Citadel User ID:");
156 setup_text[eUID] = _(
157 "Citadel needs to run under its own user ID. This would\n"
158 "typically be called \"citadel\", but if you are running Citadel\n"
159 "as a public site, you might also call it \"bbs\" or \"guest\".\n"
160 "The server will run under this user ID. Please specify that\n"
161 "user ID here. You may specify either a user name or a numeric\n"
164 setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
165 setup_text[eIP_ADDR] = _(
166 "Please specify the IP address which the server should be listening to. "
167 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
168 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
169 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
170 "listen on all addresses. "
171 "This can usually be left to the default unless multiple instances of Citadel "
172 "are running on the same computer.");
174 setup_titles[eCTDL_Port] = _("Server port number:");
175 setup_text[eCTDL_Port] = _(
176 "Specify the TCP port number on which your server will run.\n"
177 "Normally, this will be port 504, which is the official port\n"
178 "assigned by the IANA for Citadel servers. You will only need\n"
179 "to specify a different port number if you run multiple instances\n"
180 "of Citadel on the same computer and there is something else\n"
181 "already using port 504.\n");
183 setup_titles[eAuthType] = _("Authentication method to use:");
184 setup_text[eAuthType] = _(
185 "Please choose the user authentication mode. By default Citadel will use its "
186 "own internal user accounts database. If you choose Host, Citadel users will "
187 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
188 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
189 "chooses the nonstandard MS Active Directory LDAP scheme."
191 "Do not change this option unless you are sure it is required, since changing "
192 "back requires a full reinstall of Citadel."
194 " 0. Self contained authentication\n"
195 " 1. Host system integrated authentication\n"
196 " 2. External LDAP - RFC 2307 compliant directory\n"
197 " 3. External LDAP - nonstandard MS Active Directory\n"
199 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
201 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
203 setup_titles[eLDAP_Host] = _("LDAP host:");
204 setup_text[eLDAP_Host] = _(
205 "Please enter the host name or IP address of your LDAP server.\n");
207 setup_titles[eLDAP_Port] = _("LDAP port number:");
208 setup_text[eLDAP_Port] = _(
209 "Please enter the port number of the LDAP service (usually 389).\n");
211 setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
212 setup_text[eLDAP_Base_DN] = _(
213 "Please enter the Base DN to search for authentication\n"
214 "(for example: dc=example,dc=com)\n");
216 setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
217 setup_text[eLDAP_Bind_DN] = _(
218 "Please enter the DN of an account to use for binding to the LDAP server for "
219 "performing queries. The account does not require any other privileges. If "
220 "your LDAP server allows anonymous queries, you can leave this blank.\n");
222 setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
223 setup_text[eLDAP_Bind_PW] = _(
224 "If you entered a Bind DN in the previous question, you must now enter\n"
225 "the password associated with that account. Otherwise, you can leave this\n"
229 // Debug loading of locales... Strace does a better job though.
230 printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
231 printf("Text domain: %s\n", textdomain("citadel-setup"));
232 printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
235 for (i = 0; i < eMaxQuestions; i++)
236 printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
243 * Print the stack frame for a backtrace
245 void cit_backtrace(void)
247 #ifdef HAVE_BACKTRACE
248 void *stack_frames[50];
252 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
253 strings = backtrace_symbols(stack_frames, size);
254 for (i = 0; i < size; i++) {
256 fprintf(stderr, "%s\n", strings[i]);
258 fprintf(stderr, "%p\n", stack_frames[i]);
266 void title(const char *text)
268 if (setup_type == UI_TEXT) {
269 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
275 int yesno(const char *question, int default_value)
281 switch (setup_type) {
285 printf("%s\n%s [%s] --> ",
288 ( default_value ? _("Yes") : _("No") )
290 if (fgets(buf, sizeof buf, stdin))
292 answer = tolower(buf[0]);
293 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
294 answer = default_value;
296 else if (answer == 'y') {
299 else if (answer == 'n') {
303 } while ((answer < 0) || (answer > 1));
307 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' %s --yesno '%s' 15 75",
308 getenv("CTDL_DIALOG"),
310 ( default_value ? "" : "--defaultno" ),
327 void important_message(const char *title, const char *msgtext)
331 switch (setup_type) {
334 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
335 printf(" %s \n\n%s\n\n", title, msgtext);
336 printf("%s", _("Press return to continue..."));
337 if (fgets(buf, sizeof buf, stdin))
342 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --msgbox '%s' 19 72",
343 getenv("CTDL_DIALOG"),
346 int rv = system(buf);
348 fprintf(stderr, _("failed to run the dialog command\n"));
352 fprintf(stderr, "%s\n", msgtext);
357 void important_msgnum(int msgnum)
359 important_message(_("Important Message"), setup_text[msgnum]);
362 void display_error(char *error_message_format, ...)
368 va_start(arg_ptr, error_message_format);
369 StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
372 important_message(_("Error"), ChrPtr(Msg));
376 void progress(char *text, long int curr, long int cmax)
378 static long dots_printed = 0L;
380 static FILE *fp = NULL;
383 switch (setup_type) {
387 printf("%s\n", text);
388 printf("....................................................");
389 printf("..........................\r");
391 } else if (curr == cmax) {
392 printf("\r%79s\n", "");
394 a = (curr * 100) / cmax;
397 while (dots_printed < a) {
407 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --gauge '%s' 7 72 0",
408 getenv("CTDL_DIALOG"),
411 fp = popen(buf, "w");
417 else if (curr == cmax) {
419 fprintf(fp, "100\n");
425 a = (curr * 100) / cmax;
427 fprintf(fp, "%ld\n", a);
436 assert(1==0); /* If we got here then the developer is a moron */
442 int uds_connectsock(char *sockpath)
445 struct sockaddr_un addr;
447 memset(&addr, 0, sizeof(addr));
448 addr.sun_family = AF_UNIX;
449 strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
451 s = socket(AF_UNIX, SOCK_STREAM, 0);
456 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
466 * input binary data from socket
468 void serv_read(char *buf, int bytes)
473 while (len < bytes) {
474 rlen = read(serv_sock, &buf[len], bytes - len);
484 * send binary to server
486 void serv_write(char *buf, int nbytes)
488 int bytes_written = 0;
490 while (bytes_written < nbytes) {
491 retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
495 bytes_written = bytes_written + retval;
502 * input string from socket - implemented in terms of serv_read()
504 void serv_gets(char *buf)
508 /* Read one character at a time.
511 serv_read(&buf[i], 1);
512 if (buf[i] == '\n' || i == (SIZ-1))
516 /* If we got a long line, discard characters until the newline.
519 while (buf[i] != '\n') {
520 serv_read(&buf[i], 1);
524 /* Strip all trailing nonprintables (crlf)
531 * send line to server - implemented in terms of serv_write()
533 void serv_puts(char *buf)
535 serv_write(buf, strlen(buf));
541 * On systems which use xinetd, see if we can offer to install Citadel as
542 * the default telnet target.
544 void check_xinetd_entry(void) {
545 char *filename = "/etc/xinetd.d/telnet";
548 int already_citadel = 0;
551 fp = fopen(filename, "r+");
552 if (fp == NULL) return; /* Not there. Oh well... */
554 while (fgets(buf, sizeof buf, fp) != NULL) {
555 if (strstr(buf, "/citadel") != NULL) {
560 if (already_citadel) return; /* Already set up this way. */
562 /* Otherwise, prompt the user to create an entry. */
563 if (getenv("CREATE_XINETD_ENTRY") != NULL) {
564 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
569 snprintf(buf, sizeof buf,
570 _("Setup can configure the \"xinetd\" service to automatically\n"
571 "connect incoming telnet sessions to Citadel, bypassing the\n"
572 "host system login: prompt. Would you like to do this?\n"
575 if (yesno(buf, 1) == 0) {
580 fp = fopen(filename, "w");
582 "# description: telnet service for Citadel users\n"
587 " socket_type = stream\n"
590 " server = /usr/sbin/in.telnetd\n"
591 " server_args = -h -L %s/citadel\n"
592 " log_on_failure += USERID\n"
597 /* Now try to restart the service */
598 rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
600 display_error(_("failed to restart xinetd.\n"));
607 * Offer to disable other MTA's
609 void disable_other_mta(const char *mta) {
615 snprintf(buf, sizeof buf,
616 "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
617 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
620 fp = popen(buf, "r");
621 if (fp == NULL) return;
623 while (fgets(buf, sizeof buf, fp) != NULL) {
627 if (lines == 0) return; /* Nothing to do. */
629 /* Offer to replace other MTA with the vastly superior Citadel :) */
631 snprintf(buf, sizeof buf,
632 "%s \"%s\" %s%s%s%s%s%s%s",
633 _("You appear to have the "),
636 "running on your system. If you want Citadel mail\n"
639 _(" you will have to manually integrate\n"
640 "them. It is preferable to disable "),
642 _(", and use Citadel's\n"
643 "SMTP, POP3, and IMAP services.\n\n"
646 _("so that Citadel has access to ports\n"
647 "25, 110, and 143?\n")
649 if (yesno(buf, 1) == 0) {
654 snprintf(buf, sizeof buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
657 display_error("%s %s.\n", _("failed to disable other mta"), mta);
659 snprintf(buf, sizeof buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
662 display_error(" %s.\n", _("failed to disable other mta"), mta);
665 const char *other_mtas[] = {
666 "courier-authdaemon",
689 void disable_other_mtas(void)
692 if ((getenv("ACT_AS_MTA") == NULL) ||
693 (getenv("ACT_AS_MTA") &&
694 strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
695 /* Offer to disable other MTA's on the system. */
696 while (!IsEmptyStr(other_mtas[i]))
698 disable_other_mta(other_mtas[i]);
704 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
708 char dialog_result[PATH_MAX];
712 strcpy(setupmsg, "");
714 switch (setup_type) {
717 printf("\n%s\n", prompt_text);
718 printf("%s\n%s\n", _("This is currently set to:"), Target);
719 printf("%s\n", _("Enter new value or press return to leave unchanged:"));
720 if (fgets(buf, sizeof buf, stdin)){
721 buf[strlen(buf) - 1] = 0;
723 if (!IsEmptyStr(buf))
728 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
729 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --nocancel --inputbox '%s' 19 72 '%s' 2>%s",
730 getenv("CTDL_DIALOG"),
737 fprintf(stderr, "failed to run whiptail or dialog\n");
740 fp = fopen(dialog_result, "r");
742 if (fgets(Target, sizeof buf, fp)) {
743 if (Target[strlen(Target)-1] == 10) {
744 Target[strlen(Target)-1] = 0;
748 unlink(dialog_result);
752 if (*DefValue != '\0')
753 strcpy(Target, DefValue);
758 void set_bool_val(int msgpos, int *ip, char *DefValue)
760 title(setup_titles[msgpos]);
761 *ip = yesno(setup_text[msgpos], *ip);
764 void set_str_val(int msgpos, char *Target, char *DefValue)
766 strprompt(setup_titles[msgpos],
773 /* like set_str_val() but make sure we ended up with a numeric value */
774 void set_int_val(int msgpos, char *target, char *DefValue)
777 set_str_val(msgpos, target, DefValue);
778 if (!strcmp(target, "0")) return;
779 if (atoi(target) != 0) return;
784 void edit_value(int curr)
788 char ctdluidname[256];
791 if (setup_type == UI_SILENT)
793 Value = getenv(EnvNames[curr]);
802 set_str_val(curr, configs[13], Value);
806 set_str_val(curr, admin_pass, Value);
810 if (setup_type == UI_SILENT)
813 sprintf(configs[69], "%d", atoi(Value));
819 strcpy(configs[69], "0"); /* work-around for Windows */
821 i = atoi(configs[69]);
824 set_int_val(curr, configs[69], Value);
825 sprintf(configs[69], "%d", i);
828 strcpy(ctdluidname, pw->pw_name);
829 set_str_val(curr, ctdluidname, Value);
830 pw = getpwnam(ctdluidname);
832 sprintf(configs[69], "%d", pw->pw_uid);
834 else if (atoi(ctdluidname) > 0) {
835 sprintf(configs[69], "%d", atoi(ctdluidname));
843 set_str_val(curr, configs[37], Value);
847 set_int_val(curr, configs[68], Value);
851 if (setup_type == UI_SILENT)
854 //config.c_auth_mode = AUTHMODE_NATIVE;
858 if ((strcasecmp(auth, "yes") == 0) ||
859 (strcasecmp(auth, "host") == 0))
861 //config.c_auth_mode = AUTHMODE_HOST;
863 else if (strcasecmp(auth, "ldap") == 0){
864 //config.c_auth_mode = AUTHMODE_LDAP;
866 else if ((strcasecmp(auth, "ldap_ad") == 0) ||
867 (strcasecmp(auth, "active directory") == 0)){
868 //config.c_auth_mode = AUTHMODE_LDAP_AD;
873 set_int_val(curr, configs[52], Value);
878 if (IsEmptyStr(configs[32])) {
879 strcpy(configs[32], "localhost");
881 set_str_val(curr, configs[32], Value);
885 if (atoi(configs[33]) == 0) {
886 strcpy(configs[33], "389");
888 set_int_val(curr, configs[33], Value);
892 set_str_val(curr, configs[34], Value);
896 set_str_val(curr, configs[35], Value);
900 set_str_val(curr, configs[36], Value);
909 * Figure out what type of user interface we're going to use
911 int discover_ui(void)
914 /* Use "whiptail" or "dialog" if we have it */
915 if (getenv("CTDL_DIALOG") != NULL) {
925 * Strip "db" entries out of /etc/nsswitch.conf
934 int file_changed = 0;
935 char new_filename[64];
938 fp_read = fopen(NSSCONF, "r");
939 if (fp_read == NULL) {
943 strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
944 fd_write = mkstemp(new_filename);
950 while (fgets(buf, sizeof buf, fp_read) != NULL) {
952 for (i=0; i<strlen(buf_nc); ++i) {
953 if (buf_nc[i] == '#') {
957 for (i=0; i<strlen(buf_nc); ++i) {
958 if (!strncasecmp(&buf_nc[i], "db", 2)) {
960 if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
962 strcpy(&buf_nc[i], &buf_nc[i+2]);
963 strcpy(&buf[i], &buf[i+2]);
965 strcpy(&buf_nc[i], &buf_nc[i+1]);
966 strcpy(&buf[i], &buf[i+1]);
972 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
975 unlink(new_filename);
983 unlink(new_filename);
987 snprintf(question, sizeof question,
990 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
991 "one or more services. This is not necessary on most systems,\n"
992 "and it is known to crash the Citadel server when delivering\n"
993 "mail to the Internet.\n"
995 "Do you want this module to be automatically disabled?\n"
1000 if (yesno(question, 1)) {
1001 snprintf(buf, sizeof buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
1004 fprintf(stderr, "failed to edit %s.\n", NSSCONF);
1006 chmod(NSSCONF, 0644);
1008 unlink(new_filename);
1014 important_message(_("Setup finished"),
1015 _("Setup of the Citadel server is complete.\n"
1016 "If you will be using WebCit, please run its\n"
1017 "setup program now; otherwise, run './citadel'\n"
1019 important_message(_("Setup failed"),
1020 _("Setup is finished, but the Citadel server failed to start.\n"
1021 "Go back and check your configuration.\n")
1022 important_message(_("Setup finished"),
1023 _("Setup is finished. You may now start the server."));
1028 #define GetDefaultVALINT(CFGNAME, DEFL) GetDefaultValInt(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
1029 void GetDefaultValInt(int *WhereTo, const char *VarName, int DefVal)
1032 if (*WhereTo == 0) *WhereTo = DefVal;
1034 if ((setup_type == UI_SILENT) &&
1035 (ch = getenv(VarName), ch != NULL))
1037 *WhereTo = atoi(ch);
1040 #define GetDefaultVALCHAR(CFGNAME, DEFL) GetDefaultValChar(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
1041 void GetDefaultValChar(char *WhereTo, const char *VarName, char DefVal)
1044 if (*WhereTo == 0) *WhereTo = DefVal;
1046 if ((setup_type == UI_SILENT) &&
1047 (ch = getenv(VarName), ch != NULL))
1049 *WhereTo = atoi(ch);
1052 #define GetDefaultVALSTR(CFGNAME, DEFL) GetDefaultValStr(&config.CFGNAME[0], sizeof(config.CFGNAME), "CITADEL_"#CFGNAME, DEFL)
1053 void GetDefaultValStr(char *WhereTo, size_t nMax, const char *VarName, const char *DefVal)
1056 if (*WhereTo == '\0')
1057 safestrncpy(WhereTo, DefVal, nMax);
1059 if ((setup_type == UI_SILENT) &&
1060 (ch = getenv(VarName), ch != NULL))
1062 safestrncpy(WhereTo, ch, nMax);
1067 void set_default_values(void)
1071 struct utsname my_utsname;
1074 /* Determine our host name, in case we need to use it as a default */
1077 /* set some sample/default values in place of blanks... */
1078 GetDefaultVALSTR(c_nodename, my_utsname.nodename);
1079 strtok(config.c_nodename, ".");
1080 if (IsEmptyStr(config.c_fqdn) ) {
1081 if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
1082 safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
1084 safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
1087 GetDefaultVALSTR(c_humannode, _("My System"));
1088 GetDefaultVALSTR(c_phonenum, _("US 800 555 1212"));
1090 GetDefaultVALCHAR(c_initax, 4);
1092 GetDefaultVALSTR(c_moreprompt, "<more>");
1093 GetDefaultVALSTR(c_twitroom, "Trashcan");
1094 GetDefaultVALSTR(c_baseroom, BASEROOM);
1095 GetDefaultVALSTR(c_aideroom, "Aide");
1096 GetDefaultVALINT(c_port_number, 504);
1098 GetDefaultVALINT(c_sleeping, 900);
1100 if (config.c_ctdluid == 0) {
1101 pw = getpwnam("citadel");
1103 config.c_ctdluid = pw->pw_uid;
1106 if (config.c_ctdluid == 0) {
1107 pw = getpwnam("bbs");
1109 config.c_ctdluid = pw->pw_uid;
1112 if (config.c_ctdluid == 0) {
1113 pw = getpwnam("guest");
1115 config.c_ctdluid = pw->pw_uid;
1118 if (config.c_createax == 0) {
1119 config.c_createax = 3;
1122 * Negative values for maxsessions are not allowed.
1124 if (config.c_maxsessions < 0) {
1125 config.c_maxsessions = 0;
1127 /* We need a system default message expiry policy, because this is
1128 * the top level and there's no 'higher' policy to fall back on.
1129 * By default, do not expire messages at all.
1131 if (config.c_ep.expire_mode == 0) {
1132 config.c_ep.expire_mode = EXPIRE_MANUAL;
1133 config.c_ep.expire_value = 0;
1137 * Default port numbers for various services
1139 GetDefaultVALINT(c_smtp_port, 25);
1140 GetDefaultVALINT(c_pop3_port, 110);
1141 GetDefaultVALINT(c_imap_port, 143);
1142 GetDefaultVALINT(c_msa_port, 587);
1143 GetDefaultVALINT(c_smtps_port, 465);
1144 GetDefaultVALINT(c_pop3s_port, 995);
1145 GetDefaultVALINT(c_imaps_port, 993);
1146 GetDefaultVALINT(c_pftcpdict_port, -1);
1147 GetDefaultVALINT(c_managesieve_port, 2020);
1148 GetDefaultVALINT(c_xmpp_c2s_port, 5222);
1149 GetDefaultVALINT(c_xmpp_s2s_port, 5269);
1155 int main(int argc, char *argv[])
1165 char relhome[PATH_MAX]="";
1166 char ctdldir[PATH_MAX]=CTDLDIR;
1169 char *activity = NULL;
1171 /* Keep a mild groove on */
1172 program_title = _("Citadel setup program");
1174 /* set an invalid setup type */
1177 /* Check to see if we're running the web installer */
1178 if (getenv("CITADEL_INSTALLER") != NULL) {
1179 using_web_installer = 1;
1182 /* parse command line args */
1183 for (a = 0; a < argc; ++a) {
1184 if (!strncmp(argv[a], "-u", 2)) {
1185 strcpy(aaa, argv[a]);
1186 strcpy(aaa, &aaa[2]);
1187 setup_type = atoi(aaa);
1189 else if (!strcmp(argv[a], "-i")) {
1192 else if (!strcmp(argv[a], "-q")) {
1193 setup_type = UI_SILENT;
1195 else if (!strncmp(argv[a], "-h", 2)) {
1196 relh=argv[a][2]!='/';
1198 safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
1200 safestrncpy(relhome, &argv[a][2], sizeof relhome);
1207 calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1210 /* If a setup type was not specified, try to determine automatically
1211 * the best one to use out of all available types.
1213 if (setup_type < 0) {
1214 setup_type = discover_ui();
1216 if (info_only == 1) {
1217 important_message(_("Citadel Setup"), CITADEL);
1221 enable_home = ( relh | home );
1223 if (chdir(ctdl_run_dir) != 0) {
1224 display_error(_("Citadel Setup"),
1226 _("The directory you specified does not exist"),
1233 * Connect to the running Citadel server.
1235 while ((serv_sock < 0) && (nRetries < 10)) {
1236 serv_sock = uds_connectsock(file_citadel_admin_socket);
1241 if (serv_sock < 0) {
1244 _("Setup could not connect to a running Citadel server."),
1245 strerror(errno), file_citadel_admin_socket
1251 * read the server greeting
1254 if (buf[0] != '2') {
1255 display_error("%s\n", buf);
1260 * Are we connected to the correct Citadel server?
1264 if (buf[0] != '1') {
1265 display_error("%s\n", buf);
1269 while (serv_gets(buf), strcmp(buf, "000")) {
1271 if (atoi(buf) != REV_LEVEL) {
1272 display_error("%s\n",
1273 _("Your setup program and Citadel server are from different versions.")
1282 * Load the server's configuration
1284 serv_puts("CONF GET");
1286 if (buf[0] != '1') {
1287 display_error("%s\n", buf);
1290 memset(configs, 0, sizeof configs);
1292 while (serv_gets(buf), strcmp(buf, "000")) {
1293 if (a < NUM_CONFIGS) {
1294 safestrncpy(configs[a], buf, sizeof(configs[a]));
1303 /* _("Citadel Setup"), */
1305 if (setup_type == UI_TEXT) {
1306 printf("\n\n\n *** %s ***\n\n", program_title);
1309 if (setup_type == UI_DIALOG) {
1310 system("clear 2>/dev/null");
1313 set_default_values();
1315 /* Go through a series of dialogs prompting for config info */
1316 for (curr = 1; curr < eMaxQuestions; ++curr) {
1320 && (atoi(configs[52]) != AUTHMODE_LDAP)
1321 && (atoi(configs[52]) != AUTHMODE_LDAP_AD)
1323 curr += 5; /* skip LDAP questions if we're not authenticating against LDAP */
1326 if (curr == eSysAdminName) {
1327 if (atoi(configs[52]) == AUTHMODE_NATIVE) {
1328 /* for native auth mode, fetch the admin's existing pw */
1329 snprintf(buf, sizeof buf, "AGUP %s", configs[13]);
1332 if (buf[0] == '2') {
1333 extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
1337 ++curr; /* skip the password question for non-native auth modes */
1342 if ((pw = getpwuid(atoi(configs[69]))) == NULL) {
1348 create_run_directories(atoi(configs[69]), gid);
1350 activity = _("Reconfiguring Citadel server");
1351 progress(activity, 0, NUM_CONFIGS+3);
1352 sleep(1); /* Let the message appear briefly */
1353 serv_puts("CONF SET");
1355 if (buf[0] == '4') {
1356 for (i=0; i<NUM_CONFIGS; ++i) {
1357 progress(activity, i+1, NUM_CONFIGS+3);
1358 serv_puts(configs[i]);
1362 sleep(1); /* Let the message appear briefly */
1365 * Create the administrator account. It's ok if the command fails if this user already exists.
1367 progress(activity, NUM_CONFIGS+1, NUM_CONFIGS+3);
1368 snprintf(buf, sizeof buf, "CREU %s|%s", configs[13], admin_pass);
1370 progress(activity, NUM_CONFIGS+2, NUM_CONFIGS+3);
1372 progress(activity, NUM_CONFIGS+3, NUM_CONFIGS+3);
1375 * Assign the desired password and access level to the administrator account.
1377 snprintf(buf, sizeof buf, "AGUP %s", configs[13]);
1380 if (buf[0] == '2') {
1381 int admin_flags = extract_int(&buf[4], 2);
1382 int admin_times_called = extract_int(&buf[4], 3);
1383 int admin_msgs_posted = extract_int(&buf[4], 4);
1384 snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
1385 configs[13], admin_pass, admin_flags, admin_times_called, admin_msgs_posted
1392 check_xinetd_entry(); /* Check /etc/xinetd.d/telnet */
1393 disable_other_mtas(); /* Offer to disable other MTAs */
1394 fixnss(); /* Check for the 'db' nss and offer to disable it */
1397 activity = _("Setting file permissions");
1398 progress(activity, 0, 2);
1399 //chown(file_citadel_config, config.c_ctdluid, gid);
1400 progress(activity, 1, 2);
1401 chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1402 progress(activity, 2, 2);
1407 activity = _("Restarting Citadel server to apply changes");
1408 progress(activity, 0, 41);
1412 long original_start_time = extract_long(&buf[4], 3);
1414 progress(activity, 1, 41);
1415 serv_puts("DOWN 1");
1416 progress(activity, 2, 41);
1418 if (buf[0] != '2') {
1419 display_error("%s\n", buf);
1426 for (i=3; i<=6; ++i) { /* wait for server to shut down */
1427 progress(activity, i, 41);
1431 for (i=7; ((i<=38) && (serv_sock < 0)) ; ++i) { /* wait for server to start up */
1432 progress(activity, i, 41);
1433 serv_sock = uds_connectsock(file_citadel_admin_socket);
1437 progress(activity, 39, 41);
1440 progress(activity, 40, 41);
1443 long new_start_time = extract_long(&buf[4], 3);
1446 progress(activity, 41, 41);
1448 if ( (original_start_time == new_start_time)
1449 || (new_start_time <= 0)
1451 display_error("%s\n",
1452 _("Setup failed to restart Citadel server. Please restart it manually.")