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