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