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