From: Wilfried Göesgens Date: Sun, 14 Sep 2008 16:50:08 +0000 (+0000) Subject: * do linebuffered/non-blocking reads from http requests X-Git-Tag: v7.86~1937 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=7c6a8c0e7caf1da50e235049d4c4834aef1ca1be * do linebuffered/non-blocking reads from http requests * migrate HTTP-Headers to a Hashtable * adjust the rest of the party to work with this. --- diff --git a/webcit/calendar.c b/webcit/calendar.c index c9d40bc8e..b966c264b 100644 --- a/webcit/calendar.c +++ b/webcit/calendar.c @@ -1072,7 +1072,7 @@ void save_event(void) { /* * Anonymous request of freebusy data for a user */ -void do_freebusy(char *req) { +void do_freebusy(const char *req) { char who[SIZ]; char buf[SIZ]; int len; diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 4cf0b5bfe..bdf17054d 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -53,7 +53,7 @@ void DestroySession(struct wcsession **sessions_to_kill) 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; } @@ -171,40 +171,52 @@ int GenerateSessionID(void) /* * Collapse multiple cookies on one line */ -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); - } - } +////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 @@ -249,12 +261,12 @@ int lingering_close(int fd) * * \param http_cmd The HTTP request to check */ -int is_bogus(char *http_cmd) { - char *url; +int is_bogus(StrBuf *http_cmd) { + const char *url; int i, max; - url = strstr(http_cmd, " "); - if (url == NULL) return(1); + url = ChrPtr(http_cmd); + if (IsEmptyStr(url)) return(1); ++url; char *bogus_prefixes[] = { @@ -277,6 +289,7 @@ int is_bogus(char *http_cmd) { } +const char *nix(void *vptr) {return ChrPtr( (StrBuf*)vptr);} /** * \brief handle one request @@ -293,10 +306,7 @@ int is_bogus(char *http_cmd) { */ void context_loop(int *sock) { - struct httprequest *req = NULL; - struct httprequest *last = NULL; - struct httprequest *hptr; - char buf[SIZ], hold[SIZ]; + const char *buf; int desired_session = 0; int got_cookie = 0; int gzip_ok = 0; @@ -304,9 +314,14 @@ void context_loop(int *sock) char httpauth_string[1024]; char httpauth_user[1024]; char httpauth_pass[1024]; - char accept_language[256]; char *ptr = NULL; int session_is_new = 0; + int nLine = 0; + int LineLen; + void *vLine; + StrBuf *Buf, *Line, *LastLine, *HeaderName, *ReqLine, *accept_language, *ReqType, *HTTPVersion; + const char *pch, *pchs, *pche; + HashList *HTTPHeaders; strcpy(httpauth_string, ""); strcpy(httpauth_user, DEFAULT_HTTPAUTH_USER); @@ -315,111 +330,161 @@ void context_loop(int *sock) /** * Find out what it is that the web browser is asking for */ - memset(hold, 0, sizeof(hold)); + HeaderName = NewStrBuf(); + Buf = NewStrBuf(); + LastLine = NULL; + HTTPHeaders = NewHash(1, NULL); + /** + * Read in the request + */ do { - if (req_gets(sock, buf, hold, SIZ) < 0) return; - - /** - * Can we compress? - */ - if (!strncasecmp(buf, "Accept-encoding:", 16)) { - if (strstr(&buf[16], "gzip")) { - gzip_ok = 1; - } - } + nLine ++; + Line = NewStrBuf(); + if (ReqGetStrBuf(sock, Line, Buf) < 0) return; - /** - * Browser-based sessions use cookies for session authentication - */ - if (!strncasecmp(buf, "Cookie: webcit=", 15)) { - cookie_to_stuff(&buf[15], &desired_session, - NULL, 0, NULL, 0, NULL, 0); - got_cookie = 1; + LineLen = StrLength(Line); + + if (nLine == 1) { + ReqLine = Line; + continue; + } + if (LineLen == 0) { + FreeStrBuf(&Line); + continue; } - /** - * 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); + /** Do we need to Unfold? */ + if ((LastLine != NULL) && + (isspace(*ChrPtr(Line)))) { + pch = pchs = ChrPtr(Line); + pche = pchs + StrLength(Line); + while (isspace(pch) && (pch < pche)) + pch ++; + StrBufCutLeft(Line, pch - pchs); + StrBufAppendBuf(LastLine, Line, 0); + FreeStrBuf(&Line); + continue; } - if (!strncasecmp(buf, "If-Modified-Since: ", 19)) { - if_modified_since = httpdate_to_timestamp(&buf[19]); + StrBufExtract_token(HeaderName, Line, 0, ':'); + //// TODO: filter bad chars! + + pchs = ChrPtr(Line); + pch = pchs + StrLength(HeaderName) + 1; + pche = pchs + StrLength(Line); + while (isspace(*pch) && (pch < pche)) + pch ++; + StrBufCutLeft(Line, pch - pchs); + + StrBufUpCase(HeaderName); + Put(HTTPHeaders, SKEY(HeaderName), Line, HFreeStrBuf); + LastLine = Line; + } while (LineLen > 0); + FreeStrBuf(&HeaderName); + +//// dbg_PrintHash(HTTPHeaders, nix, NULL); + + + /** + * Can we compress? + */ + 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; + } - if (!strncasecmp(buf, "Accept-Language: ", 17)) { - safestrncpy(accept_language, &buf[17], sizeof accept_language); + /** + * 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)); + } - /** - * Read in the request - */ - hptr = (struct httprequest *) - malloc(sizeof(struct httprequest)); - if (req == NULL) - req = hptr; - else - last->next = hptr; - hptr->next = NULL; - last = hptr; + if (GetHash(HTTPHeaders, HKEY("IF-MODIFIED-SINCE"), &vLine) && + (vLine != NULL)) { + if_modified_since = httpdate_to_timestamp((StrBuf*)vLine); + } - safestrncpy(hptr->line, buf, sizeof hptr->line); + if (GetHash(HTTPHeaders, HKEY("ACCEPT-LANGUAGE"), &vLine) && + (vLine != NULL)) { + accept_language = (StrBuf*) vLine; + } - } while (!IsEmptyStr(buf)); /** * 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); + 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) > 10) && + (ptr = strstr(ChrPtr(ReqLine), "/webcit "), /*< Handle "/webcit" */ + (ptr != NULL))) { + StrBufCutLeft(ReqLine, 6); } - safestrncpy(buf, req->line, sizeof buf); /** Begin parsing the request. */ #ifdef TECH_PREVIEW - if ((strncmp(req->line+4, "/sslg", 5) != 0) && - (strncmp(req->line+4, "/static/", 8) != 0) && - (strncmp(req->line+4, "/wholist_section", 16) != 0)) { + 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\n", buf); + lprintf(5, "HTTP: %s %s %s\n", ChrPtr(ReqType), ChrPtr(ReqLine), ChrPtr(HTTPVersion)); #ifdef TECH_PREVIEW } #endif /** Check for bogus requests */ - if (is_bogus(buf)) { - strcpy(req->line, "GET /404 HTTP/1.1"); - strcpy(buf, "GET /404 HTTP/1.1"); + if ((StrLength(HTTPVersion) == 0) || + (StrLength(ReqType) == 0) || + is_bogus(ReqLine)) { + StrBufPlain(ReqLine, HKEY("/404 HTTP/1.1")); + StrBufPlain(ReqType, HKEY("GET")); } - - /** - * Strip out the method, leaving the URL up front... - */ - remove_token(buf, 0, ' '); - if (buf[1]==' ') buf[1]=0; + 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.1"); + 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")); } /** @@ -428,17 +493,18 @@ void context_loop(int *sock) * 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)) - && (strncasecmp(buf, "/static", 7)) - && (strncasecmp(buf, "/rss", 4)) - && (strncasecmp(buf, "/404", 4)) + 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.1"); + StrBufPlain(ReqLine, + HKEY("/static/nocookies.html" + "?force_close_session=yes")); } /** @@ -499,7 +565,9 @@ void context_loop(int *sock) 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; @@ -527,7 +595,7 @@ void context_loop(int *sock) } go_selected_language(); /* set locale */ #endif - session_loop(req); /* do transaction */ + session_loop(HTTPHeaders, ReqLine, ReqType, Buf); /* do transaction */ #ifdef ENABLE_NLS stop_selected_language(); /* unset locale */ #endif @@ -540,12 +608,10 @@ void context_loop(int *sock) pthread_mutex_unlock(&TheSession->SessionMutex); /* unbind */ /* Free the request buffer */ - while (req != NULL) { - hptr = req->next; - free(req); - req = hptr; - } - + DeleteHash(&HTTPHeaders); + FreeStrBuf(&ReqLine); + FreeStrBuf(&ReqType); + FreeStrBuf(&Buf); /* * Free up any session-local substitution variables which * were set during this transaction diff --git a/webcit/cookie_conversion.c b/webcit/cookie_conversion.c index fd3082b79..e891bccc6 100644 --- a/webcit/cookie_conversion.c +++ b/webcit/cookie_conversion.c @@ -46,7 +46,7 @@ void stuff_to_cookie(char *cookie, size_t clen, int session, * \param len the length of the string * \return the corrosponding integer value */ -int xtoi(char *in, size_t len) +int xtoi(const char *in, size_t len) { int val = 0; char c = 0; @@ -77,18 +77,23 @@ int xtoi(char *in, size_t len) * \param room the room he is in * \param room_len the length of the room string */ -void cookie_to_stuff(char *cookie, int *session, +void cookie_to_stuff(StrBuf *cookie, int *session, char *user, size_t user_len, char *pass, size_t pass_len, char *room, size_t room_len) { + const char *pch; char buf[SIZ]; int i, len; + if (strncmp(ChrPtr(cookie), HKEY("webcit=")) == 0) + StrBufCutLeft(cookie, 7); + strcpy(buf, ""); - len = strlen(cookie) / 2; + len = StrLength(cookie) / 2; + pch = ChrPtr(cookie); for (i=0; irbio, BIO_NOCLOSE); bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl), &alg_bits); - lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n", + lprintf(15, "SSL/TLS using %s on %s (%d of %d bits)\n", SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)), SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)), bits, alg_bits); pthread_setspecific(ThreadSSL, newssl); - lprintf(3, "SSL started\n"); + lprintf(15, "SSL started\n"); return(0); } @@ -438,7 +438,7 @@ void endtls(void) if (THREADSSL == NULL) return; - lprintf(5, "Ending SSL/TLS\n"); + lprintf(15, "Ending SSL/TLS\n"); SSL_shutdown(THREADSSL); ctx = SSL_get_SSL_CTX(THREADSSL); @@ -524,7 +524,7 @@ void client_write_ssl(const StrBuf *Buf) * \param timeout how long should we wait? * \returns what??? */ -int client_read_ssl(char *buf, int bytes, int timeout) +int client_read_sslbuffer(StrBuf *buf, int timeout) { #if 0 fd_set rfds; @@ -532,13 +532,13 @@ int client_read_ssl(char *buf, int bytes, int timeout) int retval; int s; #endif - int len, rlen; + char sbuf[16384]; /**< Openssl communicates in 16k blocks, so lets speak its native tongue. */ + int rlen; char junk[1]; if (THREADSSL == NULL) return(0); - len = 0; - while (len < bytes) { + while (1) { #if 0 /** * This code is disabled because we don't need it when @@ -562,7 +562,7 @@ int client_read_ssl(char *buf, int bytes, int timeout) lprintf(9, "SSL_write in client_read\n"); } } - rlen = SSL_read(THREADSSL, &buf[len], bytes - len); + rlen = SSL_read(THREADSSL, sbuf, sizeof(sbuf)); if (rlen < 1) { long errval; @@ -576,7 +576,8 @@ int client_read_ssl(char *buf, int bytes, int timeout) endtls(); return (0); } - len += rlen; + StrBufAppendBufPlain(buf, sbuf, rlen, 0); + return(1); } return (1); } diff --git a/webcit/fmt_date.c b/webcit/fmt_date.c index ac4af4337..7ae90fbb3 100644 --- a/webcit/fmt_date.c +++ b/webcit/fmt_date.c @@ -147,14 +147,14 @@ void fmt_time(char *buf, time_t thetime) * FIXME won't read asctime * Doesn't understand timezone, but we only should be using GMT/UTC anyway */ -time_t httpdate_to_timestamp(char *buf) +time_t httpdate_to_timestamp(StrBuf *buf) { time_t t = 0; struct tm tt; - char *c; + const char *c; /** Skip day of week, to number */ - for (c = buf; *c != ' '; c++) + for (c = ChrPtr(buf); *c != ' '; c++) ; c++; diff --git a/webcit/gettext.c b/webcit/gettext.c index ea810deb3..2c12eff80 100644 --- a/webcit/gettext.c +++ b/webcit/gettext.c @@ -51,7 +51,7 @@ typedef struct _lang_pref{ * \param LocaleString the string from the browser http headers */ -void httplang_to_locale(char *LocaleString) +void httplang_to_locale(StrBuf *LocaleString) { LangStruct wanted_locales[SEARCH_LANG]; LangStruct *ls; @@ -63,24 +63,32 @@ void httplang_to_locale(char *LocaleString) int av; int nBest; int nParts; - char search[1024]; + StrBuf *Buf = NULL; + StrBuf *SBuf; - safestrncpy(search, LocaleString, sizeof search); - nParts=num_tokens(search,','); + nParts=StrBufNum_tokens(LocaleString,','); for (i=0; ((i1) { int sbuflen, k; - extract_token(&sbuf[0],&buf[0], 1,'=',16); + StrBufExtract_token(SBuf,Buf, 1,'='); sbuflen=strlen(&sbuf[0]); for (k=0; kpriority=atol(&sbuf[0]); @@ -89,12 +97,12 @@ void httplang_to_locale(char *LocaleString) ls->priority=1000; } /** get the locale part */ - extract_token(&sbuf[0],&buf[0],0,';',16); + StrBufExtract_token(SBuf ,Buf, 0, ';'); /** get the lang part, which should be allways there */ - extract_token(&ls->lang[0],&sbuf[0],0,'-',16); + extract_token(&ls->lang[0], ChrPtr(SBuf), 0, '-', 16); /** get the area code if any. */ - if (num_tokens(&sbuf[0],'-')>1) { - extract_token(&ls->region[0],&sbuf[0],1,'-',16); + if (StrBufNum_tokens(SBuf,'-') > 1) { + extract_token(&ls->region[0],ChrPtr(SBuf),1,'-',16); } else { /** no ara code? use lang code */ blen=strlen(&ls->lang[0]); @@ -149,6 +157,8 @@ void httplang_to_locale(char *LocaleString) } WC->selected_language=nBest; lprintf(9, "language found: %s\n", AvailLangLoaded[WC->selected_language]); + FreeStrBuf(&Buf); + FreeStrBuf(&SBuf); } /* TODO: we skip the language weighting so far. */ diff --git a/webcit/groupdav.h b/webcit/groupdav.h index be896646c..42709a5b7 100644 --- a/webcit/groupdav.h +++ b/webcit/groupdav.h @@ -14,12 +14,20 @@ struct epdata { void groupdav_common_headers(void); -void groupdav_main(struct httprequest *, char *, int, char *); -void groupdav_get(char *); -void groupdav_put(char *, char *, char *, char *, int); -void groupdav_delete(char *, char *); -void groupdav_propfind(char *, int, char *, char *); -void groupdav_options(char *); +void groupdav_main(HashList *HTTPHeaders, + StrBuf *DavPathname, + StrBuf *DavMethod, + StrBuf *dav_content_type, + int dav_content_length, + StrBuf *dav_content, + int Offset); +void groupdav_get(const char *dav_pathname); +void groupdav_put(const char *dav_pathname, char *dav_ifmatch, + const char *dav_content_type, StrBuf *dav_content, + int offset); +void groupdav_delete(StrBuf *dav_pathname, char *dav_ifmatch); +void groupdav_propfind(const char *dav_pathname, int dav_depth, StrBuf *dav_content_type, StrBuf *dav_content, int offset); +void groupdav_options(const char *dav_pathname); long locate_message_by_uid(char *); void groupdav_folder_list(void); void euid_escapize(char *, char *); diff --git a/webcit/groupdav_delete.c b/webcit/groupdav_delete.c index 8aa9f3b57..2a76687b1 100644 --- a/webcit/groupdav_delete.c +++ b/webcit/groupdav_delete.c @@ -13,7 +13,7 @@ /* * The pathname is always going to be /groupdav/room_name/euid */ -void groupdav_delete(char *dav_pathname, char *dav_ifmatch) { +void groupdav_delete(StrBuf *dav_pathname, char *dav_ifmatch) { char dav_roomname[SIZ]; char dav_uid[SIZ]; long dav_msgnum = (-1); @@ -22,20 +22,20 @@ void groupdav_delete(char *dav_pathname, char *dav_ifmatch) { int len; /* First, break off the "/groupdav/" prefix */ - remove_token(dav_pathname, 0, '/'); - remove_token(dav_pathname, 0, '/'); + StrBufRemove_token(dav_pathname, 0, '/'); + StrBufRemove_token(dav_pathname, 0, '/'); /* Now extract the message euid */ - n = num_tokens(dav_pathname, '/'); - extract_token(dav_uid, dav_pathname, n-1, '/', sizeof dav_uid); - remove_token(dav_pathname, n-1, '/'); + n = StrBufNum_tokens(dav_pathname, '/'); + extract_token(dav_uid, ChrPtr(dav_pathname), n-1, '/', sizeof dav_uid); + StrBufRemove_token(dav_pathname, n-1, '/'); /* What's left is the room name. Remove trailing slashes. */ - len = strlen(dav_pathname); - if (dav_pathname[len-1] == '/') { - dav_pathname[len-1] = 0; + len = StrLength(dav_pathname); + if ((len > 0) && (ChrPtr(dav_pathname)[len-1] == '/')) { + StrBufCutRight(dav_pathname, 1); } - strcpy(dav_roomname, dav_pathname); + strcpy(dav_roomname, ChrPtr(dav_pathname)); /* Go to the correct room. */ if (strcasecmp(WC->wc_roomname, dav_roomname)) { diff --git a/webcit/groupdav_get.c b/webcit/groupdav_get.c index 02d4ea26b..928a5669b 100644 --- a/webcit/groupdav_get.c +++ b/webcit/groupdav_get.c @@ -86,7 +86,7 @@ void extract_preferred(char *name, char *filename, char *partnum, char *disp, * /groupdav/room_name/euid (GroupDAV) * /groupdav/room_name (webcal) */ -void groupdav_get(char *dav_pathname) { +void groupdav_get(const char *dav_pathname) { char dav_roomname[1024]; char dav_uid[1024]; long dav_msgnum = (-1); diff --git a/webcit/groupdav_main.c b/webcit/groupdav_main.c index b03650b77..9d43f7ff3 100644 --- a/webcit/groupdav_main.c +++ b/webcit/groupdav_main.c @@ -84,46 +84,45 @@ void euid_unescapize(char *target, char *source) { /* * Main entry point for GroupDAV requests */ -void groupdav_main(struct httprequest *req, - char *dav_content_type, - int dav_content_length, - char *dav_content +void groupdav_main(HashList *HTTPHeaders, + StrBuf *DavPathname, + StrBuf *DavMethod, + StrBuf *dav_content_type, + int dav_content_length, + StrBuf *dav_content, + int Offset ) { - struct httprequest *rptr; - char dav_method[256]; - char dav_pathname[256]; + void *vLine; char dav_ifmatch[256]; int dav_depth; char *ds; int i, len; - strcpy(dav_method, ""); - strcpy(dav_pathname, ""); strcpy(dav_ifmatch, ""); dav_depth = 0; - for (rptr=req; rptr!=NULL; rptr=rptr->next) { - if (!strncasecmp(rptr->line, "Host: ", 6)) { - if (IsEmptyStr(WC->http_host)) { - safestrncpy(WC->http_host, &rptr->line[6], - sizeof WC->http_host); - } - } - if (!strncasecmp(rptr->line, "If-Match: ", 10)) { - safestrncpy(dav_ifmatch, &rptr->line[10], - sizeof dav_ifmatch); - } - if (!strncasecmp(rptr->line, "Depth: ", 7)) { - if (!strcasecmp(&rptr->line[7], "infinity")) { - dav_depth = 32767; - } - else if (!strcmp(&rptr->line[7], "0")) { - dav_depth = 0; - } - else if (!strcmp(&rptr->line[7], "1")) { - dav_depth = 1; - } - } + if (IsEmptyStr(WC->http_host) && + GetHash(HTTPHeaders, HKEY("HOST"), &vLine) && + (vLine != NULL)) { + safestrncpy(WC->http_host, ChrPtr((StrBuf*)vLine), + sizeof WC->http_host); + } + if (GetHash(HTTPHeaders, HKEY("IF-MATCH"), &vLine) && + (vLine != NULL)) { + safestrncpy(dav_ifmatch, ChrPtr((StrBuf*)vLine), + sizeof dav_ifmatch); + } + if (GetHash(HTTPHeaders, HKEY("DEPTH"), &vLine) && + (vLine != NULL)) { + if (!strcasecmp(ChrPtr((StrBuf*)vLine), "infinity")) { + dav_depth = 32767; + } + else if (strcmp(ChrPtr((StrBuf*)vLine), "0") == 0) { + dav_depth = 0; + } + else if (strcmp(ChrPtr((StrBuf*)vLine), "1") == 0) { + dav_depth = 1; + } } if (!WC->logged_in) { @@ -136,9 +135,9 @@ void groupdav_main(struct httprequest *req, return; } - extract_token(dav_method, req->line, 0, ' ', sizeof dav_method); - extract_token(dav_pathname, req->line, 1, ' ', sizeof dav_pathname); - unescape_input(dav_pathname); +// extract_token(dav_method, req->line, 0, ' ', sizeof dav_method); +// extract_token(dav_pathname, req->line, 1, ' ', sizeof dav_pathname); + //// TODO unescape_input(dav_pathname); /* If the request does not begin with "/groupdav", prepend it. If * we happen to introduce a double-slash, that's ok; we'll strip it @@ -155,7 +154,7 @@ void groupdav_main(struct httprequest *req, */ /* Remove any stray double-slashes in pathname */ - while (ds=strstr(dav_pathname, "//"), ds != NULL) { + while (ds=strstr(ChrPtr(DavPathname), "//"), ds != NULL) { strcpy(ds, ds+1); } @@ -186,8 +185,8 @@ void groupdav_main(struct httprequest *req, * experiment to determine what might be involved in supporting * other variants of DAV in the future. */ - if (!strcasecmp(dav_method, "OPTIONS")) { - groupdav_options(dav_pathname); + if (!strcasecmp(ChrPtr(DavMethod), "OPTIONS")) { + groupdav_options(ChrPtr(DavPathname)); return; } @@ -195,35 +194,36 @@ void groupdav_main(struct httprequest *req, * The PROPFIND method is basically used to list all objects in a * room, or to list all relevant rooms on the server. */ - if (!strcasecmp(dav_method, "PROPFIND")) { - groupdav_propfind(dav_pathname, dav_depth, - dav_content_type, dav_content); + if (!strcasecmp(ChrPtr(DavMethod), "PROPFIND")) { + groupdav_propfind(ChrPtr(DavPathname), dav_depth, + dav_content_type, dav_content, + Offset); return; } /* * The GET method is used for fetching individual items. */ - if (!strcasecmp(dav_method, "GET")) { - groupdav_get(dav_pathname); + if (!strcasecmp(ChrPtr(DavMethod), "GET")) { + groupdav_get(ChrPtr(DavPathname)); return; } /* * The PUT method is used to add or modify items. */ - if (!strcasecmp(dav_method, "PUT")) { - groupdav_put(dav_pathname, dav_ifmatch, - dav_content_type, dav_content, - dav_content_length); + if (!strcasecmp(ChrPtr(DavMethod), "PUT")) { + groupdav_put(ChrPtr(DavPathname), dav_ifmatch, + ChrPtr(dav_content_type), dav_content, + Offset); return; } /* * The DELETE method kills, maims, and destroys. */ - if (!strcasecmp(dav_method, "DELETE")) { - groupdav_delete(dav_pathname, dav_ifmatch); + if (!strcasecmp(ChrPtr(DavMethod), "DELETE")) { + groupdav_delete(DavPathname, dav_ifmatch); return; } @@ -234,7 +234,7 @@ void groupdav_main(struct httprequest *req, groupdav_common_headers(); hprintf("Content-Type: text/plain\r\n"); wprintf("GroupDAV method \"%s\" is not implemented.\r\n", - dav_method); + ChrPtr(DavMethod)); end_burst(); } diff --git a/webcit/groupdav_options.c b/webcit/groupdav_options.c index ac902331c..366f5ffe3 100644 --- a/webcit/groupdav_options.c +++ b/webcit/groupdav_options.c @@ -12,7 +12,7 @@ /* * The pathname is always going to be /groupdav/room_name/msg_num */ -void groupdav_options(char *dav_pathname) { +void groupdav_options(const char *dav_pathname) { char dav_roomname[256]; char dav_uid[256]; long dav_msgnum = (-1); diff --git a/webcit/groupdav_propfind.c b/webcit/groupdav_propfind.c index f9b2ce0d7..6da1f0e85 100644 --- a/webcit/groupdav_propfind.c +++ b/webcit/groupdav_propfind.c @@ -62,7 +62,7 @@ long locate_message_by_uid(char *uid) { * List rooms (or "collections" in DAV terminology) which contain * interesting groupware objects. */ -void groupdav_collection_list(char *dav_pathname, int dav_depth) +void groupdav_collection_list(const char *dav_pathname, int dav_depth) { char buf[256]; char roomname[256]; @@ -227,7 +227,7 @@ void groupdav_collection_list(char *dav_pathname, int dav_depth) /* * The pathname is always going to be /groupdav/room_name/msg_num */ -void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type, char *dav_content) { +void groupdav_propfind(const char *dav_pathname, int dav_depth, StrBuf *dav_content_type, StrBuf *dav_content, int offset) { char dav_roomname[256]; char dav_uid[256]; char msgnum[256]; diff --git a/webcit/groupdav_put.c b/webcit/groupdav_put.c index b69114c2f..cbb37f385 100644 --- a/webcit/groupdav_put.c +++ b/webcit/groupdav_put.c @@ -15,7 +15,7 @@ * component. This would be for webcal:// 'publish' operations, not * for GroupDAV. */ -void groupdav_put_bigics(char *dav_content, int dav_content_length) +void groupdav_put_bigics(StrBuf *dav_content, int offset) { char buf[1024]; @@ -37,7 +37,7 @@ void groupdav_put_bigics(char *dav_content, int dav_content_length) return; } - serv_write(dav_content, dav_content_length); + serv_write(ChrPtr(dav_content) + offset, StrLength(dav_content) - offset); serv_printf("\n000"); /* Report success and not much else. */ @@ -54,10 +54,10 @@ void groupdav_put_bigics(char *dav_content, int dav_content_length) * /groupdav/room_name/euid (GroupDAV) * /groupdav/room_name (webcal) */ -void groupdav_put(char *dav_pathname, char *dav_ifmatch, - char *dav_content_type, char *dav_content, - int dav_content_length -) { +void groupdav_put(const char *dav_pathname, char *dav_ifmatch, + const char *dav_content_type, StrBuf *dav_content, + int offset) +{ char dav_roomname[1024]; char dav_uid[1024]; long new_msgnum = (-2L); @@ -119,7 +119,7 @@ void groupdav_put(char *dav_pathname, char *dav_ifmatch, /** PUT on the collection itself uploads an ICS of the entire collection. */ if (!strcasecmp(dav_uid, "")) { - groupdav_put_bigics(dav_content, dav_content_length); + groupdav_put_bigics(dav_content, offset); return; } @@ -142,7 +142,7 @@ void groupdav_put(char *dav_pathname, char *dav_ifmatch, /* Send the content to the Citadel server */ serv_printf("Content-type: %s\n\n", dav_content_type); - serv_puts(dav_content); + serv_puts(ChrPtr(dav_content) + offset); serv_puts("\n000"); /* Fetch the reply from the Citadel server */ diff --git a/webcit/rss.c b/webcit/rss.c index fb6cb5926..696ed019c 100644 --- a/webcit/rss.c +++ b/webcit/rss.c @@ -47,7 +47,7 @@ void display_rss_control(char *reply_to, char *subject) * \param roomname the room we sould print out as rss * \param request_method the way the rss is requested???? */ -void display_rss(char *roomname, char *request_method) +void display_rss(char *roomname, StrBuf *request_method) { int nummsgs; int a, b; @@ -146,7 +146,7 @@ void display_rss(char *roomname, char *request_method) hprintf("Content-Type: application/rss+xml\r\n"); hprintf("Server: %s\r\n", PACKAGE_STRING); hprintf("Connection: close\r\n"); - if (!strcasecmp(request_method, "HEAD")) + if (!strcasecmp(ChrPtr(request_method), "HEAD")) return; /* upload = NULL; WCC->is_mobile = 0; - hptr = req; - if (hptr == NULL) return; - - safestrncpy(cmd, hptr->line, sizeof cmd); - hptr = hptr->next; - extract_token(request_method, cmd, 0, ' ', sizeof request_method); - extract_token(pathname, cmd, 1, ' ', sizeof pathname); - /** Figure out the action */ index[0] = action; sizes[0] = sizeof action; @@ -1397,7 +1385,7 @@ void session_loop(struct httprequest *req) nEmpty = 0; for ( a = 0; a < 9; ++a) { - extract_token(index[a], pathname, a + 1, '/', sizes[a]); + extract_token(index[a], ChrPtr(ReqLine), a + 1, '/', sizes[a]); if (strstr(index[a], "?")) *strstr(index[a], "?") = 0; if (strstr(index[a], "&")) *strstr(index[a], "&") = 0; if (strstr(index[a], " ")) *strstr(index[a], " ") = 0; @@ -1407,113 +1395,109 @@ void session_loop(struct httprequest *req) nEmpty++; } - while (hptr != NULL) { - safestrncpy(buf, hptr->line, sizeof buf); - /* lprintf(9, "HTTP HEADER: %s\n", buf); */ - hptr = hptr->next; - if (!strncasecmp(buf, "Cookie: webcit=", 15)) { - safestrncpy(cookie, &buf[15], sizeof cookie); - cookie_to_stuff(cookie, NULL, - c_username, sizeof c_username, - c_password, sizeof c_password, - c_roomname, sizeof c_roomname); - } - else if (!strncasecmp(buf, "Authorization: Basic ", 21)) { - CtdlDecodeBase64(c_httpauth_string, &buf[21], strlen(&buf[21])); - extract_token(c_httpauth_user, c_httpauth_string, 0, ':', sizeof c_httpauth_user); - extract_token(c_httpauth_pass, c_httpauth_string, 1, ':', sizeof c_httpauth_pass); - } - else if (!strncasecmp(buf, "Content-length: ", 16)) { - ContentLength = atoi(&buf[16]); - } - else if (!strncasecmp(buf, "Content-type: ", 14)) { - safestrncpy(ContentType, &buf[14], sizeof ContentType); - } - else if (!strncasecmp(buf, "User-agent: ", 12)) { - safestrncpy(user_agent, &buf[12], sizeof user_agent); - #ifdef TECH_PREVIEW - if (is_mobile_ua(&buf[12])) { - WCC->is_mobile = 1; - } - #endif - } - else if (!strncasecmp(buf, "X-Forwarded-Host: ", 18)) { - if (follow_xff) { - safestrncpy(WCC->http_host, &buf[18], sizeof WCC->http_host); - } + if (GetHash(HTTPHeaders, HKEY("COOKIE"), &vLine) && + (vLine != NULL)){ + cookie_to_stuff((StrBuf *)vLine, NULL, + c_username, sizeof c_username, + c_password, sizeof c_password, + c_roomname, sizeof c_roomname); + } + if (GetHash(HTTPHeaders, HKEY("AUTHORIZATION"), &vLine) && + (vLine!=NULL)) { + CtdlDecodeBase64(c_httpauth_string, ChrPtr((StrBuf*)vLine), StrLength((StrBuf*)vLine)); + extract_token(c_httpauth_user, c_httpauth_string, 0, ':', sizeof c_httpauth_user); + extract_token(c_httpauth_pass, c_httpauth_string, 1, ':', sizeof c_httpauth_pass); + } + if (GetHash(HTTPHeaders, HKEY("CONTENT-LENGTH"), &vLine) && + (vLine!=NULL)) { + ContentLength = StrToi((StrBuf*)vLine); + } + if (GetHash(HTTPHeaders, HKEY("CONTENT-TYPE"), &vLine) && + (vLine!=NULL)) { + ContentType = (StrBuf*)vLine; + } + if (GetHash(HTTPHeaders, HKEY("USER-AGENT"), &vLine) && + (vLine!=NULL)) { + safestrncpy(user_agent, ChrPtr((StrBuf*)vLine), sizeof user_agent); +#ifdef TECH_PREVIEW + if ((WCC->is_mobile < 0) && is_mobile_ua(&buf[12])) { + WCC->is_mobile = 1; } - else if (!strncasecmp(buf, "Host: ", 6)) { - if (IsEmptyStr(WCC->http_host)) { - safestrncpy(WCC->http_host, &buf[6], sizeof WCC->http_host); - } + else { + WCC->is_mobile = 0; } - else if (!strncasecmp(buf, "X-Forwarded-For: ", 17)) { - safestrncpy(browser_host, &buf[17], sizeof browser_host); - while (num_tokens(browser_host, ',') > 1) { - remove_token(browser_host, 0, ','); - } - striplt(browser_host); +#endif + } + if ((follow_xff) && + GetHash(HTTPHeaders, HKEY("X-FORWARDED-HOST"), &vLine) && + (vLine != NULL)) { + safestrncpy(WCC->http_host, &buf[18], sizeof WCC->http_host); + } + if (IsEmptyStr(WCC->http_host) && + GetHash(HTTPHeaders, HKEY("HOST"), &vLine) && + (vLine!=NULL)) { + safestrncpy(WCC->http_host, + ChrPtr((StrBuf*)vLine), + sizeof WCC->http_host); + + } + if (GetHash(HTTPHeaders, HKEY("X-FORWARDED-FOR"), &vLine) && + (vLine!=NULL)) { + safestrncpy(browser_host, + ChrPtr((StrBuf*) vLine), + sizeof browser_host); + while (num_tokens(browser_host, ',') > 1) { + remove_token(browser_host, 0, ','); } + striplt(browser_host); } if (ContentLength > 0) { - int BuffSize; - - BuffSize = ContentLength + SIZ; - content = malloc(BuffSize); - memset(content, 0, BuffSize); - snprintf(content, BuffSize, "Content-type: %s\n" + content = NewStrBuf(); + StrBufPrintf(content, "Content-type: %s\n" "Content-length: %d\n\n", - ContentType, ContentLength); + ChrPtr(ContentType), ContentLength); /* hprintf("Content-type: %s\n" "Content-length: %d\n\n", ContentType, ContentLength); */ - body_start = strlen(content); + body_start = StrLength(content); /** Read the entire input data at once. */ - client_read(&WCC->http_sock, &content[body_start], ContentLength); - - if (!strncasecmp(ContentType, "application/x-www-form-urlencoded", 33)) { - StrBuf *Content; - - Content = _NewConstStrBuf(&content[body_start],ContentLength); - addurls(Content); - FreeStrBuf(&Content); - } else if (!strncasecmp(ContentType, "multipart", 9)) { - content_end = content + ContentLength + body_start; - mime_parser(content, content_end, *upload_handler, NULL, NULL, NULL, 0); + client_read(&WCC->http_sock, content, ReadBuf, ContentLength); + + if (!strncasecmp(ChrPtr(ContentType), "application/x-www-form-urlencoded", 33)) { + StrBufCutLeft(content, body_start); + addurls(content); + } else if (!strncasecmp(ChrPtr(ContentType), "multipart", 9)) { + content_end = ChrPtr(content) + ContentLength + body_start; + mime_parser(ChrPtr(content), content_end, *upload_handler, NULL, NULL, NULL, 0); } } else { content = NULL; } /* make a note of where we are in case the user wants to save it */ - safestrncpy(WCC->this_page, cmd, sizeof(WCC->this_page)); + safestrncpy(WCC->this_page, ChrPtr(ReqLine), sizeof(WCC->this_page)); remove_token(WCC->this_page, 2, ' '); remove_token(WCC->this_page, 0, ' '); /* If there are variables in the URL, we must grab them now */ - len = strlen(cmd); - for (a = 0; a < len; ++a) { - if ((cmd[a] == '?') || (cmd[a] == '&')) { - StrBuf *Params; - for (b = a; b < len; ++b) { - if (isspace(cmd[b])){ - cmd[b] = 0; - len = b - 1; - } - } - //cmd[len - a] = '\0'; - Params = _NewConstStrBuf(&cmd[a + 1], len - a); - addurls(Params); - FreeStrBuf(&Params); - cmd[a] = 0; - len = a - 1; + UrlLine = NewStrBufDup(ReqLine); + len = StrLength(UrlLine); + pch = pchs = ChrPtr(UrlLine); + pche = pchs + len; + while (pch < pche) { + if ((*pch == '?') || (*pch == '&')) { + StrBufCutLeft(UrlLine, pch - pchs + 1); + addurls(UrlLine); + break; } + pch ++; } + FreeStrBuf(&UrlLine); /* If it's a "force 404" situation then display the error and bail. */ if (!strcmp(action, "404")) { @@ -1526,7 +1510,7 @@ void session_loop(struct httprequest *req) /* Static content can be sent without connecting to Citadel. */ is_static = 0; - for (a=0; alogged_in) { WCC->killthis = 1; /* If not logged in, don't */ } /* keep the session active */ @@ -1689,9 +1675,10 @@ void session_loop(struct httprequest *req) * Automatically send requests with any method other than GET or * POST to the GroupDAV code as well. */ - if ((strcasecmp(request_method, "GET")) && (strcasecmp(request_method, "POST"))) { - groupdav_main(req, ContentType, /** do GroupDAV methods */ - ContentLength, content+body_start); + if ((strcasecmp(ChrPtr(request_method), "GET")) && (strcasecmp(ChrPtr(request_method), "POST"))) { + groupdav_main(HTTPHeaders, ReqLine, + request_method, ContentType, /** do GroupDAV methods */ + ContentLength, content, body_start); if (!WCC->logged_in) { WCC->killthis = 1; /** If not logged in, don't */ } /** keep the session active */ @@ -1774,7 +1761,7 @@ void session_loop(struct httprequest *req) SKIP_ALL_THIS_CRAP: fflush(stdout); if (content != NULL) { - free(content); + FreeStrBuf(&content); content = NULL; } free_urls(); diff --git a/webcit/webcit.h b/webcit/webcit.h index 5d678c3f6..8805d0aa8 100644 --- a/webcit/webcit.h +++ b/webcit/webcit.h @@ -196,12 +196,6 @@ extern locale_t wc_locales[]; #define NLI "(not logged in)" -/** \brief Linked list of lines appearing in an HTTP client request */ -struct httprequest { - struct httprequest *next; /**< the next request in the list */ - char line[SIZ]; /**< the request line */ -}; - /** * \brief Linked list of session variables encoded in an x-www-urlencoded content type */ @@ -470,6 +464,7 @@ struct wcsession { StrBuf *UrlFragment2; /**< second urlfragment, if NEED_URL is specified by the handler*/ StrBuf *WBuf; /**< Our output buffer */ StrBuf *HBuf; /**< Our HeaderBuffer */ + StrBuf *CLineBuf; /**< linebuffering client stuff */ HashList *ServCfg; /**< cache our server config for editing */ HashList *InetCfg; /**< Our inet server config for editing */ @@ -534,7 +529,7 @@ void end_critical_section(int which_one); void stuff_to_cookie(char *cookie, size_t clen, int session, char *user, char *pass, char *room); -void cookie_to_stuff(char *cookie, int *session, +void cookie_to_stuff(StrBuf *cookie, int *session, char *user, size_t user_len, char *pass, size_t pass_len, char *room, size_t room_len); @@ -652,11 +647,11 @@ void shutdown_sessions(void); void do_housekeeping(void); void smart_goto(char *); void worker_entry(void); -void session_loop(struct httprequest *); +void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *ReqType, StrBuf *ReadBuf); size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm); void fmt_time(char *buf, time_t thetime); void httpdate(char *buf, time_t thetime); -time_t httpdate_to_timestamp(char *buf); +time_t httpdate_to_timestamp(StrBuf *buf); void end_webcit_session(void); void page_popup(void); void http_redirect(const char *); @@ -771,7 +766,7 @@ void ical_dezonify(icalcomponent *cal); void partstat_as_string(char *buf, icalproperty *attendee); icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp); void check_attendee_availability(icalcomponent *supplied_vevent); -void do_freebusy(char *req); +void do_freebusy(const char *req); int ical_ctdl_is_overlap( struct icaltimetype t1start, struct icaltimetype t1end, @@ -798,13 +793,13 @@ long unescape_input(char *buf); void do_selected_iconbar(void); int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen); void spawn_another_worker_thread(void); -void display_rss(char *roomname, char *request_method); +void display_rss(char *roomname, StrBuf *request_method); void offer_languages(void); void set_selected_language(const char *); void go_selected_language(void); void stop_selected_language(void); void preset_locale(void); -void httplang_to_locale(char *LocaleString); +void httplang_to_locale(StrBuf *LocaleString); void StrEndTab(StrBuf *Target, int tabnum, int num_tabs); void StrBeginTab(StrBuf *Target, int tabnum, int num_tabs); void StrTabbedDialog(StrBuf *Target, int num_tabs, StrBuf *tabnames[]); @@ -813,7 +808,7 @@ void begin_tab(int tabnum, int num_tabs); void end_tab(int tabnum, int num_tabs); void str_wiki_index(char *s); int get_time_format_cached (void); -int xtoi(char *in, size_t len); +int xtoi(const char *in, size_t len); void webcit_fmt_date(char *buf, time_t thetime, int brief); int fetch_http(char *url, char *target_buf, int maxbytes); @@ -838,7 +833,7 @@ void endtls(void); void ssl_lock(int mode, int n, const char *file, int line); int starttls(int sock); extern SSL_CTX *ssl_ctx; -int client_read_ssl(char *buf, int bytes, int timeout); +int client_read_sslbuffer(StrBuf *buf, int timeout); void client_write_ssl(const StrBuf *Buf); #endif diff --git a/webcit/webserver.c b/webcit/webserver.c index 8f04c224f..d8bd34005 100644 --- a/webcit/webserver.c +++ b/webcit/webserver.c @@ -196,43 +196,38 @@ int ig_uds_server(char *sockpath, int queue_len) * 0 Request timed out. * -1 Connection is broken, or other error. */ -int client_read_to(int *sock, char *buf, int bytes, int timeout) +int client_read_to(int *sock, StrBuf *Target, StrBuf *Buf, int bytes, int timeout) { - int len, rlen; - fd_set rfds; - struct timeval tv; - int retval; - + const char *Error; + int retval = 0; #ifdef HAVE_OPENSSL if (is_https) { - return (client_read_ssl(buf, bytes, timeout)); - } -#endif + while ((retval >= 0) && (StrLength(Buf) < bytes)) + retval = client_read_sslbuffer(Buf, timeout); + if (retval >= 0) { + StrBufAppendBuf(Target, Buf, 0); /// todo: Buf > bytes? - len = 0; - while ((len < bytes) && (*sock > 0)) { - FD_ZERO(&rfds); - FD_SET(*sock, &rfds); - tv.tv_sec = timeout; - tv.tv_usec = 0; - - retval = select((*sock) + 1, &rfds, NULL, NULL, &tv); - if (FD_ISSET(*sock, &rfds) == 0) { - return (0); } - - rlen = read(*sock, &buf[len], bytes - len); - - if (rlen < 1) { - lprintf(2, "client_read() failed: %s\n", - strerror(errno)); - if (*sock > 0) - close(*sock); - *sock = -1; - return (-1); + else { + lprintf(2, "client_read_ssl() failed\n"); + return -1; } - len = len + rlen; + } +#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 @@ -364,9 +359,9 @@ long end_burst(void) * buf the buffer to write to * bytes Number of bytes to read */ -int client_read(int *sock, char *buf, int bytes) +int client_read(int *sock, StrBuf *Target, StrBuf *buf, int bytes) { - return (client_read_to(sock, buf, bytes, SLEEPING)); + return (client_read_to(sock, Target, buf, bytes, SLEEPING)); } @@ -381,38 +376,37 @@ int client_read(int *sock, char *buf, int bytes) * * returns the number of bytes read */ -int client_getln(int *sock, char *buf, int bufsiz) -{ - int i = 0; - int retval = 0; - - /* 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); -} +/////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. @@ -426,6 +420,56 @@ void graceful_shutdown_watcher(int signum) { 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; + } + } + + while ((retval >= 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); +} + + + /* * Shut us down the regular way. * signum is the signal we want to forward diff --git a/webcit/webserver.h b/webcit/webserver.h index bf3ba3ad7..82b4a51df 100644 --- a/webcit/webserver.h +++ b/webcit/webserver.h @@ -5,9 +5,10 @@ extern char *static_content_dirs[PATH_MAX]; /**< Disk representation */ extern int ndirs; extern char socket_dir[PATH_MAX]; +int ClientGetLine(int *sock, StrBuf *Target, StrBuf *CLineBuf); int client_getln(int *sock, char *buf, int bufsiz); -int client_read(int *sock, char *buf, int bytes); -int client_read_to(int *sock, char *buf, int bytes, int timeout); +int client_read(int *sock, StrBuf *Target, StrBuf *buf, int bytes); +int client_read_to(int *sock, StrBuf *Target, StrBuf *Buf, int bytes, int timeout); int lprintf(int loglevel, const char *format, ...); void wc_backtrace(void); void ShutDownWebcit(void);