Add '.png' to the list of recognized file extenstions in get_content_type()
[privoxy.git] / cgisimple.c
1 const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.104 2011/02/14 16:07:11 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       {".png",  "image/png"},
688    };
689
690    for (i = 0; i < SZ(content_types); i++)
691    {
692       if (strstr(filename, content_types[i].extension))
693       {
694          return content_types[i].content_type;
695       }
696    }
697
698    /* No match by extension, default to html */
699    return "text/html";
700 }
701
702 /*********************************************************************
703  *
704  * Function    :  cgi_send_user_manual
705  *
706  * Description :  CGI function that sends a file in the user
707  *                manual directory.
708  *
709  * Parameters  :
710  *          1  :  csp = Current client state (buffers, headers, etc...)
711  *          2  :  rsp = http_response data structure for output
712  *          3  :  parameters = map of cgi parameters
713  *
714  * CGI Parameters : file=name.html, the name of the HTML file
715  *                  (relative to user-manual from config)
716  *
717  * Returns     :  JB_ERR_OK on success
718  *                JB_ERR_MEMORY on out-of-memory error.  
719  *
720  *********************************************************************/
721 jb_err cgi_send_user_manual(struct client_state *csp,
722                             struct http_response *rsp,
723                             const struct map *parameters)
724 {
725    const char *filename;
726    char *full_path;
727    jb_err err = JB_ERR_OK;
728    const char *content_type;
729
730    assert(csp);
731    assert(rsp);
732    assert(parameters);
733
734    if (0 == strncmpic(csp->config->usermanual, "http://", 7))
735    {
736       log_error(LOG_LEVEL_CGI, "Request for local user-manual "
737          "received while user-manual delivery is disabled.");
738       return cgi_error_404(csp, rsp, parameters);
739    }
740
741    if (!parameters->first)
742    {
743       /* requested http://p.p/user-manual (without trailing slash) */
744       return cgi_redirect(rsp, CGI_PREFIX "user-manual/");
745    }
746
747    get_string_param(parameters, "file", &filename);
748    if (filename == NULL)
749    {
750       /* It's '/' so serve the index.html if there is one.  */
751       filename = "index.html";
752    }
753    else if (NULL != strchr(filename, '/') || NULL != strstr(filename, ".."))
754    {
755       /*
756        * We currently only support a flat file
757        * hierachy for the documentation.
758        */
759       log_error(LOG_LEVEL_ERROR,
760          "Rejecting the request to serve '%s' as it contains '/' or '..'",
761          filename);
762       return JB_ERR_CGI_PARAMS;
763    }
764
765    full_path = make_path(csp->config->usermanual, filename);
766    if (full_path == NULL)
767    {
768       return JB_ERR_MEMORY;
769    }
770
771    err = load_file(full_path, &rsp->body, &rsp->content_length);
772    if (JB_ERR_OK != err)
773    {
774       assert((JB_ERR_FILE == err) || (JB_ERR_MEMORY == err));
775       if (JB_ERR_FILE == err)
776       {
777          err = cgi_error_no_template(csp, rsp, full_path);
778       }
779       freez(full_path);
780       return err;
781    }
782    freez(full_path);
783
784    content_type = get_content_type(filename);
785    log_error(LOG_LEVEL_CGI,
786       "Content-Type guessed for %s: %s", filename, content_type);
787
788    return enlist_unique_header(rsp->headers, "Content-Type", content_type);
789
790 }
791
792
793 /*********************************************************************
794  *
795  * Function    :  cgi_show_version
796  *
797  * Description :  CGI function that returns a a web page describing the
798  *                file versions of Privoxy.
799  *
800  * Parameters  :
801  *          1  :  csp = Current client state (buffers, headers, etc...)
802  *          2  :  rsp = http_response data structure for output
803  *          3  :  parameters = map of cgi parameters
804  *
805  * CGI Parameters : none
806  *
807  * Returns     :  JB_ERR_OK on success
808  *                JB_ERR_MEMORY on out-of-memory error.  
809  *
810  *********************************************************************/
811 jb_err cgi_show_version(struct client_state *csp,
812                         struct http_response *rsp,
813                         const struct map *parameters)
814 {
815    struct map *exports;
816
817    assert(csp);
818    assert(rsp);
819    assert(parameters);
820
821    if (NULL == (exports = default_exports(csp, "show-version")))
822    {
823       return JB_ERR_MEMORY;
824    }
825
826    if (map(exports, "sourceversions", 1, show_rcs(), 0))
827    {
828       free_map(exports);
829       return JB_ERR_MEMORY;
830    }
831
832    return template_fill_for_cgi(csp, "show-version", exports, rsp);
833 }
834
835
836 /*********************************************************************
837  *
838  * Function    :  cgi_show_status
839  *
840  * Description :  CGI function that returns a web page describing the
841  *                current status of Privoxy.
842  *
843  * Parameters  :
844  *          1  :  csp = Current client state (buffers, headers, etc...)
845  *          2  :  rsp = http_response data structure for output
846  *          3  :  parameters = map of cgi parameters
847  *
848  * CGI Parameters :
849  *        file :  Which file to show.  Only first letter is checked,
850  *                valid values are:
851  *                - "a"ction file
852  *                - "r"egex
853  *                - "t"rust
854  *                Default is to show menu and other information.
855  *
856  * Returns     :  JB_ERR_OK on success
857  *                JB_ERR_MEMORY on out-of-memory error.  
858  *
859  *********************************************************************/
860 jb_err cgi_show_status(struct client_state *csp,
861                        struct http_response *rsp,
862                        const struct map *parameters)
863 {
864    char *s = NULL;
865    unsigned i;
866    int j;
867
868    char buf[BUFFER_SIZE];
869 #ifdef FEATURE_STATISTICS
870    float perc_rej;   /* Percentage of http requests rejected */
871    int local_urls_read;
872    int local_urls_rejected;
873 #endif /* ndef FEATURE_STATISTICS */
874    jb_err err = JB_ERR_OK;
875
876    struct map *exports;
877
878    assert(csp);
879    assert(rsp);
880    assert(parameters);
881
882    if ('\0' != *(lookup(parameters, "file")))
883    {
884       return cgi_show_file(csp, rsp, parameters);
885    }
886
887    if (NULL == (exports = default_exports(csp, "show-status")))
888    {
889       return JB_ERR_MEMORY;
890    }
891
892    s = strdup("");
893    for (j = 0; (s != NULL) && (j < Argc); j++)
894    {
895       if (!err) err = string_join  (&s, html_encode(Argv[j]));
896       if (!err) err = string_append(&s, " ");
897    }
898    if (!err) err = map(exports, "invocation", 1, s, 0);
899
900    if (!err) err = map(exports, "options", 1, csp->config->proxy_args, 1);
901    if (!err) err = show_defines(exports);
902
903    if (err) 
904    {
905       free_map(exports);
906       return JB_ERR_MEMORY;
907    }
908
909 #ifdef FEATURE_STATISTICS
910    local_urls_read     = urls_read;
911    local_urls_rejected = urls_rejected;
912
913    /*
914     * Need to alter the stats not to include the fetch of this
915     * page.
916     *
917     * Can't do following thread safely! doh!
918     *
919     * urls_read--;
920     * urls_rejected--; * This will be incremented subsequently *
921     */
922
923    if (local_urls_read == 0)
924    {
925       if (!err) err = map_block_killer(exports, "have-stats");
926    }
927    else
928    {
929       if (!err) err = map_block_killer(exports, "have-no-stats");
930
931       perc_rej = (float)local_urls_rejected * 100.0F /
932             (float)local_urls_read;
933
934       snprintf(buf, sizeof(buf), "%d", local_urls_read);
935       if (!err) err = map(exports, "requests-received", 1, buf, 1);
936
937       snprintf(buf, sizeof(buf), "%d", local_urls_rejected);
938       if (!err) err = map(exports, "requests-blocked", 1, buf, 1);
939
940       snprintf(buf, sizeof(buf), "%6.2f", perc_rej);
941       if (!err) err = map(exports, "percent-blocked", 1, buf, 1);
942    }
943
944 #else /* ndef FEATURE_STATISTICS */
945    err = err || map_block_killer(exports, "statistics");
946 #endif /* ndef FEATURE_STATISTICS */
947    
948    /* 
949     * List all action files in use, together with view and edit links,
950     * except for standard.action, which should only be viewable. (Not
951     * enforced in the editor itself)
952     * FIXME: Shouldn't include hardwired HTML here, use line template instead!
953     */
954    s = strdup("");
955    for (i = 0; i < MAX_AF_FILES; i++)
956    {
957       if (csp->actions_list[i] != NULL)
958       {
959          if (!err) err = string_append(&s, "<tr><td>");
960          if (!err) err = string_join(&s, html_encode(csp->actions_list[i]->filename));
961          snprintf(buf, sizeof(buf),
962             "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&amp;index=%u\">View</a>", i);
963          if (!err) err = string_append(&s, buf);
964
965 #ifdef FEATURE_CGI_EDIT_ACTIONS
966          if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
967             && (NULL == strstr(csp->actions_list[i]->filename, "standard.action"))
968             && (NULL != csp->config->actions_file_short[i]))
969          {
970 #ifdef HAVE_ACCESS
971             if (access(csp->config->actions_file[i], W_OK) == 0)
972             {
973 #endif /* def HAVE_ACCESS */
974                snprintf(buf, sizeof(buf), "&nbsp;&nbsp;<a href=\"/edit-actions-list?f=%u\">Edit</a>", i);
975                if (!err) err = string_append(&s, buf);
976 #ifdef HAVE_ACCESS
977             }
978             else
979             {
980                if (!err) err = string_append(&s, "&nbsp;&nbsp;<strong>No write access.</strong>");
981             }
982 #endif /* def HAVE_ACCESS */
983          }
984 #endif
985
986          if (!err) err = string_append(&s, "</td></tr>\n");
987       }
988    }
989    if (*s != '\0')   
990    {
991       if (!err) err = map(exports, "actions-filenames", 1, s, 0);
992    }
993    else
994    {
995       if (!err) err = map(exports, "actions-filenames", 1, "<tr><td>None specified</td></tr>", 1);
996    }
997
998    /* 
999     * List all re_filterfiles in use, together with view options.
1000     * FIXME: Shouldn't include hardwired HTML here, use line template instead!
1001     */
1002    s = strdup("");
1003    for (i = 0; i < MAX_AF_FILES; i++)
1004    {
1005       if (csp->rlist[i] != NULL)
1006       {
1007          if (!err) err = string_append(&s, "<tr><td>");
1008          if (!err) err = string_join(&s, html_encode(csp->rlist[i]->filename));
1009          snprintf(buf, sizeof(buf),
1010             "</td><td class=\"buttons\"><a href=\"/show-status?file=filter&amp;index=%u\">View</a>", i);
1011          if (!err) err = string_append(&s, buf);
1012          if (!err) err = string_append(&s, "</td></tr>\n");
1013       }
1014    }
1015    if (*s != '\0')   
1016    {
1017       if (!err) err = map(exports, "re-filter-filenames", 1, s, 0);
1018    }
1019    else
1020    {
1021       if (!err) err = map(exports, "re-filter-filenames", 1, "<tr><td>None specified</td></tr>", 1);
1022       if (!err) err = map_block_killer(exports, "have-filterfile");
1023    }
1024
1025 #ifdef FEATURE_TRUST
1026    if (csp->tlist)
1027    {
1028       if (!err) err = map(exports, "trust-filename", 1, html_encode(csp->tlist->filename), 0);
1029    }
1030    else
1031    {
1032       if (!err) err = map(exports, "trust-filename", 1, "None specified", 1);
1033       if (!err) err = map_block_killer(exports, "have-trustfile");
1034    }
1035 #else
1036    if (!err) err = map_block_killer(exports, "trust-support");
1037 #endif /* ndef FEATURE_TRUST */
1038
1039 #ifdef FEATURE_CGI_EDIT_ACTIONS
1040    if (!err && (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
1041    {
1042       err = map_block_killer(exports, "cgi-editor-is-disabled");
1043    }
1044 #endif /* ndef CGI_EDIT_ACTIONS */
1045
1046    if (err)
1047    {
1048       free_map(exports);
1049       return JB_ERR_MEMORY;
1050    }
1051
1052    return template_fill_for_cgi(csp, "show-status", exports, rsp);
1053 }
1054
1055  
1056 /*********************************************************************
1057  *
1058  * Function    :  cgi_show_url_info
1059  *
1060  * Description :  CGI function that determines and shows which actions
1061  *                Privoxy will perform for a given url, and which
1062  *                matches starting from the defaults have lead to that.
1063  *
1064  * Parameters  :
1065  *          1  :  csp = Current client state (buffers, headers, etc...)
1066  *          2  :  rsp = http_response data structure for output
1067  *          3  :  parameters = map of cgi parameters
1068  *
1069  * CGI Parameters :
1070  *            url : The url whose actions are to be determined.
1071  *                  If url is unset, the url-given conditional will be
1072  *                  set, so that all but the form can be suppressed in
1073  *                  the template.
1074  *
1075  * Returns     :  JB_ERR_OK on success
1076  *                JB_ERR_MEMORY on out-of-memory error.  
1077  *
1078  *********************************************************************/
1079 jb_err cgi_show_url_info(struct client_state *csp,
1080                          struct http_response *rsp,
1081                          const struct map *parameters)
1082 {
1083    char *url_param;
1084    struct map *exports;
1085    char buf[150];
1086
1087    assert(csp);
1088    assert(rsp);
1089    assert(parameters);
1090
1091    if (NULL == (exports = default_exports(csp, "show-url-info")))
1092    {
1093       return JB_ERR_MEMORY;
1094    }
1095
1096    /*
1097     * Get the url= parameter (if present) and remove any leading/trailing spaces.
1098     */
1099    url_param = strdup(lookup(parameters, "url"));
1100    if (url_param == NULL)
1101    {
1102       free_map(exports);
1103       return JB_ERR_MEMORY;
1104    }
1105    chomp(url_param);
1106
1107    /*
1108     * Handle prefixes.  4 possibilities:
1109     * 1) "http://" or "https://" prefix present and followed by URL - OK
1110     * 2) Only the "http://" or "https://" part is present, no URL - change
1111     *    to empty string so it will be detected later as "no URL".
1112     * 3) Parameter specified but doesn't start with "http(s?)://" - add a
1113     *    "http://" prefix.
1114     * 4) Parameter not specified or is empty string - let this fall through
1115     *    for now, next block of code will handle it.
1116     */
1117    if (0 == strncmp(url_param, "http://", 7))
1118    {
1119       if (url_param[7] == '\0')
1120       {
1121          /*
1122           * Empty URL (just prefix).
1123           * Make it totally empty so it's caught by the next if()
1124           */
1125          url_param[0] = '\0';
1126       }
1127    }
1128    else if (0 == strncmp(url_param, "https://", 8))
1129    {
1130       if (url_param[8] == '\0')
1131       {
1132          /*
1133           * Empty URL (just prefix).
1134           * Make it totally empty so it's caught by the next if()
1135           */
1136          url_param[0] = '\0';
1137       }
1138    }
1139    else if ((url_param[0] != '\0')
1140       && ((NULL == strstr(url_param, "://")
1141             || (strstr(url_param, "://") > strstr(url_param, "/")))))
1142    {
1143       /*
1144        * No prefix or at least no prefix before
1145        * the first slash - assume http://
1146        */
1147       char *url_param_prefixed = strdup("http://");
1148
1149       if (JB_ERR_OK != string_join(&url_param_prefixed, url_param))
1150       {
1151          free_map(exports);
1152          return JB_ERR_MEMORY;
1153       }
1154       url_param = url_param_prefixed;
1155    }
1156
1157    /*
1158     * Hide "toggle off" warning if Privoxy is toggled on.
1159     */
1160    if (
1161 #ifdef FEATURE_TOGGLE
1162        (global_toggle_state == 1) &&
1163 #endif /* def FEATURE_TOGGLE */
1164        map_block_killer(exports, "privoxy-is-toggled-off")
1165       )
1166    {
1167       free_map(exports);
1168       return JB_ERR_MEMORY;
1169    }
1170
1171    if (url_param[0] == '\0')
1172    {
1173       /* URL paramater not specified, display query form only. */
1174       free(url_param);
1175       if (map_block_killer(exports, "url-given")
1176         || map(exports, "url", 1, "", 1))
1177       {
1178          free_map(exports);
1179          return JB_ERR_MEMORY;
1180       }
1181    }
1182    else
1183    {
1184       /* Given a URL, so query it. */
1185       jb_err err;
1186       char *matches;
1187       char *s;
1188       int hits = 0;
1189       struct file_list *fl;
1190       struct url_actions *b;
1191       struct http_request url_to_query[1];
1192       struct current_action_spec action[1];
1193       int i;
1194       
1195       if (map(exports, "url", 1, html_encode(url_param), 0))
1196       {
1197          free(url_param);
1198          free_map(exports);
1199          return JB_ERR_MEMORY;
1200       }
1201
1202       init_current_action(action);
1203
1204       if (map(exports, "default", 1, current_action_to_html(csp, action), 0))
1205       {
1206          free_current_action(action);
1207          free(url_param);
1208          free_map(exports);
1209          return JB_ERR_MEMORY;
1210       }
1211
1212       memset(url_to_query, '\0', sizeof(url_to_query));
1213       err = parse_http_url(url_param, url_to_query, REQUIRE_PROTOCOL);
1214       assert((err != JB_ERR_OK) || (url_to_query->ssl == !strncmpic(url_param, "https://", 8)));
1215
1216       free(url_param);
1217
1218       if (err == JB_ERR_MEMORY)
1219       {
1220          free_http_request(url_to_query);
1221          free_current_action(action);
1222          free_map(exports);
1223          return JB_ERR_MEMORY;
1224       }
1225       else if (err)
1226       {
1227          /* Invalid URL */
1228
1229          err = map(exports, "matches", 1, "<b>[Invalid URL specified!]</b>" , 1);
1230          if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1);
1231          if (!err) err = map_block_killer(exports, "valid-url");
1232
1233          free_current_action(action);
1234          free_http_request(url_to_query);
1235
1236          if (err)
1237          {
1238             free_map(exports);
1239             return JB_ERR_MEMORY;
1240          }
1241
1242          return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1243       }
1244
1245       /*
1246        * We have a warning about SSL paths.  Hide it for unencrypted sites.
1247        */
1248       if (!url_to_query->ssl)
1249       {
1250          if (map_block_killer(exports, "https"))
1251          {
1252             free_current_action(action);
1253             free_map(exports);
1254             free_http_request(url_to_query);
1255             return JB_ERR_MEMORY;
1256          }
1257       }
1258
1259       matches = strdup("<table summary=\"\" class=\"transparent\">");
1260
1261       for (i = 0; i < MAX_AF_FILES; i++)
1262       {
1263          if (NULL == csp->config->actions_file_short[i]
1264              || !strcmp(csp->config->actions_file_short[i], "standard.action")) continue;
1265
1266          b = NULL;
1267          hits = 1;
1268          if ((fl = csp->actions_list[i]) != NULL)
1269          {
1270             if ((b = fl->f) != NULL)
1271             {
1272                /* FIXME: Hardcoded HTML! */
1273                string_append(&matches, "<tr><th>In file: ");
1274                string_join  (&matches, html_encode(csp->config->actions_file_short[i]));
1275                snprintf(buf, sizeof(buf), " <a class=\"cmd\" href=\"/show-status?file=actions&amp;index=%d\">", i);
1276                string_append(&matches, buf);
1277                string_append(&matches, "View</a>");
1278 #ifdef FEATURE_CGI_EDIT_ACTIONS
1279                if (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)
1280                {
1281 #ifdef HAVE_ACCESS
1282                   if (access(csp->config->actions_file[i], W_OK) == 0)
1283                   {
1284 #endif /* def HAVE_ACCESS */
1285                      snprintf(buf, sizeof(buf),
1286                         " <a class=\"cmd\" href=\"/edit-actions-list?f=%d\">", i);
1287                      string_append(&matches, buf);
1288                      string_append(&matches, "Edit</a>");
1289 #ifdef HAVE_ACCESS
1290                   }
1291                   else
1292                   {
1293                      string_append(&matches, " <strong>No write access.</strong>");
1294                   }
1295 #endif /* def HAVE_ACCESS */
1296                }
1297 #endif /* FEATURE_CGI_EDIT_ACTIONS */
1298
1299                string_append(&matches, "</th></tr>\n");
1300
1301                hits = 0;
1302                b = b->next;
1303             }
1304          }
1305
1306          for (; (b != NULL) && (matches != NULL); b = b->next)
1307          {
1308             if (url_match(b->url, url_to_query))
1309             {
1310                string_append(&matches, "<tr><td>{");
1311                string_join  (&matches, actions_to_html(csp, b->action));
1312                string_append(&matches, " }<br>\n<code>");
1313                string_join  (&matches, html_encode(b->url->spec));
1314                string_append(&matches, "</code></td></tr>\n");
1315
1316                if (merge_current_action(action, b->action))
1317                {
1318                   freez(matches);
1319                   free_http_request(url_to_query);
1320                   free_current_action(action);
1321                   free_map(exports);
1322                   return JB_ERR_MEMORY;
1323                }
1324                hits++;
1325             }
1326          }
1327
1328          if (!hits)
1329          {
1330             string_append(&matches, "<tr><td>(no matches in this file)</td></tr>\n");
1331          }
1332       }
1333       string_append(&matches, "</table>\n");
1334
1335       /*
1336        * XXX: Kludge to make sure the "Forward settings" section
1337        * shows what forward-override{} would do with the requested URL.
1338        * No one really cares how the CGI request would be forwarded
1339        * if it wasn't intercepted as CGI request in the first place.
1340        *
1341        * From here on the action bitmask will no longer reflect
1342        * the real url (http://config.privoxy.org/show-url-info?url=.*),
1343        * but luckily it's no longer required later on anyway.
1344        */
1345       free_current_action(csp->action);
1346       get_url_actions(csp, url_to_query);
1347
1348       /*
1349        * Fill in forwarding settings.
1350        *
1351        * The possibilities are:
1352        *  - no forwarding
1353        *  - http forwarding only
1354        *  - socks4(a) forwarding only
1355        *  - socks4(a) and http forwarding.
1356        *
1357        * XXX: Parts of this code could be reused for the
1358        * "forwarding-failed" template which currently doesn't
1359        * display the proxy port and an eventual second forwarder.
1360        */
1361       {
1362          const struct forward_spec *fwd = forward_url(csp, url_to_query);
1363
1364          if ((fwd->gateway_host == NULL) && (fwd->forward_host == NULL))
1365          {
1366             if (!err) err = map_block_killer(exports, "socks-forwarder");
1367             if (!err) err = map_block_killer(exports, "http-forwarder");
1368          }
1369          else
1370          {
1371             char port[10]; /* We save proxy ports as int but need a string here */
1372
1373             if (!err) err = map_block_killer(exports, "no-forwarder");
1374
1375             if (fwd->gateway_host != NULL)
1376             {
1377                char *socks_type = NULL;
1378
1379                switch (fwd->type)
1380                {
1381                   case SOCKS_4:
1382                      socks_type = "socks4";
1383                      break;
1384                   case SOCKS_4A:
1385                      socks_type = "socks4a";
1386                      break;
1387                   case SOCKS_5:
1388                      socks_type = "socks5";
1389                      break;
1390                   default:
1391                      log_error(LOG_LEVEL_FATAL, "Unknown socks type: %d.", fwd->type);
1392                }
1393
1394                if (!err) err = map(exports, "socks-type", 1, socks_type, 1);
1395                if (!err) err = map(exports, "gateway-host", 1, fwd->gateway_host, 1);
1396                snprintf(port, sizeof(port), "%d", fwd->gateway_port);
1397                if (!err) err = map(exports, "gateway-port", 1, port, 1);
1398             }
1399             else
1400             {
1401                if (!err) err = map_block_killer(exports, "socks-forwarder");
1402             }
1403
1404             if (fwd->forward_host != NULL)
1405             {
1406                if (!err) err = map(exports, "forward-host", 1, fwd->forward_host, 1);
1407                snprintf(port, sizeof(port), "%d", fwd->forward_port);
1408                if (!err) err = map(exports, "forward-port", 1, port, 1);
1409             }
1410             else
1411             {
1412                if (!err) err = map_block_killer(exports, "http-forwarder");
1413             }
1414          }
1415       }
1416
1417       free_http_request(url_to_query);
1418
1419       if (err || matches == NULL)
1420       {
1421          free_current_action(action);
1422          free_map(exports);
1423          return JB_ERR_MEMORY;
1424       }
1425
1426 #ifdef FEATURE_CGI_EDIT_ACTIONS
1427       if ((csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
1428       {
1429          err = map_block_killer(exports, "cgi-editor-is-disabled");
1430       }
1431 #endif /* FEATURE_CGI_EDIT_ACTIONS */
1432
1433       /*
1434        * If zlib support is available, if no content filters
1435        * are enabled or if the prevent-compression action is enabled,
1436        * suppress the "compression could prevent filtering" warning.
1437        */
1438 #ifndef FEATURE_ZLIB
1439       if (!content_filters_enabled(action) ||
1440          (action->flags & ACTION_NO_COMPRESSION))
1441 #endif
1442       {
1443          if (!err) err = map_block_killer(exports, "filters-might-be-ineffective");
1444       }
1445
1446       if (err || map(exports, "matches", 1, matches , 0))
1447       {
1448          free_current_action(action);
1449          free_map(exports);
1450          return JB_ERR_MEMORY;
1451       }
1452
1453       s = current_action_to_html(csp, action);
1454
1455       free_current_action(action);
1456
1457       if (map(exports, "final", 1, s, 0))
1458       {
1459          free_map(exports);
1460          return JB_ERR_MEMORY;
1461       }
1462    }
1463
1464    return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1465 }
1466
1467
1468 /*********************************************************************
1469  *
1470  * Function    :  cgi_robots_txt
1471  *
1472  * Description :  CGI function to return "/robots.txt".
1473  *
1474  * Parameters  :
1475  *          1  :  csp = Current client state (buffers, headers, etc...)
1476  *          2  :  rsp = http_response data structure for output
1477  *          3  :  parameters = map of cgi parameters
1478  *
1479  * CGI Parameters : None
1480  *
1481  * Returns     :  JB_ERR_OK on success
1482  *                JB_ERR_MEMORY on out-of-memory error.  
1483  *
1484  *********************************************************************/
1485 jb_err cgi_robots_txt(struct client_state *csp,
1486                       struct http_response *rsp,
1487                       const struct map *parameters)
1488 {
1489    char buf[100];
1490    jb_err err;
1491
1492    (void)csp;
1493    (void)parameters;
1494
1495    rsp->body = strdup(
1496       "# This is the Privoxy control interface.\n"
1497       "# It isn't very useful to index it, and you're likely to break stuff.\n"
1498       "# So go away!\n"
1499       "\n"
1500       "User-agent: *\n"
1501       "Disallow: /\n"
1502       "\n");
1503    if (rsp->body == NULL)
1504    {
1505       return JB_ERR_MEMORY;
1506    }
1507
1508    err = enlist_unique(rsp->headers, "Content-Type: text/plain", 13);
1509
1510    rsp->is_static = 1;
1511
1512    get_http_time(7 * 24 * 60 * 60, buf, sizeof(buf)); /* 7 days into future */
1513    if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf);
1514
1515    return (err ? JB_ERR_MEMORY : JB_ERR_OK);
1516 }
1517
1518
1519 /*********************************************************************
1520  *
1521  * Function    :  show_defines
1522  *
1523  * Description :  Add to a map the state od all conditional #defines
1524  *                used when building
1525  *
1526  * Parameters  :
1527  *          1  :  exports = map to extend
1528  *
1529  * Returns     :  JB_ERR_OK on success
1530  *                JB_ERR_MEMORY on out-of-memory error.  
1531  *
1532  *********************************************************************/
1533 static jb_err show_defines(struct map *exports)
1534 {
1535    jb_err err = JB_ERR_OK;
1536
1537 #ifdef FEATURE_ACCEPT_FILTER
1538    if (!err) err = map_conditional(exports, "FEATURE_ACCEPT_FILTER", 1);
1539 #else /* ifndef FEATURE_ACCEPT_FILTER */
1540    if (!err) err = map_conditional(exports, "FEATURE_ACCEPT_FILTER", 0);
1541 #endif /* ndef FEATURE_ACCEPT_FILTER */
1542
1543 #ifdef FEATURE_ACL
1544    if (!err) err = map_conditional(exports, "FEATURE_ACL", 1);
1545 #else /* ifndef FEATURE_ACL */
1546    if (!err) err = map_conditional(exports, "FEATURE_ACL", 0);
1547 #endif /* ndef FEATURE_ACL */
1548
1549 #ifdef FEATURE_CGI_EDIT_ACTIONS
1550    if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 1);
1551 #else /* ifndef FEATURE_CGI_EDIT_ACTIONS */
1552    if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 0);
1553 #endif /* ndef FEATURE_CGI_EDIT_ACTIONS */
1554
1555 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
1556    if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_KEEP_ALIVE", 1);
1557 #else /* ifndef FEATURE_CONNECTION_KEEP_ALIVE */
1558    if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_KEEP_ALIVE", 0);
1559 #endif /* ndef FEATURE_CONNECTION_KEEP_ALIVE */
1560
1561 #ifdef FEATURE_CONNECTION_SHARING
1562    if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_SHARING", 1);
1563 #else /* ifndef FEATURE_CONNECTION_SHARING */
1564    if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_SHARING", 0);
1565 #endif /* ndef FEATURE_CONNECTION_SHARING */
1566
1567 #ifdef FEATURE_FAST_REDIRECTS
1568    if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 1);
1569 #else /* ifndef FEATURE_FAST_REDIRECTS */
1570    if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 0);
1571 #endif /* ndef FEATURE_FAST_REDIRECTS */
1572
1573 #ifdef FEATURE_FORCE_LOAD
1574    if (!err) err = map_conditional(exports, "FEATURE_FORCE_LOAD", 1);
1575    if (!err) err = map(exports, "FORCE_PREFIX", 1, FORCE_PREFIX, 1);
1576 #else /* ifndef FEATURE_FORCE_LOAD */
1577    if (!err) err = map_conditional(exports, "FEATURE_FORCE_LOAD", 0);
1578    if (!err) err = map(exports, "FORCE_PREFIX", 1, "(none - disabled)", 1);
1579 #endif /* ndef FEATURE_FORCE_LOAD */
1580
1581 #ifdef FEATURE_GRACEFUL_TERMINATION
1582    if (!err) err = map_conditional(exports, "FEATURE_GRACEFUL_TERMINATION", 1);
1583 #else /* ifndef FEATURE_GRACEFUL_TERMINATION */
1584    if (!err) err = map_conditional(exports, "FEATURE_GRACEFUL_TERMINATION", 0);
1585 #endif /* ndef FEATURE_GRACEFUL_TERMINATION */
1586
1587 #ifdef FEATURE_IMAGE_BLOCKING
1588    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 1);
1589 #else /* ifndef FEATURE_IMAGE_BLOCKING */
1590    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 0);
1591 #endif /* ndef FEATURE_IMAGE_BLOCKING */
1592
1593 #ifdef FEATURE_IMAGE_DETECT_MSIE
1594    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_DETECT_MSIE", 1);
1595 #else /* ifndef FEATURE_IMAGE_DETECT_MSIE */
1596    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_DETECT_MSIE", 0);
1597 #endif /* ndef FEATURE_IMAGE_DETECT_MSIE */
1598
1599 #ifdef HAVE_RFC2553
1600    if (!err) err = map_conditional(exports, "FEATURE_IPV6_SUPPORT", 1);
1601 #else /* ifndef HAVE_RFC2553 */
1602    if (!err) err = map_conditional(exports, "FEATURE_IPV6_SUPPORT", 0);
1603 #endif /* ndef HAVE_RFC2553 */
1604
1605 #ifdef FEATURE_NO_GIFS
1606    if (!err) err = map_conditional(exports, "FEATURE_NO_GIFS", 1);
1607 #else /* ifndef FEATURE_NO_GIFS */
1608    if (!err) err = map_conditional(exports, "FEATURE_NO_GIFS", 0);
1609 #endif /* ndef FEATURE_NO_GIFS */
1610
1611 #ifdef FEATURE_PTHREAD
1612    if (!err) err = map_conditional(exports, "FEATURE_PTHREAD", 1);
1613 #else /* ifndef FEATURE_PTHREAD */
1614    if (!err) err = map_conditional(exports, "FEATURE_PTHREAD", 0);
1615 #endif /* ndef FEATURE_PTHREAD */
1616
1617 #ifdef FEATURE_STATISTICS
1618    if (!err) err = map_conditional(exports, "FEATURE_STATISTICS", 1);
1619 #else /* ifndef FEATURE_STATISTICS */
1620    if (!err) err = map_conditional(exports, "FEATURE_STATISTICS", 0);
1621 #endif /* ndef FEATURE_STATISTICS */
1622
1623 #ifdef FEATURE_TOGGLE
1624    if (!err) err = map_conditional(exports, "FEATURE_TOGGLE", 1);
1625 #else /* ifndef FEATURE_TOGGLE */
1626    if (!err) err = map_conditional(exports, "FEATURE_TOGGLE", 0);
1627 #endif /* ndef FEATURE_TOGGLE */
1628
1629 #ifdef FEATURE_TRUST
1630    if (!err) err = map_conditional(exports, "FEATURE_TRUST", 1);
1631 #else /* ifndef FEATURE_TRUST */
1632    if (!err) err = map_conditional(exports, "FEATURE_TRUST", 0);
1633 #endif /* ndef FEATURE_TRUST */
1634
1635 #ifdef FEATURE_ZLIB
1636    if (!err) err = map_conditional(exports, "FEATURE_ZLIB", 1);
1637 #else /* ifndef FEATURE_ZLIB */
1638    if (!err) err = map_conditional(exports, "FEATURE_ZLIB", 0);
1639 #endif /* ndef FEATURE_ZLIB */
1640
1641 #ifdef STATIC_PCRE
1642    if (!err) err = map_conditional(exports, "STATIC_PCRE", 1);
1643 #else /* ifndef STATIC_PCRE */
1644    if (!err) err = map_conditional(exports, "STATIC_PCRE", 0);
1645 #endif /* ndef STATIC_PCRE */
1646
1647 #ifdef STATIC_PCRS
1648    if (!err) err = map_conditional(exports, "STATIC_PCRS", 1);
1649 #else /* ifndef STATIC_PCRS */
1650    if (!err) err = map_conditional(exports, "STATIC_PCRS", 0);
1651 #endif /* ndef STATIC_PCRS */
1652
1653    return err;
1654 }
1655
1656
1657 /*********************************************************************
1658  *
1659  * Function    :  show_rcs
1660  *
1661  * Description :  Create a string with the rcs info for all sourcefiles
1662  *
1663  * Parameters  :  None
1664  *
1665  * Returns     :  A string, or NULL on out-of-memory.
1666  *
1667  *********************************************************************/
1668 static char *show_rcs(void)
1669 {
1670    char *result = strdup("");
1671    char buf[BUFFER_SIZE];
1672
1673    /* Instead of including *all* dot h's in the project (thus creating a
1674     * tremendous amount of dependencies), I will concede to declaring them
1675     * as extern's.  This forces the developer to add to this list, but oh well.
1676     */
1677
1678 #define SHOW_RCS(__x)              \
1679    {                               \
1680       extern const char __x[];     \
1681       snprintf(buf, sizeof(buf), " %s\n", __x);   \
1682       string_append(&result, buf); \
1683    }
1684
1685    /* In alphabetical order */
1686    SHOW_RCS(actions_h_rcs)
1687    SHOW_RCS(actions_rcs)
1688 #ifdef AMIGA
1689    SHOW_RCS(amiga_h_rcs)
1690    SHOW_RCS(amiga_rcs)
1691 #endif /* def AMIGA */
1692    SHOW_RCS(cgi_h_rcs)
1693    SHOW_RCS(cgi_rcs)
1694 #ifdef FEATURE_CGI_EDIT_ACTIONS
1695    SHOW_RCS(cgiedit_h_rcs)
1696    SHOW_RCS(cgiedit_rcs)
1697 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
1698    SHOW_RCS(cgisimple_h_rcs)
1699    SHOW_RCS(cgisimple_rcs)
1700 #ifdef __MINGW32__
1701    SHOW_RCS(cygwin_h_rcs)
1702 #endif
1703    SHOW_RCS(deanimate_h_rcs)
1704    SHOW_RCS(deanimate_rcs)
1705    SHOW_RCS(encode_h_rcs)
1706    SHOW_RCS(encode_rcs)
1707    SHOW_RCS(errlog_h_rcs)
1708    SHOW_RCS(errlog_rcs)
1709    SHOW_RCS(filters_h_rcs)
1710    SHOW_RCS(filters_rcs)
1711    SHOW_RCS(gateway_h_rcs)
1712    SHOW_RCS(gateway_rcs)
1713    SHOW_RCS(jbsockets_h_rcs)
1714    SHOW_RCS(jbsockets_rcs)
1715    SHOW_RCS(jcc_h_rcs)
1716    SHOW_RCS(jcc_rcs)
1717    SHOW_RCS(list_h_rcs)
1718    SHOW_RCS(list_rcs)
1719    SHOW_RCS(loadcfg_h_rcs)
1720    SHOW_RCS(loadcfg_rcs)
1721    SHOW_RCS(loaders_h_rcs)
1722    SHOW_RCS(loaders_rcs)
1723    SHOW_RCS(miscutil_h_rcs)
1724    SHOW_RCS(miscutil_rcs)
1725    SHOW_RCS(parsers_h_rcs)
1726    SHOW_RCS(parsers_rcs)
1727    SHOW_RCS(pcrs_rcs)
1728    SHOW_RCS(pcrs_h_rcs)
1729    SHOW_RCS(project_h_rcs)
1730    SHOW_RCS(ssplit_h_rcs)
1731    SHOW_RCS(ssplit_rcs)
1732    SHOW_RCS(urlmatch_h_rcs)
1733    SHOW_RCS(urlmatch_rcs)
1734 #ifdef _WIN32
1735 #ifndef _WIN_CONSOLE
1736    SHOW_RCS(w32log_h_rcs)
1737    SHOW_RCS(w32log_rcs)
1738    SHOW_RCS(w32res_h_rcs)
1739    SHOW_RCS(w32taskbar_h_rcs)
1740    SHOW_RCS(w32taskbar_rcs)
1741 #endif /* ndef _WIN_CONSOLE */
1742    SHOW_RCS(win32_h_rcs)
1743    SHOW_RCS(win32_rcs)
1744 #endif /* def _WIN32 */
1745
1746 #undef SHOW_RCS
1747
1748    return result;
1749
1750 }
1751
1752
1753 /*********************************************************************
1754  *
1755  * Function    :  cgi_show_file
1756  *
1757  * Description :  CGI function that shows the content of a
1758  *                configuration file.
1759  *
1760  * Parameters  :
1761  *          1  :  csp = Current client state (buffers, headers, etc...)
1762  *          2  :  rsp = http_response data structure for output
1763  *          3  :  parameters = map of cgi parameters
1764  *
1765  * CGI Parameters :
1766  *        file :  Which file to show.  Only first letter is checked,
1767  *                valid values are:
1768  *                - "a"ction file
1769  *                - "r"egex
1770  *                - "t"rust
1771  *                Default is to show menu and other information.
1772  *
1773  * Returns     :  JB_ERR_OK on success
1774  *                JB_ERR_MEMORY on out-of-memory error.  
1775  *
1776  *********************************************************************/
1777 static jb_err cgi_show_file(struct client_state *csp,
1778                             struct http_response *rsp,
1779                             const struct map *parameters)
1780 {
1781    unsigned i;
1782    const char * filename = NULL;
1783    char * file_description = NULL;
1784
1785    assert(csp);
1786    assert(rsp);
1787    assert(parameters);
1788
1789    switch (*(lookup(parameters, "file")))
1790    {
1791    case 'a':
1792       if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->actions_list[i])
1793       {
1794          filename = csp->actions_list[i]->filename;
1795          file_description = "Actions File";
1796       }
1797       break;
1798
1799    case 'f':
1800       if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->rlist[i])
1801       {
1802          filename = csp->rlist[i]->filename;
1803          file_description = "Filter File";
1804       }
1805       break;
1806
1807 #ifdef FEATURE_TRUST
1808    case 't':
1809       if (csp->tlist)
1810       {
1811          filename = csp->tlist->filename;
1812          file_description = "Trust File";
1813       }
1814       break;
1815 #endif /* def FEATURE_TRUST */
1816    }
1817
1818    if (NULL != filename)
1819    {
1820       struct map *exports;
1821       char *s;
1822       jb_err err;
1823       size_t length;
1824
1825       exports = default_exports(csp, "show-status");
1826       if (NULL == exports)
1827       {
1828          return JB_ERR_MEMORY;
1829       }
1830
1831       if ( map(exports, "file-description", 1, file_description, 1)
1832         || map(exports, "filepath", 1, html_encode(filename), 0) )
1833       {
1834          free_map(exports);
1835          return JB_ERR_MEMORY;
1836       }
1837
1838       err = load_file(filename, &s, &length);
1839       if (JB_ERR_OK != err)
1840       {
1841          if (map(exports, "contents", 1, "<h1>ERROR OPENING FILE!</h1>", 1))
1842          {
1843             free_map(exports);
1844             return JB_ERR_MEMORY;
1845          }
1846       }
1847       else
1848       {
1849          s = html_encode_and_free_original(s);
1850          if (NULL == s)
1851          {
1852             return JB_ERR_MEMORY;
1853          }
1854
1855          if (map(exports, "contents", 1, s, 0))
1856          {
1857             free_map(exports);
1858             return JB_ERR_MEMORY;
1859          }
1860       }
1861
1862       return template_fill_for_cgi(csp, "show-status-file", exports, rsp);
1863    }
1864
1865    return JB_ERR_CGI_PARAMS;
1866 }
1867
1868  
1869 /*********************************************************************
1870  *
1871  * Function    :  load_file
1872  *
1873  * Description :  Loads a file into a buffer.
1874  *
1875  * Parameters  :
1876  *          1  :  filename = Name of the file to be loaded.
1877  *          2  :  buffer   = Used to return the file's content.
1878  *          3  :  length   = Used to return the size of the file.
1879  *
1880  * Returns     :  JB_ERR_OK in case of success,
1881  *                JB_ERR_FILE in case of ordinary file loading errors
1882  *                            (fseek() and ftell() errors are fatal)
1883  *                JB_ERR_MEMORY in case of out-of-memory.
1884  *
1885  *********************************************************************/
1886 static jb_err load_file(const char *filename, char **buffer, size_t *length)
1887 {
1888    FILE *fp;
1889    long ret;
1890    jb_err err = JB_ERR_OK;
1891
1892    fp = fopen(filename, "rb");
1893    if (NULL == fp)
1894    {
1895       log_error(LOG_LEVEL_ERROR, "Failed to open %s: %E", filename);
1896       return JB_ERR_FILE;
1897    }
1898
1899    /* Get file length */
1900    if (fseek(fp, 0, SEEK_END))
1901    {
1902       log_error(LOG_LEVEL_FATAL,
1903          "Unexpected error while fseek()ing to the end of %s: %E",
1904          filename);
1905    }
1906    ret = ftell(fp);
1907    if (-1 == ret)
1908    {
1909       log_error(LOG_LEVEL_FATAL,
1910          "Unexpected ftell() error while loading %s: %E",
1911          filename);
1912    }
1913    *length = (size_t)ret;
1914
1915    /* Go back to the beginning. */
1916    if (fseek(fp, 0, SEEK_SET))
1917    {
1918       log_error(LOG_LEVEL_FATAL,
1919          "Unexpected error while fseek()ing to the beginning of %s: %E",
1920          filename);
1921    }
1922
1923    *buffer = (char *)zalloc(*length + 1);
1924    if (NULL == *buffer)
1925    {
1926       err = JB_ERR_MEMORY;
1927    }
1928    else if (!fread(*buffer, *length, 1, fp))
1929    {
1930       /*
1931        * May happen if the file size changes between fseek() and
1932        * fread(). If it does, we just log it and serve what we got.
1933        */
1934       log_error(LOG_LEVEL_ERROR,
1935          "Couldn't completely read file %s.", filename);
1936       err = JB_ERR_FILE;
1937    }
1938
1939    fclose(fp);
1940
1941    return err;
1942
1943 }
1944
1945
1946 /*
1947   Local Variables:
1948   tab-width: 3
1949   end:
1950 */