+#endif
+
+const size_t image_pattern_length = sizeof(image_pattern_data) - 1;
+const size_t image_blank_length = sizeof(image_blank_data) - 1;
+
+
+static struct http_response cgi_error_memory_response[1];
+
+static struct http_response *dispatch_known_cgi(struct client_state * csp,
+ const char * path);
+static struct map *parse_cgi_parameters(char *argstring);
+
+
+/*********************************************************************
+ *
+ * Function : dispatch_cgi
+ *
+ * Description : Checks if a request URL has either the magical
+ * hostname CGI_SITE_1_HOST (usually http://p.p/) or
+ * matches CGI_SITE_2_HOST CGI_SITE_2_PATH (usually
+ * http://config.privoxy.org/). If so, it passes
+ * the (rest of the) path onto dispatch_known_cgi, which
+ * calls the relevant CGI handler function.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : http_response if match, NULL if nonmatch or handler fail
+ *
+ *********************************************************************/
+struct http_response *dispatch_cgi(struct client_state *csp)
+{
+ const char *host = csp->http->host;
+ const char *path = csp->http->path;
+
+ /*
+ * Should we intercept ?
+ */
+
+ /* Note: "example.com" and "example.com." are equivalent hostnames. */
+
+ /* Either the host matches CGI_SITE_1_HOST ..*/
+ if ( ( (0 == strcmpic(host, CGI_SITE_1_HOST))
+ || (0 == strcmpic(host, CGI_SITE_1_HOST ".")))
+ && (path[0] == '/') )
+ {
+ /* ..then the path will all be for us. Remove leading '/' */
+ path++;
+ }
+ /* Or it's the host part CGI_SITE_2_HOST, and the path CGI_SITE_2_PATH */
+ else if ( ( (0 == strcmpic(host, CGI_SITE_2_HOST ))
+ || (0 == strcmpic(host, CGI_SITE_2_HOST ".")) )
+ && (0 == strncmpic(path, CGI_SITE_2_PATH, strlen(CGI_SITE_2_PATH))) )
+ {
+ /* take everything following CGI_SITE_2_PATH */
+ path += strlen(CGI_SITE_2_PATH);
+ if (*path == '/')
+ {
+ /* skip the forward slash after CGI_SITE_2_PATH */
+ path++;
+ }
+ else if (*path != '\0')
+ {
+ /*
+ * weirdness: URL is /configXXX, where XXX is some string
+ * Do *NOT* intercept.
+ */
+ return NULL;
+ }
+ }
+ else
+ {
+ /* Not a CGI */
+ return NULL;
+ }
+
+ /*
+ * This is a CGI call.
+ */
+
+ return dispatch_known_cgi(csp, path);
+}
+
+
+/*********************************************************************
+ *
+ * Function : grep_cgi_referrer
+ *
+ * Description : Ugly provisorical fix that greps the value of the
+ * referer HTTP header field out of a linked list of
+ * strings like found at csp->headers. Will disappear
+ * in Privoxy 3.1.
+ *
+ * FIXME: csp->headers ought to be csp->http->headers
+ * FIXME: Parsing all client header lines should
+ * happen right after the request is received!
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : pointer to value (no copy!), or NULL if none found.
+ *
+ *********************************************************************/
+char *grep_cgi_referrer(struct client_state *csp)
+{
+ struct list_entry *p;
+
+ for (p = csp->headers->first; p != NULL; p = p->next)
+ {
+ if (p->str == NULL) continue;
+ if (strncmpic(p->str, "Referer: ", 9) == 0)
+ {
+ return ((p->str) + 9);
+ }
+ }
+ return NULL;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : dispatch_known_cgi
+ *
+ * Description : Processes a CGI once dispatch_cgi has determined that
+ * it matches one of the magic prefixes. Parses the path
+ * as a cgi name plus query string, prepares a map that
+ * maps CGI parameter names to their values, initializes
+ * the http_response struct, and calls the relevant CGI
+ * handler function.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : path = Path of CGI, with the CGI prefix removed.
+ * Should not have a leading "/".
+ *
+ * Returns : http_response, or NULL on handler failure or out of
+ * memory.
+ *
+ *********************************************************************/
+static struct http_response *dispatch_known_cgi(struct client_state * csp,
+ const char * path)
+{
+ const struct cgi_dispatcher *d;
+ struct map *param_list;
+ struct http_response *rsp;
+ char *query_args_start;
+ char *path_copy;
+ char *referrer;
+ jb_err err;
+
+ if (NULL == (path_copy = strdup(path)))
+ {
+ return cgi_error_memory();
+ }
+
+ query_args_start = path_copy;
+ while (*query_args_start && *query_args_start != '?')
+ {
+ query_args_start++;
+ }
+ if (*query_args_start == '?')
+ {
+ *query_args_start++ = '\0';
+ }
+
+ if (NULL == (param_list = parse_cgi_parameters(query_args_start)))
+ {
+ free(path_copy);
+ return cgi_error_memory();
+ }
+
+ /*
+ * At this point:
+ * path_copy = CGI call name
+ * param_list = CGI params, as map
+ */
+
+ /* Get mem for response or fail*/
+ if (NULL == (rsp = alloc_http_response()))
+ {
+ free(path_copy);
+ free_map(param_list);
+ return cgi_error_memory();
+ }
+
+ log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path);
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3",
+ csp->ip_addr_str, csp->http->cmd);
+
+ /*
+ * Find and start the right CGI function
+ */
+ d = cgi_dispatchers;
+ for (;;)
+ {
+ if ((d->name == NULL) || (strcmp(path_copy, d->name) == 0))
+ {
+ /*
+ * If the called CGI is either harmless, or referred
+ * from a trusted source, start it.
+ */
+ if (d->harmless
+ || ((NULL != (referrer = grep_cgi_referrer(csp)))
+ && (0 == strncmp(referrer, "http://config.privoxy.org/", 26)))
+ )
+ {
+ err = (d->handler)(csp, rsp, param_list);
+ }
+ else
+ {
+ /*
+ * Else, modify toggle calls so that they only display
+ * the status, and deny all other calls.
+ */
+ if (0 == strcmp(path_copy, "toggle"))
+ {
+ unmap(param_list, "set");
+ err = (d->handler)(csp, rsp, param_list);
+ }
+ else
+ {
+ err = cgi_error_disabled(csp, rsp);
+ }
+ }
+
+ free(path_copy);
+ free_map(param_list);
+
+ if (err == JB_ERR_CGI_PARAMS)
+ {
+ err = cgi_error_bad_param(csp, rsp);
+ }
+ if (err && (err != JB_ERR_MEMORY))
+ {
+ /* Unexpected error! Shouldn't get here */
+ log_error(LOG_LEVEL_ERROR, "Unexpected CGI error %d in top-level handler. Please file a bug report!", err);
+ err = cgi_error_unknown(csp, rsp, err);
+ }
+ if (!err)
+ {
+ /* It worked */
+ return finish_http_response(rsp);
+ }
+ else
+ {
+ /* Error in handler, probably out-of-memory */
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+ }
+ d++;
+ }
+}
+
+
+/*********************************************************************
+ *
+ * Function : parse_cgi_parameters
+ *
+ * Description : Parse a URL-encoded argument string into name/value
+ * pairs and store them in a struct map list.
+ *
+ * Parameters :
+ * 1 : argstring = string to be parsed. Will be trashed.
+ *
+ * Returns : pointer to param list, or NULL if out of memory.
+ *
+ *********************************************************************/
+static struct map *parse_cgi_parameters(char *argstring)
+{
+ char *p;
+ char *vector[BUFFER_SIZE];
+ int pairs, i;
+ struct map *cgi_params;
+
+ if (NULL == (cgi_params = new_map()))
+ {
+ return NULL;
+ }
+
+ /*
+ * IE 5 does, of course, violate RFC 2316 Sect 4.1 and sends
+ * the fragment identifier along with the request, so we must
+ * cut it off here, so it won't pollute the CGI params:
+ */
+ if (NULL != (p = strchr(argstring, '#')))
+ {
+ *p = '\0';
+ }
+
+ pairs = ssplit(argstring, "&", vector, SZ(vector), 1, 1);
+
+ for (i = 0; i < pairs; i++)
+ {
+ if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0'))
+ {
+ *p = '\0';
+ if (map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0))
+ {
+ free_map(cgi_params);
+ return NULL;
+ }
+ }
+ }
+
+ return cgi_params;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_char_param
+ *
+ * Description : Get a single-character parameter passed to a CGI
+ * function.
+ *
+ * Parameters :
+ * 1 : parameters = map of cgi parameters
+ * 2 : param_name = The name of the parameter to read
+ *
+ * Returns : Uppercase character on success, '\0' on error.
+ *
+ *********************************************************************/
+char get_char_param(const struct map *parameters,
+ const char *param_name)
+{
+ char ch;
+
+ assert(parameters);
+ assert(param_name);
+
+ ch = *(lookup(parameters, param_name));
+ if ((ch >= 'a') && (ch <= 'z'))
+ {
+ ch = ch - 'a' + 'A';
+ }
+
+ return ch;
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_string_param
+ *
+ * Description : Get a string paramater, to be used as an
+ * ACTION_STRING or ACTION_MULTI paramater.
+ * Validates the input to prevent stupid/malicious
+ * users from corrupting their action file.
+ *
+ * Parameters :
+ * 1 : parameters = map of cgi parameters
+ * 2 : param_name = The name of the parameter to read
+ * 3 : pparam = destination for paramater. Allocated as
+ * part of the map "parameters", so don't free it.
+ * Set to NULL if not specified.
+ *
+ * Returns : JB_ERR_OK on success, or if the paramater
+ * was not specified.
+ * JB_ERR_MEMORY on out-of-memory.
+ * JB_ERR_CGI_PARAMS if the paramater is not valid.
+ *
+ *********************************************************************/
+jb_err get_string_param(const struct map *parameters,
+ const char *param_name,
+ const char **pparam)
+{
+ const char *param;
+ const char *s;
+ char ch;
+
+ assert(parameters);
+ assert(param_name);
+ assert(pparam);
+
+ *pparam = NULL;
+
+ param = lookup(parameters, param_name);
+ if (!*param)
+ {
+ return JB_ERR_OK;
+ }
+
+ if (strlen(param) >= CGI_PARAM_LEN_MAX)
+ {
+ /*
+ * Too long.
+ *
+ * Note that the length limit is arbitrary, it just seems
+ * sensible to limit it to *something*. There's no
+ * technical reason for any limit at all.
+ */
+ return JB_ERR_CGI_PARAMS;
+ }
+
+ /* Check every character to see if it's legal */
+ s = param;
+ while ((ch = *s++) != '\0')
+ {
+ if ( ((unsigned char)ch < (unsigned char)' ')
+ || (ch == '}') )
+ {
+ /* Probable hack attempt, or user accidentally used '}'. */
+ return JB_ERR_CGI_PARAMS;
+ }
+ }
+
+ /* Success */
+ *pparam = param;
+
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_number_param
+ *
+ * Description : Get a non-negative integer from the parameters
+ * passed to a CGI function.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : parameters = map of cgi parameters
+ * 3 : name = Name of CGI parameter to read
+ * 4 : pvalue = destination for value.
+ * Set to -1 on error.
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory
+ * JB_ERR_CGI_PARAMS if the parameter was not specified
+ * or is not valid.
+ *
+ *********************************************************************/
+jb_err get_number_param(struct client_state *csp,
+ const struct map *parameters,
+ char *name,
+ unsigned *pvalue)
+{
+ const char *param;
+ char ch;
+ unsigned value;
+
+ assert(csp);
+ assert(parameters);
+ assert(name);
+ assert(pvalue);
+
+ *pvalue = 0;
+
+ param = lookup(parameters, name);
+ if (!*param)
+ {
+ return JB_ERR_CGI_PARAMS;
+ }
+
+ /* We don't use atoi because I want to check this carefully... */
+
+ value = 0;
+ while ((ch = *param++) != '\0')
+ {
+ if ((ch < '0') || (ch > '9'))
+ {
+ return JB_ERR_CGI_PARAMS;
+ }
+
+ ch -= '0';
+
+ /* Note:
+ *
+ * <limits.h> defines UINT_MAX
+ *
+ * (UINT_MAX - ch) / 10 is the largest number that
+ * can be safely multiplied by 10 then have ch added.
+ */
+ if (value > ((UINT_MAX - (unsigned)ch) / 10U))
+ {
+ return JB_ERR_CGI_PARAMS;
+ }
+
+ value = value * 10 + ch;
+ }
+
+ /* Success */
+ *pvalue = value;
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : error_response
+ *
+ * Description : returns an http_response that explains the reason
+ * why a request failed.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : templatename = Which template should be used for the answer
+ * 3 : sys_err = system error number
+ *
+ * Returns : A http_response. If we run out of memory, this
+ * will be cgi_error_memory().
+ *
+ *********************************************************************/
+struct http_response *error_response(struct client_state *csp,
+ const char *templatename,
+ int sys_err)
+{
+ jb_err err;
+ struct http_response *rsp;
+ struct map * exports = default_exports(csp, NULL);
+ if (exports == NULL)
+ {
+ return cgi_error_memory();
+ }
+
+ if (NULL == (rsp = alloc_http_response()))
+ {
+ free_map(exports);
+ return cgi_error_memory();
+ }
+
+ err = map(exports, "host", 1, html_encode(csp->http->host), 0);
+ if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
+ if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
+ if (!err) err = map(exports, "error", 1, html_encode_and_free_original(safe_strerror(sys_err)), 0);
+ if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
+ if (!err)
+ {
+ err = map(exports, "host-ip", 1, html_encode(csp->http->host_ip_addr_str), 0);
+ if (err)
+ {
+ /* Some failures, like "404 no such domain", don't have an IP address. */
+ err = map(exports, "host-ip", 1, html_encode(csp->http->host), 0);
+ }
+ }
+
+
+ if (err)
+ {
+ free_map(exports);
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+
+ if (!strcmp(templatename, "no-such-domain"))
+ {
+ rsp->status = strdup("404 No such domain");
+ if (rsp->status == NULL)
+ {
+ free_map(exports);
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+ }
+ else if (!strcmp(templatename, "connect-failed"))
+ {
+ rsp->status = strdup("503 Connect failed");
+ if (rsp->status == NULL)
+ {
+ free_map(exports);
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+ }
+
+ err = template_fill_for_cgi(csp, templatename, exports, rsp);
+ if (err)
+ {
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+
+ return finish_http_response(rsp);
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_error_disabled
+ *
+ * Description : CGI function that is called to generate an error
+ * response if the actions editor or toggle CGI are
+ * accessed despite having being disabled at compile-
+ * or run-time.
+ *
+ * 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_disabled(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-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>\r\n"
+ "<head><title>500 Internal Privoxy Error</title></head>\r\n"
+ "<body>\r\n"
+ "<h1>500 Internal Privoxy Error</h1>\r\n"
+ "<p>Privoxy <b>ran out of memory</b> while processing your request.</p>\r\n"
+ "<p>Please contact your proxy administrator, or try again later</p>\r\n"
+ "</body>\r\n"
+ "</html>\r\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);
+}
+
+
+/*********************************************************************
+ *
+ * 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(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>\r\n"
+ "<head><title>500 Internal Privoxy Error</title></head>\r\n"
+ "<body>\r\n"
+ "<h1>500 Internal Privoxy Error</h1>\r\n"
+ "<p>Privoxy encountered an error while processing your request:</p>\r\n"
+ "<p><b>Could not load template file <code>";
+ static const char body_suffix[] =
+ "</code> or one of it's included components.</b></p>\r\n"
+ "<p>Please contact your proxy administrator.</p>\r\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>\r\n"
+ "</body>\r\n"
+ "</html>\r\n";
+
+ 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(strlen(body_prefix) + strlen(template_name) + strlen(body_suffix) + 1);
+ if (rsp->body == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+ strcpy(rsp->body, body_prefix);
+ strcat(rsp->body, template_name);
+ strcat(rsp->body, body_suffix);
+
+ rsp->status = strdup(status);
+ if (rsp->body == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }