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