X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=cgi.c;h=a1b719439c8e54f61d3cb0cb198f4cf726104c69;hb=132517cd11681ec17afb5c8ea510e3968dd6512d;hp=b9863c7b099386c03d2396deed1e63c428a7cf8c;hpb=452c573ab776a7b1947a2647648690c0cef48fde;p=privoxy.git
diff --git a/cgi.c b/cgi.c
index b9863c7b..a1b71943 100644
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.85 2007/01/05 14:19:02 fabiankeil Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.112 2008/08/31 16:08:12 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -11,7 +11,7 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.85 2007/01/05 14:19:02 fabiankeil Exp $";
* Functions declared include:
*
*
- * Copyright : Written by and Copyright (C) 2001-2004, 2006
+ * Copyright : Written by and Copyright (C) 2001-2004, 2006-2008
* the SourceForge Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
@@ -38,6 +38,119 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.85 2007/01/05 14:19:02 fabiankeil Exp $";
*
* Revisions :
* $Log: cgi.c,v $
+ * Revision 1.112 2008/08/31 16:08:12 fabiankeil
+ * "View the request headers" isn't more equal than the other
+ * menu items and thus doesn't need a trailing dot either.
+ *
+ * Revision 1.111 2008/08/31 15:59:02 fabiankeil
+ * There's no reason to let remote toggling support depend
+ * on FEATURE_CGI_EDIT_ACTIONS, so make sure it doesn't.
+ *
+ * Revision 1.110 2008/08/31 14:55:43 fabiankeil
+ * Add a @date@ symbol to include a date(1)-like time string
+ * in templates. Modified version of the patch Endre Szabo
+ * submitted in #2026468.
+ *
+ * Revision 1.109 2008/07/26 09:40:27 fabiankeil
+ * Remove the unconditional block in get_http_time().
+ * It's pointless now that it's no longer used to limit
+ * dummy's scope. While at it, remove obvious comments
+ * and a trailing space.
+ *
+ * Revision 1.108 2008/05/26 17:30:53 fabiankeil
+ * Provide an OpenSearch Description to access the
+ * show-url-info page through "search engine plugins".
+ *
+ * Revision 1.107 2008/05/26 16:23:19 fabiankeil
+ * - Fix spelling in template-not-found message.
+ * - Declare referrer_is_safe()'s alternative_prefix[] static.
+ *
+ * Revision 1.106 2008/05/21 15:24:38 fabiankeil
+ * Mark csp as immutable for a bunch of functions.
+ *
+ * Revision 1.105 2008/04/17 14:40:47 fabiankeil
+ * Provide get_http_time() with the buffer size so it doesn't
+ * have to blindly assume that the buffer is big enough.
+ *
+ * Revision 1.104 2008/03/26 18:07:06 fabiankeil
+ * Add hostname directive. Closes PR#1918189.
+ *
+ * Revision 1.103 2008/03/21 11:13:57 fabiankeil
+ * Only gather host information if it's actually needed.
+ * Also move the code out of accept_connection() so it's less likely
+ * to delay other incoming connections if the host is misconfigured.
+ *
+ * Revision 1.102 2008/02/23 16:33:43 fabiankeil
+ * Let forward_url() use the standard parameter ordering
+ * and mark its second parameter immutable.
+ *
+ * Revision 1.101 2008/02/03 15:45:06 fabiankeil
+ * Add SOCKS5 support for "Forwarding failure" CGI page.
+ *
+ * Revision 1.100 2007/10/17 18:40:53 fabiankeil
+ * - Send CGI pages as HTTP/1.1 unless the client asked for HTTP/1.0.
+ * - White space fix.
+ *
+ * Revision 1.99 2007/08/05 13:42:22 fabiankeil
+ * #1763173 from Stefan Huehner: declare some more functions static.
+ *
+ * Revision 1.98 2007/05/14 10:33:51 fabiankeil
+ * - Use strlcpy() and strlcat() instead of strcpy() and strcat().
+ *
+ * Revision 1.97 2007/04/09 18:11:35 fabiankeil
+ * Don't mistake VC++'s _snprintf() for a snprintf() replacement.
+ *
+ * Revision 1.96 2007/03/08 17:41:05 fabiankeil
+ * Use sizeof() more often.
+ *
+ * Revision 1.95 2007/02/10 17:01:37 fabiankeil
+ * Don't overlook map result for the forwarding-type.
+ *
+ * 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.
*
@@ -532,21 +645,19 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.85 2007/01/05 14:19:02 fabiankeil Exp $";
#include Privoxy ran out of memory while processing your request. Privoxy encountered an error while processing your request: Could not load template file 500 Internal Privoxy Error
\r\n"
"500 Internal Privoxy Error
\r\n"
"";
static const char body_suffix[] =
- "
or one of it's included components.
Please contact your proxy administrator.
\r\n" "If you are the proxy administrator, please put the required file(s)"
"in the (confdir)/templates
directory. The "
@@ -1481,6 +1721,7 @@ jb_err cgi_error_no_template(struct client_state *csp,
").
Privoxy encountered an error while processing your request:
\r\n" @@ -1553,11 +1797,16 @@ jb_err cgi_error_unknown(struct client_state *csp, static const char body_suffix[] = "\r\n" "Please " - "" + "" "file a bug report.
\r\n" "\r\n" "\r\n"; char errnumbuf[30]; + /* + * Due to sizeof(errnumbuf), body_size will be slightly + * bigger than necessary but it doesn't really matter. + */ + const size_t body_size = strlen(body_prefix) + sizeof(errnumbuf) + strlen(body_suffix) + 1; assert(csp); assert(rsp); @@ -1568,17 +1817,18 @@ 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; snprintf(errnumbuf, sizeof(errnumbuf), "%d", error_to_report); - rsp->body = malloc(strlen(body_prefix) + strlen(errnumbuf) + strlen(body_suffix) + 1); + rsp->body = malloc(body_size); if (rsp->body == NULL) { return JB_ERR_MEMORY; } - strcpy(rsp->body, body_prefix); - strcat(rsp->body, errnumbuf); - strcat(rsp->body, body_suffix); + strlcpy(rsp->body, body_prefix, body_size); + strlcat(rsp->body, errnumbuf, body_size); + strlcat(rsp->body, body_suffix, body_size); rsp->status = strdup(status); if (rsp->status == NULL) @@ -1607,7 +1857,7 @@ jb_err cgi_error_unknown(struct client_state *csp, * JB_ERR_MEMORY on out-of-memory error. * *********************************************************************/ -jb_err cgi_error_bad_param(struct client_state *csp, +jb_err cgi_error_bad_param(const struct client_state *csp, struct http_response *rsp) { struct map *exports; @@ -1722,13 +1972,15 @@ char *add_help_link(const char *item, * Parameters : * 1 : time_offset = Time returned will be current time * plus this number of seconds. - * 2 : buf = Destination for result. Must be long enough - * to hold 29 characters plus a trailing zero. + * 2 : buf = Destination for result. + * 3 : buffer_size = Size of the buffer above. Must be big + * enough to hold 29 characters plus a + * trailing zero. * * Returns : N/A * *********************************************************************/ -void get_http_time(int time_offset, char *buf) +void get_http_time(int time_offset, char *buf, size_t buffer_size) { static const char day_names[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; @@ -1739,36 +1991,29 @@ void get_http_time(int time_offset, char *buf) struct tm *t; time_t current_time; #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. - * Wrapping declaration inside HAVE_GMTIME_R keeps the compiler quiet when - * !defined HAVE_GMTIME_R. - */ - struct tm dummy; + struct tm dummy; #endif assert(buf); + assert(buffer_size > 29); - time(¤t_time); /* get current time */ + time(¤t_time); current_time += time_offset; /* get and save the gmt */ - { #if HAVE_GMTIME_R - t = gmtime_r(¤t_time, &dummy); + t = gmtime_r(¤t_time, &dummy); #elif FEATURE_PTHREAD - pthread_mutex_lock(&gmtime_mutex); - t = gmtime(¤t_time); - pthread_mutex_unlock(&gmtime_mutex); + privoxy_mutex_lock(&gmtime_mutex); + t = gmtime(¤t_time); + privoxy_mutex_unlock(&gmtime_mutex); #else - t = gmtime(¤t_time); + t = gmtime(¤t_time); #endif - } /* Format: "Sun, 06 Nov 1994 08:49:37 GMT" */ - snprintf(buf, 30, + snprintf(buf, buffer_size, "%s, %02d %s %4d %02d:%02d:%02d GMT", day_names[t->tm_wday], t->tm_mday, @@ -1781,6 +2026,51 @@ void get_http_time(int time_offset, char *buf) } +/********************************************************************* + * + * Function : get_locale_time + * + * Description : Get the time in a date(1)-like format + * according to the current locale - e.g.: + * "Fri Aug 29 19:37:12 CEST 2008" + * + * XXX: Should we allow the user to change the format? + * + * Parameters : + * 1 : buf = Destination for result. + * 2 : buffer_size = Size of the buffer above. Must be big + * enough to hold 29 characters plus a + * trailing zero. + * + * Returns : N/A + * + *********************************************************************/ +static void get_locale_time(char *buf, size_t buffer_size) +{ + struct tm *timeptr; + time_t current_time; +#if defined(HAVE_LOCALTIME_R) + struct tm dummy; +#endif + + assert(buf); + assert(buffer_size > 29); + + time(¤t_time); + +#if HAVE_LOCALTIME_R + timeptr = localtime_r(¤t_time, &dummy); +#elif FEATURE_PTHREAD + privoxy_mutex_lock(&localtime_mutex); + timeptr = localtime(¤t_time); + privoxy_mutex_unlock(&localtime_mutex); +#else + timeptr = localtime(¤t_time); +#endif + + strftime(buf, buffer_size, "%a %b %d %X %Z %Y", timeptr); + +} /********************************************************************* * @@ -1788,6 +2078,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 @@ -1796,7 +2088,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; @@ -1810,9 +2102,12 @@ struct http_response *finish_http_response(struct http_response *rsp) } /* - * Fill in the HTTP Status + * Fill in the HTTP Status, using HTTP/1.1 + * unless the client asked for HTTP/1.0. */ - snprintf(buf, sizeof(buf), "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK"); + snprintf(buf, sizeof(buf), "%s %s", + strcmpic(csp->http->ver, "HTTP/1.0") ? "HTTP/1.1" : "HTTP/1.0", + rsp->status ? rsp->status : "200 OK"); err = enlist_first(rsp->headers, buf); /* @@ -1828,14 +2123,32 @@ struct http_response *finish_http_response(struct http_response *rsp) err = enlist(rsp->headers, buf); } + 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); + /* + * 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); } /* @@ -1857,7 +2170,7 @@ struct http_response *finish_http_response(struct http_response *rsp) if (!err) { - get_http_time(0, buf); + get_http_time(0, buf, sizeof(buf)); err = enlist_unique_header(rsp->headers, "Date", buf); } @@ -1866,13 +2179,13 @@ struct http_response *finish_http_response(struct http_response *rsp) if (!err) { - get_http_time(10 * 60, buf); /* 10 * 60sec = 10 minutes */ + get_http_time(10 * 60, buf, sizeof(buf)); /* 10 * 60sec = 10 minutes */ err = enlist_unique_header(rsp->headers, "Expires", buf); } } else if (!strncmpic(rsp->status, "302", 3)) { - get_http_time(0, buf); + get_http_time(0, buf, sizeof(buf)); if (!err) err = enlist_unique_header(rsp->headers, "Date", buf); } else @@ -1889,7 +2202,7 @@ struct http_response *finish_http_response(struct http_response *rsp) * 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. * - * For documents delivered with status code 404 or 503 we set "Last-Modified" + * 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. * @@ -1898,9 +2211,11 @@ struct http_response *finish_http_response(struct http_response *rsp) */ if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache"); - get_http_time(0, buf); + get_http_time(0, buf, sizeof(buf)); if (!err) err = enlist_unique_header(rsp->headers, "Date", buf); - if (!strncmpic(rsp->status, "404", 3) || !strncmpic(rsp->status, "503", 3)) + 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"); } @@ -1988,9 +2303,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...) @@ -2005,7 +2319,7 @@ void free_http_response(struct http_response *rsp) * JB_ERR_FILE if the template file cannot be read * *********************************************************************/ -jb_err template_load(struct client_state *csp, char **template_ptr, +jb_err template_load(const struct client_state *csp, char **template_ptr, const char *templatename, int recursive) { jb_err err; @@ -2037,11 +2351,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; } @@ -2049,6 +2375,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; } @@ -2057,6 +2385,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; } @@ -2269,7 +2598,7 @@ jb_err template_fill(char **template_ptr, const struct map *exports) * JB_ERR_MEMORY on out-of-memory error * *********************************************************************/ -jb_err template_fill_for_cgi(struct client_state *csp, +jb_err template_fill_for_cgi(const struct client_state *csp, const char *templatename, struct map *exports, struct http_response *rsp) @@ -2315,10 +2644,12 @@ jb_err template_fill_for_cgi(struct client_state *csp, *********************************************************************/ struct map *default_exports(const struct client_state *csp, const char *caller) { - char buf[20]; + char buf[30]; jb_err err; struct map * exports; int local_help_exists = 0; + char *ip_address = NULL; + char *hostname = NULL; assert(csp); @@ -2328,9 +2659,23 @@ struct map *default_exports(const struct client_state *csp, const char *caller) return NULL; } + if (csp->config->hostname) + { + get_host_information(csp->cfd, &ip_address, NULL); + hostname = strdup(csp->config->hostname); + } + else + { + get_host_information(csp->cfd, &ip_address, &hostname); + } + err = map(exports, "version", 1, html_encode(VERSION), 0); - if (!err) err = map(exports, "my-ip-address", 1, html_encode(csp->my_ip_addr_str ? csp->my_ip_addr_str : "unknown"), 0); - if (!err) err = map(exports, "my-hostname", 1, html_encode(csp->my_hostname ? csp->my_hostname : "unknown"), 0); + get_locale_time(buf, sizeof(buf)); + if (!err) err = map(exports, "time", 1, html_encode(buf), 0); + if (!err) err = map(exports, "my-ip-address", 1, html_encode(ip_address ? ip_address : "unknown"), 0); + freez(ip_address); + if (!err) err = map(exports, "my-hostname", 1, html_encode(hostname ? hostname : "unknown"), 0); + freez(hostname); 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, csp->config->feature_flags), 0); @@ -2338,11 +2683,13 @@ struct map *default_exports(const struct client_state *csp, const char *caller) 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 @@ -2351,7 +2698,7 @@ struct map *default_exports(const struct client_state *csp, const char *caller) if (!err) err = map_block_killer(exports, "can-toggle"); #endif - snprintf(buf, 20, "%d", csp->config->hport); + snprintf(buf, sizeof(buf), "%d", csp->config->hport); if (!err) err = map(exports, "my-port", 1, buf, 1); if(!strcmp(CODE_STATUS, "stable")) @@ -2420,7 +2767,7 @@ jb_err map_block_killer(struct map *exports, const char *name) assert(name); assert(strlen(name) < 490); - snprintf(buf, 1000, "if-%s-start.*if-%s-end", name, name); + snprintf(buf, sizeof(buf), "if-%s-start.*if-%s-end", name, name); return map(exports, buf, 1, "", 1); } @@ -2450,7 +2797,7 @@ jb_err map_block_keep(struct map *exports, const char *name) assert(name); assert(strlen(name) < 490); - snprintf(buf, 500, "if-%s-start", name); + snprintf(buf, sizeof(buf), "if-%s-start", name); err = map(exports, buf, 1, "", 1); if (err) @@ -2458,7 +2805,7 @@ jb_err map_block_keep(struct map *exports, const char *name) return err; } - snprintf(buf, 500, "if-%s-end", name); + snprintf(buf, sizeof(buf), "if-%s-end", name); return map(exports, buf, 1, "", 1); } @@ -2497,7 +2844,7 @@ jb_err map_conditional(struct map *exports, const char *name, int choose_first) assert(name); assert(strlen(name) < 480); - snprintf(buf, 1000, (choose_first + snprintf(buf, sizeof(buf), (choose_first ? "else-not-%s@.*@endif-%s" : "if-%s-then@.*@else-not-%s"), name, name); @@ -2508,7 +2855,7 @@ jb_err map_conditional(struct map *exports, const char *name, int choose_first) return err; } - snprintf(buf, 1000, (choose_first ? "if-%s-then" : "endif-%s"), name); + snprintf(buf, sizeof(buf), (choose_first ? "if-%s-then" : "endif-%s"), name); return map(exports, buf, 1, "", 1); } @@ -2556,7 +2903,23 @@ char *make_menu(const char *self, const unsigned feature_flags) if (d->description && strcmp(d->name, self)) { - string_append(&result, "