1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/cgisimple.c,v $
5 * Purpose : Simple CGIs to get information about Privoxy's
8 * Copyright : Written by and Copyright (C) 2001-2020 the
9 * Privoxy team. https://www.privoxy.org/
11 * Based on the Internet Junkbuster originally written
12 * by and Copyright (C) 1997 Anonymous Coders and
13 * Junkbusters Corporation. http://www.junkbusters.com
15 * This program is free software; you can redistribute it
16 * and/or modify it under the terms of the GNU General
17 * Public License as published by the Free Software
18 * Foundation; either version 2 of the License, or (at
19 * your option) any later version.
21 * This program is distributed in the hope that it will
22 * be useful, but WITHOUT ANY WARRANTY; without even the
23 * implied warranty of MERCHANTABILITY or FITNESS FOR A
24 * PARTICULAR PURPOSE. See the GNU General Public
25 * License for more details.
27 * The GNU General Public License should be included with
28 * this file. If not, you can view it at
29 * http://www.gnu.org/copyleft/gpl.html
30 * or write to the Free Software Foundation, Inc., 59
31 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 **********************************************************************/
39 #include <sys/types.h>
45 #if defined (HAVE_ACCESS) && defined (HAVE_UNISTD_H)
47 #endif /* def HAVE_ACCESS && HAVE_UNISTD_H */
51 #include "cgisimple.h"
62 #ifdef FEATURE_CLIENT_TAGS
63 #include "client-tags.h"
66 static jb_err show_defines(struct map *exports);
67 static jb_err cgi_show_file(struct client_state *csp,
68 struct http_response *rsp,
69 const struct map *parameters);
70 static jb_err load_file(const char *filename, char **buffer, size_t *length);
72 /*********************************************************************
74 * Function : cgi_default
76 * Description : CGI function that is called for the CGI_SITE_1_HOST
77 * and CGI_SITE_2_HOST/CGI_SITE_2_PATH base URLs.
78 * Boring - only exports the default exports.
81 * 1 : csp = Current client state (buffers, headers, etc...)
82 * 2 : rsp = http_response data structure for output
83 * 3 : parameters = map of cgi parameters
85 * CGI Parameters : none
87 * Returns : JB_ERR_OK on success
88 * JB_ERR_MEMORY on out-of-memory
90 *********************************************************************/
91 jb_err cgi_default(struct client_state *csp,
92 struct http_response *rsp,
93 const struct map *parameters)
102 if (NULL == (exports = default_exports(csp, "")))
104 return JB_ERR_MEMORY;
107 return template_fill_for_cgi(csp, "default", exports, rsp);
111 /*********************************************************************
113 * Function : cgi_error_404
115 * Description : CGI function that is called if an unknown action was
119 * 1 : csp = Current client state (buffers, headers, etc...)
120 * 2 : rsp = http_response data structure for output
121 * 3 : parameters = map of cgi parameters
123 * CGI Parameters : none
125 * Returns : JB_ERR_OK on success
126 * JB_ERR_MEMORY on out-of-memory error.
128 *********************************************************************/
129 jb_err cgi_error_404(struct client_state *csp,
130 struct http_response *rsp,
131 const struct map *parameters)
139 if (NULL == (exports = default_exports(csp, NULL)))
141 return JB_ERR_MEMORY;
144 rsp->status = strdup_or_die("404 Privoxy configuration page not found");
146 return template_fill_for_cgi(csp, "cgi-error-404", exports, rsp);
150 #ifdef FEATURE_GRACEFUL_TERMINATION
151 /*********************************************************************
155 * Description : CGI function to shut down Privoxy.
156 * NOTE: Turning this on in a production build
157 * would be a BAD idea. An EXTREMELY BAD idea.
158 * In short, don't do it.
161 * 1 : csp = Current client state (buffers, headers, etc...)
162 * 2 : rsp = http_response data structure for output
163 * 3 : parameters = map of cgi parameters
165 * CGI Parameters : none
167 * Returns : JB_ERR_OK on success
169 *********************************************************************/
170 jb_err cgi_die (struct client_state *csp,
171 struct http_response *rsp,
172 const struct map *parameters)
174 static const char status[] = "200 OK Privoxy shutdown request received";
175 static const char body[] =
178 " <title>Privoxy shutdown request received</title>\n"
179 " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">\n"
180 " <link rel=\"stylesheet\" type=\"text/css\" href=\"" CGI_PREFIX "send-stylesheet\">\n"
183 "<h1>Privoxy shutdown request received</h1>\n"
184 "<p>Privoxy is going to shut down after the next request.</p>\n"
195 csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
197 rsp->content_length = 0;
198 rsp->head_length = 0;
201 rsp->body = strdup_or_die(body);
202 rsp->status = strdup_or_die(status);
206 #endif /* def FEATURE_GRACEFUL_TERMINATION */
209 /*********************************************************************
211 * Function : cgi_show_request
213 * Description : Show the client's request and what sed() would have
217 * 1 : csp = Current client state (buffers, headers, etc...)
218 * 2 : rsp = http_response data structure for output
219 * 3 : parameters = map of cgi parameters
221 * CGI Parameters : none
223 * Returns : JB_ERR_OK on success
224 * JB_ERR_MEMORY on out-of-memory error.
226 *********************************************************************/
227 jb_err cgi_show_request(struct client_state *csp,
228 struct http_response *rsp,
229 const struct map *parameters)
238 if (NULL == (exports = default_exports(csp, "show-request")))
240 return JB_ERR_MEMORY;
244 * Repair the damage done to the IOB by get_header()
246 for (p = csp->client_iob->buf; p < csp->client_iob->cur; p++)
248 if (*p == '\0') *p = '\n';
252 * Export the original client's request and the one we would
253 * be sending to the server if this wasn't a CGI call
256 if (map(exports, "client-request", 1, html_encode(csp->client_iob->buf), 0))
259 return JB_ERR_MEMORY;
262 if (map(exports, "processed-request", 1,
263 html_encode_and_free_original(list_to_text(csp->headers)), 0))
266 return JB_ERR_MEMORY;
269 return template_fill_for_cgi(csp, "show-request", exports, rsp);
273 #ifdef FEATURE_CLIENT_TAGS
274 /*********************************************************************
276 * Function : cgi_create_client_tag_form
278 * Description : Creates a HTML form to enable or disable a given
280 * XXX: Could use a template.
283 * 1 : form = Buffer to fill with the generated form
284 * 2 : size = Size of the form buffer
285 * 3 : tag = Name of the tag this form should affect
286 * 4 : toggle_state = Desired state after the button pressed 0
287 * 5 : expires = Whether or not the tag should be enabled.
288 * Only checked if toggle_state is 1.
292 *********************************************************************/
293 static void cgi_create_client_tag_form(char *form, size_t size,
294 const char *tag, int toggle_state, int expires)
298 if (toggle_state == 1)
300 button_name = (expires == 1) ? "Enable" : "Enable temporarily";
304 assert(toggle_state == 0);
305 button_name = "Disable";
309 "<form method=\"GET\" action=\""CGI_PREFIX"toggle-client-tag\" style=\"display: inline\">\n"
310 " <input type=\"hidden\" name=\"tag\" value=\"%s\">\n"
311 " <input type=\"hidden\" name=\"toggle-state\" value=\"%i\">\n"
312 " <input type=\"hidden\" name=\"expires\" value=\"%u\">\n"
313 " <input type=\"submit\" value=\"%s\">\n"
314 "</form>", tag, toggle_state, !expires, button_name);
317 /*********************************************************************
319 * Function : cgi_show_client_tags
321 * Description : Shows the tags that can be set based on the client
325 * 1 : csp = Current client state (buffers, headers, etc...)
326 * 2 : rsp = http_response data structure for output
327 * 3 : parameters = map of cgi parameters
329 * CGI Parameters : none
331 * Returns : JB_ERR_OK on success
332 * JB_ERR_MEMORY on out-of-memory error.
334 *********************************************************************/
335 jb_err cgi_show_client_tags(struct client_state *csp,
336 struct http_response *rsp,
337 const struct map *parameters)
340 struct client_tag_spec *this_tag;
341 jb_err err = JB_ERR_OK;
342 char *client_tag_status;
344 time_t refresh_delay;
350 if (NULL == (exports = default_exports(csp, "client-tags")))
352 return JB_ERR_MEMORY;
354 assert(csp->client_address != NULL);
356 this_tag = csp->config->client_tags;
357 if (this_tag->name == NULL)
359 client_tag_status = strdup_or_die("<p>No tags available.</p>\n");
363 client_tag_status = strdup_or_die("<table border=\"1\">\n"
364 "<tr><th>Tag name</th>\n"
365 "<th>Current state</th><th>Change state</th><th>Description</th></tr>\n");
366 while ((this_tag != NULL) && (this_tag->name != NULL))
370 privoxy_mutex_lock(&client_tags_mutex);
371 tag_state = client_has_requested_tag(csp->client_address, this_tag->name);
372 privoxy_mutex_unlock(&client_tags_mutex);
373 if (!err) err = string_append(&client_tag_status, "<tr><td>");
374 if (!err) err = string_append(&client_tag_status, this_tag->name);
375 if (!err) err = string_append(&client_tag_status, "</td><td>");
376 if (!err) err = string_append(&client_tag_status, tag_state == 1 ? "Enabled" : "Disabled");
377 if (!err) err = string_append(&client_tag_status, "</td><td>");
378 cgi_create_client_tag_form(buf, sizeof(buf), this_tag->name, !tag_state, 1);
379 if (!err) err = string_append(&client_tag_status, buf);
382 cgi_create_client_tag_form(buf, sizeof(buf), this_tag->name, !tag_state, 0);
383 if (!err) err = string_append(&client_tag_status, buf);
385 if (!err) err = string_append(&client_tag_status, "</td><td>");
386 if (!err) err = string_append(&client_tag_status, this_tag->description);
387 if (!err) err = string_append(&client_tag_status, "</td></tr>\n");
392 this_tag = this_tag->next;
394 if (!err) err = string_append(&client_tag_status, "</table>\n");
398 return JB_ERR_MEMORY;
401 refresh_delay = get_next_tag_timeout_for_client(csp->client_address);
402 if (refresh_delay != 0)
404 snprintf(buf, sizeof(buf), "%u", csp->config->client_tag_lifetime);
405 if (map(exports, "refresh-delay", 1, buf, 1))
407 freez(client_tag_status);
409 return JB_ERR_MEMORY;
414 err = map_block_killer(exports, "tags-expire");
415 if (err != JB_ERR_OK)
417 freez(client_tag_status);
422 if (map(exports, "client-tags", 1, client_tag_status, 0))
425 return JB_ERR_MEMORY;
428 if (map(exports, "client-ip-addr", 1, csp->client_address, 1))
431 return JB_ERR_MEMORY;
434 return template_fill_for_cgi(csp, "client-tags", exports, rsp);
438 /*********************************************************************
440 * Function : cgi_toggle_client_tag
442 * Description : Toggles a client tag and redirects to the show-tags
446 * 1 : csp = Current client state (buffers, headers, etc...)
447 * 2 : rsp = http_response data structure for output
448 * 3 : parameters = map of cgi parameters
450 * CGI Parameters : none
451 * 1 : tag = Name of the tag to enable or disable
452 * 2 : toggle-state = How to toggle the tag (0/1)
453 * 3 : expires = Set to 1 if the tag should be enabled
454 * temporarily, otherwise set to 0
456 * Returns : JB_ERR_OK on success
457 * JB_ERR_MEMORY on out-of-memory error.
459 *********************************************************************/
460 jb_err cgi_toggle_client_tag(struct client_state *csp,
461 struct http_response *rsp,
462 const struct map *parameters)
464 const char *toggled_tag;
465 const char *toggle_state;
466 const char *tag_expires;
473 toggled_tag = lookup(parameters, "tag");
474 if (*toggled_tag == '\0')
476 log_error(LOG_LEVEL_ERROR, "Received tag toggle request without tag");
480 tag_expires = lookup(parameters, "expires");
481 if (*tag_expires == '0')
487 time_to_live = csp->config->client_tag_lifetime;
489 toggle_state = lookup(parameters, "toggle-state");
490 if (*toggle_state == '1')
492 enable_client_specific_tag(csp, toggled_tag, time_to_live);
496 disable_client_specific_tag(csp, toggled_tag);
499 rsp->status = strdup_or_die("302 Done dealing with toggle request");
500 if (enlist_unique_header(rsp->headers,
501 "Location", CGI_PREFIX "client-tags"))
503 return JB_ERR_MEMORY;
508 #endif /* def FEATURE_CLIENT_TAGS */
511 /*********************************************************************
513 * Function : cgi_send_banner
515 * Description : CGI function that returns a banner.
518 * 1 : csp = Current client state (buffers, headers, etc...)
519 * 2 : rsp = http_response data structure for output
520 * 3 : parameters = map of cgi parameters
523 * type : Selects the type of banner between "trans", "logo",
524 * and "auto". Defaults to "logo" if absent or invalid.
525 * "auto" means to select as if we were image-blocking.
526 * (Only the first character really counts; b and t are
529 * Returns : JB_ERR_OK on success
530 * JB_ERR_MEMORY on out-of-memory error.
532 *********************************************************************/
533 jb_err cgi_send_banner(struct client_state *csp,
534 struct http_response *rsp,
535 const struct map *parameters)
537 char imagetype = lookup(parameters, "type")[0];
540 * If type is auto, then determine the right thing
541 * to do from the set-image-blocker action
543 if (imagetype == 'a')
550 #ifdef FEATURE_IMAGE_BLOCKING
551 if ((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
553 static const char prefix1[] = CGI_PREFIX "send-banner?type=";
554 static const char prefix2[] = "http://" CGI_SITE_1_HOST "/send-banner?type=";
555 const char *p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
559 /* Use default - nothing to do here. */
561 else if (0 == strcmpic(p, "blank"))
565 else if (0 == strcmpic(p, "pattern"))
571 * If the action is to call this CGI, determine
574 else if (0 == strncmpic(p, prefix1, sizeof(prefix1) - 1))
576 imagetype = p[sizeof(prefix1) - 1];
578 else if (0 == strncmpic(p, prefix2, sizeof(prefix2) - 1))
580 imagetype = p[sizeof(prefix2) - 1];
584 * Everything else must (should) be a URL to
592 #endif /* def FEATURE_IMAGE_BLOCKING */
596 * Now imagetype is either the non-auto type we were called with,
597 * or it was auto and has since been determined. In any case, we
598 * can proceed to actually answering the request by sending a redirect
599 * or an image as appropriate:
601 if (imagetype == 'r')
603 rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
604 if (enlist_unique_header(rsp->headers, "Location",
605 csp->action->string[ACTION_STRING_IMAGE_BLOCKER]))
607 return JB_ERR_MEMORY;
612 if ((imagetype == 'b') || (imagetype == 't'))
614 rsp->body = bindup(image_blank_data, image_blank_length);
615 rsp->content_length = image_blank_length;
619 rsp->body = bindup(image_pattern_data, image_pattern_length);
620 rsp->content_length = image_pattern_length;
623 if (rsp->body == NULL)
625 return JB_ERR_MEMORY;
627 if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
629 return JB_ERR_MEMORY;
640 /*********************************************************************
642 * Function : cgi_transparent_image
644 * Description : CGI function that sends a 1x1 transparent image.
647 * 1 : csp = Current client state (buffers, headers, etc...)
648 * 2 : rsp = http_response data structure for output
649 * 3 : parameters = map of cgi parameters
651 * CGI Parameters : None
653 * Returns : JB_ERR_OK on success
654 * JB_ERR_MEMORY on out-of-memory error.
656 *********************************************************************/
657 jb_err cgi_transparent_image(struct client_state *csp,
658 struct http_response *rsp,
659 const struct map *parameters)
664 rsp->body = bindup(image_blank_data, image_blank_length);
665 rsp->content_length = image_blank_length;
667 if (rsp->body == NULL)
669 return JB_ERR_MEMORY;
672 if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
674 return JB_ERR_MEMORY;
684 /*********************************************************************
686 * Function : cgi_send_default_favicon
688 * Description : CGI function that sends the standard favicon.
691 * 1 : csp = Current client state (buffers, headers, etc...)
692 * 2 : rsp = http_response data structure for output
693 * 3 : parameters = map of cgi parameters
695 * CGI Parameters : None
697 * Returns : JB_ERR_OK on success
698 * JB_ERR_MEMORY on out-of-memory error.
700 *********************************************************************/
701 jb_err cgi_send_default_favicon(struct client_state *csp,
702 struct http_response *rsp,
703 const struct map *parameters)
705 static const char default_favicon_data[] =
706 "\000\000\001\000\001\000\020\020\002\000\000\000\000\000\260"
707 "\000\000\000\026\000\000\000\050\000\000\000\020\000\000\000"
708 "\040\000\000\000\001\000\001\000\000\000\000\000\100\000\000"
709 "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000"
710 "\000\000\377\377\377\000\377\000\052\000\017\360\000\000\077"
711 "\374\000\000\161\376\000\000\161\376\000\000\361\377\000\000"
712 "\361\377\000\000\360\017\000\000\360\007\000\000\361\307\000"
713 "\000\361\307\000\000\361\307\000\000\360\007\000\000\160\036"
714 "\000\000\177\376\000\000\077\374\000\000\017\360\000\000\360"
715 "\017\000\000\300\003\000\000\200\001\000\000\200\001\000\000"
716 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
717 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
718 "\000\000\200\001\000\000\200\001\000\000\300\003\000\000\360"
720 static const size_t favicon_length = sizeof(default_favicon_data) - 1;
725 rsp->body = bindup(default_favicon_data, favicon_length);
726 rsp->content_length = favicon_length;
728 if (rsp->body == NULL)
730 return JB_ERR_MEMORY;
733 if (enlist(rsp->headers, "Content-Type: image/x-icon"))
735 return JB_ERR_MEMORY;
745 /*********************************************************************
747 * Function : cgi_send_error_favicon
749 * Description : CGI function that sends the favicon for error pages.
752 * 1 : csp = Current client state (buffers, headers, etc...)
753 * 2 : rsp = http_response data structure for output
754 * 3 : parameters = map of cgi parameters
756 * CGI Parameters : None
758 * Returns : JB_ERR_OK on success
759 * JB_ERR_MEMORY on out-of-memory error.
761 *********************************************************************/
762 jb_err cgi_send_error_favicon(struct client_state *csp,
763 struct http_response *rsp,
764 const struct map *parameters)
766 static const char error_favicon_data[] =
767 "\000\000\001\000\001\000\020\020\002\000\000\000\000\000\260"
768 "\000\000\000\026\000\000\000\050\000\000\000\020\000\000\000"
769 "\040\000\000\000\001\000\001\000\000\000\000\000\100\000\000"
770 "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000"
771 "\000\000\377\377\377\000\000\000\377\000\017\360\000\000\077"
772 "\374\000\000\161\376\000\000\161\376\000\000\361\377\000\000"
773 "\361\377\000\000\360\017\000\000\360\007\000\000\361\307\000"
774 "\000\361\307\000\000\361\307\000\000\360\007\000\000\160\036"
775 "\000\000\177\376\000\000\077\374\000\000\017\360\000\000\360"
776 "\017\000\000\300\003\000\000\200\001\000\000\200\001\000\000"
777 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
778 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
779 "\000\000\200\001\000\000\200\001\000\000\300\003\000\000\360"
781 static const size_t favicon_length = sizeof(error_favicon_data) - 1;
786 rsp->body = bindup(error_favicon_data, favicon_length);
787 rsp->content_length = favicon_length;
789 if (rsp->body == NULL)
791 return JB_ERR_MEMORY;
794 if (enlist(rsp->headers, "Content-Type: image/x-icon"))
796 return JB_ERR_MEMORY;
806 /*********************************************************************
808 * Function : cgi_send_stylesheet
810 * Description : CGI function that sends a css stylesheet found
811 * in the cgi-style.css template
814 * 1 : csp = Current client state (buffers, headers, etc...)
815 * 2 : rsp = http_response data structure for output
816 * 3 : parameters = map of cgi parameters
818 * CGI Parameters : None
820 * Returns : JB_ERR_OK on success
821 * JB_ERR_MEMORY on out-of-memory error.
823 *********************************************************************/
824 jb_err cgi_send_stylesheet(struct client_state *csp,
825 struct http_response *rsp,
826 const struct map *parameters)
835 err = template_load(csp, &rsp->body, "cgi-style.css", 0);
837 if (err == JB_ERR_FILE)
840 * No way to tell user; send empty stylesheet
842 log_error(LOG_LEVEL_ERROR, "Could not find cgi-style.css template");
846 return err; /* JB_ERR_MEMORY */
849 if (enlist(rsp->headers, "Content-Type: text/css"))
851 return JB_ERR_MEMORY;
859 /*********************************************************************
861 * Function : cgi_send_url_info_osd
863 * Description : CGI function that sends the OpenSearch Description
864 * template for the show-url-info page. It allows to
865 * access the page through "search engine plugins".
868 * 1 : csp = Current client state (buffers, headers, etc...)
869 * 2 : rsp = http_response data structure for output
870 * 3 : parameters = map of cgi parameters
872 * CGI Parameters : None
874 * Returns : JB_ERR_OK on success
875 * JB_ERR_MEMORY on out-of-memory error.
877 *********************************************************************/
878 jb_err cgi_send_url_info_osd(struct client_state *csp,
879 struct http_response *rsp,
880 const struct map *parameters)
882 jb_err err = JB_ERR_MEMORY;
883 struct map *exports = default_exports(csp, NULL);
890 err = template_fill_for_cgi(csp, "url-info-osd.xml", exports, rsp);
891 if (JB_ERR_OK == err)
893 err = enlist(rsp->headers,
894 "Content-Type: application/opensearchdescription+xml");
903 /*********************************************************************
905 * Function : get_content_type
907 * Description : Use the file extension to guess the content type
908 * header we should use to serve the file.
911 * 1 : filename = Name of the file whose content type
914 * Returns : The guessed content type.
916 *********************************************************************/
917 static const char *get_content_type(const char *filename)
922 const char extension[6];
923 const char content_type[11];
925 static const struct content_type content_types[] =
927 {".css", "text/css"},
928 {".jpg", "image/jpeg"},
929 {".jpeg", "image/jpeg"},
930 {".png", "image/png"},
933 for (i = 0; i < SZ(content_types); i++)
935 if (strstr(filename, content_types[i].extension))
937 return content_types[i].content_type;
941 /* No match by extension, default to html */
945 /*********************************************************************
947 * Function : cgi_send_user_manual
949 * Description : CGI function that sends a file in the user
953 * 1 : csp = Current client state (buffers, headers, etc...)
954 * 2 : rsp = http_response data structure for output
955 * 3 : parameters = map of cgi parameters
957 * CGI Parameters : file=name.html, the name of the HTML file
958 * (relative to user-manual from config)
960 * Returns : JB_ERR_OK on success
961 * JB_ERR_MEMORY on out-of-memory error.
963 *********************************************************************/
964 jb_err cgi_send_user_manual(struct client_state *csp,
965 struct http_response *rsp,
966 const struct map *parameters)
968 const char *filename;
970 jb_err err = JB_ERR_OK;
971 const char *content_type;
977 if (0 == strncmpic(csp->config->usermanual, "http://", 7))
979 log_error(LOG_LEVEL_CGI, "Request for local user-manual "
980 "received while user-manual delivery is disabled.");
981 return cgi_error_404(csp, rsp, parameters);
984 if (!parameters->first)
986 /* requested http://p.p/user-manual (without trailing slash) */
987 return cgi_redirect(rsp, CGI_PREFIX "user-manual/");
990 get_string_param(parameters, "file", &filename);
991 if (filename == NULL)
993 /* It's '/' so serve the index.html if there is one. */
994 filename = "index.html";
996 else if (NULL != strchr(filename, '/') || NULL != strstr(filename, ".."))
999 * We currently only support a flat file
1000 * hierarchy for the documentation.
1002 log_error(LOG_LEVEL_ERROR,
1003 "Rejecting the request to serve '%s' as it contains '/' or '..'",
1005 return JB_ERR_CGI_PARAMS;
1008 full_path = make_path(csp->config->usermanual, filename);
1009 if (full_path == NULL)
1011 return JB_ERR_MEMORY;
1014 err = load_file(full_path, &rsp->body, &rsp->content_length);
1015 if (JB_ERR_OK != err)
1017 assert((JB_ERR_FILE == err) || (JB_ERR_MEMORY == err));
1018 if (JB_ERR_FILE == err)
1020 err = cgi_error_no_template(csp, rsp, full_path);
1027 content_type = get_content_type(filename);
1028 log_error(LOG_LEVEL_CGI,
1029 "Content-Type guessed for %s: %s", filename, content_type);
1031 return enlist_unique_header(rsp->headers, "Content-Type", content_type);
1036 #ifdef FEATURE_EXTENDED_STATISTICS
1037 /*********************************************************************
1039 * Function : get_block_reason_statistics_table
1041 * Description : Produces the block reason statistic table content.
1044 * 1 : csp = Current client state (buffers, headers, etc...)
1046 * Returns : Pointer to the HTML statistic table content or
1047 * NULL on out of memory
1049 *********************************************************************/
1050 static char *get_block_reason_statistics_table(const struct client_state *csp)
1052 char buf[BUFFER_SIZE];
1055 struct file_list *fl;
1056 jb_err err = JB_ERR_OK;
1058 statistics = strdup_or_die("");
1060 /* Run through all action files. */
1061 for (i = 0; i < MAX_AF_FILES; i++)
1063 struct url_actions *b;
1064 struct action_spec *last_action = NULL;
1066 if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
1068 /* Skip empty files */
1072 /* Go through all the actions. */
1073 for (b = b->next; NULL != b; b = b->next)
1075 if (last_action == b->action)
1079 if ((b->action->add & ACTION_BLOCK))
1081 unsigned long long count;
1082 const char *block_reason = b->action->string[ACTION_STRING_BLOCK];
1083 const char *encoded_block_reason = html_encode(block_reason);
1085 if (encoded_block_reason == NULL)
1090 get_block_reason_count(block_reason, &count);
1091 snprintf(buf, sizeof(buf),
1092 "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>\n",
1093 encoded_block_reason, count);
1094 freez(encoded_block_reason);
1096 if (!err) err = string_append(&statistics, buf);
1098 last_action = b->action;
1107 /*********************************************************************
1109 * Function : get_filter_statistics_table
1111 * Description : Produces the filter statistic table content.
1114 * 1 : csp = Current client state (buffers, headers, etc...)
1116 * Returns : Pointer to the HTML statistic table content or
1117 * NULL on out of memory
1119 *********************************************************************/
1120 static char *get_filter_statistics_table(const struct client_state *csp)
1122 char buf[BUFFER_SIZE];
1125 struct file_list *fl;
1126 struct re_filterfile_spec *b;
1127 jb_err err = JB_ERR_OK;
1129 statistics = strdup_or_die("");
1131 for (i = 0; i < MAX_AF_FILES; i++)
1134 if ((NULL == fl) || (NULL == fl->f))
1137 * Either there are no filter files left or this
1138 * filter file just contains no valid filters.
1140 * Continue to be sure we don't miss valid filter
1141 * files that are chained after empty or invalid ones.
1146 for (b = fl->f; b != NULL; b = b->next)
1148 if (b->type == FT_CONTENT_FILTER)
1150 unsigned long long executions;
1151 unsigned long long response_bodies_modified;
1152 unsigned long long hits;
1154 get_filter_statistics(b->name, &executions, &response_bodies_modified, &hits);
1155 snprintf(buf, sizeof(buf),
1156 "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>"
1157 "<td style=\"text-align: right\">%llu</td>"
1158 "<td style=\"text-align: right\">%llu</td><tr>\n",
1159 b->name, executions, response_bodies_modified, hits);
1161 if (!err) err = string_append(&statistics, buf);
1169 #endif /* def FEATURE_EXTENDED_STATISTICS */
1172 /*********************************************************************
1174 * Function : cgi_show_status
1176 * Description : CGI function that returns a web page describing the
1177 * current status of Privoxy.
1180 * 1 : csp = Current client state (buffers, headers, etc...)
1181 * 2 : rsp = http_response data structure for output
1182 * 3 : parameters = map of cgi parameters
1185 * file : Which file to show. Only first letter is checked,
1190 * Default is to show menu and other information.
1192 * Returns : JB_ERR_OK on success
1193 * JB_ERR_MEMORY on out-of-memory error.
1195 *********************************************************************/
1196 jb_err cgi_show_status(struct client_state *csp,
1197 struct http_response *rsp,
1198 const struct map *parameters)
1204 char buf[BUFFER_SIZE];
1205 #ifdef FEATURE_STATISTICS
1206 float perc_rej; /* Percentage of http requests rejected */
1207 int local_urls_read;
1208 int local_urls_rejected;
1209 #endif /* ndef FEATURE_STATISTICS */
1210 jb_err err = JB_ERR_OK;
1212 struct map *exports;
1218 if ('\0' != *(lookup(parameters, "file")))
1220 return cgi_show_file(csp, rsp, parameters);
1223 if (NULL == (exports = default_exports(csp, "show-status")))
1225 return JB_ERR_MEMORY;
1229 for (j = 0; (s != NULL) && (j < Argc); j++)
1231 if (!err) err = string_join (&s, html_encode(Argv[j]));
1232 if (!err) err = string_append(&s, " ");
1234 if (!err) err = map(exports, "invocation", 1, s, 0);
1236 if (!err) err = map(exports, "options", 1, csp->config->proxy_args, 1);
1237 if (!err) err = show_defines(exports);
1242 return JB_ERR_MEMORY;
1245 #ifdef FEATURE_STATISTICS
1246 local_urls_read = urls_read;
1247 local_urls_rejected = urls_rejected;
1250 * Need to alter the stats not to include the fetch of this
1253 * Can't do following thread safely! doh!
1256 * urls_rejected--; * This will be incremented subsequently *
1259 if (local_urls_read == 0)
1261 if (!err) err = map_block_killer(exports, "have-stats");
1265 if (!err) err = map_block_killer(exports, "have-no-stats");
1267 perc_rej = (float)local_urls_rejected * 100.0F /
1268 (float)local_urls_read;
1270 snprintf(buf, sizeof(buf), "%d", local_urls_read);
1271 if (!err) err = map(exports, "requests-received", 1, buf, 1);
1273 snprintf(buf, sizeof(buf), "%d", local_urls_rejected);
1274 if (!err) err = map(exports, "requests-blocked", 1, buf, 1);
1276 snprintf(buf, sizeof(buf), "%6.2f", perc_rej);
1277 if (!err) err = map(exports, "percent-blocked", 1, buf, 1);
1280 #else /* ndef FEATURE_STATISTICS */
1281 if (!err) err = map_block_killer(exports, "statistics");
1282 #endif /* ndef FEATURE_STATISTICS */
1284 #ifdef FEATURE_EXTENDED_STATISTICS
1287 char *block_reason_statistics = get_block_reason_statistics_table(csp);
1288 if (block_reason_statistics != NULL)
1290 err = map(exports, "block-reason-statistics", 1, block_reason_statistics, 0);
1294 err = map_block_killer(exports, "extended-statistics");
1299 char *filter_statistics = get_filter_statistics_table(csp);
1300 if (filter_statistics != NULL)
1302 err = map(exports, "filter-statistics", 1, filter_statistics, 0);
1306 err = map_block_killer(exports, "extended-statistics");
1309 #else /* ndef FEATURE_EXTENDED_STATISTICS */
1310 if (!err) err = map_block_killer(exports, "extended-statistics");
1311 #endif /* def FEATURE_EXTENDED_STATISTICS */
1314 * List all action files in use, together with view and edit links,
1315 * except for standard.action, which should only be viewable. (Not
1316 * enforced in the editor itself)
1317 * FIXME: Shouldn't include hardwired HTML here, use line template instead!
1320 for (i = 0; i < MAX_AF_FILES; i++)
1322 if (csp->actions_list[i] != NULL)
1324 if (!err) err = string_append(&s, "<tr><td>");
1325 if (!err) err = string_join(&s, html_encode(csp->actions_list[i]->filename));
1326 snprintf(buf, sizeof(buf),
1327 "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&index=%u\">View</a>", i);
1328 if (!err) err = string_append(&s, buf);
1330 #ifdef FEATURE_CGI_EDIT_ACTIONS
1331 if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
1332 && (NULL != csp->config->actions_file_short[i]))
1335 if (access(csp->config->actions_file[i], W_OK) == 0)
1337 #endif /* def HAVE_ACCESS */
1338 snprintf(buf, sizeof(buf), " <a href=\"/edit-actions-list?f=%u\">Edit</a>", i);
1339 if (!err) err = string_append(&s, buf);
1344 if (!err) err = string_append(&s, " <strong>No write access.</strong>");
1346 #endif /* def HAVE_ACCESS */
1350 if (!err) err = string_append(&s, "</td></tr>\n");
1353 if (!err && *s != '\0')
1355 err = map(exports, "actions-filenames", 1, s, 0);
1359 if (!err) err = map(exports, "actions-filenames", 1, "<tr><td>None specified</td></tr>", 1);
1364 * List all re_filterfiles in use, together with view options.
1365 * FIXME: Shouldn't include hardwired HTML here, use line template instead!
1368 for (i = 0; i < MAX_AF_FILES; i++)
1370 if (csp->rlist[i] != NULL)
1372 if (!err) err = string_append(&s, "<tr><td>");
1373 if (!err) err = string_join(&s, html_encode(csp->rlist[i]->filename));
1374 snprintf(buf, sizeof(buf),
1375 "</td><td class=\"buttons\"><a href=\"/show-status?file=filter&index=%u\">View</a>", i);
1376 if (!err) err = string_append(&s, buf);
1377 if (!err) err = string_append(&s, "</td></tr>\n");
1380 if (!err && *s != '\0')
1382 err = map(exports, "re-filter-filenames", 1, s, 0);
1386 if (!err) err = map(exports, "re-filter-filenames", 1, "<tr><td>None specified</td></tr>", 1);
1387 if (!err) err = map_block_killer(exports, "have-filterfile");
1391 #ifdef FEATURE_TRUST
1394 if (!err) err = map(exports, "trust-filename", 1, html_encode(csp->tlist->filename), 0);
1398 if (!err) err = map(exports, "trust-filename", 1, "None specified", 1);
1399 if (!err) err = map_block_killer(exports, "have-trustfile");
1402 if (!err) err = map_block_killer(exports, "trust-support");
1403 #endif /* ndef FEATURE_TRUST */
1405 #ifdef FEATURE_CGI_EDIT_ACTIONS
1406 if (!err && (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
1408 err = map_block_killer(exports, "cgi-editor-is-disabled");
1410 #endif /* ndef CGI_EDIT_ACTIONS */
1412 if (!err) err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
1417 return JB_ERR_MEMORY;
1420 return template_fill_for_cgi(csp, "show-status", exports, rsp);
1424 /*********************************************************************
1426 * Function : cgi_show_url_info
1428 * Description : CGI function that determines and shows which actions
1429 * Privoxy will perform for a given url, and which
1430 * matches starting from the defaults have lead to that.
1433 * 1 : csp = Current client state (buffers, headers, etc...)
1434 * 2 : rsp = http_response data structure for output
1435 * 3 : parameters = map of cgi parameters
1438 * url : The url whose actions are to be determined.
1439 * If url is unset, the url-given conditional will be
1440 * set, so that all but the form can be suppressed in
1443 * Returns : JB_ERR_OK on success
1444 * JB_ERR_MEMORY on out-of-memory error.
1446 *********************************************************************/
1447 jb_err cgi_show_url_info(struct client_state *csp,
1448 struct http_response *rsp,
1449 const struct map *parameters)
1452 struct map *exports;
1459 if (NULL == (exports = default_exports(csp, "show-url-info")))
1461 return JB_ERR_MEMORY;
1465 * Get the url= parameter (if present) and remove any leading/trailing spaces.
1467 url_param = strdup_or_die(lookup(parameters, "url"));
1471 * Handle prefixes. 4 possibilities:
1472 * 1) "http://" or "https://" prefix present and followed by URL - OK
1473 * 2) Only the "http://" or "https://" part is present, no URL - change
1474 * to empty string so it will be detected later as "no URL".
1475 * 3) Parameter specified but doesn't start with "http(s?)://" - add a
1477 * 4) Parameter not specified or is empty string - let this fall through
1478 * for now, next block of code will handle it.
1480 if (0 == strncmp(url_param, "http://", 7))
1482 if (url_param[7] == '\0')
1485 * Empty URL (just prefix).
1486 * Make it totally empty so it's caught by the next if ()
1488 url_param[0] = '\0';
1491 else if (0 == strncmp(url_param, "https://", 8))
1493 if (url_param[8] == '\0')
1496 * Empty URL (just prefix).
1497 * Make it totally empty so it's caught by the next if ()
1499 url_param[0] = '\0';
1502 else if ((url_param[0] != '\0')
1503 && ((NULL == strstr(url_param, "://")
1504 || (strstr(url_param, "://") > strstr(url_param, "/")))))
1507 * No prefix or at least no prefix before
1508 * the first slash - assume http://
1510 char *url_param_prefixed = strdup_or_die("http://");
1512 if (JB_ERR_OK != string_join(&url_param_prefixed, url_param))
1515 return JB_ERR_MEMORY;
1517 url_param = url_param_prefixed;
1521 * Hide "toggle off" warning if Privoxy is toggled on.
1524 #ifdef FEATURE_TOGGLE
1525 (global_toggle_state == 1) &&
1526 #endif /* def FEATURE_TOGGLE */
1527 map_block_killer(exports, "privoxy-is-toggled-off")
1532 return JB_ERR_MEMORY;
1535 if (url_param[0] == '\0')
1537 /* URL parameter not specified, display query form only. */
1539 if (map_block_killer(exports, "url-given")
1540 || map(exports, "url", 1, "", 1))
1543 return JB_ERR_MEMORY;
1548 /* Given a URL, so query it. */
1553 struct file_list *fl;
1554 struct url_actions *b;
1555 struct http_request url_to_query[1];
1556 struct current_action_spec action[1];
1559 if (map(exports, "url", 1, html_encode(url_param), 0))
1563 return JB_ERR_MEMORY;
1566 init_current_action(action);
1568 if (map(exports, "default", 1, current_action_to_html(csp, action), 0))
1570 free_current_action(action);
1573 return JB_ERR_MEMORY;
1576 memset(url_to_query, '\0', sizeof(url_to_query));
1577 err = parse_http_url(url_param, url_to_query, REQUIRE_PROTOCOL);
1578 assert((err != JB_ERR_OK) || (url_to_query->ssl == !strncmpic(url_param, "https://", 8)));
1582 if (err == JB_ERR_MEMORY)
1584 free_http_request(url_to_query);
1585 free_current_action(action);
1587 return JB_ERR_MEMORY;
1593 err = map(exports, "matches", 1, "<b>[Invalid URL specified!]</b>" , 1);
1594 if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1);
1595 if (!err) err = map_block_killer(exports, "valid-url");
1597 free_current_action(action);
1598 free_http_request(url_to_query);
1603 return JB_ERR_MEMORY;
1606 return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1610 * We have a warning about SSL paths. Hide it for unencrypted sites
1611 * and unconditionally if https inspection is enabled.
1613 #ifndef FEATURE_HTTPS_INSPECTION
1614 if (!url_to_query->ssl)
1617 if (map_block_killer(exports, "https-and-no-https-inspection"))
1619 free_current_action(action);
1621 free_http_request(url_to_query);
1622 return JB_ERR_MEMORY;
1626 matches = strdup_or_die("<table summary=\"\" class=\"transparent\">");
1628 for (i = 0; i < MAX_AF_FILES; i++)
1630 if (NULL == csp->config->actions_file_short[i]
1631 || !strcmp(csp->config->actions_file_short[i], "standard.action")) continue;
1635 if ((fl = csp->actions_list[i]) != NULL)
1637 if ((b = fl->f) != NULL)
1639 /* FIXME: Hardcoded HTML! */
1640 string_append(&matches, "<tr><th>In file: ");
1641 string_join (&matches, html_encode(csp->config->actions_file_short[i]));
1642 snprintf(buf, sizeof(buf), " <a class=\"cmd\" href=\"/show-status?file=actions&index=%d\">", i);
1643 string_append(&matches, buf);
1644 string_append(&matches, "View</a>");
1645 #ifdef FEATURE_CGI_EDIT_ACTIONS
1646 if (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
1649 if (access(csp->config->actions_file[i], W_OK) == 0)
1651 #endif /* def HAVE_ACCESS */
1652 snprintf(buf, sizeof(buf),
1653 " <a class=\"cmd\" href=\"/edit-actions-list?f=%d\">", i);
1654 string_append(&matches, buf);
1655 string_append(&matches, "Edit</a>");
1660 string_append(&matches, " <strong>No write access.</strong>");
1662 #endif /* def HAVE_ACCESS */
1664 #endif /* FEATURE_CGI_EDIT_ACTIONS */
1666 string_append(&matches, "</th></tr>\n");
1673 for (; (b != NULL) && (matches != NULL); b = b->next)
1675 if (url_match(b->url, url_to_query))
1677 string_append(&matches, "<tr><td>{");
1678 string_join (&matches, actions_to_html(csp, b->action));
1679 string_append(&matches, " }<br>\n<code>");
1680 string_join (&matches, html_encode(b->url->spec));
1681 string_append(&matches, "</code></td></tr>\n");
1683 if (merge_current_action(action, b->action))
1686 free_http_request(url_to_query);
1687 free_current_action(action);
1689 return JB_ERR_MEMORY;
1697 string_append(&matches, "<tr><td>(no matches in this file)</td></tr>\n");
1700 string_append(&matches, "</table>\n");
1703 * XXX: Kludge to make sure the "Forward settings" section
1704 * shows what forward-override{} would do with the requested URL.
1705 * No one really cares how the CGI request would be forwarded
1706 * if it wasn't intercepted as CGI request in the first place.
1708 * From here on the action bitmask will no longer reflect
1709 * the real url (http://config.privoxy.org/show-url-info?url=.*),
1710 * but luckily it's no longer required later on anyway.
1712 free_current_action(csp->action);
1713 get_url_actions(csp, url_to_query);
1716 * Fill in forwarding settings.
1718 * The possibilities are:
1720 * - http forwarding only
1721 * - socks4(a) forwarding only
1722 * - socks4(a) and http forwarding.
1724 * XXX: Parts of this code could be reused for the
1725 * "forwarding-failed" template which currently doesn't
1726 * display the proxy port and an eventual second forwarder.
1729 const struct forward_spec *fwd = forward_url(csp, url_to_query);
1731 if ((fwd->gateway_host == NULL) && (fwd->forward_host == NULL))
1733 if (!err) err = map_block_killer(exports, "socks-forwarder");
1734 if (!err) err = map_block_killer(exports, "http-forwarder");
1738 char port[10]; /* We save proxy ports as int but need a string here */
1740 if (!err) err = map_block_killer(exports, "no-forwarder");
1742 if (fwd->gateway_host != NULL)
1744 char *socks_type = NULL;
1749 socks_type = "socks4";
1752 socks_type = "socks4a";
1755 socks_type = "socks5";
1758 socks_type = "socks5t";
1761 log_error(LOG_LEVEL_FATAL, "Unknown socks type: %d.", fwd->type);
1764 if (!err) err = map(exports, "socks-type", 1, socks_type, 1);
1765 if (!err) err = map(exports, "gateway-host", 1, fwd->gateway_host, 1);
1766 snprintf(port, sizeof(port), "%d", fwd->gateway_port);
1767 if (!err) err = map(exports, "gateway-port", 1, port, 1);
1771 if (!err) err = map_block_killer(exports, "socks-forwarder");
1774 if (fwd->forward_host != NULL)
1776 if (!err) err = map(exports, "forward-host", 1, fwd->forward_host, 1);
1777 snprintf(port, sizeof(port), "%d", fwd->forward_port);
1778 if (!err) err = map(exports, "forward-port", 1, port, 1);
1782 if (!err) err = map_block_killer(exports, "http-forwarder");
1787 free_http_request(url_to_query);
1789 if (err || matches == NULL)
1791 free_current_action(action);
1793 return JB_ERR_MEMORY;
1796 #ifdef FEATURE_CGI_EDIT_ACTIONS
1797 if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
1799 err = map_block_killer(exports, "cgi-editor-is-disabled");
1801 #endif /* FEATURE_CGI_EDIT_ACTIONS */
1804 * If zlib support is available, if no content filters
1805 * are enabled or if the prevent-compression action is enabled,
1806 * suppress the "compression could prevent filtering" warning.
1808 #ifndef FEATURE_ZLIB
1809 if (!content_filters_enabled(action) ||
1810 (action->flags & ACTION_NO_COMPRESSION))
1813 if (!err) err = map_block_killer(exports, "filters-might-be-ineffective");
1816 if (err || map(exports, "matches", 1, matches , 0))
1818 free_current_action(action);
1820 return JB_ERR_MEMORY;
1823 s = current_action_to_html(csp, action);
1825 free_current_action(action);
1827 if (map(exports, "final", 1, s, 0))
1830 return JB_ERR_MEMORY;
1834 return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1838 /*********************************************************************
1840 * Function : cgi_robots_txt
1842 * Description : CGI function to return "/robots.txt".
1845 * 1 : csp = Current client state (buffers, headers, etc...)
1846 * 2 : rsp = http_response data structure for output
1847 * 3 : parameters = map of cgi parameters
1849 * CGI Parameters : None
1851 * Returns : JB_ERR_OK on success
1852 * JB_ERR_MEMORY on out-of-memory error.
1854 *********************************************************************/
1855 jb_err cgi_robots_txt(struct client_state *csp,
1856 struct http_response *rsp,
1857 const struct map *parameters)
1865 rsp->body = strdup_or_die(
1866 "# This is the Privoxy control interface.\n"
1867 "# It isn't very useful to index it, and you're likely to break stuff.\n"
1874 err = enlist_unique(rsp->headers, "Content-Type: text/plain", 13);
1878 get_http_time(7 * 24 * 60 * 60, buf, sizeof(buf)); /* 7 days into future */
1879 if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf);
1881 return (err ? JB_ERR_MEMORY : JB_ERR_OK);
1885 /*********************************************************************
1887 * Function : show_defines
1889 * Description : Add to a map the state of all conditional #defines
1890 * used when building
1893 * 1 : exports = map to extend
1895 * Returns : JB_ERR_OK on success
1896 * JB_ERR_MEMORY on out-of-memory error.
1898 *********************************************************************/
1899 static jb_err show_defines(struct map *exports)
1901 jb_err err = JB_ERR_OK;
1904 const char name[31];
1905 const unsigned char is_available;
1908 static const struct feature features[] = {
1910 "FEATURE_64_BIT_TIME_T",
1911 #if (SIZEOF_TIME_T == 8)
1918 "FEATURE_ACCEPT_FILTER",
1919 #ifdef FEATURE_ACCEPT_FILTER
1935 #ifdef FEATURE_BROTLI
1942 "FEATURE_CGI_EDIT_ACTIONS",
1943 #ifdef FEATURE_CGI_EDIT_ACTIONS
1950 "FEATURE_CLIENT_TAGS",
1951 #ifdef FEATURE_CLIENT_TAGS
1958 "FEATURE_COMPRESSION",
1959 #ifdef FEATURE_COMPRESSION
1966 "FEATURE_CONNECTION_KEEP_ALIVE",
1967 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
1974 "FEATURE_CONNECTION_SHARING",
1975 #ifdef FEATURE_CONNECTION_SHARING
1982 "FEATURE_EXTERNAL_FILTERS",
1983 #ifdef FEATURE_EXTERNAL_FILTERS
1990 "FEATURE_FAST_REDIRECTS",
1991 #ifdef FEATURE_FAST_REDIRECTS
1998 "FEATURE_FORCE_LOAD",
1999 #ifdef FEATURE_FORCE_LOAD
2006 "FEATURE_GRACEFUL_TERMINATION",
2007 #ifdef FEATURE_GRACEFUL_TERMINATION
2014 "FEATURE_HTTPS_INSPECTION",
2015 #ifdef FEATURE_HTTPS_INSPECTION
2022 "FEATURE_IMAGE_BLOCKING",
2023 #ifdef FEATURE_IMAGE_BLOCKING
2030 "FEATURE_IPV6_SUPPORT",
2039 #ifdef FEATURE_NO_GIFS
2047 #ifdef FEATURE_PTHREAD
2054 "FEATURE_STATISTICS",
2055 #ifdef FEATURE_STATISTICS
2062 "FEATURE_STRPTIME_SANITY_CHECKS",
2063 #ifdef FEATURE_STRPTIME_SANITY_CHECKS
2071 #ifdef FEATURE_TOGGLE
2079 #ifdef FEATURE_TRUST
2094 "FEATURE_DYNAMIC_PCRE",
2095 #ifdef FEATURE_DYNAMIC_PCRE
2102 "FEATURE_EXTENDED_STATISTICS",
2103 #ifdef FEATURE_EXTENDED_STATISTICS
2110 "FEATURE_PCRE_HOST_PATTERNS",
2111 #ifdef FEATURE_PCRE_HOST_PATTERNS
2119 for (i = 0; i < SZ(features); i++)
2121 err = map_conditional(exports, features[i].name, features[i].is_available);
2133 /*********************************************************************
2135 * Function : cgi_show_file
2137 * Description : CGI function that shows the content of a
2138 * configuration file.
2141 * 1 : csp = Current client state (buffers, headers, etc...)
2142 * 2 : rsp = http_response data structure for output
2143 * 3 : parameters = map of cgi parameters
2146 * file : Which file to show. Only first letter is checked,
2151 * Default is to show menu and other information.
2153 * Returns : JB_ERR_OK on success
2154 * JB_ERR_MEMORY on out-of-memory error.
2156 *********************************************************************/
2157 static jb_err cgi_show_file(struct client_state *csp,
2158 struct http_response *rsp,
2159 const struct map *parameters)
2162 const char * filename = NULL;
2163 char * file_description = NULL;
2169 switch (*(lookup(parameters, "file")))
2172 if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->actions_list[i])
2174 filename = csp->actions_list[i]->filename;
2175 file_description = "Actions File";
2180 if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->rlist[i])
2182 filename = csp->rlist[i]->filename;
2183 file_description = "Filter File";
2187 #ifdef FEATURE_TRUST
2191 filename = csp->tlist->filename;
2192 file_description = "Trust File";
2195 #endif /* def FEATURE_TRUST */
2198 if (NULL != filename)
2200 struct map *exports;
2205 exports = default_exports(csp, "show-status");
2206 if (NULL == exports)
2208 return JB_ERR_MEMORY;
2211 if (map(exports, "file-description", 1, file_description, 1)
2212 || map(exports, "filepath", 1, html_encode(filename), 0))
2215 return JB_ERR_MEMORY;
2218 err = load_file(filename, &s, &length);
2219 if (JB_ERR_OK != err)
2221 if (map(exports, "contents", 1, "<h1>ERROR OPENING FILE!</h1>", 1))
2224 return JB_ERR_MEMORY;
2229 s = html_encode_and_free_original(s);
2233 return JB_ERR_MEMORY;
2236 if (map(exports, "contents", 1, s, 0))
2239 return JB_ERR_MEMORY;
2243 return template_fill_for_cgi(csp, "show-status-file", exports, rsp);
2246 return JB_ERR_CGI_PARAMS;
2250 /*********************************************************************
2252 * Function : load_file
2254 * Description : Loads a file into a buffer.
2257 * 1 : filename = Name of the file to be loaded.
2258 * 2 : buffer = Used to return the file's content.
2259 * 3 : length = Used to return the size of the file.
2261 * Returns : JB_ERR_OK in case of success,
2262 * JB_ERR_FILE in case of ordinary file loading errors
2263 * (fseek() and ftell() errors are fatal)
2264 * JB_ERR_MEMORY in case of out-of-memory.
2266 *********************************************************************/
2267 static jb_err load_file(const char *filename, char **buffer, size_t *length)
2271 jb_err err = JB_ERR_OK;
2273 fp = fopen(filename, "rb");
2276 log_error(LOG_LEVEL_ERROR, "Failed to open %s: %E", filename);
2280 /* Get file length */
2281 if (fseek(fp, 0, SEEK_END))
2283 log_error(LOG_LEVEL_FATAL,
2284 "Unexpected error while fseek()ing to the end of %s: %E",
2290 log_error(LOG_LEVEL_FATAL,
2291 "Unexpected ftell() error while loading %s: %E",
2294 *length = (size_t)ret;
2296 /* Go back to the beginning. */
2297 if (fseek(fp, 0, SEEK_SET))
2299 log_error(LOG_LEVEL_FATAL,
2300 "Unexpected error while fseek()ing to the beginning of %s: %E",
2304 *buffer = zalloc_or_die(*length + 1);
2306 if (1 != fread(*buffer, *length, 1, fp))
2309 * May theoretically happen if the file size changes between
2310 * fseek() and fread() because it's edited in-place. Privoxy
2311 * and common text editors don't do that, thus we just fail.
2313 log_error(LOG_LEVEL_ERROR,
2314 "Couldn't completely read file %s.", filename);