Added constants for anchoring selection bitmap
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.13 2001/05/31 21:21:30 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *                Functions declared include:
8  *                   `acl_addr', `add_stats', `block_acl', `block_imageurl',
9  *                   `block_url', `url_actions', `domaincmp', `dsplit',
10  *                   `filter_popups', `forward_url', 'redirect_url',
11  *                   `ij_untrusted_url', `intercept_url', `re_process_buffer',
12  *                   `show_proxy_args', 'ijb_send_banner', and `trust_url'
13  *
14  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
15  *                IJBSWA team.  http://ijbswa.sourceforge.net
16  *
17  *                Based on the Internet Junkbuster originally written
18  *                by and Copyright (C) 1997 Anonymous Coders and 
19  *                Junkbusters Corporation.  http://www.junkbusters.com
20  *
21  *                This program is free software; you can redistribute it 
22  *                and/or modify it under the terms of the GNU General
23  *                Public License as published by the Free Software
24  *                Foundation; either version 2 of the License, or (at
25  *                your option) any later version.
26  *
27  *                This program is distributed in the hope that it will
28  *                be useful, but WITHOUT ANY WARRANTY; without even the
29  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
30  *                PARTICULAR PURPOSE.  See the GNU General Public
31  *                License for more details.
32  *
33  *                The GNU General Public License should be included with
34  *                this file.  If not, you can view it at
35  *                http://www.gnu.org/copyleft/gpl.html
36  *                or write to the Free Software Foundation, Inc., 59
37  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
38  *
39  * Revisions   :
40  *    $Log: filters.c,v $
41  *    Revision 1.13  2001/05/31 21:21:30  jongfoster
42  *    Permissionsfile / actions file changes:
43  *    - Changed "permission" to "action" throughout
44  *    - changes to file format to allow string parameters
45  *    - Moved helper functions to actions.c
46  *
47  *    Revision 1.12  2001/05/31 17:35:20  oes
48  *
49  *     - Enhanced domain part globbing with infix and prefix asterisk
50  *       matching and optional unanchored operation
51  *
52  *    Revision 1.11  2001/05/29 11:53:23  oes
53  *    "See why" link added to "blocked" page
54  *
55  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
56  *    Unified blocklist/imagelist/permissionslist.
57  *    File format is still under discussion, but the internal changes
58  *    are (mostly) done.
59  *
60  *    Also modified interceptor behaviour:
61  *    - We now intercept all URLs beginning with one of the following
62  *      prefixes (and *only* these prefixes):
63  *        * http://i.j.b/
64  *        * http://ijbswa.sf.net/config/
65  *        * http://ijbswa.sourceforge.net/config/
66  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
67  *    - Internal changes so that intercepted and fast redirect pages
68  *      are not replaced with an image.
69  *    - Interceptors now have the option to send a binary page direct
70  *      to the client. (i.e. ijb-send-banner uses this)
71  *    - Implemented show-url-info interceptor.  (Which is why I needed
72  *      the above interceptors changes - a typical URL is
73  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
74  *      The previous mechanism would not have intercepted that, and
75  *      if it had been intercepted then it then it would have replaced
76  *      it with an image.)
77  *
78  *    Revision 1.9  2001/05/27 22:17:04  oes
79  *
80  *    - re_process_buffer no longer writes the modified buffer
81  *      to the client, which was very ugly. It now returns the
82  *      buffer, which it is then written by chat.
83  *
84  *    - content_length now adjusts the Content-Length: header
85  *      for modified documents rather than crunch()ing it.
86  *      (Length info in csp->content_length, which is 0 for
87  *      unmodified documents)
88  *
89  *    - For this to work, sed() is called twice when filtering.
90  *
91  *    Revision 1.8  2001/05/26 17:13:28  jongfoster
92  *    Filled in a function comment.
93  *
94  *    Revision 1.7  2001/05/26 15:26:15  jongfoster
95  *    ACL feature now provides more security by immediately dropping
96  *    connections from untrusted hosts.
97  *
98  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
99  *    Automatic reloading of config file.
100  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
101  *    Most of the global variables have been moved to a new
102  *    struct configuration_spec, accessed through csp->config->globalname
103  *    Most of the globals remaining are used by the Win32 GUI.
104  *
105  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
106  *    Hard tabs->Spaces
107  *
108  *    Revision 1.4  2001/05/22 18:46:04  oes
109  *
110  *    - Enabled filtering banners by size rather than URL
111  *      by adding patterns that replace all standard banner
112  *      sizes with the "Junkbuster" gif to the re_filterfile
113  *
114  *    - Enabled filtering WebBugs by providing a pattern
115  *      which kills all 1x1 images
116  *
117  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
118  *      which is selected by the (nonstandard and therefore
119  *      capital) letter 'U' in the option string.
120  *      It causes the quantifiers to be ungreedy by default.
121  *      Appending a ? turns back to greedy (!).
122  *
123  *    - Added a new interceptor ijb-send-banner, which
124  *      sends back the "Junkbuster" gif. Without imagelist or
125  *      MSIE detection support, or if tinygif = 1, or the
126  *      URL isn't recognized as an imageurl, a lame HTML
127  *      explanation is sent instead.
128  *
129  *    - Added new feature, which permits blocking remote
130  *      script redirects and firing back a local redirect
131  *      to the browser.
132  *      The feature is conditionally compiled, i.e. it
133  *      can be disabled with --disable-fast-redirects,
134  *      plus it must be activated by a "fast-redirects"
135  *      line in the config file, has its own log level
136  *      and of course wants to be displayed by show-proxy-args
137  *      Note: Boy, all the #ifdefs in 1001 locations and
138  *      all the fumbling with configure.in and acconfig.h
139  *      were *way* more work than the feature itself :-(
140  *
141  *    - Because a generic redirect template was needed for
142  *      this, tinygif = 3 now uses the same.
143  *
144  *    - Moved GIFs, and other static HTTP response templates
145  *      to project.h
146  *
147  *    - Some minor fixes
148  *
149  *    - Removed some >400 CRs again (Jon, you really worked
150  *      a lot! ;-)
151  *
152  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
153  *    Removing last hardcoded JunkBusters.com URLs.
154  *
155  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
156  *    Version 2.9.4 checkin.
157  *    - Merged popupfile and cookiefile, and added control over PCRS
158  *      filtering, in new "permissionsfile".
159  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
160  *      file error you now get a message box (in the Win32 GUI) rather
161  *      than the program exiting with no explanation.
162  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
163  *      skipping.
164  *    - Removed tabs from "config"
165  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
166  *    - Bumped up version number.
167  *
168  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
169  *    Initial import of version 2.9.3 source tree
170  *
171  *
172  *********************************************************************/
173 \f
174
175 #include "config.h"
176
177 #include <stdio.h>
178 #include <sys/types.h>
179 #include <stdlib.h>
180 #include <ctype.h>
181 #include <string.h>
182
183 #ifndef _WIN32
184 #include <unistd.h>
185 #include <netinet/in.h>
186 #else
187 #include <winsock2.h>
188 #endif
189
190 #include "project.h"
191 #include "filters.h"
192 #include "encode.h"
193 #include "jcc.h"
194 #include "showargs.h"
195 #include "parsers.h"
196 #include "ssplit.h"
197 #include "gateway.h"
198 #include "jbsockets.h"
199 #include "errlog.h"
200 #include "jbsockets.h"
201 #include "miscutil.h"
202 #include "actions.h"
203
204 #ifdef _WIN32
205 #include "win32.h"
206 #endif
207
208 const char filters_h_rcs[] = FILTERS_H_VERSION;
209
210 /* Fix a problem with Solaris.  There should be no effect on other
211  * platforms.
212  * Solaris's isspace() is a macro which uses it's argument directly
213  * as an array index.  Therefore we need to make sure that high-bit
214  * characters generate +ve values, and ideally we also want to make
215  * the argument match the declared parameter type of "int".
216  */
217 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
218
219
220 static const char CBLOCK[] = 
221 #ifdef AMIGA 
222        "HTTP/1.0 403 Request for blocked URL\n" 
223 #else /* ifndef AMIGA */
224        "HTTP/1.0 202 Request for blocked URL\n"
225 #endif /* ndef AMIGA */
226        "Pragma: no-cache\n"
227        "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
228        "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
229        "Content-Type: text/html\n\n"
230        "<html>\n"
231        "<head>\n"
232        "<title>Internet Junkbuster: Request for blocked URL</title>\n"
233        "</head>\n"
234        WHITEBG
235        "<center><h1>"
236        BANNER
237        "</h1></center>\n"
238       "<p align=center>Your request for <b>%s%s</b>\n"
239       "was blocked.<br><a href=\"http://i.j.b/show-url-info?url=%s%s\">See why</a>"
240 #ifdef FORCE_LOAD
241        " or <a href=\"http://%s" FORCE_PREFIX "%s\">"
242        "go there anyway.</a>"
243 #endif /* def FORCE_LOAD */
244       "</p>\n"
245       "</body>\n"
246       "</html>\n";
247
248 #ifdef TRUST_FILES
249 static const char CTRUST[] =
250 #ifdef AMIGA 
251        "HTTP/1.0 403 Request for untrusted URL\n"
252 #else /* ifndef AMIGA */
253        "HTTP/1.0 202 Request for untrusted URL\n"
254 #endif /* ndef AMIGA */
255        "Pragma: no-cache\n"
256        "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
257        "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
258        "Content-Type: text/html\n\n"
259        "<html>\n"
260        "<head>\n"
261        "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
262        "</head>\n"
263        WHITEBG
264        "<center>"
265        "<a href=http://i.j.b/ij-untrusted-url?%s+%s+%s>"
266        BANNER
267        "</a>"
268        "</center>"
269        "</body>\n"
270        "</html>\n";
271 #endif /* def TRUST_FILES */
272
273
274 #ifdef ACL_FILES
275 /*********************************************************************
276  *
277  * Function    :  block_acl
278  *
279  * Description :  Block this request?
280  *                Decide yes or no based on ACL file.
281  *
282  * Parameters  :
283  *          1  :  dst = The proxy or gateway address this is going to.
284  *                      Or NULL to check all possible targets.
285  *          2  :  csp = Current client state (buffers, headers, etc...)
286  *                      Also includes the client IP address.
287  *
288  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
289  *
290  *********************************************************************/
291 int block_acl(struct access_control_addr *dst,
292               struct client_state *csp)
293 {
294    struct file_list *fl;
295    struct access_control_list *a, *acl;
296
297    /* if not using an access control list, then permit the connection */
298    if (((fl = csp->alist) == NULL) || 
299        ((acl = (struct access_control_list *) fl->f) == NULL))
300    {
301       return(0);
302    }
303
304    /* search the list */
305    for (a = acl->next ; a ; a = a->next)
306    {
307       if ((csp->ip_addr_long & a->src->mask) == a->src->addr)
308       {
309          if (dst == NULL)
310          {
311             /* Just want to check if they have any access */
312             if (a->action == ACL_PERMIT)
313             {
314                return(0);
315             }
316          }
317          else if ( ((dst->addr & a->dst->mask) == a->dst->addr)
318            && ((dst->port == a->dst->port) || (a->dst->port == 0)))
319          {
320             if (a->action == ACL_PERMIT)
321             {
322                return(0);
323             }
324             else
325             {
326                return(1);
327             }
328          }
329       }
330    }
331
332    return(1);
333
334 }
335
336
337 /*********************************************************************
338  *
339  * Function    :  acl_addr
340  *
341  * Description :  Called from `load_aclfile' to parse an ACL address.
342  *
343  * Parameters  :
344  *          1  :  aspec = String specifying ACL address.
345  *          2  :  aca = struct access_control_addr to fill in.
346  *
347  * Returns     :  0 => Ok, everything else is an error.
348  *
349  *********************************************************************/
350 int acl_addr(char *aspec, struct access_control_addr *aca)
351 {
352    int i, masklength, port;
353    char *p;
354
355    masklength = 32;
356    port       =  0;
357
358    if ((p = strchr(aspec, '/')))
359    {
360       *p++ = '\0';
361
362       if (ijb_isdigit(*p) == 0)
363       {
364          return(-1);
365       }
366       masklength = atoi(p);
367    }
368
369    if ((masklength < 0) || (masklength > 32))
370    {
371       return(-1);
372    }
373
374    if ((p = strchr(aspec, ':')))
375    {
376       *p++ = '\0';
377
378       if (ijb_isdigit(*p) == 0)
379       {
380          return(-1);
381       }
382       port = atoi(p);
383    }
384
385    aca->port = port;
386
387    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
388
389    if (aca->addr == -1)
390    {
391       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
392       return(-1);
393    }
394
395    /* build the netmask */
396    aca->mask = 0;
397    for (i=1; i <= masklength ; i++)
398    {
399       aca->mask |= (1 << (32 - i));
400    }
401
402    /* now mask off the host portion of the ip address
403     * (i.e. save on the network portion of the address).
404     */
405    aca->addr = aca->addr & aca->mask;
406
407    return(0);
408
409 }
410 #endif /* def ACL_FILES */
411
412
413 /*********************************************************************
414  *
415  * Function    :  block_url
416  *
417  * Description :  Called from `chat'.  Check to see if we need to block this.
418  *
419  * Parameters  :
420  *          1  :  http = http_request request to "check" for blocked
421  *          2  :  csp = Current client state (buffers, headers, etc...)
422  *
423  * Returns     :  NULL => unblocked, else string to HTML block description.
424  *
425  *********************************************************************/
426 char *block_url(struct http_request *http, struct client_state *csp)
427 {
428    char *p;
429    int n;
430    int factor = 2;
431
432    if ((csp->action->flags & ACTION_BLOCK) == 0)
433    {
434       return(NULL);
435    }
436    else
437    {
438 #ifdef FORCE_LOAD
439       factor++;
440 #endif /* def FORCE_LOAD */
441
442       n  = strlen(CBLOCK);
443       n += factor * strlen(http->hostport);
444       n += factor * strlen(http->path);
445
446       p = (char *)malloc(n);
447
448 #ifdef FORCE_LOAD
449       sprintf(p, CBLOCK, http->hostport, http->path, http->hostport, http->path,
450               http->hostport, http->path);
451 #else
452       sprintf(p, CBLOCK, http->hostport, http->path, http->hostport, http->path);
453 #endif /* def FORCE_LOAD */
454
455       return(p);
456    }
457 }
458
459
460 #ifdef IMAGE_BLOCKING
461 /*********************************************************************
462  *
463  * Function    :  block_imageurl
464  *
465  * Description :  Given a URL which is blocked, decide whether to 
466  *                send the "blocked" image or HTML.
467  *
468  * Parameters  :
469  *          1  :  http = URL to check.
470  *          2  :  csp = Current client state (buffers, headers, etc...)
471  *
472  * Returns     :  True (nonzero) if URL is in image list, false (0)
473  *                otherwise
474  *
475  *********************************************************************/
476 int block_imageurl(struct http_request *http, struct client_state *csp)
477 {
478 #ifdef DETECT_MSIE_IMAGES
479    if ((csp->accept_types 
480        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
481        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
482    {
483       return 1;
484    }
485    else if ((csp->accept_types 
486        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
487        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
488    {
489       return 0;
490    }
491 #endif
492
493    return ((csp->action->flags & ACTION_IMAGE) != 0);
494 }
495 #endif /* def IMAGE_BLOCKING */
496
497
498 #ifdef PCRS
499 /*********************************************************************
500  *
501  * Function    :  re_process_buffer
502  *
503  * Description :  Apply all jobs from the joblist (aka. Perl regexp's) to
504  *                the text buffer that's been accumulated in csp->iob->buf
505  *                and set csp->content_length to the modified size.
506  *
507  * Parameters  :
508  *          1  :  csp = Current client state (buffers, headers, etc...)
509  *
510  * Returns     :  a pointer to the (newly allocated) modified buffer.
511  *                
512  *
513  *********************************************************************/
514 char *re_process_buffer(struct client_state *csp)
515 {
516    int hits=0;
517    int size = csp->iob->eod - csp->iob->cur;
518    char *old=csp->iob->cur, *new = NULL;
519    pcrs_job *job, *joblist;
520
521    struct file_list *fl;
522    struct re_filterfile_spec *b;
523
524    /* Sanity first ;-) */
525    if (size <= 0)
526    {
527       return(strdup(""));
528    }
529
530    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
531    {
532       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
533       return(strdup(""));
534    }
535
536    joblist = b->joblist;
537
538
539    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
540               csp->http->hostport, csp->http->path, size);
541
542    /* Apply all jobs from the joblist */
543    for (job = joblist; NULL != job; job = job->next)
544    {
545       hits += pcrs_exec_substitution(job, old, size, &new, &size);
546       if (old != csp->iob->cur) free(old);
547       old=new;
548    }
549
550    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
551
552    csp->content_length = size;
553
554    /* fwiw, reset the iob */
555    IOB_RESET(csp);
556    return(new);
557
558 }
559 #endif /* def PCRS */
560
561
562 #ifdef TRUST_FILES
563 /*********************************************************************
564  *
565  * Function    :  trust_url
566  *
567  * Description :  Should we "trust" this URL?  See "trustfile" line in config.
568  *
569  * Parameters  :
570  *          1  :  http = http_request request for requested URL
571  *          2  :  csp = Current client state (buffers, headers, etc...)
572  *
573  * Returns     :  NULL => trusted, else string to HTML "untrusted" description.
574  *
575  *********************************************************************/
576 char *trust_url(struct http_request *http, struct client_state *csp)
577 {
578    struct file_list *fl;
579    struct block_spec *b;
580    struct url_spec url[1], **tl, *t;
581    char *p, *h;
582    char *hostport, *path, *refer;
583    struct http_request rhttp[1];
584    int n;
585
586    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
587    {
588       return(NULL);
589    }
590
591    *url = dsplit(http->host);
592
593    /* if splitting the domain fails, punt */
594    if (url->dbuf == NULL) return(NULL);
595
596    memset(rhttp, '\0', sizeof(*rhttp));
597
598    for (b = b->next; b ; b = b->next)
599    {
600       if ((b->url->port == 0) || (b->url->port == http->port))
601       {
602          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
603          {
604             if ((b->url->path == NULL) ||
605 #ifdef REGEX
606                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
607 #else
608                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
609 #endif
610             )
611             {
612                freez(url->dbuf);
613                freez(url->dvec);
614
615                if (b->reject == 0) return(NULL);
616
617                hostport = url_encode(http->hostport);
618                path     = url_encode(http->path);
619
620                if (csp->referrer)
621                {
622                   refer = url_encode(csp->referrer);
623                }
624                else
625                {
626                   refer = url_encode("undefined");
627                }
628
629                n  = strlen(CTRUST);
630                n += strlen(hostport);
631                n += strlen(path);
632                n += strlen(refer);
633
634                p = (char *)malloc(n);
635
636                sprintf(p, CTRUST, hostport, path, refer);
637
638                freez(hostport);
639                freez(path);
640                freez(refer);
641
642                return(p);
643             }
644          }
645       }
646    }
647
648    freez(url->dbuf);
649    freez(url->dvec);
650
651    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
652    {
653       /* no referrer was supplied */
654       goto trust_url_not_trusted;
655    }
656
657    /* forge a URL from the referrer so we can use
658     * convert_url() to parse it into its components.
659     */
660
661    p = NULL;
662    p = strsav(p, "GET ");
663    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
664    p = strsav(p, " HTTP/1.0");
665
666    parse_http_request(p, rhttp, csp);
667
668    if (rhttp->cmd == NULL)
669    {
670       freez(p);
671       goto trust_url_not_trusted;
672    }
673
674    freez(p);
675
676    *url = dsplit(rhttp->host);
677
678    /* if splitting the domain fails, punt */
679    if (url->dbuf == NULL) goto trust_url_not_trusted;
680
681    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
682    {
683       if ((t->port == 0) || (t->port == rhttp->port))
684       {
685          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
686          {
687             if ((t->path == NULL) ||
688 #ifdef REGEX
689                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
690 #else
691                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
692 #endif
693             )
694             {
695                /* if the URL's referrer is from a trusted referrer, then
696                 * add the target spec to the trustfile as an unblocked
697                 * domain and return NULL (which means it's OK).
698                 */
699
700                FILE *fp;
701
702                freez(url->dbuf);
703                freez(url->dvec);
704
705                if ((fp = fopen(csp->config->trustfile, "a")))
706                {
707                   h = NULL;
708
709                   h = strsav(h, "~");
710                   h = strsav(h, http->hostport);
711
712                   p = http->path;
713                   if ((*p++ == '/')
714                   && (*p++ == '~'))
715                   {
716                   /* since this path points into a user's home space
717                    * be sure to include this spec in the trustfile.
718                    */
719                      if ((p = strchr(p, '/')))
720                      {
721                         *p = '\0';
722                         h = strsav(h, http->path);
723                         h = strsav(h, "/");
724                      }
725                   }
726
727                   free_http_request(rhttp);
728
729                   fprintf(fp, "%s\n", h);
730                   freez(h);
731                   fclose(fp);
732                }
733                return(NULL);
734             }
735          }
736       }
737    }
738
739 trust_url_not_trusted:
740    free_http_request(rhttp);
741
742    hostport = url_encode(http->hostport);
743    path     = url_encode(http->path);
744
745    if (csp->referrer)
746    {
747       refer = url_encode(csp->referrer);
748    }
749    else
750    {
751       refer = url_encode("undefined");
752    }
753
754    n  = strlen(CTRUST);
755    n += strlen(hostport);
756    n += strlen(path);
757    n += strlen(refer);
758
759    p = (char *)malloc(n);
760    sprintf(p, CTRUST, hostport, path, refer);
761
762    freez(hostport);
763    freez(path);
764    freez(refer);
765
766    return(p);
767
768 }
769 #endif /* def TRUST_FILES */
770
771
772 static const char C_HOME_PAGE[] =
773    "HTTP/1.0 200 OK\n"
774    "Pragma: no-cache\n"
775    "Expires: Thu Jul 31, 1997 07:42:22 pm GMT\n"
776    "Content-Type: text/html\n\n"
777    "<html>\n"
778    "<head>\n"
779    "<title>Internet Junkbuster: Information</title>\n"
780    "</head>\n"
781    BODY
782    "<h1><center>"
783    BANNER
784    "</h1></center>\n"
785    "<p><a href=\"" HOME_PAGE_URL "\">JunkBuster web site</a></p>\n"
786    "<p><a href=\"http://i.j.b/show-proxy-arg\">Proxy configuration</a></p>\n"
787    "<p><a href=\"http://i.j.b/show-url-info\">Look up a URL</a></p>\n"
788    "</body>\n"
789    "</html>\n";
790
791
792 /*********************************************************************
793  *
794  * Function    :  intercept_url
795  *
796  * Description :  checks the URL `basename' against a list of URLs to
797  *                snarf. If it matches, it calls the associated function
798  *                which returns an HTML page to send back to the client.
799  *                Right now, we snarf:
800  *                      "show-proxy-args", and
801  *                      "ij-untrusted-url" (optional w/TRUST_FILES)
802  *
803  * Parameters  :
804  *          1  :  http = http_request request, check `basename's of blocklist
805  *          2  :  csp = Current client state (buffers, headers, etc...)
806  *
807  * Returns     :  1 if it intercepts & handles the request.
808  *
809  *********************************************************************/
810 int intercept_url(struct http_request *http, struct client_state *csp)
811 {
812    char *basename = NULL;
813    const struct interceptors *v;
814
815    if (0 == strcmpic(http->host,"i.j.b"))
816    {
817       /*
818        * Catch http://i.j.b/...
819        */
820       basename = http->path;
821    }
822    else if ( ( (0 == strcmpic(http->host,"ijbswa.sourceforge.net"))
823             || (0 == strcmpic(http->host,"ijbswa.sf.net")) )
824             && (0 == strncmpic(http->path,"/config", 7))
825             && ((http->path[7] == '/') || (http->path[7] == '\0')))
826    {
827       /*
828        * Catch http://ijbswa.sourceforge.net/config/...
829        * and   http://ijbswa.sf.net/config/...
830        */
831       basename = http->path + 7;
832    }
833
834    if (!basename)
835    {
836       /* Don't want to intercept */
837       return(0);
838    }
839
840    /* We have intercepted it. */
841
842    /* remove any leading slash */
843    if (*basename == '/')
844    {
845       basename++;
846    }
847
848    log_error(LOG_LEVEL_GPC, "%s%s intercepted!", http->hostport, http->path);
849    log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", 
850                             csp->ip_addr_str, http->cmd); 
851
852    for (v = intercept_patterns; v->str; v++)
853    {
854       if (strncmp(basename, v->str, v->len) == 0)
855       {
856          char * p = ((v->interceptor)(http, csp));
857
858          if (p != NULL)
859          {
860             /* Send HTML redirection result */
861             write_socket(csp->cfd, p, strlen(p));
862
863             freez(p);
864          }
865          return(1);
866       }
867    }
868
869    write_socket(csp->cfd, C_HOME_PAGE, strlen(C_HOME_PAGE));
870
871    return(1);
872 }
873
874 #ifdef FAST_REDIRECTS
875 /*********************************************************************
876  *
877  * Function    :  redirect_url
878  *
879  * Description :  Checks for redirection URLs and returns a HTTP redirect
880  *                to the destination URL.
881  *
882  * Parameters  :
883  *          1  :  http = http_request request, check `basename's of blocklist
884  *          2  :  csp = Current client state (buffers, headers, etc...)
885  *
886  * Returns     :  NULL if URL was clean, HTTP redirect otherwise.
887  *
888  *********************************************************************/
889 char *redirect_url(struct http_request *http, struct client_state *csp)
890 {
891    char *p, *q;
892
893    p = q = csp->http->path;
894    log_error(LOG_LEVEL_REDIRECTS, "checking path: %s", p);
895
896    /* find the last URL encoded in the request */
897    while (p = strstr(p, "http://"))
898    {
899       q = p++;
900    }
901
902    /* if there was any, generate and return a HTTP redirect */
903    if (q != csp->http->path)
904    {
905       log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q);
906
907       p = (char *)malloc(strlen(HTTP_REDIRECT_TEMPLATE) + strlen(q));
908       sprintf(p, HTTP_REDIRECT_TEMPLATE, q);
909       return(p);
910    }
911    else
912    {
913       return(NULL);
914    }
915
916 }
917 #endif /* def FAST_REDIRECTS */
918
919 /*********************************************************************
920  *
921  * Function    :  url_actions
922  *
923  * Description :  Gets the actions for this URL.
924  *
925  * Parameters  :
926  *          1  :  http = http_request request for blocked URLs
927  *          2  :  csp = Current client state (buffers, headers, etc...)
928  *
929  * Returns     :  N/A
930  *
931  *********************************************************************/
932 void url_actions(struct http_request *http, 
933                  struct client_state *csp)
934 {
935    struct file_list *fl;
936    struct url_actions *b;
937
938    init_current_action(csp->action);
939
940    if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
941    {
942       return;
943    }
944
945    apply_url_actions(csp->action, http, b);
946 }
947
948
949 /*********************************************************************
950  *
951  * Function    :  apply_url_actions
952  *
953  * Description :  Applies a list of URL actions.
954  *
955  * Parameters  :
956  *          1  :  action = Destination.
957  *          2  :  http = Current URL
958  *          3  :  b = list of URL actions to apply
959  *
960  * Returns     :  N/A
961  *
962  *********************************************************************/
963 void apply_url_actions(struct current_action_spec *action, 
964                        struct http_request *http, 
965                        struct url_actions *b)
966 {
967    struct url_spec url[1];
968
969    if (b == NULL)
970    {
971       /* Should never happen */
972       return;
973    }
974
975    *url = dsplit(http->host);
976
977    /* if splitting the domain fails, punt */
978    if (url->dbuf == NULL)
979    {
980       return;
981    }
982
983    for (b = b->next; NULL != b; b = b->next)
984    {
985       if ((b->url->port == 0) || (b->url->port == http->port))
986       {
987          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
988          {
989             if ((b->url->path == NULL) ||
990 #ifdef REGEX
991                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
992 #else
993                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
994 #endif
995             )
996             {
997                merge_current_action(action, b->action);
998             }
999          }
1000       }
1001    }
1002
1003    freez(url->dbuf);
1004    freez(url->dvec);
1005 }
1006
1007
1008 /*********************************************************************
1009  *
1010  * Function    :  forward_url
1011  *
1012  * Description :  Should we forward this to another proxy?
1013  *
1014  * Parameters  :
1015  *          1  :  http = http_request request for current URL
1016  *          2  :  csp = Current client state (buffers, headers, etc...)
1017  *
1018  * Returns     :  Return gw_default for no forward match,
1019  *                else a gateway pointer to a specific forwarding proxy.
1020  *
1021  *********************************************************************/
1022 const struct gateway *forward_url(struct http_request *http, struct client_state *csp)
1023 {
1024    struct file_list *fl;
1025    struct forward_spec *b;
1026    struct url_spec url[1];
1027
1028    if (((fl = csp->flist) == NULL) || ((b = fl->f) == NULL))
1029    {
1030       return(gw_default);
1031    }
1032
1033    *url = dsplit(http->host);
1034
1035    /* if splitting the domain fails, punt */
1036    if (url->dbuf == NULL) return(gw_default);
1037
1038    for (b = b->next; b ; b = b->next)
1039    {
1040       if ((b->url->port == 0) || (b->url->port == http->port))
1041       {
1042          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
1043          {
1044             if ((b->url->path == NULL) ||
1045 #ifdef REGEX
1046                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
1047 #else
1048                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
1049 #endif
1050             )
1051             {
1052                freez(url->dbuf);
1053                freez(url->dvec);
1054                return(b->gw);
1055             }
1056          }
1057       }
1058    }
1059
1060    freez(url->dbuf);
1061    freez(url->dvec);
1062    return(gw_default);
1063
1064 }
1065
1066
1067 /*********************************************************************
1068  *
1069  * Function    :  dsplit
1070  *
1071  * Description :  Takes a domain and returns a pointer to a url_spec
1072  *                structure populated with dbuf, dcnt and dvec.  The
1073  *                other fields in the structure that is returned are zero.
1074  *
1075  * Parameters  :
1076  *          1  :  domain = a URL address
1077  *
1078  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
1079  *
1080  *********************************************************************/
1081 struct url_spec dsplit(char *domain)
1082 {
1083    struct url_spec ret[1];
1084    char *v[BUFSIZ];
1085    int size;
1086    char *p;
1087
1088    memset(ret, '\0', sizeof(*ret));
1089
1090    if (domain[strlen(domain) - 1] == '.')
1091    {
1092           ret->unanchored |= ANCHOR_RIGHT;
1093         }
1094         if (domain[0] == '.')
1095    {
1096           ret->unanchored |= ANCHOR_LEFT;
1097         }
1098
1099    ret->dbuf = strdup(domain);
1100
1101    /* map to lower case */
1102    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
1103
1104    /* split the domain name into components */
1105    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
1106
1107    if (ret->dcnt <= 0)
1108    {
1109       memset(ret, '\0', sizeof(ret));
1110       return(*ret);
1111    }
1112
1113    /* save a copy of the pointers in dvec */
1114    size = ret->dcnt * sizeof(*ret->dvec);
1115
1116    if ((ret->dvec = (char **)malloc(size)))
1117    {
1118       memcpy(ret->dvec, v, size);
1119    }
1120
1121
1122    return(*ret);
1123
1124 }
1125
1126
1127 /*********************************************************************
1128  *
1129  * Function    :  domaincmp
1130  *
1131  * Description :  Domain-wise Compare fqdn's. Governed by the bimap in
1132  *                pattern->unachored, the comparison is un-, left-,
1133  *                right-anchored, or both.
1134  *                The individual domain names are compared with
1135  *                trivialmatch().
1136  *
1137  * Parameters  :
1138  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
1139  *          2  :  fqdn = domain name against which the patterns are compared.
1140  *
1141  * Returns     :  0 => domains are equivalent, else no match.
1142  *
1143  *********************************************************************/
1144 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
1145 {
1146    char **pv, **fv;  /* vectors  */
1147    int    pn,   fn;  /* counters */
1148    char  *p,   *f;   /* chars    */
1149
1150    pv = pattern->dvec;
1151    fv = fqdn->dvec;
1152    fn = pn = 0;
1153
1154    while (fn < fqdn->dcnt && pn < pattern->dcnt)
1155    {
1156       p = pv[pn];
1157       f = fv[fn];
1158
1159       if (simplematch(p, f))
1160       {
1161                   if(pn || !(pattern->unanchored & ANCHOR_LEFT))
1162          {
1163             return 1;
1164          }
1165       }
1166       else
1167       {
1168          pn++;
1169       }
1170       fn++;
1171    }
1172
1173    return ((pn < pattern->dcnt) || ((fn < fqdn->dcnt) && !(pattern->unanchored & ANCHOR_RIGHT)));
1174
1175 }
1176
1177
1178 /* intercept functions */
1179
1180 /*********************************************************************
1181  *
1182  * Function    :  show_proxy_args
1183  *
1184  * Description :  This "crunch"es "http:/any.thing/show-proxy-args" and
1185  *                returns a web page describing the current status of IJB.
1186  *
1187  * Parameters  :
1188  *          1  :  http = ignored
1189  *          2  :  csp = Current client state (buffers, headers, etc...)
1190  *
1191  * Returns     :  A string that contains the current status of IJB.
1192  *
1193  *********************************************************************/
1194 char *show_proxy_args(struct http_request *http, struct client_state *csp)
1195 {
1196    char *s = NULL;
1197
1198 #ifdef SPLIT_PROXY_ARGS
1199    FILE * fp;
1200    char buf[BUFSIZ];
1201    char * p;
1202    const char * filename = NULL;
1203    const char * file_description = NULL;
1204    char * query_string = strrchr(http->path, '?');
1205    char which_file = '\0';
1206
1207
1208    if (query_string != NULL)
1209    {
1210       /* first char past the last '?' (maybe '\0')*/
1211       which_file = query_string[1];
1212    }
1213    switch (which_file)
1214    {
1215    case 'p':
1216       if (csp->actions_list)
1217       {
1218          filename = csp->actions_list->filename;
1219          file_description = "Actions List";
1220       }
1221       break;
1222    case 'f':
1223       if (csp->flist)
1224       {
1225          filename = csp->flist->filename;
1226          file_description = "Forward List";
1227       }
1228       break;
1229
1230 #ifdef ACL_FILES
1231    case 'a':
1232       if (csp->alist)
1233       {
1234          filename = csp->alist->filename;
1235          file_description = "Access Control List";
1236       }
1237       break;
1238 #endif /* def ACL_FILES */
1239
1240 #ifdef PCRS
1241    case 'r':
1242       if (csp->rlist)
1243       {
1244          filename = csp->rlist->filename;
1245          file_description = "RE Filter List";
1246       }
1247       break;
1248 #endif /* def PCRS */
1249
1250 #ifdef TRUST_FILES
1251    case 't':
1252       if (csp->tlist)
1253       {
1254          filename = csp->tlist->filename;
1255          file_description = "Trust List";
1256       }
1257       break;
1258 #endif /* def TRUST_FILES */
1259    }
1260
1261    if (filename)
1262    {
1263       /* Display specified file */
1264       /* FIXME: Add HTTP headers so this isn't cached */
1265       s = strsav(s,
1266          "HTTP/1.0 200 OK\n"
1267          "Server: IJ/" VERSION "\n"
1268          "Content-type: text/html\n"
1269          "Pragma: no-cache\n"
1270          "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
1271          "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1272          "\n"
1273
1274          "<html>"
1275          "<head>"
1276          "<title>Internet Junkbuster Proxy Status - ");
1277       s = strsav(s, file_description);
1278       s = strsav(s, 
1279          "</title>"
1280          "</head>\n"
1281          "<body bgcolor=\"#f8f8f0\" link=\"#000078\" alink=\"#ff0022\" vlink=\"#787878\">\n"
1282          "<center>\n"
1283          "<h1>" BANNER "\n");
1284       s = strsav(s, file_description);
1285       s = strsav(s, 
1286          "</h1></center>\n"
1287          "<p><a href=\"show-proxy-args\">Back to proxy status</a></p>\n"
1288          "<h2>");
1289       s = strsav(s, file_description);
1290       s = strsav(s,
1291          "</h2>\n"
1292          "Contents of file &quot;<code>");
1293       p = html_encode(filename);
1294       s = strsav(s, p);
1295       freez(p);
1296       s = strsav(s,
1297          "</code>&quot;:<br>\n"
1298          "</p>\n"
1299          "<pre>");
1300       
1301       if ((fp = fopen(filename, "r")) == NULL)
1302       {
1303          s = strsav(s, "</pre><h1>ERROR OPENING FILE!</h1><pre>");
1304       }
1305       else
1306       {
1307          while (fgets(buf, sizeof(buf), fp))
1308          {
1309             p = html_encode(buf);
1310             if (p)
1311             {
1312                s = strsav(s, p);
1313                freez(p);
1314                s = strsav(s, "<br>");
1315             }
1316          }
1317          fclose(fp);
1318       }
1319
1320       s = strsav(s,
1321          "</pre>\n"
1322          "<br>\n"
1323          "<p><a href=\"show-proxy-args\">Back to proxy status</a></p>\n"
1324          "<br>\n"
1325          "<small><small><p>\n"
1326          "The " BANNER " Proxy - \n"
1327          "<a href=\"" HOME_PAGE_URL "\">" HOME_PAGE_URL "</a>\n"
1328          "</small></small>"
1329          "</body></html>\n");
1330       return(s);
1331    }
1332 #endif /* def SPLIT_PROXY_ARGS */
1333    
1334    s = strsav(s, csp->config->proxy_args_header);
1335    s = strsav(s, csp->config->proxy_args_invocation);
1336 #ifdef STATISTICS
1337    s = add_stats(s);
1338 #endif /* def STATISTICS */
1339    s = strsav(s, csp->config->proxy_args_gateways);
1340
1341 #ifdef SPLIT_PROXY_ARGS
1342    s = strsav(s, 
1343       "<h2>The following files are in use:</h2>\n"
1344       "<p>(Click a filename to view it)</p>\n"
1345       "<ul>\n");
1346
1347    if (csp->actions_list)
1348    {
1349       s = strsav(s, "<li>Actions List: <a href=\"show-proxy-args?permit\"><code>");
1350       s = strsav(s, csp->actions_list->filename);
1351       s = strsav(s, "</code></a></li>\n");
1352    }
1353
1354    if (csp->flist)
1355    {
1356       s = strsav(s, "<li>Forward List: <a href=\"show-proxy-args?forward\"><code>");
1357       s = strsav(s, csp->flist->filename);
1358       s = strsav(s, "</code></a></li>\n");
1359    }
1360
1361 #ifdef ACL_FILES
1362    if (csp->alist)
1363    {
1364       s = strsav(s, "<li>Access Control List: <a href=\"show-proxy-args?acl\"><code>");
1365       s = strsav(s, csp->alist->filename);
1366       s = strsav(s, "</code></a></li>\n");
1367    }
1368 #endif /* def ACL_FILES */
1369
1370 #ifdef PCRS
1371    if (csp->rlist)
1372    {
1373       s = strsav(s, "<li>RE Filter List: <a href=\"show-proxy-args?re\"><code>");
1374       s = strsav(s, csp->rlist->filename);
1375       s = strsav(s, "</code></a></li>\n");
1376    }
1377 #endif /* def PCRS */
1378
1379 #ifdef TRUST_FILES
1380    if (csp->tlist)
1381    {
1382       s = strsav(s, "<li>Trust List: <a href=\"show-proxy-args?trust\"><code>");
1383       s = strsav(s, csp->tlist->filename);
1384       s = strsav(s, "</code></a></li>\n");
1385    }
1386 #endif /* def TRUST_FILES */
1387
1388    s = strsav(s, "</ul>");
1389
1390 #else /* ifndef SPLIT_PROXY_ARGS */
1391    if (csp->clist)
1392    {
1393       s = strsav(s, csp->clist->proxy_args);
1394    }
1395
1396    if (csp->flist)
1397    {
1398       s = strsav(s, csp->flist->proxy_args);
1399    }
1400
1401 #ifdef ACL_FILES
1402    if (csp->alist)
1403    {
1404       s = strsav(s, csp->alist->proxy_args);
1405    }
1406 #endif /* def ACL_FILES */
1407
1408 #ifdef PCRS
1409    if (csp->rlist)
1410    {
1411       s = strsav(s, csp->rlist->proxy_args);
1412    }
1413 #endif /* def PCRS */
1414
1415 #ifdef TRUST_FILES
1416    if (csp->tlist)
1417    {
1418       s = strsav(s, csp->tlist->proxy_args);
1419    }
1420 #endif /* def TRUST_FILES */
1421
1422 #endif /* ndef SPLIT_PROXY_ARGS */
1423
1424    s = strsav(s, csp->config->proxy_args_trailer);
1425
1426    return(s);
1427
1428 }
1429
1430
1431 static const char C_URL_INFO_HEADER[] =
1432    "HTTP/1.0 200 OK\n"
1433    "Pragma: no-cache\n"
1434    "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1435    "Content-Type: text/html\n\n"
1436    "<html>\n"
1437    "<head>\n"
1438    "<title>Internet Junkbuster: URL Info</title>\n"
1439    "</head>\n"
1440    BODY
1441    "<h1><center>"
1442    BANNER
1443    "</h1></center>\n"
1444    "<p>Information for: <a href=\"http://%s\">http://%s</a></p>\n";
1445 static const char C_URL_INFO_FOOTER[] =
1446    "\n</p>\n"
1447    "</body>\n"
1448    "</html>\n";
1449
1450 static const char C_URL_INFO_FORM[] =
1451    "HTTP/1.0 200 OK\n"
1452    "Pragma: no-cache\n"
1453    "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1454    "Content-Type: text/html\n\n"
1455    "<html>\n"
1456    "<head>\n"
1457    "<title>Internet Junkbuster: URL Info</title>\n"
1458    "</head>\n"
1459    BODY
1460    "<h1><center>"
1461    BANNER
1462    "</h1></center>\n"
1463    "<form method=\"GET\" action=\"http://i.j.b/show-url-info\">\n"
1464    "<p>Please enter a URL, without the leading &quot;http://&quot;:</p>"
1465    "<p><input type=\"text\" name=\"url\" size=\"80\">"
1466    "<input type=\"submit\" value=\"Info\"></p>\n"
1467    "</form>\n"
1468    "</body>\n"
1469    "</html>\n";
1470
1471
1472  /*********************************************************************
1473  *
1474  * Function    :  ijb_show_url_info
1475  *
1476  * Description :  (please fill me in)
1477  *
1478  * Parameters  :
1479  *          1  :  http = http_request request for crunched URL
1480  *          2  :  csp = Current client state (buffers, headers, etc...)
1481  *
1482  * Returns     :  ???FIXME
1483  *
1484  *********************************************************************/
1485 char *ijb_show_url_info(struct http_request *http, struct client_state *csp)
1486 {
1487    char * query_string = strchr(http->path, '?');
1488    char * host = NULL;
1489    
1490    if (query_string != NULL)
1491    {
1492       query_string = url_decode(query_string + 1);
1493       if (strncmpic(query_string, "url=", 4) == 0)
1494       {
1495          host = strdup(query_string + 4);
1496       }
1497       freez(query_string);
1498    }
1499    if (host != NULL)
1500    {
1501       char * result;
1502       char * path;
1503       char * s;
1504       int port = 80;
1505       struct file_list *fl;
1506       struct url_actions *b;
1507       struct url_spec url[1];
1508       struct current_action_spec action[1];
1509
1510       init_current_action(action);
1511
1512       result = (char *)malloc(sizeof(C_URL_INFO_HEADER) + 2 * strlen(host));
1513       sprintf(result, C_URL_INFO_HEADER, host, host);
1514
1515       s = current_action_to_text(action);
1516       result = strsav(result, "<h3>Defaults:</h3>\n<p><b>{");
1517       result = strsav(result, s);
1518       result = strsav(result, " }</b></p>\n<h3>Patterns affecting the URL:</h3>\n<p>\n");
1519       freez(s);
1520
1521       s = strchr(host, '/');
1522       if (s != NULL)
1523       {
1524          path = strdup(s);
1525          *s = '\0';
1526       }
1527       else
1528       {
1529          path = strdup("");
1530       }
1531       s = strchr(host, ':');
1532       if (s != NULL)
1533       {
1534          *s++ = '\0';
1535          port = atoi(s);
1536       }
1537
1538       if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
1539       {
1540          freez(host);
1541          freez(path);
1542          result = strsav(result, C_URL_INFO_FOOTER);
1543          return result;
1544       }
1545
1546       *url = dsplit(host);
1547
1548       /* if splitting the domain fails, punt */
1549       if (url->dbuf == NULL)
1550       {
1551          freez(host);
1552          freez(path);
1553          result = strsav(result, C_URL_INFO_FOOTER);
1554          return result;
1555       }
1556
1557       for (b = b->next; NULL != b; b = b->next)
1558       {
1559          if ((b->url->port == 0) || (b->url->port == port))
1560          {
1561             if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
1562             {
1563                if ((b->url->path == NULL) ||
1564 #ifdef REGEX
1565                   (regexec(b->url->preg, path, 0, NULL, 0) == 0)
1566 #else
1567                   (strncmp(b->url->path, path, b->url->pathlen) == 0)
1568 #endif
1569                )
1570                {
1571                   s = actions_to_text(b->action);
1572                   result = strsav(result, "<b>{");
1573                   result = strsav(result, s);
1574                   result = strsav(result, " }</b><br>\n<code>");
1575                   result = strsav(result, b->url->spec);
1576                   result = strsav(result, "</code><br>\n<br>\n");
1577                   freez(s);
1578
1579                   merge_current_action(action, b->action);
1580                }
1581             }
1582          }
1583       }
1584
1585       freez(url->dbuf);
1586       freez(url->dvec);
1587
1588       freez(host);
1589       freez(path);
1590
1591       s = current_action_to_text(action);
1592       result = strsav(result, "</p>\n<h2>Final Results:</h2>\n<p><b>{");
1593       result = strsav(result, s);
1594       result = strsav(result, " }</b><br>\n<br>\n");
1595       freez(s);
1596
1597       free_current_action(action);
1598
1599       result = strsav(result, C_URL_INFO_FOOTER);
1600       return result;
1601    }
1602    else
1603    {
1604       return strdup(C_URL_INFO_FORM);
1605    }
1606 }
1607
1608
1609 /*********************************************************************
1610  *
1611  * Function    :  ijb_send_banner
1612  *
1613  * Description :  This "crunch"es "http://i.j.b/ijb-send-banner and
1614  *                sends the image.
1615  *
1616  * Parameters  :
1617  *          1  :  http = http_request request for crunched URL
1618  *          2  :  csp = Current client state (buffers, headers, etc...)
1619  *
1620  * Returns     :  NULL, indicating that it has already sent the data.
1621  *
1622  *********************************************************************/
1623 char *ijb_send_banner(struct http_request *http, struct client_state *csp)
1624 {
1625    write_socket(csp->cfd, JBGIF, sizeof(JBGIF)-1);
1626    
1627    return(NULL);
1628 }
1629
1630 #ifdef TRUST_FILES
1631 /*********************************************************************
1632  *
1633  * Function    :  ij_untrusted_url
1634  *
1635  * Description :  This "crunch"es "http:/any.thing/ij-untrusted-url" and
1636  *                returns a web page describing why it was untrusted.
1637  *
1638  * Parameters  :
1639  *          1  :  http = http_request request for crunched URL
1640  *          2  :  csp = Current client state (buffers, headers, etc...)
1641  *
1642  * Returns     :  A string that contains why this was untrusted.
1643  *
1644  *********************************************************************/
1645 char *ij_untrusted_url(struct http_request *http, struct client_state *csp)
1646 {
1647    int n;
1648    char *hostport, *path, *refer, *p, *v[9];
1649    char buf[BUFSIZ];
1650    struct url_spec **tl, *t;
1651
1652
1653    static const char format[] =
1654       "HTTP/1.0 200 OK\r\n"
1655       "Pragma: no-cache\n"
1656       "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
1657       "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1658       "Content-Type: text/html\n\n"
1659       "<html>\n"
1660       "<head>\n"
1661       "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
1662       "</head>\n"
1663       BODY
1664       "<center><h1>"
1665       BANNER
1666       "</h1></center>"
1667       "The " BANNER " Proxy "
1668       "<A href=\"" HOME_PAGE_URL "\">"
1669       "(" HOME_PAGE_URL ") </A>"
1670       "intercepted the request for %s%s\n"
1671       "because the URL is not trusted.\n"
1672       "<br><br>\n";
1673
1674    if ((n = ssplit(http->path, "?+", v, SZ(v), 0, 0)) == 4)
1675    {
1676       hostport = url_decode(v[1]);
1677       path     = url_decode(v[2]);
1678       refer    = url_decode(v[3]);
1679    }
1680    else
1681    {
1682       hostport = strdup("undefined_host");
1683       path     = strdup("/undefined_path");
1684       refer    = strdup("undefined");
1685    }
1686
1687    n  = sizeof(format);
1688    n += strlen(hostport);
1689    n += strlen(path    );
1690
1691    if ((p = (char *)malloc(n)))
1692    {
1693       sprintf(p, format, hostport, path);
1694    }
1695
1696    strsav(p, "The referrer in this request was <strong>");
1697    strsav(p, refer);
1698    strsav(p, "</strong><br>\n");
1699
1700    freez(hostport);
1701    freez(path    );
1702    freez(refer   );
1703
1704    p = strsav(p, "<h3>The following referrers are trusted</h3>\n");
1705
1706    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
1707    {
1708       sprintf(buf, "%s<br>\n", t->spec);
1709       p = strsav(p, buf);
1710    }
1711
1712    if (csp->config->trust_info->next)
1713    {
1714       struct list *l;
1715
1716       strcpy(buf,
1717          "<p>"
1718          "You can learn more about what this means "
1719          "and what you may be able to do about it by "
1720          "reading the following documents:<br>\n"
1721          "<ol>\n"
1722       );
1723
1724       p = strsav(p, buf);
1725
1726       for (l = csp->config->trust_info->next; l ; l = l->next)
1727       {
1728          sprintf(buf,
1729             "<li> <a href=%s>%s</a><br>\n",
1730                l->str, l->str);
1731          p = strsav(p, buf);
1732       }
1733
1734       p = strsav(p, "</ol>\n");
1735    }
1736
1737    p = strsav(p, "</body>\n" "</html>\n");
1738
1739    return(p);
1740
1741 }
1742 #endif /* def TRUST_FILES */
1743
1744
1745 #ifdef STATISTICS
1746 /*********************************************************************
1747  *
1748  * Function    :  add_stats
1749  *
1750  * Description :  Statistics function of JB.  Called by `show_proxy_args'.
1751  *
1752  * Parameters  :
1753  *          1  :  s = string that holds the proxy args description page
1754  *
1755  * Returns     :  A pointer to the descriptive status web page.
1756  *
1757  *********************************************************************/
1758 char *add_stats(char *s)
1759 {
1760    /*
1761     * Output details of the number of requests rejected and
1762     * accepted. This is switchable in the junkbuster config.
1763     * Does nothing if this option is not enabled.
1764     */
1765
1766    float perc_rej;   /* Percentage of http requests rejected */
1767    char out_str[81];
1768    int local_urls_read     = urls_read;
1769    int local_urls_rejected = urls_rejected;
1770
1771    /*
1772     * Need to alter the stats not to include the fetch of this
1773     * page.
1774     *
1775     * Can't do following thread safely! doh!
1776     *
1777     * urls_read--;
1778     * urls_rejected--; * This will be incremented subsequently *
1779     */
1780
1781    s = strsav(s,"<h2>Statistics for this " BANNER ":</h2>\n");
1782
1783    if (local_urls_read == 0)
1784    {
1785
1786       s = strsav(s,"No activity so far!\n");
1787
1788    }
1789    else
1790    {
1791
1792       perc_rej = (float)local_urls_rejected * 100.0F /
1793             (float)local_urls_read;
1794
1795       sprintf(out_str,
1796          "%d requests received, %d filtered "
1797          "(%6.2f %%).",
1798          local_urls_read, 
1799          local_urls_rejected, perc_rej);
1800
1801       s = strsav(s,out_str);
1802    }
1803
1804    return(s);
1805 }
1806 #endif /* def STATISTICS */
1807
1808
1809 /*
1810   Local Variables:
1811   tab-width: 3
1812   end:
1813 */