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