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