X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fcontext_loop.c;h=cb1a907f62771e01c281dd84139fbaba86025ab3;hb=bedf5c0b955473d8ad02eaf628e8d209f534f2b6;hp=995393b074249556716ba992d2aa85b46830ac77;hpb=7836de1a004d9353cadb71885e360715884d1601;p=citadel.git diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 995393b07..cb1a907f6 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -1,50 +1,31 @@ /* * $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 * */ - -#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; @@ -56,64 +37,76 @@ 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); + free_march_list(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) ) { @@ -125,8 +118,8 @@ BREAKOUT: pthread_mutex_unlock(&SessionListMutex); } -/* - * Wake up occasionally and clean house +/** + * \brief Wake up occasionally and clean house */ void housekeeping_loop(void) { @@ -137,11 +130,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) @@ -156,8 +150,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) { @@ -165,7 +162,7 @@ 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 { safestrncpy(buf, hold, SIZ); @@ -186,7 +183,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 */ @@ -225,24 +224,43 @@ int lingering_close(int fd) -/* - * 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. +/** + * \brief sanity requests + * Check for bogus requests coming from brain-dead Windows boxes. + * + * \param http_cmd The HTTP request to check */ int is_bogus(char *http_cmd) { - - if (!strncasecmp(http_cmd, "GET /scripts/root.exe", 21)) return(1); - if (!strncasecmp(http_cmd, "GET /c/winnt", 12)) return(2); - if (!strncasecmp(http_cmd, "GET /MSADC/", 11)) return(3); + char *url; + int i, max; + + url = strstr(http_cmd, " "); + if (url == NULL) return(1); + ++url; + + char *bogus_prefixes[] = { + "/scripts/root.exe", /**< Worms and trojans and viruses, oh my! */ + "/c/winnt", + "/MSADC/", + "/_vti", /**< Broken Microsoft DAV implementation */ + "/MSOffice" /**< Stoopid MSOffice thinks everyone is IIS */ + }; + + max = sizeof(bogus_prefixes) / sizeof(char *); + + for (i=0; i 0); + /** + * 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 */ - if (is_bogus(buf)) goto bail; + /** Check for bogus requests */ + if (is_bogus(buf)) { + strcpy(req->line, "GET /404 HTTP/1.1"); + strcpy(buf, "GET /404 HTTP/1.1"); + } - /* - * 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... */ 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"); } 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. @@ -347,38 +407,40 @@ void context_loop(int sock) && (strncasecmp(buf, "/freebusy", 9)) && (strncasecmp(buf, "/do_logout", 10)) && (strncasecmp(buf, "/groupdav", 9)) + && (strncasecmp(buf, "/static", 7)) + && (strncasecmp(buf, "/rss", 4)) + && (strncasecmp(buf, "/404", 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"); } - /* + /** * See if there's an existing session open with the desired ID or user/pass */ TheSession = NULL; - if ( (TheSession == NULL) && (strlen(httpauth_user) > 0) ) { + if (TheSession == NULL) { pthread_mutex_lock(&SessionListMutex); for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - if ( (!strcasecmp(sptr->httpauth_user, httpauth_user)) + + /** 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; } - } - pthread_mutex_unlock(&SessionListMutex); - } - if ( (TheSession == NULL) && (desired_session != 0) ) { - pthread_mutex_lock(&SessionListMutex); - for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - if (sptr->wc_session == desired_session) { + /** 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) { @@ -388,41 +450,67 @@ void context_loop(int sock) memset(TheSession, 0, sizeof(struct wcsession)); TheSession->serv_sock = (-1); TheSession->chat_sock = (-1); - TheSession->wc_session = GenerateSessionID(); + + /* If we're recreating a session that expired, it's best to give it the same + * session number that it had before. The client browser ought to pick up + * the new session number and start using it, but in some rare situations it + * doesn't, and that's a Bad Thing because it causes lots of spurious sessions + * to get created. + */ + if (desired_session == 0) { + TheSession->wc_session = GenerateSessionID(); + } + else { + TheSession->wc_session = desired_session; + } + strcpy(TheSession->httpauth_user, httpauth_user); strcpy(TheSession->httpauth_pass, httpauth_pass); pthread_mutex_init(&TheSession->SessionMutex, NULL); - pthread_mutex_lock(&SessionListMutex); + TheSession->nonce = rand(); 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 */ - 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 */ -bail: while (req != NULL) { + /** Free the request buffer */ + while (req != NULL) { hptr = req->next; free(req); req = hptr; } - /* Free up any session-local substitution variables which + /** + * Free up any session-local substitution variables which * were set during this transaction */ clear_local_substs(); } +/*@}*/