+ rsp->status = strdup_or_die("403 Request not trusted or feature disabled");
+
+ if (NULL == (exports = default_exports(csp, "cgi-error-disabled")))
+ {
+ return JB_ERR_MEMORY;
+ }
+ if (map(exports, "url", 1, html_encode(csp->http->url), 0))
+ {
+ /* Not important enough to do anything */
+ log_error(LOG_LEVEL_ERROR, "Failed to fill in url.");
+ }
+
+ return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_init_error_messages
+ *
+ * Description : Call at the start of the program to initialize
+ * the error message used by cgi_error_memory().
+ *
+ * Parameters : N/A
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+void cgi_init_error_messages(void)
+{
+ memset(cgi_error_memory_response, '\0', sizeof(*cgi_error_memory_response));
+ cgi_error_memory_response->head =
+ "HTTP/1.0 500 Internal Privoxy Error\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ cgi_error_memory_response->body =
+ "<html>\n"
+ "<head>\n"
+ " <title>500 Internal Privoxy Error</title>\n"
+ " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
+ "</head>\n"
+ "<body>\n"
+ "<h1>500 Internal Privoxy Error</h1>\n"
+ "<p>Privoxy <b>ran out of memory</b> while processing your request.</p>\n"
+ "<p>Please contact your proxy administrator, or try again later</p>\n"
+ "</body>\n"
+ "</html>\n";
+
+ cgi_error_memory_response->head_length =
+ strlen(cgi_error_memory_response->head);
+ cgi_error_memory_response->content_length =
+ strlen(cgi_error_memory_response->body);
+ cgi_error_memory_response->crunch_reason = OUT_OF_MEMORY;
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_error_memory
+ *
+ * Description : Called if a CGI function runs out of memory.
+ * Returns a statically-allocated error response.
+ *
+ * Parameters : N/A
+ *
+ * Returns : http_response data structure for output. This is
+ * statically allocated, for obvious reasons.
+ *
+ *********************************************************************/
+struct http_response *cgi_error_memory(void)
+{
+ /* assert that it's been initialized. */
+ assert(cgi_error_memory_response->head);
+
+ return cgi_error_memory_response;
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_error_no_template
+ *
+ * Description : Almost-CGI function that is called if a template
+ * cannot be loaded. Note this is not a true CGI,
+ * it takes a template name rather than a map of
+ * parameters.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : template_name = Name of template that could not
+ * be loaded.
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_error_no_template(const struct client_state *csp,
+ struct http_response *rsp,
+ const char *template_name)
+{
+ static const char status[] =
+ "500 Internal Privoxy Error";
+ static const char body_prefix[] =
+ "<html>\n"
+ "<head>\n"
+ " <title>500 Internal Privoxy Error</title>\n"
+ " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
+ "</head>\n"
+ "<body>\n"
+ "<h1>500 Internal Privoxy Error</h1>\n"
+ "<p>Privoxy encountered an error while processing your request:</p>\n"
+ "<p><b>Could not load template file <code>";
+ static const char body_suffix[] =
+ "</code> or one of its included components.</b></p>\n"
+ "<p>Please contact your proxy administrator.</p>\n"
+ "<p>If you are the proxy administrator, please put the required file(s)"
+ "in the <code><i>(confdir)</i>/templates</code> directory. The "
+ "location of the <code><i>(confdir)</i></code> directory "
+ "is specified in the main Privoxy <code>config</code> "
+ "file. (It's typically the Privoxy install directory"
+#ifndef _WIN32
+ ", or <code>/etc/privoxy/</code>"
+#endif /* ndef _WIN32 */
+ ").</p>\n"
+ "</body>\n"
+ "</html>\n";
+ const size_t body_size = strlen(body_prefix) + strlen(template_name) + strlen(body_suffix) + 1;
+
+ assert(csp);
+ assert(rsp);
+ assert(template_name);
+
+ /* 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;
+
+ rsp->body = malloc_or_die(body_size);
+ strlcpy(rsp->body, body_prefix, body_size);
+ strlcat(rsp->body, template_name, body_size);
+ strlcat(rsp->body, body_suffix, body_size);
+
+ rsp->status = strdup(status);
+ if (rsp->status == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * 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 relevant 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(const 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[] =
+ "<html>\n"
+ "<head>\n"
+ " <title>500 Internal Privoxy Error</title>\n"
+ " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
+ "</head>\n"
+ "<body>\n"
+ "<h1>500 Internal Privoxy Error</h1>\n"
+ "<p>Privoxy encountered an error while processing your request:</p>\n"
+ "<p><b>Unexpected internal error: ";
+ static const char body_suffix[] =
+ "</b></p>\n"
+ "<p>Please "
+ "<a href=\"https://sourceforge.net/p/ijbswa/bugs/\">"
+ "file a bug report</a>.</p>\n"
+ "</body>\n"
+ "</html>\n";
+ /* Includes room for larger error numbers in the future. */
+ const size_t body_size = sizeof(body_prefix) + sizeof(body_suffix) + 5;
+ 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;
+ rsp->crunch_reason = INTERNAL_ERROR;
+
+ rsp->body = malloc_or_die(body_size);
+
+ snprintf(rsp->body, body_size, "%s%d%s", body_prefix, error_to_report, body_suffix);
+
+ rsp->status = strdup(status);
+ if (rsp->status == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_error_bad_param
+ *
+ * Description : CGI function that is called if the parameters
+ * (query string) for a CGI were wrong.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ *
+ * CGI Parameters : none
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_error_bad_param(const struct client_state *csp,
+ struct http_response *rsp)
+{
+ struct map *exports;
+
+ assert(csp);
+ assert(rsp);
+
+ if (NULL == (exports = default_exports(csp, NULL)))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ return template_fill_for_cgi(csp, "cgi-error-bad-param", exports, rsp);
+}
+
+
+/*********************************************************************
+ *
+ * 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("<a href=\"");
+ if (!strncmpic(config->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, "</a>");
+
+ return result;
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_http_time
+ *
+ * Description : Get the time in a format suitable for use in a
+ * HTTP header - e.g.:
+ * "Sun, 06 Nov 1994 08:49:37 GMT"
+ *
+ * Parameters :
+ * 1 : time_offset = Time returned will be current time
+ * plus this number of seconds.
+ * 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, size_t buffer_size)
+{
+ struct tm *t;
+ time_t current_time;
+#if defined(HAVE_GMTIME_R)
+ struct tm dummy;
+#endif
+
+ assert(buf);
+ assert(buffer_size > (size_t)29);
+
+ time(¤t_time);
+
+ current_time += time_offset;
+
+ /* get and save the gmt */
+#if HAVE_GMTIME_R
+ t = gmtime_r(¤t_time, &dummy);
+#elif defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_lock(&gmtime_mutex);
+ t = gmtime(¤t_time);
+ privoxy_mutex_unlock(&gmtime_mutex);
+#else
+ t = gmtime(¤t_time);
+#endif
+
+ strftime(buf, buffer_size, "%a, %d %b %Y %H:%M:%S GMT", t);
+
+}
+
+/*********************************************************************
+ *
+ * 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 > (size_t)29);
+
+ time(¤t_time);
+
+#if HAVE_LOCALTIME_R
+ timeptr = localtime_r(¤t_time, &dummy);
+#elif defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_lock(&localtime_mutex);
+ timeptr = localtime(¤t_time);
+#else
+ timeptr = localtime(¤t_time);
+#endif
+
+ strftime(buf, buffer_size, "%a %b %d %X %Z %Y", timeptr);
+
+#if !defined(HAVE_LOCALTIME_R) && defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_unlock(&localtime_mutex);
+#endif
+}
+
+
+#ifdef FEATURE_COMPRESSION
+/*********************************************************************
+ *
+ * Function : compress_buffer
+ *
+ * Description : Compresses the content of a buffer with zlib's deflate
+ * Allocates a new buffer for the result, free'ing it is
+ * up to the caller.
+ *
+ * Parameters :
+ * 1 : buffer = buffer whose content should be compressed
+ * 2 : buffer_length = length of the buffer
+ * 3 : compression_level = compression level for compress2()
+ *
+ * Returns : NULL on error, otherwise a pointer to the compressed
+ * content of the input buffer.
+ *
+ *********************************************************************/
+char *compress_buffer(char *buffer, size_t *buffer_length, int compression_level)
+{
+ char *compressed_buffer;
+ uLongf new_length;
+ assert(-1 <= compression_level && compression_level <= 9);
+
+ /* Let zlib figure out the maximum length of the compressed data */
+ new_length = compressBound((uLongf)*buffer_length);
+
+ compressed_buffer = malloc_or_die(new_length);
+
+ if (Z_OK != compress2((Bytef *)compressed_buffer, &new_length,
+ (Bytef *)buffer, *buffer_length, compression_level))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "compress2() failed. Buffer size: %lu, compression level: %d.",
+ new_length, compression_level);
+ freez(compressed_buffer);
+ return NULL;
+ }
+
+ log_error(LOG_LEVEL_RE_FILTER,
+ "Compressed content from %lu to %lu bytes. Compression level: %d",
+ *buffer_length, new_length, compression_level);
+
+ *buffer_length = (size_t)new_length;
+
+ return compressed_buffer;
+
+}
+#endif
+
+
+/*********************************************************************
+ *
+ * Function : finish_http_response
+ *
+ * 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
+ *
+ * Returns : A http_response, usually the rsp parameter.
+ * On error, free()s rsp and returns cgi_error_memory()
+ *
+ *********************************************************************/
+struct http_response *finish_http_response(struct client_state *csp, struct http_response *rsp)
+{
+ char buf[BUFFER_SIZE];
+ jb_err err;
+
+ /* Special case - do NOT change this statically allocated response,
+ * which is ready for output anyway.
+ */
+ if (rsp == cgi_error_memory_response)
+ {
+ return rsp;
+ }
+
+ /*
+ * Add "Cross-origin resource sharing" (CORS) headers if enabled
+ */
+ if (NULL != csp->config->cors_allowed_origin)
+ {
+ enlist_unique_header(rsp->headers, "Access-Control-Allow-Origin",
+ csp->config->cors_allowed_origin);
+ enlist_unique_header(rsp->headers, "Access-Control-Allow-Methods", "GET,POST");
+ enlist_unique_header(rsp->headers, "Access-Control-Allow-Headers", "X-Requested-With");
+ enlist_unique_header(rsp->headers, "Access-Control-Max-Age", "86400");
+ }
+
+ /*
+ * Fill in the HTTP Status, using HTTP/1.1
+ * unless the client asked for HTTP/1.0.
+ */
+ snprintf(buf, sizeof(buf), "%s %s",
+ strcmpic(csp->http->version, "HTTP/1.0") ? "HTTP/1.1" : "HTTP/1.0",
+ rsp->status ? rsp->status : "200 OK");
+ err = enlist_first(rsp->headers, buf);
+
+ /*
+ * Set the Content-Length
+ */
+ if (rsp->content_length == 0)
+ {
+ rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
+ }
+
+#ifdef FEATURE_COMPRESSION
+ if (!err && (csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+ && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
+ {
+ char *compressed_content;
+
+ compressed_content = compress_buffer(rsp->body, &rsp->content_length,
+ csp->config->compression_level);
+ if (NULL != compressed_content)
+ {
+ freez(rsp->body);
+ rsp->body = compressed_content;
+ err = enlist_unique_header(rsp->headers, "Content-Encoding", "deflate");
+ }
+ }
+#endif
+
+ if (!err)
+ {
+ snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length);
+ /*
+ * Signal serve() that the client will be able to figure out
+ * the end of the response without having to close the connection.
+ */
+ csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET;
+ 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);
+ }
+
+ /*
+ * Fill in the rest of the default headers:
+ *
+ * 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.
+ * Cache-Control: set to "no-cache" if applicable.
+ *
+ * See http://www.w3.org/Protocols/rfc2068/rfc2068
+ */
+ if (rsp->is_static)
+ {
+ /*
+ * Set Expires to about 10 min into the future so it'll get reloaded
+ * occasionally, e.g. if Privoxy gets upgraded.
+ */
+
+ if (!err)
+ {
+ get_http_time(0, buf, sizeof(buf));
+ err = enlist_unique_header(rsp->headers, "Date", buf);
+ }
+
+ /* Some date in the past. */
+ if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Sat, 17 Jun 2000 12:00:00 GMT");
+
+ if (!err)
+ {
+ 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, sizeof(buf));
+ if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
+ }
+ else
+ {
+ /*
+ * 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 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.
+ */
+ if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
+
+ get_http_time(0, buf, sizeof(buf));
+ if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
+ if (!strncmpic(rsp->status, "403", 3)
+ || !strncmpic(rsp->status, "404", 3)
+ || !strncmpic(rsp->status, "502", 3)
+ || !strncmpic(rsp->status, "503", 3)
+ || !strncmpic(rsp->status, "504", 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");
+ }
+
+ if (!err && (!(csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)
+ || (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)))
+ {
+ err = enlist_unique_header(rsp->headers, "Connection", "close");
+ }
+
+ /*
+ * Write the head
+ */
+ if (err || (NULL == (rsp->head = list_to_text(rsp->headers))))
+ {
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+ rsp->head_length = strlen(rsp->head);
+
+ return rsp;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : alloc_http_response
+ *
+ * Description : Allocates a new http_response structure.
+ *
+ * Parameters : N/A
+ *
+ * Returns : pointer to a new http_response, or NULL.
+ *
+ *********************************************************************/
+struct http_response *alloc_http_response(void)
+{
+ return (struct http_response *) zalloc(sizeof(struct http_response));