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