X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fcontext_loop.c;h=208521386eb318cefe76e92a27da39b1a5a6462c;hb=27e6a22f021cffe2cee1a41accbf9d2f78b20dab;hp=3e3302a1b6685fb3983ac4700d671caf94bbf2df;hpb=0edef76d13c068cd29bd45ca16e4a504f72cacf0;p=citadel.git diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 3e3302a1b..208521386 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -1,120 +1,114 @@ /* - * context_loop.c + * $Id$ * * 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. * - * $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" +#include "modules_init.h" /* Only one thread may manipulate SessionList at a time... */ pthread_mutex_t SessionListMutex; -struct wcsession *SessionList = NULL; +wcsession *SessionList = NULL; /**< our sessions ????*/ -pthread_key_t MyConKey; /* TSD key for MySession() */ +pthread_key_t MyConKey; /**< TSD key for MySession() */ -void free_attachments(struct wcsession *sess) { - struct wc_attachment *att; - while (sess->first_attachment != NULL) { - att = sess->first_attachment; - sess->first_attachment = sess->first_attachment->next; - free(att->data); - free(att); +void DestroySession(wcsession **sessions_to_kill) +{ + 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); } + DeleteServInfo(&((*sessions_to_kill)->serv_info)); + free_march_list((*sessions_to_kill)); + + session_destroy_modules(*sessions_to_kill); + + free((*sessions_to_kill)); + (*sessions_to_kill) = NULL; } +void shutdown_sessions(void) +{ + wcsession *sptr; + + for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { + sptr->killthis = 1; + } +} void do_housekeeping(void) { - struct wcsession *sptr, *ss, *session_to_kill; + wcsession *sptr, *ss; + 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); + pthread_mutex_unlock(&sessions_to_kill->SessionMutex); + sptr = sessions_to_kill->next; + + DestroySession(&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,7 +120,7 @@ BREAKOUT: pthread_mutex_unlock(&SessionListMutex); } -/* +/* * Wake up occasionally and clean house */ void housekeeping_loop(void) @@ -139,11 +133,9 @@ void housekeeping_loop(void) /* + * 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 - * */ int GenerateSessionID(void) { @@ -157,41 +149,10 @@ int GenerateSessionID(void) } -/* - * Collapse multiple cookies on one line - */ -int req_gets(int sock, char *buf, char *hold) -{ - int a; - - if (strlen(hold) == 0) { - strcpy(buf, ""); - a = client_gets(sock, buf); - if (a<1) return(-1); - } else { - strcpy(buf, hold); - } - strcpy(hold, ""); - - if (!strncasecmp(buf, "Cookie: ", 8)) { - for (a = 0; a < strlen(buf); ++a) - if (buf[a] == ';') { - sprintf(hold, "Cookie: %s", &buf[a + 1]); - buf[a] = 0; - while (isspace(hold[8])) - strcpy(&hold[8], &hold[9]); - return(0); - } - } - - return(0); -} - /* * lingering_close() a`la Apache. see * http://www.apache.org/docs/misc/fin_wait_2.html for rationale */ - int lingering_close(int fd) { char buf[SIZ]; @@ -227,23 +188,42 @@ 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. + * Look for commonly-found probes of malware such as worms, viruses, trojans, and Microsoft Office. + * Short-circuit these requests so we don't have to send them through the full processing loop. */ -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); +int is_bogus(StrBuf *http_cmd) { + const char *url; + int i, max; + const 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 */ + "/nonexistenshit" /* Exploit found in the wild January 2009 */ + }; + + url = ChrPtr(http_cmd); + if (IsEmptyStr(url)) return(1); + ++url; + + max = sizeof(bogus_prefixes) / sizeof(char *); + + for (i=0; i= 8) && (strstr(ChrPtr(ReqLine), "/webcit/")) ) { + StrBufCutLeft(ReqLine, 7); + } + + /* Begin parsing the request. */ +#ifdef TECH_PREVIEW + if ((strncmp(ChrPtr(ReqLine), "/sslg", 5) != 0) && + (strncmp(ChrPtr(ReqLine), "/static/", 8) != 0) && + (strncmp(ChrPtr(ReqLine), "/tiny_mce/", 10) != 0) && + (strncmp(ChrPtr(ReqLine), "/wholist_section", 16) != 0) && + (strstr(ChrPtr(ReqLine), "wholist_section") == NULL)) { +#endif + lprintf(5, "HTTP: %s %s %s\n", ChrPtr(ReqType), ChrPtr(ReqLine), ChrPtr(HTTPVersion)); +#ifdef TECH_PREVIEW + } +#endif + + /** Check for bogus requests */ + if ((StrLength(HTTPVersion) == 0) || + (StrLength(ReqType) == 0) || + is_bogus(ReqLine)) { + StrBufPlain(ReqLine, HKEY("/404 HTTP/1.1")); + StrBufPlain(ReqType, HKEY("GET")); + } + FreeStrBuf(&HTTPVersion); + + /** * While we're at it, gracefully handle requests for the - * robots.txt file... + * 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"); + if (!strncasecmp(ChrPtr(ReqLine), "/robots.txt", 11)) { + StrBufPlain(ReqLine, + HKEY("/static/robots.txt" + "?force_close_session=yes HTTP/1.1")); + StrBufPlain(ReqType, HKEY("GET")); + } + else if (!strncasecmp(ChrPtr(ReqLine), "/favicon.ico", 12)) { + StrBufPlain(ReqLine, HKEY("/static/favicon.ico")); + StrBufPlain(ReqType, HKEY("GET")); } - /* 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. */ - else if ( (strcmp(buf, "/")) - && (strncasecmp(buf, "/listsub", 8)) - && (strncasecmp(buf, "/freebusy", 9)) + else if ( (StrLength(ReqLine) > 1 ) + && (strncasecmp(ChrPtr(ReqLine), "/listsub", 8)) + && (strncasecmp(ChrPtr(ReqLine), "/freebusy", 9)) + && (strncasecmp(ChrPtr(ReqLine), "/do_logout", 10)) + && (strncasecmp(ChrPtr(ReqLine), "/groupdav", 9)) + && (strncasecmp(ChrPtr(ReqLine), "/static", 7)) + && (strncasecmp(ChrPtr(ReqLine), "/rss", 4)) + && (strncasecmp(ChrPtr(ReqLine), "/404", 4)) && (got_cookie == 0)) { - strcpy(req->line, "GET /static/nocookies.html" - "?force_close_session=yes HTTP/1.0"); + StrBufPlain(ReqLine, + HKEY("/static/nocookies.html" + "?force_close_session=yes")); } - /* 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, "/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) { + for (sptr = SessionList; + ((sptr != NULL) && (TheSession == NULL)); + sptr = sptr->next) { + + /** If HTTP-AUTH, look for a session with matching credentials */ + if ( (!IsEmptyStr(httpauth_user)) + &&(!strcasecmp(ChrPtr(sptr->httpauth_user), httpauth_user)) + &&(!strcasecmp(ChrPtr(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) { lprintf(3, "Creating a new session\n"); - TheSession = (struct wcsession *) - malloc(sizeof(struct wcsession)); - memset(TheSession, 0, sizeof(struct wcsession)); + TheSession = (wcsession *) + malloc(sizeof(wcsession)); + memset(TheSession, 0, sizeof(wcsession)); + TheSession->headers = HTTPHeaders; TheSession->serv_sock = (-1); TheSession->chat_sock = (-1); - TheSession->wc_session = GenerateSessionID(); - pthread_mutex_init(&TheSession->SessionMutex, NULL); + + /* 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; + } + TheSession->httpauth_user = NewStrBufPlain(httpauth_user, -1); + TheSession->httpauth_pass = NewStrBufPlain(httpauth_user, -1); + + pthread_setspecific(MyConKey, (void *)TheSession); + session_new_modules(TheSession); + + pthread_mutex_init(&TheSession->SessionMutex, NULL); pthread_mutex_lock(&SessionListMutex); + TheSession->nonce = rand(); TheSession->next = SessionList; + TheSession->is_mobile = -1; SessionList = TheSession; pthread_mutex_unlock(&SessionListMutex); + session_is_new = 1; } + TheSession->headers = HTTPHeaders; /* * A future improvement might be to check the session integrity @@ -391,16 +514,50 @@ void context_loop(int sock) */ 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 */ + TheSession->http_sock = *sock; + TheSession->gzip_ok = gzip_ok; + + session_attach_modules(TheSession); + + session_loop(ReqLine, ReqType, Buf, &Pos); /* do transaction */ + session_detach_modules(TheSession); + + TheSession->headers = NULL; pthread_mutex_unlock(&TheSession->SessionMutex); /* unbind */ /* Free the request buffer */ -bail: while (req != NULL) { - hptr = req->next; - free(req); - req = hptr; - } + DeleteHash(&HTTPHeaders); + FreeStrBuf(&ReqLine); + FreeStrBuf(&ReqType); + FreeStrBuf(&Buf); +} + +void tmplput_nonce(StrBuf *Target, WCTemplputParams *TP) +{ + wcsession *WCC = WC; + StrBufAppendPrintf(Target, "%ld", + (WCC != NULL)? WCC->nonce:0); +} + +void tmplput_current_user(StrBuf *Target, WCTemplputParams *TP) +{ + StrBufAppendTemplate(Target, TP, WC->wc_fullname, 0); +} + +void tmplput_current_room(StrBuf *Target, WCTemplputParams *TP) +{ + StrBufAppendTemplate(Target, TP, WC->wc_roomname, 0); +} + + + +void +InitModule_CONTEXT +(void) +{ + RegisterNamespace("CURRENT_USER", 0, 1, tmplput_current_user, CTX_NONE); + RegisterNamespace("CURRENT_ROOM", 0, 1, tmplput_current_room, CTX_NONE); + RegisterNamespace("NONCE", 0, 0, tmplput_nonce, 0); }