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