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