diff -ru ./actionlist.h /tmp/unionfs/privoxy-devel-oben/actionlist.h --- ./actionlist.h Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/actionlist.h Wed Aug 2 20:51:07 2006 @@ -126,14 +126,43 @@ DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "first", 0) DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "last", 1) DEFINE_ACTION_BOOL ("downgrade-http-version", ACTION_DOWNGRADE) -DEFINE_ACTION_BOOL ("fast-redirects", ACTION_FAST_REDIRECTS) +DEFINE_ACTION_STRING ("fast-redirects", ACTION_FAST_REDIRECTS, ACTION_STRING_FAST_REDIRECTS) +DEFINE_CGI_PARAM_RADIO ("fast-redirects", ACTION_FAST_REDIRECTS, ACTION_STRING_FAST_REDIRECTS, "simple-check", 0) +DEFINE_CGI_PARAM_RADIO ("fast-redirects", ACTION_FAST_REDIRECTS, ACTION_STRING_FAST_REDIRECTS, "check-decoded-url", 1) DEFINE_ACTION_MULTI ("filter", ACTION_MULTI_FILTER) DEFINE_ACTION_BOOL ("handle-as-image", ACTION_IMAGE) DEFINE_ACTION_BOOL ("hide-forwarded-for-headers", ACTION_HIDE_FORWARDED) DEFINE_ACTION_STRING ("hide-from-header", ACTION_HIDE_FROM, ACTION_STRING_FROM) DEFINE_CGI_PARAM_RADIO ("hide-from-header", ACTION_HIDE_FROM, ACTION_STRING_FROM, "block", 1) DEFINE_CGI_PARAM_CUSTOM ("hide-from-header", ACTION_HIDE_FROM, ACTION_STRING_FROM, "spam_me_senseless@sittingduck.xyz") +DEFINE_ACTION_BOOL ("filter-headers", ACTION_FILTER_HEADERS) +DEFINE_ACTION_STRING ("crunch-server-header", ACTION_CRUNCH_SERVER_HEADER, ACTION_STRING_SERVER_HEADER) +DEFINE_CGI_PARAM_NO_RADIO("crunch-server-header", ACTION_CRUNCH_SERVER_HEADER, ACTION_STRING_SERVER_HEADER, "X-Whatever:") +DEFINE_ACTION_STRING ("crunch-client-header", ACTION_CRUNCH_CLIENT_HEADER, ACTION_STRING_CLIENT_HEADER) +DEFINE_CGI_PARAM_NO_RADIO("crunch-client-header", ACTION_CRUNCH_CLIENT_HEADER, ACTION_STRING_CLIENT_HEADER, "X-Whatever:") +DEFINE_ACTION_STRING ("hide-accept-language", ACTION_HIDE_ACCEPT_LANGUAGE, ACTION_STRING_LANGUAGE) +DEFINE_CGI_PARAM_RADIO ("hide-accept-language", ACTION_HIDE_ACCEPT_LANGUAGE, ACTION_STRING_LANGUAGE, "block", 0) +DEFINE_CGI_PARAM_CUSTOM ("hide-accept-language", ACTION_HIDE_ACCEPT_LANGUAGE, ACTION_STRING_LANGUAGE, "de-de") +DEFINE_ACTION_STRING ("content-type-overwrite", ACTION_CONTENT_TYPE_OVERWRITE, ACTION_STRING_CONTENT_TYPE) +DEFINE_CGI_PARAM_NO_RADIO("content-type-overwrite", ACTION_CONTENT_TYPE_OVERWRITE, ACTION_STRING_CONTENT_TYPE, "text/html") +DEFINE_ACTION_STRING ("hide-content-disposition", ACTION_HIDE_CONTENT_DISPOSITION, ACTION_STRING_CONTENT_DISPOSITION) +DEFINE_CGI_PARAM_RADIO ("hide-content-disposition", ACTION_HIDE_CONTENT_DISPOSITION, ACTION_STRING_CONTENT_DISPOSITION, "block", 0) +DEFINE_CGI_PARAM_CUSTOM ("hide-content-disposition", ACTION_HIDE_CONTENT_DISPOSITION, ACTION_STRING_CONTENT_DISPOSITION, "attachment; filename=WHATEVER.txt") +DEFINE_ACTION_STRING ("hide-if-modified-since", ACTION_HIDE_IF_MODIFIED_SINCE, ACTION_STRING_IF_MODIFIED_SINCE) +DEFINE_CGI_PARAM_RADIO ("hide-if-modified-since", ACTION_HIDE_IF_MODIFIED_SINCE, ACTION_STRING_IF_MODIFIED_SINCE, "block", 0) +DEFINE_CGI_PARAM_CUSTOM ("hide-if-modified-since", ACTION_HIDE_IF_MODIFIED_SINCE, ACTION_STRING_IF_MODIFIED_SINCE, "-1") +DEFINE_ACTION_STRING ("overwrite-last-modified", ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED) +DEFINE_CGI_PARAM_RADIO ("overwrite-last-modified", ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED, "block", 0) +DEFINE_CGI_PARAM_RADIO ("overwrite-last-modified", ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED, "reset-to-request-time", 1) +DEFINE_CGI_PARAM_RADIO ("overwrite-last-modified", ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED, "randomize", 2) +DEFINE_ACTION_BOOL ("crunch-if-none-match", ACTION_CRUNCH_IF_NONE_MATCH) +DEFINE_ACTION_BOOL ("force-text-mode", ACTION_FORCE_TEXT_MODE) +DEFINE_ACTION_BOOL ("handle-as-empty-document", ACTION_HANDLE_AS_EMPTY_DOCUMENT) +DEFINE_ACTION_BOOL ("treat-forbidden-connects-like-blocks", ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS) +DEFINE_ACTION_STRING ("redirect", ACTION_REDIRECT, ACTION_STRING_REDIRECT) +DEFINE_CGI_PARAM_NO_RADIO("redirect", ACTION_REDIRECT, ACTION_STRING_REDIRECT, "http://localhost/") DEFINE_ACTION_STRING ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER) +DEFINE_CGI_PARAM_RADIO ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "conditional-block", 2) DEFINE_CGI_PARAM_RADIO ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "forge", 1) DEFINE_CGI_PARAM_RADIO ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "block", 0) DEFINE_CGI_PARAM_CUSTOM ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "http://www.google.com/") diff -ru ./cgi.c /tmp/unionfs/privoxy-devel-oben/cgi.c --- ./cgi.c Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/cgi.c Mon May 29 20:28:22 2006 @@ -1763,16 +1763,31 @@ else { /* - * Compliant browsers should not cache this due to the "Cache-Control" - * setting. However, to be certain, we also set both "Last-Modified" - * and "Expires" to the current time. + * Setting "Cache-Control" to "no-cache" and "Expires" to + * the current time doesn't exactly forbid caching, it just + * requires the client to revalidate the cached copy. + * + * If a temporary problem occurres and the user tries again after + * getting Privoxy's error message, a compliant browser may set the + * If-Modified-Since header with the content of the error page's + * Last-Modified header. More often than not, the document on the server + * is older than Privoxy's error message, the server would send status code + * 304 and the browser would display the outdated error message again and again. + * + * As a last resort we set "Last-Modified" to Tim Berners-Lee's birthday, + * which predates the age of any page on the web and can be safely used to + * "revalidate" without getting a status code 304. + * + * There is no need to let the useless If-Modified-Since header reach the + * server, it is therefore stripped by client_if_modified_since in parsers.c. */ if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache"); get_http_time(0, buf); if (!err) err = enlist_unique_header(rsp->headers, "Date", buf); - if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf); + if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Wed, 08 Jun 1955 12:00:00 GMT"); if (!err) err = enlist_unique_header(rsp->headers, "Expires", "Sat, 17 Jun 2000 12:00:00 GMT"); + if (!err) err = enlist_unique_header(rsp->headers, "Pragma", "no-cache"); } diff -ru ./default.action.master /tmp/unionfs/privoxy-devel-oben/default.action.master --- ./default.action.master Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/default.action.master Wed Jul 19 15:12:15 2006 @@ -201,7 +201,7 @@ # responses as well. Use this action for servers that use HTTP/1.1 # protocol features that Privoxy currently can't handle yet. # -# +fast-redirects +# +fast-redirects{check-decoded-url} # Many sites, like yahoo.com, don't just link to other sites. # Instead, they will link to some script on their own server, # giving the destination as a parameter, which will then redirect @@ -217,7 +217,7 @@ # time is wasted, while your browser aks the server for one redirect # after the other. Plus, it feeds the advertisers. # -# The +fast-redirects option enables interception of these requests +# The +fast-redirects{check-decoded-url} option enables interception of these requests # by Privoxy, who will cut off all but the last valid URL in the # request and send a local redirect back to your browser without # contacting the intermediate sites. @@ -1606,7 +1606,7 @@ #MASTER# PROBLEM-URL: http://www.hh.schule.de/ak/nt/ www.hh.schule.de/ak/nt/ -{+fast-redirects -block} +{+fast-redirects{check-decoded-url} -block} #MASTER# PROBLEM-URL: http://isbn.nu/0596001088/price/2.html www.commission-junction.com/track/ #MASTER# PROBLEM-URL: http://uk.rd.yahoo.com/M=200059723.200849546.202365062.200414073/D=ukhmpg/S=15426100:TEAR/A=200396897/R=1119/id=img1_nocap_dial/*http://ad.uk.doubleclick.net/clk;5982435;8261020;g?http://www.lunnpoly.com diff -ru ./errlog.c /tmp/unionfs/privoxy-devel-oben/errlog.c --- ./errlog.c Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/errlog.c Sat Jun 3 21:04:39 2006 @@ -288,7 +288,7 @@ static FILE *logfp = NULL; /* logging detail level. */ -static int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO); +int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO); /* static functions */ static void fatal_error(const char * error_message); @@ -415,6 +415,14 @@ } log_error(LOG_LEVEL_INFO, "Privoxy version " VERSION); + log_error(LOG_LEVEL_INFO, "The patch you are using is not supported by the Privoxy team!\n\n" + "Before you send any bug reports to the Privoxy developers, " + "make sure the problem exists in vanilla Privoxy as well.\n" + "Feel free to report problems introduced by this patch to: " + "Fabian Keil \n" + "Additional information about this patch can be found at: " + "http://www.fabiankeil.de/sourcecode/privoxy/\n" + "Patch version: 2006-08-02\n"); if (prog_name != NULL) { log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name); diff -ru ./filters.c /tmp/unionfs/privoxy-devel-oben/filters.c --- ./filters.c Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/filters.c Mon Jun 19 16:22:03 2006 @@ -721,6 +721,7 @@ struct http_response *block_url(struct client_state *csp) { struct http_response *rsp; + const char *new_content_type = NULL; /* * If it's not blocked, don't block it ;-) @@ -729,7 +730,10 @@ { return NULL; } - + if (csp->action->flags & ACTION_REDIRECT) + { + log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block."); + } /* * Else, prepare a response */ @@ -750,11 +754,26 @@ /* determine HOW images should be blocked */ p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER]; +/* + * In Privoxy current the next if blocks belongs below the char *p line + * which should be proper indendet, but to let this patch apply against 3.0.3 + * as well ... yadayada + */ + if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) + { + log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image."); + } #if 1 /* Two alternative strategies, use this one for now: */ /* and handle accordingly: */ if ((p == NULL) || (0 == strcmpic(p, "pattern"))) { + rsp->status = strdup("403 Request blocked by Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } rsp->body = bindup(image_pattern_data, image_pattern_length); if (rsp->body == NULL) { @@ -772,6 +791,12 @@ else if (0 == strcmpic(p, "blank")) { + rsp->status = strdup("403 Request blocked by Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } rsp->body = bindup(image_blank_data, image_blank_length); if (rsp->body == NULL) { @@ -828,6 +853,34 @@ } #endif /* Preceeding code is disabled for now */ } + else if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) + { + /* + * Send empty document. + */ + new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE]; + + freez(rsp->body); + rsp->body = strdup(" "); + rsp->content_length = 1; + + rsp->status = strdup("403 Request blocked by Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + if (new_content_type != 0) + { + log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type); + if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type)) + { + free_http_response(rsp); + return cgi_error_memory(); + } + } + + } else #endif /* def FEATURE_IMAGE_BLOCKING */ @@ -1070,18 +1123,55 @@ { char *p, *q; struct http_response *rsp; + char *redirect_mode = NULL; + int x, y; - p = q = csp->http->path; - log_error(LOG_LEVEL_REDIRECTS, "checking path for redirects: %s", p); - - /* - * find the last URL encoded in the request - */ - while ((p = strstr(p, "http://")) != NULL) + if ((csp->action->flags & ACTION_REDIRECT)) { - q = p++; + q = csp->action->string[ACTION_STRING_REDIRECT]; } + else + { + redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS]; + if (0 == strcmpic(redirect_mode, "check-decoded-url")) + { + p = q = csp->http->path; + log_error(LOG_LEVEL_REDIRECTS, "Decoding path: %s if necessary.", p); + while (*p) + { + if (*p == '%') /* Escape sequence? */ + { + /* Yes, translate from hexadecimal to decimal */ + p++; + /* First byte */ + x=((int)*p++)-48; + if (x>9) x-=7; + x<<=4; + /* Second byte */ + y=((int)*p++)-48; + if (y>9)y-=7; + /* Merge */ + *q++=(char)(x|y); + } + else + { + /* No, forward character. */ + *q++=*p++; + } + } + *q='\0'; + } + p = q = csp->http->path; + log_error(LOG_LEVEL_REDIRECTS, "Checking path for redirects: %s", p); + /* + * find the last URL encoded in the request + */ + while ((p = strstr(p, "http://")) != NULL) + { + q = p++; + } + } /* * if there was any, generate and return a HTTP redirect */ diff -ru ./jbsockets.c /tmp/unionfs/privoxy-devel-oben/jbsockets.c --- ./jbsockets.c Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/jbsockets.c Mon Jul 31 12:32:41 2006 @@ -787,6 +787,7 @@ { struct sockaddr_in inaddr; struct hostent *hostp; + unsigned int dns_retries = 0; #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS) struct hostent result; #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) @@ -807,8 +808,13 @@ if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1) { #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) - gethostbyname_r(host, &result, hbuf, - HOSTENT_BUFFER_SIZE, &hostp, &thd_err); + while ( gethostbyname_r(host, &result, hbuf, + HOSTENT_BUFFER_SIZE, &hostp, &thd_err) + && (thd_err == TRY_AGAIN) && (dns_retries++ < 10) ) + { + log_error(LOG_LEVEL_ERROR, "%u. timeout while trying to resolve %s. Trying again.", + dns_retries, host); + } #elif defined(HAVE_GETHOSTBYNAME_R_5_ARGS) hostp = gethostbyname_r(host, &result, hbuf, HOSTENT_BUFFER_SIZE, &thd_err); @@ -823,10 +829,20 @@ } #elif OSX_DARWIN pthread_mutex_lock(&gethostbyname_mutex); - hostp = gethostbyname(host); + while ( NULL == (hostp = gethostbyname(host)) + && (h_errno == TRY_AGAIN) && (dns_retries++ < 10) ) + { + log_error(LOG_LEVEL_ERROR, "%u. timeout while trying to resolve %s. Trying again.", + dns_retries, host); + } pthread_mutex_unlock(&gethostbyname_mutex); #else - hostp = gethostbyname(host); + while ( NULL == (hostp = gethostbyname(host)) + && (h_errno == TRY_AGAIN) && (dns_retries++ < 10) ) + { + log_error(LOG_LEVEL_ERROR, "%u. timeout while trying to resolve %s. Trying again.", + dns_retries, host); + } #endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */ /* * On Mac OSX, if a domain exists but doesn't have a type A diff -ru ./jcc.c /tmp/unionfs/privoxy-devel-oben/jcc.c --- ./jcc.c Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/jcc.c Mon Jul 17 12:05:31 2006 @@ -860,6 +860,7 @@ int server_body; int ms_iis5_hack = 0; int byte_count = 0; + unsigned int socks_retries = 0; const struct forward_spec * fwd; struct http_request *http; int len; /* for buffer sizes */ @@ -1013,13 +1014,22 @@ || (csp->action->flags & ACTION_LIMIT_CONNECT && !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) ) { - strcpy(buf, CFORBIDDEN); - write_socket(csp->cfd, buf, strlen(buf)); - - log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str); - log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str); - - return; + if (csp->action->flags & ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS) + { + /* The response will violate the specs, but makes unblocking easier. */ + log_error(LOG_LEVEL_ERROR, "Marking suspicious CONNECT request from %s for blocking.", + csp->ip_addr_str); + csp->action->flags |= ACTION_BLOCK; + http->ssl = 0; + } + else + { + strcpy(buf, CFORBIDDEN); + write_socket(csp->cfd, buf, strlen(buf)); + log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str); + return; + } } } @@ -1177,7 +1187,7 @@ csp->flags |= CSP_FLAG_REJECTED; #endif /* def FEATURE_STATISTICS */ - /* Log (FIXME: All intercept reasons apprear as "crunch" with Status 200) */ + /* Log (FIXME: All intercept reasons appear as "crunch" with Status 200) */ log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", csp->ip_addr_str, http->ocmd); @@ -1209,7 +1219,12 @@ /* here we connect to the server, gateway, or the forwarder */ - csp->sfd = forwarded_connect(fwd, http, csp); + while ( (csp->sfd = forwarded_connect(fwd, http, csp)) + && (errno == EINVAL) && (socks_retries++ < 3)) + { + log_error(LOG_LEVEL_ERROR, "failed the %u. time to connect to %s. Trying again.", + socks_retries, http->hostport); + } if (csp->sfd == JB_INVALID_SOCKET) { @@ -1439,7 +1454,8 @@ csp->content_length = csp->iob->eod - csp->iob->cur; } - hdr = sed(server_patterns, add_server_headers, csp); + hdr = sed(server_patterns_light, NULL, csp); + if (hdr == NULL) { /* FIXME Should handle error properly */ diff -ru ./parsers.c /tmp/unionfs/privoxy-devel-oben/parsers.c --- ./parsers.c Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/parsers.c Wed Aug 2 20:12:13 2006 @@ -444,6 +444,7 @@ #include #include #include +#include #if !defined(_WIN32) && !defined(__OS2__) #include @@ -482,37 +483,50 @@ const struct parsers client_patterns[] = { - { "referer:", 8, client_referrer }, + { "referer:", 8, client_referrer }, { "user-agent:", 11, client_uagent }, - { "ua-", 3, client_ua }, - { "from:", 5, client_from }, - { "cookie:", 7, client_send_cookie }, + { "ua-", 3, client_ua }, + { "from:", 5, client_from }, + { "cookie:", 7, client_send_cookie }, { "x-forwarded-for:", 16, client_x_forwarded }, { "Accept-Encoding:", 16, client_accept_encoding }, { "TE:", 3, client_te }, { "Host:", 5, client_host }, -/* { "if-modified-since:", 18, crumble }, */ + { "if-modified-since:", 18, client_if_modified_since }, { "Keep-Alive:", 11, crumble }, { "connection:", 11, crumble }, { "proxy-connection:", 17, crumble }, { "max-forwards:", 13, client_max_forwards }, - { NULL, 0, NULL } + { "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_header }, + { NULL, 0, NULL } }; - const struct parsers server_patterns[] = { - { "HTTP", 4, server_http }, - { "set-cookie:", 11, server_set_cookie }, - { "connection:", 11, crumble }, - { "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 }, - { "Keep-Alive:", 11, crumble }, + { "HTTP", 4, server_http }, + { "set-cookie:", 11, server_set_cookie }, + { "connection:", 11, crumble }, + { "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 }, + { "Keep-Alive:", 11, crumble }, + { "content-disposition:", 20, server_content_disposition }, + { "Last-Modified:", 14, server_last_modified }, + { "*", 0, crunch_server_header }, + { "*", 0, filter_header }, { NULL, 0, NULL } }; +const struct parsers server_patterns_light[] = { + { "Content-Length:", 15, server_content_length }, + { "Transfer-Encoding:", 18, server_transfer_coding }, + { NULL, 0, NULL } +}; const add_header_func_ptr add_client_headers[] = { client_host_adder, @@ -530,7 +544,6 @@ NULL }; - /********************************************************************* * * Function : flush_socket @@ -779,27 +792,62 @@ const struct parsers *v; const add_header_func_ptr *f; jb_err err = JB_ERR_OK; + int first_run; + + /* + * If filtering is enabled, sed is run twice, + * but most of the work needs to be done only once. + */ + first_run = (more_headers != NULL ) ? 1 : 0; - for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++) + if (first_run) /* Parse and print */ { - for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next) + for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++) { - /* Header crunch()ed in previous run? -> ignore */ - if (p->str == NULL) continue; + for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next) + { + /* Header crunch()ed in previous run? -> ignore */ + if (p->str == NULL) continue; - if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str); + if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str); - if (strncmpic(p->str, v->str, v->len) == 0) - { - err = v->parser(csp, (char **)&(p->str)); + /* Does the current parser handle this header? */ + if ((strncmpic(p->str, v->str, v->len) == 0) || (v->len == CHECK_EVERY_HEADER_REMAINING)) + { + err = v->parser(csp, (char **)&(p->str)); + } } } + /* place any additional headers on the csp->headers list */ + for (f = more_headers; (err == JB_ERR_OK) && (*f) ; f++) + { + err = (*f)(csp); + } } - - /* place any additional headers on the csp->headers list */ - for (f = more_headers; (err == JB_ERR_OK) && (*f) ; f++) + else /* Parse only */ { - err = (*f)(csp); + /* + * The second run is only needed if the body was modified + * and the content-lenght has changed. + */ + if (strncmpic(csp->http->cmd, "HEAD", 4)) + { + /*XXX: Code duplication*/ + for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++) + { + for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next) + { + /* Header crunch()ed in previous run? -> ignore */ + if (p->str == NULL) continue; + + /* Does the current parser handle this header? */ + if (strncmpic(p->str, v->str, v->len) == 0) + { + err = v->parser(csp, (char **)&(p->str)); + } + } + } + } } if (err != JB_ERR_OK) @@ -816,6 +864,149 @@ /********************************************************************* * + * Function : filter_header + * + * Description : Executes all text substitutions from all applying + * +filter actions on the header. + * Most of the code was copied from pcrs_filter_response, + * including the rather short variable names + * + * 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_header(struct client_state *csp, char **header) +{ + int hits=0; + int matches; + size_t size = strlen(*header); + + char *newheader = NULL; + pcrs_job *job; + + struct file_list *fl; + struct re_filterfile_spec *b; + struct list_entry *filtername; + + int i, found_filters = 0; + +/* Lets stay compatible to Privoxy 3.0.3 */ +#ifndef MAX_AF_FILES +# define MAX_AF_FILES 1 +# define INDEX_OR_NOT +#else +# define INDEX_OR_NOT [i] +#endif + + if (!(csp->action->flags & ACTION_FILTER_HEADERS)) + { + return(JB_ERR_OK); + } + log_error(LOG_LEVEL_RE_FILTER, "Entered filter_headers"); + /* + * Need to check the set of re_filterfiles... + */ + for (i = 0; i < MAX_AF_FILES; i++) + { + fl = csp->rlist INDEX_OR_NOT; + if (NULL != fl) + { + if (NULL != fl->f) + { + found_filters = 1; + break; + } + } + } + + if (0 == found_filters) + { + log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering."); + return(JB_ERR_OK); + } + + for (i = 0; i < MAX_AF_FILES; i++) + { + fl = csp->rlist INDEX_OR_NOT; + if ((NULL == fl) || (NULL == fl->f)) + break; + /* + * For all applying +filter actions, look if a filter by that + * name exists and if yes, execute it's pcrs_joblist on the + * buffer. + */ + for (b = fl->f; b; b = b->next) + { + for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first; + filtername ; filtername = filtername->next) + { + if (strcmp(b->name, filtername->str) == 0) + { + int current_hits = 0; + + if ( NULL == b->joblist ) + { + log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name); + continue; + } + + log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s (size %d) with filter %s...", + *header, size, b->name); + + /* Apply all jobs from the joblist */ + for (job = b->joblist; NULL != job; job = job->next) + { + matches = pcrs_execute(job, *header, size, &newheader, &size); + if ( 0 < matches ) + { + current_hits += matches; + log_error(LOG_LEVEL_HEADER, "Transforming \"%s\" to \"%s\"", *header, newheader); + freez(*header); + *header = newheader; + } + else if ( 0 == matches ) + { + /* Filter doesn't change header */ + freez(newheader); + } + else + { + /* RegEx failure */ + log_error(LOG_LEVEL_ERROR, "Filtering \'%s\' with \'%s\' didn't work out: %s", + *header, b->name, pcrs_strerror(matches)); + if( newheader != NULL) + { + log_error(LOG_LEVEL_ERROR, "Freeing what's left: %s", newheader); + freez(newheader); + } + } + } + log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size); + hits += current_hits; + } + } + } + } + + if ( 0 == size ) + { + log_error(LOG_LEVEL_HEADER, "Removing empty header %s", *header); + freez(*header); + } + log_error(LOG_LEVEL_RE_FILTER, "Leaving filter headers"); + return(JB_ERR_OK); + +} + + +/********************************************************************* + * * Function : crumble * * Description : This is called if a header matches a pattern to "crunch" @@ -833,12 +1024,47 @@ *********************************************************************/ jb_err crumble(struct client_state *csp, char **header) { - log_error(LOG_LEVEL_HEADER, "crunch!"); + log_error(LOG_LEVEL_HEADER, "crumble crunched: %s!", *header); freez(*header); return JB_ERR_OK; } +/********************************************************************* + * + * Function : crunch_server_header + * + * Description : Crunch server header if it matches a string supplied by the + * user. Called from `sed'. + * + * 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 crunch_server_header(struct client_state *csp, char **header) +{ + const char *crunch_pattern; + /*Is there a header to crunch*/ + + if ((csp->action->flags & ACTION_CRUNCH_SERVER_HEADER)) + { + crunch_pattern = csp->action->string[ACTION_STRING_SERVER_HEADER]; + /*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); + freez(*header); + } + } + + return JB_ERR_OK; +} /********************************************************************* * * Function : server_content_type @@ -863,9 +1089,14 @@ *********************************************************************/ jb_err server_content_type(struct client_state *csp, char **header) { + const char *newval; + + newval = csp->action->string[ACTION_STRING_CONTENT_TYPE]; + if (csp->content_type != CT_TABOO) { if ((strstr(*header, " text/") && !strstr(*header, "plain")) + || strstr(*header, "xml") || strstr(*header, "application/x-javascript")) csp->content_type = CT_TEXT; else if (strstr(*header, " image/gif")) @@ -875,7 +1106,52 @@ else csp->content_type = 0; } - + /* + * Are we enabling text mode by force? + */ + if (csp->action->flags & ACTION_FORCE_TEXT_MODE) + { + /* + * Do we really have to? + */ + if (csp->content_type == CT_TEXT) + { + log_error(LOG_LEVEL_HEADER, "Text mode is already enabled."); + } + else + { + csp->content_type = CT_TEXT; + log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!"); + } + } + /* + * Are we messing with the content type? + */ + if (csp->action->flags & ACTION_CONTENT_TYPE_OVERWRITE) + { + /* + * Make sure the user doesn't accidently + * change the content type of binary documents. + */ + if (csp->content_type == CT_TEXT) + { + freez(*header); + *header = strdup("Content-Type: "); + string_append(header, newval); + + if (header == NULL) + { + log_error(LOG_LEVEL_HEADER, "Insufficient memory. Conten-Type crunched without replacement!"); + return JB_ERR_MEMORY; + } + log_error(LOG_LEVEL_HEADER, "Modified: %s!", *header); + } + else + { + log_error(LOG_LEVEL_HEADER, "%s not replaced. It doesn't look like text. " + "Enable force-text-mode if you know what you're doing.", *header); + } + } return JB_ERR_OK; } @@ -925,6 +1201,7 @@ { freez(*header); *header = strdup("Transfer-Encoding: identity"); + log_error(LOG_LEVEL_HEADER, "Set: %s", *header); return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; } } @@ -985,8 +1262,12 @@ *********************************************************************/ jb_err server_content_length(struct client_state *csp, char **header) { - if (csp->content_length != 0) /* Content length has been modified */ + if (csp->content_length != 0) /* Content length could have been modified */ { + /* + * XXX: Shouldn't we check if csp->content_length + * is different than the original value? + */ freez(*header); *header = (char *) zalloc(100); if (*header == NULL) @@ -1032,6 +1313,189 @@ return JB_ERR_OK; } +/********************************************************************* + * + * Function : server_content_disposition + * + * Description : If enabled, blocks or modifies the "content-disposition" header. + * Called from `sed'. + * + * 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 server_content_disposition(struct client_state *csp, char **header) +{ + const char *newval; + + /* + * Are we messing with the content-disposition header? + */ + if ((csp->action->flags & ACTION_HIDE_CONTENT_DISPOSITION) == 0) + { + /*Me tinks not*/ + return JB_ERR_OK; + } + + newval = csp->action->string[ACTION_STRING_CONTENT_DISPOSITION]; + + if ((newval == NULL) || (0 == strcmpic(newval, "block")) ) + { + /* + * Blocking content-disposition header + */ + log_error(LOG_LEVEL_HEADER, "Crunching %s!", *header); + freez(*header); + return JB_ERR_OK; + } + else + { + /* + * Replacing content-disposition header + */ + freez(*header); + *header = strdup("content-disposition: "); + string_append(header, newval); + + if (*header == NULL) + { + log_error(LOG_LEVEL_HEADER, "Insufficent memory. content-disposition header not fully replaced."); + } + else + { + log_error(LOG_LEVEL_HEADER, "content-disposition header crunched and replaced with: %s", *header); + } + } + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; +} + +/********************************************************************* + * + * Function : server_last_modified + * + * Description : Changes Last-Modified header to the actual date + * to help hide-if-modified-since. + * Called from `sed'. + * + * 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 server_last_modified(struct client_state *csp, char **header) +{ + const char *newval; + char buf[BUFFER_SIZE]; + + char newheader[50]; + struct tm *timeptr; + time_t now, last_modified; + long int rtime; + long int days, hours, minutes, seconds; + + /* + * Are we messing with the Last-Modified header? + */ + if ((csp->action->flags & ACTION_OVERWRITE_LAST_MODIFIED) == 0) + { + /*Nope*/ + return JB_ERR_OK; + } + + newval = csp->action->string[ACTION_STRING_LAST_MODIFIED]; + + if (0 == strcmpic(newval, "block") ) + { + /* + * Blocking Last-Modified header. Useless but why not. + */ + log_error(LOG_LEVEL_HEADER, "Crunching %s!", *header); + freez(*header); + return JB_ERR_OK; + } + else if (0 == strcmpic(newval, "reset-to-request-time")) + { + /* + * Setting Last-Modified Header to now. + */ + get_http_time(0, buf); + freez(*header); + *header = strdup("Last-Modified: "); + string_append(header, buf); + + if (*header == NULL) + { + log_error(LOG_LEVEL_HEADER, "Insufficent memory. Last-Modified header got lost, buhu."); + } + else + { + log_error(LOG_LEVEL_HEADER, "Reset to present time: %s", *header); + } + } + else if (0 == strcmpic(newval, "randomize")) + { + log_error(LOG_LEVEL_HEADER, "Randomizing: %s", *header); + now = time(NULL); + timeptr = gmtime(&now); + if (strptime(*header, "Last-Modified: %a, %d %b %Y %T", timeptr) == NULL) + { + log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header); + freez(*header); + } + else + { + last_modified = timegm(timeptr); + rtime = difftime(now, last_modified); + if (rtime) + { + rtime = random() % rtime + 1; + last_modified += rtime; + timeptr = gmtime(&last_modified); + strftime(newheader, sizeof(newheader), "%a, %d %b %Y %T GMT", timeptr); + freez(*header); + *header = strdup("Last-Modified: "); + string_append(header, newheader); + + if (*header == NULL) + { + log_error(LOG_LEVEL_ERROR, " Insufficent memory, header crunched without replacement."); + return JB_ERR_MEMORY; + } + + if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */ + { + days = rtime / (3600 * 24); + hours = rtime / 3600 % 24; + minutes = rtime / 60 % 60; + seconds = rtime % 60; + + log_error(LOG_LEVEL_HEADER, "Randomized: %s (added %d da%s %d hou%s %d minut%s %d second%s", + *header, days, (days == 1) ? "y" : "ys", hours, (hours == 1) ? "r" : "rs", + minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)"); + } + } + else + { + log_error(LOG_LEVEL_HEADER, "Randomized ... or not. No time difference to work with."); + } + } + } + + return JB_ERR_OK; +} /********************************************************************* * @@ -1129,10 +1593,13 @@ jb_err client_referrer(struct client_state *csp, char **header) { const char *newval; - + const char *host; + char *referer; + int hostlenght; + #ifdef FEATURE_FORCE_LOAD /* Since the referrer can include the prefix even - * even if the request itself is non-forced, we must + * if the request itself is non-forced, we must * clean it unconditionally */ strclean(*header, FORCE_PREFIX); @@ -1146,27 +1613,73 @@ return JB_ERR_OK; } - freez(*header); - newval = csp->action->string[ACTION_STRING_REFERER]; + if ((0 != strcmpic(newval, "conditional-block"))) + { + freez(*header); + } if ((newval == NULL) || (0 == strcmpic(newval, "block")) ) { /* * Blocking referer */ - log_error(LOG_LEVEL_HEADER, "crunch!"); + log_error(LOG_LEVEL_HEADER, "Referer crunched!"); return JB_ERR_OK; } - else if (0 == strncmpic(newval, "http://", 7)) + else if (0 == strcmpic(newval, "conditional-block")) { /* - * We have a specific (fixed) referer we want to send. + * Block referer if host has changed. */ - log_error(LOG_LEVEL_HEADER, "modified"); + if (NULL == (host = strdup(csp->http->hostport))) + { + freez(*header); + log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary host copy."); + return JB_ERR_MEMORY; + } + if (NULL == (referer = strdup(*header))) + { + freez(*header); + freez(host); + log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary referer copy."); + return JB_ERR_MEMORY; + } + hostlenght = strlen(host); + if ( hostlenght < (strlen(referer)-17) ) /*referer begins with 'Referer: http[s]://'*/ + { + /*Shorten referer to make sure the referer is blocked + *if www.example.org/www.example.com-shall-see-the-referer/ + *links to www.example.com/ + */ + referer[hostlenght+17] = '\n'; + } + if ( 0 == strstr(referer, host)) /*Host has changed*/ + { + log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header); + freez(*header); + } + else + { + log_error(LOG_LEVEL_HEADER, "%s (not modified, still on %s)", *header, host); + } + freez(referer); + freez(host); + return JB_ERR_OK; + } + else if (0 != strcmpic(newval, "forge")) + { + /* + * We have a specific (fixed) referer we want to send. + */ + if ((0 != strncmpic(newval, "http://", 7)) && (0 != strncmpic(newval, "https://", 8))) + { + log_error(LOG_LEVEL_HEADER, "Parameter: +referrer{%s} is a bad idea, but I don't care.", newval); + } *header = strdup("Referer: "); string_append(header, newval); + log_error(LOG_LEVEL_HEADER, "Referer overwritten with: %s", *header); return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; } @@ -1176,23 +1689,115 @@ * Forge a referer as http://[hostname:port of REQUEST]/ * to fool stupid checks for in-site links */ - if (0 != strcmpic(newval, "forge")) - { - /* - * Invalid choice - but forge is probably the best default. - */ - log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval); - } *header = strdup("Referer: http://"); string_append(header, csp->http->hostport); string_append(header, "/"); - log_error(LOG_LEVEL_HEADER, "crunch+forge to %s", *header); + log_error(LOG_LEVEL_HEADER, "Referer forged to: %s", *header); return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; } } +/********************************************************************* + * + * Function : client_accept_language + * + * Description : Handle the "Accept-Language" config setting properly. + * Called from `sed'. + * + * 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 client_accept_language(struct client_state *csp, char **header) +{ + const char *newval; + + /* + * Are we messing with the Accept-Language? + */ + if ((csp->action->flags & ACTION_HIDE_ACCEPT_LANGUAGE) == 0) + { + /*I don't think so*/ + return JB_ERR_OK; + } + + newval = csp->action->string[ACTION_STRING_LANGUAGE]; + + if ((newval == NULL) || (0 == strcmpic(newval, "block")) ) + { + /* + * Blocking Accept-Language header + */ + log_error(LOG_LEVEL_HEADER, "Crunching Accept-Language!"); + freez(*header); + return JB_ERR_OK; + } + else + { + /* + * Replacing Accept-Language header + */ + freez(*header); + *header = strdup("Accept-Language: "); + string_append(header, newval); + + if (*header == NULL) + { + log_error(LOG_LEVEL_ERROR, " Insufficent memory. Accept-Language header crunched without replacement."); + } + else + { + log_error(LOG_LEVEL_HEADER, "Accept-Language header crunched and replaced with: %s", *header); + } + } + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; +} + +/********************************************************************* + * + * Function : crunch_client_header + * + * Description : Crunch client header if it matches a string supplied by the + * user. Called from `sed'. + * + * 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 crunch_client_header(struct client_state *csp, char **header) +{ + const char *crunch_pattern; + /*Is there a header to crunch*/ + + if ((csp->action->flags & ACTION_CRUNCH_CLIENT_HEADER)) + { + crunch_pattern = csp->action->string[ACTION_STRING_CLIENT_HEADER]; + + /*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); + freez(*header); + } + } + return JB_ERR_OK; +} + /********************************************************************* * @@ -1228,16 +1833,15 @@ return JB_ERR_OK; } - log_error(LOG_LEVEL_HEADER, "modified"); - freez(*header); *header = strdup("User-Agent: "); string_append(header, newval); + log_error(LOG_LEVEL_HEADER, "Modified: %s", *header); + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; } - /********************************************************************* * * Function : client_ua @@ -1259,7 +1863,7 @@ { if ((csp->action->flags & ACTION_HIDE_USER_AGENT) != 0) { - log_error(LOG_LEVEL_HEADER, "crunch!"); + log_error(LOG_LEVEL_HEADER, "crunched User-Agent!"); freez(*header); } @@ -1303,7 +1907,7 @@ */ if ((newval == NULL) || (0 == strcmpic(newval, "block")) ) { - log_error(LOG_LEVEL_HEADER, "crunch!"); + log_error(LOG_LEVEL_HEADER, "crunched From!"); return JB_ERR_OK; } @@ -1394,7 +1998,7 @@ else { freez(*header); - log_error(LOG_LEVEL_HEADER, " crunch!"); + log_error(LOG_LEVEL_HEADER, "crunched x-forwarded-for!"); } return JB_ERR_OK; @@ -1520,6 +2124,179 @@ return JB_ERR_OK; } +/********************************************************************* + * + * Function : client_if_modified_since + * + * Description : Remove or modify the If-Modified-Since header. + * + * 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 client_if_modified_since(struct client_state *csp, char **header) +{ + char newheader[50]; + struct tm *timeptr; + time_t tm = 0; + const char *newval; + time_t rtime; + time_t hours, minutes, seconds; + int negative = 0; + char * endptr; + + if ( 0 == strcmpic(*header, "If-Modified-Since: Wed, 08 Jun 1955 12:00:00 GMT")) + { + /* + * The client got an error message because of a temporary problem, + * the problem is gone and the client now tries to revalidate our + * error message on the real server. The revalidation would always + * end with the transmission of the whole document and there is + * no need to expose the bogus If-Modified-Since header. + */ + log_error(LOG_LEVEL_HEADER, "Crunching useless If-Modified-Since header."); + freez(*header); + } + else if (csp->action->flags & ACTION_HIDE_IF_MODIFIED_SINCE) + { + newval = csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE]; + + if ((0 == strcmpic(newval, "block"))) + { + log_error(LOG_LEVEL_HEADER, "Crunching %s", *header); + freez(*header); + } + else /* add random value */ + { + /* + * tm must be initinalized to prevent segmentation faults. + */ + timeptr = gmtime(&tm); + if (strptime(*header, "If-Modified-Since: %a, %d %b %Y %T", timeptr) == NULL) + { + log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header); + freez(*header); + } + else + { + rtime = strtol(newval, &endptr, 0); + + log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d hou%s)", + *header, rtime, (rtime == 1 || rtime == -1) ? "r": "rs"); + + rtime *= 3600; + rtime = random() % rtime; + + if(newval[0] == '-') + { + rtime *= -1; + } + tm = timegm(timeptr) + rtime; + timeptr = gmtime(&tm); + strftime(newheader, sizeof(newheader), "%a, %d %b %Y %T GMT", timeptr); + + freez(*header); + *header = strdup("If-Modified-Since: "); + string_append(header, newheader); + + if (*header == NULL) + { + log_error(LOG_LEVEL_HEADER, " Insufficent memory, header crunched without replacement."); + return JB_ERR_MEMORY; + } + + if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */ + { + if(rtime < 0) + { + rtime *= -1; + negative = 1; + } + hours = rtime / 3600 % 24; + minutes = rtime / 60 % 60; + seconds = rtime % 60; + + log_error(LOG_LEVEL_HEADER, "Randomized: %s (%s %d hou%s %d minut%s %d second%s", + *header, (negative) ? "subtracted" : "added", hours, (hours == 1) ? "r" : "rs", + minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)"); + } + } + } + } + + return JB_ERR_OK; +} + +/********************************************************************* + * + * Function : client_if_none_match + * + * Description : Remove the If-None-Match header. + * + * 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 client_if_none_match(struct client_state *csp, char **header) +{ + if (csp->action->flags & ACTION_CRUNCH_IF_NONE_MATCH) + { + log_error(LOG_LEVEL_HEADER, "Crunching %s", *header); + freez(*header); + } + + return JB_ERR_OK; +} + +/********************************************************************* + * + * Function : client_x_filter + * + * Description : Disables filtering if the client set "X-Filter: No". + * Called from `sed'. + * + * 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 + * + *********************************************************************/ +jb_err client_x_filter(struct client_state *csp, char **header) +{ + if ( 0 == strcmpic(*header, "X-Filter: No")) + { + if (csp->action->flags & ACTION_FORCE_TEXT_MODE) + { + log_error(LOG_LEVEL_HEADER, "force-text-mode overruled the client's request to disable filtering!"); + } + else + { + csp->content_type = CT_TABOO; + log_error(LOG_LEVEL_HEADER, "Disabled filter mode on behalf of the client."); + } + log_error(LOG_LEVEL_HEADER, "Crunching %s", *header); + freez(*header); + } + return JB_ERR_OK; +} /* the following functions add headers directly to the header list */ @@ -1761,6 +2538,7 @@ *********************************************************************/ jb_err connection_close_adder(struct client_state *csp) { + log_error(LOG_LEVEL_HEADER, "Adding: Connection: close"); return enlist(csp->headers, "Connection: close"); } diff -ru ./parsers.h /tmp/unionfs/privoxy-devel-oben/parsers.h --- ./parsers.h Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/parsers.h Sun Jul 16 18:50:38 2006 @@ -188,6 +188,7 @@ extern const struct parsers client_patterns[]; extern const struct parsers server_patterns[]; +extern const struct parsers server_patterns_light[]; extern const add_header_func_ptr add_client_headers[]; extern const add_header_func_ptr add_server_headers[]; @@ -197,6 +198,7 @@ extern char *get_header(struct client_state *csp); extern char *get_header_value(const struct list *header_list, const char *header_name); extern char *sed(const struct parsers pats[], const add_header_func_ptr more_headers[], struct client_state *csp); +extern void get_http_time(int time_offset, char *buf); extern jb_err crumble (struct client_state *csp, char **header); extern jb_err client_referrer (struct client_state *csp, char **header); @@ -209,6 +211,12 @@ extern jb_err client_te (struct client_state *csp, char **header); extern jb_err client_max_forwards (struct client_state *csp, char **header); extern jb_err client_host(struct client_state *csp, char **header); +extern jb_err client_if_modified_since(struct client_state *csp, char **header); +extern jb_err client_accept_language (struct client_state *csp, char **header); +extern jb_err client_if_none_match (struct client_state *csp, char **header); +extern jb_err crunch_client_header (struct client_state *csp, char **header); +extern jb_err filter_header (struct client_state *csp, char **header); +extern jb_err client_x_filter (struct client_state *csp, char **header); extern jb_err client_host_adder (struct client_state *csp); @@ -226,6 +234,9 @@ extern jb_err server_content_encoding(struct client_state *csp, char **header); extern jb_err server_transfer_coding (struct client_state *csp, char **header); extern jb_err server_http (struct client_state *csp, char **header); +extern jb_err crunch_server_header (struct client_state *csp, char **header); +extern jb_err server_last_modified (struct client_state *csp, char **header); +extern jb_err server_content_disposition(struct client_state *csp, char **header); #ifdef FEATURE_FORCE_LOAD extern int strclean(const char *string, const char *substring); @@ -234,6 +245,8 @@ /* Revision control strings from this header and associated .c file */ extern const char parsers_rcs[]; extern const char parsers_h_rcs[]; + +extern int debug; #ifdef __cplusplus } /* extern "C" */ diff -ru ./project.h /tmp/unionfs/privoxy-devel-oben/project.h --- ./project.h Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/project.h Sun Jul 16 20:10:09 2006 @@ -880,19 +880,74 @@ #define ACTION_JPEG_INSPECT 0x00020000UL /** Action string index: How to deanimate GIFs */ -#define ACTION_STRING_DEANIMATE 0 +#define ACTION_STRING_DEANIMATE 0 /** Action string index: Replacement for "From:" header */ -#define ACTION_STRING_FROM 1 +#define ACTION_STRING_FROM 1 /** Action string index: How to block images */ -#define ACTION_STRING_IMAGE_BLOCKER 2 +#define ACTION_STRING_IMAGE_BLOCKER 2 /** Action string index: Replacement for "Referer:" header */ -#define ACTION_STRING_REFERER 3 +#define ACTION_STRING_REFERER 3 /** Action string index: Replacement for "User-Agent:" header */ -#define ACTION_STRING_USER_AGENT 4 +#define ACTION_STRING_USER_AGENT 4 /** Action string index: Legal CONNECT ports. */ -#define ACTION_STRING_LIMIT_CONNECT 5 +#define ACTION_STRING_LIMIT_CONNECT 5 +/** Action string index: Server headers containing this pattern are crunched*/ +#define ACTION_STRING_SERVER_HEADER 6 +/** Action string index: Client headers containing this pattern are crunched*/ +#define ACTION_STRING_CLIENT_HEADER 7 +/** Action string index: Replacement for the "Accept-Language:" header*/ +#define ACTION_STRING_LANGUAGE 8 +/** Action string index: Replacement for the "Content-Type:" header*/ +#define ACTION_STRING_CONTENT_TYPE 9 +/** Action string index: Replacement for the "content-dispostion:" header*/ +#define ACTION_STRING_CONTENT_DISPOSITION 10 +/** Action string index: Replacement for the "If-Modified-Since:" header*/ +#define ACTION_STRING_IF_MODIFIED_SINCE 11 +/** Action string index: Replacement for the "Last-Modified:" header. */ +#define ACTION_STRING_LAST_MODIFIED 12 +/** Action string index: Redirect URL */ +#define ACTION_STRING_REDIRECT 13 +/** Action string index: Decode before redirect? */ +#define ACTION_STRING_FAST_REDIRECTS 14 /** Number of string actions. */ -#define ACTION_STRING_COUNT 6 +#define ACTION_STRING_COUNT 15 + +/* + * These defines really belong a few lines higher, + * but moving them down here makes this patch apply + * against Privoxy 3.0.3 as well. + */ +/** Action bitmap: Crunch or modify "if-modified-since" header. */ +#define ACTION_HIDE_IF_MODIFIED_SINCE 0x00040000UL +/** Action bitmap: Overwrite Content-Type header. */ +#define ACTION_CONTENT_TYPE_OVERWRITE 0x00080000UL +/** Action bitmap: Crunch specified server header. */ +#define ACTION_CRUNCH_SERVER_HEADER 0x00100000UL +/** Action bitmap: Crunch specified client header */ +#define ACTION_CRUNCH_CLIENT_HEADER 0x00200000UL +/** Action bitmap: Enable text mode by force */ +#define ACTION_FORCE_TEXT_MODE 0x00400000UL +/** Action bitmap: Enable text mode by force */ +#define ACTION_CRUNCH_IF_NONE_MATCH 0x00800000UL +/** Action bitmap: Enable content-dispostion crunching */ +#define ACTION_HIDE_CONTENT_DISPOSITION 0x01000000UL +/** Action bitmap: Replace or block Last-Modified header */ +#define ACTION_OVERWRITE_LAST_MODIFIED 0x02000000UL +/** Action bitmap: Replace or block Accept-Language header */ +#define ACTION_HIDE_ACCEPT_LANGUAGE 0x04000000UL +/** Action bitmap: Block as empty document */ +#define ACTION_HANDLE_AS_EMPTY_DOCUMENT 0x08000000UL +/** Action bitmap: Redirect request. */ +#define ACTION_REDIRECT 0x10000000UL +/** Action bitmap: Answer blocked Connects verbosely */ +#define ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS 0x20000000UL +/** Action bitmap: Filter headers with pcre */ +#define ACTION_FILTER_HEADERS 0x40000000UL + + +/*To make the ugly hack in sed easier to understand*/ +#define CHECK_EVERY_HEADER_REMAINING 0 + /** Index into current_action_spec::multi[] for headers to add. */ #define ACTION_MULTI_ADD_HEADER 0 diff -ru ./standard.action /tmp/unionfs/privoxy-devel-oben/standard.action --- ./standard.action Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/standard.action Wed Jul 19 14:18:31 2006 @@ -129,7 +129,7 @@ +crunch-incoming-cookies \ +deanimate-gifs{last} \ -downgrade-http-version \ -+fast-redirects \ ++fast-redirects{check-decoded-url} \ +filter{js-annoyances} \ -filter{js-events} \ +filter{html-annoyances} \ diff -ru ./templates/edit-actions-for-url /tmp/unionfs/privoxy-devel-oben/templates/edit-actions-for-url --- ./templates/edit-actions-for-url Wed Aug 2 20:51:48 2006 +++ /tmp/unionfs/privoxy-devel-oben/templates/edit-actions-for-url Wed Jul 19 15:51:38 2006 @@ -393,6 +393,18 @@ + + +

