X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=parsers.c;h=cf2eea8f9fd405c94a584005ae952ca191778f4e;hp=317eee430b6098030be6755e789dceefec1ced5e;hb=42e4fa330b6bae707912dc050900fda316a5224f;hpb=ac79da36ceabd35970b6a8ce92b794be37cf1427 diff --git a/parsers.c b/parsers.c index 317eee43..cf2eea8f 100644 --- a/parsers.c +++ b/parsers.c @@ -1,4 +1,4 @@ -const char parsers_rcs[] = "$Id: parsers.c,v 1.88 2007/02/07 11:27:12 fabiankeil Exp $"; +const char parsers_rcs[] = "$Id: parsers.c,v 1.95 2007/03/25 14:26:40 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/parsers.c,v $ @@ -10,13 +10,12 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.88 2007/02/07 11:27:12 fabiankeil * `client_uagent', `client_x_forwarded', * `client_x_forwarded_adder', `client_xtra_adder', * `content_type', `crumble', `destroy_list', `enlist', - * `flush_socket', ``get_header', `sed', `filter_server_header' - * `filter_client_header', `filter_header', `crunch_server_header', + * `flush_socket', ``get_header', `sed', `filter_header' * `server_content_encoding', `server_content_disposition', * `server_last_modified', `client_accept_language', * `crunch_client_header', `client_if_modified_since', * `client_if_none_match', `get_destination_from_headers', - * `parse_header_time' and `server_set_cookie'. + * `parse_header_time', `decompress_iob' and `server_set_cookie'. * * Copyright : Written by and Copyright (C) 2001-2007 the SourceForge * Privoxy team. http://www.privoxy.org/ @@ -45,6 +44,49 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.88 2007/02/07 11:27:12 fabiankeil * * Revisions : * $Log: parsers.c,v $ + * Revision 1.95 2007/03/25 14:26:40 fabiankeil + * - Fix warnings when compiled with glibc. + * - Don't use crumble() for cookie crunching. + * - Move cookie time parsing into parse_header_time(). + * - Let parse_header_time() return a jb_err code + * instead of a pointer that can only be used to + * check for NULL anyway. + * + * Revision 1.94 2007/03/21 12:23:53 fabiankeil + * - Add better protection against malicious gzip headers. + * - Stop logging the first hundred bytes of decompressed content. + * It looks like it's working and there is always debug 16. + * - Log the content size after decompression in decompress_iob() + * instead of pcrs_filter_response(). + * + * Revision 1.93 2007/03/20 15:21:44 fabiankeil + * - Use dedicated header filter actions instead of abusing "filter". + * Replace "filter-client-headers" and "filter-client-headers" + * with "server-header-filter" and "client-header-filter". + * - Remove filter_client_header() and filter_client_header(), + * filter_header() now checks the shiny new + * CSP_FLAG_CLIENT_HEADER_PARSING_DONE flag instead. + * + * Revision 1.92 2007/03/05 13:25:32 fabiankeil + * - Cosmetical changes for LOG_LEVEL_RE_FILTER messages. + * - Handle "Cookie:" and "Connection:" headers a bit smarter + * (don't crunch them just to recreate them later on). + * - Add another non-standard time format for the cookie + * expiration date detection. + * - Fix a valgrind warning. + * + * Revision 1.91 2007/02/24 12:27:32 fabiankeil + * Improve cookie expiration date detection. + * + * Revision 1.90 2007/02/08 19:12:35 fabiankeil + * Don't run server_content_length() the first time + * sed() parses server headers; only adjust the + * Content-Length header if the page was modified. + * + * Revision 1.89 2007/02/07 16:52:11 fabiankeil + * Fix log messages regarding the cookie time format + * (cookie and request URL were mixed up). + * * Revision 1.88 2007/02/07 11:27:12 fabiankeil * - Let decompress_iob() * - not corrupt the content if decompression fails @@ -615,6 +657,13 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.88 2007/02/07 11:27:12 fabiankeil #include #include #include + +#ifdef __GLIBC__ +/* + * Convince GNU's libc to provide a strptime prototype. + */ +#define __USE_XOPEN +#endif /*__GLIBC__ */ #include #ifdef FEATURE_ZLIB @@ -672,23 +721,22 @@ const struct parsers client_patterns[] = { { "Host:", 5, client_host }, { "if-modified-since:", 18, client_if_modified_since }, { "Keep-Alive:", 11, crumble }, - { "connection:", 11, crumble }, + { "connection:", 11, connection }, { "proxy-connection:", 17, crumble }, { "max-forwards:", 13, client_max_forwards }, { "Accept-Language:", 16, client_accept_language }, { "if-none-match:", 14, client_if_none_match }, { "X-Filter:", 9, client_x_filter }, { "*", 0, crunch_client_header }, - { "*", 0, filter_client_header }, + { "*", 0, filter_header }, { NULL, 0, NULL } }; const struct parsers server_patterns[] = { { "HTTP", 4, server_http }, { "set-cookie:", 11, server_set_cookie }, - { "connection:", 11, crumble }, + { "connection:", 11, connection }, { "Content-Type:", 13, server_content_type }, - { "Content-Length:", 15, server_content_length }, { "Content-MD5:", 12, server_content_md5 }, { "Content-Encoding:", 17, server_content_encoding }, { "Transfer-Encoding:", 18, server_transfer_coding }, @@ -696,7 +744,7 @@ const struct parsers server_patterns[] = { { "content-disposition:", 20, server_content_disposition }, { "Last-Modified:", 14, server_last_modified }, { "*", 0, crunch_server_header }, - { "*", 0, filter_server_header }, + { "*", 0, filter_header }, { NULL, 0, NULL } }; @@ -863,13 +911,18 @@ jb_err decompress_iob(struct client_state *csp) char *cur; /* Current iob position (to keep the original * iob->cur unmodified if we return early) */ size_t bufsize; /* allocated size of the new buffer */ + size_t old_size; /* Content size before decompression */ size_t skip_size; /* Number of bytes at the beginning of the iob that we should NOT decompress. */ int status; /* return status of the inflate() call */ z_stream zstr; /* used by calls to zlib */ + assert(csp->iob->cur - csp->iob->buf > 0); + assert(csp->iob->eod - csp->iob->cur > 0); + bufsize = csp->iob->size; skip_size = (size_t)(csp->iob->cur - csp->iob->buf); + old_size = (size_t)(csp->iob->eod - csp->iob->cur); cur = csp->iob->cur; @@ -947,7 +1000,7 @@ jb_err decompress_iob(struct client_state *csp) skip_bytes = *cur++; skip_bytes = *cur++ << 8; - assert(skip_bytes == *csp->iob->cur-2 + ((*csp->iob->cur-1) << 8)); + assert(skip_bytes == *csp->iob->cur - 2 + ((*csp->iob->cur - 1) << 8)); /* * The number of bytes to skip should be positive @@ -969,14 +1022,16 @@ jb_err decompress_iob(struct client_state *csp) /* Skip the filename if necessary. */ if (flags & 0x08) { - /* A null-terminated string follows. */ - while (*cur++); + /* A null-terminated string is supposed to follow. */ + while (*cur++ && (cur < csp->iob->eod)); + } /* Skip the comment if necessary. */ if (flags & 0x10) { - while (*cur++); + /* A null-terminated string is supposed to follow. */ + while (*cur++ && (cur < csp->iob->eod)); } /* Skip the CRC if necessary. */ @@ -984,6 +1039,18 @@ jb_err decompress_iob(struct client_state *csp) { cur += 2; } + + if (cur >= csp->iob->eod) + { + /* + * If the current position pointer reached or passed + * the buffer end, we were obviously tricked to skip + * too much. + */ + log_error (LOG_LEVEL_ERROR, + "Malformed gzip header detected. Aborting decompression."); + return JB_ERR_COMPRESS; + } } } else if (csp->content_type & CT_DEFLATE) @@ -1165,15 +1232,18 @@ jb_err decompress_iob(struct client_state *csp) && (csp->iob->cur <= csp->iob->eod) && (csp->iob->eod <= csp->iob->buf + csp->iob->size)) { - char t = csp->iob->cur[100]; - csp->iob->cur[100] = '\0'; - /* - * XXX: The debug level should be lowered - * before the next stable release. - */ - log_error(LOG_LEVEL_INFO, "Sucessfully decompressed: %s", csp->iob->cur); - csp->iob->cur[100] = t; - return JB_ERR_OK; + const size_t new_size = (size_t)(csp->iob->eod - csp->iob->cur); + if (new_size > 0) + { + log_error(LOG_LEVEL_RE_FILTER, + "Decompression successful. Old size: %d, new size: %d.", + old_size, new_size); + } + else + { + /* zlib thinks this is OK, so lets do the same. */ + log_error(LOG_LEVEL_INFO, "Decompression didn't result in any content."); + } } else { @@ -1185,6 +1255,8 @@ jb_err decompress_iob(struct client_state *csp) return JB_ERR_COMPRESS; } + return JB_ERR_OK; + } #endif /* defined(FEATURE_ZLIB) */ @@ -1393,64 +1465,12 @@ char *sed(const struct parsers pats[], /* here begins the family of parser functions that reformat header lines */ -/********************************************************************* - * - * Function : filter_server_header - * - * Description : Checks if server header filtering is enabled. - * If it is, filter_header is called to do the work. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * 2 : header = On input, pointer to header to modify. - * On output, pointer to the modified header, or NULL - * to remove the header. This function frees the - * original string if necessary. - * - * Returns : JB_ERR_OK on success and always succeeds - * - *********************************************************************/ -jb_err filter_server_header(struct client_state *csp, char **header) -{ - if (csp->action->flags & ACTION_FILTER_SERVER_HEADERS) - { - filter_header(csp, header); - } - return(JB_ERR_OK); -} - -/********************************************************************* - * - * Function : filter_client_header - * - * Description : Checks if client header filtering is enabled. - * If it is, filter_header is called to do the work. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * 2 : header = On input, pointer to header to modify. - * On output, pointer to the modified header, or NULL - * to remove the header. This function frees the - * original string if necessary. - * - * Returns : JB_ERR_OK on success and always succeeds - * - *********************************************************************/ -jb_err filter_client_header(struct client_state *csp, char **header) -{ - if (csp->action->flags & ACTION_FILTER_CLIENT_HEADERS) - { - filter_header(csp, header); - } - return(JB_ERR_OK); -} - /********************************************************************* * * Function : filter_header * * Description : Executes all text substitutions from all applying - * +filter actions on the header. + * +(server|client)-header-filter actions on the header. * Most of the code was copied from pcrs_filter_response, * including the rather short variable names * @@ -1478,6 +1498,19 @@ jb_err filter_header(struct client_state *csp, char **header) struct list_entry *filtername; int i, found_filters = 0; + int wanted_filter_type; + int multi_action_index; + + if (csp->flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE) + { + wanted_filter_type = FT_SERVER_HEADER_FILTER; + multi_action_index = ACTION_MULTI_SERVER_HEADER_FILTER; + } + else + { + wanted_filter_type = FT_CLIENT_HEADER_FILTER; + multi_action_index = ACTION_MULTI_CLIENT_HEADER_FILTER; + } /* * Need to check the set of re_filterfiles... @@ -1524,7 +1557,13 @@ jb_err filter_header(struct client_state *csp, char **header) */ for (b = fl->f; b; b = b->next) { - for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first; + if (b->type != wanted_filter_type) + { + /* Skip other filter types */ + continue; + } + + for (filtername = csp->action->multi[multi_action_index]->first; filtername ; filtername = filtername->next) { if (strcmp(b->name, filtername->str) == 0) @@ -1537,7 +1576,7 @@ jb_err filter_header(struct client_state *csp, char **header) continue; } - log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s (size %d) with filter %s...", + log_error(LOG_LEVEL_RE_FILTER, "filtering \'%s\' (size %d) with \'%s\' ...", *header, size, b->name); /* Apply all jobs from the joblist */ @@ -1568,7 +1607,7 @@ jb_err filter_header(struct client_state *csp, char **header) } } } - log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size); + log_error(LOG_LEVEL_RE_FILTER, "... produced %d hits (new size %d).", current_hits, size); hits += current_hits; } } @@ -1590,6 +1629,56 @@ jb_err filter_header(struct client_state *csp, char **header) } +/********************************************************************* + * + * Function : connection + * + * Description : Makes sure that the value of the Connection: header + * is "close" and signals connection_close_adder + * to do nothing. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err connection(struct client_state *csp, char **header) +{ + char *old_header = *header; + + /* Do we have a 'Connection: close' header? */ + if (strcmpic(*header, "Connection: close")) + { + /* No, create one */ + *header = strdup("Connection: close"); + if (header == NULL) + { + return JB_ERR_MEMORY; + } + log_error(LOG_LEVEL_HEADER, "Replaced: \'%s\' with \'%s\'", old_header, *header); + freez(old_header); + } + + /* Signal connection_close_adder() to return early. */ + if (csp->flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE) + { + csp->flags |= CSP_FLAG_SERVER_CONNECTION_CLOSE_SET; + } + else + { + csp->flags |= CSP_FLAG_CLIENT_CONNECTION_CLOSE_SET; + } + + return JB_ERR_OK; +} + + /********************************************************************* * * Function : crumble @@ -1634,13 +1723,13 @@ jb_err crumble(struct client_state *csp, char **header) jb_err crunch_server_header(struct client_state *csp, char **header) { const char *crunch_pattern; - /*Is there a header to crunch*/ + /* Do we feel like crunching? */ if ((csp->action->flags & ACTION_CRUNCH_SERVER_HEADER)) { crunch_pattern = csp->action->string[ACTION_STRING_SERVER_HEADER]; - /*Is the current header the lucky one?*/ + /* Is the current header the lucky one? */ if (strstr(*header, crunch_pattern)) { log_error(LOG_LEVEL_HEADER, "Crunching server header: %s (contains: %s)", *header, crunch_pattern); @@ -1894,8 +1983,20 @@ jb_err server_content_encoding(struct client_state *csp, char **header) * Body is compressed, turn off pcrs and gif filtering. */ csp->content_type |= CT_TABOO; + + /* + * Log a warning if the user expects the content to be filtered. + */ + if ((csp->rlist != NULL) && + (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]))) + { + log_error(LOG_LEVEL_INFO, + "Compressed content detected, content filtering disabled. " + "Consider recompiling Privoxy with zlib support or " + "enable the prevent-compression action."); + } } -#endif /* !defined(FEATURE_ZLIB) */ +#endif /* defined(FEATURE_ZLIB) */ return JB_ERR_OK; @@ -1923,12 +2024,10 @@ jb_err server_content_encoding(struct client_state *csp, char **header) jb_err server_content_length(struct client_state *csp, char **header) { const size_t max_header_length = 80; - if (csp->content_length != 0) /* Content length could have been modified */ + + /* Regenerate header if the content was modified. */ + if (csp->flags & CSP_FLAG_MODIFIED) { - /* - * XXX: Shouldn't we check if csp->content_length - * is different than the original value? - */ freez(*header); *header = (char *) zalloc(max_header_length); if (*header == NULL) @@ -2112,6 +2211,8 @@ jb_err server_last_modified(struct client_state *csp, char **header) } else if (0 == strcmpic(newval, "randomize")) { + const char *header_time = *header + sizeof("Last-Modified:"); + log_error(LOG_LEVEL_HEADER, "Randomizing: %s", *header); now = time(NULL); #ifdef HAVE_GMTIME_R @@ -2123,9 +2224,9 @@ jb_err server_last_modified(struct client_state *csp, char **header) #else timeptr = gmtime(&now); #endif - if ((timeptr = parse_header_time(*header, &last_modified)) == NULL) + if (JB_ERR_OK != parse_header_time(header_time, &last_modified)) { - log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header); + log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s in %s (crunching!)", header_time, *header); freez(*header); } else @@ -2177,6 +2278,7 @@ jb_err server_last_modified(struct client_state *csp, char **header) return JB_ERR_OK; } + /********************************************************************* * * Function : client_accept_encoding @@ -2464,13 +2566,13 @@ jb_err client_accept_language(struct client_state *csp, char **header) jb_err crunch_client_header(struct client_state *csp, char **header) { const char *crunch_pattern; - /*Is there a header to crunch*/ - + + /* Do we feel like crunching? */ if ((csp->action->flags & ACTION_CRUNCH_CLIENT_HEADER)) { crunch_pattern = csp->action->string[ACTION_STRING_CLIENT_HEADER]; - /*Is the current header the lucky one?*/ + /* Is the current header the lucky one? */ if (strstr(*header, crunch_pattern)) { log_error(LOG_LEVEL_HEADER, "Crunching client header: %s (contains: %s)", *header, crunch_pattern); @@ -2606,9 +2708,10 @@ jb_err client_from(struct client_state *csp, char **header) * * Function : client_send_cookie * - * Description : Handle the "cookie" header properly. Called from `sed'. - * If cookie is accepted, add it to the cookie_list, - * else we crunch it. Mmmmmmmmmmm ... cookie ...... + * Description : Crunches the "cookie" header if necessary. + * Called from `sed'. + * + * XXX: Stupid name, doesn't send squat. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -2623,25 +2726,13 @@ jb_err client_from(struct client_state *csp, char **header) *********************************************************************/ jb_err client_send_cookie(struct client_state *csp, char **header) { - jb_err result = JB_ERR_OK; - - if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0) + if (csp->action->flags & ACTION_NO_COOKIE_READ) { - /* strlen("cookie: ") == 8 */ - result = enlist(csp->cookie_list, *header + 8); - } - else - { - log_error(LOG_LEVEL_HEADER, "Crunched outgoing cookie -- yum!"); + log_error(LOG_LEVEL_HEADER, "Crunched outgoing cookie: %s", *header); + freez(*header); } - /* - * Always remove the cookie here. The cookie header - * will be sent at the end of the header. - */ - freez(*header); - - return result; + return JB_ERR_OK; } @@ -2822,6 +2913,9 @@ jb_err client_host(struct client_state *csp, char **header) csp->http->hostport, csp->http->host, csp->http->port); } + /* Signal client_host_adder() to return right away */ + csp->flags |= CSP_FLAG_HOST_HEADER_IS_SET; + return JB_ERR_OK; } @@ -2879,9 +2973,11 @@ jb_err client_if_modified_since(struct client_state *csp, char **header) } else /* add random value */ { - if ((timeptr = parse_header_time(*header, &tm)) == NULL) + const char *header_time = *header + sizeof("If-Modified-Since:"); + + if (JB_ERR_OK != parse_header_time(header_time, &tm)) { - log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header); + log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s in %s (crunching!)", header_time, *header); freez(*header); } else @@ -3038,8 +3134,16 @@ jb_err client_host_adder(struct client_state *csp) char *p; jb_err err; + if (csp->flags & CSP_FLAG_HOST_HEADER_IS_SET) + { + /* Header already set by the client, nothing to do. */ + return JB_ERR_OK; + } + if ( !csp->http->hostport || !*(csp->http->hostport)) { + /* XXX: When does this happen and why is it OK? */ + log_error(LOG_LEVEL_INFO, "Weirdness in client_host_adder detected and ignored."); return JB_ERR_OK; } @@ -3055,6 +3159,7 @@ jb_err client_host_adder(struct client_state *csp) p = csp->http->hostport; } + /* XXX: Just add it, we already made sure that it will be unique */ log_error(LOG_LEVEL_HEADER, "addh-unique: Host: %s", p); err = enlist_unique_header(csp->headers, "Host", p); return err; @@ -3068,6 +3173,8 @@ jb_err client_host_adder(struct client_state *csp) * * Description : Used in the add_client_headers list. Called from `sed'. * + * XXX: Remove csp->cookie_list which is no longer used. + * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * @@ -3148,6 +3255,8 @@ jb_err client_cookie_adder(struct client_state *csp) *********************************************************************/ jb_err client_accept_encoding_adder(struct client_state *csp) { + assert(0); /* Not in use */ + if ( ((csp->action->flags & ACTION_NO_COMPRESSION) != 0) && (!strcmpic(csp->http->ver, "HTTP/1.1")) ) { @@ -3243,9 +3352,10 @@ jb_err client_x_forwarded_adder(struct client_state *csp) * * Function : connection_close_adder * - * Description : Adds a "Connection: close" header to csp->headers - * as a temporary fix for the needed but missing HTTP/1.1 - * support. Called from `sed'. + * Description : "Temporary" fix for the needed but missing HTTP/1.1 + * support. Adds a "Connection: close" header to csp->headers + * unless the header was already present. Called from `sed'. + * * FIXME: This whole function shouldn't be neccessary! * * Parameters : @@ -3257,7 +3367,27 @@ jb_err client_x_forwarded_adder(struct client_state *csp) *********************************************************************/ jb_err connection_close_adder(struct client_state *csp) { + const unsigned int flags = csp->flags; + + /* + * Return right away if + * + * - we're parsing server headers and the server header + * "Connection: close" is already set, or if + * + * - we're parsing client headers and the client header + * "Connection: close" is already set. + */ + if ((flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE + && flags & CSP_FLAG_SERVER_CONNECTION_CLOSE_SET) + ||(!(flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE) + && flags & CSP_FLAG_CLIENT_CONNECTION_CLOSE_SET)) + { + return JB_ERR_OK; + } + log_error(LOG_LEVEL_HEADER, "Adding: Connection: close"); + return enlist(csp->headers, "Connection: close"); } @@ -3285,6 +3415,9 @@ jb_err connection_close_adder(struct client_state *csp) *********************************************************************/ jb_err server_http(struct client_state *csp, char **header) { + /* Signal that were now parsing server headers. */ + csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE; + sscanf(*header, "HTTP/%*d.%*d %d", &(csp->http->status)); if (csp->http->status == 206) { @@ -3330,7 +3463,6 @@ jb_err server_set_cookie(struct client_state *csp, char **header) time_t now; time_t cookie_time; struct tm tm_now; - struct tm tm_cookie; time(&now); #ifdef FEATURE_COOKIE_JAR @@ -3348,10 +3480,10 @@ jb_err server_set_cookie(struct client_state *csp, char **header) tm_now = *localtime_r(&now, &tm_now); #elif FEATURE_PTHREAD pthread_mutex_lock(&localtime_mutex); - tm_now = *localtime (&now); + tm_now = *localtime (&now); pthread_mutex_unlock(&localtime_mutex); #else - tm_now = *localtime (&now); + tm_now = *localtime (&now); #endif strftime(tempbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); @@ -3362,8 +3494,8 @@ jb_err server_set_cookie(struct client_state *csp, char **header) if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0) { - log_error(LOG_LEVEL_HEADER, "Crunched incoming cookie -- yum!"); - return crumble(csp, header); + log_error(LOG_LEVEL_HEADER, "Crunching incoming cookie: %s", *header); + freez(*header); } else if ((csp->action->flags & ACTION_NO_COOKIE_KEEP) != 0) { @@ -3409,39 +3541,12 @@ jb_err server_set_cookie(struct client_state *csp, char **header) * if the cookie is still valid, if yes, * rewrite it to a session cookie. */ - if (strncmpic(cur_tag, "expires=", 8) == 0) + if ((strncmpic(cur_tag, "expires=", 8) == 0) && *(cur_tag + 8)) { - char *match; - /* - * Try the valid time formats we know about. - * - * XXX: Maybe the log messages should be removed - * for the next stable release. They just exist to - * see which time format gets the most hits and - * should be checked for first. - */ - if (NULL != (match = strptime(cur_tag, "expires=%a, %e-%b-%y %H:%M:%S ", &tm_cookie))) - { - log_error(LOG_LEVEL_HEADER, - "cookie \'%s\' send by %s appears to be using time format 1.", - *header, csp->http->url); - } - else if (NULL != (match = strptime(cur_tag, "expires=%A, %e-%b-%Y %H:%M:%S ", &tm_cookie))) - { - log_error(LOG_LEVEL_HEADER, - "cookie \'%s\' send by %s appears to be using time format 2.", - *header, csp->http->url); - - } - else if (NULL != (match = strptime(cur_tag, "expires=%a, %e-%b-%Y %H:%M:%S ", &tm_cookie))) - { - log_error(LOG_LEVEL_HEADER, - "cookie \'%s\' send by %s appears to be using time format 3.", - *header, csp->http->url); - } + char *expiration_date = cur_tag + 8; /* Skip "[Ee]xpires=" */ - /* Did any of them match? */ - if (NULL == match) + /* Did we detect the date properly? */ + if (JB_ERR_OK != parse_header_time(expiration_date, &cookie_time)) { /* * Nope, treat it as if it was still valid. @@ -3449,7 +3554,7 @@ jb_err server_set_cookie(struct client_state *csp, char **header) * XXX: Should we remove the whole cookie instead? */ log_error(LOG_LEVEL_ERROR, - "Can't parse %s. Unsupported time format?", cur_tag); + "Can't parse \'%s\', send by %s. Unsupported time format?", cur_tag, csp->http->url); memmove(cur_tag, next_tag, strlen(next_tag) + 1); changed = 1; } @@ -3489,7 +3594,6 @@ jb_err server_set_cookie(struct client_state *csp, char **header) * anyway, which in many cases will be shorter * than a browser session. */ - cookie_time = timegm(&tm_cookie); if (cookie_time - now < 0) { log_error(LOG_LEVEL_HEADER, @@ -3499,11 +3603,10 @@ jb_err server_set_cookie(struct client_state *csp, char **header) } else { - log_error(LOG_LEVEL_HEADER, - "Cookie \'%s\' is still valid and has to be rewritten.", *header); - /* - * Delete the tag by copying the rest of the string over it. + * Still valid, delete expiration date by copying + * the rest of the string over it. + * * (Note that we cannot just use "strcpy(cur_tag, next_tag)", * since the behaviour of strcpy is undefined for overlapping * strings.) @@ -3583,54 +3686,48 @@ int strclean(const char *string, const char *substring) * * Function : parse_header_time * - * Description : Transforms time inside a HTTP header into - * the usual time format. + * Description : Parses time formats used in HTTP header strings + * to get the numerical respresentation. * * Parameters : - * 1 : header = header to parse - * 2 : tm = storage for the resulting time in seconds + * 1 : header_time = HTTP header time as string. + * 2 : result = storage for header_time in seconds * - * Returns : Time struct containing the header time, or - * NULL in case of a parsing problems. + * Returns : JB_ERR_OK if the time format was recognized, or + * JB_ERR_PARSE otherwise. * *********************************************************************/ -struct tm *parse_header_time(char *header, time_t *tm) { - - char * timestring; +jb_err parse_header_time(const char *header_time, time_t *result) +{ struct tm gmt; - struct tm * timeptr; /* - * Initializing gmt to prevent time zone offsets. + * Zero out gmt to prevent time zone offsets. * * While this is only necessary on some platforms * (mingw32 for example), I don't know how to * detect these automatically and doing it everywhere * shouldn't hurt. */ - time(tm); -#ifdef HAVE_LOCALTIME_R - gmt = *localtime_r(tm, &gmt); -#elif FEATURE_PTHREAD - pthread_mutex_lock(&localtime_mutex); - gmt = *localtime(tm); - pthread_mutex_unlock(&localtime_mutex); -#else - gmt = *localtime(tm); -#endif - - /* Skipping header name */ - timestring = strstr(header, ": "); - if (strptime(timestring, ": %a, %d %b %Y %H:%M:%S", &gmt) == NULL) + memset(&gmt, 0, sizeof(gmt)); + + /* Tue, 02 Jun 2037 20:00:00 */ + if ((NULL == strptime(header_time, "%a, %d %b %Y %H:%M:%S", &gmt)) + /* Tue, 02-Jun-2037 20:00:00 */ + && (NULL == strptime(header_time, "%a, %d-%b-%Y %H:%M:%S", &gmt)) + /* Tue, 02-Jun-37 20:00:00 */ + && (NULL == strptime(header_time, "%a, %d-%b-%y %H:%M:%S", &gmt)) + /* Tuesday, 02-Jun-2037 20:00:00 */ + && (NULL == strptime(header_time, "%A, %d-%b-%Y %H:%M:%S", &gmt)) + /* Tuesday Jun 02 20:00:00 2037 */ + && (NULL == strptime(header_time, "%A %b %d %H:%M:%S %Y", &gmt))) { - timeptr = NULL; - } - else - { - *tm = timegm(&gmt); - timeptr = &gmt; + return JB_ERR_PARSE; } - return(timeptr); + + *result = timegm(&gmt); + + return JB_ERR_OK; }