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