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