Cleaned up and updated to reflect the changesin
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.17 2001/06/09 10:55:28 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.17  2001/06/09 10:55:28  jongfoster
42  *    Changing BUFSIZ ==> BUFFER_SIZE
43  *
44  *    Revision 1.16  2001/06/07 23:10:26  jongfoster
45  *    Allowing unanchored domain patterns to back off and retry
46  *    if they partially match.  Optimized right-anchored patterns.
47  *    Moving ACL and forward files into config file.
48  *    Replacing struct gateway with struct forward_spec
49  *
50  *    Revision 1.15  2001/06/03 19:12:00  oes
51  *    extracted-CGI relevant stuff
52  *
53  *    Revision 1.14  2001/06/01 10:30:55  oes
54  *    Added optional left-anchoring to domaincmp
55  *
56  *    Revision 1.13  2001/05/31 21:21:30  jongfoster
57  *    Permissionsfile / actions file changes:
58  *    - Changed "permission" to "action" throughout
59  *    - changes to file format to allow string parameters
60  *    - Moved helper functions to actions.c
61  *
62  *    Revision 1.12  2001/05/31 17:35:20  oes
63  *
64  *     - Enhanced domain part globbing with infix and prefix asterisk
65  *       matching and optional unanchored operation
66  *
67  *    Revision 1.11  2001/05/29 11:53:23  oes
68  *    "See why" link added to "blocked" page
69  *
70  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
71  *    Unified blocklist/imagelist/permissionslist.
72  *    File format is still under discussion, but the internal changes
73  *    are (mostly) done.
74  *
75  *    Also modified interceptor behaviour:
76  *    - We now intercept all URLs beginning with one of the following
77  *      prefixes (and *only* these prefixes):
78  *        * http://i.j.b/
79  *        * http://ijbswa.sf.net/config/
80  *        * http://ijbswa.sourceforge.net/config/
81  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
82  *    - Internal changes so that intercepted and fast redirect pages
83  *      are not replaced with an image.
84  *    - Interceptors now have the option to send a binary page direct
85  *      to the client. (i.e. ijb-send-banner uses this)
86  *    - Implemented show-url-info interceptor.  (Which is why I needed
87  *      the above interceptors changes - a typical URL is
88  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
89  *      The previous mechanism would not have intercepted that, and
90  *      if it had been intercepted then it then it would have replaced
91  *      it with an image.)
92  *
93  *    Revision 1.9  2001/05/27 22:17:04  oes
94  *
95  *    - re_process_buffer no longer writes the modified buffer
96  *      to the client, which was very ugly. It now returns the
97  *      buffer, which it is then written by chat.
98  *
99  *    - content_length now adjusts the Content-Length: header
100  *      for modified documents rather than crunch()ing it.
101  *      (Length info in csp->content_length, which is 0 for
102  *      unmodified documents)
103  *
104  *    - For this to work, sed() is called twice when filtering.
105  *
106  *    Revision 1.8  2001/05/26 17:13:28  jongfoster
107  *    Filled in a function comment.
108  *
109  *    Revision 1.7  2001/05/26 15:26:15  jongfoster
110  *    ACL feature now provides more security by immediately dropping
111  *    connections from untrusted hosts.
112  *
113  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
114  *    Automatic reloading of config file.
115  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
116  *    Most of the global variables have been moved to a new
117  *    struct configuration_spec, accessed through csp->config->globalname
118  *    Most of the globals remaining are used by the Win32 GUI.
119  *
120  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
121  *    Hard tabs->Spaces
122  *
123  *    Revision 1.4  2001/05/22 18:46:04  oes
124  *
125  *    - Enabled filtering banners by size rather than URL
126  *      by adding patterns that replace all standard banner
127  *      sizes with the "Junkbuster" gif to the re_filterfile
128  *
129  *    - Enabled filtering WebBugs by providing a pattern
130  *      which kills all 1x1 images
131  *
132  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
133  *      which is selected by the (nonstandard and therefore
134  *      capital) letter 'U' in the option string.
135  *      It causes the quantifiers to be ungreedy by default.
136  *      Appending a ? turns back to greedy (!).
137  *
138  *    - Added a new interceptor ijb-send-banner, which
139  *      sends back the "Junkbuster" gif. Without imagelist or
140  *      MSIE detection support, or if tinygif = 1, or the
141  *      URL isn't recognized as an imageurl, a lame HTML
142  *      explanation is sent instead.
143  *
144  *    - Added new feature, which permits blocking remote
145  *      script redirects and firing back a local redirect
146  *      to the browser.
147  *      The feature is conditionally compiled, i.e. it
148  *      can be disabled with --disable-fast-redirects,
149  *      plus it must be activated by a "fast-redirects"
150  *      line in the config file, has its own log level
151  *      and of course wants to be displayed by show-proxy-args
152  *      Note: Boy, all the #ifdefs in 1001 locations and
153  *      all the fumbling with configure.in and acconfig.h
154  *      were *way* more work than the feature itself :-(
155  *
156  *    - Because a generic redirect template was needed for
157  *      this, tinygif = 3 now uses the same.
158  *
159  *    - Moved GIFs, and other static HTTP response templates
160  *      to project.h
161  *
162  *    - Some minor fixes
163  *
164  *    - Removed some >400 CRs again (Jon, you really worked
165  *      a lot! ;-)
166  *
167  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
168  *    Removing last hardcoded JunkBusters.com URLs.
169  *
170  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
171  *    Version 2.9.4 checkin.
172  *    - Merged popupfile and cookiefile, and added control over PCRS
173  *      filtering, in new "permissionsfile".
174  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
175  *      file error you now get a message box (in the Win32 GUI) rather
176  *      than the program exiting with no explanation.
177  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
178  *      skipping.
179  *    - Removed tabs from "config"
180  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
181  *    - Bumped up version number.
182  *
183  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
184  *    Initial import of version 2.9.3 source tree
185  *
186  *
187  *********************************************************************/
188 \f
189
190 #include "config.h"
191
192 #include <stdio.h>
193 #include <sys/types.h>
194 #include <stdlib.h>
195 #include <ctype.h>
196 #include <string.h>
197
198 #ifndef _WIN32
199 #include <unistd.h>
200 #include <netinet/in.h>
201 #else
202 #include <winsock2.h>
203 #endif
204
205 #include "project.h"
206 #include "filters.h"
207 #include "encode.h"
208 #include "jcc.h"
209 #include "showargs.h"
210 #include "parsers.h"
211 #include "ssplit.h"
212 #include "gateway.h"
213 #include "jbsockets.h"
214 #include "errlog.h"
215 #include "jbsockets.h"
216 #include "miscutil.h"
217 #include "actions.h"
218 #include "cgi.h"
219 #include "list.h"
220
221 #ifdef _WIN32
222 #include "win32.h"
223 #endif
224
225 const char filters_h_rcs[] = FILTERS_H_VERSION;
226
227 /* Fix a problem with Solaris.  There should be no effect on other
228  * platforms.
229  * Solaris's isspace() is a macro which uses it's argument directly
230  * as an array index.  Therefore we need to make sure that high-bit
231  * characters generate +ve values, and ideally we also want to make
232  * the argument match the declared parameter type of "int".
233  */
234 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
235
236
237 #ifdef ACL_FILES
238 /*********************************************************************
239  *
240  * Function    :  block_acl
241  *
242  * Description :  Block this request?
243  *                Decide yes or no based on ACL file.
244  *
245  * Parameters  :
246  *          1  :  dst = The proxy or gateway address this is going to.
247  *                      Or NULL to check all possible targets.
248  *          2  :  csp = Current client state (buffers, headers, etc...)
249  *                      Also includes the client IP address.
250  *
251  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
252  *
253  *********************************************************************/
254 int block_acl(struct access_control_addr *dst,
255               struct client_state *csp)
256 {
257    struct access_control_list *acl = csp->config->acl;
258
259    /* if not using an access control list, then permit the connection */
260    if (acl == NULL)
261    {
262       return(0);
263    }
264
265    /* search the list */
266    while (acl != NULL)
267    {
268       if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
269       {
270          if (dst == NULL)
271          {
272             /* Just want to check if they have any access */
273             if (acl->action == ACL_PERMIT)
274             {
275                return(0);
276             }
277          }
278          else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
279            && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
280          {
281             if (acl->action == ACL_PERMIT)
282             {
283                return(0);
284             }
285             else
286             {
287                return(1);
288             }
289          }
290       }
291       acl = acl->next;
292    }
293
294    return(1);
295
296 }
297
298
299 /*********************************************************************
300  *
301  * Function    :  acl_addr
302  *
303  * Description :  Called from `load_aclfile' to parse an ACL address.
304  *
305  * Parameters  :
306  *          1  :  aspec = String specifying ACL address.
307  *          2  :  aca = struct access_control_addr to fill in.
308  *
309  * Returns     :  0 => Ok, everything else is an error.
310  *
311  *********************************************************************/
312 int acl_addr(char *aspec, struct access_control_addr *aca)
313 {
314    int i, masklength, port;
315    char *p;
316
317    masklength = 32;
318    port       =  0;
319
320    if ((p = strchr(aspec, '/')))
321    {
322       *p++ = '\0';
323
324       if (ijb_isdigit(*p) == 0)
325       {
326          return(-1);
327       }
328       masklength = atoi(p);
329    }
330
331    if ((masklength < 0) || (masklength > 32))
332    {
333       return(-1);
334    }
335
336    if ((p = strchr(aspec, ':')))
337    {
338       *p++ = '\0';
339
340       if (ijb_isdigit(*p) == 0)
341       {
342          return(-1);
343       }
344       port = atoi(p);
345    }
346
347    aca->port = port;
348
349    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
350
351    if (aca->addr == -1)
352    {
353       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
354       return(-1);
355    }
356
357    /* build the netmask */
358    aca->mask = 0;
359    for (i=1; i <= masklength ; i++)
360    {
361       aca->mask |= (1 << (32 - i));
362    }
363
364    /* now mask off the host portion of the ip address
365     * (i.e. save on the network portion of the address).
366     */
367    aca->addr = aca->addr & aca->mask;
368
369    return(0);
370
371 }
372 #endif /* def ACL_FILES */
373
374
375 /*********************************************************************
376  *
377  * Function    :  block_url
378  *
379  * Description :  Called from `chat'.  Check to see if we need to block this.
380  *
381  * Parameters  :
382  *          1  :  csp = Current client state (buffers, headers, etc...)
383  *
384  * Returns     :  NULL => unblocked, else HTTP block response
385  *
386  *********************************************************************/
387 struct http_response *block_url(struct client_state *csp)
388 {
389    char *p;
390         struct http_response *rsp;
391    struct map *exports = NULL;
392
393    /* 
394     * If it's not blocked, don't block it ;-)
395     */
396    if ((csp->action->flags & ACTION_BLOCK) == 0)
397    {
398       return(NULL);
399    }
400
401    /* 
402     * Else, prepare a response
403     */
404    if (NULL == ( rsp = (struct http_response *)zalloc(sizeof(*rsp))))
405    {
406       return NULL;
407    }
408
409    /*
410     * If it's an image-url, send back an image or redirect
411     * as specified by the relevant +image action
412     */
413 #ifdef IMAGE_BLOCKING
414         if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
415         && is_imageurl(csp))
416         {
417            /* determine HOW images should be blocked */
418       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
419
420       /* and handle accordingly: */
421       if ((p == NULL) || (0 == strcmpic(p, "logo")))
422       {
423          rsp->body = bindup(JBGIF, sizeof(JBGIF));
424          rsp->content_length = sizeof(JBGIF);
425          enlist_unique_header(rsp->headers, "Content-Type", "image/gif");
426       }
427
428       else if (0 == strcmpic(p, "blank"))
429       {
430          rsp->body = bindup(BLANKGIF, sizeof(BLANKGIF));
431          rsp->content_length = sizeof(BLANKGIF);
432          enlist_unique_header(rsp->headers, "Content-Type", "image/gif");
433       }
434
435       else
436       {
437          rsp->status = strdup("302 Local Redirect from Junkbuster");
438          enlist_unique_header(rsp->headers, "Location", p);
439       }
440    }  
441    else
442 #endif /* def IMAGE_BLOCKING */
443
444    /* 
445     * Else, generate an HTML "blocked" message:
446     */
447    {
448
449            exports = default_exports(csp, NULL);           
450 #ifdef FORCE_LOAD
451       exports = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
452 #else
453       exports = map_block_killer(exports, "force-support");
454 #endif /* ndef FORCE_LOAD */
455
456       exports = map(exports, "hostport", 1, csp->http->hostport, 1);
457       exports = map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
458       exports = map(exports, "path", 1, csp->http->path, 1);
459       exports = map(exports, "path-html", 1, html_encode(csp->http->path), 0);
460
461       rsp->body = fill_template(csp, "blocked", exports);
462       free_map(exports);
463   
464       rsp->status = strdup("403 Request for blocked URL"); 
465    }
466
467    return(finish_http_response(rsp));
468
469 }
470
471
472 #ifdef TRUST_FILES
473 /*********************************************************************
474  *
475  * Function    :  trust_url FIXME: I should be called distrust_url
476  *
477  * Description :  Calls is_untrusted_url to determine if the URL is trusted
478  *                and if not, returns a HTTP 304 response with a reject message.
479  *
480  * Parameters  :
481  *          1  :  csp = Current client state (buffers, headers, etc...)
482  *
483  * Returns     :  NULL => trusted, else http_response.
484  *
485  *********************************************************************/
486 struct http_response *trust_url(struct client_state *csp)
487 {
488    struct http_response *rsp;
489    struct map *exports = NULL;
490    char buf[BUFFER_SIZE], *p = NULL;
491    struct url_spec **tl, *t;
492
493    /*
494     * Don't bother to work on trusted URLs
495     */
496    if (!is_untrusted_url(csp))
497    {
498      return NULL;
499    }
500
501    /* 
502     * Else, prepare a response:
503     */
504    if (NULL == ( rsp = (struct http_response *)zalloc(sizeof(*rsp))))
505    {
506       return NULL;
507    }
508    exports = default_exports(csp, NULL);
509
510    /* 
511     * Export the host, port, and referrer information
512          */
513    exports = map(exports, "hostport", 1, csp->http->hostport, 1);
514    exports = map(exports, "path", 1, csp->http->path, 1);
515    exports = map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
516    exports = map(exports, "path-html", 1, html_encode(csp->http->path), 0);
517
518    if (csp->referrer && strlen(csp->referrer) > 9)
519    {
520       exports = map(exports, "referrer", 1, csp->referrer + 9, 1);
521       exports = map(exports, "referrer-html", 1, html_encode(csp->referrer + 9), 0);
522    }
523    else
524    {
525       exports = map(exports, "referrer", 1, "unknown", 1);
526       exports = map(exports, "referrer-html", 1, "unknown", 1);
527    }
528
529    /*
530     * Export the trust list
531     */
532    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
533    {
534       sprintf(buf, "<li>%s</li>\n", t->spec);
535       p = strsav(p, buf);
536    }
537    exports = map(exports, "trusted-referrers", 1, p, 0);
538    p = NULL;
539
540    /*
541     * Export the trust info, if available
542     */
543    if (csp->config->trust_info->next)
544    {
545       struct list *l;
546
547       for (l = csp->config->trust_info->next; l ; l = l->next)
548       {
549          sprintf(buf,
550             "<li> <a href=%s>%s</a><br>\n",
551                l->str, l->str);
552          p = strsav(p, buf);
553       }
554       exports = map(exports, "trust-info", 1, p, 0);
555    }
556    else
557         {
558            exports = map_block_killer(exports, "have-trust-info");
559         }
560    
561    /*
562     * Export the force prefix or the force conditional block killer
563     */
564 #ifdef FORCE_LOAD
565       exports = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
566 #else
567       exports = map_block_killer(exports, "force-support");
568 #endif /* ndef FORCE_LOAD */
569
570    /*
571     * Build the response
572     */
573    rsp->body = fill_template(csp, "untrusted", exports);
574    free_map(exports);
575
576    return(finish_http_response(rsp));
577
578 }
579 #endif /* def TRUST_FILES */
580
581
582 #ifdef FAST_REDIRECTS
583 /*********************************************************************
584  *
585  * Function    :  redirect_url
586  *
587  * Description :  Checks for redirection URLs and returns a HTTP redirect
588  *                to the destination URL, if necessary
589  *
590  * Parameters  :
591  *          1  :  csp = Current client state (buffers, headers, etc...)
592  *
593  * Returns     :  NULL if URL was clean, HTTP redirect otherwise.
594  *
595  *********************************************************************/
596 struct http_response *redirect_url(struct client_state *csp)
597 {
598    char *p, *q;
599    struct http_response *rsp;
600
601    p = q = csp->http->path;
602    log_error(LOG_LEVEL_REDIRECTS, "checking path for redirects: %s", p);
603
604    /* 
605     * find the last URL encoded in the request
606     */
607    while (p = strstr(p, "http://"))
608    {
609       q = p++;
610    }
611
612    /* 
613     * if there was any, generate and return a HTTP redirect
614     */
615    if (q != csp->http->path)
616    {
617       log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q);
618
619       if (NULL == ( rsp = zalloc(sizeof(*rsp))))
620       {
621          return NULL;
622       }
623
624       rsp->status = strdup("302 Local Redirect from Junkbuster");
625       enlist_unique_header(rsp->headers, "Location", q);
626
627       return(finish_http_response(rsp));
628    }
629    else
630    {
631       return(NULL);
632    }
633
634 }
635 #endif /* def FAST_REDIRECTS */
636
637
638 #ifdef IMAGE_BLOCKING
639 /*********************************************************************
640  *
641  * Function    :  is_imageurl
642  *
643  * Description :  Given a URL, decide whether it is an image or not,
644  *                using either the info from a previous +image action
645  *                or, #ifdef DETECT_MSIE_IMAGES, the info from the
646  *                browser's accept header.
647  *                
648  * Parameters  :
649  *          1  :  csp = Current client state (buffers, headers, etc...)
650  *
651  * Returns     :  True (nonzero) if URL is an image, false (0)
652  *                otherwise
653  *
654  *********************************************************************/
655 int is_imageurl(struct client_state *csp)
656 {
657 #ifdef DETECT_MSIE_IMAGES
658    if ((csp->accept_types 
659        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
660        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
661    {
662       return 1;
663    }
664    else if ((csp->accept_types 
665        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
666        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
667    {
668       return 0;
669    }
670 #endif
671
672    return ((csp->action->flags & ACTION_IMAGE) != 0);
673
674 }
675 #endif /* def IMAGE_BLOCKING */
676
677
678 #ifdef TRUST_FILES
679 /*********************************************************************
680  *
681  * Function    :  is_untrusted_url
682  *
683  * Description :  Should we "distrust" this URL (and block it)?
684  *
685  *                Yes if it matches a line in the trustfile, or if the
686  *                    referrer matches a line starting with "+" in the
687  *                    trustfile.
688  *                No  otherwise.
689  *
690  * Parameters  :
691  *          1  :  csp = Current client state (buffers, headers, etc...)
692  *
693  * Returns     :  0 => trusted, 1 => untrusted
694  *
695  *********************************************************************/
696 int is_untrusted_url(struct client_state *csp)
697 {
698    struct file_list *fl;
699    struct block_spec *b;
700    struct url_spec url[1], **tl, *t;
701    struct http_request rhttp[1];
702    char *p, *h;
703
704    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
705    {
706       return(0);
707    }
708
709    *url = dsplit(csp->http->host);
710
711    /* if splitting the domain fails, punt */
712    if (url->dbuf == NULL) return(0);
713
714    memset(rhttp, '\0', sizeof(*rhttp));
715
716    for (b = b->next; b ; b = b->next)
717    {
718       if ((b->url->port == 0) || (b->url->port == csp->http->port))
719       {
720          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
721          {
722             if ((b->url->path == NULL) ||
723 #ifdef REGEX
724                (regexec(b->url->preg, csp->http->path, 0, NULL, 0) == 0)
725 #else
726                (strncmp(b->url->path, csp->http->path, b->url->pathlen) == 0)
727 #endif
728             )
729             {
730                freez(url->dbuf);
731                freez(url->dvec);
732
733                if (b->reject == 0) return(0);
734
735                return(1);
736             }
737          }
738       }
739    }
740
741    freez(url->dbuf);
742    freez(url->dvec);
743
744    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
745    {
746       /* no referrer was supplied */
747            return(1);
748    }
749
750    /* forge a URL from the referrer so we can use
751     * convert_url() to parse it into its components.
752     */
753
754    p = NULL;
755    p = strsav(p, "GET ");
756    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
757    p = strsav(p, " HTTP/1.0");
758
759    parse_http_request(p, rhttp, csp);
760    freez(p);
761
762    if (rhttp->cmd == NULL)
763    {
764       return(1);
765    }
766
767    *url = dsplit(rhttp->host);
768
769    /* if splitting the domain fails, punt */
770    if (url->dbuf == NULL) return(1);
771
772    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
773    {
774       if ((t->port == 0) || (t->port == rhttp->port))
775       {
776          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
777          {
778             if ((t->path == NULL) ||
779 #ifdef REGEX
780                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
781 #else
782                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
783 #endif
784             )
785             {
786                /* if the URL's referrer is from a trusted referrer, then
787                 * add the target spec to the trustfile as an unblocked
788                 * domain and return NULL (which means it's OK).
789                 */
790
791                FILE *fp;
792
793                freez(url->dbuf);
794                freez(url->dvec);
795
796                if ((fp = fopen(csp->config->trustfile, "a")))
797                {
798                   h = NULL;
799
800                   h = strsav(h, "~");
801                   h = strsav(h, csp->http->hostport);
802
803                   p = csp->http->path;
804                   if ((*p++ == '/')
805                   && (*p++ == '~'))
806                   {
807                   /* since this path points into a user's home space
808                    * be sure to include this spec in the trustfile.
809                    */
810                      if ((p = strchr(p, '/')))
811                      {
812                         *p = '\0';
813                         h = strsav(h, csp->http->path); /* FIXME: p?! */
814                         h = strsav(h, "/");
815                      }
816                   }
817
818                   fprintf(fp, "%s\n", h);
819                   freez(h);
820                   fclose(fp);
821                }
822                return(0);
823             }
824          }
825       }
826    }
827
828 }
829 #endif /* def TRUST_FILES */
830
831
832 #ifdef PCRS
833 /*********************************************************************
834  *
835  * Function    :  re_process_buffer
836  *
837  * Description :  Apply all the pcrs jobs from the joblist (re_filterfile)
838  *                to the text buffer that's been accumulated in 
839  *                csp->iob->buf and set csp->content_length to the modified
840  *                size.
841  *
842  * Parameters  :
843  *          1  :  csp = Current client state (buffers, headers, etc...)
844  *
845  * Returns     :  a pointer to the (newly allocated) modified buffer.
846  *                or an empty string in case something went wrong
847  *                
848  *********************************************************************/
849 char *re_process_buffer(struct client_state *csp)
850 {
851    int hits=0;
852    int size = csp->iob->eod - csp->iob->cur;
853
854    char *old = csp->iob->cur, *new = NULL;
855    pcrs_job *job;
856
857    struct file_list *fl;
858    struct re_filterfile_spec *b;
859
860    /* Sanity first ;-) */
861    if (size <= 0)
862    {
863       return(strdup(""));
864    }
865
866    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
867    {
868       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
869       return(strdup(""));
870    }
871
872    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
873               csp->http->hostport, csp->http->path, size);
874
875    /* Apply all jobs from the joblist */
876    for (job = b->joblist; NULL != job; job = job->next)
877    {
878       hits += pcrs_execute(job, old, size, &new, &size);
879       if (old != csp->iob->cur) free(old);
880       old=new;
881    }
882
883    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
884
885    csp->content_length = size;
886
887    /* fwiw, reset the iob */
888    IOB_RESET(csp);
889    return(new);
890
891 }
892 #endif /* def PCRS */
893
894
895 /*********************************************************************
896  *
897  * Function    :  url_actions
898  *
899  * Description :  Gets the actions for this URL.
900  *
901  * Parameters  :
902  *          1  :  http = http_request request for blocked URLs
903  *          2  :  csp = Current client state (buffers, headers, etc...)
904  *
905  * Returns     :  N/A
906  *
907  *********************************************************************/
908 void url_actions(struct http_request *http, 
909                  struct client_state *csp)
910 {
911    struct file_list *fl;
912    struct url_actions *b;
913
914    init_current_action(csp->action);
915
916    if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
917    {
918       return;
919    }
920
921    apply_url_actions(csp->action, http, b);
922 }
923
924
925 /*********************************************************************
926  *
927  * Function    :  apply_url_actions
928  *
929  * Description :  Applies a list of URL actions.
930  *
931  * Parameters  :
932  *          1  :  action = Destination.
933  *          2  :  http = Current URL
934  *          3  :  b = list of URL actions to apply
935  *
936  * Returns     :  N/A
937  *
938  *********************************************************************/
939 void apply_url_actions(struct current_action_spec *action, 
940                        struct http_request *http, 
941                        struct url_actions *b)
942 {
943    struct url_spec url[1];
944
945    if (b == NULL)
946    {
947       /* Should never happen */
948       return;
949    }
950
951    *url = dsplit(http->host);
952
953    /* if splitting the domain fails, punt */
954    if (url->dbuf == NULL)
955    {
956       return;
957    }
958
959    for (b = b->next; NULL != b; b = b->next)
960    {
961       if ((b->url->port == 0) || (b->url->port == http->port))
962       {
963          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
964          {
965             if ((b->url->path == NULL) ||
966 #ifdef REGEX
967                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
968 #else
969                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
970 #endif
971             )
972             {
973                merge_current_action(action, b->action);
974             }
975          }
976       }
977    }
978
979    freez(url->dbuf);
980    freez(url->dvec);
981 }
982
983
984 /*********************************************************************
985  *
986  * Function    :  forward_url
987  *
988  * Description :  Should we forward this to another proxy?
989  *
990  * Parameters  :
991  *          1  :  http = http_request request for current URL
992  *          2  :  csp = Current client state (buffers, headers, etc...)
993  *
994  * Returns     :  Pointer to forwarding information.
995  *
996  *********************************************************************/
997 const struct forward_spec * forward_url(struct http_request *http,
998                                         struct client_state *csp)
999 {
1000    static const struct forward_spec fwd_default[1] = { 0 }; /* All zeroes */
1001    struct forward_spec *fwd = csp->config->forward;
1002    struct url_spec url[1];
1003
1004    if (fwd == NULL)
1005    {
1006       return(fwd_default);
1007    }
1008
1009    *url = dsplit(http->host);
1010
1011    /* if splitting the domain fails, punt */
1012    if (url->dbuf == NULL)
1013    {
1014       return(fwd_default);
1015    }
1016
1017    while (fwd != NULL)
1018    {
1019       if ((fwd->url->port == 0) || (fwd->url->port == http->port))
1020       {
1021          if ((fwd->url->domain[0] == '\0') || (domaincmp(fwd->url, url) == 0))
1022          {
1023             if ((fwd->url->path == NULL) ||
1024 #ifdef REGEX
1025                (regexec(fwd->url->preg, http->path, 0, NULL, 0) == 0)
1026 #else
1027                (strncmp(fwd->url->path, http->path, fwd->url->pathlen) == 0)
1028 #endif
1029             )
1030             {
1031                freez(url->dbuf);
1032                freez(url->dvec);
1033                return(fwd);
1034             }
1035          }
1036       }
1037       fwd = fwd->next;
1038    }
1039
1040    freez(url->dbuf);
1041    freez(url->dvec);
1042    return(fwd_default);
1043
1044 }
1045
1046
1047 /*********************************************************************
1048  *
1049  * Function    :  dsplit
1050  *
1051  * Description :  Takes a domain and returns a pointer to a url_spec
1052  *                structure populated with dbuf, dcnt and dvec.  The
1053  *                other fields in the structure that is returned are zero.
1054  *
1055  * Parameters  :
1056  *          1  :  domain = a URL address
1057  *
1058  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
1059  *
1060  *********************************************************************/
1061 struct url_spec dsplit(char *domain)
1062 {
1063    struct url_spec ret[1];
1064    char *v[BUFFER_SIZE];
1065    int size;
1066    char *p;
1067
1068    memset(ret, '\0', sizeof(*ret));
1069
1070    if (domain[strlen(domain) - 1] == '.')
1071    {
1072           ret->unanchored |= ANCHOR_RIGHT;
1073         }
1074         if (domain[0] == '.')
1075    {
1076           ret->unanchored |= ANCHOR_LEFT;
1077         }
1078
1079    ret->dbuf = strdup(domain);
1080
1081    /* map to lower case */
1082    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
1083
1084    /* split the domain name into components */
1085    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
1086
1087    if (ret->dcnt <= 0)
1088    {
1089       memset(ret, '\0', sizeof(ret));
1090       return(*ret);
1091    }
1092
1093    /* save a copy of the pointers in dvec */
1094    size = ret->dcnt * sizeof(*ret->dvec);
1095
1096    if ((ret->dvec = (char **)malloc(size)))
1097    {
1098       memcpy(ret->dvec, v, size);
1099    }
1100
1101
1102    return(*ret);
1103
1104 }
1105
1106
1107 /*********************************************************************
1108  *
1109  * Function    :  simple_domaincmp
1110  *
1111  * Description :  Domain-wise Compare fqdn's.  The comparison is 
1112  *                both left- and right-anchored.  The individual
1113  *                domain names are compared with simplematch().
1114  *                This is only used by domaincmp.
1115  *
1116  * Parameters  :
1117  *          1  :  pv = array of patterns to compare
1118  *          2  :  fv = array of domain components to compare
1119  *          3  :  len = length of the arrays (both arrays are the
1120  *                      same length - if they weren't, it couldn't
1121  *                      possibly be a match).
1122  *
1123  * Returns     :  0 => domains are equivalent, else no match.
1124  *
1125  *********************************************************************/
1126 static int simple_domaincmp(char **pv, char **fv, int len)
1127 {
1128    int n;
1129
1130    for (n = 0; n < len; n++)
1131    {
1132       if (simplematch(pv[n], fv[n]))
1133       {
1134          return 1;
1135       }
1136    }
1137
1138    return 0;
1139 }
1140
1141
1142 /*********************************************************************
1143  *
1144  * Function    :  domaincmp
1145  *
1146  * Description :  Domain-wise Compare fqdn's. Governed by the bimap in
1147  *                pattern->unachored, the comparison is un-, left-,
1148  *                right-anchored, or both.
1149  *                The individual domain names are compared with
1150  *                simplematch().
1151  *
1152  * Parameters  :
1153  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
1154  *          2  :  fqdn = domain name against which the patterns are compared.
1155  *
1156  * Returns     :  0 => domains are equivalent, else no match.
1157  *
1158  *********************************************************************/
1159 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
1160 {
1161    char **pv, **fv;  /* vectors  */
1162    int    plen, flen;
1163    int unanchored = pattern->unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
1164
1165    plen = pattern->dcnt;
1166    flen = fqdn->dcnt;
1167
1168    if (flen < plen)
1169    {
1170       /* fqdn is too short to match this pattern */
1171       return 1;
1172    }
1173
1174    pv   = pattern->dvec;
1175    fv   = fqdn->dvec;
1176
1177    if (unanchored == ANCHOR_LEFT)
1178    {
1179       /*
1180        * Right anchored.
1181        *
1182        * Convert this into a fully anchored pattern with
1183        * the fqdn and pattern the same length
1184        */
1185       fv += (flen - plen); /* flen - plen >= 0 due to check above */
1186       return simple_domaincmp(pv, fv, plen);
1187    }
1188    else if (unanchored == 0)
1189    {
1190       /* Fully anchored, check length */
1191       if (flen != plen)
1192       {
1193          return 1;
1194       }
1195       return simple_domaincmp(pv, fv, plen);
1196    }
1197    else if (unanchored == ANCHOR_RIGHT)
1198    {
1199       /* Left anchored, ignore all extra in fqdn */
1200       return simple_domaincmp(pv, fv, plen);
1201    }
1202    else
1203    {
1204       /* Unanchored */
1205       int n;
1206       int maxn = flen - plen;
1207       for (n = 0; n <= maxn; n++)
1208       {
1209          if (!simple_domaincmp(pv, fv, plen))
1210          {
1211             return 0;
1212          }
1213          /*
1214           * Doesn't match from start of fqdn
1215           * Try skipping first part of fqdn
1216           */
1217          fv++;
1218       }
1219       return 1;
1220    }
1221 }
1222
1223
1224
1225 /*
1226   Local Variables:
1227   tab-width: 3
1228   end:
1229 */