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