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