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