7bbfff1131b37ed268e1eca3a76c97916af318ea
[privoxy.git] / cgi.c
1 const char cgi_rcs[] = "$Id: cgi.c,v 1.22 2001/09/16 11:00:10 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
5  *
6  * Purpose     :  Declares functions to intercept request, generate
7  *                html or gif answers, and to compose HTTP resonses.
8  *                
9  *                Functions declared include:
10  * 
11  *
12  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
13  *                IJBSWA team.  http://ijbswa.sourceforge.net
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  * Revisions   :
38  *    $Log: cgi.c,v $
39  *    Revision 1.22  2001/09/16 11:00:10  jongfoster
40  *    New function alloc_http_response, for symmetry with free_http_response
41  *
42  *    Revision 1.21  2001/09/13 23:53:03  jongfoster
43  *    Support for both static and dynamically generated CGI pages.
44  *    Correctly setting Last-Modified: and Expires: HTTP headers.
45  *
46  *    Revision 1.20  2001/09/13 23:40:36  jongfoster
47  *    (Cosmetic only) Indentation correction
48  *
49  *    Revision 1.19  2001/09/13 23:31:25  jongfoster
50  *    Moving image data to cgi.c rather than cgi.h.
51  *
52  *    Revision 1.18  2001/08/05 16:06:20  jongfoster
53  *    Modifiying "struct map" so that there are now separate header and
54  *    "map_entry" structures.  This means that functions which modify a
55  *    map no longer need to return a pointer to the modified map.
56  *    Also, it no longer reverses the order of the entries (which may be
57  *    important with some advanced template substitutions).
58  *
59  *    Revision 1.17  2001/08/05 15:57:38  oes
60  *    Adapted finish_http_response to new list_to_text
61  *
62  *    Revision 1.16  2001/08/01 21:33:18  jongfoster
63  *    Changes to fill_template() that reduce memory usage without having
64  *    an impact on performance.  I also renamed some variables so as not
65  *    to clash with the C++ keywords "new" and "template".
66  *
67  *    Revision 1.15  2001/08/01 21:19:22  jongfoster
68  *    Moving file version information to a separate CGI page.
69  *
70  *    Revision 1.14  2001/08/01 00:19:03  jongfoster
71  *    New function: map_conditional() for an if-then-else syntax.
72  *    Changing to use new version of show_defines()
73  *
74  *    Revision 1.13  2001/07/30 22:08:36  jongfoster
75  *    Tidying up #defines:
76  *    - All feature #defines are now of the form FEATURE_xxx
77  *    - Permanently turned off WIN_GUI_EDIT
78  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
79  *
80  *    Revision 1.12  2001/07/29 18:47:05  jongfoster
81  *    Adding missing #include "loadcfg.h"
82  *
83  *    Revision 1.11  2001/07/18 17:24:37  oes
84  *    Changed to conform to new pcrs interface
85  *
86  *    Revision 1.10  2001/07/13 13:53:13  oes
87  *    Removed all #ifdef PCRS and related code
88  *
89  *    Revision 1.9  2001/06/29 21:45:41  oes
90  *    Indentation, CRLF->LF, Tab-> Space
91  *
92  *    Revision 1.8  2001/06/29 13:21:46  oes
93  *    - Cosmetics: renamed and reordered functions, variables,
94  *      texts, improved comments  etc
95  *
96  *    - Removed ij_untrusted_url() The relevant
97  *      info is now part of the "untrusted" page,
98  *      which is generated by filters.c:trust_url()
99  *
100  *    - Generators of content now call finish_http_response()
101  *      themselves, making jcc.c:chat() a little less
102  *      cluttered
103  *
104  *    - Removed obsolete "Pragma: no-cache" from our headers
105  *
106  *    - http_responses now know their head length
107  *
108  *    - fill_template now uses the new interface to pcrs, so that
109  *       - long jobs (like whole files) no longer have to be assembled
110  *         in a fixed size buffer
111  *       - the new T (trivial) option is used, and the replacement may
112  *         contain Perl syntax backrefs without confusing pcrs
113  *
114  *    - Introduced default_exports() which generates a set of exports
115  *      common to all CGIs and other content generators
116  *
117  *    - Introduced convenience function map_block_killer()
118  *
119  *    - Introduced convenience function make_menu()
120  *
121  *    - Introduced CGI-like function error_response() which generates
122  *      the "No such domain" and "Connect failed" messages using the
123  *      CGI platform
124  *
125  *    - cgi_show_url_info:
126  *      - adapted to new CGI features
127  *      - form and answers now generated from same template
128  *      - http:// prefix in URL now OK
129  *
130  *    - cgi_show_status:
131  *      - adapted to new CGI features
132  *      - no longer uses csp->init_proxy_args
133  *
134  *    - cgi_default:
135  *      - moved menu generation to make_menu()
136  *
137  *    - add_stats now writes single export map entries instead
138  *      of a fixed string
139  *
140  *    - Moved redirect_url() to filters.c
141  *
142  *    - Fixed mem leak in free_http_response(), map_block_killer(),
143  *
144  *    - Removed logentry from cancelled commit
145  *
146  *    Revision 1.7  2001/06/09 10:51:58  jongfoster
147  *    Changing "show URL info" handler to new style.
148  *    Changing BUFSIZ ==> BUFFER_SIZE
149  *
150  *    Revision 1.6  2001/06/07 23:05:19  jongfoster
151  *    Removing code related to old forward and ACL files.
152  *
153  *    Revision 1.5  2001/06/05 19:59:16  jongfoster
154  *    Fixing multiline character string (a GCC-only "feature"), and snprintf (it's _snprintf under VC++).
155  *
156  *    Revision 1.4  2001/06/04 10:41:52  swa
157  *    show version string of cgi.h and cgi.c
158  *
159  *    Revision 1.3  2001/06/03 19:12:16  oes
160  *    introduced new cgi handling
161  *
162  *    No revisions before 1.3
163  *
164  **********************************************************************/
165 \f
166
167 #include "config.h"
168
169 #include <stdio.h>
170 #include <sys/types.h>
171 #include <stdlib.h>
172 #include <ctype.h>
173 #include <string.h>
174 #include <assert.h>
175
176 #ifdef _WIN32
177 #define snprintf _snprintf
178 #endif /* def _WIN32 */
179
180 #include "project.h"
181 #include "cgi.h"
182 #include "list.h"
183 #include "encode.h"
184 #include "ssplit.h"
185 #include "jcc.h"
186 #include "filters.h"
187 #include "actions.h"
188 #include "errlog.h"
189 #include "miscutil.h"
190 #include "showargs.h"
191 #include "loadcfg.h"
192
193 const char cgi_h_rcs[] = CGI_H_VERSION;
194
195 const struct cgi_dispatcher cgi_dispatcher[] = {
196    { "show-status", 
197          11, cgi_show_status,  
198          "Show information about the current configuration" }, 
199    { "show-url-info",
200          13, cgi_show_url_info, 
201          "Show which actions apply to a URL and why"  },
202    { "show-version", 
203          12, cgi_show_version,  
204          "Show the source code version numbers" }, 
205    { "send-banner",
206          11, cgi_send_banner, 
207          "HIDE Send the transparent or \"Junkbuster\" gif" },
208    { "",
209          0, cgi_default,
210          "Junkbuster main page" },
211    { NULL, 0, NULL, NULL }
212 };
213
214
215 /*
216  * Some images
217  *
218  * Hint: You can encode your own GIFs like that:
219  * perl -e 'while (read STDIN, $c, 1) { printf("\\%.3o,", unpack("C", $c)); }'
220  */
221
222 const char image_junkbuster_gif_data[] =
223    "GIF89aD\000\013\000\360\000\000\000\000\000\377\377\377!"
224    "\371\004\001\000\000\001\000,\000\000\000\000D\000\013\000"
225    "\000\002a\214\217\251\313\355\277\000\200G&K\025\316hC\037"
226    "\200\234\230Y\2309\235S\230\266\206\372J\253<\3131\253\271"
227    "\270\215\342\254\013\203\371\202\264\334P\207\332\020o\266"
228    "N\215I\332=\211\312\3513\266:\026AK)\364\370\365aobr\305"
229    "\372\003S\275\274k2\354\254z\347?\335\274x\306^9\374\276"
230    "\037Q\000\000;";
231
232 const int image_junkbuster_gif_length = sizeof(image_junkbuster_gif_data) - 1;
233
234
235 const char image_blank_gif_data[] =
236    "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000"
237    "\000!\371\004\001\000\000\000\000,\000\000\000\000\001"
238    "\000\001\000\000\002\002D\001\000;";
239
240 const int image_blank_gif_length = sizeof(image_blank_gif_data) - 1;
241
242
243 /*********************************************************************
244  * 
245  * Function    :  dispatch_cgi
246  *
247  * Description :  Checks if a request URL has either the magical hostname
248  *                i.j.b or matches HOME_PAGE_URL/config/. If so, it parses
249  *                the (rest of the) path as a cgi name plus query string,
250  *                prepares a map that maps CGI parameter names to their values,
251  *                initializes the http_response struct, and calls the 
252  *                relevant CGI handler function.
253  *
254  * Parameters  :
255  *          1  :  csp = Current client state (buffers, headers, etc...)
256  *
257  * Returns     :  http_response if match, NULL if nonmatch or handler fail
258  *
259  *********************************************************************/
260 struct http_response *dispatch_cgi(struct client_state *csp)
261 {
262    char *argstring = NULL;
263    const struct cgi_dispatcher *d;
264    struct map *param_list;
265    struct http_response *rsp;
266
267    /*
268     * Should we intercept ?
269     */
270
271    /* Either the host matches CGI_PREFIX_HOST ..*/
272    if (0 == strcmpic(csp->http->host, CGI_PREFIX_HOST))
273    {
274       /* ..then the path will all be for us */
275       argstring = csp->http->path;
276    }
277    /* Or it's the host part HOME_PAGE_URL, and the path /config ? */
278    else if (   (0 == strcmpic(csp->http->host, HOME_PAGE_URL + 7 ))
279             && (0 == strncmpic(csp->http->path,"/config", 7))
280             && ((csp->http->path[7] == '/') || (csp->http->path[7] == '\0')))
281    {
282       /* then it's everything following "/config" */
283       argstring = csp->http->path + 7;
284    }
285    else
286    {
287       return NULL;
288    }
289
290    /* 
291     * This is a CGI call.
292     */
293
294    /* Get mem for response or fail*/
295    if (NULL == (rsp = alloc_http_response()))
296    {
297       return NULL;
298    }
299
300    /* Remove leading slash */
301    if (*argstring == '/')
302    {
303       argstring++;
304    }
305
306    log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path);
307    log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", 
308                             csp->ip_addr_str, csp->http->cmd); 
309
310    /* Find and start the right CGI function*/
311    for (d = cgi_dispatcher; d->handler; d++)
312    {
313       if (strncmp(argstring, d->name, d->name_length) == 0)
314       {
315          if (NULL == (param_list = 
316              parse_cgi_parameters(argstring + d->name_length)))
317          {
318             free_map(param_list);
319             free_http_response(rsp);
320             return(NULL);
321          }
322          if ((d->handler)(csp, rsp, param_list))
323          {
324             free_map(param_list);
325             free_http_response(rsp);
326             return(NULL);
327          }
328
329          free_map(param_list);
330          return(finish_http_response(rsp));
331       }
332    }
333
334    /* Can't get here, since cgi_default will match all requests */
335    free_http_response(rsp);
336    return(NULL);
337
338 }
339
340
341 /*********************************************************************
342  *
343  * Function    :  parse_cgi_parameters
344  *
345  * Description :  Parse a URL-encoded argument string into name/value
346  *                pairs and store them in a struct map list.
347  *
348  * Parameters  :
349  *          1  :  string = string to be parsed 
350  *
351  * Returns     :  pointer to param list, or NULL if out of memory.
352  *
353  *********************************************************************/
354 struct map *parse_cgi_parameters(char *argstring)
355 {
356    char *tmp, *p;
357    char *vector[BUFFER_SIZE];
358    int pairs, i;
359    struct map *cgi_params;
360
361    if (NULL == (cgi_params = new_map()))
362    {
363       return NULL;
364    }
365
366    if(*argstring == '?')
367    {
368       argstring++;
369    }
370    if (NULL == (tmp = strdup(argstring)))
371    {
372       free_map(cgi_params);
373       return NULL;
374    }
375
376    pairs = ssplit(tmp, "&", vector, SZ(vector), 1, 1);
377
378    for (i = 0; i < pairs; i++)
379    {
380       if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0'))
381       {
382          *p = '\0';
383          map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0);
384       }
385    }
386
387    free(tmp);
388    return(cgi_params);
389
390 }
391
392
393 /*********************************************************************
394  *
395  * Function    :  cgi_default
396  *
397  * Description :  CGI function that is called if no action was given.
398  *                Lists menu of available unhidden CGIs.
399  *               
400  * Parameters  :
401  *           1 :  csp = Current client state (buffers, headers, etc...)
402  *           2 :  rsp = http_response data structure for output
403  *           3 :  parameters = map of cgi parameters
404  *
405  * Returns     :  0
406  *
407  *********************************************************************/
408 int cgi_default(struct client_state *csp, struct http_response *rsp,
409                 struct map *parameters)
410 {
411    char *p;
412    char *tmp = NULL;
413    struct map * exports = default_exports(csp, "");
414
415    /* If there were other parameters, export a dump as "cgi-parameters" */
416    if(parameters)
417    {
418       p = dump_map(parameters);
419       tmp = strsav(tmp, "<p>What made you think this cgi takes parameters?\n"
420                         "Anyway, here they are, in case you're interested:</p>\n");
421       tmp = strsav(tmp, p);
422       map(exports, "cgi-parameters", 1, tmp, 0);
423       free(p);
424    }
425    else
426    {
427       map(exports, "cgi-parameters", 1, "", 1);
428    }
429
430    rsp->body = fill_template(csp, "default", exports);
431    free_map(exports);
432    return(0);
433
434 }
435
436
437 /*********************************************************************
438  *
439  * Function    :  cgi_send_banner
440  *
441  * Description :  CGI function that returns a banner. 
442  *
443  * Parameters  :
444  *           1 :  csp = Current client state (buffers, headers, etc...)
445  *           2 :  rsp = http_response data structure for output
446  *           3 :  parameters = map of cgi parameters
447  *
448  * CGI Parameters :
449  *           type : Selects the type of banner between "trans" and "jb".
450  *                  Defaults to "jb" if absent or != "trans".
451  *
452  * Returns     :  0
453  *
454  *********************************************************************/
455 int cgi_send_banner(struct client_state *csp, struct http_response *rsp,
456                     struct map *parameters)
457 {
458    if(strcmp(lookup(parameters, "type"), "trans"))
459    {
460       rsp->body = bindup(image_junkbuster_gif_data, image_junkbuster_gif_length);
461       rsp->content_length = image_junkbuster_gif_length;
462    }
463    else
464    {
465       rsp->body = bindup(image_blank_gif_data, image_blank_gif_length);
466       rsp->content_length = image_blank_gif_length;
467    }   
468
469    enlist(rsp->headers, "Content-Type: image/gif");
470    rsp->is_static = 1;
471
472    return(0);
473
474 }
475
476
477 /*********************************************************************
478  *
479  * Function    :  cgi_show_version
480  *
481  * Description :  CGI function that returns a a web page describing the
482  *                file versions of IJB.
483  *
484  * Parameters  :
485  *           1 :  csp = Current client state (buffers, headers, etc...)
486  *           2 :  rsp = http_response data structure for output
487  *           3 :  parameters = map of cgi parameters
488  *
489  * CGI Parameters :
490  *           type : Selects the type of banner between "trans" and "jb".
491  *                  Defaults to "jb" if absent or != "trans".
492  *
493  * Returns     :  0
494  *
495  *********************************************************************/
496 int cgi_show_version(struct client_state *csp, struct http_response *rsp,
497                      struct map *parameters)
498 {
499    struct map * exports = default_exports(csp, "show-version");
500
501    map(exports, "sourceversions", 1, show_rcs(), 0);  
502
503    rsp->body = fill_template(csp, "show-version", exports);
504    free_map(exports);
505    return(0);
506
507 }
508
509  
510 /*********************************************************************
511  *
512  * Function    :  cgi_show_status
513  *
514  * Description :  CGI function that returns a a web page describing the
515  *                current status of IJB.
516  *
517  * Parameters  :
518  *           1 :  csp = Current client state (buffers, headers, etc...)
519  *           2 :  rsp = http_response data structure for output
520  *           3 :  parameters = map of cgi parameters
521  *
522  * CGI Parameters :
523  *           type : Selects the type of banner between "trans" and "jb".
524  *                  Defaults to "jb" if absent or != "trans".
525  *
526  * Returns     :  0
527  *
528  *********************************************************************/
529 int cgi_show_status(struct client_state *csp, struct http_response *rsp,
530                     struct map *parameters)
531 {
532    char *s = NULL;
533    int i;
534
535    FILE * fp;
536    char buf[BUFFER_SIZE];
537    char * p;
538    const char * filename = NULL;
539    char * file_description = NULL;
540
541    struct map * exports = default_exports(csp, "show-status");
542
543    switch (*(lookup(parameters, "file")))
544    {
545    case 'p':
546       if (csp->actions_list)
547       {
548          filename = csp->actions_list->filename;
549          file_description = "Actions List";
550       }
551       break;
552
553    case 'r':
554       if (csp->rlist)
555       {
556          filename = csp->rlist->filename;
557          file_description = "Regex Filter List";
558       }
559       break;
560
561 #ifdef FEATURE_TRUST
562    case 't':
563       if (csp->tlist)
564       {
565          filename = csp->tlist->filename;
566          file_description = "Trust List";
567       }
568       break;
569 #endif /* def FEATURE_TRUST */
570    }
571
572    if (NULL != filename)
573    {
574       map(exports, "file-description", 1, file_description, 1);
575       map(exports, "filepath", 1, html_encode(filename), 0);
576
577       if ((fp = fopen(filename, "r")) == NULL)
578       {
579          map(exports, "content", 1, "<h1>ERROR OPENING FILE!</h1>", 1);
580       }
581       else
582       {
583          while (fgets(buf, sizeof(buf), fp))
584          {
585             p = html_encode(buf);
586             if (p)
587             {
588                s = strsav(s, p);
589                freez(p);
590                s = strsav(s, "<br>");
591             }
592          }
593          fclose(fp);
594          map(exports, "contents", 1, s, 0);
595       }
596       rsp->body = fill_template(csp, "show-status-file", exports);
597       free_map(exports);
598       return(0);
599
600    }
601
602    map(exports, "redirect-url", 1, REDIRECT_URL, 1);
603    
604    s = NULL;
605    for (i=0; i < Argc; i++)
606    {
607       s = strsav(s, Argv[i]);
608       s = strsav(s, " ");
609    }
610    map(exports, "invocation", 1, s, 0);
611
612    map(exports, "options", 1, csp->config->proxy_args, 1);
613    show_defines(exports);
614
615 #ifdef FEATURE_STATISTICS
616    add_stats(exports);
617 #else /* ndef FEATURE_STATISTICS */
618    map_block_killer(exports, "statistics");
619 #endif /* ndef FEATURE_STATISTICS */
620
621    if (csp->actions_list)
622    {
623       map(exports, "actions-filename", 1,  csp->actions_list->filename, 1);
624    }
625    else
626    {
627       map(exports, "actions-filename", 1, "None specified", 1);
628    }
629
630    if (csp->rlist)
631    {
632       map(exports, "re-filter-filename", 1,  csp->rlist->filename, 1);
633    }
634    else
635    {
636       map(exports, "re-filter-filename", 1, "None specified", 1);
637    }
638
639 #ifdef FEATURE_TRUST
640    if (csp->tlist)
641    {
642       map(exports, "trust-filename", 1,  csp->tlist->filename, 1);
643    }
644    else
645    {
646        map(exports, "trust-filename", 1, "None specified", 1);
647    }
648 #else
649    map_block_killer(exports, "trust-support");
650 #endif /* ndef FEATURE_TRUST */
651
652    rsp->body = fill_template(csp, "show-status", exports);
653    free_map(exports);
654    return(0);
655
656 }
657
658  
659  /*********************************************************************
660  *
661  * Function    :  cgi_show_url_info
662  *
663  * Description :  CGI function that determines and shows which actions
664  *                junkbuster will perform for a given url, and which
665  *                matches starting from the defaults have lead to that.
666  *
667  * Parameters  :
668  *           1 :  csp = Current client state (buffers, headers, etc...)
669  *           2 :  rsp = http_response data structure for output
670  *           3 :  parameters = map of cgi parameters
671  *
672  * CGI Parameters :
673  *            url : The url whose actions are to be determined.
674  *                  If url is unset, the url-given conditional will be
675  *                  set, so that all but the form can be suppressed in
676  *                  the template.
677  *
678  * Returns     :  0
679  *
680  *********************************************************************/
681 int cgi_show_url_info(struct client_state *csp, struct http_response *rsp,
682                       struct map *parameters)
683 {
684    char *url_param;
685    char *host = NULL;
686    struct map * exports = default_exports(csp, "show-url-info");
687
688    if (NULL == (url_param = strdup(lookup(parameters, "url"))) || *url_param == '\0')
689    {
690       map_block_killer(exports, "url-given");
691       map(exports, "url", 1, "", 1);
692    }
693    else
694    {
695       char *matches = NULL;
696       char *path;
697       char *s;
698       int port = 80;
699       int hits = 0;
700       struct file_list *fl;
701       struct url_actions *b;
702       struct url_spec url[1];
703       struct current_action_spec action[1];
704       
705       host = url_param;
706       host += (strncmp(url_param, "http://", 7)) ? 0 : 7;
707
708       map(exports, "url", 1, host, 1);
709       map(exports, "url-html", 1, html_encode(host), 0);
710
711       init_current_action(action);
712
713       s = current_action_to_text(action);
714       map(exports, "default", 1, s , 0);
715
716       if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
717       {
718          map(exports, "matches", 1, "none" , 1);
719          map(exports, "final", 1, lookup(exports, "default"), 1);
720
721          freez(url_param);
722          free_current_action(action);
723
724          rsp->body = fill_template(csp, "show-url-info", exports);
725          free_map(exports);
726
727          return 0;
728       }
729
730       s = strchr(host, '/');
731       if (s != NULL)
732       {
733          path = strdup(s);
734          *s = '\0';
735       }
736       else
737       {
738          path = strdup("");
739       }
740       s = strchr(host, ':');
741       if (s != NULL)
742       {
743          *s++ = '\0';
744          port = atoi(s);
745          s = NULL;
746       }
747
748       *url = dsplit(host);
749
750       /* if splitting the domain fails, punt */
751       if (url->dbuf == NULL)
752       {
753          map(exports, "matches", 1, "none" , 1);
754          map(exports, "final", 1, lookup(exports, "default"), 1);
755
756          freez(url_param);
757          freez(path);
758          free_current_action(action);
759
760          rsp->body = fill_template(csp, "show-url-info", exports);
761          free_map(exports);
762
763          return 0;
764       }
765
766       for (b = b->next; NULL != b; b = b->next)
767       {
768          if ((b->url->port == 0) || (b->url->port == port))
769          {
770             if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
771             {
772                if ((b->url->path == NULL) ||
773 #ifdef REGEX
774                   (regexec(b->url->preg, path, 0, NULL, 0) == 0)
775 #else
776                   (strncmp(b->url->path, path, b->url->pathlen) == 0)
777 #endif
778                )
779                {
780                   s = actions_to_text(b->action);
781                   matches = strsav(matches, "<b>{");
782                   matches = strsav(matches, s);
783                   matches = strsav(matches, " }</b><br>\n<code>");
784                   matches = strsav(matches, b->url->spec);
785                   matches = strsav(matches, "</code><br>\n<br>\n");
786                   freez(s);
787
788                   merge_current_action(action, b->action);
789                   hits++;
790                }
791             }
792          }
793       }
794
795       if (hits)
796       {
797          map(exports, "matches", 1, matches , 0);
798       }
799       else
800       {
801          map(exports, "matches", 1, "none", 1);
802       }
803       matches = NULL;
804
805       freez(url->dbuf);
806       freez(url->dvec);
807
808       freez(url_param);
809       freez(path);
810
811       s = current_action_to_text(action);
812       map(exports, "final", 1, s, 0);
813       s = NULL;
814
815       free_current_action(action);
816    }
817
818    rsp->body = fill_template(csp, "show-url-info", exports);
819    free_map(exports);
820    return 0;
821
822 }
823
824
825 /*********************************************************************
826  *
827  * Function    :  error_response
828  *
829  * Description :  returns an http_response that explains the reason
830  *                why a request failed.
831  *
832  * Parameters  :
833  *          1  :  csp = Current client state (buffers, headers, etc...)
834  *          2  :  templatename = Which template should be used for the answer
835  *          3  :  errno = system error number
836  *
837  * Returns     :  NULL if no memory, else http_response
838  *
839  *********************************************************************/
840 struct http_response *error_response(struct client_state *csp, const char *templatename, int err)
841 {
842    struct http_response *rsp;
843    struct map * exports = default_exports(csp, NULL);
844
845    if (NULL == (rsp = alloc_http_response()))
846    {
847       return NULL;
848    }  
849
850    map(exports, "host-html", 1, html_encode(csp->http->host), 0);
851    map(exports, "hostport", 1, csp->http->hostport, 1);
852    map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
853    map(exports, "path", 1, csp->http->path, 1);
854    map(exports, "path-html", 1, html_encode(csp->http->path), 0);
855    map(exports, "error", 1, safe_strerror(err), 0);
856    map(exports, "host-ip", 1, csp->http->host_ip_addr_str, 1);
857
858    rsp->body = fill_template(csp, templatename, exports);
859    free_map(exports);
860   
861    if (!strcmp(templatename, "no-such-domain"))
862    {
863       rsp->status = strdup("404 No such domain"); 
864    }
865    else if (!strcmp(templatename, "connect-failed"))
866    {
867       rsp->status = strdup("503 Connect failed");
868    }
869
870    return(finish_http_response(rsp));
871 }
872
873
874 /*********************************************************************
875  *
876  * Function    :  get_http_time
877  *
878  * Description :  Get the time in a format suitable for use in a
879  *                HTTP header - e.g.:
880  *                "Sun, 06 Nov 1994 08:49:37 GMT"
881  *
882  * Parameters  :  
883  *          1  :  time_offset = Time returned will be current time
884  *                              plus this number of seconds.
885  *          2  :  buf = Destination for result.  Must be long enough
886  *                      to hold 29 characters plus a trailing zero.
887  *
888  * Returns     :  N/A
889  *
890  *********************************************************************/
891 static void get_http_time(int time_offset, char * buf)
892 {
893    static const char day_names[7][4] =
894       { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
895    static const char month_names[12][4] =
896       { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
897         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
898
899    struct tm *t;
900    time_t current_time;
901
902    assert(buf);
903
904    time(&current_time); /* get current time */
905
906 /* FIXME: is this needed?  time() can't fail on Win32.  What about Linux?
907    if(current_time <= 0)
908    {
909       return NULL;
910    }
911 */
912
913    current_time += time_offset;
914
915    /* get and save the gmt */
916    t = gmtime(&current_time);
917
918    /* Format: "Sun, 06 Nov 1994 08:49:37 GMT" */
919    snprintf(buf, 30,
920       "%s, %02d %s %4d %02d:%02d:%02d GMT",
921       day_names[t->tm_wday],
922       t->tm_mday,
923       month_names[t->tm_mon],
924       t->tm_year + 1900,
925       t->tm_hour,
926       t->tm_min,
927       t->tm_sec
928       );
929    buf[32] = '\0';
930 }
931
932
933 /*********************************************************************
934  *
935  * Function    :  finish_http_response
936  *
937  * Description :  Fill in the missing headers in an http response,
938  *                and flatten the headers to an http head.
939  *
940  * Parameters  :
941  *          1  :  rsp = pointer to http_response to be processed
942  *
943  * Returns     :  http_response, or NULL on failiure
944  *
945  *********************************************************************/
946 struct http_response *finish_http_response(struct http_response *rsp)
947 {
948    char buf[BUFFER_SIZE];
949
950    /* 
951     * Fill in the HTTP Status
952     */
953    sprintf(buf, "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK");
954    enlist_first(rsp->headers, buf);
955
956    /* 
957     * Set the Content-Length
958     */
959    if (rsp->content_length == 0)
960    {
961       rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
962    }
963    sprintf(buf, "Content-Length: %d", rsp->content_length);
964    enlist(rsp->headers, buf);
965
966    /* 
967     * Fill in the default headers:
968     *
969     * Content-Type: default to text/html if not already specified.
970     * Date: set to current date/time.
971     * Last-Modified: set to date/time the page was last changed.
972     * Expires: set to date/time page next needs reloading.
973     * Cache-Control: set to "no-cache" if applicable.
974     * 
975     * See http://www.w3.org/Protocols/rfc2068/rfc2068
976     */
977    enlist_unique(rsp->headers, "Content-Type: text/html", 13);
978
979    if (rsp->is_static)
980    {
981       /*
982        * Set Expires to about 10 min into the future so it'll get reloaded
983        * occasionally, e.g. if IJB gets upgraded.
984        */
985
986       get_http_time(0, buf);
987       enlist_unique_header(rsp->headers, "Date", buf);
988
989       /* Some date in the past. */
990       enlist_unique_header(rsp->headers, "Last-Modified", "Sat, 17 Jun 2000 12:00:00 GMT");
991
992       get_http_time(10 * 60, buf); /* 10 * 60sec = 10 minutes */
993       enlist_unique_header(rsp->headers, "Expires", buf);
994    }
995    else
996    {
997       /*
998        * Compliant browsers should not cache this due to the "Cache-Control"
999        * setting.  However, to be certain, we also set both "Last-Modified"
1000        * and "Expires" to the current time.
1001        */
1002       enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
1003       get_http_time(0, buf);
1004       enlist_unique_header(rsp->headers, "Date", buf);
1005       enlist_unique_header(rsp->headers, "Last-Modified", buf);
1006       enlist_unique_header(rsp->headers, "Expires", buf);
1007    }
1008
1009
1010    /* 
1011     * Write the head
1012     */
1013    if (NULL == (rsp->head = list_to_text(rsp->headers)))
1014    {
1015       free_http_response(rsp);
1016       return(NULL);
1017    }
1018    rsp->head_length = strlen(rsp->head);
1019
1020    return(rsp);
1021
1022 }
1023
1024
1025 /*********************************************************************
1026  *
1027  * Function    :  alloc_http_response
1028  *
1029  * Description :  Allocates a new http_response structure.
1030  *
1031  * Parameters  :  N/A
1032  *
1033  * Returns     :  pointer to a new http_response, or NULL.
1034  *
1035  *********************************************************************/
1036 struct http_response * alloc_http_response(void)
1037 {
1038    return (struct http_response *) zalloc(sizeof(struct http_response));
1039 }
1040
1041
1042 /*********************************************************************
1043  *
1044  * Function    :  free_http_response
1045  *
1046  * Description :  Free the memory occupied by an http_response
1047  *                and its depandant structures.
1048  *
1049  * Parameters  :
1050  *          1  :  rsp = pointer to http_response to be freed
1051  *
1052  * Returns     :  N/A
1053  *
1054  *********************************************************************/
1055 void free_http_response(struct http_response *rsp)
1056 {
1057    if(rsp)
1058    {
1059       freez(rsp->status);
1060       freez(rsp->head);
1061       freez(rsp->body);
1062       destroy_list(rsp->headers);
1063       freez(rsp);
1064    }
1065
1066 }
1067
1068
1069 /*********************************************************************
1070  *
1071  * Function    :  fill_template
1072  *
1073  * Description :  CGI support function that loads a given HTML
1074  *                template from the confdir, and fills it in
1075  *                by replacing @name@ with value using pcrs,
1076  *                for each item in the output map.
1077  *
1078  * Parameters  :
1079  *           1 :  csp = Current client state (buffers, headers, etc...)
1080  *           3 :  template = name of the HTML template to be used
1081  *           2 :  exports = map with fill in symbol -> name pairs
1082  *
1083  * Returns     :  char * with filled out form, or NULL if failiure
1084  *
1085  *********************************************************************/
1086 char *fill_template(struct client_state *csp, const char *templatename, struct map *exports)
1087 {
1088    struct map_entry *m;
1089    pcrs_job *job;
1090    char buf[BUFFER_SIZE];
1091    char *tmp_out_buffer;
1092    char *file_buffer = NULL;
1093    int size;
1094    int error;
1095    FILE *fp;
1096
1097
1098    /*
1099     * Open template file or fail
1100     */
1101    snprintf(buf, BUFFER_SIZE, "%s/templates/%s", csp->config->confdir, templatename);
1102
1103    if(NULL == (fp = fopen(buf, "r")))
1104    {
1105       log_error(LOG_LEVEL_ERROR, "error loading template %s: %E", buf);
1106       return NULL;
1107    }
1108    
1109
1110    /* 
1111     * Read the file, ignoring comments
1112     */
1113    while (fgets(buf, BUFFER_SIZE, fp))
1114    {
1115       /* skip lines starting with '#' */
1116       if(*buf == '#') continue;
1117    
1118       file_buffer = strsav(file_buffer, buf);
1119    }
1120    fclose(fp);
1121
1122
1123    /*
1124     * Execute the jobs
1125     */
1126    size = strlen(file_buffer) + 1;
1127
1128    /* 
1129     * Assemble pcrs joblist from exports map
1130     */
1131    for (m = exports->first; m != NULL; m = m->next)
1132    {
1133       /* Enclose name in @@ */
1134       snprintf(buf, BUFFER_SIZE, "@%s@", m->name);
1135
1136       /* Make and run job. */
1137       job = pcrs_compile(buf, m->value, "sigTU",  &error);
1138       if (job == NULL) 
1139       {
1140          log_error(LOG_LEVEL_ERROR, "Error compiling template fill job %s: %d", m->name, error);
1141       }
1142       else
1143       {
1144          pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size);
1145          if (file_buffer != tmp_out_buffer)
1146          {
1147             free(file_buffer);
1148             file_buffer = tmp_out_buffer;
1149          }
1150          pcrs_free_job(job);
1151       }
1152    }
1153
1154
1155    /*
1156     * Return
1157     */
1158    return(file_buffer);
1159
1160 }
1161
1162
1163 /*********************************************************************
1164  *
1165  * Function    :  default_exports
1166  *
1167  * Description :  returns a struct map list that contains exports
1168  *                which are common to all CGI functions.
1169  *
1170  * Parameters  :
1171  *          1  :  exports = Structure to write output to.  This
1172  *                structure should be newly allocated and will be
1173  *                zeroed.
1174  *          1  :  csp = Current client state (buffers, headers, etc...)
1175  *          2  :  caller = name of CGI who calls us and which should
1176  *                         be excluded from the generated menu.
1177  * Returns     :  NULL if no memory, else map
1178  *
1179  *********************************************************************/
1180 struct map * default_exports(const struct client_state *csp, const char *caller)
1181 {
1182    char buf[20];
1183    struct map * exports = new_map();
1184
1185    map(exports, "version", 1, VERSION, 1);
1186    map(exports, "my-ip-address", 1, csp->my_ip_addr_str ? csp->my_ip_addr_str : "unknown", 1);
1187    map(exports, "my-hostname", 1, csp->my_hostname ? csp->my_hostname : "unknown", 1);
1188    map(exports, "admin-address", 1, csp->config->admin_address ? csp->config->admin_address : "fill@me.in.please", 1);
1189    map(exports, "homepage", 1, HOME_PAGE_URL, 1);
1190    map(exports, "default-cgi", 1, HOME_PAGE_URL "/config", 1);
1191    map(exports, "menu", 1, make_menu(caller), 0);
1192    map(exports, "code-status", 1, CODE_STATUS, 1);
1193
1194    snprintf(buf, 20, "%d", csp->config->hport);
1195    map(exports, "my-port", 1, buf, 1);
1196
1197    if(!strcmp(CODE_STATUS, "stable"))
1198    {
1199       map_block_killer(exports, "unstable");
1200    }
1201
1202    if(csp->config->proxy_info_url != NULL)
1203    {
1204       map(exports, "proxy-info-url", 1, csp->config->proxy_info_url, 1);
1205    }
1206    else
1207    {
1208       map_block_killer(exports, "have-proxy-info");
1209    }   
1210
1211    return (exports);
1212 }
1213
1214
1215 /*********************************************************************
1216  *
1217  * Function    :  map_block_killer
1218  *
1219  * Description :  Convenience function.
1220  *                Adds a "killer" for the conditional HTML-template
1221  *                block <name>, i.e. a substitution of the regex
1222  *                "if-<name>-start.*if-<name>-end" to the given
1223  *                export list.
1224  *
1225  * Parameters  :  
1226  *          1  :  exports = map to extend
1227  *          2  :  name = name of conditional block
1228  *
1229  * Returns     :  extended map
1230  *
1231  *********************************************************************/
1232 void map_block_killer(struct map *exports, const char *name)
1233 {
1234    char buf[1000]; /* Will do, since the names are hardwired */
1235
1236    snprintf(buf, 1000, "if-%s-start.*if-%s-end", name, name);
1237    map(exports, buf, 1, "", 1);
1238 }
1239
1240
1241 /*********************************************************************
1242  *
1243  * Function    :  map_conditional
1244  *
1245  * Description :  Convenience function.
1246  *                Adds an "if-then-else" for the conditional HTML-template
1247  *                block <name>, i.e. a substitution of the form:
1248  *                @if-<name>-then@
1249  *                   True text
1250  *                @else-not-<name>@
1251  *                   False text
1252  *                @endif-<name>@
1253  *
1254  *                The control structure and one of the alternatives
1255  *                will be hidden.
1256  *
1257  * Parameters  :  
1258  *          1  :  exports = map to extend
1259  *          2  :  name = name of conditional block
1260  *          3  :  choose_first = nonzero for first, zero for second.
1261  *
1262  * Returns     :  extended map
1263  *
1264  *********************************************************************/
1265 void map_conditional(struct map *exports, const char *name, int choose_first)
1266 {
1267    char buf[1000]; /* Will do, since the names are hardwired */
1268
1269    snprintf(buf, 1000, (choose_first
1270       ? "else-not-%s@.*@endif-%s"
1271       : "if-%s-then@.*@else-not-%s"),
1272       name, name);
1273    map(exports, buf, 1, "", 1);
1274
1275    snprintf(buf, 1000, (choose_first ? "if-%s-then" : "endif-%s"), name);
1276    map(exports, buf, 1, "", 1);
1277 }
1278
1279
1280 /*********************************************************************
1281  *
1282  * Function    :  make_menu
1283  *
1284  * Description :  Returns an HTML-formatted menu of the available 
1285  *                unhidden CGIs, excluding the one given in <self>.
1286  *
1287  * Parameters  :  self = name of CGI to leave out, can be NULL
1288  *
1289  * Returns     :  menu string
1290  *
1291  *********************************************************************/
1292 char *make_menu(const char *self)
1293 {
1294    const struct cgi_dispatcher *d;
1295    char buf[BUFFER_SIZE];
1296    char *result = NULL;
1297
1298    if (self == NULL)
1299    {
1300       self = "NO-SUCH-CGI!";
1301    }
1302
1303    /* List available unhidden CGI's and export as "other-cgis" */
1304    for (d = cgi_dispatcher; d->handler; d++)
1305    {
1306       if (strncmp(d->description, "HIDE", 4) && strcmp(d->name, self))
1307       {
1308          snprintf(buf, BUFFER_SIZE, "<li><a href=%s/config/%s>%s</a></li>\n",
1309                HOME_PAGE_URL, d->name, d->description);
1310          result = strsav(result, buf);
1311       }
1312    }
1313    return(result);
1314
1315 }
1316
1317
1318 /*********************************************************************
1319  *
1320  * Function    :  dump_map
1321  *
1322  * Description :  HTML-dump a map for debugging
1323  *
1324  * Parameters  :
1325  *          1  :  the_map = map to dump
1326  *
1327  * Returns     :  string with HTML
1328  *
1329  *********************************************************************/
1330 char *dump_map(const struct map *the_map)
1331 {
1332    struct map_entry *cur_entry = the_map->first;
1333    char *ret = NULL;
1334
1335    ret = strsav(ret, "<table>\n");
1336
1337    while (cur_entry)
1338    {
1339       ret = strsav(ret, "<tr><td><b>");
1340       ret = strsav(ret, cur_entry->name);
1341       ret = strsav(ret, "</b></td><td>");
1342       ret = strsav(ret, cur_entry->value);
1343       ret = strsav(ret, "</td></tr>\n");
1344       cur_entry = cur_entry->next;
1345    }
1346
1347    ret = strsav(ret, "</table>\n");
1348    return(ret);
1349
1350 }
1351
1352
1353 #ifdef FEATURE_STATISTICS
1354 /*********************************************************************
1355  *
1356  * Function    :  add_stats
1357  *
1358  * Description :  Add the blocking statistics to a given map.
1359  *
1360  * Parameters  :
1361  *          1  :  exports = map to write to.
1362  *
1363  * Returns     :  pointer to extended map
1364  *
1365  *********************************************************************/
1366 struct map *add_stats(struct map *exports)
1367 {
1368    float perc_rej;   /* Percentage of http requests rejected */
1369    char buf[1000];
1370    int local_urls_read     = urls_read;
1371    int local_urls_rejected = urls_rejected;
1372
1373    /*
1374     * Need to alter the stats not to include the fetch of this
1375     * page.
1376     *
1377     * Can't do following thread safely! doh!
1378     *
1379     * urls_read--;
1380     * urls_rejected--; * This will be incremented subsequently *
1381     */
1382
1383    if (local_urls_read == 0)
1384    {
1385       map_block_killer(exports, "have-stats");
1386    }
1387    else
1388    {
1389       map_block_killer(exports, "have-no-stats");
1390
1391       perc_rej = (float)local_urls_rejected * 100.0F /
1392             (float)local_urls_read;
1393
1394       sprintf(buf, "%d", local_urls_read);
1395       map(exports, "requests-received", 1, buf, 1);
1396
1397       sprintf(buf, "%d", local_urls_rejected);
1398       map(exports, "requests-blocked", 1, buf, 1);
1399
1400       sprintf(buf, "%6.2f", perc_rej);
1401       map(exports, "percent-blocked", 1, buf, 1);
1402    }
1403
1404    return(exports);
1405
1406 }
1407 #endif /* def FEATURE_STATISTICS */
1408
1409 /*
1410   Local Variables:
1411   tab-width: 3
1412   end:
1413 */