+#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 != '/')
+ {
+ query_args_start++;
+ }
+ if (*query_args_start == '/')
+ {
+ *query_args_start++ = '\0';
+ if ((param_list = new_map()))
+ {
+ map(param_list, "file", 1, url_decode(query_args_start), 0);
+ }
+ }
+ else
+ {
+ 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;
+ }