X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fcontext_loop.c;h=1f0cf05e963c74dea87b103619cc6b92791fc031;hb=b4ad306de9560ec731db113ed509a1172209c444;hp=12a641e1aa8a8768c08ee14ea4fd1decbb4bed28;hpb=59578851dc724c78ba658202856c9d9734b26630;p=citadel.git diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 12a641e1a..1f0cf05e9 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -8,43 +8,20 @@ * */ -#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... */ 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() */ +/* + * free the memory used for viewing atachments + */ void free_attachments(struct wcsession *sess) { struct wc_attachment *att; @@ -57,6 +34,39 @@ void free_attachments(struct wcsession *sess) { } +void DestroySession(struct 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); + } + free_attachments((*sessions_to_kill)); + free_march_list((*sessions_to_kill)); + DeleteHash(&((*sessions_to_kill)->hash_prefs)); + DeleteHash(&((*sessions_to_kill)->IconBarSetttings)); + DeleteHash(&((*sessions_to_kill)->ServCfg)); + FreeStrBuf(&((*sessions_to_kill)->UrlFragment1)); + FreeStrBuf(&((*sessions_to_kill)->UrlFragment2)); + FreeStrBuf(&((*sessions_to_kill)->WBuf)); + FreeStrBuf(&((*sessions_to_kill)->HBuf)); + FreeStrBuf(&((*sessions_to_kill)->CLineBuf)); + free((*sessions_to_kill)); + (*sessions_to_kill) = NULL; +} + +void shutdown_sessions(void) +{ + struct wcsession *sptr; + + for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { + sptr->killthis = 1; + } +} + void do_housekeeping(void) { struct wcsession *sptr, *ss; @@ -64,7 +74,7 @@ void do_housekeeping(void) int num_sessions = 0; static int num_threads = MIN_WORKER_THREADS; - /* + /** * Lock the session list, moving any candidates for euthanasia into * a separate list. */ @@ -73,16 +83,16 @@ void do_housekeeping(void) for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { ++num_sessions; - /* Kill idle sessions */ + /** Kill idle sessions */ if ((time(NULL) - (sptr->lastreq)) > (time_t) WEBCIT_TIMEOUT) { sptr->killthis = 1; } - /* Remove sessions flagged for kill */ + /** Remove sessions flagged for kill */ if (sptr->killthis) { - /* remove session from linked list */ + /** remove session from linked list */ if (sptr == SessionList) { SessionList = SessionList->next; } @@ -98,26 +108,21 @@ void do_housekeeping(void) } pthread_mutex_unlock(&SessionListMutex); - /* + /** * 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); - } - free_attachments(sessions_to_kill); pthread_mutex_unlock(&sessions_to_kill->SessionMutex); sptr = sessions_to_kill->next; - free(sessions_to_kill); - sessions_to_kill = sessions_to_kill->next; + + DestroySession(&sessions_to_kill); + sessions_to_kill = sptr; --num_sessions; } - /* + /** * If there are more sessions than threads, then we should spawn * more threads ... up to a predefined maximum. */ @@ -131,8 +136,8 @@ void do_housekeeping(void) } -/* - * Wake up occasionally and clean house +/** + * \brief Wake up occasionally and clean house */ void housekeeping_loop(void) { @@ -143,11 +148,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) @@ -165,38 +171,56 @@ 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 { - safestrncpy(buf, hold, SIZ); - } - 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); - } - } +////int req_gets(int *sock, char *buf, char *hold, size_t hlen) +////{ +//// int a, b; +//// +//// if (IsEmptyStr(hold)) { +//// strcpy(buf, ""); +//// a = client_getln(sock, buf, SIZ); +//// if (a<1) return(-1); +//// } else { +//// safestrncpy(buf, hold, SIZ); +//// } +//// strcpy(hold, ""); +//// +//// if (!strncasecmp(buf, "Cookie: ", 8)) { +//// int len; +//// len = strlen(buf); +//// for (a = 0; a < len; ++a) +//// if (buf[a] == ';') { +//// // we don't refresh len, because of we +//// // only exit from here. +//// snprintf(hold, hlen, "Cookie: %s", &buf[a + 1]); +//// buf[a] = 0; +//// b = 8; +//// while (isspace(hold[b])) +//// b++; +//// +//// memmove(&hold[8], &hold[b], len - b + 1); +//// return(0); +//// } +//// } +//// +//// return(0); +////} - return(0); + +/* + * Collapse multiple cookies on one line + */ +int ReqGetStrBuf(int *sock, StrBuf *Target, StrBuf *buf) +{ + + return ClientGetLine(sock, Target, buf); } + + /* * 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]; @@ -231,24 +255,44 @@ 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); +int is_bogus(StrBuf *http_cmd) { + const char *url; + int i, max; + + url = ChrPtr(http_cmd); + if (IsEmptyStr(url)) 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; inext = hptr; - hptr->next = NULL; - last = hptr; + StrBufExtract_token(HeaderName, Line, 0, ':'); + //// TODO: filter bad chars! - safestrncpy(hptr->line, buf, sizeof hptr->line); + pchs = ChrPtr(Line); + pch = pchs + StrLength(HeaderName) + 1; + pche = pchs + StrLength(Line); + while (isspace(*pch) && (pch < pche)) + pch ++; + StrBufCutLeft(Line, pch - pchs); - } while (strlen(buf) > 0); + StrBufUpCase(HeaderName); + Put(HTTPHeaders, SKEY(HeaderName), Line, HFreeStrBuf); + LastLine = Line; + } while (LineLen > 0); + FreeStrBuf(&HeaderName); - safestrncpy(buf, req->line, sizeof buf); - lprintf(5, "HTTP: %s\n", buf); +//// dbg_PrintHash(HTTPHeaders, nix, NULL); - /* 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. + /** + * Can we compress? */ - remove_token(buf, 0, ' '); - if (buf[1]==' ') buf[1]=0; + if (GetHash(HTTPHeaders, HKEY("ACCEPT-ENCODING"), &vLine) && + (vLine != NULL)) { + buf = ChrPtr((StrBuf*)vLine); + if (strstr(&buf[16], "gzip")) { + gzip_ok = 1; + } + } - /* + /** + * Browser-based sessions use cookies for session authentication + */ + if (GetHash(HTTPHeaders, HKEY("COOKIE"), &vLine) && + (vLine != NULL)) { + cookie_to_stuff(vLine, &desired_session, + NULL, 0, NULL, 0, NULL, 0); + got_cookie = 1; + } + + /** + * GroupDAV-based sessions use HTTP authentication + */ + if (GetHash(HTTPHeaders, HKEY("AUTHORIZATION"), &vLine) && + (vLine != NULL)) { + Line = (StrBuf*)vLine; + if (strncasecmp(ChrPtr(Line), "Basic ", 6)) { + StrBufCutLeft(Line, 6); + CtdlDecodeBase64(httpauth_string, ChrPtr(Line), StrLength(Line)); + extract_token(httpauth_user, httpauth_string, 0, ':', sizeof httpauth_user); + extract_token(httpauth_pass, httpauth_string, 1, ':', sizeof httpauth_pass); + } + else + lprintf(1, "Authentication sheme not supported! [%s]\n", ChrPtr(Line)); + } + + if (GetHash(HTTPHeaders, HKEY("IF-MODIFIED-SINCE"), &vLine) && + (vLine != NULL)) { + if_modified_since = httpdate_to_timestamp((StrBuf*)vLine); + } + + if (GetHash(HTTPHeaders, HKEY("ACCEPT-LANGUAGE"), &vLine) && + (vLine != NULL)) { + accept_language = (StrBuf*) vLine; + } + + + /** + * 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. + */ + + ReqType = NewStrBuf(); + HTTPVersion = NewStrBuf(); + StrBufExtract_token(HTTPVersion, ReqLine, 2, ' '); + StrBufExtract_token(ReqType, ReqLine, 0, ' '); + StrBufCutLeft(ReqLine, StrLength(ReqType) + 1); + StrBufCutRight(ReqLine, StrLength(HTTPVersion) + 1); + + if ((StrLength(ReqLine) >= 8) && + (ptr = strstr(ChrPtr(ReqLine), "/webcit/"), /*< Handle "/webcit/" */ + (ptr != NULL))) { + 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), "/wholist_section", 16) != 0)) { +#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 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(buf, "/favicon.ico", 12)) { - strcpy(req->line, "GET /static/favicon.ico"); + 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)) - && (strncasecmp(buf, "/do_logout", 10)) - && (strncasecmp(buf, "/groupdav", 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")); } - /* + /** * See if there's an existing session open with the desired ID or user/pass */ TheSession = NULL; @@ -367,14 +517,14 @@ void context_loop(int sock) pthread_mutex_lock(&SessionListMutex); for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - /* If HTTP-AUTH, look for a session with matching credentials */ - if ( (strlen(httpauth_user) > 0) + /** If HTTP-AUTH, look for a session with matching credentials */ + if ( (!IsEmptyStr(httpauth_user)) &&(!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 cookie-session, look for a session with matching session ID */ if ( (desired_session != 0) && (sptr->wc_session == desired_session)) { TheSession = sptr; } @@ -383,7 +533,7 @@ void context_loop(int sock) pthread_mutex_unlock(&SessionListMutex); } - /* + /** * Create a new session if we have to */ if (TheSession == NULL) { @@ -393,15 +543,35 @@ 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); + TheSession->hash_prefs = NewHash(1,NULL); /* Get a hash table for the user preferences */ pthread_mutex_init(&TheSession->SessionMutex, NULL); - pthread_mutex_lock(&SessionListMutex); + TheSession->urlstrings = NULL; + TheSession->vars = NULL; + TheSession->nonce = rand(); + TheSession->WBuf = NULL; + TheSession->CLineBuf = NewStrBuf(); TheSession->next = SessionList; + TheSession->is_mobile = -1; SessionList = TheSession; pthread_mutex_unlock(&SessionListMutex); + session_is_new = 1; } /* @@ -414,20 +584,53 @@ void context_loop(int sock) */ pthread_mutex_lock(&TheSession->SessionMutex); /* bind */ pthread_setspecific(MyConKey, (void *)TheSession); - TheSession->http_sock = sock; + + TheSession->urlstrings = NewHash(1,NULL); + TheSession->vars = NewHash(1,NULL); + TheSession->http_sock = *sock; TheSession->lastreq = time(NULL); /* log */ - session_loop(req); /* do transaction */ + 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(HTTPHeaders, ReqLine, ReqType, Buf); /* do transaction */ +#ifdef ENABLE_NLS + stop_selected_language(); /* unset locale */ +#endif + DeleteHash(&TheSession->urlstrings); + DeleteHash(&TheSession->vars); + FreeStrBuf(&TheSession->WBuf); + FreeStrBuf(&TheSession->HBuf); + + pthread_mutex_unlock(&TheSession->SessionMutex); /* unbind */ /* Free the request buffer */ -bail: while (req != NULL) { - hptr = req->next; - free(req); - req = hptr; - } - - /* Free up any session-local substitution variables which + DeleteHash(&HTTPHeaders); + FreeStrBuf(&ReqLine); + FreeStrBuf(&ReqType); + FreeStrBuf(&Buf); + /* + * Free up any session-local substitution variables which * were set during this transaction */ - clear_local_substs(); + + +} + +void tmpl_nonce(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType) +{ + struct wcsession *WCC = WC; + StrBufAppendPrintf(Target, "%ld", + (WCC != NULL)? WCC->nonce:0); +} + +void +InitModule_CONTEXT +(void) +{ + RegisterNamespace("NONCE", 0, 0, tmpl_nonce, 0); }