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