X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fsetup.c;h=f0ad438a376009f5248b489c1c49a28966bb19d0;hb=1e656d277fe91b7c4f5d73eab4a0dd0b7a173145;hp=068618d6914f9cc1d6f9ce4a94dd613ae683c91d;hpb=497328c858ab7429611437bd44c0fb77fae0fef7;p=citadel.git diff --git a/citadel/setup.c b/citadel/setup.c index 068618d69..f0ad438a3 100644 --- a/citadel/setup.c +++ b/citadel/setup.c @@ -1,7 +1,7 @@ /* * $Id$ * - * Citadel/UX setup utility + * Citadel setup utility * */ @@ -20,265 +20,105 @@ #include #include #include - +#include +#include #include "citadel.h" #include "axdefs.h" #include "sysdep.h" #include "config.h" -#include "tools.h" - -#ifndef DISABLE_CURSES -#if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H) -#ifdef HAVE_NCURSES_H -#include -#else -#include -#endif -#endif -#endif +#include "citadel_dirs.h" -#define MAXSETUP 3 /* How many setup questions to ask */ +#define MAXSETUP 5 /* How many setup questions to ask */ #define UI_TEXT 0 /* Default setup type -- text only */ -#define UI_DIALOG 1 /* Use the 'dialog' program (REMOVED) */ -#define UI_CURSES 2 /* Use curses */ +#define UI_DIALOG 2 /* Use the 'dialog' program */ #define UI_SILENT 3 /* Silent running, for use in scripts */ #define SERVICE_NAME "citadel" #define PROTO_NAME "tcp" +#define NSSCONF "/etc/nsswitch.conf" int setup_type; -char setup_directory[SIZ]; -char init_entry[SIZ]; +char setup_directory[PATH_MAX]; +int using_web_installer = 0; +int enable_home = 1; char *setup_titles[] = { "Citadel Home Directory", "System Administrator", "Citadel User ID", + "Server IP address", "Server port number", + "Authentication mode" }; -char *setup_text[] = -{ -"Enter the full pathname of the directory in which the Citadel installation\n" -"you are creating or updating resides. If you specify a directory other\n" -"than the default, you will need to specify the -h flag to the server when\n" -"you start it up.\n", - -"Enter the name of the system administrator (which is probably you).\n" -"When an account is created with this name, it will automatically be\n" -"assigned the highest access level.\n", - -"Citadel needs to run under its own user ID. This would typically be\n" -"called \"citadel\", but if you are running Citadel as a public BBS, you\n" -"might also call it \"bbs\" or \"guest\". The server will run under this\n" -"user ID. Please specify that user ID here. You may specify either a\n" -"user name or a numeric UID.\n", - -"Specify the TCP port number on which your server will run. Normally, this\n" -"will be port 504, which is the official port assigned by the IANA for\n" -"Citadel servers. You'll only need to specify a different port number if\n" -"you run multiple instances of Citadel on the same computer and there's\n" -"something else already using port 504.\n", - -"Setup has detected that you currently have data files from a Citadel/UX\n" -"version 3.2x installation. The program 'conv_32_40' can upgrade your\n" -"files to version 4.0x format.\n" -" Setup will now exit. Please either run 'conv_32_40' or delete your data\n" -"files, and run setup again.\n" - -}; - struct config config; -int direction; - -/* - * Set an entry in inittab to the desired state - */ -void set_init_entry(char *which_entry, char *new_state) { - char *inittab = NULL; - FILE *fp; - char buf[SIZ]; - char entry[SIZ]; - char levels[SIZ]; - char state[SIZ]; - char prog[SIZ]; - - inittab = strdup(""); - if (inittab == NULL) return; - - fp = fopen("/etc/inittab", "r"); - if (fp == NULL) return; - - while(fgets(buf, sizeof buf, fp) != NULL) { - - if (num_tokens(buf, ':') == 4) { - extract_token(entry, buf, 0, ':'); - extract_token(levels, buf, 1, ':'); - extract_token(state, buf, 2, ':'); - extract_token(prog, buf, 3, ':'); /* includes 0x0a LF */ - - if (!strcmp(entry, which_entry)) { - strcpy(state, new_state); - sprintf(buf, "%s:%s:%s:%s", - entry, levels, state, prog); - } - } - - inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2); - if (inittab == NULL) { - fclose(fp); - return; - } - - strcat(inittab, buf); - } - fclose(fp); - fp = fopen("/etc/inittab", "w"); - if (fp != NULL) { - fwrite(inittab, strlen(inittab), 1, fp); - fclose(fp); - kill(1, SIGHUP); /* Tell init to re-read /etc/inittab */ - } - free(inittab); -} + /* calculate all our path on a central place */ + /* where to keep our config */ + +char *setup_text[] = { +#ifndef HAVE_RUN_DIR +"Enter the full pathname of the directory in which the Citadel\n" +"installation you are creating or updating resides. If you\n" +"specify a directory other than the default, you will need to\n" +"specify the -h flag to the server when you start it up.\n", +#else +"Enter the subdirectory name for an alternate installation of " +"Citadel. To do a default installation just leave it blank." +"If you specify a directory other than the default, you will need to\n" +"specify the -h flag to the server when you start it up.\n" +"note that it may not have a leading /", +#endif +"Enter the name of the system administrator (which is probably\n" +"you). When an account is created with this name, it will\n" +"automatically be given administrator-level access.\n", + +"Citadel needs to run under its own user ID. This would\n" +"typically be called \"citadel\", but if you are running Citadel\n" +"as a public BBS, you might also call it \"bbs\" or \"guest\".\n" +"The server will run under this user ID. Please specify that\n" +"user ID here. You may specify either a user name or a numeric\n" +"UID.\n", + +"Specify the IP address on which your server will run. If you\n" +"leave this blank, or if you specify 0.0.0.0, Citadel will listen\n" +"on all addresses. You can usually skip this unless you are\n" +"running multiple instances of Citadel on the same computer.\n", + +"Specify the TCP port number on which your server will run.\n" +"Normally, this will be port 504, which is the official port\n" +"assigned by the IANA for Citadel servers. You will only need\n" +"to specify a different port number if you run multiple instances\n" +"of Citadel on the same computer and there is something else\n" +"already using port 504.\n", + +"Normally, a Citadel system uses a \"black box\" authentication mode.\n" +"This means that users do not have accounts or home directories on\n" +"the underlying host system -- Citadel manages its own user database.\n" +"However, if you wish to override this behavior, you can enable the\n" +"host based authentication mode which is traditional for Unix systems.\n" +"WARNING: do *not* change this setting once your system is installed.\n" +"\n" +"(Answer \"no\" unless you completely understand this option)\n" +"Do you want to enable host based authentication mode?\n" -/* - * Shut down the Citadel service if necessary, during setup. - */ -void shutdown_service(void) { - FILE *infp; - char buf[SIZ]; - char looking_for[SIZ]; - int have_entry = 0; - char entry[SIZ]; - char prog[SIZ]; - - strcpy(init_entry, ""); - - /* Determine the fully qualified path name of citserver */ - snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR); - - /* Pound through /etc/inittab line by line. Set have_entry to 1 if - * an entry is found which we believe starts citserver. - */ - infp = fopen("/etc/inittab", "r"); - if (infp == NULL) { - return; - } else { - while (fgets(buf, sizeof buf, infp) != NULL) { - buf[strlen(buf) - 1] = 0; - extract_token(entry, buf, 0, ':'); - extract_token(prog, buf, 3, ':'); - if (!strncasecmp(prog, looking_for, - strlen(looking_for))) { - ++have_entry; - strcpy(init_entry, entry); - } - } - fclose(infp); - } - - /* Bail out if there's nothing to do. */ - if (!have_entry) return; - - set_init_entry(init_entry, "off"); -} - - -/* - * Start the Citadel service. - */ -void start_the_service(void) { - if (strlen(init_entry) > 0) { - set_init_entry(init_entry, "respawn"); - } -} +}; +struct config config; +int direction; void cleanup(int exitcode) { -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - if (setup_type == UI_CURSES) { - clear(); - refresh(); - endwin(); - } -#endif - exit(exitcode); } -/* Gets a line from the terminal */ -/* Where on the screen to start */ -/* Pointer to string buffer */ -/* Maximum length - if negative, no-show */ -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) -void getlin(int yp, int xp, char *string, int lim) { - int a, b; - char flag; - - flag = 0; - if (lim < 0) { - lim = (0 - lim); - flag = 1; - } - move(yp, xp); - standout(); - for (a = 0; a < lim; ++a) - addch('-'); - refresh(); - move(yp, xp); - for (a = 0; a < lim; ++a) - addch(' '); - move(yp, xp); - printw("%s", string); - GLA:move(yp, xp + strlen(string)); - refresh(); - a = getch(); - if (a == 127) - a = 8; - a = (a & 127); - if (a == 10) - a = 13; - if ((a == 8) && (strlen(string) == 0)) - goto GLA; - if ((a != 13) && (a != 8) && (strlen(string) == lim)) - goto GLA; - if ((a == 8) && (string[0] != 0)) { - string[strlen(string) - 1] = 0; - move(yp, xp + strlen(string)); - addch(' '); - goto GLA; - } - if ((a == 13) || (a == 10)) { - standend(); - move(yp, xp); - for (a = 0; a < lim; ++a) - addch(' '); - mvprintw(yp, xp, "%s", string); - refresh(); - return; - } - b = strlen(string); - string[b] = a; - string[b + 1] = 0; - if (flag == 0) - addch(a); - if (flag == 1) - addch('*'); - goto GLA; -} -#endif - - void title(char *text) { @@ -288,63 +128,45 @@ void title(char *text) } -void hit_any_key(void) -{ - char junk[5]; - -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - if (setup_type == UI_CURSES) { - mvprintw(20, 0, "Press any key to continue... "); - refresh(); - getch(); - return; - } -#endif - if (setup_type == UI_TEXT) { - printf("Press return to continue..."); - fgets(junk, 5, stdin); - } -} -int yesno(char *question) +int yesno(char *question, int default_value) { + int i = 0; int answer = 0; - char buf[4096]; + char buf[SIZ]; switch (setup_type) { case UI_TEXT: do { - printf("%s\nYes/No --> ", question); - fgets(buf, 4096, stdin); + printf("%s\nYes/No [%s] --> ", + question, + ( default_value ? "Yes" : "No" ) + ); + fgets(buf, sizeof buf, stdin); answer = tolower(buf[0]); - if (answer == 'y') + if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) + answer = default_value; + else if (answer == 'y') answer = 1; else if (answer == 'n') answer = 0; } while ((answer < 0) || (answer > 1)); break; -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - case UI_CURSES: - do { - clear(); - standout(); - mvprintw(1, 20, "Question"); - standend(); - mvprintw(10, 0, "%-80s", question); - mvprintw(20, 0, "%80s", ""); - mvprintw(20, 0, "Yes/No -> "); - refresh(); - answer = getch(); - answer = tolower(answer); - if (answer == 'y') - answer = 1; - else if (answer == 'n') - answer = 0; - } while ((answer < 0) || (answer > 1)); + case UI_DIALOG: + sprintf(buf, "exec %s %s --yesno '%s' 15 75", + getenv("CTDL_DIALOG"), + ( default_value ? "" : "--defaultno" ), + question); + i = system(buf); + if (i == 0) { + answer = 1; + } + else { + answer = 0; + } break; -#endif } return (answer); @@ -353,29 +175,23 @@ int yesno(char *question) void important_message(char *title, char *msgtext) { + char buf[SIZ]; switch (setup_type) { case UI_TEXT: 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"); printf(" %s \n\n%s\n\n", title, msgtext); - hit_any_key(); + printf("Press return to continue..."); + fgets(buf, sizeof buf, stdin); break; -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - case UI_CURSES: - clear(); - move(1, 20); - standout(); - printw(" Important Message "); - standend(); - move(3, 0); - printw("%s", msgtext); - refresh(); - hit_any_key(); + case UI_DIALOG: + sprintf(buf, "exec %s --msgbox '%s' 19 72", + getenv("CTDL_DIALOG"), + msgtext); + system(buf); break; -#endif - } } @@ -391,8 +207,10 @@ void display_error(char *error_message) void progress(char *text, long int curr, long int cmax) { - static long dots_printed; - long a; + static long dots_printed = 0L; + long a = 0; + static FILE *fp = NULL; + char buf[SIZ]; switch (setup_type) { @@ -418,35 +236,32 @@ void progress(char *text, long int curr, long int cmax) } break; -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - case UI_CURSES: + case UI_DIALOG: if (curr == 0) { - clear(); - move(5, 20); - printw("%s\n", text); - move(10, 1); - printf(".........................."); - printf(".........................."); - printf("..........................\r"); - refresh(); - dots_printed = 0; - } else if (curr == cmax) { - clear(); - refresh(); - } else { + sprintf(buf, "exec %s --gauge '%s' 7 72 0", + getenv("CTDL_DIALOG"), + text); + fp = popen(buf, "w"); + if (fp != NULL) { + fprintf(fp, "0\n"); + fflush(fp); + } + } + else if (curr == cmax) { + if (fp != NULL) { + fprintf(fp, "100\n"); + pclose(fp); + fp = NULL; + } + } + else { a = (curr * 100) / cmax; - a = a * 78; - a = a / 100; - move(10, 1); - dots_printed = 0; - while (dots_printed < a) { - printw("*"); - ++dots_printed; + if (fp != NULL) { + fprintf(fp, "%ld\n", a); + fflush(fp); } - refresh(); } break; -#endif } } @@ -461,122 +276,405 @@ void check_services_entry(void) { int i; FILE *sfp; + char errmsg[256]; if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) { - for (i=0; i<3; ++i) { - progress("Adding service entry...", i, 3); + for (i=0; i<=2; ++i) { + progress("Adding service entry...", i, 2); if (i == 0) { sfp = fopen("/etc/services", "a"); if (sfp == NULL) { - display_error(strerror(errno)); + sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno)); + display_error(errmsg); } else { - fprintf(sfp, "%s 504/tcp\n", - SERVICE_NAME); + fprintf(sfp, "%s 504/tcp\n", SERVICE_NAME); fclose(sfp); } } - sleep(1); } } } + + /* - * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab + * delete_inittab_entry() -- Remove obsolete /etc/inittab entry for Citadel * */ -void check_inittab_entry(void) +void delete_inittab_entry(void) { FILE *infp; - char buf[SIZ]; - char looking_for[SIZ]; - char question[128]; - char entryname[5]; + FILE *outfp; + char looking_for[256]; + char buf[1024]; + char outfilename[32]; + int changes_made = 0; /* Determine the fully qualified path name of citserver */ - snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR); + snprintf(looking_for, + sizeof looking_for, + "%s/citserver", + ctdl_sbin_dir + ); + + /* Now tweak /etc/inittab */ + infp = fopen("/etc/inittab", "r"); + if (infp == NULL) { + + /* If /etc/inittab does not exist, return quietly. + * Not all host platforms have it. + */ + if (errno == ENOENT) { + return; + } - /* If there's already an entry, then we have nothing left to do. */ - if (strlen(init_entry) > 0) { + /* Other errors might mean something really did go wrong. + */ + sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno)); + display_error(buf); return; } - /* Otherwise, prompt the user to create an entry. */ - snprintf(question, sizeof question, - "There is no '%s' entry in /etc/inittab.\n" - "Would you like to add one?", - looking_for); - if (yesno(question) == 0) + strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX"); + outfp = fdopen(mkstemp(outfilename), "w+"); + if (outfp == NULL) { + sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno)); + display_error(buf); + fclose(infp); return; + } - /* Generate a unique entry name for /etc/inittab */ - snprintf(entryname, sizeof entryname, "c0"); - do { - ++entryname[1]; - if (entryname[1] > '9') { - entryname[1] = 0; - ++entryname[0]; - if (entryname[0] > 'z') { - display_error( - "Can't generate a unique entry name"); - return; - } + while (fgets(buf, sizeof buf, infp) != NULL) { + if (strstr(buf, looking_for) != NULL) { + fwrite("#", 1, 1, outfp); + ++changes_made; } + fwrite(buf, strlen(buf), 1, outfp); + } + + fclose(infp); + fclose(outfp); + + if (changes_made) { + sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename); + system(buf); + system("/sbin/init q 2>/dev/null"); + } + else { + unlink(outfilename); + } +} + + +/* + * install_init_scripts() -- Try to configure to start Citadel at boot + * + */ +void install_init_scripts(void) +{ + struct stat etcinitd; + FILE *fp; + char *initfile = "/etc/init.d/citadel"; + char command[SIZ]; + + if ((stat("/etc/init.d/", &etcinitd) == -1) && + (errno == ENOENT)) + { + if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) && + (errno == ENOENT)) + initfile = CTDLDIR"/citadel.init"; + else + initfile = "/etc/rc.d/init.d/citadel"; + } + + fp = fopen(initfile, "r"); + if (fp != NULL) { + if (yesno("Citadel already appears to be configured to start at boot.\n" + "Would you like to keep your boot configuration as is?\n", 1) == 1) { + return; + } + fclose(fp); + + } + + if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) { + return; + } + + fp = fopen(initfile, "w"); + if (fp == NULL) { + display_error("Cannot create /etc/init.d/citadel"); + return; + } + + fprintf(fp, "#!/bin/sh\n" + "#\n" + "# Init file for Citadel\n" + "#\n" + "# chkconfig: - 79 30\n" + "# description: Citadel service\n" + "# processname: citserver\n" + "# pidfile: %s/citadel.pid\n" + "\n" + "CITADEL_DIR=%s\n" + , + setup_directory, + setup_directory + ); + fprintf(fp, "\n" + "test -d /var/run || exit 0\n" + "\n" + "case \"$1\" in\n" + "\n" + "start) echo -n \"Starting Citadel... \"\n" + " if $CITADEL_DIR/citserver -d -h$CITADEL_DIR\n" + " then\n" + " echo \"ok\"\n" + " else\n" + " echo \"failed\"\n" + " fi\n"); + fprintf(fp, " ;;\n" + "stop) echo -n \"Stopping Citadel... \"\n" + " if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n" + " echo \"ok\"\n" + " else\n" + " echo \"failed\"\n" + " fi\n" + " rm -f %s/citadel.pid 2>/dev/null\n" + , + setup_directory + ); + fprintf(fp, " ;;\n" + "restart) $0 stop\n" + " $0 start\n" + " ;;\n" + "*) echo \"Usage: $0 {start|stop|restart}\"\n" + " exit 1\n" + " ;;\n" + "esac\n" + ); + + fclose(fp); + chmod(initfile, 0755); + + /* Set up the run levels. */ + system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null"); + snprintf(command, sizeof(command), "for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/S79citadel ; done 2>/dev/null", initfile); + system(command); + snprintf(command, sizeof(command),"for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/K30citadel ; done 2>/dev/null", initfile); + system(command); + +} + + + + + + +/* + * On systems which use xinetd, see if we can offer to install Citadel as + * the default telnet target. + */ +void check_xinetd_entry(void) { + char *filename = "/etc/xinetd.d/telnet"; + FILE *fp; + char buf[SIZ]; + int already_citadel = 0; + + fp = fopen(filename, "r+"); + if (fp == NULL) return; /* Not there. Oh well... */ + + while (fgets(buf, sizeof buf, fp) != NULL) { + if (strstr(buf, setup_directory) != NULL) already_citadel = 1; + } + fclose(fp); + if (already_citadel) return; /* Already set up this way. */ + + /* Otherwise, prompt the user to create an entry. */ + if (getenv("CREATE_XINETD_ENTRY") != NULL) { + if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) { + return; + } + } + else { snprintf(buf, sizeof buf, - "grep %s: /etc/inittab >/dev/null 2>&1", entryname); - } while (system(buf) == 0); + "Setup can configure the \"xinetd\" service to automatically\n" + "connect incoming telnet sessions to Citadel, bypassing the\n" + "host system login: prompt. Would you like to do this?\n" + ); + if (yesno(buf, 1) == 0) { + return; + } + } - /* Now write it out to /etc/inittab */ - infp = fopen("/etc/inittab", "a"); - if (infp == NULL) { - display_error(strerror(errno)); - } else { - fprintf(infp, "# Start the Citadel/UX server...\n"); - fprintf(infp, "%s:2345:respawn:%s -h%s\n", - entryname, looking_for, setup_directory); - fclose(infp); - strcpy(init_entry, entryname); + fp = fopen(filename, "w"); + fprintf(fp, + "# description: telnet service for Citadel users\n" + "service telnet\n" + "{\n" + " disable = no\n" + " flags = REUSE\n" + " socket_type = stream\n" + " wait = no\n" + " user = root\n" + " server = /usr/sbin/in.telnetd\n" + " server_args = -h -L %s/citadel\n" + " log_on_failure += USERID\n" + "}\n", + ctdl_bin_dir); + fclose(fp); + + /* Now try to restart the service */ + system("/etc/init.d/xinetd restart >/dev/null 2>&1"); +} + + + +/* + * Offer to disable other MTA's + */ +void disable_other_mta(char *mta) { + char buf[SIZ]; + FILE *fp; + int lines = 0; + + sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; " + "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null", + mta, mta); + fp = popen(buf, "r"); + if (fp == NULL) return; + + while (fgets(buf, sizeof buf, fp) != NULL) { + ++lines; + } + fclose(fp); + if (lines == 0) return; /* Nothing to do. */ + + + /* Offer to replace other MTA with the vastly superior Citadel :) */ + + if (getenv("ACT_AS_MTA")) { + if (strcasecmp(getenv("ACT_AS_MTA"), "yes")) { + return; + } + } + else { + snprintf(buf, sizeof buf, + "You appear to have the \"%s\" email program\n" + "running on your system. If you want Citadel mail\n" + "connected with %s, you will have to manually integrate\n" + "them. It is preferable to disable %s, and use Citadel's\n" + "SMTP, POP3, and IMAP services.\n\n" + "May we disable %s so that Citadel has access to ports\n" + "25, 110, and 143?\n", + mta, mta, mta, mta + ); + if (yesno(buf, 1) == 0) { + return; + } } + + sprintf(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); + system(buf); + sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta); + system(buf); } -void set_str_val(int msgpos, char str[]) -{ + +/* + * Check to see if our server really works. Returns 0 on success. + */ +int test_server(void) { + char cmd[256]; + char cookie[256]; + FILE *fp; char buf[4096]; - char tempfile[PATH_MAX]; + int found_it = 0; + + /* Generate a silly little cookie. We're going to write it out + * to the server and try to get it back. The cookie does not + * have to be secret ... just unique. + */ + sprintf(cookie, "--test--%d--", getpid()); + + sprintf(cmd, "%s/sendcommand %s%s ECHO %s 2>&1", + ctdl_sbin_dir, + (enable_home)?"-h":"", + (enable_home)?ctdl_run_dir:"", + cookie); + + fp = popen(cmd, "r"); + if (fp == NULL) return(errno); + + while (fgets(buf, sizeof buf, fp) != NULL) { + if ( (buf[0]=='2') + && (strstr(buf, cookie) != NULL) ) { + ++found_it; + } + } + pclose(fp); + + if (found_it) { + return(0); + } + return(-1); +} + +void strprompt(char *prompt_title, char *prompt_text, char *str) +{ + char buf[SIZ]; char setupmsg[SIZ]; + char dialog_result[PATH_MAX]; + FILE *fp = NULL; - strcpy(tempfile, tmpnam(NULL)); strcpy(setupmsg, ""); switch (setup_type) { case UI_TEXT: - title(setup_titles[msgpos]); - printf("\n%s\n", setup_text[msgpos]); + title(prompt_title); + printf("\n%s\n", prompt_text); printf("This is currently set to:\n%s\n", str); printf("Enter new value or press return to leave unchanged:\n"); - fgets(buf, 4096, stdin); + fgets(buf, sizeof buf, stdin); buf[strlen(buf) - 1] = 0; - if (strlen(buf) != 0) + if (!IsEmptyStr(buf)) strcpy(str, buf); break; -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - case UI_CURSES: - clear(); - move(1, ((80 - strlen(setup_titles[msgpos])) / 2)); - standout(); - printw("%s", setup_titles[msgpos]); - standend(); - move(3, 0); - printw("%s", setup_text[msgpos]); - refresh(); - getlin(20, 0, str, 80); + + case UI_DIALOG: + CtdlMakeTempFileName(dialog_result, sizeof dialog_result); + sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s", + getenv("CTDL_DIALOG"), + prompt_text, + str, + dialog_result); + system(buf); + fp = fopen(dialog_result, "r"); + if (fp != NULL) { + fgets(str, sizeof buf, fp); + if (str[strlen(str)-1] == 10) { + str[strlen(str)-1] = 0; + } + fclose(fp); + unlink(dialog_result); + } break; -#endif + } } +void set_bool_val(int msgpos, int *ip) { + title(setup_titles[msgpos]); + *ip = yesno(setup_text[msgpos], *ip); +} + +void set_str_val(int msgpos, char *str) { + strprompt(setup_titles[msgpos], setup_text[msgpos], str); +} + void set_int_val(int msgpos, int *ip) { char buf[16]; @@ -608,38 +706,96 @@ void edit_value(int curr) { int i; struct passwd *pw; - char bbsuidname[SIZ]; + char ctdluidname[256]; switch (curr) { case 1: - set_str_val(curr, config.c_sysadm); + if (setup_type == UI_SILENT) + { + if (getenv("SYSADMIN_NAME")) { + strcpy(config.c_sysadm, getenv("SYSADMIN_NAME")); + } + } + else { + set_str_val(curr, config.c_sysadm); + } break; case 2: - i = config.c_bbsuid; - pw = getpwuid(i); - if (pw == NULL) { - set_int_val(curr, &i); - config.c_bbsuid = i; + if (setup_type == UI_SILENT) + { + if (getenv("CITADEL_UID")) { + config.c_ctdluid = atoi(getenv("CITADEL_UID")); + } } - else { - strcpy(bbsuidname, pw->pw_name); - set_str_val(curr, bbsuidname); - pw = getpwnam(bbsuidname); - if (pw != NULL) { - config.c_bbsuid = pw->pw_uid; + else + { +#ifdef __CYGWIN__ + config.c_ctdluid = 0; /* XXX Windows hack, prob. insecure */ +#else + i = config.c_ctdluid; + pw = getpwuid(i); + if (pw == NULL) { + set_int_val(curr, &i); + config.c_ctdluid = i; } - else if (atoi(bbsuidname) > 0) { - config.c_bbsuid = atoi(bbsuidname); + else { + strcpy(ctdluidname, pw->pw_name); + set_str_val(curr, ctdluidname); + pw = getpwnam(ctdluidname); + if (pw != NULL) { + config.c_ctdluid = pw->pw_uid; + } + else if (atoi(ctdluidname) > 0) { + config.c_ctdluid = atoi(ctdluidname); + } } +#endif } break; case 3: - set_int_val(curr, &config.c_port_number); + if (setup_type == UI_SILENT) + { + if (getenv("IP_ADDR")) { + strcpy(config.c_ip_addr, getenv("IP_ADDR")); + } + } + else { + set_str_val(curr, config.c_ip_addr); + } + break; + + case 4: + if (setup_type == UI_SILENT) + { + if (getenv("CITADEL_PORT")) { + config.c_port_number = atoi(getenv("CITADEL_PORT")); + } + } + else + { + set_int_val(curr, &config.c_port_number); + } break; + case 5: + if (setup_type == UI_SILENT) + { + if (getenv("ENABLE_UNIX_AUTH")) { + if (!strcasecmp(getenv("ENABLE_UNIX_AUTH"), "yes")) { + config.c_auth_mode = 1; + } + else { + config.c_auth_mode = 0; + } + } + } + else { + set_bool_val(curr, &config.c_auth_mode); + } + break; } } @@ -652,7 +808,7 @@ void write_config_to_disk(void) FILE *fp; int fd; - if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) { + if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) { display_error("setup: cannot open citadel.config"); cleanup(1); } @@ -674,9 +830,11 @@ void write_config_to_disk(void) int discover_ui(void) { -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - return UI_CURSES; -#endif + /* Use "dialog" if we have it */ + if (getenv("CTDL_DIALOG") != NULL) { + return UI_DIALOG; + } + return UI_TEXT; } @@ -684,10 +842,100 @@ int discover_ui(void) +/* + * Strip "db" entries out of /etc/nsswitch.conf + */ +void fixnss(void) { + FILE *fp_read; + int fd_write; + char buf[256]; + char buf_nc[256]; + char question[512]; + int i; + int changed = 0; + int file_changed = 0; + char new_filename[64]; + + fp_read = fopen(NSSCONF, "r"); + if (fp_read == NULL) { + return; + } + + strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX"); + fd_write = mkstemp(new_filename); + if (fd_write < 0) { + fclose(fp_read); + return; + } + + while (fgets(buf, sizeof buf, fp_read) != NULL) { + changed = 0; + strcpy(buf_nc, buf); + for (i=0; i 0) { + if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) { + changed = 1; + file_changed = 1; + strcpy(&buf_nc[i], &buf_nc[i+2]); + strcpy(&buf[i], &buf[i+2]); + if (buf[i]==32) { + strcpy(&buf_nc[i], &buf_nc[i+1]); + strcpy(&buf[i], &buf[i+1]); + } + } + } + } + } + if (write(fd_write, buf, strlen(buf)) != strlen(buf)) { + fclose(fp_read); + close(fd_write); + unlink(new_filename); + return; + } + } + + fclose(fp_read); + + if (!file_changed) { + unlink(new_filename); + return; + } + + snprintf(question, sizeof question, + "\n" + "/etc/nsswitch.conf is configured to use the 'db' module for\n" + "one or more services. This is not necessary on most systems,\n" + "and it is known to crash the Citadel server when delivering\n" + "mail to the Internet.\n" + "\n" + "Do you want this module to be automatically disabled?\n" + "\n" + ); + + if (yesno(question, 1)) { + sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF); + system(buf); + } + unlink(new_filename); +} + + + + + + + + int main(int argc, char *argv[]) { int a; - int curr; + int curr; char aaa[128]; FILE *fp; int old_setup_level = 0; @@ -696,10 +944,21 @@ int main(int argc, char *argv[]) struct passwd *pw; struct hostent *he; gid_t gid; + int relh=0; + int home=0; + char relhome[PATH_MAX]=""; + char ctdldir[PATH_MAX]=CTDLDIR; + + CtdlInitBase64Table(); /* set an invalid setup type */ setup_type = (-1); + /* Check to see if we're running the web installer */ + if (getenv("CITADEL_INSTALLER") != NULL) { + using_web_installer = 1; + } + /* parse command line args */ for (a = 0; a < argc; ++a) { if (!strncmp(argv[a], "-u", 2)) { @@ -722,36 +981,59 @@ int main(int argc, char *argv[]) if (setup_type < 0) { setup_type = discover_ui(); } -#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES) - if (setup_type == UI_CURSES) { - initscr(); - raw(); - noecho(); - } -#endif - if (info_only == 1) { - important_message("Citadel/UX Setup", CITADEL); + important_message("Citadel Setup", CITADEL); cleanup(0); } /* Get started in a valid setup directory. */ - strcpy(setup_directory, BBSDIR); - set_str_val(0, setup_directory); - if (chdir(setup_directory) != 0) { - important_message("Citadel/UX Setup", - "The directory you specified does not exist."); - cleanup(errno); + strcpy(setup_directory, ctdl_run_dir); + if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) { + strcpy(setup_directory, getenv("CITADEL")); + } + else { + set_str_val(0, setup_directory); + } + + home=(setup_directory[1]!='\0'); + relh=home&(setup_directory[1]!='/'); + if (!relh) { + safestrncpy(ctdl_home_directory, setup_directory, sizeof ctdl_home_directory); + } + else { + safestrncpy(relhome, ctdl_home_directory, sizeof relhome); + } + + calc_dirs_n_files(relh, home, relhome, ctdldir, 0); + + enable_home=(relh|home); + + if (home) { + if (chdir(setup_directory) == 0) { + strcpy(file_citadel_config, "./citadel.config"); + } + else { + important_message("Citadel Setup", + "The directory you specified does not exist."); + cleanup(errno); + } } /* Determine our host name, in case we need to use it as a default */ uname(&my_utsname); - /* See if we need to shut down the Citadel service. */ - for (a=0; a<=5; ++a) { - progress("Shutting down the Citadel service...", a, 5); - if (a == 0) shutdown_service(); - sleep(1); + /* Try to stop Citadel if we can */ + if (!access("/etc/init.d/citadel", X_OK)) { + system("/etc/init.d/citadel stop"); + } + + /* Make sure Citadel is not running. */ + if (test_server() == 0) { + important_message("Citadel Setup", + "The Citadel service is still running.\n" + "Please stop the service manually and run " + "setup again."); + cleanup(1); } /* Now begin. */ @@ -759,7 +1041,7 @@ int main(int argc, char *argv[]) case UI_TEXT: printf("\n\n\n" - " *** Citadel/UX setup program ***\n\n"); + " *** Citadel setup program ***\n\n"); break; } @@ -774,8 +1056,7 @@ int main(int argc, char *argv[]) * to be when we rewrite it, because we replace the old file with a * completely new copy. */ - - if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND, + if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) == -1) { display_error("setup: cannot append citadel.config"); cleanup(errno); @@ -790,7 +1071,7 @@ int main(int argc, char *argv[]) fclose(fp); /* now we re-open it, and read the old or blank configuration */ - fp = fopen("citadel.config", "rb"); + fp = fopen(file_citadel_config, "rb"); if (fp == NULL) { display_error("setup: cannot open citadel.config"); cleanup(errno); @@ -799,11 +1080,11 @@ int main(int argc, char *argv[]) fclose(fp); /* set some sample/default values in place of blanks... */ - if (strlen(config.c_nodename) == 0) + if (IsEmptyStr(config.c_nodename)) safestrncpy(config.c_nodename, my_utsname.nodename, sizeof config.c_nodename); strtok(config.c_nodename, "."); - if (strlen(config.c_fqdn) == 0) { + if (IsEmptyStr(config.c_fqdn) ) { if ((he = gethostbyname(my_utsname.nodename)) != NULL) safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn); @@ -811,49 +1092,41 @@ int main(int argc, char *argv[]) safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn); } - if (strlen(config.c_humannode) == 0) + if (IsEmptyStr(config.c_humannode)) strcpy(config.c_humannode, "My System"); - if (strlen(config.c_phonenum) == 0) + if (IsEmptyStr(config.c_phonenum)) strcpy(config.c_phonenum, "US 800 555 1212"); if (config.c_initax == 0) { config.c_initax = 4; } - if (strlen(config.c_moreprompt) == 0) + if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, ""); - if (strlen(config.c_twitroom) == 0) + if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan"); - if (strlen(config.c_bucket_dir) == 0) - strcpy(config.c_bucket_dir, "bitbucket"); - if (strlen(config.c_net_password) == 0) - strcpy(config.c_net_password, "netpassword"); - if (strlen(config.c_baseroom) == 0) - strcpy(config.c_baseroom, "Lobby"); - if (strlen(config.c_aideroom) == 0) + if (IsEmptyStr(config.c_baseroom)) + strcpy(config.c_baseroom, BASEROOM); + if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide"); if (config.c_port_number == 0) { config.c_port_number = 504; } - if (config.c_ipgm_secret == 0) { - srand(getpid()); - config.c_ipgm_secret = rand(); - } if (config.c_sleeping == 0) { config.c_sleeping = 900; } - if (config.c_bbsuid == 0) { + if (config.c_ctdluid == 0) { pw = getpwnam("citadel"); if (pw != NULL) - config.c_bbsuid = pw->pw_uid; + config.c_ctdluid = pw->pw_uid; } - if (config.c_bbsuid == 0) { + if (config.c_ctdluid == 0) { pw = getpwnam("bbs"); if (pw != NULL) - config.c_bbsuid = pw->pw_uid; + config.c_ctdluid = pw->pw_uid; } - if (config.c_bbsuid == 0) { + if (config.c_ctdluid == 0) { pw = getpwnam("guest"); if (pw != NULL) - config.c_bbsuid = pw->pw_uid; + config.c_ctdluid = pw->pw_uid; } if (config.c_createax == 0) { config.c_createax = 3; @@ -866,10 +1139,11 @@ int main(int argc, char *argv[]) } /* We need a system default message expiry policy, because this is * the top level and there's no 'higher' policy to fall back on. + * By default, do not expire messages at all. */ if (config.c_ep.expire_mode == 0) { - config.c_ep.expire_mode = EXPIRE_NUMMSGS; - config.c_ep.expire_value = 150; + config.c_ep.expire_mode = EXPIRE_MANUAL; + config.c_ep.expire_value = 0; } /* @@ -878,49 +1152,35 @@ int main(int argc, char *argv[]) if (config.c_smtp_port == 0) config.c_smtp_port = 25; if (config.c_pop3_port == 0) config.c_pop3_port = 110; if (config.c_imap_port == 0) config.c_imap_port = 143; + if (config.c_msa_port == 0) config.c_msa_port = 587; + if (config.c_smtps_port == 0) config.c_smtps_port = 465; + if (config.c_pop3s_port == 0) config.c_pop3s_port = 995; + if (config.c_imaps_port == 0) config.c_imaps_port = 993; + if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1; + if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020; /* Go through a series of dialogs prompting for config info */ - if (setup_type != UI_SILENT) { - for (curr = 1; curr <= MAXSETUP; ++curr) { - edit_value(curr); - } + for (curr = 1; curr <= MAXSETUP; ++curr) { + edit_value(curr); } - /* - if (setuid(config.c_bbsuid) != 0) { - important_message("Citadel/UX Setup", - "Failed to change the user ID to your Citadel user."); - cleanup(errno); - } - */ - /***** begin version update section ***** */ /* take care of any updating that is necessary */ old_setup_level = config.c_setup_level; - if (old_setup_level == 0) + if (old_setup_level == 0) { goto NEW_INST; + } - if (old_setup_level < 323) { - important_message("Citadel/UX Setup", - "This Citadel/UX installation is too old " + if (old_setup_level < 555) { + important_message("Citadel Setup", + "This Citadel installation is too old " "to be upgraded."); cleanup(1); } write_config_to_disk(); - if ((config.c_setup_level / 10) == 32) { - important_msgnum(31); - cleanup(0); - } - if (config.c_setup_level < 400) { - config.c_setup_level = 400; - } - /* end of 3.23 -> 4.00 update section */ - - /* end of 4.00 -> 4.02 update section */ - old_setup_level = config.c_setup_level; /* end of version update section */ @@ -932,52 +1192,121 @@ NEW_INST: write_config_to_disk(); - mkdir("info", 0700); - mkdir("bio", 0700); - mkdir("userpics", 0700); - mkdir("messages", 0700); - mkdir("help", 0700); - mkdir("images", 0700); - mkdir("netconfigs", 0700); - mkdir(config.c_bucket_dir, 0700); + mkdir(ctdl_info_dir, 0700); + chmod(ctdl_info_dir, 0700); + chown(ctdl_info_dir, config.c_ctdluid, -1); + + mkdir(ctdl_bio_dir, 0700); + chmod(ctdl_bio_dir, 0700); + chown(ctdl_bio_dir, config.c_ctdluid, -1); - /* Delete a bunch of old files from Citadel v4; don't need anymore */ - system("rm -fr ./chatpipes ./expressmsgs ./sessions 2>/dev/null"); + mkdir(ctdl_usrpic_dir, 0700); + chmod(ctdl_usrpic_dir, 0700); + chown(ctdl_usrpic_dir, config.c_ctdluid, -1); - /* Delete the old citadel.log file; this facility has been removed */ + mkdir(ctdl_message_dir, 0700); + chmod(ctdl_message_dir, 0700); + chown(ctdl_message_dir, config.c_ctdluid, -1); + + mkdir(ctdl_hlp_dir, 0700); + chmod(ctdl_hlp_dir, 0700); + chown(ctdl_hlp_dir, config.c_ctdluid, -1); + + mkdir(ctdl_image_dir, 0700); + chmod(ctdl_image_dir, 0700); + chown(ctdl_image_dir, config.c_ctdluid, -1); + + mkdir(ctdl_bb_dir, 0700); + chmod(ctdl_bb_dir, 0700); + chown(ctdl_bb_dir, config.c_ctdluid, -1); + + mkdir(ctdl_file_dir, 0700); + chmod(ctdl_file_dir, 0700); + chown(ctdl_file_dir, config.c_ctdluid, -1); + + mkdir(ctdl_netcfg_dir, 0700); + chmod(ctdl_netcfg_dir, 0700); + chown(ctdl_netcfg_dir, config.c_ctdluid, -1); + + /* Delete files and directories used by older Citadel versions */ + system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null"); unlink("citadel.log"); + unlink("weekly"); check_services_entry(); /* Check /etc/services */ - check_inittab_entry(); /* Check /etc/inittab */ +#ifndef __CYGWIN__ + delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */ + check_xinetd_entry(); /* Check /etc/xinetd.d/telnet */ + + /* Offer to disable other MTA's on the system. */ + disable_other_mta("courier-authdaemon"); + disable_other_mta("courier-imap"); + disable_other_mta("courier-imap-ssl"); + disable_other_mta("courier-pop"); + disable_other_mta("courier-pop3"); + disable_other_mta("courier-pop3d"); + disable_other_mta("cyrmaster"); + disable_other_mta("cyrus"); + disable_other_mta("dovecot"); + disable_other_mta("exim"); + disable_other_mta("exim4"); + disable_other_mta("imapd"); + disable_other_mta("mta"); + disable_other_mta("pop3d"); + disable_other_mta("popd"); + disable_other_mta("postfix"); + disable_other_mta("qmail"); + disable_other_mta("saslauthd"); + disable_other_mta("sendmail"); + disable_other_mta("vmailmgrd"); +#endif + + /* Check for the 'db' nss and offer to disable it */ + fixnss(); - if ((pw = getpwuid(config.c_bbsuid)) == NULL) + if ((pw = getpwuid(config.c_ctdluid)) == NULL) gid = getgid(); else gid = pw->pw_gid; - progress("Setting file permissions", 0, 5); - chown(".", config.c_bbsuid, gid); - progress("Setting file permissions", 1, 5); - chown("citadel.config", config.c_bbsuid, gid); - progress("Setting file permissions", 2, 5); - snprintf(aaa, sizeof aaa, - "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null", - (long)config.c_bbsuid, (long)gid); - system(aaa); - progress("Setting file permissions", 3, 5); - chmod("citadel.config", S_IRUSR | S_IWUSR); - progress("Setting file permissions", 4, 5); - - /* See if we can start the Citadel service. */ - if (strlen(init_entry) > 0) { - for (a=0; a<=5; ++a) { - progress("Starting the Citadel service...", a, 5); - if (a == 0) start_the_service(); - sleep(1); + progress("Setting file permissions", 0, 3); + chown(ctdl_run_dir, config.c_ctdluid, gid); + progress("Setting file permissions", 1, 3); + chown(file_citadel_config, config.c_ctdluid, gid); + progress("Setting file permissions", 2, 3); + chmod(file_citadel_config, S_IRUSR | S_IWUSR); + progress("Setting file permissions", 3, 3); + + /* + * If we're running on SysV, install init scripts. + */ + if (!access("/var/run", W_OK)) { + + if (getenv("NO_INIT_SCRIPTS") == NULL) { + install_init_scripts(); } - important_message("Setup finished", - "Setup is finished. You may now log in."); + + if (!access("/etc/init.d/citadel", X_OK)) { + system("/etc/init.d/citadel start"); + sleep(3); + } + + if (test_server() == 0) { + important_message("Setup finished", + "Setup of the Citadel server is complete.\n" + "If you will be using WebCit, please run its\n" + "setup program now; otherwise, run './citadel'\n" + "to log in.\n"); + } + else { + important_message("Setup failed", + "Setup is finished, but the Citadel server failed to start.\n" + "Go back and check your configuration.\n" + ); + } + } + else { important_message("Setup finished", "Setup is finished. You may now start the server."); @@ -986,3 +1315,5 @@ NEW_INST: cleanup(0); return 0; } + +