X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=cgi.c;h=866755d4f70a6c539205a11d76cdb160ede87723;hp=32e6bec4e6746b1f6075fd2c0ea474563df82be7;hb=7055dabf9dd0294cc8e1cf78e12a1b606e89d684;hpb=e21e921820c7b16f2594d269749e11ef785e179f
diff --git a/cgi.c b/cgi.c
index 32e6bec4..866755d4 100644
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.74 2006/09/06 18:45:03 fabiankeil Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.94 2007/02/08 19:44:49 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -11,8 +11,8 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.74 2006/09/06 18:45:03 fabiankeil Exp $";
* Functions declared include:
*
*
- * Copyright : Written by and Copyright (C) 2001 the SourceForge
- * Privoxy team. http://www.privoxy.org/
+ * Copyright : Written by and Copyright (C) 2001-2004, 2006-2007
+ * the SourceForge Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
* by and Copyright (C) 1997 Anonymous Coders and
@@ -38,6 +38,103 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.74 2006/09/06 18:45:03 fabiankeil Exp $";
*
* Revisions :
* $Log: cgi.c,v $
+ * Revision 1.94 2007/02/08 19:44:49 fabiankeil
+ * Use a transparent background for the PNG replacement pattern.
+ *
+ * Revision 1.93 2007/02/07 10:45:22 fabiankeil
+ * - Save the reason for generating http_responses.
+ * - Fix --disable-toggle (again).
+ * - Use TBL birthday hack for 403 responses as well.
+ * - Uglify the @menu@ again to fix JavaScript
+ * errors on the "blocked" template.
+ * - Escape an ampersand in cgi_error_unknown().
+ *
+ * Revision 1.92 2007/01/28 13:41:17 fabiankeil
+ * - Add HEAD support to finish_http_response.
+ * - Add error favicon to internal HTML error messages.
+ *
+ * Revision 1.91 2007/01/27 13:09:16 fabiankeil
+ * Add new config option "templdir" to
+ * change the templates directory.
+ *
+ * Revision 1.90 2007/01/25 13:47:26 fabiankeil
+ * Added "forwarding-failed" template support for error_response().
+ *
+ * Revision 1.89 2007/01/23 15:51:16 fabiankeil
+ * Add favicon delivery functions.
+ *
+ * Revision 1.88 2007/01/23 13:14:32 fabiankeil
+ * - Map variables that aren't guaranteed to be
+ * pure ASCII html_encoded.
+ * - Use CGI_PREFIX to generate URL for user manual
+ * CGI page to make sure CGI_SITE_2_PATH is included.
+ *
+ * Revision 1.87 2007/01/22 15:34:13 fabiankeil
+ * - "Protect" against a rather lame JavaScript-based
+ * Privoxy detection "attack" and check the referrer
+ * before delivering the CGI style sheet.
+ * - Move referrer check for unsafe CGI pages into
+ * referrer_is_safe() and log the result.
+ * - Map @url@ in cgi-error-disabled page.
+ * It's required for the "go there anyway" link.
+ * - Mark *csp as immutable for grep_cgi_referrer().
+ *
+ * Revision 1.86 2007/01/09 11:54:26 fabiankeil
+ * Fix strdup() error handling in cgi_error_unknown()
+ * and cgi_error_no_template(). Reported by Markus Elfring.
+ *
+ * Revision 1.85 2007/01/05 14:19:02 fabiankeil
+ * Handle pcrs_execute() errors in template_fill() properly.
+ *
+ * Revision 1.84 2006/12/28 17:54:22 fabiankeil
+ * Fixed gcc43 conversion warnings and replaced sprintf
+ * calls with snprintf to give OpenBSD's gcc one less reason
+ * to complain.
+ *
+ * Revision 1.83 2006/12/17 19:35:19 fabiankeil
+ * Escape ampersand in Privoxy menu.
+ *
+ * Revision 1.82 2006/12/17 17:53:39 fabiankeil
+ * Suppress the toggle link if remote toggling is disabled.
+ *
+ * Revision 1.81 2006/12/09 13:49:16 fabiankeil
+ * Fix configure option --disable-toggle.
+ * Thanks to Peter Thoenen for reporting this.
+ *
+ * Revision 1.80 2006/12/08 14:45:32 fabiankeil
+ * Don't lose the FORCE_PREFIX in case of
+ * connection problems. Fixes #612235.
+ *
+ * Revision 1.79 2006/11/13 19:05:50 fabiankeil
+ * Make pthread mutex locking more generic. Instead of
+ * checking for OSX and OpenBSD, check for FEATURE_PTHREAD
+ * and use mutex locking unless there is an _r function
+ * available. Better safe than sorry.
+ *
+ * Fixes "./configure --disable-pthread" and should result
+ * in less threading-related problems on pthread-using platforms,
+ * but it still doesn't fix BR#1122404.
+ *
+ * Revision 1.78 2006/09/21 19:22:07 fabiankeil
+ * Use CGI_PREFIX to check the referrer.
+ * The check for "http://config.privoxy.org/" fails
+ * if the user modified CGI_SITE_2_HOST.
+ *
+ * Revision 1.77 2006/09/21 15:17:23 fabiankeil
+ * Adjusted headers for Privoxy's cgi responses:
+ * Don't set Last-Modified, Expires and Cache-Control
+ * headers for redirects; always set "Connection: close".
+ *
+ * Revision 1.76 2006/09/07 14:06:38 fabiankeil
+ * Only predate the Last-Modified header for cgi responses
+ * that are delivered with status code 404 or 503.
+ *
+ * Revision 1.75 2006/09/07 11:56:39 fabiankeil
+ * Mark cgi_send_user_manual as harmless,
+ * to fix the access denied problem Hal spotted.
+ * The manual has no secret content, therefore we
+ * don't have to care about "secure" referrers.
+ *
* Revision 1.74 2006/09/06 18:45:03 fabiankeil
* Incorporate modified version of Roland Rosenfeld's patch to
* optionally access the user-manual via Privoxy. Closes patch 679075.
@@ -490,6 +587,7 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.74 2006/09/06 18:45:03 fabiankeil Exp $";
#include "encode.h"
#include "ssplit.h"
#include "errlog.h"
+#include "filters.h"
#include "miscutil.h"
#include "cgisimple.h"
#ifdef FEATURE_CGI_EDIT_ACTIONS
@@ -498,7 +596,6 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.74 2006/09/06 18:45:03 fabiankeil Exp $";
#include "loadcfg.h"
/* loadcfg.h is for global_toggle_state only */
#ifdef FEATURE_PTHREAD
-#include Privoxy ran out of memory while processing your request. Privoxy encountered an error while processing your request: Privoxy encountered an error while processing your request:500 Internal Privoxy Error
\r\n"
"500 Internal Privoxy Error
\r\n"
"500 Internal Privoxy Error
\r\n"
"
Please " - "" + "" "file a bug report.
\r\n" "\r\n" "\r\n"; @@ -1503,8 +1723,9 @@ jb_err cgi_error_unknown(struct client_state *csp, rsp->content_length = 0; rsp->head_length = 0; rsp->is_static = 0; + rsp->reason = RSP_REASON_INTERNAL_ERROR; - sprintf(errnumbuf, "%d", error_to_report); + snprintf(errnumbuf, sizeof(errnumbuf), "%d", error_to_report); rsp->body = malloc(strlen(body_prefix) + strlen(errnumbuf) + strlen(body_suffix) + 1); if (rsp->body == NULL) @@ -1516,7 +1737,7 @@ jb_err cgi_error_unknown(struct client_state *csp, strcat(rsp->body, body_suffix); rsp->status = strdup(status); - if (rsp->body == NULL) + if (rsp->status == NULL) { return JB_ERR_MEMORY; } @@ -1673,7 +1894,7 @@ void get_http_time(int time_offset, char *buf) struct tm *t; time_t current_time; -#if defined(HAVE_GMTIME_R) && !defined(OSX_DARWIN) +#if defined(HAVE_GMTIME_R) /* * Declare dummy up here (instead of inside get/set gmt block) so it * doesn't go out of scope before it's potentially used in snprintf later. @@ -1691,12 +1912,12 @@ void get_http_time(int time_offset, char *buf) /* get and save the gmt */ { -#ifdef OSX_DARWIN +#if HAVE_GMTIME_R + t = gmtime_r(¤t_time, &dummy); +#elif FEATURE_PTHREAD pthread_mutex_lock(&gmtime_mutex); t = gmtime(¤t_time); pthread_mutex_unlock(&gmtime_mutex); -#elif HAVE_GMTIME_R - t = gmtime_r(¤t_time, &dummy); #else t = gmtime(¤t_time); #endif @@ -1723,6 +1944,8 @@ void get_http_time(int time_offset, char *buf) * * Description : Fill in the missing headers in an http response, * and flatten the headers to an http head. + * For HEAD requests the body is freed once + * the Content-Length header is set. * * Parameters : * 1 : rsp = pointer to http_response to be processed @@ -1731,7 +1954,7 @@ void get_http_time(int time_offset, char *buf) * On error, free()s rsp and returns cgi_error_memory() * *********************************************************************/ -struct http_response *finish_http_response(struct http_response *rsp) +struct http_response *finish_http_response(const struct client_state *csp, struct http_response *rsp) { char buf[BUFFER_SIZE]; jb_err err; @@ -1747,7 +1970,7 @@ struct http_response *finish_http_response(struct http_response *rsp) /* * Fill in the HTTP Status */ - sprintf(buf, "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK"); + snprintf(buf, sizeof(buf), "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK"); err = enlist_first(rsp->headers, buf); /* @@ -1759,14 +1982,41 @@ struct http_response *finish_http_response(struct http_response *rsp) } if (!err) { - sprintf(buf, "Content-Length: %d", (int)rsp->content_length); + snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length); err = enlist(rsp->headers, buf); } - /* - * Fill in the default headers: + if (0 == strcmpic(csp->http->gpc, "head")) + { + /* + * The client only asked for the head. Dispose + * the body and log an offensive message. + * + * While it may seem to be a bit inefficient to + * prepare the body if it isn't needed, it's the + * only way to get the Content-Length right for + * dynamic pages. We could have disposed the body + * earlier, but not without duplicating the + * Content-Length setting code above. + */ + log_error(LOG_LEVEL_CGI, "Preparing to give head to %s.", csp->ip_addr_str); + freez(rsp->body); + rsp->content_length = 0; + } + + if (strncmpic(rsp->status, "302", 3)) + { + /* + * If it's not a redirect without any content, + * set the Content-Type to text/html if it's + * not already specified. + */ + if (!err) err = enlist_unique(rsp->headers, "Content-Type: text/html", 13); + } + + /* + * Fill in the rest of the default headers: * - * Content-Type: default to text/html if not already specified. * Date: set to current date/time. * Last-Modified: set to date/time the page was last changed. * Expires: set to date/time page next needs reloading. @@ -1774,8 +2024,6 @@ struct http_response *finish_http_response(struct http_response *rsp) * * See http://www.w3.org/Protocols/rfc2068/rfc2068 */ - if (!err) err = enlist_unique(rsp->headers, "Content-Type: text/html", 13); - if (rsp->is_static) { /* @@ -1798,6 +2046,11 @@ struct http_response *finish_http_response(struct http_response *rsp) err = enlist_unique_header(rsp->headers, "Expires", buf); } } + else if (!strncmpic(rsp->status, "302", 3)) + { + get_http_time(0, buf); + if (!err) err = enlist_unique_header(rsp->headers, "Date", buf); + } else { /* @@ -1805,16 +2058,16 @@ struct http_response *finish_http_response(struct http_response *rsp) * 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 + * If a temporary problem occurs 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. + * For documents delivered with status code 403, 404 and 503 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. @@ -1823,11 +2076,27 @@ struct http_response *finish_http_response(struct http_response *rsp) 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", "Wed, 08 Jun 1955 12:00:00 GMT"); + if (!strncmpic(rsp->status, "403", 3) + || !strncmpic(rsp->status, "404", 3) + || !strncmpic(rsp->status, "503", 3)) + { + if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Wed, 08 Jun 1955 12:00:00 GMT"); + } + else + { + if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf); + } 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"); } + /* + * Quoting RFC 2616: + * + * HTTP/1.1 applications that do not support persistent connections MUST + * include the "close" connection option in every message. + */ + if (!err) err = enlist_unique_header(rsp->headers, "Connection", "close"); /* * Write the head @@ -1897,9 +2166,8 @@ void free_http_response(struct http_response *rsp) * Function : template_load * * Description : CGI support function that loads a given HTML - * template from the confdir, ignoring comment - * lines and following #include statements up to - * a depth of 1. + * template, ignoring comment lines and following + * #include statements up to a depth of 1. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -1946,11 +2214,23 @@ jb_err template_load(struct client_state *csp, char **template_ptr, } } - /* Generate full path */ + /* + * Generate full path using either templdir + * or confdir/templates as base directory. + */ + if (NULL != csp->config->templdir) + { + templates_dir_path = strdup(csp->config->templdir); + } + else + { + templates_dir_path = make_path(csp->config->confdir, "templates"); + } - templates_dir_path = make_path(csp->config->confdir, "templates"); if (templates_dir_path == NULL) { + log_error(LOG_LEVEL_ERROR, "Out of memory while generating template path for %s.", + templatename); return JB_ERR_MEMORY; } @@ -1958,6 +2238,8 @@ jb_err template_load(struct client_state *csp, char **template_ptr, free(templates_dir_path); if (full_path == NULL) { + log_error(LOG_LEVEL_ERROR, "Out of memory while generating full template path for %s.", + templatename); return JB_ERR_MEMORY; } @@ -1966,6 +2248,7 @@ jb_err template_load(struct client_state *csp, char **template_ptr, file_buffer = strdup(""); if (file_buffer == NULL) { + log_error(LOG_LEVEL_ERROR, "Not enough free memory to buffer %s.", full_path); free(full_path); return JB_ERR_MEMORY; } @@ -2047,7 +2330,7 @@ jb_err template_load(struct client_state *csp, char **template_ptr, * Caller must free(). * 2 : exports = map with fill in symbol -> name pairs * - * Returns : JB_ERR_OK on success + * Returns : JB_ERR_OK on success (and for uncritical errors) * JB_ERR_MEMORY on out-of-memory error * *********************************************************************/ @@ -2118,15 +2401,35 @@ jb_err template_fill(char **template_ptr, const struct map *exports) } else { - pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size); - free(file_buffer); + error = pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size); + pcrs_free_job(job); if (NULL == tmp_out_buffer) { *template_ptr = NULL; return JB_ERR_MEMORY; } - file_buffer = tmp_out_buffer; + + if (error < 0) + { + /* + * Substitution failed, keep the original buffer, + * log the problem and ignore it. + * + * The user might see some unresolved @CGI_VARIABLES@, + * but returning a special CGI error page seems unreasonable + * and could mask more important error messages. + */ + free(tmp_out_buffer); + log_error(LOG_LEVEL_ERROR, "Failed to execute s/%s/%s/%s. %s", + buf, m->value, flags, pcrs_strerror(error)); + } + else + { + /* Substitution succeeded, use modified buffer. */ + free(file_buffer); + file_buffer = tmp_out_buffer; + } } } @@ -2222,16 +2525,18 @@ struct map *default_exports(const struct client_state *csp, const char *caller) if (!err) err = map(exports, "my-hostname", 1, html_encode(csp->my_hostname ? csp->my_hostname : "unknown"), 0); if (!err) err = map(exports, "homepage", 1, html_encode(HOME_PAGE_URL), 0); if (!err) err = map(exports, "default-cgi", 1, html_encode(CGI_PREFIX), 0); - if (!err) err = map(exports, "menu", 1, make_menu(caller), 0); + if (!err) err = map(exports, "menu", 1, make_menu(caller, csp->config->feature_flags), 0); if (!err) err = map(exports, "code-status", 1, CODE_STATUS, 1); if (!strncmpic(csp->config->usermanual, "file://", 7) || !strncmpic(csp->config->usermanual, "http", 4)) { - if (!err) err = map(exports, "user-manual", 1, csp->config->usermanual ,1); + /* Manual is located somewhere else, just link to it. */ + if (!err) err = map(exports, "user-manual", 1, html_encode(csp->config->usermanual), 0); } else { - if (!err) err = map(exports, "user-manual", 1, "http://"CGI_SITE_2_HOST"/user-manual/" ,1); + /* Manual is delivered by Privoxy. */ + if (!err) err = map(exports, "user-manual", 1, html_encode(CGI_PREFIX"user-manual/"), 0); } if (!err) err = map(exports, "actions-help-prefix", 1, ACTIONS_HELP_PREFIX ,1); #ifdef FEATURE_TOGGLE @@ -2408,14 +2713,18 @@ jb_err map_conditional(struct map *exports, const char *name, int choose_first) * * Description : Returns an HTML-formatted menu of the available * unhidden CGIs, excluding the one given in