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]);
268 void title(const char *text)
270 if (setup_type == UI_TEXT) {
271 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
277 int yesno(const char *question, int default_value)
283 switch (setup_type) {
287 printf("%s\n%s [%s] --> ",
290 ( default_value ? _("Yes") : _("No") )
292 if (fgets(buf, sizeof buf, stdin))
294 answer = tolower(buf[0]);
295 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
296 answer = default_value;
298 else if (answer == 'y') {
301 else if (answer == 'n') {
305 } while ((answer < 0) || (answer > 1));
309 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' %s --yesno '%s' 15 75",
310 getenv("CTDL_DIALOG"),
312 ( default_value ? "" : "--defaultno" ),
329 void important_message(const char *title, const char *msgtext)
333 switch (setup_type) {
336 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");
337 printf(" %s \n\n%s\n\n", title, msgtext);
338 printf("%s", _("Press return to continue..."));
339 if (fgets(buf, sizeof buf, stdin));
343 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --msgbox '%s' 19 72",
344 getenv("CTDL_DIALOG"),
347 int rv = system(buf);
349 fprintf(stderr, _("failed to run the dialog command\n"));
353 fprintf(stderr, "%s\n", msgtext);
358 void important_msgnum(int msgnum)
360 important_message(_("Important Message"), setup_text[msgnum]);
363 void display_error(char *error_message_format, ...)
369 va_start(arg_ptr, error_message_format);
370 StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
373 important_message(_("Error"), ChrPtr(Msg));
377 void progress(char *text, long int curr, long int cmax)
379 static long dots_printed = 0L;
381 static FILE *fp = NULL;
384 switch (setup_type) {
388 printf("%s\n", text);
389 printf("....................................................");
390 printf("..........................\r");
392 } else if (curr == cmax) {
393 printf("\r%79s\n", "");
395 a = (curr * 100) / cmax;
398 while (dots_printed < a) {
408 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --gauge '%s' 7 72 0",
409 getenv("CTDL_DIALOG"),
412 fp = popen(buf, "w");
418 else if (curr == cmax) {
420 fprintf(fp, "100\n");
426 a = (curr * 100) / cmax;
428 fprintf(fp, "%ld\n", a);
437 assert(1==0); /* If we got here then the developer is a moron */
443 int uds_connectsock(char *sockpath)
446 struct sockaddr_un addr;
448 memset(&addr, 0, sizeof(addr));
449 addr.sun_family = AF_UNIX;
450 strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
452 s = socket(AF_UNIX, SOCK_STREAM, 0);
457 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
467 * input binary data from socket
469 void serv_read(char *buf, int bytes)
474 while (len < bytes) {
475 rlen = read(serv_sock, &buf[len], bytes - len);
485 * send binary to server
487 void serv_write(char *buf, int nbytes)
489 int bytes_written = 0;
491 while (bytes_written < nbytes) {
492 retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
496 bytes_written = bytes_written + retval;
503 * input string from socket - implemented in terms of serv_read()
505 void serv_gets(char *buf)
509 /* Read one character at a time.
512 serv_read(&buf[i], 1);
513 if (buf[i] == '\n' || i == (SIZ-1))
517 /* If we got a long line, discard characters until the newline.
520 while (buf[i] != '\n') {
521 serv_read(&buf[i], 1);
525 /* Strip all trailing nonprintables (crlf)
532 * send line to server - implemented in terms of serv_write()
534 void serv_puts(char *buf)
536 serv_write(buf, strlen(buf));
542 * On systems which use xinetd, see if we can offer to install Citadel as
543 * the default telnet target.
545 void check_xinetd_entry(void) {
546 char *filename = "/etc/xinetd.d/telnet";
549 int already_citadel = 0;
552 fp = fopen(filename, "r+");
553 if (fp == NULL) return; /* Not there. Oh well... */
555 while (fgets(buf, sizeof buf, fp) != NULL) {
556 if (strstr(buf, "/citadel") != NULL) {
561 if (already_citadel) return; /* Already set up this way. */
563 /* Otherwise, prompt the user to create an entry. */
564 if (getenv("CREATE_XINETD_ENTRY") != NULL) {
565 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
570 snprintf(buf, sizeof buf,
571 _("Setup can configure the \"xinetd\" service to automatically\n"
572 "connect incoming telnet sessions to Citadel, bypassing the\n"
573 "host system login: prompt. Would you like to do this?\n"
576 if (yesno(buf, 1) == 0) {
581 fp = fopen(filename, "w");
583 "# description: telnet service for Citadel users\n"
588 " socket_type = stream\n"
591 " server = /usr/sbin/in.telnetd\n"
592 " server_args = -h -L %s/citadel\n"
593 " log_on_failure += USERID\n"
598 /* Now try to restart the service */
599 rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
601 display_error(_("failed to restart xinetd.\n"));
608 * Offer to disable other MTA's
610 void disable_other_mta(const char *mta) {
616 snprintf(buf, sizeof buf,
617 "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
618 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
621 fp = popen(buf, "r");
622 if (fp == NULL) return;
624 while (fgets(buf, sizeof buf, fp) != NULL) {
628 if (lines == 0) return; /* Nothing to do. */
630 /* Offer to replace other MTA with the vastly superior Citadel :) */
632 snprintf(buf, sizeof buf,
633 "%s \"%s\" %s%s%s%s%s%s%s",
634 _("You appear to have the "),
637 "running on your system. If you want Citadel mail\n"
640 _(" you will have to manually integrate\n"
641 "them. It is preferable to disable "),
643 _(", and use Citadel's\n"
644 "SMTP, POP3, and IMAP services.\n\n"
647 _("so that Citadel has access to ports\n"
648 "25, 110, and 143?\n")
650 if (yesno(buf, 1) == 0) {
655 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);
658 display_error("%s %s.\n", _("failed to disable other mta"), mta);
660 snprintf(buf, sizeof buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
663 display_error(" %s.\n", _("failed to disable other mta"), mta);
666 const char *other_mtas[] = {
667 "courier-authdaemon",
690 void disable_other_mtas(void)
693 if ((getenv("ACT_AS_MTA") == NULL) ||
694 (getenv("ACT_AS_MTA") &&
695 strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
696 /* Offer to disable other MTA's on the system. */
697 while (!IsEmptyStr(other_mtas[i]))
699 disable_other_mta(other_mtas[i]);
705 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
709 char dialog_result[PATH_MAX];
713 strcpy(setupmsg, "");
715 switch (setup_type) {
718 printf("\n%s\n", prompt_text);
719 printf("%s\n%s\n", _("This is currently set to:"), Target);
720 printf("%s\n", _("Enter new value or press return to leave unchanged:"));
721 if (fgets(buf, sizeof buf, stdin)){
722 buf[strlen(buf) - 1] = 0;
724 if (!IsEmptyStr(buf))
729 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
730 snprintf(buf, sizeof buf, "exec %s --backtitle '%s' --nocancel --inputbox '%s' 19 72 '%s' 2>%s",
731 getenv("CTDL_DIALOG"),
738 fprintf(stderr, "failed to run whiptail or dialog\n");
741 fp = fopen(dialog_result, "r");
743 if (fgets(Target, sizeof buf, fp)) {
744 if (Target[strlen(Target)-1] == 10) {
745 Target[strlen(Target)-1] = 0;
749 unlink(dialog_result);
753 if (*DefValue != '\0')
754 strcpy(Target, DefValue);
759 void set_bool_val(int msgpos, int *ip, char *DefValue)
761 title(setup_titles[msgpos]);
762 *ip = yesno(setup_text[msgpos], *ip);
765 void set_str_val(int msgpos, char *Target, char *DefValue)
767 strprompt(setup_titles[msgpos],
774 /* like set_str_val() but make sure we ended up with a numeric value */
775 void set_int_val(int msgpos, char *target, char *DefValue)
778 set_str_val(msgpos, target, DefValue);
779 if (!strcmp(target, "0")) return;
780 if (atoi(target) != 0) return;
785 void edit_value(int curr)
789 char ctdluidname[256];
792 if (setup_type == UI_SILENT)
794 Value = getenv(EnvNames[curr]);
803 set_str_val(curr, configs[13], Value);
807 set_str_val(curr, admin_pass, Value);
811 if (setup_type == UI_SILENT)
814 sprintf(configs[69], "%d", atoi(Value));
820 strcpy(configs[69], "0"); /* work-around for Windows */
822 i = atoi(configs[69]);
825 set_int_val(curr, configs[69], Value);
826 sprintf(configs[69], "%d", i);
829 strcpy(ctdluidname, pw->pw_name);
830 set_str_val(curr, ctdluidname, Value);
831 pw = getpwnam(ctdluidname);
833 sprintf(configs[69], "%d", pw->pw_uid);
835 else if (atoi(ctdluidname) > 0) {
836 sprintf(configs[69], "%d", atoi(ctdluidname));
844 set_str_val(curr, configs[37], Value);
848 set_int_val(curr, configs[68], Value);
852 if (setup_type == UI_SILENT)
855 //config.c_auth_mode = AUTHMODE_NATIVE;
859 if ((strcasecmp(auth, "yes") == 0) ||
860 (strcasecmp(auth, "host") == 0))
862 //config.c_auth_mode = AUTHMODE_HOST;
864 else if (strcasecmp(auth, "ldap") == 0){
865 //config.c_auth_mode = AUTHMODE_LDAP;
867 else if ((strcasecmp(auth, "ldap_ad") == 0) ||
868 (strcasecmp(auth, "active directory") == 0)){
869 //config.c_auth_mode = AUTHMODE_LDAP_AD;
874 set_int_val(curr, configs[52], Value);
879 if (IsEmptyStr(configs[32])) {
880 strcpy(configs[32], "localhost");
882 set_str_val(curr, configs[32], Value);
886 if (atoi(configs[33]) == 0) {
887 strcpy(configs[33], "389");
889 set_int_val(curr, configs[33], Value);
893 set_str_val(curr, configs[34], Value);
897 set_str_val(curr, configs[35], Value);
901 set_str_val(curr, configs[36], Value);
910 * Figure out what type of user interface we're going to use
912 int discover_ui(void)
915 /* Use "whiptail" or "dialog" if we have it */
916 if (getenv("CTDL_DIALOG") != NULL) {
926 * Strip "db" entries out of /etc/nsswitch.conf
935 int file_changed = 0;
936 char new_filename[64];
939 fp_read = fopen(NSSCONF, "r");
940 if (fp_read == NULL) {
944 strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
945 fd_write = mkstemp(new_filename);
951 while (fgets(buf, sizeof buf, fp_read) != NULL) {
953 for (i=0; i<strlen(buf_nc); ++i) {
954 if (buf_nc[i] == '#') {
958 for (i=0; i<strlen(buf_nc); ++i) {
959 if (!strncasecmp(&buf_nc[i], "db", 2)) {
961 if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
963 strcpy(&buf_nc[i], &buf_nc[i+2]);
964 strcpy(&buf[i], &buf[i+2]);
966 strcpy(&buf_nc[i], &buf_nc[i+1]);
967 strcpy(&buf[i], &buf[i+1]);
973 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
976 unlink(new_filename);
984 unlink(new_filename);
988 snprintf(question, sizeof question,
991 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
992 "one or more services. This is not necessary on most systems,\n"
993 "and it is known to crash the Citadel server when delivering\n"
994 "mail to the Internet.\n"
996 "Do you want this module to be automatically disabled?\n"
1001 if (yesno(question, 1)) {
1002 snprintf(buf, sizeof buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
1005 fprintf(stderr, "failed to edit %s.\n", NSSCONF);
1007 chmod(NSSCONF, 0644);
1009 unlink(new_filename);
1015 important_message(_("Setup finished"),
1016 _("Setup of the Citadel server is complete.\n"
1017 "If you will be using WebCit, please run its\n"
1018 "setup program now; otherwise, run './citadel'\n"
1020 important_message(_("Setup failed"),
1021 _("Setup is finished, but the Citadel server failed to start.\n"
1022 "Go back and check your configuration.\n")
1023 important_message(_("Setup finished"),
1024 _("Setup is finished. You may now start the server."));
1029 #define GetDefaultVALINT(CFGNAME, DEFL) GetDefaultValInt(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
1030 void GetDefaultValInt(int *WhereTo, const char *VarName, int DefVal)
1033 if (*WhereTo == 0) *WhereTo = DefVal;
1035 if ((setup_type == UI_SILENT) &&
1036 (ch = getenv(VarName), ch != NULL))
1038 *WhereTo = atoi(ch);
1041 #define GetDefaultVALCHAR(CFGNAME, DEFL) GetDefaultValChar(&config.CFGNAME, "CITADEL_"#CFGNAME, DEFL)
1042 void GetDefaultValChar(char *WhereTo, const char *VarName, char DefVal)
1045 if (*WhereTo == 0) *WhereTo = DefVal;
1047 if ((setup_type == UI_SILENT) &&
1048 (ch = getenv(VarName), ch != NULL))
1050 *WhereTo = atoi(ch);
1053 #define GetDefaultVALSTR(CFGNAME, DEFL) GetDefaultValStr(&config.CFGNAME[0], sizeof(config.CFGNAME), "CITADEL_"#CFGNAME, DEFL)
1054 void GetDefaultValStr(char *WhereTo, size_t nMax, const char *VarName, const char *DefVal)
1057 if (*WhereTo == '\0')
1058 safestrncpy(WhereTo, DefVal, nMax);
1060 if ((setup_type == UI_SILENT) &&
1061 (ch = getenv(VarName), ch != NULL))
1063 safestrncpy(WhereTo, ch, nMax);
1068 void set_default_values(void)
1072 struct utsname my_utsname;
1075 /* Determine our host name, in case we need to use it as a default */
1078 /* set some sample/default values in place of blanks... */
1079 GetDefaultVALSTR(c_nodename, my_utsname.nodename);
1080 strtok(config.c_nodename, ".");
1081 if (IsEmptyStr(config.c_fqdn) ) {
1082 if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
1083 safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
1085 safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
1088 GetDefaultVALSTR(c_humannode, _("My System"));
1089 GetDefaultVALSTR(c_phonenum, _("US 800 555 1212"));
1091 GetDefaultVALCHAR(c_initax, 4);
1093 GetDefaultVALSTR(c_moreprompt, "<more>");
1094 GetDefaultVALSTR(c_twitroom, "Trashcan");
1095 GetDefaultVALSTR(c_baseroom, BASEROOM);
1096 GetDefaultVALSTR(c_aideroom, "Aide");
1097 GetDefaultVALINT(c_port_number, 504);
1099 GetDefaultVALINT(c_sleeping, 900);
1101 if (config.c_ctdluid == 0) {
1102 pw = getpwnam("citadel");
1104 config.c_ctdluid = pw->pw_uid;
1107 if (config.c_ctdluid == 0) {
1108 pw = getpwnam("bbs");
1110 config.c_ctdluid = pw->pw_uid;
1113 if (config.c_ctdluid == 0) {
1114 pw = getpwnam("guest");
1116 config.c_ctdluid = pw->pw_uid;
1119 if (config.c_createax == 0) {
1120 config.c_createax = 3;
1123 * Negative values for maxsessions are not allowed.
1125 if (config.c_maxsessions < 0) {
1126 config.c_maxsessions = 0;
1128 /* We need a system default message expiry policy, because this is
1129 * the top level and there's no 'higher' policy to fall back on.
1130 * By default, do not expire messages at all.
1132 if (config.c_ep.expire_mode == 0) {
1133 config.c_ep.expire_mode = EXPIRE_MANUAL;
1134 config.c_ep.expire_value = 0;
1138 * Default port numbers for various services
1140 GetDefaultVALINT(c_smtp_port, 25);
1141 GetDefaultVALINT(c_pop3_port, 110);
1142 GetDefaultVALINT(c_imap_port, 143);
1143 GetDefaultVALINT(c_msa_port, 587);
1144 GetDefaultVALINT(c_smtps_port, 465);
1145 GetDefaultVALINT(c_pop3s_port, 995);
1146 GetDefaultVALINT(c_imaps_port, 993);
1147 GetDefaultVALINT(c_pftcpdict_port, -1);
1148 GetDefaultVALINT(c_managesieve_port, 2020);
1149 GetDefaultVALINT(c_xmpp_c2s_port, 5222);
1150 GetDefaultVALINT(c_xmpp_s2s_port, 5269);
1156 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 serv_sock = uds_connectsock(file_citadel_admin_socket);
1236 if (serv_sock < 0) {
1239 _("Setup could not connect to a running Citadel server.")
1245 * read the server greeting
1248 if (buf[0] != '2') {
1249 display_error("%s\n", buf);
1254 * Are we connected to the correct Citadel server?
1258 if (buf[0] != '1') {
1259 display_error("%s\n", buf);
1263 while (serv_gets(buf), strcmp(buf, "000")) {
1265 if (atoi(buf) != REV_LEVEL) {
1266 display_error("%s\n",
1267 _("Your setup program and Citadel server are from different versions.")
1276 * Load the server's configuration
1278 serv_puts("CONF GET");
1280 if (buf[0] != '1') {
1281 display_error("%s\n", buf);
1284 memset(configs, 0, sizeof configs);
1286 while (serv_gets(buf), strcmp(buf, "000")) {
1287 if (a < NUM_CONFIGS) {
1288 safestrncpy(configs[a], buf, sizeof(configs[a]));
1297 /* _("Citadel Setup"), */
1299 if (setup_type == UI_TEXT) {
1300 printf("\n\n\n *** %s ***\n\n", program_title);
1303 if (setup_type == UI_DIALOG) {
1304 system("clear 2>/dev/null");
1307 set_default_values();
1309 /* Go through a series of dialogs prompting for config info */
1310 for (curr = 1; curr < eMaxQuestions; ++curr) {
1313 && (atoi(configs[52]) != AUTHMODE_LDAP)
1314 && (atoi(configs[52]) != AUTHMODE_LDAP_AD)
1316 curr += 5; /* skip LDAP questions if we're not authenticating against LDAP */
1319 if (curr == eSysAdminName) {
1320 if (atoi(configs[52]) == AUTHMODE_NATIVE) {
1321 /* for native auth mode, fetch the admin's existing pw */
1322 snprintf(buf, sizeof buf, "AGUP %s", configs[13]);
1325 if (buf[0] == '2') {
1326 extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
1330 ++curr; /* skip the password question for non-native auth modes */
1335 if ((pw = getpwuid(atoi(configs[69]))) == NULL) {
1341 create_run_directories(atoi(configs[69]), gid);
1343 activity = _("Reconfiguring Citadel server");
1344 progress(activity, 0, NUM_CONFIGS+3);
1345 sleep(1); /* Let the message appear briefly */
1346 serv_puts("CONF SET");
1348 if (buf[0] == '4') {
1349 for (i=0; i<NUM_CONFIGS; ++i) {
1350 progress(activity, i+1, NUM_CONFIGS+3);
1351 serv_puts(configs[i]);
1355 sleep(1); /* Let the message appear briefly */
1358 * Create the administrator account. It's ok if the command fails if this user already exists.
1360 progress(activity, NUM_CONFIGS+1, NUM_CONFIGS+3);
1361 snprintf(buf, sizeof buf, "CREU %s|%s", configs[13], admin_pass);
1363 progress(activity, NUM_CONFIGS+2, NUM_CONFIGS+3);
1365 progress(activity, NUM_CONFIGS+3, NUM_CONFIGS+3);
1368 * Assign the desired password and access level to the administrator account.
1370 snprintf(buf, sizeof buf, "AGUP %s", configs[13]);
1373 if (buf[0] == '2') {
1374 int admin_flags = extract_int(&buf[4], 2);
1375 int admin_times_called = extract_int(&buf[4], 3);
1376 int admin_msgs_posted = extract_int(&buf[4], 4);
1377 snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
1378 configs[13], admin_pass, admin_flags, admin_times_called, admin_msgs_posted
1385 check_xinetd_entry(); /* Check /etc/xinetd.d/telnet */
1386 disable_other_mtas(); /* Offer to disable other MTAs */
1387 fixnss(); /* Check for the 'db' nss and offer to disable it */
1390 activity = _("Setting file permissions");
1391 progress(activity, 0, 2);
1392 //chown(file_citadel_config, config.c_ctdluid, gid);
1393 progress(activity, 1, 2);
1394 chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1395 progress(activity, 2, 2);
1400 activity = _("Restarting Citadel server to apply changes");
1401 progress(activity, 0, 41);
1405 long original_start_time = extract_long(&buf[4], 3);
1407 progress(activity, 1, 41);
1408 serv_puts("DOWN 1");
1409 progress(activity, 2, 41);
1411 if (buf[0] != '2') {
1412 display_error("%s\n", buf);
1419 for (i=3; i<=6; ++i) { /* wait for server to shut down */
1420 progress(activity, i, 41);
1424 for (i=7; ((i<=38) && (serv_sock < 0)) ; ++i) { /* wait for server to start up */
1425 progress(activity, i, 41);
1426 serv_sock = uds_connectsock(file_citadel_admin_socket);
1430 progress(activity, 39, 41);
1433 progress(activity, 40, 41);
1436 long new_start_time = extract_long(&buf[4], 3);
1439 progress(activity, 41, 41);
1441 if ( (original_start_time == new_start_time)
1442 || (new_start_time <= 0)
1444 display_error("%s\n",
1445 _("Setup failed to restart Citadel server. Please restart it manually.")