X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=cgi.c;h=dd3259551798d9932685495e6963d816ecdce449;hb=ad516e4492c7329826c1ff236ce68a2b8609a647;hp=cc07379deb07d4f292aa401896fb692df73ca6cc;hpb=3927cfda9f7369f4c6c9b5f82f90ebe73ca4277a;p=privoxy.git
diff --git a/cgi.c b/cgi.c
index cc07379d..dd325955 100644
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.57 2002/03/26 22:29:54 swa Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.77 2006/09/21 15:17:23 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -38,6 +38,163 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.57 2002/03/26 22:29:54 swa Exp $";
*
* Revisions :
* $Log: cgi.c,v $
+ * 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.
+ *
+ * Formatting changed to Privoxy style, added call to
+ * cgi_error_no_template if the requested file doesn't
+ * exist and modified check whether or not Privoxy itself
+ * should serve the manual. Should work cross-platform now.
+ *
+ * Revision 1.73 2006/08/03 02:46:41 david__schmidt
+ * Incorporate Fabian Keil's patch work:
http://www.fabiankeil.de/sourcecode/privoxy/
+ *
+ * Revision 1.72 2006/07/18 14:48:45 david__schmidt
+ * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ * with what was really the latest development (the v_3_0_branch branch)
+ *
+ * Revision 1.70.2.13 2004/02/17 13:30:23 oes
+ * Moved cgi_error_disabled() from cgiedit.c to
+ * cgi.c to re-enable build with --disable-editor.
+ * Fixes Bug #892744. Thanks to Matthew Fischer
+ * for spotting.
+ *
+ * Revision 1.70.2.12 2003/12/17 16:33:16 oes
+ * - Added new function cgi_redirect to handle creation of
+ * HTTP redirect messages formerly repeated in the code.
+ * - Send cgi_error_disabled instead of cgi_error_404 when
+ * referrer check fails
+ * - Dynamic content now gets Expires header field with date
+ * in the past
+ *
+ * Revision 1.70.2.11 2003/10/23 12:29:26 oes
+ * Bugfix: Transparent PNG was not transparent. Thanks to
+ * Dan Razzell of Starfish Systems for notice and new PNG.
+ *
+ * Revision 1.70.2.10 2003/06/06 07:54:25 oes
+ * Security fix: dspatch_known_cgi no longer considers an empty
+ * referrer safe for critical CGIs, since malicious links could
+ * reside on https:// locations which browsers don't advertize as
+ * referrers. Closes bug #749916, thanks to Jeff Epler for the
+ * hint. Goodbye One-Click[tm] toggling :-(
+ *
+ * Revision 1.70.2.9 2003/05/08 15:11:31 oes
+ * Nit
+ *
+ * Revision 1.70.2.8 2003/04/29 13:33:51 oes
+ * Killed a compiler warning on OSX
+ *
+ * Revision 1.70.2.7 2003/04/03 13:50:58 oes
+ * - Don't call cgi_error_disabled ifndef FEATURE_CGI_EDIT_ACTIONS
+ * (fixes bug #710056)
+ * - Show toggle info only if we have it
+ *
+ * Revision 1.70.2.6 2003/03/12 01:26:25 david__schmidt
+ * Move declaration of struct tm dummy outside of a control block so it is
+ * accessible later on during snprintf in get_http_time.
+ *
+ * Revision 1.70.2.5 2003/03/11 11:53:58 oes
+ * Cosmetic: Renamed cryptic variable
+ *
+ * Revision 1.70.2.4 2003/03/07 03:41:03 david__schmidt
+ * Wrapping all *_r functions (the non-_r versions of them) with mutex semaphores for OSX. Hopefully this will take care of all of those pesky crash reports.
+ *
+ * Revision 1.70.2.3 2002/11/28 18:14:32 oes
+ * Disable access to critical CGIs via untrusted referrers.
+ * This prevents users from being tricked by malicious websites
+ * into making unintentional configuration changes:
+ *
+ * - Added flag to each cgi_dispatcher that allows or denies
+ * external linking
+ * - Introduced proviorical function that greps for the
+ * referrer header before regular header parsing happens
+ * - Added safety check to dispatch_known_cgi. CGI is called
+ * if (cgi harmless || no referrer || we are referrer).
+ * Else a) toggle calls are modified not to change status and
+ * b) all other calls are denied.
+ *
+ * Revision 1.70.2.2 2002/11/12 16:20:37 oes
+ * Added missing #ifdef FEATURE_TOGGLE around g_bToggleIJB; fixes bug #636651
+ *
+ * Revision 1.70.2.1 2002/08/05 11:17:46 oes
+ * Fixed Bug #587820, i.e. added workaround for IE bug that includes fragment identifier in (cgi) query
+ *
+ * Revision 1.70 2002/05/19 11:33:20 jongfoster
+ * If a CGI error was not handled, and propogated back to
+ * dispatch_known_cgi(), then it was assumed to be "out of memory".
+ * This gave a very misleading error message.
+ *
+ * Now other errors will cause a simple message giving the error
+ * number and asking the user to report a bug.
+ *
+ * Bug report:
+ * http://sourceforge.net/tracker/index.php?func=detail
+ * &aid=557905&group_id=11118&atid=111118
+ *
+ * Revision 1.69 2002/05/14 21:28:40 oes
+ * - Fixed add_help_link to link to the (now split) actions
+ * part of the config chapter
+ * - Renamed helplink export to actions-help-prefix
+ *
+ * Revision 1.68 2002/05/12 21:36:29 jongfoster
+ * Correcting function comments
+ *
+ * Revision 1.67 2002/04/30 12:02:07 oes
+ * Nit: updated a comment
+ *
+ * Revision 1.66 2002/04/26 18:32:57 jongfoster
+ * Fixing a memory leak on error
+ *
+ * Revision 1.65 2002/04/26 12:53:51 oes
+ * - New function add_help_link
+ * - default_exports now exports links to the user manual
+ * and a prefix for links into the config chapter
+ *
+ * Revision 1.64 2002/04/24 02:17:21 oes
+ * - Better descriptions for CGIs
+ * - Hide edit-actions, more shortcuts
+ * - Moved get_char_param, get_string_param and get_number_param here
+ * from cgiedit.c
+ *
+ * Revision 1.63 2002/04/15 19:06:43 jongfoster
+ * Typos
+ *
+ * Revision 1.62 2002/04/10 19:59:46 jongfoster
+ * Fixes to #include in templates:
+ * - Didn't close main file if loading an included template fails.
+ * - I'm paranoid and want to disallow "#include /etc/passwd".
+ *
+ * Revision 1.61 2002/04/10 13:37:48 oes
+ * Made templates modular: template_load now recursive with max depth 1
+ *
+ * Revision 1.60 2002/04/08 20:50:25 swa
+ * fixed JB spelling
+ *
+ * Revision 1.59 2002/04/05 15:51:51 oes
+ * - added send-stylesheet CGI
+ * - bugfix: error-pages now get correct request protocol
+ * - fixed
+ * - kludged CGI descriptions and menu not to break JS syntax
+ *
+ * Revision 1.58 2002/03/29 03:33:13 david__schmidt
+ * Fix Mac OSX compiler warnings
+ *
* Revision 1.57 2002/03/26 22:29:54 swa
* we have a new homepage!
*
@@ -135,7 +292,7 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.57 2002/03/26 22:29:54 swa Exp $";
*
* CGI actions file editor that works and is actually useful.
*
- * Ability to toggle JunkBuster remotely using a CGI call.
+ * Ability to toggle Junkbuster remotely using a CGI call.
*
* You can turn off both the above features in the main configuration
* file, e.g. if you are running a multi-user proxy.
@@ -335,6 +492,7 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.57 2002/03/26 22:29:54 swa Exp $";
#include Privoxy encountered an error while processing your request: Could not load template file ";
static const char body_suffix[] =
- "
Please contact your proxy administrator.
\r\n" - "If you are the proxy administrator, please put the required file " + "
If you are the proxy administrator, please put the required file(s)"
"in the (confdir)/templates
directory. The "
"location of the (confdir)
directory "
"is specified in the main Privoxy config
"
@@ -970,6 +1463,83 @@ jb_err cgi_error_no_template(struct client_state *csp,
}
+/*********************************************************************
+ *
+ * Function : cgi_error_unknown
+ *
+ * Description : Almost-CGI function that is called if an unexpected
+ * error occurs in the top-level CGI dispatcher.
+ * In this context, "unexpected" means "anything other
+ * than JB_ERR_MEMORY or JB_ERR_CGI_PARAMS" - CGIs are
+ * expected to handle all other errors internally,
+ * since they can give more relavent error messages
+ * that way.
+ *
+ * Note this is not a true CGI, it takes an error
+ * code rather than a map of parameters.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : error_to_report = Error code to report.
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_error_unknown(struct client_state *csp,
+ struct http_response *rsp,
+ jb_err error_to_report)
+{
+ static const char status[] =
+ "500 Internal Privoxy Error";
+ static const char body_prefix[] =
+ "\r\n"
+ "
Privoxy encountered an error while processing your request:
\r\n" + "Unexpected internal error: "; + static const char body_suffix[] = + "
\r\n" + "Please " + "" + "file a bug report.
\r\n" + "\r\n" + "\r\n"; + char errnumbuf[30]; + assert(csp); + assert(rsp); + + /* Reset rsp, if needed */ + freez(rsp->status); + freez(rsp->head); + freez(rsp->body); + rsp->content_length = 0; + rsp->head_length = 0; + rsp->is_static = 0; + + sprintf(errnumbuf, "%d", error_to_report); + + rsp->body = malloc(strlen(body_prefix) + strlen(errnumbuf) + strlen(body_suffix) + 1); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + strcpy(rsp->body, body_prefix); + strcat(rsp->body, errnumbuf); + strcat(rsp->body, body_suffix); + + rsp->status = strdup(status); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + + return JB_ERR_OK; +} + + /********************************************************************* * * Function : cgi_error_bad_param @@ -1004,6 +1574,93 @@ jb_err cgi_error_bad_param(struct client_state *csp, } +/********************************************************************* + * + * Function : cgi_redirect + * + * Description : CGI support function to generate a HTTP redirect + * message + * + * Parameters : + * 1 : rsp = http_response data structure for output + * 2 : target = string with the target URL + * + * CGI Parameters : None + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err cgi_redirect (struct http_response * rsp, const char *target) +{ + jb_err err; + + assert(rsp); + assert(target); + + err = enlist_unique_header(rsp->headers, "Location", target); + + rsp->status = strdup("302 Local Redirect from Privoxy"); + if (rsp->status == NULL) + { + return JB_ERR_MEMORY; + } + + return err; +} + + +/********************************************************************* + * + * Function : add_help_link + * + * Description : Produce a copy of the string given as item, + * embedded in an HTML link to its corresponding + * section (item name in uppercase) in the actions + * chapter of the user manual, (whose URL is given in + * the config and defaults to our web site). + * + * FIXME: I currently only work for actions, and would + * like to be generalized for other topics. + * + * Parameters : + * 1 : item = item (will NOT be free()d.) + * It is assumed to be HTML-safe. + * 2 : config = The current configuration. + * + * Returns : String with item embedded in link, or NULL on + * out-of-memory + * + *********************************************************************/ +char *add_help_link(const char *item, + struct configuration_spec *config) +{ + char *result; + + if (!item) return NULL; + + result = strdup("usermanual, "file://", 7) || + !strncmpic(config->usermanual, "http", 4)) + { + string_append(&result, config->usermanual); + } + else + { + string_append(&result, "http://"); + string_append(&result, CGI_SITE_2_HOST); + string_append(&result, "/user-manual/"); + } + string_append(&result, ACTIONS_HELP_PREFIX); + string_join (&result, string_toupper(item)); + string_append(&result, "\">"); + string_append(&result, item); + string_append(&result, " "); + + return result; +} + + /********************************************************************* * * Function : get_http_time @@ -1031,6 +1688,15 @@ void get_http_time(int time_offset, char *buf) struct tm *t; time_t current_time; +#if defined(HAVE_GMTIME_R) && !defined(OSX_DARWIN) + /* + * 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; +#endif assert(buf); @@ -1040,8 +1706,11 @@ void get_http_time(int time_offset, char *buf) /* get and save the gmt */ { -#ifdef HAVE_GMTIME_R - struct tm dummy; +#ifdef OSX_DARWIN + 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); @@ -1109,10 +1778,19 @@ struct http_response *finish_http_response(struct http_response *rsp) err = enlist(rsp->headers, buf); } - /* - * Fill in the default headers: + 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. @@ -1120,8 +1798,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) { /* @@ -1144,21 +1820,55 @@ 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 { /* - * 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 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. + * + * For documents delivered with status code 404 or 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. */ 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, "Expires", buf); + if (!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 @@ -1229,25 +1939,31 @@ void free_http_response(struct http_response *rsp) * * Description : CGI support function that loads a given HTML * template from the confdir, ignoring comment - * lines. + * lines and following #include statements up to + * a depth of 1. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * 2 : template_ptr = Destination for pointer to loaded * template text. - * 3 : template = name of the HTML template to be used + * 3 : templatename = name of the HTML template to be used + * 4 : recursive = Flag set if this function calls itself + * following an #include statament * * Returns : JB_ERR_OK on success * JB_ERR_MEMORY on out-of-memory error. * JB_ERR_FILE if the template file cannot be read * *********************************************************************/ -jb_err template_load(struct client_state *csp, char ** template_ptr, - const char *templatename) +jb_err template_load(struct client_state *csp, char **template_ptr, + const char *templatename, int recursive) { + jb_err err; char *templates_dir_path; char *full_path; char *file_buffer; + char *included_module; + const char *p; FILE *fp; char buf[BUFFER_SIZE]; @@ -1257,9 +1973,21 @@ jb_err template_load(struct client_state *csp, char ** template_ptr, *template_ptr = NULL; - /* - * Open template file or fail - */ + /* Validate template name. Paranoia. */ + for (p = templatename; *p != 0; p++) + { + if ( ((*p < 'a') || (*p > 'z')) + && ((*p < 'A') || (*p > 'Z')) + && ((*p < '0') || (*p > '9')) + && (*p != '-') + && (*p != '.')) + { + /* Illegal character */ + return JB_ERR_FILE; + } + } + + /* Generate full path */ templates_dir_path = make_path(csp->config->confdir, "templates"); if (templates_dir_path == NULL) @@ -1274,6 +2002,8 @@ jb_err template_load(struct client_state *csp, char ** template_ptr, return JB_ERR_MEMORY; } + /* Allocate buffer */ + file_buffer = strdup(""); if (file_buffer == NULL) { @@ -1281,6 +2011,8 @@ jb_err template_load(struct client_state *csp, char ** template_ptr, return JB_ERR_MEMORY; } + /* Open template file */ + if (NULL == (fp = fopen(full_path, "r"))) { log_error(LOG_LEVEL_ERROR, "Cannot open template file %s: %E", full_path); @@ -1291,15 +2023,34 @@ jb_err template_load(struct client_state *csp, char ** template_ptr, free(full_path); /* - * Read the file, ignoring comments. + * Read the file, ignoring comments, and honoring #include + * statements, unless we're already called recursively. * * FIXME: The comment handling could break with lines >BUFFER_SIZE long. * This is unlikely in practise. */ while (fgets(buf, BUFFER_SIZE, fp)) { + if (!recursive && !strncmp(buf, "#include ", 9)) + { + if (JB_ERR_OK != (err = template_load(csp, &included_module, chomp(buf + 9), 1))) + { + free(file_buffer); + fclose(fp); + return err; + } + + if (string_join(&file_buffer, included_module)) + { + fclose(fp); + return JB_ERR_MEMORY; + } + + continue; + } + /* skip lines starting with '#' */ - if(*buf == '#') + if (*buf == '#') { continue; } @@ -1442,6 +2193,7 @@ jb_err template_fill(char **template_ptr, const struct map *exports) * 2 : templatename = name of the HTML template to be used * 3 : exports = map with fill in symbol -> name pairs. * Will be freed by this function. + * 4 : rsp = Response structure to fill in. * * Returns : JB_ERR_OK on success * JB_ERR_MEMORY on out-of-memory error @@ -1459,7 +2211,7 @@ jb_err template_fill_for_cgi(struct client_state *csp, assert(exports); assert(rsp); - err = template_load(csp, &rsp->body, templatename); + err = template_load(csp, &rsp->body, templatename, 0); if (err == JB_ERR_FILE) { free_map(exports); @@ -1513,7 +2265,21 @@ struct map *default_exports(const struct client_state *csp, const char *caller) 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, "code-status", 1, CODE_STATUS, 1); - if (!err) err = map_conditional(exports, "enabled-display", g_bToggleIJB); + 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); + } + else + { + if (!err) err = map(exports, "user-manual", 1, "http://"CGI_SITE_2_HOST"/user-manual/" ,1); + } + if (!err) err = map(exports, "actions-help-prefix", 1, ACTIONS_HELP_PREFIX ,1); +#ifdef FEATURE_TOGGLE + if (!err) err = map_conditional(exports, "enabled-display", global_toggle_state); +#else + if (!err) err = map_block_killer(exports, "can-toggle"); +#endif snprintf(buf, 20, "%d", csp->config->hport); if (!err) err = map(exports, "my-port", 1, buf, 1); @@ -1682,9 +2448,10 @@ jb_err map_conditional(struct map *exports, const char *name, int choose_first) * Function : make_menu * * Description : Returns an HTML-formatted menu of the available - * unhidden CGIs, excluding the one given in