X-Git-Url: https://code.citadel.org/?p=citadel.git;a=blobdiff_plain;f=webcit%2Fwebserver.c;h=612ac5d086507408efc3119001df8b84115bc917;hp=d66a008db00068d1900402646a714a0e1fdfe78d;hb=8f0a07617656512ca31a029814d04fc254ca9d33;hpb=c6dffcc49d48847b48e06b09bd97ec6cca99231b diff --git a/webcit/webserver.c b/webcit/webserver.c index d66a008db..612ac5d08 100644 --- a/webcit/webserver.c +++ b/webcit/webserver.c @@ -1,11 +1,9 @@ /* - * $Id$ - * * This contains a simple multithreaded TCP server manager. It sits around * waiting on the specified port for incoming HTTP connections. When a * connection is established, it calls context_loop() from context_loop.c. * - * Copyright (c) 1996-2008 by the citadel.org developers. + * Copyright (c) 1996-2011 by the citadel.org developers. * This program is released under the terms of the GNU General Public License v3. * */ @@ -13,676 +11,166 @@ #include "webcit.h" #include "webserver.h" -#if HAVE_BACKTRACE -#include -#endif #include "modules_init.h" #ifndef HAVE_SNPRINTF int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp); #endif -int verbosity = 9; /* Logging level */ -int msock; /* master listening socket */ + +extern int msock; /* master listening socket */ +extern int verbosity; /* Logging level */ +extern char static_icon_dir[PATH_MAX]; /* where should we find our mime icons */ int is_https = 0; /* Nonzero if I am an HTTPS service */ int follow_xff = 0; /* Follow X-Forwarded-For: header */ -int home_specified = 0; /* did the user specify a homedir? */ -int time_to_die = 0; /* Nonzero if server is shutting down */ -extern void *context_loop(int*); -extern void *housekeeping_loop(void); +int DisableGzip = 0; +struct redirector *redir = NULL; +char *default_landing_page = NULL; +int num_redir = 0; extern pthread_mutex_t SessionListMutex; extern pthread_key_t MyConKey; - -char ctdl_key_dir[PATH_MAX]=SSL_DIR; -char file_crpt_file_key[PATH_MAX]=""; -char file_crpt_file_csr[PATH_MAX]=""; -char file_crpt_file_cer[PATH_MAX]=""; - -char socket_dir[PATH_MAX]; /* where to talk to our citadel server */ -static const char editor_absolut_dir[PATH_MAX]=EDITORDIR; /* nailed to what configure gives us. */ -static char static_dir[PATH_MAX]; /* calculated on startup */ -static char static_local_dir[PATH_MAX]; /* calculated on startup */ -static char static_icon_dir[PATH_MAX]; /* where should we find our mime icons? */ -char *static_dirs[]={ /* needs same sort order as the web mapping */ - (char*)static_dir, /* our templates on disk */ - (char*)static_local_dir, /* user provided templates disk */ - (char*)editor_absolut_dir, /* the editor on disk */ - (char*)static_icon_dir /* our icons... */ -}; - -/* - * Subdirectories from which the client may request static content - * - * (If you add more, remember to increment 'ndirs' below) - */ -char *static_content_dirs[] = { - "static", /* static templates */ - "static.local", /* site local static templates */ - "tiny_mce" /* rich text editor */ -}; - -int ndirs=3; - - +extern void *housekeeping_loop(void); +extern int webcit_tcp_server(char *ip_addr, int port_number, int queue_len); +extern int webcit_uds_server(char *sockpath, int queue_len); +extern void graceful_shutdown_watcher(int signum); +extern void graceful_shutdown(int signum); +extern void start_daemon(char *pid_file); +extern void webcit_calc_dirs_n_files(int relh, const char *basedir, int home, char *webcitdir, char *relhome); +extern void worker_entry(void); +extern void drop_root(uid_t UID); + +char socket_dir[PATH_MAX]; /* where to talk to our citadel server */ char *server_cookie = NULL; /* our Cookie connection to the client */ int http_port = PORT_NUM; /* Port to listen on */ -char *ctdlhost = DEFAULT_HOST; /* our name */ -char *ctdlport = DEFAULT_PORT; /* our Port */ -int setup_wizard = 0; /* should we run the setup wizard? \todo */ -char wizard_filename[PATH_MAX]; /* where's the setup wizard? */ +char *ctdlhost = DEFAULT_HOST; /* Host name or IP address of Citadel server */ +char *ctdlport = DEFAULT_PORT; /* Port number of Citadel server */ +int setup_wizard = 0; /* should we run the setup wizard? */ +char wizard_filename[PATH_MAX]; /* location of file containing the last webcit version against which we ran setup wizard */ int running_as_daemon = 0; /* should we deamonize on startup? */ -/* - * This is a generic function to set up a master socket for listening on - * a TCP port. The server shuts down if the bind fails. - * - * ip_addr IP address to bind - * port_number port number to bind - * queue_len number of incoming connections to allow in the queue - */ -int ig_tcp_server(char *ip_addr, int port_number, int queue_len) -{ - struct sockaddr_in sin; - int s, i; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (ip_addr == NULL) { - sin.sin_addr.s_addr = INADDR_ANY; - } else { - sin.sin_addr.s_addr = inet_addr(ip_addr); - } - - if (sin.sin_addr.s_addr == INADDR_NONE) { - sin.sin_addr.s_addr = INADDR_ANY; - } - - if (port_number == 0) { - lprintf(1, "Cannot start: no port number specified.\n"); - exit(WC_EXIT_BIND); - } - sin.sin_port = htons((u_short) port_number); - - s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto)); - if (s < 0) { - lprintf(1, "Can't create a socket: %s\n", strerror(errno)); - exit(WC_EXIT_BIND); - } - /* Set some socket options that make sense. */ - i = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); - - fcntl(s, F_SETFL, O_NONBLOCK); /* maide: this statement is incorrect - there should be a preceding F_GETFL - and a bitwise OR with the previous - fd flags */ - - if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - lprintf(1, "Can't bind: %s\n", strerror(errno)); - exit(WC_EXIT_BIND); - } - if (listen(s, queue_len) < 0) { - lprintf(1, "Can't listen: %s\n", strerror(errno)); - exit(WC_EXIT_BIND); - } - return (s); -} - - - -/* - * Create a Unix domain socket and listen on it - * sockpath - file name of the unix domain socket - * queue_len - Number of incoming connections to allow in the queue - */ -int ig_uds_server(char *sockpath, int queue_len) -{ - struct sockaddr_un addr; - int s; - int i; - int actual_queue_len; - - actual_queue_len = queue_len; - if (actual_queue_len < 5) actual_queue_len = 5; - - i = unlink(sockpath); - if (i != 0) if (errno != ENOENT) { - lprintf(1, "webcit: can't unlink %s: %s\n", - sockpath, strerror(errno)); - exit(WC_EXIT_BIND); - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path); - - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s < 0) { - lprintf(1, "webcit: Can't create a socket: %s\n", - strerror(errno)); - exit(WC_EXIT_BIND); - } - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - lprintf(1, "webcit: Can't bind: %s\n", - strerror(errno)); - exit(WC_EXIT_BIND); - } - - if (listen(s, actual_queue_len) < 0) { - lprintf(1, "webcit: Can't listen: %s\n", - strerror(errno)); - exit(WC_EXIT_BIND); - } +/* #define DBG_PRINNT_HOOKS_AT_START */ +#ifdef DBG_PRINNT_HOOKS_AT_START +extern HashList *HandlerHash; +const char foobuf[32]; +const char *nix(void *vptr) {snprintf(foobuf, 32, "%0x", (long) vptr); return foobuf;} +#endif +extern int dbg_analyze_msg; +extern int dbg_backtrace_template_errors; +extern int DumpTemplateI18NStrings; +extern StrBuf *I18nDump; +void InitTemplateCache(void); +extern int LoadTemplates; - chmod(sockpath, 0777); - return(s); -} /* - * Read data from the client socket. - * - * sock socket fd to read from - * buf buffer to read into - * bytes number of bytes to read - * timeout Number of seconds to wait before timing out - * - * Possible return values: - * 1 Requested number of bytes has been read. - * 0 Request timed out. - * -1 Connection is broken, or other error. + * Handle redirects to legacy web servers */ -int client_read_to(int *sock, StrBuf *Target, StrBuf *Buf, int bytes, int timeout) -{ - const char *Error; - int retval = 0; - -#ifdef HAVE_OPENSSL - if (is_https) { - while ((StrLength(Buf) + StrLength(Target) < bytes) && - (retval >= 0)) - retval = client_read_sslbuffer(Buf, timeout); - if (retval >= 0) { - StrBufAppendBuf(Target, Buf, 0); /// todo: Buf > bytes? -#ifdef HTTP_TRACING - write(2, "\033[32m", 5); - write(2, buf, bytes); - write(2, "\033[30m", 5); -#endif - return 1; - } - else { - lprintf(2, "client_read_ssl() failed\n"); - return -1; +void handle_redir(void) { + if (num_redir > 0) { + int i; + const char *req = ChrPtr(WC->Hdr->this_page); + if (!req) { + do_404(); + return; } - } -#endif - - if (StrLength(Buf) > 0) {//// todo: what if Buf > bytes? - StrBufAppendBuf(Target, Buf, 0); - } - retval = StrBufReadBLOB(Target, - sock, - (StrLength(Target) > 0), - bytes - StrLength(Target), - &Error); - if (retval < 0) { - lprintf(2, "client_read() failed: %s\n", - Error); - return retval; - } - -#ifdef HTTP_TRACING - write(2, "\033[32m", 5); - write(2, buf, bytes); - write(2, "\033[30m", 5); -#endif - return 1; -} - - -/* - * Begin buffering HTTP output so we can transmit it all in one write operation later. - */ -void begin_burst(void) -{ - if (WC->WBuf == NULL) - WC->WBuf = NewStrBufPlain(NULL, 32768); -} - - -/* - * Finish buffering HTTP output. [Compress using zlib and] output with a Content-Length: header. - */ -long end_burst(void) -{ - struct wcsession *WCC = WC; - const char *ptr, *eptr; - long count; - ssize_t res; - fd_set wset; - int fdflags; - -#ifdef HAVE_ZLIB - /* Perform gzip compression, if enabled and supported by client */ - if ((WCC->gzip_ok) && CompressBuffer(WCC->WBuf)) - { - hprintf("Content-encoding: gzip\r\n"); - } -#endif /* HAVE_ZLIB */ - - hprintf("Content-length: %d\r\n\r\n", StrLength(WCC->WBuf)); - - ptr = ChrPtr(WCC->HBuf); - count = StrLength(WCC->HBuf); - eptr = ptr + count; - -#ifdef HAVE_OPENSSL - if (is_https) { - client_write_ssl(WCC->HBuf); - client_write_ssl(WCC->WBuf); - return (count); - } -#endif - - -#ifdef HTTP_TRACING - - write(2, "\033[34m", 5); - write(2, ptr, StrLength(WCC->WBuf)); - write(2, "\033[30m", 5); -#endif - fdflags = fcntl(WC->http_sock, F_GETFL); - - while (ptr < eptr) { - if ((fdflags & O_NONBLOCK) == O_NONBLOCK) { - FD_ZERO(&wset); - FD_SET(WCC->http_sock, &wset); - if (select(WCC->http_sock + 1, NULL, &wset, NULL, NULL) == -1) { - lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno)); - return -1; - } - } - - if ((res = write(WCC->http_sock, - ptr, - count)) == -1) { - lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno)); - wc_backtrace(); - return res; - } - count -= res; - ptr += res; - } - - ptr = ChrPtr(WCC->WBuf); - count = StrLength(WCC->WBuf); - eptr = ptr + count; - -#ifdef HTTP_TRACING - - write(2, "\033[34m", 5); - write(2, ptr, StrLength(WCC->WBuf)); - write(2, "\033[30m", 5); -#endif - - while (ptr < eptr) { - if ((fdflags & O_NONBLOCK) == O_NONBLOCK) { - FD_ZERO(&wset); - FD_SET(WCC->http_sock, &wset); - if (select(WCC->http_sock + 1, NULL, &wset, NULL, NULL) == -1) { - lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno)); - return -1; - } - } - - if ((res = write(WCC->http_sock, - ptr, - count)) == -1) { - lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno)); - wc_backtrace(); - return res; - } - count -= res; - ptr += res; - } - - return StrLength(WCC->WBuf); -} - - - -/* - * Read data from the client socket with default timeout. - * (This is implemented in terms of client_read_to() and could be - * justifiably moved out of sysdep.c) - * - * sock the socket fd to read from - * buf the buffer to write to - * bytes Number of bytes to read - */ -int client_read(int *sock, StrBuf *Target, StrBuf *buf, int bytes) -{ - return (client_read_to(sock, Target, buf, bytes, SLEEPING)); -} - - -/* - * Get a LF-terminated line of text from the client. - * (This is implemented in terms of client_read() and could be - * justifiably moved out of sysdep.c) - * - * sock socket fd to get client line from - * buf buffer to write read data to - * bufsiz how many bytes to read - * - * returns the number of bytes read - */ -/////int client_getln(int *sock, char *buf, int bufsiz) -/////{ -///// int i, retval; -///// -///// /* Read one character at a time.*/ -///// for (i = 0; *sock > 0; i++) { -///// retval = client_read(sock, &buf[i], 1); -///// if (retval < 0) -///// return retval; -///// if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1)) -///// break; -///// if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) { -///// /* Non printable character recieved from client */ -///// return(-1); -///// } -///// } -///// -///// /* If we got a long line, discard characters until the newline. */ -///// if (i == (bufsiz-1)) -///// while (buf[i] != '\n' && retval == 1) -///// retval = client_read(sock, &buf[i], 1); -///// -///// /* -///// * Strip any trailing non-printable characters. -///// */ -///// buf[i] = 0; -///// while ((i > 0) && (!isprint(buf[i - 1]))) { -///// buf[--i] = 0; -///// } -///// return (retval); -/////} - -/* - * Shut us down the regular way. - * signum is the signal we want to forward - */ -pid_t current_child; -void graceful_shutdown_watcher(int signum) { - lprintf (1, "bye; shutting down watcher."); - kill(current_child, signum); - if (signum != SIGHUP) - exit(0); -} - - -int ClientGetLine(int *sock, StrBuf *Target, StrBuf *CLineBuf) -{ - const char *Error, *pch, *pchs; - int rlen, len, retval = 0; - - if (is_https) { - if (StrLength(CLineBuf) > 0) { - pchs = ChrPtr(CLineBuf); - pch = strchr(pchs, '\n'); - if (pch != NULL) { - rlen = 0; - len = pch - pchs; - if (len > 0 && (*(pch - 1) == '\r') ) - rlen ++; - StrBufSub(Target, CLineBuf, 0, len - rlen); - StrBufCutLeft(CLineBuf, len + 1); - return len - rlen; + if (req[0] == '/') ++req; + syslog(9, "handle_redir() called; redirect this: %s", req); + for (i=0; i= 0) && - (pchs = ChrPtr(CLineBuf), - pch = strchr(pchs, '\n'), - pch == NULL)) - retval = client_read_sslbuffer(CLineBuf, SLEEPING); - if ((retval > 0) && (pch != NULL)) { - rlen = 0; - len = pch - pchs; - if (len > 0 && (*(pch - 1) == '\r') ) - rlen ++; - StrBufSub(Target, CLineBuf, 0, len - rlen); - StrBufCutLeft(CLineBuf, len + 1); - return len - rlen; - - } - else - return -1; } - else - return StrBufTCP_read_buffered_line(Target, - CLineBuf, - sock, - 5, - 1, - &Error); + do_404(); } /* - * Shut us down the regular way. - * signum is the signal we want to forward + * load redirect strings (for supporting transition of legacy web servers to citadel on the same host) */ -pid_t current_child; -void graceful_shutdown(int signum) { -// kill(current_child, signum); - char wd[SIZ]; - FILE *FD; - int fd; - getcwd(wd, SIZ); - lprintf (1, "bye going down gracefull.[%d][%s]\n", signum, wd); - fd = msock; - msock = -1; - time_to_die = 1; - FD=fdopen(fd, "a+"); - fflush (FD); - fclose (FD); - close(fd); -} - - -/* - * Start running as a daemon. - */ -void start_daemon(char *pid_file) -{ - int status = 0; - pid_t child = 0; - FILE *fp; - int do_restart = 0; - - current_child = 0; - - /* Close stdin/stdout/stderr and replace them with /dev/null. - * We don't just call close() because we don't want these fd's - * to be reused for other files. - */ - chdir("/"); - - signal(SIGHUP, SIG_IGN); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - - child = fork(); - if (child != 0) { - exit(0); +void load_redirs(char *filename) { + char buf[1024]; + int num_redir_alloc = num_redir; + FILE *fp = fopen(filename, "r"); + if (!fp) { + syslog(1, "Cannot open %s: %s", filename, strerror(errno)); + return; } - setsid(); - umask(0); - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); - signal(SIGTERM, graceful_shutdown_watcher); - signal(SIGHUP, graceful_shutdown_watcher); + while (fgets(buf, sizeof buf, fp) != NULL) { + char *ch; - do { - current_child = fork(); + buf[strlen(buf)-1] = 0; - - if (current_child < 0) { - perror("fork"); - ShutDownLibCitadel (); - exit(errno); - } - - else if (current_child == 0) { // child process -// signal(SIGTERM, graceful_shutdown); - signal(SIGHUP, graceful_shutdown); + ch = strchr(buf, '#'); + if (ch) strcpy(ch, ""); + striplt(buf); + if (!IsEmptyStr(buf)) { - return; /* continue starting webcit. */ - } - - else { // watcher process -// signal(SIGTERM, SIG_IGN); -// signal(SIGHUP, SIG_IGN); - if (pid_file) { - fp = fopen(pid_file, "w"); - if (fp != NULL) { - fprintf(fp, "%d\n", getpid()); - fclose(fp); + if (num_redir >= num_redir_alloc) { + if (num_redir_alloc == 0) { + num_redir_alloc = 10; } + else { + num_redir_alloc = num_redir_alloc * 2; + } + redir = realloc(redir, sizeof(struct redirector) * num_redir_alloc ); } - waitpid(current_child, &status, 0); - } - - do_restart = 0; - - /* Did the main process exit with an actual exit code? */ - if (WIFEXITED(status)) { - - /* Exit code 0 means the watcher should exit */ - if (WEXITSTATUS(status) == 0) { - do_restart = 0; - } - - /* Exit code 101-109 means the watcher should exit */ - else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) { - do_restart = 0; - } - - /* Any other exit code means we should restart. */ - else { - do_restart = 1; + + extract_token(redir[num_redir].urlpart, buf, 0, '|', sizeof(redir[num_redir].urlpart)); + extract_token(redir[num_redir].redirect_to, buf, 1, '|', sizeof(redir[num_redir].redirect_to)); + WebcitAddUrlHandler(redir[num_redir].urlpart, strlen(redir[num_redir].urlpart), "", 0, handle_redir, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC); + if (!strcasecmp(redir[num_redir].urlpart, "home")) { + default_landing_page = redir[num_redir].redirect_to ; } + ++num_redir; } - /* Any other type of termination (signals, etc.) should also restart. */ - else { - do_restart = 1; - } - - } while (do_restart); - - if (pid_file) { - unlink(pid_file); } - ShutDownLibCitadel (); - exit(WEXITSTATUS(status)); + fclose(fp); } -/* - * Spawn an additional worker thread into the pool. - */ -void spawn_another_worker_thread() -{ - pthread_t SessThread; /* Thread descriptor */ - pthread_attr_t attr; /* Thread attributes */ - int ret; - - lprintf(3, "Creating a new thread\n"); - - /* set attributes for the new thread */ - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - /* - * Our per-thread stacks need to be bigger than the default size, otherwise - * the MIME parser crashes on FreeBSD, and the IMAP service crashes on - * 64-bit Linux. - */ - if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) { - lprintf(1, "pthread_attr_setstacksize: %s\n", - strerror(ret)); - pthread_attr_destroy(&attr); - } - - /* now create the thread */ - if (pthread_create(&SessThread, &attr, - (void *(*)(void *)) worker_entry, NULL) - != 0) { - lprintf(1, "Can't create thread: %s\n", strerror(errno)); - } - - /* free up the attributes */ - pthread_attr_destroy(&attr); -} -//#define DBG_PRINNT_HOOKS_AT_START -#ifdef DBG_PRINNT_HOOKS_AT_START -const char foobuf[32]; -const char *nix(void *vptr) {snprintf(foobuf, 32, "%0x", (long) vptr); return foobuf;} -#endif -void InitTemplateCache(void); -extern int LoadTemplates; -extern void LoadZoneFiles(void); /* * Here's where it all begins. */ int main(int argc, char **argv) { + uid_t UID = -1; + size_t basesize = 2; /* how big should strbufs be on creation? */ pthread_t SessThread; /* Thread descriptor */ pthread_attr_t attr; /* Thread attributes */ - int a, i; /* General-purpose variables */ + int a; /* General-purpose variable */ char tracefile[PATH_MAX]; - char ip_addr[256]="0.0.0.0"; - char dirbuffer[PATH_MAX]=""; + char ip_addr[256]="*"; int relh=0; int home=0; - int home_specified=0; char relhome[PATH_MAX]=""; char webcitdir[PATH_MAX] = DATADIR; char *pidfile = NULL; char *hdir; - const char *basedir; -#ifdef ENABLE_NLS - char *locale = NULL; - char *mo = NULL; -#endif /* ENABLE_NLS */ + const char *basedir = NULL; char uds_listen_path[PATH_MAX]; /* listen on a unix domain socket? */ + const char *I18nDumpFile = NULL; + FILE *rvfp = NULL; + int rv = 0; - HandlerHash = NewHash(1, NULL); - PreferenceHooks = NewHash(1, NULL); - WirelessTemplateCache = NewHash(1, NULL); - WirelessLocalTemplateCache = NewHash(1, NULL); - LocalTemplateCache = NewHash(1, NULL); - TemplateCache = NewHash(1, NULL); - GlobalNS = NewHash(1, NULL); - Iterators = NewHash(1, NULL); - Contitionals = NewHash(1, NULL); - LoadZoneFiles(); + WildFireInitBacktrace(argv[0], 2); + start_modules (); #ifdef DBG_PRINNT_HOOKS_AT_START - dbg_PrintHash(HandlerHash, nix, NULL); +/* dbg_PrintHash(HandlerHash, nix, NULL);*/ #endif /* Ensure that we are linked to the correct version of libcitadel */ @@ -698,21 +186,27 @@ int main(int argc, char **argv) /* Parse command line */ #ifdef HAVE_OPENSSL - while ((a = getopt(argc, argv, "h:i:p:t:T:x:dD:cfs")) != EOF) + while ((a = getopt(argc, argv, "u:h:i:p:t:T:B:x:dD:G:r:cfsS:Z")) != EOF) #else - while ((a = getopt(argc, argv, "h:i:p:t:T:x:dD:cf")) != EOF) + while ((a = getopt(argc, argv, "u:h:i:p:t:T:B:x:dD:G:r:cfZ")) != EOF) #endif switch (a) { + case 'u': + UID = atol(optarg); + break; + case 'r': + load_redirs(optarg); + break; case 'h': hdir = strdup(optarg); relh=hdir[0]!='/'; - if (!relh) safestrncpy(webcitdir, hdir, - sizeof webcitdir); - else - safestrncpy(relhome, relhome, - sizeof relhome); + if (!relh) { + safestrncpy(webcitdir, hdir, sizeof webcitdir); + } + else { + safestrncpy(relhome, relhome, sizeof relhome); + } /* free(hdir); TODO: SHOULD WE DO THIS? */ - home_specified = 1; home=1; break; case 'd': @@ -722,6 +216,11 @@ int main(int argc, char **argv) pidfile = strdup(optarg); running_as_daemon = 1; break; + case 'B': /* Basesize */ + basesize = atoi(optarg); + if (basesize > 2) + StartLibCitadel(basesize); + break; case 'i': safestrncpy(ip_addr, optarg, sizeof ip_addr); break; @@ -733,12 +232,17 @@ int main(int argc, char **argv) break; case 't': safestrncpy(tracefile, optarg, sizeof tracefile); - freopen(tracefile, "w", stdout); - freopen(tracefile, "w", stderr); - freopen(tracefile, "r", stdin); + rvfp = freopen(tracefile, "w", stdout); + rvfp = freopen(tracefile, "w", stderr); + rvfp = freopen(tracefile, "r", stdin); break; case 'T': LoadTemplates = atoi(optarg); + dbg_analyze_msg = (LoadTemplates && (1<<1)) != 0; + dbg_backtrace_template_errors = (LoadTemplates && (1<<2)) != 0; + break; + case 'Z': + DisableGzip = 1; break; case 'x': verbosity = atoi(optarg); @@ -755,28 +259,44 @@ int main(int argc, char **argv) if (gethostname (&server_cookie[strlen(server_cookie)], 200) != 0) { - lprintf(2, "gethostname: %s\n", - strerror(errno)); + syslog(2, "gethostname: %s", strerror(errno)); free(server_cookie); } } break; +#ifdef HAVE_OPENSSL case 's': is_https = 1; break; + case 'S': + is_https = 1; + ssl_cipher_list = strdup(optarg); + break; +#endif + case 'G': + DumpTemplateI18NStrings = 1; + I18nDump = NewStrBufPlain(HKEY("int templatestrings(void)\n{\n")); + I18nDumpFile = optarg; + break; default: fprintf(stderr, "usage: webcit " "[-i ip_addr] [-p http_port] " "[-t tracefile] [-c] [-f] " "[-T Templatedebuglevel] " - "[-d] " + "[-d] [-Z] [-G i18ndumpfile] " #ifdef HAVE_OPENSSL - "[-s] " + "[-s] [-S cipher_suites]" #endif "[remotehost [remoteport]]\n"); return 1; } + /* Start the logger */ + openlog("webcit", + ( running_as_daemon ? (LOG_PID) : (LOG_PID | LOG_PERROR) ), + LOG_DAEMON + ); + if (optind < argc) { ctdlhost = argv[optind]; if (++optind < argc) @@ -784,93 +304,75 @@ int main(int argc, char **argv) } /* daemonize, if we were asked to */ - if (running_as_daemon) { + if (!DumpTemplateI18NStrings && running_as_daemon) { start_daemon(pidfile); } else { -/// signal(SIGTERM, graceful_shutdown); + signal(SIGINT, graceful_shutdown); signal(SIGHUP, graceful_shutdown); } - /* Tell 'em who's in da house */ - lprintf(1, PACKAGE_STRING "\n"); - lprintf(1, "Copyright (C) 1996-2008 by the Citadel development team.\n" - "This software is distributed under the terms of the " - "GNU General Public License.\n\n" - ); - - - /* initialize the International Bright Young Thing */ -#ifdef ENABLE_NLS - initialize_locales(); - - locale = setlocale(LC_ALL, ""); - - mo = malloc(strlen(webcitdir) + 20); - lprintf(9, "Message catalog directory: %s\n", bindtextdomain("webcit", LOCALEDIR"/locale")); - free(mo); - lprintf(9, "Text domain: %s\n", textdomain("webcit")); - lprintf(9, "Text domain Charset: %s\n", bind_textdomain_codeset("webcit","UTF8")); - preset_locale(); -#endif - - - /* calculate all our path on a central place */ - /* where to keep our config */ - -#define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\ - snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \ - (home&!relh)?webcitdir:basedir, \ - ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \ - ((basedir!=webcitdir)&(home&!relh))?"/":"", \ - relhome, \ - (relhome[0]!='\0')?"/":"",\ - dirbuffer,\ - (dirbuffer[0]!='\0')?"/":""); - basedir=RUNDIR; - COMPUTE_DIRECTORY(socket_dir); - basedir=WWWDIR "/static"; - COMPUTE_DIRECTORY(static_dir); - basedir=WWWDIR "/static/icons"; - COMPUTE_DIRECTORY(static_icon_dir); - basedir=WWWDIR "/static.local"; - COMPUTE_DIRECTORY(static_local_dir); + webcit_calc_dirs_n_files(relh, basedir, home, webcitdir, relhome); + LoadIconDir(static_icon_dir); - snprintf(file_crpt_file_key, - sizeof file_crpt_file_key, - "%s/citadel.key", - ctdl_key_dir); - snprintf(file_crpt_file_csr, - sizeof file_crpt_file_csr, - "%s/citadel.csr", - ctdl_key_dir); - snprintf(file_crpt_file_cer, - sizeof file_crpt_file_cer, - "%s/citadel.cer", - ctdl_key_dir); + /* Tell 'em who's in da house */ + syslog(1, "%s", PACKAGE_STRING); + syslog(1, "Copyright (C) 1996-2011 by the citadel.org team"); + syslog(1, " "); + syslog(1, "This program is open source software: you can redistribute it and/or"); + syslog(1, "modify it under the terms of the GNU General Public License as published"); + syslog(1, "by the Free Software Foundation, either version 3 of the License, or"); + syslog(1, "(at your option) any later version."); + syslog(1, " "); + syslog(1, "This program is distributed in the hope that it will be useful,"); + syslog(1, "but WITHOUT ANY WARRANTY; without even the implied warranty of"); + syslog(1, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"); + syslog(1, "GNU General Public License for more details."); + syslog(1, " "); + syslog(1, "You should have received a copy of the GNU General Public License"); + syslog(1, "along with this program. If not, see ."); + syslog(1, " "); + + + /* initialize various subsystems */ - /* we should go somewhere we can leave our coredump, if enabled... */ - lprintf(9, "Changing directory to %s\n", socket_dir); - if (chdir(webcitdir) != 0) { - perror("chdir"); - } - LoadIconDir(static_icon_dir); + initialise_modules(); InitTemplateCache(); + if (DumpTemplateI18NStrings) { + FILE *fd; + StrBufAppendBufPlain(I18nDump, HKEY("}\n"), 0); + if (StrLength(I18nDump) < 50) { + syslog(1, "********************************************************************************\n"); + syslog(1, "* No strings found in templates! Are you sure they're there? *\n"); + syslog(1, "********************************************************************************\n"); + return -1; + } + fd = fopen(I18nDumpFile, "w"); + if (fd == NULL) { + syslog(1, "********************************************************************************\n"); + syslog(1, "* unable to open I18N dumpfile [%s] *\n", I18nDumpFile); + syslog(1, "********************************************************************************\n"); + return -1; + } + rv = fwrite(ChrPtr(I18nDump), 1, StrLength(I18nDump), fd); + fclose(fd); + return 0; + } - initialise_modules(); - initialize_viewdefs(); - initialize_axdefs(); /* Tell libical to return an error instead of aborting if it sees badly formed iCalendar data. */ icalerror_errors_are_fatal = 0; + /* Use our own prefix on tzid's generated from system tzdata */ + icaltimezone_set_tzid_prefix("/citadel.org/"); + /* * Set up a place to put thread-specific data. * We only need a single pointer per thread - it points to the * wcsession struct to which the thread is currently bound. */ if (pthread_key_create(&MyConKey, NULL) != 0) { - lprintf(1, "Can't create TSD key: %s\n", strerror(errno)); + syslog(1, "Can't create TSD key: %s\n", strerror(errno)); } InitialiseSemaphores (); @@ -882,26 +384,31 @@ int main(int argc, char **argv) */ #ifdef HAVE_OPENSSL if (pthread_key_create(&ThreadSSL, NULL) != 0) { - lprintf(1, "Can't create TSD key: %s\n", strerror(errno)); + syslog(1, "Can't create TSD key: %s\n", strerror(errno)); } #endif /* * Bind the server to our favorite port. - * There is no need to check for errors, because ig_tcp_server() + * There is no need to check for errors, because webcit_tcp_server() * exits if it doesn't succeed. */ if (!IsEmptyStr(uds_listen_path)) { - lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path); - msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH); + syslog(2, "Attempting to create listener socket at %s...\n", uds_listen_path); + msock = webcit_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH); } else { - lprintf(2, "Attempting to bind to port %d...\n", http_port); - msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH); + syslog(2, "Attempting to bind to port %d...\n", http_port); + msock = webcit_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH); + } + if (msock < 0) + { + ShutDownWebcit(); + return -msock; } - lprintf(2, "Listening on socket %d\n", msock); + syslog(2, "Listening on socket %d\n", msock); signal(SIGPIPE, SIG_IGN); pthread_mutex_init(&SessionListMutex, NULL); @@ -923,211 +430,17 @@ int main(int argc, char **argv) init_ssl(); } #endif + drop_root(UID); - /* Start a few initial worker threads */ - for (i = 0; i < (MIN_WORKER_THREADS); ++i) { - spawn_another_worker_thread(); - } - - /* now the original thread becomes another worker */ + /* Become a worker thread. More worker threads will be spawned as they are needed. */ worker_entry(); - ShutDownLibCitadel (); - DeleteHash(&HandlerHash); - DeleteHash(&PreferenceHooks); + ShutDownLibCitadel(); return 0; } -void ShutDownWebcit(void) -{ - DeleteHash(&ZoneHash); - free_zone_directory (); - icaltimezone_release_zone_tab (); - icalmemory_free_ring (); - ShutDownLibCitadel (); - DeleteHash(&HandlerHash); - DeleteHash(&PreferenceHooks); - DeleteHash(&GlobalNS); - DeleteHash(&WirelessTemplateCache); - DeleteHash(&WirelessLocalTemplateCache); - DeleteHash(&TemplateCache); - DeleteHash(&LocalTemplateCache); - DeleteHash(&Iterators); - DeleteHash(&Contitionals); -#ifdef ENABLE_NLS - ShutdownLocale(); -#endif -#ifdef HAVE_OPENSSL - if (is_https) { - shutdown_ssl(); - } -#endif -} - -/* - * Entry point for worker threads - */ -void worker_entry(void) -{ - int ssock; - int i = 0; - int fail_this_transaction = 0; - int ret; - struct timeval tv; - fd_set readset, tempset; - - tv.tv_sec = 0; - tv.tv_usec = 10000; - FD_ZERO(&readset); - FD_SET(msock, &readset); - - do { - /* Only one thread can accept at a time */ - fail_this_transaction = 0; - ssock = -1; - errno = EAGAIN; - do { - ret = -1; /* just one at once should select... */ - begin_critical_section(S_SELECT); - - FD_ZERO(&tempset); - if (msock > 0) FD_SET(msock, &tempset); - tv.tv_sec = 0; - tv.tv_usec = 10000; - if (msock > 0) ret = select(msock+1, &tempset, NULL, NULL, &tv); - end_critical_section(S_SELECT); - if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN)) - {// EINTR and EAGAIN are thrown but not of interest. - lprintf(2, "accept() failed:%d %s\n", - errno, strerror(errno)); - } - else if ((ret > 0) && (msock > 0) && FD_ISSET(msock, &tempset)) - {// Successfully selected, and still not shutting down? Accept! - ssock = accept(msock, NULL, 0); - } - - } while ((msock > 0) && (ssock < 0) && (time_to_die == 0)); - - if ((msock == -1)||(time_to_die)) - {// ok, we're going down. - int shutdown = 0; - - /* the first to come here will have to do the cleanup. - * make shure its realy just one. - */ - begin_critical_section(S_SHUTDOWN); - if (msock == -1) - { - msock = -2; - shutdown = 1; - } - end_critical_section(S_SHUTDOWN); - if (shutdown == 1) - {// we're the one to cleanup the mess. - lprintf(2, "I'm master shutdown: tagging sessions to be killed.\n"); - shutdown_sessions(); - lprintf(2, "master shutdown: waiting for others\n"); - sleeeeeeeeeep(1); // wait so some others might finish... - lprintf(2, "master shutdown: cleaning up sessions\n"); - do_housekeeping(); - lprintf(2, "master shutdown: cleaning up libical\n"); - - ShutDownWebcit(); - - lprintf(2, "master shutdown exiting!.\n"); - exit(0); - } - break; - } - if (ssock < 0 ) continue; - - if (msock < 0) { - if (ssock > 0) close (ssock); - lprintf(2, "inbetween."); - pthread_exit(NULL); - } else { // Got it? do some real work! - /* Set the SO_REUSEADDR socket option */ - i = 1; - setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, - &i, sizeof(i)); - - /* If we are an HTTPS server, go crypto now. */ -#ifdef HAVE_OPENSSL - if (is_https) { - if (starttls(ssock) != 0) { - fail_this_transaction = 1; - close(ssock); - } - } -#endif - - if (fail_this_transaction == 0) { - - /* Perform an HTTP transaction... */ - context_loop(&ssock); - - /* Shut down SSL/TLS if required... */ -#ifdef HAVE_OPENSSL - if (is_https) { - endtls(); - } -#endif - - /* ...and close the socket. */ - if (ssock > 0) - lingering_close(ssock); - } - - } - - } while (!time_to_die); - - lprintf (1, "bye\n"); - pthread_exit(NULL); -} - -/* - * print log messages - * logs to stderr if loglevel is lower than the verbosity set at startup - * - * loglevel level of the message - * format the printf like format string - * ... the strings to put into format - */ -int lprintf(int loglevel, const char *format, ...) -{ - va_list ap; - - if (loglevel <= verbosity) { - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fflush(stderr); - } - return 1; -} -/* - * print the actual stack frame. - */ -void wc_backtrace(void) -{ -#ifdef HAVE_BACKTRACE - void *stack_frames[50]; - size_t size, i; - char **strings; - size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*)); - strings = backtrace_symbols(stack_frames, size); - for (i = 0; i < size; i++) { - if (strings != NULL) - lprintf(1, "%s\n", strings[i]); - else - lprintf(1, "%p\n", stack_frames[i]); - } - free(strings); -#endif -}