Reorder gateway_host in struct forward_spec to save memory
[privoxy.git] / cgisimple.c
1 /*********************************************************************
2  *
3  * File        :  $Source: /cvsroot/ijbswa/current/cgisimple.c,v $
4  *
5  * Purpose     :  Simple CGIs to get information about Privoxy's
6  *                status.
7  *
8  * Copyright   :  Written by and Copyright (C) 2001-2020 the
9  *                Privoxy team. https://www.privoxy.org/
10  *
11  *                Based on the Internet Junkbuster originally written
12  *                by and Copyright (C) 1997 Anonymous Coders and
13  *                Junkbusters Corporation.  http://www.junkbusters.com
14  *
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.
20  *
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.
26  *
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.
32  *
33  **********************************************************************/
34
35
36 #include "config.h"
37
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <assert.h>
44
45 #if defined (HAVE_ACCESS) && defined (HAVE_UNISTD_H)
46 #include <unistd.h>
47 #endif /* def HAVE_ACCESS && HAVE_UNISTD_H */
48
49 #include "project.h"
50 #include "cgi.h"
51 #include "cgisimple.h"
52 #include "list.h"
53 #include "encode.h"
54 #include "jcc.h"
55 #include "filters.h"
56 #include "actions.h"
57 #include "miscutil.h"
58 #include "loadcfg.h"
59 #include "parsers.h"
60 #include "urlmatch.h"
61 #include "errlog.h"
62 #ifdef FEATURE_CLIENT_TAGS
63 #include "client-tags.h"
64 #endif
65
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);
71
72 /*********************************************************************
73  *
74  * Function    :  cgi_default
75  *
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.
79  *
80  * Parameters  :
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
84  *
85  * CGI Parameters : none
86  *
87  * Returns     :  JB_ERR_OK on success
88  *                JB_ERR_MEMORY on out-of-memory
89  *
90  *********************************************************************/
91 jb_err cgi_default(struct client_state *csp,
92                    struct http_response *rsp,
93                    const struct map *parameters)
94 {
95    struct map *exports;
96
97    (void)parameters;
98
99    assert(csp);
100    assert(rsp);
101
102    if (NULL == (exports = default_exports(csp, "")))
103    {
104       return JB_ERR_MEMORY;
105    }
106
107    return template_fill_for_cgi(csp, "default", exports, rsp);
108 }
109
110
111 /*********************************************************************
112  *
113  * Function    :  cgi_error_404
114  *
115  * Description :  CGI function that is called if an unknown action was
116  *                given.
117  *
118  * Parameters  :
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
122  *
123  * CGI Parameters : none
124  *
125  * Returns     :  JB_ERR_OK on success
126  *                JB_ERR_MEMORY on out-of-memory error.
127  *
128  *********************************************************************/
129 jb_err cgi_error_404(struct client_state *csp,
130                      struct http_response *rsp,
131                      const struct map *parameters)
132 {
133    struct map *exports;
134
135    assert(csp);
136    assert(rsp);
137    assert(parameters);
138
139    if (NULL == (exports = default_exports(csp, NULL)))
140    {
141       return JB_ERR_MEMORY;
142    }
143
144    rsp->status = strdup_or_die("404 Privoxy configuration page not found");
145
146    return template_fill_for_cgi(csp, "cgi-error-404", exports, rsp);
147 }
148
149
150 #ifdef FEATURE_GRACEFUL_TERMINATION
151 /*********************************************************************
152  *
153  * Function    :  cgi_die
154  *
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.
159  *
160  * Parameters  :
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
164  *
165  * CGI Parameters : none
166  *
167  * Returns     :  JB_ERR_OK on success
168  *
169  *********************************************************************/
170 jb_err cgi_die (struct client_state *csp,
171                 struct http_response *rsp,
172                 const struct map *parameters)
173 {
174    static const char status[] = "200 OK Privoxy shutdown request received";
175    static const char body[] =
176       "<html>\n"
177       "<head>\n"
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"
181       "</head>\n"
182       "<body>\n"
183       "<h1>Privoxy shutdown request received</h1>\n"
184       "<p>Privoxy is going to shut down after the next request.</p>\n"
185       "</body>\n"
186       "</html>\n";
187
188    assert(csp);
189    assert(rsp);
190    assert(parameters);
191
192    /* quit */
193    g_terminate = 1;
194
195    csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
196
197    rsp->content_length = 0;
198    rsp->head_length = 0;
199    rsp->is_static = 0;
200
201    rsp->body = strdup_or_die(body);
202    rsp->status = strdup_or_die(status);
203
204    return JB_ERR_OK;
205 }
206 #endif /* def FEATURE_GRACEFUL_TERMINATION */
207
208
209 /*********************************************************************
210  *
211  * Function    :  cgi_show_request
212  *
213  * Description :  Show the client's request and what sed() would have
214  *                made of it.
215  *
216  * Parameters  :
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
220  *
221  * CGI Parameters : none
222  *
223  * Returns     :  JB_ERR_OK on success
224  *                JB_ERR_MEMORY on out-of-memory error.
225  *
226  *********************************************************************/
227 jb_err cgi_show_request(struct client_state *csp,
228                         struct http_response *rsp,
229                         const struct map *parameters)
230 {
231    char *p;
232    struct map *exports;
233
234    assert(csp);
235    assert(rsp);
236    assert(parameters);
237
238    if (NULL == (exports = default_exports(csp, "show-request")))
239    {
240       return JB_ERR_MEMORY;
241    }
242
243    /*
244     * Repair the damage done to the IOB by get_header()
245     */
246    for (p = csp->client_iob->buf; p < csp->client_iob->cur; p++)
247    {
248       if (*p == '\0') *p = '\n';
249    }
250
251    /*
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
254     */
255
256    if (map(exports, "client-request", 1, html_encode(csp->client_iob->buf), 0))
257    {
258       free_map(exports);
259       return JB_ERR_MEMORY;
260    }
261
262    if (map(exports, "processed-request", 1,
263          html_encode_and_free_original(
264 #ifdef FEATURE_HTTPS_INSPECTION
265                                        csp->http->ssl ?
266                                        list_to_text(csp->https_headers) :
267 #endif
268                                        list_to_text(csp->headers)
269                                        ), 0))
270    {
271       free_map(exports);
272       return JB_ERR_MEMORY;
273    }
274
275    return template_fill_for_cgi(csp, "show-request", exports, rsp);
276 }
277
278
279 #ifdef FEATURE_CLIENT_TAGS
280 /*********************************************************************
281  *
282  * Function    :  cgi_create_client_tag_form
283  *
284  * Description :  Creates a HTML form to enable or disable a given
285  *                client tag.
286  *                XXX: Could use a template.
287  *
288  * Parameters  :
289  *          1  :  form = Buffer to fill with the generated form
290  *          2  :  size = Size of the form buffer
291  *          3  :  tag = Name of the tag this form should affect
292  *          4  :  toggle_state = Desired state after the button pressed 0
293  *          5  :  expires = Whether or not the tag should be enabled.
294  *                          Only checked if toggle_state is 1.
295  *
296  * Returns     :  void
297  *
298  *********************************************************************/
299 static void cgi_create_client_tag_form(char *form, size_t size,
300    const char *tag, int toggle_state, int expires)
301 {
302    char *button_name;
303
304    if (toggle_state == 1)
305    {
306       button_name = (expires == 1) ? "Enable" : "Enable temporarily";
307    }
308    else
309    {
310       assert(toggle_state == 0);
311       button_name = "Disable";
312    }
313
314    snprintf(form, size,
315       "<form method=\"GET\" action=\""CGI_PREFIX"toggle-client-tag\" style=\"display: inline\">\n"
316       " <input type=\"hidden\" name=\"tag\" value=\"%s\">\n"
317       " <input type=\"hidden\" name=\"toggle-state\" value=\"%i\">\n"
318       " <input type=\"hidden\" name=\"expires\" value=\"%u\">\n"
319       " <input type=\"submit\" value=\"%s\">\n"
320       "</form>", tag, toggle_state, !expires, button_name);
321 }
322
323 /*********************************************************************
324  *
325  * Function    :  cgi_show_client_tags
326  *
327  * Description :  Shows the tags that can be set based on the client
328  *                address (opt-in).
329  *
330  * Parameters  :
331  *          1  :  csp = Current client state (buffers, headers, etc...)
332  *          2  :  rsp = http_response data structure for output
333  *          3  :  parameters = map of cgi parameters
334  *
335  * CGI Parameters : none
336  *
337  * Returns     :  JB_ERR_OK on success
338  *                JB_ERR_MEMORY on out-of-memory error.
339  *
340  *********************************************************************/
341 jb_err cgi_show_client_tags(struct client_state *csp,
342                         struct http_response *rsp,
343                         const struct map *parameters)
344 {
345    struct map *exports;
346    struct client_tag_spec *this_tag;
347    jb_err err = JB_ERR_OK;
348    char *client_tag_status;
349    char buf[1000];
350    time_t refresh_delay;
351
352    assert(csp);
353    assert(rsp);
354    assert(parameters);
355
356    if (NULL == (exports = default_exports(csp, "client-tags")))
357    {
358       return JB_ERR_MEMORY;
359    }
360    assert(csp->client_address != NULL);
361
362    this_tag = csp->config->client_tags;
363    if (this_tag->name == NULL)
364    {
365       client_tag_status = strdup_or_die("<p>No tags have been configured.</p>\n");
366    }
367    else
368    {
369       client_tag_status = strdup_or_die("<table border=\"1\">\n"
370          "<tr><th>Tag name</th>\n"
371          "<th>Current state</th><th>Change state</th><th>Description</th></tr>\n");
372       while ((this_tag != NULL) && (this_tag->name != NULL))
373       {
374          int tag_state;
375
376          privoxy_mutex_lock(&client_tags_mutex);
377          tag_state = client_has_requested_tag(csp->client_address, this_tag->name);
378          privoxy_mutex_unlock(&client_tags_mutex);
379          if (!err) err = string_append(&client_tag_status, "<tr><td>");
380          if (!err) err = string_append(&client_tag_status, this_tag->name);
381          if (!err) err = string_append(&client_tag_status, "</td><td>");
382          if (!err) err = string_append(&client_tag_status, tag_state == 1 ? "Enabled" : "Disabled");
383          if (!err) err = string_append(&client_tag_status, "</td><td>");
384          cgi_create_client_tag_form(buf, sizeof(buf), this_tag->name, !tag_state, 1);
385          if (!err) err = string_append(&client_tag_status, buf);
386          if (tag_state == 0)
387          {
388             cgi_create_client_tag_form(buf, sizeof(buf), this_tag->name, !tag_state, 0);
389             if (!err) err = string_append(&client_tag_status, buf);
390          }
391          if (!err) err = string_append(&client_tag_status, "</td><td>");
392          if (!err) err = string_append(&client_tag_status, this_tag->description);
393          if (!err) err = string_append(&client_tag_status, "</td></tr>\n");
394          if (err)
395          {
396             break;
397          }
398          this_tag = this_tag->next;
399       }
400       if (!err) err = string_append(&client_tag_status, "</table>\n");
401       if (err)
402       {
403          free_map(exports);
404          return JB_ERR_MEMORY;
405       }
406    }
407    refresh_delay = get_next_tag_timeout_for_client(csp->client_address);
408    if (refresh_delay != 0)
409    {
410       snprintf(buf, sizeof(buf), "%u", csp->config->client_tag_lifetime);
411       if (map(exports, "refresh-delay", 1, buf, 1))
412       {
413          freez(client_tag_status);
414          free_map(exports);
415          return JB_ERR_MEMORY;
416       }
417    }
418    else
419    {
420       err = map_block_killer(exports, "tags-expire");
421       if (err != JB_ERR_OK)
422       {
423          freez(client_tag_status);
424          return err;
425       }
426    }
427
428    if (map(exports, "client-tags", 1, client_tag_status, 0))
429    {
430       free_map(exports);
431       return JB_ERR_MEMORY;
432    }
433
434    if (map(exports, "client-ip-addr", 1, csp->client_address, 1))
435    {
436       free_map(exports);
437       return JB_ERR_MEMORY;
438    }
439
440    return template_fill_for_cgi(csp, "client-tags", exports, rsp);
441 }
442
443
444 /*********************************************************************
445  *
446  * Function    :  cgi_toggle_client_tag
447  *
448  * Description :  Toggles a client tag and redirects to the show-tags
449  *                page
450  *
451  * Parameters  :
452  *          1  :  csp = Current client state (buffers, headers, etc...)
453  *          2  :  rsp = http_response data structure for output
454  *          3  :  parameters = map of cgi parameters
455  *
456  * CGI Parameters : none
457  *          1  :  tag = Name of the tag to enable or disable
458  *          2  :  toggle-state = How to toggle the tag (0/1)
459  *          3  :  expires = Set to 1 if the tag should be enabled
460  *                          temporarily, otherwise set to 0
461  *
462  * Returns     :  JB_ERR_OK on success
463  *                JB_ERR_MEMORY on out-of-memory error.
464  *
465  *********************************************************************/
466 jb_err cgi_toggle_client_tag(struct client_state *csp,
467                              struct http_response *rsp,
468                              const struct map *parameters)
469 {
470    const char *toggled_tag;
471    const char *toggle_state;
472    const char *tag_expires;
473    time_t time_to_live;
474
475    assert(csp);
476    assert(rsp);
477    assert(parameters);
478
479    toggled_tag = lookup(parameters, "tag");
480    if (*toggled_tag == '\0')
481    {
482       log_error(LOG_LEVEL_ERROR, "Received tag toggle request without tag");
483    }
484    else
485    {
486       tag_expires = lookup(parameters, "expires");
487       if (*tag_expires == '0')
488       {
489          time_to_live = 0;
490       }
491       else
492       {
493          time_to_live = csp->config->client_tag_lifetime;
494       }
495       toggle_state = lookup(parameters, "toggle-state");
496       if (*toggle_state == '1')
497       {
498          enable_client_specific_tag(csp, toggled_tag, time_to_live);
499       }
500       else
501       {
502          disable_client_specific_tag(csp, toggled_tag);
503       }
504    }
505    rsp->status = strdup_or_die("302 Done dealing with toggle request");
506    if (enlist_unique_header(rsp->headers,
507          "Location", CGI_PREFIX "client-tags"))
508    {
509          return JB_ERR_MEMORY;
510    }
511    return JB_ERR_OK;
512
513 }
514 #endif /* def FEATURE_CLIENT_TAGS */
515
516
517 /*********************************************************************
518  *
519  * Function    :  cgi_send_banner
520  *
521  * Description :  CGI function that returns a banner.
522  *
523  * Parameters  :
524  *          1  :  csp = Current client state (buffers, headers, etc...)
525  *          2  :  rsp = http_response data structure for output
526  *          3  :  parameters = map of cgi parameters
527  *
528  * CGI Parameters :
529  *           type : Selects the type of banner between "trans", "logo",
530  *                  and "auto". Defaults to "logo" if absent or invalid.
531  *                  "auto" means to select as if we were image-blocking.
532  *                  (Only the first character really counts; b and t are
533  *                  equivalent).
534  *
535  * Returns     :  JB_ERR_OK on success
536  *                JB_ERR_MEMORY on out-of-memory error.
537  *
538  *********************************************************************/
539 jb_err cgi_send_banner(struct client_state *csp,
540                        struct http_response *rsp,
541                        const struct map *parameters)
542 {
543    char imagetype = lookup(parameters, "type")[0];
544
545    /*
546     * If type is auto, then determine the right thing
547     * to do from the set-image-blocker action
548     */
549    if (imagetype == 'a')
550    {
551       /*
552        * Default to pattern
553        */
554       imagetype = 'p';
555
556 #ifdef FEATURE_IMAGE_BLOCKING
557       if ((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
558       {
559          static const char prefix1[] = CGI_PREFIX "send-banner?type=";
560          static const char prefix2[] = "http://" CGI_SITE_1_HOST "/send-banner?type=";
561          const char *p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
562
563          if (p == NULL)
564          {
565             /* Use default - nothing to do here. */
566          }
567          else if (0 == strcmpic(p, "blank"))
568          {
569             imagetype = 'b';
570          }
571          else if (0 == strcmpic(p, "pattern"))
572          {
573             imagetype = 'p';
574          }
575
576          /*
577           * If the action is to call this CGI, determine
578           * the argument:
579           */
580          else if (0 == strncmpic(p, prefix1, sizeof(prefix1) - 1))
581          {
582             imagetype = p[sizeof(prefix1) - 1];
583          }
584          else if (0 == strncmpic(p, prefix2, sizeof(prefix2) - 1))
585          {
586             imagetype = p[sizeof(prefix2) - 1];
587          }
588
589          /*
590           * Everything else must (should) be a URL to
591           * redirect to.
592           */
593          else
594          {
595             imagetype = 'r';
596          }
597       }
598 #endif /* def FEATURE_IMAGE_BLOCKING */
599    }
600
601    /*
602     * Now imagetype is either the non-auto type we were called with,
603     * or it was auto and has since been determined. In any case, we
604     * can proceed to actually answering the request by sending a redirect
605     * or an image as appropriate:
606     */
607    if (imagetype == 'r')
608    {
609       rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
610       if (enlist_unique_header(rsp->headers, "Location",
611                                csp->action->string[ACTION_STRING_IMAGE_BLOCKER]))
612       {
613          return JB_ERR_MEMORY;
614       }
615    }
616    else
617    {
618       if ((imagetype == 'b') || (imagetype == 't'))
619       {
620          rsp->body = bindup(image_blank_data, image_blank_length);
621          rsp->content_length = image_blank_length;
622       }
623       else
624       {
625          rsp->body = bindup(image_pattern_data, image_pattern_length);
626          rsp->content_length = image_pattern_length;
627       }
628
629       if (rsp->body == NULL)
630       {
631          return JB_ERR_MEMORY;
632       }
633       if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
634       {
635          return JB_ERR_MEMORY;
636       }
637
638       rsp->is_static = 1;
639    }
640
641    return JB_ERR_OK;
642
643 }
644
645
646 /*********************************************************************
647  *
648  * Function    :  cgi_transparent_image
649  *
650  * Description :  CGI function that sends a 1x1 transparent image.
651  *
652  * Parameters  :
653  *          1  :  csp = Current client state (buffers, headers, etc...)
654  *          2  :  rsp = http_response data structure for output
655  *          3  :  parameters = map of cgi parameters
656  *
657  * CGI Parameters : None
658  *
659  * Returns     :  JB_ERR_OK on success
660  *                JB_ERR_MEMORY on out-of-memory error.
661  *
662  *********************************************************************/
663 jb_err cgi_transparent_image(struct client_state *csp,
664                              struct http_response *rsp,
665                              const struct map *parameters)
666 {
667    (void)csp;
668    (void)parameters;
669
670    rsp->body = bindup(image_blank_data, image_blank_length);
671    rsp->content_length = image_blank_length;
672
673    if (rsp->body == NULL)
674    {
675       return JB_ERR_MEMORY;
676    }
677
678    if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
679    {
680       return JB_ERR_MEMORY;
681    }
682
683    rsp->is_static = 1;
684
685    return JB_ERR_OK;
686
687 }
688
689
690 /*********************************************************************
691  *
692  * Function    :  cgi_send_default_favicon
693  *
694  * Description :  CGI function that sends the standard favicon.
695  *
696  * Parameters  :
697  *          1  :  csp = Current client state (buffers, headers, etc...)
698  *          2  :  rsp = http_response data structure for output
699  *          3  :  parameters = map of cgi parameters
700  *
701  * CGI Parameters : None
702  *
703  * Returns     :  JB_ERR_OK on success
704  *                JB_ERR_MEMORY on out-of-memory error.
705  *
706  *********************************************************************/
707 jb_err cgi_send_default_favicon(struct client_state *csp,
708                                 struct http_response *rsp,
709                                 const struct map *parameters)
710 {
711    static const char default_favicon_data[] =
712       "\000\000\001\000\001\000\020\020\002\000\000\000\000\000\260"
713       "\000\000\000\026\000\000\000\050\000\000\000\020\000\000\000"
714       "\040\000\000\000\001\000\001\000\000\000\000\000\100\000\000"
715       "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000"
716       "\000\000\377\377\377\000\377\000\052\000\017\360\000\000\077"
717       "\374\000\000\161\376\000\000\161\376\000\000\361\377\000\000"
718       "\361\377\000\000\360\017\000\000\360\007\000\000\361\307\000"
719       "\000\361\307\000\000\361\307\000\000\360\007\000\000\160\036"
720       "\000\000\177\376\000\000\077\374\000\000\017\360\000\000\360"
721       "\017\000\000\300\003\000\000\200\001\000\000\200\001\000\000"
722       "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
723       "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
724       "\000\000\200\001\000\000\200\001\000\000\300\003\000\000\360"
725       "\017\000\000";
726    static const size_t favicon_length = sizeof(default_favicon_data) - 1;
727
728    (void)csp;
729    (void)parameters;
730
731    rsp->body = bindup(default_favicon_data, favicon_length);
732    rsp->content_length = favicon_length;
733
734    if (rsp->body == NULL)
735    {
736       return JB_ERR_MEMORY;
737    }
738
739    if (enlist(rsp->headers, "Content-Type: image/x-icon"))
740    {
741       return JB_ERR_MEMORY;
742    }
743
744    rsp->is_static = 1;
745
746    return JB_ERR_OK;
747
748 }
749
750
751 /*********************************************************************
752  *
753  * Function    :  cgi_send_error_favicon
754  *
755  * Description :  CGI function that sends the favicon for error pages.
756  *
757  * Parameters  :
758  *          1  :  csp = Current client state (buffers, headers, etc...)
759  *          2  :  rsp = http_response data structure for output
760  *          3  :  parameters = map of cgi parameters
761  *
762  * CGI Parameters : None
763  *
764  * Returns     :  JB_ERR_OK on success
765  *                JB_ERR_MEMORY on out-of-memory error.
766  *
767  *********************************************************************/
768 jb_err cgi_send_error_favicon(struct client_state *csp,
769                               struct http_response *rsp,
770                               const struct map *parameters)
771 {
772    static const char error_favicon_data[] =
773       "\000\000\001\000\001\000\020\020\002\000\000\000\000\000\260"
774       "\000\000\000\026\000\000\000\050\000\000\000\020\000\000\000"
775       "\040\000\000\000\001\000\001\000\000\000\000\000\100\000\000"
776       "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000"
777       "\000\000\377\377\377\000\000\000\377\000\017\360\000\000\077"
778       "\374\000\000\161\376\000\000\161\376\000\000\361\377\000\000"
779       "\361\377\000\000\360\017\000\000\360\007\000\000\361\307\000"
780       "\000\361\307\000\000\361\307\000\000\360\007\000\000\160\036"
781       "\000\000\177\376\000\000\077\374\000\000\017\360\000\000\360"
782       "\017\000\000\300\003\000\000\200\001\000\000\200\001\000\000"
783       "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
784       "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
785       "\000\000\200\001\000\000\200\001\000\000\300\003\000\000\360"
786       "\017\000\000";
787    static const size_t favicon_length = sizeof(error_favicon_data) - 1;
788
789    (void)csp;
790    (void)parameters;
791
792    rsp->body = bindup(error_favicon_data, favicon_length);
793    rsp->content_length = favicon_length;
794
795    if (rsp->body == NULL)
796    {
797       return JB_ERR_MEMORY;
798    }
799
800    if (enlist(rsp->headers, "Content-Type: image/x-icon"))
801    {
802       return JB_ERR_MEMORY;
803    }
804
805    rsp->is_static = 1;
806
807    return JB_ERR_OK;
808
809 }
810
811
812 /*********************************************************************
813  *
814  * Function    :  cgi_send_stylesheet
815  *
816  * Description :  CGI function that sends a css stylesheet found
817  *                in the cgi-style.css template
818  *
819  * Parameters  :
820  *          1  :  csp = Current client state (buffers, headers, etc...)
821  *          2  :  rsp = http_response data structure for output
822  *          3  :  parameters = map of cgi parameters
823  *
824  * CGI Parameters : None
825  *
826  * Returns     :  JB_ERR_OK on success
827  *                JB_ERR_MEMORY on out-of-memory error.
828  *
829  *********************************************************************/
830 jb_err cgi_send_stylesheet(struct client_state *csp,
831                            struct http_response *rsp,
832                            const struct map *parameters)
833 {
834    jb_err err;
835
836    assert(csp);
837    assert(rsp);
838
839    (void)parameters;
840
841    err = template_load(csp, &rsp->body, "cgi-style.css", 0);
842
843    if (err == JB_ERR_FILE)
844    {
845       /*
846        * No way to tell user; send empty stylesheet
847        */
848       log_error(LOG_LEVEL_ERROR, "Could not find cgi-style.css template");
849    }
850    else if (err)
851    {
852       return err; /* JB_ERR_MEMORY */
853    }
854
855    if (enlist(rsp->headers, "Content-Type: text/css"))
856    {
857       return JB_ERR_MEMORY;
858    }
859
860    return JB_ERR_OK;
861
862 }
863
864
865 /*********************************************************************
866  *
867  * Function    :  cgi_send_url_info_osd
868  *
869  * Description :  CGI function that sends the OpenSearch Description
870  *                template for the show-url-info page. It allows to
871  *                access the page through "search engine plugins".
872  *
873  * Parameters  :
874  *          1  :  csp = Current client state (buffers, headers, etc...)
875  *          2  :  rsp = http_response data structure for output
876  *          3  :  parameters = map of cgi parameters
877  *
878  * CGI Parameters : None
879  *
880  * Returns     :  JB_ERR_OK on success
881  *                JB_ERR_MEMORY on out-of-memory error.
882  *
883  *********************************************************************/
884 jb_err cgi_send_url_info_osd(struct client_state *csp,
885                                struct http_response *rsp,
886                                const struct map *parameters)
887 {
888    jb_err err = JB_ERR_MEMORY;
889    struct map *exports = default_exports(csp, NULL);
890
891    (void)csp;
892    (void)parameters;
893
894    if (NULL != exports)
895    {
896       err = template_fill_for_cgi(csp, "url-info-osd.xml", exports, rsp);
897       if (JB_ERR_OK == err)
898       {
899          err = enlist(rsp->headers,
900             "Content-Type: application/opensearchdescription+xml");
901       }
902    }
903
904    return err;
905
906 }
907
908
909 /*********************************************************************
910  *
911  * Function    :  get_content_type
912  *
913  * Description :  Use the file extension to guess the content type
914  *                header we should use to serve the file.
915  *
916  * Parameters  :
917  *          1  :  filename = Name of the file whose content type
918  *                           we care about
919  *
920  * Returns     :  The guessed content type.
921  *
922  *********************************************************************/
923 static const char *get_content_type(const char *filename)
924 {
925    int i;
926    struct content_type
927    {
928       const char extension[6];
929       const char content_type[11];
930    };
931    static const struct content_type content_types[] =
932    {
933       {".css",  "text/css"},
934       {".jpg",  "image/jpeg"},
935       {".jpeg", "image/jpeg"},
936       {".png",  "image/png"},
937    };
938
939    for (i = 0; i < SZ(content_types); i++)
940    {
941       if (strstr(filename, content_types[i].extension))
942       {
943          return content_types[i].content_type;
944       }
945    }
946
947    /* No match by extension, default to html */
948    return "text/html";
949 }
950
951 /*********************************************************************
952  *
953  * Function    :  cgi_send_user_manual
954  *
955  * Description :  CGI function that sends a file in the user
956  *                manual directory.
957  *
958  * Parameters  :
959  *          1  :  csp = Current client state (buffers, headers, etc...)
960  *          2  :  rsp = http_response data structure for output
961  *          3  :  parameters = map of cgi parameters
962  *
963  * CGI Parameters : file=name.html, the name of the HTML file
964  *                  (relative to user-manual from config)
965  *
966  * Returns     :  JB_ERR_OK on success
967  *                JB_ERR_MEMORY on out-of-memory error.
968  *
969  *********************************************************************/
970 jb_err cgi_send_user_manual(struct client_state *csp,
971                             struct http_response *rsp,
972                             const struct map *parameters)
973 {
974    const char *filename;
975    char *full_path;
976    jb_err err = JB_ERR_OK;
977    const char *content_type;
978
979    assert(csp);
980    assert(rsp);
981    assert(parameters);
982
983    if (0 == strncmpic(csp->config->usermanual, "http://", 7))
984    {
985       log_error(LOG_LEVEL_CGI, "Request for local user-manual "
986          "received while user-manual delivery is disabled.");
987       return cgi_error_404(csp, rsp, parameters);
988    }
989
990    if (!parameters->first)
991    {
992       /* requested http://p.p/user-manual (without trailing slash) */
993       return cgi_redirect(rsp, CGI_PREFIX "user-manual/");
994    }
995
996    get_string_param(parameters, "file", &filename);
997    if (filename == NULL)
998    {
999       /* It's '/' so serve the index.html if there is one.  */
1000       filename = "index.html";
1001    }
1002    else if (NULL != strchr(filename, '/') || NULL != strstr(filename, ".."))
1003    {
1004       /*
1005        * We currently only support a flat file
1006        * hierarchy for the documentation.
1007        */
1008       log_error(LOG_LEVEL_ERROR,
1009          "Rejecting the request to serve '%s' as it contains '/' or '..'",
1010          filename);
1011       return JB_ERR_CGI_PARAMS;
1012    }
1013
1014    full_path = make_path(csp->config->usermanual, filename);
1015    if (full_path == NULL)
1016    {
1017       return JB_ERR_MEMORY;
1018    }
1019
1020    err = load_file(full_path, &rsp->body, &rsp->content_length);
1021    if (JB_ERR_OK != err)
1022    {
1023       assert((JB_ERR_FILE == err) || (JB_ERR_MEMORY == err));
1024       if (JB_ERR_FILE == err)
1025       {
1026          err = cgi_error_no_template(csp, rsp, full_path);
1027       }
1028       freez(full_path);
1029       return err;
1030    }
1031    freez(full_path);
1032
1033    content_type = get_content_type(filename);
1034    log_error(LOG_LEVEL_CGI,
1035       "Content-Type guessed for %s: %s", filename, content_type);
1036
1037    return enlist_unique_header(rsp->headers, "Content-Type", content_type);
1038
1039 }
1040
1041
1042 #ifdef FEATURE_EXTENDED_STATISTICS
1043 /*********************************************************************
1044  *
1045  * Function    :  get_block_reason_statistics_table
1046  *
1047  * Description :  Produces the block reason statistic table content.
1048  *
1049  * Parameters  :
1050  *          1  :  csp = Current client state (buffers, headers, etc...)
1051  *
1052  * Returns     :  Pointer to the HTML statistic table content or
1053  *                NULL on out of memory
1054  *
1055  *********************************************************************/
1056 static char *get_block_reason_statistics_table(const struct client_state *csp)
1057 {
1058    char buf[BUFFER_SIZE];
1059    char *statistics;
1060    int i;
1061    struct file_list *fl;
1062    jb_err err = JB_ERR_OK;
1063
1064    statistics = strdup_or_die("");
1065
1066    /* Run through all action files. */
1067    for (i = 0; i < MAX_AF_FILES; i++)
1068    {
1069       struct url_actions *b;
1070       struct action_spec *last_action = NULL;
1071
1072       if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
1073       {
1074          /* Skip empty files */
1075          continue;
1076       }
1077
1078       /* Go through all the actions. */
1079       for (b = b->next; NULL != b; b = b->next)
1080       {
1081          if (last_action == b->action)
1082          {
1083             continue;
1084          }
1085          if ((b->action->add & ACTION_BLOCK))
1086          {
1087             unsigned long long count;
1088             const char *block_reason = b->action->string[ACTION_STRING_BLOCK];
1089             const char *encoded_block_reason = html_encode(block_reason);
1090
1091             if (encoded_block_reason == NULL)
1092             {
1093                freez(statistics);
1094                return NULL;
1095             }
1096             get_block_reason_count(block_reason, &count);
1097             snprintf(buf, sizeof(buf),
1098                "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>\n",
1099                encoded_block_reason, count);
1100             freez(encoded_block_reason);
1101
1102             if (!err) err = string_append(&statistics, buf);
1103          }
1104          last_action = b->action;
1105       }
1106    }
1107
1108    return statistics;
1109
1110 }
1111
1112
1113 /*********************************************************************
1114  *
1115  * Function    :  get_filter_statistics_table
1116  *
1117  * Description :  Produces the filter statistic table content.
1118  *
1119  * Parameters  :
1120  *          1  :  csp = Current client state (buffers, headers, etc...)
1121  *
1122  * Returns     :  Pointer to the HTML statistic table content or
1123  *                NULL on out of memory
1124  *
1125  *********************************************************************/
1126 static char *get_filter_statistics_table(const struct client_state *csp)
1127 {
1128    char buf[BUFFER_SIZE];
1129    char *statistics;
1130    int i;
1131    struct file_list *fl;
1132    struct re_filterfile_spec *b;
1133    jb_err err = JB_ERR_OK;
1134
1135    statistics = strdup_or_die("");
1136
1137    for (i = 0; i < MAX_AF_FILES; i++)
1138    {
1139      fl = csp->rlist[i];
1140      if ((NULL == fl) || (NULL == fl->f))
1141      {
1142         /*
1143          * Either there are no filter files left or this
1144          * filter file just contains no valid filters.
1145          *
1146          * Continue to be sure we don't miss valid filter
1147          * files that are chained after empty or invalid ones.
1148          */
1149         continue;
1150      }
1151
1152      for (b = fl->f; b != NULL; b = b->next)
1153      {
1154         if (b->type == FT_CONTENT_FILTER)
1155         {
1156            unsigned long long executions;
1157            unsigned long long response_bodies_modified;
1158            unsigned long long hits;
1159
1160            get_filter_statistics(b->name, &executions, &response_bodies_modified, &hits);
1161            snprintf(buf, sizeof(buf),
1162               "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>"
1163               "<td style=\"text-align: right\">%llu</td>"
1164               "<td style=\"text-align: right\">%llu</td><tr>\n",
1165               b->name, executions, response_bodies_modified, hits);
1166
1167            if (!err) err = string_append(&statistics, buf);
1168         }
1169      }
1170    }
1171
1172    return statistics;
1173
1174 }
1175 #endif /* def FEATURE_EXTENDED_STATISTICS */
1176
1177
1178 /*********************************************************************
1179  *
1180  * Function    :  cgi_show_status
1181  *
1182  * Description :  CGI function that returns a web page describing the
1183  *                current status of Privoxy.
1184  *
1185  * Parameters  :
1186  *          1  :  csp = Current client state (buffers, headers, etc...)
1187  *          2  :  rsp = http_response data structure for output
1188  *          3  :  parameters = map of cgi parameters
1189  *
1190  * CGI Parameters :
1191  *        file :  Which file to show.  Only first letter is checked,
1192  *                valid values are:
1193  *                - "a"ction file
1194  *                - "r"egex
1195  *                - "t"rust
1196  *                Default is to show menu and other information.
1197  *
1198  * Returns     :  JB_ERR_OK on success
1199  *                JB_ERR_MEMORY on out-of-memory error.
1200  *
1201  *********************************************************************/
1202 jb_err cgi_show_status(struct client_state *csp,
1203                        struct http_response *rsp,
1204                        const struct map *parameters)
1205 {
1206    char *s = NULL;
1207    unsigned i;
1208    int j;
1209
1210    char buf[BUFFER_SIZE];
1211 #ifdef FEATURE_STATISTICS
1212    float perc_rej;   /* Percentage of http requests rejected */
1213    int local_urls_read;
1214    int local_urls_rejected;
1215 #endif /* ndef FEATURE_STATISTICS */
1216    jb_err err = JB_ERR_OK;
1217
1218    struct map *exports;
1219
1220    assert(csp);
1221    assert(rsp);
1222    assert(parameters);
1223
1224    if ('\0' != *(lookup(parameters, "file")))
1225    {
1226       return cgi_show_file(csp, rsp, parameters);
1227    }
1228
1229    if (NULL == (exports = default_exports(csp, "show-status")))
1230    {
1231       return JB_ERR_MEMORY;
1232    }
1233
1234    s = strdup("");
1235    for (j = 0; (s != NULL) && (j < Argc); j++)
1236    {
1237       if (!err) err = string_join  (&s, html_encode(Argv[j]));
1238       if (!err) err = string_append(&s, " ");
1239    }
1240    if (!err) err = map(exports, "invocation", 1, s, 0);
1241
1242    if (!err) err = map(exports, "options", 1, csp->config->proxy_args, 1);
1243    if (!err) err = show_defines(exports);
1244
1245    if (err)
1246    {
1247       free_map(exports);
1248       return JB_ERR_MEMORY;
1249    }
1250
1251 #ifdef FEATURE_STATISTICS
1252    local_urls_read     = urls_read;
1253    local_urls_rejected = urls_rejected;
1254
1255    /*
1256     * Need to alter the stats not to include the fetch of this
1257     * page.
1258     *
1259     * Can't do following thread safely! doh!
1260     *
1261     * urls_read--;
1262     * urls_rejected--; * This will be incremented subsequently *
1263     */
1264
1265    if (local_urls_read == 0)
1266    {
1267       if (!err) err = map_block_killer(exports, "have-stats");
1268    }
1269    else
1270    {
1271       if (!err) err = map_block_killer(exports, "have-no-stats");
1272
1273       perc_rej = (float)local_urls_rejected * 100.0F /
1274             (float)local_urls_read;
1275
1276       snprintf(buf, sizeof(buf), "%d", local_urls_read);
1277       if (!err) err = map(exports, "requests-received", 1, buf, 1);
1278
1279       snprintf(buf, sizeof(buf), "%d", local_urls_rejected);
1280       if (!err) err = map(exports, "requests-blocked", 1, buf, 1);
1281
1282       snprintf(buf, sizeof(buf), "%6.2f", perc_rej);
1283       if (!err) err = map(exports, "percent-blocked", 1, buf, 1);
1284    }
1285
1286 #else /* ndef FEATURE_STATISTICS */
1287    if (!err) err = map_block_killer(exports, "statistics");
1288 #endif /* ndef FEATURE_STATISTICS */
1289
1290 #ifdef FEATURE_EXTENDED_STATISTICS
1291    if (!err)
1292    {
1293       char *block_reason_statistics = get_block_reason_statistics_table(csp);
1294       if (block_reason_statistics != NULL)
1295       {
1296          err = map(exports, "block-reason-statistics", 1, block_reason_statistics, 0);
1297       }
1298       else
1299       {
1300          err = map_block_killer(exports, "extended-statistics");
1301       }
1302    }
1303    if (!err)
1304    {
1305       char *filter_statistics = get_filter_statistics_table(csp);
1306       if (filter_statistics != NULL)
1307       {
1308          err = map(exports, "filter-statistics", 1, filter_statistics, 0);
1309       }
1310       else
1311       {
1312          err = map_block_killer(exports, "extended-statistics");
1313       }
1314    }
1315 #else /* ndef FEATURE_EXTENDED_STATISTICS */
1316    if (!err) err = map_block_killer(exports, "extended-statistics");
1317 #endif /* def FEATURE_EXTENDED_STATISTICS */
1318
1319    /*
1320     * List all action files in use, together with view and edit links,
1321     * except for standard.action, which should only be viewable. (Not
1322     * enforced in the editor itself)
1323     * FIXME: Shouldn't include hardwired HTML here, use line template instead!
1324     */
1325    s = strdup("");
1326    for (i = 0; i < MAX_AF_FILES; i++)
1327    {
1328       if (csp->actions_list[i] != NULL)
1329       {
1330          if (!err) err = string_append(&s, "<tr><td>");
1331          if (!err) err = string_join(&s, html_encode(csp->actions_list[i]->filename));
1332          snprintf(buf, sizeof(buf),
1333             "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&amp;index=%u\">View</a>", i);
1334          if (!err) err = string_append(&s, buf);
1335
1336 #ifdef FEATURE_CGI_EDIT_ACTIONS
1337          if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
1338             && (NULL != csp->config->actions_file_short[i]))
1339          {
1340 #ifdef HAVE_ACCESS
1341             if (access(csp->config->actions_file[i], W_OK) == 0)
1342             {
1343 #endif /* def HAVE_ACCESS */
1344                snprintf(buf, sizeof(buf), "&nbsp;&nbsp;<a href=\"/edit-actions-list?f=%u\">Edit</a>", i);
1345                if (!err) err = string_append(&s, buf);
1346 #ifdef HAVE_ACCESS
1347             }
1348             else
1349             {
1350                if (!err) err = string_append(&s, "&nbsp;&nbsp;<strong>No write access.</strong>");
1351             }
1352 #endif /* def HAVE_ACCESS */
1353          }
1354 #endif
1355
1356          if (!err) err = string_append(&s, "</td></tr>\n");
1357       }
1358    }
1359    if (!err && *s != '\0')
1360    {
1361       err = map(exports, "actions-filenames", 1, s, 0);
1362    }
1363    else
1364    {
1365       if (!err) err = map(exports, "actions-filenames", 1, "<tr><td>None specified</td></tr>", 1);
1366       freez(s);
1367    }
1368
1369    /*
1370     * List all re_filterfiles in use, together with view options.
1371     * FIXME: Shouldn't include hardwired HTML here, use line template instead!
1372     */
1373    s = strdup("");
1374    for (i = 0; i < MAX_AF_FILES; i++)
1375    {
1376       if (csp->rlist[i] != NULL)
1377       {
1378          if (!err) err = string_append(&s, "<tr><td>");
1379          if (!err) err = string_join(&s, html_encode(csp->rlist[i]->filename));
1380          snprintf(buf, sizeof(buf),
1381             "</td><td class=\"buttons\"><a href=\"/show-status?file=filter&amp;index=%u\">View</a>", i);
1382          if (!err) err = string_append(&s, buf);
1383          if (!err) err = string_append(&s, "</td></tr>\n");
1384       }
1385    }
1386    if (!err && *s != '\0')
1387    {
1388       err = map(exports, "re-filter-filenames", 1, s, 0);
1389    }
1390    else
1391    {
1392       if (!err) err = map(exports, "re-filter-filenames", 1, "<tr><td>None specified</td></tr>", 1);
1393       if (!err) err = map_block_killer(exports, "have-filterfile");
1394       freez(s);
1395    }
1396
1397 #ifdef FEATURE_TRUST
1398    if (csp->tlist)
1399    {
1400       if (!err) err = map(exports, "trust-filename", 1, html_encode(csp->tlist->filename), 0);
1401    }
1402    else
1403    {
1404       if (!err) err = map(exports, "trust-filename", 1, "None specified", 1);
1405       if (!err) err = map_block_killer(exports, "have-trustfile");
1406    }
1407 #else
1408    if (!err) err = map_block_killer(exports, "trust-support");
1409 #endif /* ndef FEATURE_TRUST */
1410
1411 #ifdef FEATURE_CGI_EDIT_ACTIONS
1412    if (!err && (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
1413    {
1414       err = map_block_killer(exports, "cgi-editor-is-disabled");
1415    }
1416 #endif /* ndef CGI_EDIT_ACTIONS */
1417
1418    if (!err) err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
1419
1420    if (err)
1421    {
1422       free_map(exports);
1423       return JB_ERR_MEMORY;
1424    }
1425
1426    return template_fill_for_cgi(csp, "show-status", exports, rsp);
1427 }
1428
1429
1430 /*********************************************************************
1431  *
1432  * Function    :  cgi_show_url_info
1433  *
1434  * Description :  CGI function that determines and shows which actions
1435  *                Privoxy will perform for a given url, and which
1436  *                matches starting from the defaults have lead to that.
1437  *
1438  * Parameters  :
1439  *          1  :  csp = Current client state (buffers, headers, etc...)
1440  *          2  :  rsp = http_response data structure for output
1441  *          3  :  parameters = map of cgi parameters
1442  *
1443  * CGI Parameters :
1444  *            url : The url whose actions are to be determined.
1445  *                  If url is unset, the url-given conditional will be
1446  *                  set, so that all but the form can be suppressed in
1447  *                  the template.
1448  *
1449  * Returns     :  JB_ERR_OK on success
1450  *                JB_ERR_MEMORY on out-of-memory error.
1451  *
1452  *********************************************************************/
1453 jb_err cgi_show_url_info(struct client_state *csp,
1454                          struct http_response *rsp,
1455                          const struct map *parameters)
1456 {
1457    char *url_param;
1458    struct map *exports;
1459    char buf[150];
1460
1461    assert(csp);
1462    assert(rsp);
1463    assert(parameters);
1464
1465    if (NULL == (exports = default_exports(csp, "show-url-info")))
1466    {
1467       return JB_ERR_MEMORY;
1468    }
1469
1470    /*
1471     * Get the url= parameter (if present) and remove any leading/trailing spaces.
1472     */
1473    url_param = strdup_or_die(lookup(parameters, "url"));
1474    chomp(url_param);
1475
1476    /*
1477     * Handle prefixes.  4 possibilities:
1478     * 1) "http://" or "https://" prefix present and followed by URL - OK
1479     * 2) Only the "http://" or "https://" part is present, no URL - change
1480     *    to empty string so it will be detected later as "no URL".
1481     * 3) Parameter specified but doesn't start with "http(s?)://" - add a
1482     *    "http://" prefix.
1483     * 4) Parameter not specified or is empty string - let this fall through
1484     *    for now, next block of code will handle it.
1485     */
1486    if (0 == strncmp(url_param, "http://", 7))
1487    {
1488       if (url_param[7] == '\0')
1489       {
1490          /*
1491           * Empty URL (just prefix).
1492           * Make it totally empty so it's caught by the next if ()
1493           */
1494          url_param[0] = '\0';
1495       }
1496    }
1497    else if (0 == strncmp(url_param, "https://", 8))
1498    {
1499       if (url_param[8] == '\0')
1500       {
1501          /*
1502           * Empty URL (just prefix).
1503           * Make it totally empty so it's caught by the next if ()
1504           */
1505          url_param[0] = '\0';
1506       }
1507    }
1508    else if ((url_param[0] != '\0')
1509       && ((NULL == strstr(url_param, "://")
1510             || (strstr(url_param, "://") > strstr(url_param, "/")))))
1511    {
1512       /*
1513        * No prefix or at least no prefix before
1514        * the first slash - assume http://
1515        */
1516       char *url_param_prefixed = strdup_or_die("http://");
1517
1518       if (JB_ERR_OK != string_join(&url_param_prefixed, url_param))
1519       {
1520          free_map(exports);
1521          return JB_ERR_MEMORY;
1522       }
1523       url_param = url_param_prefixed;
1524    }
1525
1526    /*
1527     * Hide "toggle off" warning if Privoxy is toggled on.
1528     */
1529    if (
1530 #ifdef FEATURE_TOGGLE
1531        (global_toggle_state == 1) &&
1532 #endif /* def FEATURE_TOGGLE */
1533        map_block_killer(exports, "privoxy-is-toggled-off")
1534       )
1535    {
1536       freez(url_param);
1537       free_map(exports);
1538       return JB_ERR_MEMORY;
1539    }
1540
1541    if (url_param[0] == '\0')
1542    {
1543       /* URL parameter not specified, display query form only. */
1544       free(url_param);
1545       if (map_block_killer(exports, "url-given")
1546         || map(exports, "url", 1, "", 1))
1547       {
1548          free_map(exports);
1549          return JB_ERR_MEMORY;
1550       }
1551    }
1552    else
1553    {
1554       /* Given a URL, so query it. */
1555       jb_err err;
1556       char *matches;
1557       char *s;
1558       int hits = 0;
1559       struct file_list *fl;
1560       struct url_actions *b;
1561       struct http_request url_to_query[1];
1562       struct current_action_spec action[1];
1563       int i;
1564
1565       if (map(exports, "url", 1, html_encode(url_param), 0))
1566       {
1567          free(url_param);
1568          free_map(exports);
1569          return JB_ERR_MEMORY;
1570       }
1571
1572       init_current_action(action);
1573
1574       if (map(exports, "default", 1, current_action_to_html(csp, action), 0))
1575       {
1576          free_current_action(action);
1577          free(url_param);
1578          free_map(exports);
1579          return JB_ERR_MEMORY;
1580       }
1581
1582       memset(url_to_query, '\0', sizeof(url_to_query));
1583       err = parse_http_url(url_param, url_to_query, REQUIRE_PROTOCOL);
1584       assert((err != JB_ERR_OK) || (url_to_query->ssl == !strncmpic(url_param, "https://", 8)));
1585
1586       free(url_param);
1587
1588       if (err == JB_ERR_MEMORY)
1589       {
1590          free_http_request(url_to_query);
1591          free_current_action(action);
1592          free_map(exports);
1593          return JB_ERR_MEMORY;
1594       }
1595       else if (err)
1596       {
1597          /* Invalid URL */
1598
1599          err = map(exports, "matches", 1, "<b>[Invalid URL specified!]</b>" , 1);
1600          if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1);
1601          if (!err) err = map_block_killer(exports, "valid-url");
1602
1603          free_current_action(action);
1604          free_http_request(url_to_query);
1605
1606          if (err)
1607          {
1608             free_map(exports);
1609             return JB_ERR_MEMORY;
1610          }
1611
1612          return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1613       }
1614
1615       /*
1616        * We have a warning about SSL paths. Hide it for unencrypted sites
1617        * and unconditionally if https inspection is enabled.
1618        */
1619 #ifndef FEATURE_HTTPS_INSPECTION
1620       if (!url_to_query->ssl)
1621 #endif
1622       {
1623          if (map_block_killer(exports, "https-and-no-https-inspection"))
1624          {
1625             free_current_action(action);
1626             free_map(exports);
1627             free_http_request(url_to_query);
1628             return JB_ERR_MEMORY;
1629          }
1630       }
1631
1632       matches = strdup_or_die("<table summary=\"\" class=\"transparent\">");
1633
1634       for (i = 0; i < MAX_AF_FILES; i++)
1635       {
1636          if (NULL == csp->config->actions_file_short[i]
1637              || !strcmp(csp->config->actions_file_short[i], "standard.action")) continue;
1638
1639          b = NULL;
1640          hits = 1;
1641          if ((fl = csp->actions_list[i]) != NULL)
1642          {
1643             if ((b = fl->f) != NULL)
1644             {
1645                /* FIXME: Hardcoded HTML! */
1646                string_append(&matches, "<tr><th>In file: ");
1647                string_join  (&matches, html_encode(csp->config->actions_file_short[i]));
1648                snprintf(buf, sizeof(buf), " <a class=\"cmd\" href=\"/show-status?file=actions&amp;index=%d\">", i);
1649                string_append(&matches, buf);
1650                string_append(&matches, "View</a>");
1651 #ifdef FEATURE_CGI_EDIT_ACTIONS
1652                if (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
1653                {
1654 #ifdef HAVE_ACCESS
1655                   if (access(csp->config->actions_file[i], W_OK) == 0)
1656                   {
1657 #endif /* def HAVE_ACCESS */
1658                      snprintf(buf, sizeof(buf),
1659                         " <a class=\"cmd\" href=\"/edit-actions-list?f=%d\">", i);
1660                      string_append(&matches, buf);
1661                      string_append(&matches, "Edit</a>");
1662 #ifdef HAVE_ACCESS
1663                   }
1664                   else
1665                   {
1666                      string_append(&matches, " <strong>No write access.</strong>");
1667                   }
1668 #endif /* def HAVE_ACCESS */
1669                }
1670 #endif /* FEATURE_CGI_EDIT_ACTIONS */
1671
1672                string_append(&matches, "</th></tr>\n");
1673
1674                hits = 0;
1675                b = b->next;
1676             }
1677          }
1678
1679          for (; (b != NULL) && (matches != NULL); b = b->next)
1680          {
1681             if (url_match(b->url, url_to_query))
1682             {
1683                string_append(&matches, "<tr><td>{");
1684                string_join  (&matches, actions_to_html(csp, b->action));
1685                string_append(&matches, " }<br>\n<code>");
1686                string_join  (&matches, html_encode(b->url->spec));
1687                string_append(&matches, "</code></td></tr>\n");
1688
1689                if (merge_current_action(action, b->action))
1690                {
1691                   freez(matches);
1692                   free_http_request(url_to_query);
1693                   free_current_action(action);
1694                   free_map(exports);
1695                   return JB_ERR_MEMORY;
1696                }
1697                hits++;
1698             }
1699          }
1700
1701          if (!hits)
1702          {
1703             string_append(&matches, "<tr><td>(no matches in this file)</td></tr>\n");
1704          }
1705       }
1706       string_append(&matches, "</table>\n");
1707
1708       /*
1709        * XXX: Kludge to make sure the "Forward settings" section
1710        * shows what forward-override{} would do with the requested URL.
1711        * No one really cares how the CGI request would be forwarded
1712        * if it wasn't intercepted as CGI request in the first place.
1713        *
1714        * From here on the action bitmask will no longer reflect
1715        * the real url (http://config.privoxy.org/show-url-info?url=.*),
1716        * but luckily it's no longer required later on anyway.
1717        */
1718       free_current_action(csp->action);
1719       get_url_actions(csp, url_to_query);
1720
1721       /*
1722        * Fill in forwarding settings.
1723        *
1724        * The possibilities are:
1725        *  - no forwarding
1726        *  - http forwarding only
1727        *  - socks4(a) forwarding only
1728        *  - socks4(a) and http forwarding.
1729        *
1730        * XXX: Parts of this code could be reused for the
1731        * "forwarding-failed" template which currently doesn't
1732        * display the proxy port and an eventual second forwarder.
1733        */
1734       {
1735          const struct forward_spec *fwd = forward_url(csp, url_to_query);
1736
1737          if ((fwd->gateway_host == NULL) && (fwd->forward_host == NULL))
1738          {
1739             if (!err) err = map_block_killer(exports, "socks-forwarder");
1740             if (!err) err = map_block_killer(exports, "http-forwarder");
1741          }
1742          else
1743          {
1744             char port[10]; /* We save proxy ports as int but need a string here */
1745
1746             if (!err) err = map_block_killer(exports, "no-forwarder");
1747
1748             if (fwd->gateway_host != NULL)
1749             {
1750                char *socks_type = NULL;
1751
1752                switch (fwd->type)
1753                {
1754                   case SOCKS_4:
1755                      socks_type = "socks4";
1756                      break;
1757                   case SOCKS_4A:
1758                      socks_type = "socks4a";
1759                      break;
1760                   case SOCKS_5:
1761                      socks_type = "socks5";
1762                      break;
1763                   case SOCKS_5T:
1764                      socks_type = "socks5t";
1765                      break;
1766                   default:
1767                      log_error(LOG_LEVEL_FATAL, "Unknown socks type: %d.", fwd->type);
1768                }
1769
1770                if (!err) err = map(exports, "socks-type", 1, socks_type, 1);
1771                if (!err) err = map(exports, "gateway-host", 1, fwd->gateway_host, 1);
1772                snprintf(port, sizeof(port), "%d", fwd->gateway_port);
1773                if (!err) err = map(exports, "gateway-port", 1, port, 1);
1774             }
1775             else
1776             {
1777                if (!err) err = map_block_killer(exports, "socks-forwarder");
1778             }
1779
1780             if (fwd->forward_host != NULL)
1781             {
1782                if (!err) err = map(exports, "forward-host", 1, fwd->forward_host, 1);
1783                snprintf(port, sizeof(port), "%d", fwd->forward_port);
1784                if (!err) err = map(exports, "forward-port", 1, port, 1);
1785             }
1786             else
1787             {
1788                if (!err) err = map_block_killer(exports, "http-forwarder");
1789             }
1790          }
1791       }
1792
1793       free_http_request(url_to_query);
1794
1795       if (err || matches == NULL)
1796       {
1797          free_current_action(action);
1798          free_map(exports);
1799          return JB_ERR_MEMORY;
1800       }
1801
1802 #ifdef FEATURE_CGI_EDIT_ACTIONS
1803       if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
1804       {
1805          err = map_block_killer(exports, "cgi-editor-is-disabled");
1806       }
1807 #endif /* FEATURE_CGI_EDIT_ACTIONS */
1808
1809       /*
1810        * If zlib support is available, if no content filters
1811        * are enabled or if the prevent-compression action is enabled,
1812        * suppress the "compression could prevent filtering" warning.
1813        */
1814 #ifndef FEATURE_ZLIB
1815       if (!content_filters_enabled(action) ||
1816          (action->flags & ACTION_NO_COMPRESSION))
1817 #endif
1818       {
1819          if (!err) err = map_block_killer(exports, "filters-might-be-ineffective");
1820       }
1821
1822       if (err || map(exports, "matches", 1, matches , 0))
1823       {
1824          free_current_action(action);
1825          free_map(exports);
1826          return JB_ERR_MEMORY;
1827       }
1828
1829       s = current_action_to_html(csp, action);
1830
1831       free_current_action(action);
1832
1833       if (map(exports, "final", 1, s, 0))
1834       {
1835          free_map(exports);
1836          return JB_ERR_MEMORY;
1837       }
1838    }
1839
1840    return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1841 }
1842
1843
1844 /*********************************************************************
1845  *
1846  * Function    :  cgi_robots_txt
1847  *
1848  * Description :  CGI function to return "/robots.txt".
1849  *
1850  * Parameters  :
1851  *          1  :  csp = Current client state (buffers, headers, etc...)
1852  *          2  :  rsp = http_response data structure for output
1853  *          3  :  parameters = map of cgi parameters
1854  *
1855  * CGI Parameters : None
1856  *
1857  * Returns     :  JB_ERR_OK on success
1858  *                JB_ERR_MEMORY on out-of-memory error.
1859  *
1860  *********************************************************************/
1861 jb_err cgi_robots_txt(struct client_state *csp,
1862                       struct http_response *rsp,
1863                       const struct map *parameters)
1864 {
1865    char buf[100];
1866    jb_err err;
1867
1868    (void)csp;
1869    (void)parameters;
1870
1871    rsp->body = strdup_or_die(
1872       "# This is the Privoxy control interface.\n"
1873       "# It isn't very useful to index it, and you're likely to break stuff.\n"
1874       "# So go away!\n"
1875       "\n"
1876       "User-agent: *\n"
1877       "Disallow: /\n"
1878       "\n");
1879
1880    err = enlist_unique(rsp->headers, "Content-Type: text/plain", 13);
1881
1882    rsp->is_static = 1;
1883
1884    get_http_time(7 * 24 * 60 * 60, buf, sizeof(buf)); /* 7 days into future */
1885    if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf);
1886
1887    return (err ? JB_ERR_MEMORY : JB_ERR_OK);
1888 }
1889
1890
1891 /*********************************************************************
1892  *
1893  * Function    :  show_defines
1894  *
1895  * Description :  Add to a map the state of all conditional #defines
1896  *                used when building
1897  *
1898  * Parameters  :
1899  *          1  :  exports = map to extend
1900  *
1901  * Returns     :  JB_ERR_OK on success
1902  *                JB_ERR_MEMORY on out-of-memory error.
1903  *
1904  *********************************************************************/
1905 static jb_err show_defines(struct map *exports)
1906 {
1907    jb_err err = JB_ERR_OK;
1908    int i;
1909    struct feature {
1910       const char name[31];
1911       const unsigned char is_available;
1912    };
1913
1914    static const struct feature features[] = {
1915       {
1916          "FEATURE_64_BIT_TIME_T",
1917 #if (SIZEOF_TIME_T == 8)
1918          1,
1919 #else
1920          0,
1921 #endif
1922       },
1923       {
1924          "FEATURE_ACCEPT_FILTER",
1925 #ifdef FEATURE_ACCEPT_FILTER
1926          1,
1927 #else
1928          0,
1929 #endif
1930       },
1931       {
1932          "FEATURE_ACL",
1933 #ifdef FEATURE_ACL
1934          1,
1935 #else
1936          0,
1937 #endif
1938       },
1939       {
1940          "FEATURE_BROTLI",
1941 #ifdef FEATURE_BROTLI
1942          1,
1943 #else
1944          0,
1945 #endif
1946       },
1947       {
1948          "FEATURE_CGI_EDIT_ACTIONS",
1949 #ifdef FEATURE_CGI_EDIT_ACTIONS
1950          1,
1951 #else
1952          0,
1953 #endif
1954       },
1955       {
1956          "FEATURE_CLIENT_TAGS",
1957 #ifdef FEATURE_CLIENT_TAGS
1958          1,
1959 #else
1960          0,
1961 #endif
1962       },
1963       {
1964          "FEATURE_COMPRESSION",
1965 #ifdef FEATURE_COMPRESSION
1966          1,
1967 #else
1968          0,
1969 #endif
1970       },
1971       {
1972          "FEATURE_CONNECTION_KEEP_ALIVE",
1973 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
1974          1,
1975 #else
1976          0,
1977 #endif
1978       },
1979       {
1980          "FEATURE_CONNECTION_SHARING",
1981 #ifdef FEATURE_CONNECTION_SHARING
1982          1,
1983 #else
1984          0,
1985 #endif
1986       },
1987       {
1988          "FEATURE_EXTERNAL_FILTERS",
1989 #ifdef FEATURE_EXTERNAL_FILTERS
1990          1,
1991 #else
1992          0,
1993 #endif
1994       },
1995       {
1996          "FEATURE_FAST_REDIRECTS",
1997 #ifdef FEATURE_FAST_REDIRECTS
1998          1,
1999 #else
2000          0,
2001 #endif
2002       },
2003       {
2004          "FEATURE_FORCE_LOAD",
2005 #ifdef FEATURE_FORCE_LOAD
2006          1,
2007 #else
2008          0,
2009 #endif
2010       },
2011       {
2012          "FEATURE_GRACEFUL_TERMINATION",
2013 #ifdef FEATURE_GRACEFUL_TERMINATION
2014          1,
2015 #else
2016          0,
2017 #endif
2018       },
2019       {
2020          "FEATURE_HTTPS_INSPECTION",
2021 #ifdef FEATURE_HTTPS_INSPECTION
2022          1,
2023 #else
2024          0,
2025 #endif
2026       },
2027       {
2028          "FEATURE_IMAGE_BLOCKING",
2029 #ifdef FEATURE_IMAGE_BLOCKING
2030          1,
2031 #else
2032          0,
2033 #endif
2034       },
2035       {
2036          "FEATURE_IPV6_SUPPORT",
2037 #ifdef HAVE_RFC2553
2038          1,
2039 #else
2040          0,
2041 #endif
2042       },
2043       {
2044          "FEATURE_NO_GIFS",
2045 #ifdef FEATURE_NO_GIFS
2046          1,
2047 #else
2048          0,
2049 #endif
2050       },
2051       {
2052          "FEATURE_PTHREAD",
2053 #ifdef FEATURE_PTHREAD
2054          1,
2055 #else
2056          0,
2057 #endif
2058       },
2059       {
2060          "FEATURE_STATISTICS",
2061 #ifdef FEATURE_STATISTICS
2062          1,
2063 #else
2064          0,
2065 #endif
2066       },
2067       {
2068          "FEATURE_STRPTIME_SANITY_CHECKS",
2069 #ifdef FEATURE_STRPTIME_SANITY_CHECKS
2070          1,
2071 #else
2072          0,
2073 #endif
2074       },
2075       {
2076          "FEATURE_TOGGLE",
2077 #ifdef FEATURE_TOGGLE
2078          1,
2079 #else
2080          0,
2081 #endif
2082       },
2083       {
2084          "FEATURE_TRUST",
2085 #ifdef FEATURE_TRUST
2086          1,
2087 #else
2088          0,
2089 #endif
2090       },
2091       {
2092          "FEATURE_ZLIB",
2093 #ifdef FEATURE_ZLIB
2094          1,
2095 #else
2096          0,
2097 #endif
2098       },
2099       {
2100          "FEATURE_DYNAMIC_PCRE",
2101 #ifdef FEATURE_DYNAMIC_PCRE
2102          1,
2103 #else
2104          0,
2105 #endif
2106       },
2107       {
2108          "FEATURE_EXTENDED_STATISTICS",
2109 #ifdef FEATURE_EXTENDED_STATISTICS
2110          1,
2111 #else
2112          0,
2113 #endif
2114       },
2115       {
2116          "FEATURE_PCRE_HOST_PATTERNS",
2117 #ifdef FEATURE_PCRE_HOST_PATTERNS
2118          1,
2119 #else
2120          0,
2121 #endif
2122       }
2123    };
2124
2125    for (i = 0; i < SZ(features); i++)
2126    {
2127       err = map_conditional(exports, features[i].name, features[i].is_available);
2128       if (err)
2129       {
2130          break;
2131       }
2132    }
2133
2134    return err;
2135
2136 }
2137
2138
2139 /*********************************************************************
2140  *
2141  * Function    :  cgi_show_file
2142  *
2143  * Description :  CGI function that shows the content of a
2144  *                configuration file.
2145  *
2146  * Parameters  :
2147  *          1  :  csp = Current client state (buffers, headers, etc...)
2148  *          2  :  rsp = http_response data structure for output
2149  *          3  :  parameters = map of cgi parameters
2150  *
2151  * CGI Parameters :
2152  *        file :  Which file to show.  Only first letter is checked,
2153  *                valid values are:
2154  *                - "a"ction file
2155  *                - "r"egex
2156  *                - "t"rust
2157  *                Default is to show menu and other information.
2158  *
2159  * Returns     :  JB_ERR_OK on success
2160  *                JB_ERR_MEMORY on out-of-memory error.
2161  *
2162  *********************************************************************/
2163 static jb_err cgi_show_file(struct client_state *csp,
2164                             struct http_response *rsp,
2165                             const struct map *parameters)
2166 {
2167    unsigned i;
2168    const char * filename = NULL;
2169    char * file_description = NULL;
2170
2171    assert(csp);
2172    assert(rsp);
2173    assert(parameters);
2174
2175    switch (*(lookup(parameters, "file")))
2176    {
2177    case 'a':
2178       if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->actions_list[i])
2179       {
2180          filename = csp->actions_list[i]->filename;
2181          file_description = "Actions File";
2182       }
2183       break;
2184
2185    case 'f':
2186       if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->rlist[i])
2187       {
2188          filename = csp->rlist[i]->filename;
2189          file_description = "Filter File";
2190       }
2191       break;
2192
2193 #ifdef FEATURE_TRUST
2194    case 't':
2195       if (csp->tlist)
2196       {
2197          filename = csp->tlist->filename;
2198          file_description = "Trust File";
2199       }
2200       break;
2201 #endif /* def FEATURE_TRUST */
2202    }
2203
2204    if (NULL != filename)
2205    {
2206       struct map *exports;
2207       char *s;
2208       jb_err err;
2209       size_t length;
2210
2211       exports = default_exports(csp, "show-status");
2212       if (NULL == exports)
2213       {
2214          return JB_ERR_MEMORY;
2215       }
2216
2217       if (map(exports, "file-description", 1, file_description, 1)
2218         || map(exports, "filepath", 1, html_encode(filename), 0))
2219       {
2220          free_map(exports);
2221          return JB_ERR_MEMORY;
2222       }
2223
2224       err = load_file(filename, &s, &length);
2225       if (JB_ERR_OK != err)
2226       {
2227          if (map(exports, "contents", 1, "<h1>ERROR OPENING FILE!</h1>", 1))
2228          {
2229             free_map(exports);
2230             return JB_ERR_MEMORY;
2231          }
2232       }
2233       else
2234       {
2235          s = html_encode_and_free_original(s);
2236          if (NULL == s)
2237          {
2238             free_map(exports);
2239             return JB_ERR_MEMORY;
2240          }
2241
2242          if (map(exports, "contents", 1, s, 0))
2243          {
2244             free_map(exports);
2245             return JB_ERR_MEMORY;
2246          }
2247       }
2248
2249       return template_fill_for_cgi(csp, "show-status-file", exports, rsp);
2250    }
2251
2252    return JB_ERR_CGI_PARAMS;
2253 }
2254
2255
2256 /*********************************************************************
2257  *
2258  * Function    :  load_file
2259  *
2260  * Description :  Loads a file into a buffer.
2261  *
2262  * Parameters  :
2263  *          1  :  filename = Name of the file to be loaded.
2264  *          2  :  buffer   = Used to return the file's content.
2265  *          3  :  length   = Used to return the size of the file.
2266  *
2267  * Returns     :  JB_ERR_OK in case of success,
2268  *                JB_ERR_FILE in case of ordinary file loading errors
2269  *                            (fseek() and ftell() errors are fatal)
2270  *                JB_ERR_MEMORY in case of out-of-memory.
2271  *
2272  *********************************************************************/
2273 static jb_err load_file(const char *filename, char **buffer, size_t *length)
2274 {
2275    FILE *fp;
2276    long ret;
2277    jb_err err = JB_ERR_OK;
2278
2279    fp = fopen(filename, "rb");
2280    if (NULL == fp)
2281    {
2282       log_error(LOG_LEVEL_ERROR, "Failed to open %s: %E", filename);
2283       return JB_ERR_FILE;
2284    }
2285
2286    /* Get file length */
2287    if (fseek(fp, 0, SEEK_END))
2288    {
2289       log_error(LOG_LEVEL_FATAL,
2290          "Unexpected error while fseek()ing to the end of %s: %E",
2291          filename);
2292    }
2293    ret = ftell(fp);
2294    if (-1 == ret)
2295    {
2296       log_error(LOG_LEVEL_FATAL,
2297          "Unexpected ftell() error while loading %s: %E",
2298          filename);
2299    }
2300    *length = (size_t)ret;
2301
2302    /* Go back to the beginning. */
2303    if (fseek(fp, 0, SEEK_SET))
2304    {
2305       log_error(LOG_LEVEL_FATAL,
2306          "Unexpected error while fseek()ing to the beginning of %s: %E",
2307          filename);
2308    }
2309
2310    *buffer = zalloc_or_die(*length + 1);
2311
2312    if (1 != fread(*buffer, *length, 1, fp))
2313    {
2314       /*
2315        * May theoretically happen if the file size changes between
2316        * fseek() and fread() because it's edited in-place. Privoxy
2317        * and common text editors don't do that, thus we just fail.
2318        */
2319       log_error(LOG_LEVEL_ERROR,
2320          "Couldn't completely read file %s.", filename);
2321       freez(*buffer);
2322       err = JB_ERR_FILE;
2323    }
2324
2325    fclose(fp);
2326
2327    return err;
2328
2329 }
2330
2331
2332 /*
2333   Local Variables:
2334   tab-width: 3
2335   end:
2336 */