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