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