Warning:

+

+ This Privoxy version is not supported by the Privoxy Team. + You are using an unofficial patch.
+ Patch-Version: 2006-08-02. +

+ + + # This will only appear if CODE_STATUS is "alpha" or "beta". See configure.in @@ -536,7 +548,8 @@ name="downgrade_http_version" value="N" @downgrade-http-version-n@> - downgrade-http-version + downgrade-http-version Change HTTP/1.1 requests to HTTP/1.0. Only change if you know what you're doing! @@ -553,7 +566,22 @@ fast-redirects Bypass some click-tracking URLs. - + + +   +   +   +   + + +
+ + +   hide-forwarded-for-headers Block any existing X-Forwarded-for header, and do not add a new one. + + + + + + hide-if-modified-since + Remove or randomize If-Modified-Since header. + + +   +   +   +   + + Useful for filter testing.
+ + hour(s). + To appreciate this option a small amount of paranoia is required, + but at least in theory the If-Modified-Since header could be used + to keep track of your visits. + + + + + + + + overwrite-last-modified + Remove or randomize If-Modified-Since header. + + +   +   +   +   + + Useless.
+ +
+ +
+ + + + + + + + crunch-if-none-match + Remove If-None-Match header to circumvent browser cache. Useful for filter testing, + but hurts performance. + + + + + + content-type-overwrite + Replace Content-Type header. Useful to let the browser render broken + XHTML as broken + HTML. By default it only applies to + text documents, if you know what you're doing you + can enable force-text-mode to modify binary content types as well. + + +   +   +   +   + New Content-Type:
+ + + + + + + + force-text-mode + + Enable filtering on documents whose Content-Type wasn't recognized as text. + Do think twice, nothing is alright. + + + + + + + handle-as-empty-document + + Block with an empty document instead of an Image or HTML message. + The empty document contains only a space and can safely be parsed + as JavaScript or Style Sheet. Use content-type-overwrite to specify the + Content-Type, default is test/html. + + + + + + + treat-forbidden-connects-like-blocks + + Answer forbidden Connect requests with the usual block message. + May be a protocol violation, but makes unblocking easier. + + + + + + + + crunch-client-header + Remove header(s) matching the supplied pattern. Together with add-header you can + build a custom header replacement action. + + +   +   +   +   + Header string to suppress:
+ + + + + + + + + crunch-server-header + Remove server header(s) matching the supplied pattern. + + +   +   +   +   + Header string to suppress:
+ + + + + + + + hide-accept-language + Pretend to have different language settings. (Makes a fake User-Agent more believable, + but can lead to foreign content.) + + +   +   +   +   + + This isn't the option you're looking for.
+
+
+ + + + + + + hide-content-disposition + Block or overwrite the content-disposition header. Useful to view a document inside the browser, + even if you were supposed to save it first, or to change the suggested file name. + + +   +   +   +   + +
+
+
+ + + + + + + redirect + Redirect to another address. + + +   +   +   +   + Address to redirect to:
+ + + + + + + + filter-headers + Apply filters to the headers as well. Only enable this if you know what you're doing. + + + + + Fake as this web address:
+ value="@hide-referrer-param@">
+ +
+ - kill-popups + kill-popups Filter the website through a built-in filter to disable many JavaScript pop-up windows.