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