X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fcontext_loop.c;h=467b559f881d4fb9a327897bf0b45df0133f298e;hb=1e32899153e9e52aaec1e651e0c33a563b8aaed8;hp=5d01bf297ec5ed5bd0d6f359812cc617ebde736e;hpb=29790d4e1d6057e91904eddc4d3049f475d76e16;p=citadel.git diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 5d01bf297..467b559f8 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -1,51 +1,31 @@ /* - * context_loop.c - * + * $Id$ + */ +/** + * \defgroup WebServerII some of the webserver stuff. * This is the other half of the webserver. It handles the task of hooking * up HTTP requests with the sessions they belong to, using HTTP cookies to * keep track of things. If the HTTP request doesn't belong to any currently * active session, a new session is started. + * \ingroup WebcitHttpServer * - * $Id$ */ - -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_FCNTL_H -#include -#endif -#include -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_LIMITS_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include +/*@{*/ #include "webcit.h" #include "webserver.h" -/* Only one thread may manipulate SessionList at a time... */ +/** Only one thread may manipulate SessionList at a time... */ pthread_mutex_t SessionListMutex; -struct wcsession *SessionList = NULL; +struct wcsession *SessionList = NULL; /**< our sessions ????*/ -pthread_key_t MyConKey; /* TSD key for MySession() */ +pthread_key_t MyConKey; /**< TSD key for MySession() */ +/** + * \brief free the memory used for viewing atachments + * \param sess the session object to destroy + */ void free_attachments(struct wcsession *sess) { struct wc_attachment *att; @@ -57,64 +37,75 @@ void free_attachments(struct wcsession *sess) { } } - +/** + * \brief what?????? + */ void do_housekeeping(void) { - struct wcsession *sptr, *ss, *session_to_kill; + struct wcsession *sptr, *ss; + struct wcsession *sessions_to_kill = NULL; int num_sessions = 0; static int num_threads = MIN_WORKER_THREADS; - do { - session_to_kill = NULL; - pthread_mutex_lock(&SessionListMutex); - num_sessions = 0; - for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - ++num_sessions; - - /* Kill idle sessions */ - if ((time(NULL) - (sptr->lastreq)) > - (time_t) WEBCIT_TIMEOUT) { - sptr->killthis = 1; - } - - /* Remove sessions flagged for kill */ - if (sptr->killthis) { - - lprintf(3, "Destroying session %d\n", - sptr->wc_session); + /** + * Lock the session list, moving any candidates for euthanasia into + * a separate list. + */ + pthread_mutex_lock(&SessionListMutex); + num_sessions = 0; + for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { + ++num_sessions; + + /** Kill idle sessions */ + if ((time(NULL) - (sptr->lastreq)) > + (time_t) WEBCIT_TIMEOUT) { + sptr->killthis = 1; + } - /* remove session from linked list */ - if (sptr == SessionList) { - SessionList = SessionList->next; - } - else for (ss=SessionList;ss!=NULL;ss=ss->next) { - if (ss->next == sptr) { - ss->next = ss->next->next; - } - } + /** Remove sessions flagged for kill */ + if (sptr->killthis) { - session_to_kill = sptr; - goto BREAKOUT; + /** remove session from linked list */ + if (sptr == SessionList) { + SessionList = SessionList->next; } - } -BREAKOUT: pthread_mutex_unlock(&SessionListMutex); - - if (session_to_kill != NULL) { - pthread_mutex_lock(&session_to_kill->SessionMutex); - close(session_to_kill->serv_sock); - close(session_to_kill->chat_sock); - if (session_to_kill->preferences != NULL) { - free(session_to_kill->preferences); + else for (ss=SessionList;ss!=NULL;ss=ss->next) { + if (ss->next == sptr) { + ss->next = ss->next->next; + } } - free_attachments(session_to_kill); - pthread_mutex_unlock(&session_to_kill->SessionMutex); - free(session_to_kill); + + sptr->next = sessions_to_kill; + sessions_to_kill = sptr; } + } + pthread_mutex_unlock(&SessionListMutex); - } while (session_to_kill != NULL); + /** + * Now free up and destroy the culled sessions. + */ + while (sessions_to_kill != NULL) { + lprintf(3, "Destroying session %d\n", sessions_to_kill->wc_session); + pthread_mutex_lock(&sessions_to_kill->SessionMutex); + close(sessions_to_kill->serv_sock); + close(sessions_to_kill->chat_sock); + if (sessions_to_kill->preferences != NULL) { + free(sessions_to_kill->preferences); + } + if (sessions_to_kill->cache_fold != NULL) { + free(sessions_to_kill->cache_fold); + } + free_attachments(sessions_to_kill); + pthread_mutex_unlock(&sessions_to_kill->SessionMutex); + sptr = sessions_to_kill->next; + free(sessions_to_kill); + sessions_to_kill = sptr; + --num_sessions; + } - /* - * See if we need more worker threads + /** + * If there are more sessions than threads, then we should spawn + * more threads ... up to a predefined maximum. */ while ( (num_sessions > num_threads) && (num_threads <= MAX_WORKER_THREADS) ) { @@ -126,8 +117,8 @@ BREAKOUT: pthread_mutex_unlock(&SessionListMutex); } -/* - * Wake up occasionally and clean house +/** + * \brief Wake up occasionally and clean house */ void housekeeping_loop(void) { @@ -138,11 +129,12 @@ void housekeeping_loop(void) } -/* +/** + * \brief Create a Session id * Generate a unique WebCit session ID (which is not the same thing as the * Citadel session ID). * - * FIXME ... ensure that session number is truly unique + * \todo FIXME ... ensure that session number is truly unique * */ int GenerateSessionID(void) @@ -157,8 +149,11 @@ int GenerateSessionID(void) } -/* - * Collapse multiple cookies on one line +/** + * \brief Collapse multiple cookies on one line + * \param sock a socket? + * \param buf some bunch of chars? + * \param hold hold what? */ int req_gets(int sock, char *buf, char *hold) { @@ -166,10 +161,10 @@ int req_gets(int sock, char *buf, char *hold) if (strlen(hold) == 0) { strcpy(buf, ""); - a = client_gets(sock, buf); + a = client_getln(sock, buf, SIZ); if (a<1) return(-1); } else { - strcpy(buf, hold); + safestrncpy(buf, hold, SIZ); } strcpy(hold, ""); @@ -187,7 +182,9 @@ int req_gets(int sock, char *buf, char *hold) return(0); } -/* +/** + * \brief close some fd for some reason??? + * \param fd the fd to close?????? * lingering_close() a`la Apache. see * http://www.apache.org/docs/misc/fin_wait_2.html for rationale */ @@ -226,11 +223,13 @@ int lingering_close(int fd) -/* +/** + * \brief sanity requests * Check for bogus requests coming from (for example) brain-dead * Windoze boxes that are infected with the latest worm-of-the-week. * If we detect one of these, bail out without bothering our Citadel * server. + * \param http_cmd the cmd to check */ int is_bogus(char *http_cmd) { @@ -243,7 +242,8 @@ int is_bogus(char *http_cmd) { -/* +/** + * \brief handle one request * This loop gets called once for every HTTP connection made to WebCit. At * this entry point we have an HTTP socket with a browser allegedly on the * other end, but we have not yet bound to a WebCit session. @@ -253,6 +253,7 @@ int is_bogus(char *http_cmd) { * transaction loop. Afterwards, we unbind from the session. When this * function returns, the worker thread is then free to handle another * transaction. + * \param sock the socket we will put our answer to */ void context_loop(int sock) { @@ -262,22 +263,64 @@ void context_loop(int sock) char buf[SIZ], hold[SIZ]; int desired_session = 0; int got_cookie = 0; + int gzip_ok = 0; struct wcsession *TheSession, *sptr; - int outside_frameset_allowed = 0; - - /* + char httpauth_string[1024]; + char httpauth_user[1024]; + char httpauth_pass[1024]; + char accept_language[256]; + char *ptr = NULL; + int session_is_new = 0; + + strcpy(httpauth_string, ""); + strcpy(httpauth_user, DEFAULT_HTTPAUTH_USER); + strcpy(httpauth_pass, DEFAULT_HTTPAUTH_PASS); + + /** * Find out what it is that the web browser is asking for */ memset(hold, 0, sizeof(hold)); do { if (req_gets(sock, buf, hold) < 0) return; + /** + * Can we compress? + */ + if (!strncasecmp(buf, "Accept-encoding:", 16)) { + if (strstr(&buf[16], "gzip")) { + gzip_ok = 1; + } + } + + /** + * Browser-based sessions use cookies for session authentication + */ if (!strncasecmp(buf, "Cookie: webcit=", 15)) { cookie_to_stuff(&buf[15], &desired_session, - NULL, NULL, NULL); + NULL, 0, NULL, 0, NULL, 0); got_cookie = 1; } + /** + * GroupDAV-based sessions use HTTP authentication + */ + if (!strncasecmp(buf, "Authorization: Basic ", 21)) { + CtdlDecodeBase64(httpauth_string, &buf[21], strlen(&buf[21])); + extract_token(httpauth_user, httpauth_string, 0, ':', sizeof httpauth_user); + extract_token(httpauth_pass, httpauth_string, 1, ':', sizeof httpauth_pass); + } + + if (!strncasecmp(buf, "If-Modified-Since: ", 19)) { + if_modified_since = httpdate_to_timestamp(&buf[19]); + } + + if (!strncasecmp(buf, "Accept-Language: ", 17)) { + safestrncpy(accept_language, &buf[17], sizeof accept_language); + } + + /** + * Read in the request + */ hptr = (struct httprequest *) malloc(sizeof(struct httprequest)); if (req == NULL) @@ -287,39 +330,54 @@ void context_loop(int sock) hptr->next = NULL; last = hptr; - strcpy(hptr->line, buf); + safestrncpy(hptr->line, buf, sizeof hptr->line); } while (strlen(buf) > 0); - strcpy(buf, req->line); + /** + * If the request is prefixed by "/webcit" then chop that off. This + * allows a front end web server to forward all /webcit requests to us + * while still using the same web server port for other things. + */ + + ptr = strstr(req->line, " /webcit "); /*< Handle "/webcit" */ + if (ptr != NULL) { + strcpy(ptr+2, ptr+8); + } + + ptr = strstr(req->line, " /webcit"); /*< Handle "/webcit/" */ + if (ptr != NULL) { + strcpy(ptr+1, ptr+8); + } + + /** Begin parsing the request. */ + + safestrncpy(buf, req->line, sizeof buf); lprintf(5, "HTTP: %s\n", buf); - /* Check for bogus requests */ + /** Check for bogus requests */ if (is_bogus(buf)) goto bail; - /* - * If requesting a non-root page, there should already be a cookie - * set. If there isn't, the client browser has cookies turned off - * (or doesn't support them) and we have to barf & bail. + /** + * Strip out the method, leaving the URL up front... */ - if (!strncasecmp(buf, "GET ", 4)) strcpy(buf, &buf[4]); - else if (!strncasecmp(buf, "HEAD ", 5)) strcpy(buf, &buf[5]); - else if (!strncasecmp(buf, "POST ", 5)) strcpy(buf, &buf[5]); + remove_token(buf, 0, ' '); if (buf[1]==' ') buf[1]=0; - /* + /** * While we're at it, gracefully handle requests for the * robots.txt and favicon.ico files. */ if (!strncasecmp(buf, "/robots.txt", 11)) { strcpy(req->line, "GET /static/robots.txt" - "?force_close_session=yes HTTP/1.0"); + "?force_close_session=yes HTTP/1.1"); } - if (!strncasecmp(buf, "/favicon.ico", 12)) { + else if (!strncasecmp(buf, "/favicon.ico", 12)) { strcpy(req->line, "GET /static/favicon.ico"); } - /* These are the URL's which may be executed without a + /** + * These are the URL's which may be executed without a * session cookie already set. If it's not one of these, * force the session to close because cookies are * probably disabled on the client browser. @@ -328,46 +386,40 @@ void context_loop(int sock) && (strncasecmp(buf, "/listsub", 8)) && (strncasecmp(buf, "/freebusy", 9)) && (strncasecmp(buf, "/do_logout", 10)) + && (strncasecmp(buf, "/groupdav", 9)) + && (strncasecmp(buf, "/static", 7)) + && (strncasecmp(buf, "/rss", 4)) && (got_cookie == 0)) { strcpy(req->line, "GET /static/nocookies.html" - "?force_close_session=yes HTTP/1.0"); + "?force_close_session=yes HTTP/1.1"); } - /* These are the URL's which may be executed outside of the - * main frameset. If it's not one of these, the page will - * need JavaScript added to force the frameset to reload. - */ - if ( (!strcasecmp(buf, "/")) - || (!strcasecmp(buf, "/static/mainframeset.html")) - || (!strcasecmp(buf, "/static/robots.txt")) - || (!strncasecmp(buf, "/do_welcome", 11)) - || (!strncasecmp(buf, "/do_logout", 10)) - || (!strncasecmp(buf, "/page_popup", 11)) - || (!strncasecmp(buf, "/page_user", 10)) /* Sometimes this is wrong */ - || (!strncasecmp(buf, "/listsub", 8)) - || (!strncasecmp(buf, "/freebusy", 9)) - || (!strncasecmp(buf, "/termquit", 9)) ) { - outside_frameset_allowed = 1; - } - else { - outside_frameset_allowed = 0; - } - - /* - * See if there's an existing session open with the desired ID + /** + * See if there's an existing session open with the desired ID or user/pass */ TheSession = NULL; - if (desired_session != 0) { + + if (TheSession == NULL) { pthread_mutex_lock(&SessionListMutex); for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - if (sptr->wc_session == desired_session) { + + /** If HTTP-AUTH, look for a session with matching credentials */ + if ( (strlen(httpauth_user) > 0) + &&(!strcasecmp(sptr->httpauth_user, httpauth_user)) + &&(!strcasecmp(sptr->httpauth_pass, httpauth_pass)) ) { TheSession = sptr; } + + /** If cookie-session, look for a session with matching session ID */ + if ( (desired_session != 0) && (sptr->wc_session == desired_session)) { + TheSession = sptr; + } + } pthread_mutex_unlock(&SessionListMutex); } - /* + /** * Create a new session if we have to */ if (TheSession == NULL) { @@ -378,34 +430,52 @@ void context_loop(int sock) TheSession->serv_sock = (-1); TheSession->chat_sock = (-1); TheSession->wc_session = GenerateSessionID(); + strcpy(TheSession->httpauth_user, httpauth_user); + strcpy(TheSession->httpauth_pass, httpauth_pass); pthread_mutex_init(&TheSession->SessionMutex, NULL); - pthread_mutex_lock(&SessionListMutex); TheSession->next = SessionList; SessionList = TheSession; pthread_mutex_unlock(&SessionListMutex); + session_is_new = 1; } - /* + /** * A future improvement might be to check the session integrity * at this point before continuing. */ - /* + /** * Bind to the session and perform the transaction */ - pthread_mutex_lock(&TheSession->SessionMutex); /* bind */ + pthread_mutex_lock(&TheSession->SessionMutex); /*< bind */ pthread_setspecific(MyConKey, (void *)TheSession); TheSession->http_sock = sock; - TheSession->lastreq = time(NULL); /* log */ - TheSession->outside_frameset_allowed = outside_frameset_allowed; - session_loop(req); /* do transaction */ - pthread_mutex_unlock(&TheSession->SessionMutex); /* unbind */ + TheSession->lastreq = time(NULL); /*< log */ + TheSession->gzip_ok = gzip_ok; +#ifdef ENABLE_NLS + if (session_is_new) { + httplang_to_locale(accept_language); + } + go_selected_language(); /*< set locale */ +#endif + session_loop(req); /*< do transaction */ +#ifdef ENABLE_NLS + stop_selected_language(); /*< unset locale */ +#endif + pthread_mutex_unlock(&TheSession->SessionMutex); /*< unbind */ - /* Free the request buffer */ + /** Free the request buffer */ bail: while (req != NULL) { hptr = req->next; free(req); req = hptr; } + + /** + * Free up any session-local substitution variables which + * were set during this transaction + */ + clear_local_substs(); } +/*@}*/