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