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