+ * CGI Parameters : None
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_send_default_favicon(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ static const char default_favicon_data[] =
+ "\000\000\001\000\001\000\020\020\002\000\000\000\000\000\260"
+ "\000\000\000\026\000\000\000\050\000\000\000\020\000\000\000"
+ "\040\000\000\000\001\000\001\000\000\000\000\000\100\000\000"
+ "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000"
+ "\000\000\377\377\377\000\377\000\052\000\017\360\000\000\077"
+ "\374\000\000\161\376\000\000\161\376\000\000\361\377\000\000"
+ "\361\377\000\000\360\017\000\000\360\007\000\000\361\307\000"
+ "\000\361\307\000\000\361\307\000\000\360\007\000\000\160\036"
+ "\000\000\177\376\000\000\077\374\000\000\017\360\000\000\360"
+ "\017\000\000\300\003\000\000\200\001\000\000\200\001\000\000"
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
+ "\000\000\200\001\000\000\200\001\000\000\300\003\000\000\360"
+ "\017\000\000";
+ static const size_t favicon_length = sizeof(default_favicon_data) - 1;
+
+ (void)csp;
+ (void)parameters;
+
+ rsp->body = bindup(default_favicon_data, favicon_length);
+ rsp->content_length = favicon_length;
+
+ if (rsp->body == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ if (enlist(rsp->headers, "Content-Type: image/x-icon"))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ rsp->is_static = 1;
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_send_error_favicon
+ *
+ * Description : CGI function that sends the favicon for error pages.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters : None
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_send_error_favicon(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ static const char error_favicon_data[] =
+ "\000\000\001\000\001\000\020\020\002\000\000\000\000\000\260"
+ "\000\000\000\026\000\000\000\050\000\000\000\020\000\000\000"
+ "\040\000\000\000\001\000\001\000\000\000\000\000\100\000\000"
+ "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000"
+ "\000\000\377\377\377\000\000\000\377\000\017\360\000\000\077"
+ "\374\000\000\161\376\000\000\161\376\000\000\361\377\000\000"
+ "\361\377\000\000\360\017\000\000\360\007\000\000\361\307\000"
+ "\000\361\307\000\000\361\307\000\000\360\007\000\000\160\036"
+ "\000\000\177\376\000\000\077\374\000\000\017\360\000\000\360"
+ "\017\000\000\300\003\000\000\200\001\000\000\200\001\000\000"
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
+ "\000\000\200\001\000\000\200\001\000\000\300\003\000\000\360"
+ "\017\000\000";
+ static const size_t favicon_length = sizeof(error_favicon_data) - 1;
+
+ (void)csp;
+ (void)parameters;
+
+ rsp->body = bindup(error_favicon_data, favicon_length);
+ rsp->content_length = favicon_length;
+
+ if (rsp->body == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ if (enlist(rsp->headers, "Content-Type: image/x-icon"))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ rsp->is_static = 1;
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_send_stylesheet
+ *
+ * Description : CGI function that sends a css stylesheet found
+ * in the cgi-style.css template
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters : None
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_send_stylesheet(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ jb_err err;
+
+ assert(csp);
+ assert(rsp);
+
+ (void)parameters;
+
+ err = template_load(csp, &rsp->body, "cgi-style.css", 0);
+
+ if (err == JB_ERR_FILE)
+ {
+ /*
+ * No way to tell user; send empty stylesheet
+ */
+ log_error(LOG_LEVEL_ERROR, "Could not find cgi-style.css template");
+ }
+ else if (err)
+ {
+ return err; /* JB_ERR_MEMORY */
+ }
+
+ if (enlist(rsp->headers, "Content-Type: text/css"))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_send_url_info_osd
+ *
+ * Description : CGI function that sends the OpenSearch Description
+ * template for the show-url-info page. It allows to
+ * access the page through "search engine plugins".
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters : None
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_send_url_info_osd(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ jb_err err = JB_ERR_MEMORY;
+ struct map *exports = default_exports(csp, NULL);
+
+ (void)csp;
+ (void)parameters;
+
+ if (NULL != exports)
+ {
+ err = template_fill_for_cgi(csp, "url-info-osd.xml", exports, rsp);
+ if (JB_ERR_OK == err)
+ {
+ err = enlist(rsp->headers,
+ "Content-Type: application/opensearchdescription+xml");
+ }
+ }
+
+ return err;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_content_type
+ *
+ * Description : Use the file extension to guess the content type
+ * header we should use to serve the file.
+ *
+ * Parameters :
+ * 1 : filename = Name of the file whose content type
+ * we care about
+ *
+ * Returns : The guessed content type.
+ *
+ *********************************************************************/
+static const char *get_content_type(const char *filename)
+{
+ int i;
+ struct content_type
+ {
+ const char extension[6];
+ const char content_type[11];
+ };
+ static const struct content_type content_types[] =
+ {
+ {".css", "text/css"},
+ {".jpg", "image/jpeg"},
+ {".jpeg", "image/jpeg"},
+ {".png", "image/png"},
+ };
+
+ for (i = 0; i < SZ(content_types); i++)
+ {
+ if (strstr(filename, content_types[i].extension))
+ {
+ return content_types[i].content_type;
+ }
+ }
+
+ /* No match by extension, default to html */
+ return "text/html";
+}
+
+/*********************************************************************
+ *
+ * Function : cgi_send_user_manual
+ *
+ * Description : CGI function that sends a file in the user
+ * manual directory.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters : file=name.html, the name of the HTML file
+ * (relative to user-manual from config)
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_send_user_manual(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ const char *filename;
+ char *full_path;
+ jb_err err = JB_ERR_OK;
+ const char *content_type;
+
+ assert(csp);
+ assert(rsp);
+ assert(parameters);
+
+ if (0 == strncmpic(csp->config->usermanual, "http://", 7))
+ {
+ log_error(LOG_LEVEL_CGI, "Request for local user-manual "
+ "received while user-manual delivery is disabled.");
+ return cgi_error_404(csp, rsp, parameters);
+ }
+
+ if (!parameters->first)
+ {
+ /* requested http://p.p/user-manual (without trailing slash) */
+ return cgi_redirect(rsp, CGI_PREFIX "user-manual/");
+ }
+
+ get_string_param(parameters, "file", &filename);
+ if (filename == NULL)
+ {
+ /* It's '/' so serve the index.html if there is one. */
+ filename = "index.html";
+ }
+ else if (NULL != strchr(filename, '/') || NULL != strstr(filename, ".."))
+ {
+ /*
+ * We currently only support a flat file
+ * hierarchy for the documentation.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "Rejecting the request to serve '%s' as it contains '/' or '..'",
+ filename);
+ return JB_ERR_CGI_PARAMS;
+ }
+
+ full_path = make_path(csp->config->usermanual, filename);
+ if (full_path == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ err = load_file(full_path, &rsp->body, &rsp->content_length);
+ if (JB_ERR_OK != err)
+ {
+ assert((JB_ERR_FILE == err) || (JB_ERR_MEMORY == err));
+ if (JB_ERR_FILE == err)
+ {
+ err = cgi_error_no_template(csp, rsp, full_path);
+ }
+ freez(full_path);
+ return err;
+ }
+ freez(full_path);
+
+ content_type = get_content_type(filename);
+ log_error(LOG_LEVEL_CGI,
+ "Content-Type guessed for %s: %s", filename, content_type);
+
+ return enlist_unique_header(rsp->headers, "Content-Type", content_type);
+
+}
+
+
+#ifdef FEATURE_EXTENDED_STATISTICS
+/*********************************************************************
+ *
+ * Function : get_block_reason_statistics_table
+ *
+ * Description : Produces the block reason statistic table content.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Pointer to the HTML statistic table content or
+ * NULL on out of memory
+ *
+ *********************************************************************/
+static char *get_block_reason_statistics_table(const struct client_state *csp)
+{
+ char buf[BUFFER_SIZE];
+ char *statistics;
+ int i;
+ struct file_list *fl;
+ jb_err err = JB_ERR_OK;
+
+ statistics = strdup_or_die("");
+
+ /* Run through all action files. */
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ struct url_actions *b;
+ struct action_spec *last_action = NULL;
+
+ if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
+ {
+ /* Skip empty files */
+ continue;
+ }
+
+ /* Go through all the actions. */
+ for (b = b->next; NULL != b; b = b->next)
+ {
+ if (last_action == b->action)
+ {
+ continue;
+ }
+ if ((b->action->add & ACTION_BLOCK))
+ {
+ unsigned long long count;
+ const char *block_reason = b->action->string[ACTION_STRING_BLOCK];
+ const char *encoded_block_reason = html_encode(block_reason);
+
+ if (encoded_block_reason == NULL)
+ {
+ freez(statistics);
+ return NULL;
+ }
+ get_block_reason_count(block_reason, &count);
+ snprintf(buf, sizeof(buf),
+ "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>\n",
+ encoded_block_reason, count);
+ freez(encoded_block_reason);
+
+ if (!err) err = string_append(&statistics, buf);
+ }
+ last_action = b->action;
+ }
+ }
+
+ return statistics;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_filter_statistics_table
+ *
+ * Description : Produces the filter statistic table content.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Pointer to the HTML statistic table content or
+ * NULL on out of memory
+ *
+ *********************************************************************/
+static char *get_filter_statistics_table(const struct client_state *csp)
+{
+ char buf[BUFFER_SIZE];
+ char *statistics;
+ int i;
+ struct file_list *fl;
+ struct re_filterfile_spec *b;
+ jb_err err = JB_ERR_OK;
+
+ statistics = strdup_or_die("");
+
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ fl = csp->rlist[i];
+ if ((NULL == fl) || (NULL == fl->f))
+ {
+ /*
+ * Either there are no filter files left or this
+ * filter file just contains no valid filters.
+ *
+ * Continue to be sure we don't miss valid filter
+ * files that are chained after empty or invalid ones.
+ */
+ continue;
+ }
+
+ for (b = fl->f; b != NULL; b = b->next)
+ {
+ if (b->type == FT_CONTENT_FILTER)
+ {
+ unsigned long long executions;
+ unsigned long long response_bodies_modified;
+ unsigned long long hits;
+
+ get_filter_statistics(b->name, &executions, &response_bodies_modified, &hits);
+ snprintf(buf, sizeof(buf),
+ "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>"
+ "<td style=\"text-align: right\">%llu</td>"
+ "<td style=\"text-align: right\">%llu</td><tr>\n",
+ b->name, executions, response_bodies_modified, hits);
+
+ if (!err) err = string_append(&statistics, buf);
+ }
+ }
+ }
+
+ return statistics;
+
+}
+#endif /* def FEATURE_EXTENDED_STATISTICS */
+
+
+/*********************************************************************
+ *
+ * Function : cgi_show_status
+ *
+ * Description : CGI function that returns a web page describing the
+ * current status of Privoxy.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters :
+ * file : Which file to show. Only first letter is checked,
+ * valid values are:
+ * - "a"ction file
+ * - "r"egex
+ * - "t"rust
+ * Default is to show menu and other information.
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_show_status(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ char *s = NULL;
+ unsigned i;
+ int j;
+
+ char buf[BUFFER_SIZE];
+#ifdef FEATURE_STATISTICS
+ float perc_rej; /* Percentage of http requests rejected */
+ int local_urls_read;
+ int local_urls_rejected;
+#endif /* ndef FEATURE_STATISTICS */
+ jb_err err = JB_ERR_OK;
+
+ struct map *exports;
+
+ assert(csp);
+ assert(rsp);
+ assert(parameters);
+
+ if ('\0' != *(lookup(parameters, "file")))
+ {
+ return cgi_show_file(csp, rsp, parameters);
+ }
+
+ if (NULL == (exports = default_exports(csp, "show-status")))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ s = strdup("");
+ for (j = 0; (s != NULL) && (j < Argc); j++)
+ {
+ if (!err) err = string_join (&s, html_encode(Argv[j]));
+ if (!err) err = string_append(&s, " ");
+ }
+ if (!err) err = map(exports, "invocation", 1, s, 0);
+
+ if (!err) err = map(exports, "options", 1, csp->config->proxy_args, 1);
+ if (!err) err = show_defines(exports);
+
+ if (err)
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+#ifdef FEATURE_STATISTICS
+ local_urls_read = urls_read;
+ local_urls_rejected = urls_rejected;
+
+ /*
+ * Need to alter the stats not to include the fetch of this
+ * page.
+ *
+ * Can't do following thread safely! doh!
+ *
+ * urls_read--;
+ * urls_rejected--; * This will be incremented subsequently *
+ */
+
+ if (local_urls_read == 0)
+ {
+ if (!err) err = map_block_killer(exports, "have-stats");
+ }
+ else
+ {
+ if (!err) err = map_block_killer(exports, "have-no-stats");
+
+ perc_rej = (float)local_urls_rejected * 100.0F /
+ (float)local_urls_read;
+
+ snprintf(buf, sizeof(buf), "%d", local_urls_read);
+ if (!err) err = map(exports, "requests-received", 1, buf, 1);
+
+ snprintf(buf, sizeof(buf), "%d", local_urls_rejected);
+ if (!err) err = map(exports, "requests-blocked", 1, buf, 1);
+
+ snprintf(buf, sizeof(buf), "%6.2f", perc_rej);
+ if (!err) err = map(exports, "percent-blocked", 1, buf, 1);
+ }
+
+#else /* ndef FEATURE_STATISTICS */
+ if (!err) err = map_block_killer(exports, "statistics");
+#endif /* ndef FEATURE_STATISTICS */
+
+#ifdef FEATURE_EXTENDED_STATISTICS
+ if (!err)
+ {
+ char *block_reason_statistics = get_block_reason_statistics_table(csp);
+ if (block_reason_statistics != NULL)
+ {
+ err = map(exports, "block-reason-statistics", 1, block_reason_statistics, 0);
+ }
+ else
+ {
+ err = map_block_killer(exports, "extended-statistics");
+ }
+ }
+ if (!err)
+ {
+ char *filter_statistics = get_filter_statistics_table(csp);
+ if (filter_statistics != NULL)
+ {
+ err = map(exports, "filter-statistics", 1, filter_statistics, 0);
+ }
+ else
+ {
+ err = map_block_killer(exports, "extended-statistics");
+ }
+ }
+#else /* ndef FEATURE_EXTENDED_STATISTICS */
+ if (!err) err = map_block_killer(exports, "extended-statistics");
+#endif /* def FEATURE_EXTENDED_STATISTICS */
+
+ /*
+ * List all action files in use, together with view and edit links,
+ * except for standard.action, which should only be viewable. (Not
+ * enforced in the editor itself)
+ * FIXME: Shouldn't include hardwired HTML here, use line template instead!
+ */
+ s = strdup("");
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ if (csp->actions_list[i] != NULL)
+ {
+ if (!err) err = string_append(&s, "<tr><td>");
+ if (!err) err = string_join(&s, html_encode(csp->actions_list[i]->filename));
+ snprintf(buf, sizeof(buf),
+ "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&index=%u\">View</a>", i);
+ if (!err) err = string_append(&s, buf);
+
+#ifdef FEATURE_CGI_EDIT_ACTIONS
+ if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
+ && (NULL != csp->config->actions_file_short[i]))
+ {
+#ifdef HAVE_ACCESS
+ if (access(csp->config->actions_file[i], W_OK) == 0)
+ {
+#endif /* def HAVE_ACCESS */
+ snprintf(buf, sizeof(buf), " <a href=\"/edit-actions-list?f=%u\">Edit</a>", i);
+ if (!err) err = string_append(&s, buf);
+#ifdef HAVE_ACCESS
+ }
+ else
+ {
+ if (!err) err = string_append(&s, " <strong>No write access.</strong>");
+ }
+#endif /* def HAVE_ACCESS */
+ }
+#endif
+
+ if (!err) err = string_append(&s, "</td></tr>\n");
+ }
+ }
+ if (!err && *s != '\0')
+ {
+ err = map(exports, "actions-filenames", 1, s, 0);
+ }
+ else
+ {
+ if (!err) err = map(exports, "actions-filenames", 1, "<tr><td>None specified</td></tr>", 1);
+ freez(s);
+ }
+
+ /*
+ * List all re_filterfiles in use, together with view options.
+ * FIXME: Shouldn't include hardwired HTML here, use line template instead!
+ */
+ s = strdup("");
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ if (csp->rlist[i] != NULL)
+ {
+ if (!err) err = string_append(&s, "<tr><td>");
+ if (!err) err = string_join(&s, html_encode(csp->rlist[i]->filename));
+ snprintf(buf, sizeof(buf),
+ "</td><td class=\"buttons\"><a href=\"/show-status?file=filter&index=%u\">View</a>", i);
+ if (!err) err = string_append(&s, buf);
+ if (!err) err = string_append(&s, "</td></tr>\n");
+ }
+ }
+ if (!err && *s != '\0')
+ {
+ err = map(exports, "re-filter-filenames", 1, s, 0);
+ }
+ else
+ {
+ if (!err) err = map(exports, "re-filter-filenames", 1, "<tr><td>None specified</td></tr>", 1);
+ if (!err) err = map_block_killer(exports, "have-filterfile");
+ freez(s);
+ }
+
+#ifdef FEATURE_TRUST
+ if (csp->tlist)
+ {
+ if (!err) err = map(exports, "trust-filename", 1, html_encode(csp->tlist->filename), 0);
+ }
+ else
+ {
+ if (!err) err = map(exports, "trust-filename", 1, "None specified", 1);
+ if (!err) err = map_block_killer(exports, "have-trustfile");
+ }
+#else
+ if (!err) err = map_block_killer(exports, "trust-support");
+#endif /* ndef FEATURE_TRUST */
+
+#ifdef FEATURE_CGI_EDIT_ACTIONS
+ if (!err && (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
+ {
+ err = map_block_killer(exports, "cgi-editor-is-disabled");
+ }
+#endif /* ndef CGI_EDIT_ACTIONS */
+
+ if (!err) err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
+
+ if (err)
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ return template_fill_for_cgi(csp, "show-status", exports, rsp);
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_show_url_info
+ *
+ * Description : CGI function that determines and shows which actions
+ * Privoxy will perform for a given url, and which
+ * matches starting from the defaults have lead to that.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters :
+ * url : The url whose actions are to be determined.
+ * If url is unset, the url-given conditional will be
+ * set, so that all but the form can be suppressed in
+ * the template.
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_show_url_info(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ char *url_param;
+ struct map *exports;
+ char buf[150];
+
+ assert(csp);
+ assert(rsp);
+ assert(parameters);
+
+ if (NULL == (exports = default_exports(csp, "show-url-info")))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ /*
+ * Get the url= parameter (if present) and remove any leading/trailing spaces.
+ */
+ url_param = strdup_or_die(lookup(parameters, "url"));
+ chomp(url_param);
+
+ /*
+ * Handle prefixes. 4 possibilities:
+ * 1) "http://" or "https://" prefix present and followed by URL - OK
+ * 2) Only the "http://" or "https://" part is present, no URL - change
+ * to empty string so it will be detected later as "no URL".
+ * 3) Parameter specified but doesn't start with "http(s?)://" - add a
+ * "http://" prefix.
+ * 4) Parameter not specified or is empty string - let this fall through
+ * for now, next block of code will handle it.
+ */
+ if (0 == strncmp(url_param, "http://", 7))
+ {
+ if (url_param[7] == '\0')
+ {
+ /*
+ * Empty URL (just prefix).
+ * Make it totally empty so it's caught by the next if ()
+ */
+ url_param[0] = '\0';
+ }
+ }
+ else if (0 == strncmp(url_param, "https://", 8))
+ {
+ if (url_param[8] == '\0')
+ {
+ /*
+ * Empty URL (just prefix).
+ * Make it totally empty so it's caught by the next if ()
+ */
+ url_param[0] = '\0';
+ }
+ }
+ else if ((url_param[0] != '\0')
+ && ((NULL == strstr(url_param, "://")
+ || (strstr(url_param, "://") > strstr(url_param, "/")))))
+ {
+ /*
+ * No prefix or at least no prefix before
+ * the first slash - assume http://
+ */
+ char *url_param_prefixed = strdup_or_die("http://");
+
+ if (JB_ERR_OK != string_join(&url_param_prefixed, url_param))
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+ url_param = url_param_prefixed;
+ }
+
+ /*
+ * Hide "toggle off" warning if Privoxy is toggled on.
+ */
+ if (
+#ifdef FEATURE_TOGGLE
+ (global_toggle_state == 1) &&
+#endif /* def FEATURE_TOGGLE */
+ map_block_killer(exports, "privoxy-is-toggled-off")
+ )
+ {
+ freez(url_param);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ if (url_param[0] == '\0')
+ {
+ /* URL parameter not specified, display query form only. */
+ free(url_param);
+ if (map_block_killer(exports, "url-given")
+ || map(exports, "url", 1, "", 1))
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+ }
+ else
+ {
+ /* Given a URL, so query it. */
+ jb_err err;
+ char *matches;
+ char *s;
+ int hits = 0;
+ struct file_list *fl;
+ struct url_actions *b;
+ struct http_request url_to_query[1];
+ struct current_action_spec action[1];
+ int i;
+
+ if (map(exports, "url", 1, html_encode(url_param), 0))
+ {
+ free(url_param);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ init_current_action(action);
+
+ if (map(exports, "default", 1, current_action_to_html(csp, action), 0))
+ {
+ free_current_action(action);
+ free(url_param);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ memset(url_to_query, '\0', sizeof(url_to_query));
+ err = parse_http_url(url_param, url_to_query, REQUIRE_PROTOCOL);
+ assert((err != JB_ERR_OK) || (url_to_query->ssl == !strncmpic(url_param, "https://", 8)));
+
+ free(url_param);
+
+ if (err == JB_ERR_MEMORY)
+ {
+ free_http_request(url_to_query);
+ free_current_action(action);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+ else if (err)
+ {
+ /* Invalid URL */
+
+ err = map(exports, "matches", 1, "<b>[Invalid URL specified!]</b>" , 1);
+ if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1);
+ if (!err) err = map_block_killer(exports, "valid-url");
+
+ free_current_action(action);
+ free_http_request(url_to_query);
+
+ if (err)
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
+ }
+
+ /*
+ * We have a warning about SSL paths. Hide it for unencrypted sites
+ * and unconditionally if https inspection is enabled.
+ */
+#ifndef FEATURE_HTTPS_INSPECTION
+ if (!url_to_query->ssl)
+#endif
+ {
+ if (map_block_killer(exports, "https-and-no-https-inspection"))
+ {
+ free_current_action(action);
+ free_map(exports);
+ free_http_request(url_to_query);
+ return JB_ERR_MEMORY;
+ }
+ }
+
+ matches = strdup_or_die("<table summary=\"\" class=\"transparent\">");
+
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ if (NULL == csp->config->actions_file_short[i]
+ || !strcmp(csp->config->actions_file_short[i], "standard.action")) continue;
+
+ b = NULL;
+ hits = 1;
+ if ((fl = csp->actions_list[i]) != NULL)
+ {
+ if ((b = fl->f) != NULL)
+ {
+ /* FIXME: Hardcoded HTML! */
+ string_append(&matches, "<tr><th>In file: ");
+ string_join (&matches, html_encode(csp->config->actions_file_short[i]));
+ snprintf(buf, sizeof(buf), " <a class=\"cmd\" href=\"/show-status?file=actions&index=%d\">", i);
+ string_append(&matches, buf);
+ string_append(&matches, "View</a>");
+#ifdef FEATURE_CGI_EDIT_ACTIONS
+ if (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
+ {
+#ifdef HAVE_ACCESS
+ if (access(csp->config->actions_file[i], W_OK) == 0)
+ {
+#endif /* def HAVE_ACCESS */
+ snprintf(buf, sizeof(buf),
+ " <a class=\"cmd\" href=\"/edit-actions-list?f=%d\">", i);
+ string_append(&matches, buf);
+ string_append(&matches, "Edit</a>");
+#ifdef HAVE_ACCESS
+ }
+ else
+ {
+ string_append(&matches, " <strong>No write access.</strong>");
+ }
+#endif /* def HAVE_ACCESS */
+ }
+#endif /* FEATURE_CGI_EDIT_ACTIONS */
+
+ string_append(&matches, "</th></tr>\n");
+
+ hits = 0;
+ b = b->next;
+ }
+ }
+
+ for (; (b != NULL) && (matches != NULL); b = b->next)
+ {
+ if (url_match(b->url, url_to_query))
+ {
+ string_append(&matches, "<tr><td>{");
+ string_join (&matches, actions_to_html(csp, b->action));
+ string_append(&matches, " }<br>\n<code>");
+ string_join (&matches, html_encode(b->url->spec));
+ string_append(&matches, "</code></td></tr>\n");
+
+ if (merge_current_action(action, b->action))
+ {
+ freez(matches);
+ free_http_request(url_to_query);
+ free_current_action(action);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+ hits++;
+ }
+ }
+
+ if (!hits)
+ {
+ string_append(&matches, "<tr><td>(no matches in this file)</td></tr>\n");
+ }
+ }
+ string_append(&matches, "</table>\n");
+
+ /*
+ * XXX: Kludge to make sure the "Forward settings" section
+ * shows what forward-override{} would do with the requested URL.
+ * No one really cares how the CGI request would be forwarded
+ * if it wasn't intercepted as CGI request in the first place.
+ *
+ * From here on the action bitmask will no longer reflect
+ * the real url (http://config.privoxy.org/show-url-info?url=.*),
+ * but luckily it's no longer required later on anyway.
+ */
+ free_current_action(csp->action);
+ get_url_actions(csp, url_to_query);
+
+ /*
+ * Fill in forwarding settings.
+ *
+ * The possibilities are:
+ * - no forwarding
+ * - http forwarding only
+ * - socks4(a) forwarding only
+ * - socks4(a) and http forwarding.
+ *
+ * XXX: Parts of this code could be reused for the
+ * "forwarding-failed" template which currently doesn't
+ * display the proxy port and an eventual second forwarder.
+ */
+ {
+ const struct forward_spec *fwd = forward_url(csp, url_to_query);
+
+ if ((fwd->gateway_host == NULL) && (fwd->forward_host == NULL))
+ {
+ if (!err) err = map_block_killer(exports, "socks-forwarder");
+ if (!err) err = map_block_killer(exports, "http-forwarder");
+ }
+ else
+ {
+ char port[10]; /* We save proxy ports as int but need a string here */
+
+ if (!err) err = map_block_killer(exports, "no-forwarder");
+
+ if (fwd->gateway_host != NULL)
+ {
+ char *socks_type = NULL;
+
+ switch (fwd->type)
+ {
+ case SOCKS_4:
+ socks_type = "socks4";
+ break;
+ case SOCKS_4A:
+ socks_type = "socks4a";
+ break;
+ case SOCKS_5:
+ socks_type = "socks5";
+ break;
+ case SOCKS_5T:
+ socks_type = "socks5t";
+ break;
+ default:
+ log_error(LOG_LEVEL_FATAL, "Unknown socks type: %d.", fwd->type);
+ }
+
+ if (!err) err = map(exports, "socks-type", 1, socks_type, 1);
+ if (!err) err = map(exports, "gateway-host", 1, fwd->gateway_host, 1);
+ snprintf(port, sizeof(port), "%d", fwd->gateway_port);
+ if (!err) err = map(exports, "gateway-port", 1, port, 1);
+ }
+ else
+ {
+ if (!err) err = map_block_killer(exports, "socks-forwarder");
+ }
+
+ if (fwd->forward_host != NULL)
+ {
+ if (!err) err = map(exports, "forward-host", 1, fwd->forward_host, 1);
+ snprintf(port, sizeof(port), "%d", fwd->forward_port);
+ if (!err) err = map(exports, "forward-port", 1, port, 1);
+ }
+ else
+ {
+ if (!err) err = map_block_killer(exports, "http-forwarder");
+ }
+ }
+ }
+
+ free_http_request(url_to_query);
+
+ if (err || matches == NULL)
+ {
+ free_current_action(action);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+#ifdef FEATURE_CGI_EDIT_ACTIONS
+ if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
+ {
+ err = map_block_killer(exports, "cgi-editor-is-disabled");
+ }
+#endif /* FEATURE_CGI_EDIT_ACTIONS */
+
+ /*
+ * If zlib support is available, if no content filters
+ * are enabled or if the prevent-compression action is enabled,
+ * suppress the "compression could prevent filtering" warning.
+ */
+#ifndef FEATURE_ZLIB
+ if (!content_filters_enabled(action) ||
+ (action->flags & ACTION_NO_COMPRESSION))
+#endif
+ {
+ if (!err) err = map_block_killer(exports, "filters-might-be-ineffective");
+ }
+
+ if (err || map(exports, "matches", 1, matches , 0))
+ {
+ free_current_action(action);
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ s = current_action_to_html(csp, action);
+
+ free_current_action(action);
+
+ if (map(exports, "final", 1, s, 0))
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+ }
+
+ return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
+}
+
+
+/*********************************************************************
+ *
+ * Function : cgi_robots_txt
+ *
+ * Description : CGI function to return "/robots.txt".
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters : None
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.