c6255526aec32db06986e050b6797aa3e4871f7c
[privoxy.git] / cgi.c
1 const char cgi_rcs[] = "$Id: $";
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  *
40  *
41  **********************************************************************/
42 \f
43
44 #include "config.h"
45
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <stdlib.h>
49 #include <ctype.h>
50 #include <string.h>
51
52 #include "project.h"
53 #include "cgi.h"
54 #include "list.h"
55 #include "pcrs.h"
56 #include "encode.h"
57 #include "ssplit.h"
58 #include "jcc.h"
59 #include "filters.h"
60 #include "actions.h"
61 #include "errlog.h"
62 #include "miscutil.h"
63 #include "showargs.h"
64
65
66 const struct cgi_dispatcher cgi_dispatchers[] = {
67    { "show-status", 
68          11, cgi_show_status,  
69          "Show information about the version and configuration" }, 
70 /* { "show-url-info",
71          13, cgi_show_url_info, 
72          "Show which actions apply to a URL and why"  },*/
73    { "send-banner",
74          11, cgi_send_banner, 
75          "HIDE Send the transparent or \"Junkbuster\" gif" },
76 #ifdef TRUST_FILES
77 /* { "untrusted-url",
78          15, ij_untrusted_url,
79               "HIDE Show why a URL was not trusted" }, */
80 #endif /* def TRUST_FILES */
81    { "",
82          0, cgi_default,
83          "HIDE Send a page linking to all unhidden CGIs" },
84    { NULL, 0, NULL, NULL }
85 };
86
87
88 /*********************************************************************
89  * 
90  * Function    :  dispatch_cgi
91  *
92  * Description :  Checks if a request URL has either the magical hostname
93  *                i.j.b or matches HOME_PAGE_URL/config/. If so, it parses
94  *                the (rest of the) path as a cgi name plus query string,
95  *                prepares a map that maps CGI parameter names to their values,
96  *                initializes the http_response struct, and calls the 
97  *                relevant CGI handler function.
98  *
99  * Parameters  :
100  *          1  :  csp = Current client state (buffers, headers, etc...)
101  *
102  * Returns     :  http_response if match, NULL if nonmatch or handler fail
103  *
104  *********************************************************************/
105 struct http_response *cgi_dispatch(struct client_state *csp)
106 {
107    char *argstring = NULL;
108    const struct cgi_dispatcher *d;
109    struct map *param_list;
110    struct http_response *response;
111
112    /*
113     * Should we intercept ?
114     */
115
116    /* Either the host matches CGI_PREFIX_HOST ..*/
117    if (0 == strcmpic(csp->http->host, CGI_PREFIX_HOST))
118    {
119       /* ..then the path will all be for us */
120       argstring = csp->http->path;
121    }
122    /* Or it's the host part of HOME_PAGE_URL ? */
123    else if (   (0 == strcmpic(csp->http->host, *&HOME_PAGE_URL + 7 ))
124             && (0 == strncmpic(csp->http->path,"/config", 7))
125             && ((csp->http->path[7] == '/') || (csp->http->path[7] == '\0')))
126    {
127       /* then it's everything following "/config" */
128       argstring = csp->http->path + 7;
129    }
130    else
131    {
132       return NULL;
133    }
134
135    /* 
136     * We have intercepted it.
137     */
138
139    /* Get mem for response */
140    if (NULL == ( response = zalloc(sizeof(*response))))
141    {
142       return NULL;
143    }
144
145    /* remove any leading slash */
146    if (*argstring == '/')
147    {
148       argstring++;
149    }
150
151    log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path);
152    log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", 
153                             csp->ip_addr_str, csp->http->cmd); 
154
155    for (d = cgi_dispatchers; d->handler; d++)
156    {
157       if (strncmp(argstring, d->name, d->name_length) == 0)
158       {
159          param_list = parse_cgi(argstring + d->name_length);
160          if ((d->handler)(csp, response, param_list))
161               {
162                  freez(response);
163               }
164
165               free_map(param_list);
166               return(response);
167       }
168    }
169
170    freez(response);
171    return(NULL);
172
173 }
174
175
176 /*********************************************************************
177  *
178  * Function    :  parse_cgi
179  *
180  * Description :  Parse a URL-encoded argument string into name/value
181  *                pairs and store them in a struct map list.
182  *
183  * Parameters  :
184  *          1  :  string = string to be parsed 
185  *
186  * Returns     :  poniter to param list, or NULL if failiure
187  *
188  *********************************************************************/
189 struct map *parse_cgi(char *argstring)
190 {
191    char *tmp, *p;
192    char *vector[BUFSIZ];
193    int pairs, i;
194    struct map *cgi_params = NULL;
195
196    if(*argstring == '?') argstring++;
197    tmp = strdup(argstring);
198
199    pairs = ssplit(tmp, "&", vector, SZ(vector), 1, 1);
200
201    for (i = 0; i < pairs; i++)
202    {
203       if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0'))
204       {
205          *p = '\0';
206          cgi_params = map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0);
207       }
208    }
209
210    free(tmp);
211    return(cgi_params);
212
213 }
214
215
216 /*********************************************************************
217  *
218  * Function    :  make_http_response
219  *
220  * Description :  Fill in the missing headers in an http response,
221  *                and flatten the headers to an http head.
222  *
223  * Parameters  :
224  *          1  :  rsp = pointer to http_response to be processed
225  *
226  * Returns     :  length of http head, or 0 on failiure
227  *
228  *********************************************************************/
229 int make_http_response(struct http_response *rsp)
230 {
231   char buf[BUFSIZ];
232
233   /* Fill in the HTTP Status */
234   sprintf(buf, "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK");
235   enlist_first(rsp->headers, buf);
236
237   /* Set the Content-Length */
238   if (rsp->content_length == 0)
239   {
240      rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
241   }
242
243
244   sprintf(buf, "Content-Length: %d", rsp->content_length);
245   enlist(rsp->headers, buf);
246
247   /* Fill in the default headers FIXME: Are these correct? sequence OK? check rfc! */
248   enlist_unique(rsp->headers, "Pragma: no-cache", 7);
249   enlist_unique(rsp->headers, "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT", 14);
250   enlist_unique(rsp->headers, "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT", 8);
251   enlist_unique(rsp->headers, "Content-Type: text/html", 13);
252   enlist(rsp->headers, "");
253   
254
255   /* Write the head */
256   if (NULL == (rsp->head = list_to_text(rsp->headers)))
257   {
258     free_http_response(rsp);
259     return(0);
260   }
261  
262   return(strlen(rsp->head));
263 }
264   
265
266 /*********************************************************************
267  *
268  * Function    :  free_http_response
269  *
270  * Description :  Free the memory occupied by an http_response
271  *                and its depandant structures.
272  *
273  * Parameters  :
274  *          1  :  rsp = pointer to http_response to be freed
275  *
276  * Returns     :  N/A
277  *
278  *********************************************************************/
279 void free_http_response(struct http_response *rsp)
280 {
281    if(rsp)
282    {
283       freez(rsp->status);
284       freez(rsp->head);
285       freez(rsp->body);
286       destroy_list(rsp->headers);
287    }
288 }
289
290
291 /*********************************************************************
292  *
293  * Function    :  fill_template
294  *
295  * Description :  CGI support function that loads a given HTML
296  *                template from the confdir, and fills it in
297  *                by replacing @name@ with value using pcrs,
298  *                for each item in the output map.
299  *
300  * Parameters  :
301  *           1 :  csp = Current client state (buffers, headers, etc...)
302  *           3 :  template = name of the HTML template to be used
303  *           2 :  answers = map with fill in symbol -> name pairs
304  *                FIXME: needs better name!
305  *
306  * Returns     :  char * with filled out form, or NULL if failiure
307  *
308  *********************************************************************/
309 char *fill_template(struct client_state *csp, char *template, struct map *answers)
310 {
311    struct map *m;
312    pcrs_job *job, *joblist = NULL;
313    char buf[BUFSIZ];
314    char *new, *old = NULL;
315    int error, size;
316    FILE *fp;
317
318    /*
319     * Open template file or fail
320     */
321    snprintf(buf, BUFSIZ, "%s/templates/%s", csp->config->confdir, template);
322
323    if(NULL == (fp = fopen(buf, "r")))
324         {
325            log_error(LOG_LEVEL_ERROR, "error loading template %s: %E", buf);
326       return NULL;
327         }
328         
329    /* 
330     * Assemble pcrs joblist from answers map
331     */
332    for (m = answers; m; m = m->next)
333         {
334            int error;
335
336            snprintf(buf, BUFSIZ, "s°@%s@°%s°ig", m->name, m->value);
337
338            if(NULL == (job = pcrs_make_job(buf, &error)))
339                 {
340                   log_error(LOG_LEVEL_ERROR, "Adding template fill job %s failed with error %d",
341                                                 buf, error);
342                   while ( NULL != (joblist = pcrs_free_job(joblist)) ) {};
343                   return NULL;
344                 }
345                 else
346                 {
347                    job->next = joblist;
348                         joblist = job;
349                 }
350         }
351
352    /* 
353     * Read the file, ignoring comments
354     */
355         while (fgets(buf, BUFSIZ, fp))
356         {
357       /* skip lines starting with '#' */
358            if(*buf == '#') continue;
359         
360       old = strsav(old, buf);
361         }
362         fclose(fp);
363
364    /*
365     * Execute the jobs
366     */
367         size = strlen(old) + 1;
368    new = old;
369
370    for (job = joblist; NULL != job; job = job->next)
371    {
372            pcrs_exec_substitution(job, old, size, &new, &size);
373       if (old != buf) free(old);
374       old=new;
375         }
376
377    /*
378     * Free the jobs & return
379     */
380    while ( NULL != (joblist = pcrs_free_job(joblist)) ) {};
381    return(new);
382
383 }
384
385
386 /*********************************************************************
387  *
388  * Function    :  dump_map
389  *
390  * Description :  HTML-dump a map for debugging
391  *
392  * Parameters  :
393  *          1  :  map = map to dump
394  *
395  * Returns     :  string with HTML
396  *
397  *********************************************************************/
398 char *dump_map(struct map *map)
399 {
400    struct map *p = map;
401    char *ret = NULL;
402
403
404    ret = strsav(ret, "<table>\n");
405
406    while (p)
407    {
408       ret = strsav(ret, "<tr><td><b>");
409       ret = strsav(ret, p->name);
410       ret = strsav(ret, "</b></td><td>");
411       ret = strsav(ret, p->value);
412       ret = strsav(ret, "</td></tr>\n");
413       p = p->next;
414    }
415
416    ret = strsav(ret, "</table>\n");
417    return(ret);
418 }
419
420
421 /*********************************************************************
422  *
423  * Function    :  cgi_default
424  *
425  * Description :  CGI function that is called if no action was given
426  *                lists menu of available unhidden CGIs.
427  *               
428  * Parameters  :
429  *           1 :  csp = Current client state (buffers, headers, etc...)
430  *           2 :  rsp = http_response data structure for output
431  *           3 :  parameters = map of cgi parameters
432  *
433  * Returns     :  0
434  *
435  *********************************************************************/
436 int cgi_default(struct client_state *csp, struct http_response *rsp,
437                 struct map *parameters)
438 {
439    char *p, *tmp = NULL;
440    char buf[BUFSIZ];
441    const struct cgi_dispatcher *d;
442         struct map *exports = NULL;
443
444    /* List available unhidden CGI's and export as "other-cgis" */
445    for (d = cgi_dispatchers; d->handler; d++)
446    {
447       if (strncmp(d->description, "HIDE", 4))
448            {
449          snprintf(buf, BUFSIZ, "<li><a href=%s/config/%s>%s</a></li>",
450                                   HOME_PAGE_URL, d->name, d->description);
451          tmp = strsav(tmp, buf);
452       }
453         }
454         exports = map(exports, "other-cgis", 1, tmp, 0);
455
456    /* If there were other parameters, export a dump as "cgi-parameters" */
457    if(parameters)
458         {
459       p = dump_map(parameters);
460            tmp = strsav(tmp, "<p>What made you think this cgi takes options?\n
461                          Anyway, here they are, in case you're interested:</p>\n");
462                 tmp = strsav(tmp, p);
463                 exports = map(exports, "cgi-parameters", 1, tmp, 0);
464       free(p);
465         }
466         else
467         {
468            exports = map(exports, "cgi-parameters", 1, "", 1);
469         }
470
471    rsp->body = fill_template(csp, "default", exports);
472
473    free_map(exports);
474    return(0);
475
476 }
477
478
479 /*********************************************************************
480  *
481  * Function    :  cgi_send_banner
482  *
483  * Description :  CGI function that returns a banner. 
484  *
485  * Parameters  :
486  *           1 :  csp = Current client state (buffers, headers, etc...)
487  *           2 :  rsp = http_response data structure for output
488  *           3 :  parameters = map of cgi parameters
489  *
490  * CGI Parameters :
491  *           type : Selects the type of banner between "trans" and "jb".
492  *                  Defaults to "jb" if absent or != "trans".
493  *
494  * Returns     :  0
495  *
496  *********************************************************************/
497 int cgi_send_banner(struct client_state *csp, struct http_response *rsp,
498                     struct map *parameters)
499 {
500    if(strcmp(lookup(parameters, "type"), "trans"))
501    {
502      rsp->body = bindup(CJBGIF, sizeof(CJBGIF));
503      rsp->content_length = sizeof(CJBGIF);
504    }
505    else
506    {
507      rsp->body = bindup(CBLANKGIF, sizeof(CBLANKGIF));
508      rsp->content_length = sizeof(CBLANKGIF);
509    }   
510
511    enlist(rsp->headers, "Content-Type: image/gif");
512
513    return(0);
514 }
515
516
517 #ifdef FAST_REDIRECTS
518 /*********************************************************************
519  *
520  * Function    :  redirect_url
521  *
522  * Description :  Checks for redirection URLs and returns a HTTP redirect
523  *                to the destination URL.
524  *
525  * Parameters  :
526  *          1  :  http = http_request request, check `basename's of blocklist
527  *          2  :  csp = Current client state (buffers, headers, etc...)
528  *
529  * Returns     :  NULL if URL was clean, HTTP redirect otherwise.
530  *
531  *********************************************************************/
532 char *redirect_url(struct http_request *http, struct client_state *csp)
533 {
534    char *p, *q;
535
536    p = q = csp->http->path;
537    log_error(LOG_LEVEL_REDIRECTS, "checking path: %s", p);
538
539    /* find the last URL encoded in the request */
540    while (p = strstr(p, "http://"))
541    {
542       q = p++;
543    }
544
545    /* if there was any, generate and return a HTTP redirect */
546    if (q != csp->http->path)
547    {
548       log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q);
549
550       p = (char *)malloc(strlen(HTTP_REDIRECT_TEMPLATE) + strlen(q));
551       sprintf(p, HTTP_REDIRECT_TEMPLATE, q);
552       return(p);
553    }
554    else
555    {
556       return(NULL);
557    }
558
559 }
560 #endif /* def FAST_REDIRECTS */
561
562
563
564 /*********************************************************************
565  *
566  * Function    :  cgi_show_status
567  *
568  * Description :  CGI function that returns a a web page describing the
569  *                current status of IJB.
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 :
577  *           type : Selects the type of banner between "trans" and "jb".
578  *                  Defaults to "jb" if absent or != "trans".
579  *
580  * Returns     :  0
581  *
582  *********************************************************************/
583 int cgi_show_status(struct client_state *csp, struct http_response *rsp,
584                     struct map *parameters)
585 {
586    char *s = NULL;
587    const struct gateway *g;
588    int i;
589
590    struct map *exports = NULL;
591
592 #ifdef SPLIT_PROXY_ARGS
593    FILE * fp;
594    char buf[BUFSIZ];
595    char * p;
596    const char * filename = NULL;
597    char * file_description = NULL;
598
599
600    p = lookup(parameters, "file");
601    switch (*p)
602    {
603    case 'p':
604       if (csp->actions_list)
605       {
606          filename = csp->actions_list->filename;
607          file_description = "Actions List";
608       }
609       break;
610    case 'f':
611       if (csp->flist)
612       {
613          filename = csp->flist->filename;
614          file_description = "Forward List";
615       }
616       break;
617
618 #ifdef ACL_FILES
619    case 'a':
620       if (csp->alist)
621       {
622          filename = csp->alist->filename;
623          file_description = "Access Control List";
624       }
625       break;
626 #endif /* def ACL_FILES */
627
628 #ifdef PCRS
629    case 'r':
630       if (csp->rlist)
631       {
632          filename = csp->rlist->filename;
633          file_description = "Regex Filter List";
634       }
635       break;
636 #endif /* def PCRS */
637
638 #ifdef TRUST_FILES
639    case 't':
640       if (csp->tlist)
641       {
642          filename = csp->tlist->filename;
643          file_description = "Trust List";
644       }
645       break;
646 #endif /* def TRUST_FILES */
647    }
648
649    if (NULL != filename)
650    {
651            exports = map(exports, "filename", 1, file_description, 1);
652       exports = map(exports, "filepath", 1, html_encode(filename), 0);
653
654       if ((fp = fopen(filename, "r")) == NULL)
655       {
656          exports = map(exports, "content", 1, "</pre><h1>ERROR OPENING FILE!</h1><pre>", 1);
657       }
658       else
659       {
660          while (fgets(buf, sizeof(buf), fp))
661          {
662             p = html_encode(buf);
663             if (p)
664             {
665                s = strsav(s, p);
666                freez(p);
667                s = strsav(s, "<br>");
668             }
669          }
670          fclose(fp);
671          exports = map(exports, "contents", 1, s, 0);
672      }
673          rsp->body = fill_template(csp, "show-status-file", exports);;
674          free_map(exports);
675          return(0);
676
677    }
678
679 #endif /* def SPLIT_PROXY_ARGS */
680
681    exports = map(exports, "redirect-url", 1, REDIRECT_URL, 1);
682    exports = map(exports, "version", 1, VERSION, 1);
683    exports = map(exports, "home-page", 1, HOME_PAGE_URL, 1);
684    exports = map(exports, "invocation-args", 1, csp->config->proxy_args_header, 1);
685    exports = map(exports, "gateways", 1, csp->config->proxy_args_gateways, 1);
686    exports = map(exports, "gateway-protocols", 1, s, 0);
687
688
689 #ifdef STATISTICS
690    exports = map(exports, "statistics", 1, add_stats(NULL), 0);
691 #else
692    exports = map(exports, "statistics", 1, "", 1);
693 #endif /* ndef STATISTICS */
694
695 #ifdef SPLIT_PROXY_ARGS
696    if (csp->actions_list)
697    {
698       exports = map(exports, "actions-filename", 1,  csp->actions_list->filename, 1);
699         }
700    else
701         {
702            exports = map(exports, "actions-filename", 1, "None specified", 1);
703         }
704
705    if (csp->flist)
706    {
707       exports = map(exports, "forward-filename", 1,  csp->flist->filename, 1);
708         }
709    else
710         {
711            exports = map(exports, "forward-filename", 1, "None specified", 1);
712         }
713
714 #ifdef ACL_FILES
715    if (csp->alist)
716    {
717       exports = map(exports, "acl-filename", 1,  csp->alist->filename, 1);
718         }
719    else
720         {
721            exports = map(exports, "acl-filename", 1, "None specified", 1);
722         }
723 #else
724    exports = map(exports, "acl-killer-start.*acl-killer-end", 1, "", 1);
725 #endif /* ndef ACL_FILES */
726
727 #ifdef PCRS
728    if (csp->rlist)
729    {
730       exports = map(exports, "re-filter-filename", 1,  csp->rlist->filename, 1);
731         }
732    else
733         {
734            exports = map(exports, "re-filter-filename", 1, "None specified", 1);
735         }
736 #else
737    exports = map(exports, "re-filter-killer-start.*re-filter-killer-end", 1, "", 1);
738 #endif /* ndef PCRS */
739
740 #ifdef TRUST_FILES
741    if (csp->tlist)
742    {
743       exports = map(exports, "trust-filename", 1,  csp->tlist->filename, 1);
744         }
745    else
746         {
747            exports = map(exports, "trust-filename", 1, "None specified", 1);
748         }
749 #else
750    exports = map(exports, "acl-killer-start.*acl-killer-end", 1, "", 1);
751 #endif /* ndef TRUST_FILES */
752
753    exports = map(exports, ".list", 1, "" , 1);
754
755 #else /* ifndef SPLIT_PROXY_ARGS */
756    exports = map(exports, "magic-eliminator-start.*magic-eliminator-end", 1, "", 1);
757
758    if (csp->clist)
759    {
760       map(exports, "clist", 1, csp->clist->proxy_args , 1);
761    }
762
763    if (csp->flist)
764    {
765       map(exports, "flist", 1, csp->flist->proxy_args , 1);
766         }
767
768 #ifdef ACL_FILES
769    if (csp->alist)
770    {
771       map(exports, "alist", 1, csp->alist->proxy_args , 1);
772         }
773 #endif /* def ACL_FILES */
774
775 #ifdef PCRS
776    if (csp->rlist)
777    {
778       map(exports, "rlist", 1, csp->rlist->proxy_args , 1);
779         }
780 #endif /* def PCRS */
781
782 #ifdef TRUST_FILES
783     if (csp->tlist)
784    {
785       map(exports, "tlist", 1, csp->tlist->proxy_args , 1);
786         }
787 #endif /* def TRUST_FILES */
788
789 #endif /* ndef SPLIT_PROXY_ARGS */
790
791         s = end_proxy_args(csp->config);
792    exports = map(exports, "rcs-and-defines", 1, s , 0);
793
794
795    rsp->body = fill_template(csp, "show-status", exports);
796    free_map(exports);
797    return(0);
798
799 }
800
801  
802  /*********************************************************************
803  *
804  * Function    :  cgi_show_url_info
805  *
806  * Description :  (please fill me in)
807  *
808  * Parameters  :
809  *          1  :  http = http_request request for crunched URL
810  *          2  :  csp = Current client state (buffers, headers, etc...)
811  *
812  * Returns     :  ???FIXME
813  *
814  *********************************************************************/
815 char *cgi_show_url_info(struct http_request *http, struct client_state *csp)
816 {
817    char * query_string = strchr(http->path, '?');
818    char * host = NULL;
819    
820    if (query_string != NULL)
821    {
822       query_string = url_decode(query_string + 1);
823       if (strncmpic(query_string, "url=", 4) == 0)
824       {
825          host = strdup(query_string + 4);
826       }
827       freez(query_string);
828    }
829    if (host != NULL)
830    {
831       char * result;
832       char * path;
833       char * s;
834       int port = 80;
835       struct file_list *fl;
836       struct url_actions *b;
837       struct url_spec url[1];
838       struct current_action_spec action[1];
839
840       init_current_action(action);
841
842       result = (char *)malloc(sizeof(C_URL_INFO_HEADER) + 2 * strlen(host));
843       sprintf(result, C_URL_INFO_HEADER, host, host);
844
845       s = current_action_to_text(action);
846       result = strsav(result, "<h3>Defaults:</h3>\n<p><b>{");
847       result = strsav(result, s);
848       result = strsav(result, " }</b></p>\n<h3>Patterns affecting the URL:</h3>\n<p>\n");
849       freez(s);
850
851       s = strchr(host, '/');
852       if (s != NULL)
853       {
854          path = strdup(s);
855          *s = '\0';
856       }
857       else
858       {
859          path = strdup("");
860       }
861       s = strchr(host, ':');
862       if (s != NULL)
863       {
864          *s++ = '\0';
865          port = atoi(s);
866       }
867
868       if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
869       {
870          freez(host);
871          freez(path);
872          result = strsav(result, C_URL_INFO_FOOTER);
873          return result;
874       }
875
876       *url = dsplit(host);
877
878       /* if splitting the domain fails, punt */
879       if (url->dbuf == NULL)
880       {
881          freez(host);
882          freez(path);
883          result = strsav(result, C_URL_INFO_FOOTER);
884          return result;
885       }
886
887       for (b = b->next; NULL != b; b = b->next)
888       {
889          if ((b->url->port == 0) || (b->url->port == port))
890          {
891             if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
892             {
893                if ((b->url->path == NULL) ||
894 #ifdef REGEX
895                   (regexec(b->url->preg, path, 0, NULL, 0) == 0)
896 #else
897                   (strncmp(b->url->path, path, b->url->pathlen) == 0)
898 #endif
899                )
900                {
901                   s = actions_to_text(b->action);
902                   result = strsav(result, "<b>{");
903                   result = strsav(result, s);
904                   result = strsav(result, " }</b><br>\n<code>");
905                   result = strsav(result, b->url->spec);
906                   result = strsav(result, "</code><br>\n<br>\n");
907                   freez(s);
908
909                   merge_current_action(action, b->action);
910                }
911             }
912          }
913       }
914
915       freez(url->dbuf);
916       freez(url->dvec);
917
918       freez(host);
919       freez(path);
920
921       s = current_action_to_text(action);
922       result = strsav(result, "</p>\n<h2>Final Results:</h2>\n<p><b>{");
923       result = strsav(result, s);
924       result = strsav(result, " }</b><br>\n<br>\n");
925       freez(s);
926
927       free_current_action(action);
928
929       result = strsav(result, C_URL_INFO_FOOTER);
930       return result;
931    }
932    else
933    {
934       return strdup(C_URL_INFO_FORM);
935    }
936 }
937
938
939
940 #ifdef TRUST_FILES
941 /*********************************************************************
942  *
943  * Function    :  ij_untrusted_url
944  *
945  * Description :  This "crunch"es "http:/any.thing/ij-untrusted-url" and
946  *                returns a web page describing why it was untrusted.
947  *
948  * Parameters  :
949  *          1  :  http = http_request request for crunched URL
950  *          2  :  csp = Current client state (buffers, headers, etc...)
951  *
952  * Returns     :  A string that contains why this was untrusted.
953  *
954  *********************************************************************/
955 char *ij_untrusted_url(struct http_request *http, struct client_state *csp)
956 {
957    int n;
958    char *hostport, *path, *refer, *p, *v[9];
959    char buf[BUFSIZ];
960    struct url_spec **tl, *t;
961
962
963    static const char format[] =
964       "HTTP/1.0 200 OK\r\n"
965       "Pragma: no-cache\n"
966       "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
967       "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
968       "Content-Type: text/html\n\n"
969       "<html>\n"
970       "<head>\n"
971       "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
972       "</head>\n"
973       BODY
974       "<center><h1>"
975       BANNER
976       "</h1></center>"
977       "The " BANNER " Proxy "
978       "<A href=\"" HOME_PAGE_URL "\">"
979       "(" HOME_PAGE_URL ") </A>"
980       "intercepted the request for %s%s\n"
981       "because the URL is not trusted.\n"
982       "<br><br>\n";
983
984    if ((n = ssplit(http->path, "?+", v, SZ(v), 0, 0)) == 4)
985    {
986       hostport = url_decode(v[1]);
987       path     = url_decode(v[2]);
988       refer    = url_decode(v[3]);
989    }
990    else
991    {
992       hostport = strdup("undefined_host");
993       path     = strdup("/undefined_path");
994       refer    = strdup("undefined");
995    }
996
997    n  = sizeof(format);
998    n += strlen(hostport);
999    n += strlen(path    );
1000
1001    if ((p = (char *)malloc(n)))
1002    {
1003       sprintf(p, format, hostport, path);
1004    }
1005
1006    strsav(p, "The referrer in this request was <strong>");
1007    strsav(p, refer);
1008    strsav(p, "</strong><br>\n");
1009
1010    freez(hostport);
1011    freez(path    );
1012    freez(refer   );
1013
1014    p = strsav(p, "<h3>The following referrers are trusted</h3>\n");
1015
1016    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
1017    {
1018       sprintf(buf, "%s<br>\n", t->spec);
1019       p = strsav(p, buf);
1020    }
1021
1022    if (csp->config->trust_info->next)
1023    {
1024       struct list *l;
1025
1026       strcpy(buf,
1027          "<p>"
1028          "You can learn more about what this means "
1029          "and what you may be able to do about it by "
1030          "reading the following documents:<br>\n"
1031          "<ol>\n"
1032       );
1033
1034       p = strsav(p, buf);
1035
1036       for (l = csp->config->trust_info->next; l ; l = l->next)
1037       {
1038          sprintf(buf,
1039             "<li> <a href=%s>%s</a><br>\n",
1040                l->str, l->str);
1041          p = strsav(p, buf);
1042       }
1043
1044       p = strsav(p, "</ol>\n");
1045    }
1046
1047    p = strsav(p, "</body>\n" "</html>\n");
1048
1049    return(p);
1050
1051 }
1052 #endif /* def TRUST_FILES */
1053
1054
1055 #ifdef STATISTICS
1056 /*********************************************************************
1057  *
1058  * Function    :  add_stats
1059  *
1060  * Description :  Statistics function of JB.  Called by `show_proxy_args'.
1061  *
1062  * Parameters  :
1063  *          1  :  s = string that holds the proxy args description page
1064  *
1065  * Returns     :  A pointer to the descriptive status web page.
1066  *
1067  *********************************************************************/
1068 char *add_stats(char *s)
1069 {
1070    /*
1071     * Output details of the number of requests rejected and
1072     * accepted. This is switchable in the junkbuster config.
1073     * Does nothing if this option is not enabled.
1074     */
1075
1076    float perc_rej;   /* Percentage of http requests rejected */
1077    char out_str[81];
1078    int local_urls_read     = urls_read;
1079    int local_urls_rejected = urls_rejected;
1080
1081    /*
1082     * Need to alter the stats not to include the fetch of this
1083     * page.
1084     *
1085     * Can't do following thread safely! doh!
1086     *
1087     * urls_read--;
1088     * urls_rejected--; * This will be incremented subsequently *
1089     */
1090
1091    s = strsav(s,"<h2>Statistics for this " BANNER ":</h2>\n");
1092
1093    if (local_urls_read == 0)
1094    {
1095
1096       s = strsav(s,"No activity so far!\n");
1097
1098    }
1099    else
1100    {
1101
1102       perc_rej = (float)local_urls_rejected * 100.0F /
1103             (float)local_urls_read;
1104
1105       sprintf(out_str,
1106          "%d requests received, %d filtered "
1107          "(%6.2f %%).",
1108          local_urls_read, 
1109          local_urls_rejected, perc_rej);
1110
1111       s = strsav(s,out_str);
1112    }
1113
1114    return(s);
1115 }
1116 #endif /* def STATISTICS */
1117
1118 /*
1119   Local Variables:
1120   tab-width: 3
1121   end:
1122 */