Update all patches to CVS version as of 2007-10-14.
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.93 2007/09/29 10:21:16 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', `execute_single_pcrs_command',
14  *                   `rewrite_url', `get_last_url'
15  *
16  * Copyright   :  Written by and Copyright (C) 2001, 2004-2007 the SourceForge
17  *                Privoxy team. http://www.privoxy.org/
18  *
19  *                Based on the Internet Junkbuster originally written
20  *                by and Copyright (C) 1997 Anonymous Coders and
21  *                Junkbusters Corporation.  http://www.junkbusters.com
22  *
23  *                This program is free software; you can redistribute it
24  *                and/or modify it under the terms of the GNU General
25  *                Public License as published by the Free Software
26  *                Foundation; either version 2 of the License, or (at
27  *                your option) any later version.
28  *
29  *                This program is distributed in the hope that it will
30  *                be useful, but WITHOUT ANY WARRANTY; without even the
31  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
32  *                PARTICULAR PURPOSE.  See the GNU General Public
33  *                License for more details.
34  *
35  *                The GNU General Public License should be included with
36  *                this file.  If not, you can view it at
37  *                http://www.gnu.org/copyleft/gpl.html
38  *                or write to the Free Software Foundation, Inc., 59
39  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
40  *
41  * Revisions   :
42  *    $Log: filters.c,v $
43  *    Revision 1.93  2007/09/29 10:21:16  fabiankeil
44  *    - Move get_filter_function() from jcc.c to filters.c
45  *      so the filter functions can be static.
46  *    - Don't bother filtering body-less responses.
47  *
48  *    Revision 1.92  2007/09/28 16:38:55  fabiankeil
49  *    - Execute content filters through execute_content_filter().
50  *    - Add prepare_for_filtering() so filter functions don't have to
51  *      care about de-chunking and decompression. As a side effect this enables
52  *      decompression for gif_deanimate_response() and jpeg_inspect_response().
53  *    - Change remove_chunked_transfer_coding()'s return type to jb_err.
54  *      Some clowns feel like chunking empty responses in which case
55  *      (size == 0) is valid but previously would be interpreted as error.
56  *
57  *    Revision 1.91  2007/09/02 15:31:20  fabiankeil
58  *    Move match_portlist() from filter.c to urlmatch.c.
59  *    It's used for url matching, not for filtering.
60  *
61  *    Revision 1.90  2007/09/02 12:44:17  fabiankeil
62  *    Remove newline at the end of a log_error() message.
63  *
64  *    Revision 1.89  2007/08/05 13:42:23  fabiankeil
65  *    #1763173 from Stefan Huehner: declare some more functions static.
66  *
67  *    Revision 1.88  2007/06/01 16:41:11  fabiankeil
68  *    Add forward-override{} to change the forwarding settings through
69  *    action sections. This is mainly interesting to forward different
70  *    clients differently (for example based on User-Agent or request
71  *    origin).
72  *
73  *    Revision 1.87  2007/04/30 15:53:10  fabiankeil
74  *    Make sure filters with dynamic jobs actually use them.
75  *
76  *    Revision 1.86  2007/04/30 15:03:28  fabiankeil
77  *    - Introduce dynamic pcrs jobs that can resolve variables.
78  *    - Don't run redirect functions more than once,
79  *      unless they are activated more than once.
80  *
81  *    Revision 1.85  2007/03/21 12:24:47  fabiankeil
82  *    - Log the content size after decompression in decompress_iob()
83  *      instead of pcrs_filter_response().
84  *
85  *    Revision 1.84  2007/03/20 15:16:34  fabiankeil
86  *    Use dedicated header filter actions instead of abusing "filter".
87  *    Replace "filter-client-headers" and "filter-client-headers"
88  *    with "server-header-filter" and "client-header-filter".
89  *
90  *    Revision 1.83  2007/03/17 15:20:05  fabiankeil
91  *    New config option: enforce-blocks.
92  *
93  *    Revision 1.82  2007/03/13 11:28:43  fabiankeil
94  *    - Fix port handling in acl_addr() and use a temporary acl spec
95  *      copy so error messages don't contain a truncated version.
96  *    - Log size of iob before and after decompression.
97  *
98  *    Revision 1.81  2007/03/05 14:40:53  fabiankeil
99  *    - Cosmetical changes for LOG_LEVEL_RE_FILTER messages.
100  *    - Hide the "Go there anyway" link for blocked CONNECT
101  *      requests where going there anyway doesn't work anyway.
102  *
103  *    Revision 1.80  2007/02/07 10:55:20  fabiankeil
104  *    - Save the reason for generating http_responses.
105  *    - Block (+block) with status code 403 instead of 404.
106  *    - Use a different kludge to remember a failed decompression.
107  *
108  *    Revision 1.79  2007/01/31 16:21:38  fabiankeil
109  *    Search for Max-Forwards headers case-insensitive,
110  *    don't generate the "501 unsupported" message for invalid
111  *    Max-Forwards values and don't increase negative ones.
112  *
113  *    Revision 1.78  2007/01/28 13:41:18  fabiankeil
114  *    - Add HEAD support to finish_http_response.
115  *    - Add error favicon to internal HTML error messages.
116  *
117  *    Revision 1.77  2007/01/12 15:36:44  fabiankeil
118  *    Mark *csp as immutable for is_untrusted_url()
119  *    and is_imageurl(). Closes FR 1237736.
120  *
121  *    Revision 1.76  2007/01/01 19:36:37  fabiankeil
122  *    Integrate a modified version of Wil Mahan's
123  *    zlib patch (PR #895531).
124  *
125  *    Revision 1.75  2006/12/29 18:30:46  fabiankeil
126  *    Fixed gcc43 conversion warnings,
127  *    changed sprintf calls to snprintf.
128  *
129  *    Revision 1.74  2006/12/24 17:37:38  fabiankeil
130  *    Adjust comment in pcrs_filter_response()
131  *    to recent pcrs changes. Hohoho.
132  *
133  *    Revision 1.73  2006/12/23 16:01:02  fabiankeil
134  *    Don't crash if pcre returns an error code
135  *    that pcrs didn't expect. Fixes BR 1621173.
136  *
137  *    Revision 1.72  2006/12/22 18:52:53  fabiankeil
138  *    Modified is_untrusted_url to complain in case of
139  *    write errors and to give a reason when adding new
140  *    entries to the trustfile. Closes FR 1097611.
141  *
142  *    Revision 1.71  2006/12/22 14:24:52  fabiankeil
143  *    Skip empty filter files in pcrs_filter_response,
144  *    but don't ignore the ones that come afterwards.
145  *    Fixes parts of BR 1619208.
146  *
147  *    Revision 1.70  2006/12/09 13:33:15  fabiankeil
148  *    Added some sanity checks for get_last_url().
149  *    Fixed possible segfault caused by my last commit.
150  *
151  *    Revision 1.69  2006/12/08 12:39:13  fabiankeil
152  *    Let get_last_url() catch https URLs as well.
153  *
154  *    Revision 1.68  2006/12/05 14:45:48  fabiankeil
155  *    Make sure get_last_url() behaves like advertised
156  *    and fast-redirects{} can be combined with redirect{}.
157  *
158  *    Revision 1.67  2006/11/28 15:19:43  fabiankeil
159  *    Implemented +redirect{s@foo@bar@} to generate
160  *    a redirect based on a rewritten version of the
161  *    original URL.
162  *
163  *    Revision 1.66  2006/09/23 13:26:38  roro
164  *    Replace TABs by spaces in source code.
165  *
166  *    Revision 1.65  2006/09/21 12:54:43  fabiankeil
167  *    Fix +redirect{}. Didn't work with -fast-redirects.
168  *
169  *    Revision 1.64  2006/08/31 10:55:49  fabiankeil
170  *    Block requests for untrusted URLs with status
171  *    code 403 instead of 200.
172  *
173  *    Revision 1.63  2006/08/31 10:11:28  fabiankeil
174  *    Don't free p which is still in use and will be later
175  *    freed by free_map(). Don't claim the referrer is unknown
176  *    when the client didn't set one.
177  *
178  *    Revision 1.62  2006/08/14 00:27:47  david__schmidt
179  *    Feature request 595948: Re-Filter logging in single line
180  *
181  *    Revision 1.61  2006/08/03 02:46:41  david__schmidt
182  *    Incorporate Fabian Keil's patch work:\rhttp://www.fabiankeil.de/sourcecode/privoxy/
183  *
184  *    Revision 1.60  2006/07/18 14:48:46  david__schmidt
185  *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
186  *    with what was really the latest development (the v_3_0_branch branch)
187  *
188  *    Revision 1.58.2.9  2006/01/29 23:10:56  david__schmidt
189  *    Multiple filter file support
190  *
191  *    Revision 1.58.2.8  2005/05/07 21:50:55  david__schmidt
192  *    A few memory leaks plugged (mostly on error paths)
193  *
194  *    Revision 1.58.2.7  2004/10/03 12:53:32  david__schmidt
195  *    Add the ability to check jpeg images for invalid
196  *    lengths of comment blocks.  Defensive strategy
197  *    against the exploit:
198  *       Microsoft Security Bulletin MS04-028
199  *       Buffer Overrun in JPEG Processing (GDI+) Could
200  *       Allow Code Execution (833987)
201  *    Enabled with +inspect-jpegs in actions files.
202  *
203  *    Revision 1.58.2.6  2003/12/06 22:18:27  gliptak
204  *    Correcting compile problem with FEATURE_IMAGE_BLOCKING
205  *
206  *    Revision 1.58.2.5  2003/11/11 13:10:31  oes
207  *    Fixed bug #839859: "See why" link URL now gets url-encoded.
208  *
209  *    Revision 1.58.2.4  2003/02/28 12:52:45  oes
210  *    Fixed a typo
211  *
212  *    Revision 1.58.2.3  2002/09/25 14:51:51  oes
213  *    Added basic support for OPTIONS and TRACE HTTP methods:
214  *    New function direct_response which handles OPTIONS and
215  *    TRACE requests whose Max-Forwards header field is zero.
216  *
217  *    Revision 1.58.2.2  2002/08/01 17:18:28  oes
218  *    Fixed BR 537651 / SR 579724 (MSIE image detect improper for IE/Mac)
219  *
220  *    Revision 1.58.2.1  2002/07/26 15:18:53  oes
221  *    - Bugfix: Executing a filters without jobs no longer results in
222  *      turing off *all* filters.
223  *    - Security fix: Malicious web servers can't cause a seg fault
224  *      through bogus chunk sizes anymore
225  *
226  *    Revision 1.58  2002/04/24 02:11:17  oes
227  *    Jon's multiple AF patch: url_actions now evaluates rules
228  *    from all AFs.
229  *
230  *    Revision 1.57  2002/04/08 20:38:34  swa
231  *    fixed JB spelling
232  *
233  *    Revision 1.56  2002/04/05 15:51:24  oes
234  *     - bugfix: error-pages now get correct request protocol
235  *     - fix for invalid HTML in trust info
236  *
237  *    Revision 1.55  2002/04/02 16:13:51  oes
238  *    Fix: No "Go there anyway" for SSL
239  *
240  *    Revision 1.54  2002/04/02 14:55:56  oes
241  *    Bugfix: is_untrusted_url() now depends on FEATURE_TRUST, not FEATURE_COOKIE_JAR
242  *
243  *    Revision 1.53  2002/03/26 22:29:54  swa
244  *    we have a new homepage!
245  *
246  *    Revision 1.52  2002/03/24 16:35:57  jongfoster
247  *    Removing logo
248  *
249  *    Revision 1.51  2002/03/24 15:23:33  jongfoster
250  *    Name changes
251  *
252  *    Revision 1.50  2002/03/24 13:25:43  swa
253  *    name change related issues
254  *
255  *    Revision 1.49  2002/03/16 20:29:14  oes
256  *    Cosmetics
257  *
258  *    Revision 1.48  2002/03/13 20:25:34  oes
259  *    Better logging for content filters
260  *
261  *    Revision 1.47  2002/03/13 00:30:52  jongfoster
262  *    Killing warnings
263  *    Added option of always sending redirect for imageblock,
264  *    currently disabled with #if 0.
265  *
266  *    Revision 1.46  2002/03/12 01:42:49  oes
267  *    Introduced modular filters
268  *
269  *    Revision 1.45  2002/03/08 16:47:50  oes
270  *    Added choice beween GIF and PNG built-in images
271  *
272  *    Revision 1.44  2002/03/07 03:49:31  oes
273  *     - Fixed compiler warnings etc
274  *     - Changed built-in images from GIF to PNG
275  *       (with regard to Unisys patent issue)
276  *     - Added a 4x4 pattern PNG which is less intrusive
277  *       than the logo but also clearly marks the deleted banners
278  *
279  *    Revision 1.43  2002/01/22 23:51:59  jongfoster
280  *    Replacing strsav() with the safer string_append().
281  *
282  *    Adding missing html_encode() to error message generators.  Where encoded
283  *    and unencoded versions of a string were provided, removing the unencoded
284  *    one.
285  *
286  *    Revision 1.42  2002/01/17 21:00:32  jongfoster
287  *    Moving all our URL and URL pattern parsing code to urlmatch.c.
288  *
289  *    Using a single, simple url_match(pattern,url) function - rather than
290  *    the 3-line match routine which was repeated all over the place.
291  *
292  *    Renaming free_url to free_url_spec, since it frees a struct url_spec.
293  *
294  *    Using parse_http_url() to parse URLs without faking a HTTP
295  *    request line for parse_http_request().
296  *
297  *    Revision 1.41  2001/11/13 00:14:07  jongfoster
298  *    Fixing stupid bug now I've figured out what || means.
299  *    (It always returns 0 or 1, not one of it's paramaters.)
300  *
301  *    Revision 1.40  2001/10/26 17:37:55  oes
302  *    - Re-enabled Netscape 200/404 bug workaround in block_url():
303  *      - Removed OS/2 special case
304  *      - Made block_url() independant from sed() having been run
305  *    - Made trust_url independant from sed() having been run
306  *    - Made is_imageurl independant from sed() having been run.
307  *      It now checks User-Agent: and Accept: by itself.
308  *
309  *
310  *    Revision 1.39  2001/10/25 03:40:48  david__schmidt
311  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
312  *    threads to call select() simultaneously.  So, it's time to do a real, live,
313  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
314  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
315  *
316  *    Revision 1.38  2001/10/23 21:32:33  jongfoster
317  *    Adding error-checking to selected functions
318  *
319  *    Revision 1.37  2001/10/22 15:33:56  david__schmidt
320  *    Special-cased OS/2 out of the Netscape-abort-on-404-in-js problem in
321  *    filters.c.  Added a FIXME in front of the offending code.  I'll gladly
322  *    put in a better/more robust fix for all parties if one is presented...
323  *    It seems that just returning 200 instead of 404 would pretty much fix
324  *    it for everyone, but I don't know all the history of the problem.
325  *
326  *    Revision 1.36  2001/10/10 16:44:16  oes
327  *    Added match_portlist function
328  *
329  *    Revision 1.35  2001/10/07 15:41:23  oes
330  *    Replaced 6 boolean members of csp with one bitmap (csp->flags)
331  *
332  *    New function remove_chunked_transfer_coding that strips chunked
333  *      transfer coding to plain and is called by pcrs_filter_response
334  *      and gif_deanimate_response if neccessary
335  *
336  *    Improved handling of zero-change re_filter runs
337  *
338  *    pcrs_filter_response and gif_deanimate_response now remove
339  *      chunked transfer codeing before processing the body.
340  *
341  *    Revision 1.34  2001/09/20 15:49:36  steudten
342  *
343  *    Fix BUG: Change int size to size_t size in pcrs_filter_response().
344  *    See cgi.c fill_template().
345  *
346  *    Revision 1.33  2001/09/16 17:05:14  jongfoster
347  *    Removing unused #include showarg.h
348  *
349  *    Revision 1.32  2001/09/16 13:21:27  jongfoster
350  *    Changes to use new list functions.
351  *
352  *    Revision 1.31  2001/09/16 11:38:02  jongfoster
353  *    Splitting fill_template() into 2 functions:
354  *    template_load() loads the file
355  *    template_fill() performs the PCRS regexps.
356  *    This is because the CGI edit interface has a "table row"
357  *    template which is used many times in the page - this
358  *    change means it's only loaded from disk once.
359  *
360  *    Revision 1.30  2001/09/16 11:00:10  jongfoster
361  *    New function alloc_http_response, for symmetry with free_http_response
362  *
363  *    Revision 1.29  2001/09/13 23:32:40  jongfoster
364  *    Moving image data to cgi.c rather than cgi.h
365  *    Fixing a GPF under Win32 (and any other OS that protects global
366  *    constants from being written to).
367  *
368  *    Revision 1.28  2001/09/10 10:18:51  oes
369  *    Silenced compiler warnings
370  *
371  *    Revision 1.27  2001/08/05 16:06:20  jongfoster
372  *    Modifiying "struct map" so that there are now separate header and
373  *    "map_entry" structures.  This means that functions which modify a
374  *    map no longer need to return a pointer to the modified map.
375  *    Also, it no longer reverses the order of the entries (which may be
376  *    important with some advanced template substitutions).
377  *
378  *    Revision 1.26  2001/07/30 22:08:36  jongfoster
379  *    Tidying up #defines:
380  *    - All feature #defines are now of the form FEATURE_xxx
381  *    - Permanently turned off WIN_GUI_EDIT
382  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
383  *
384  *    Revision 1.25  2001/07/26 10:09:46  oes
385  *    Made browser detection a little less naive
386  *
387  *    Revision 1.24  2001/07/25 17:22:51  oes
388  *    Added workaround for Netscape bug that prevents display of page when loading a component fails.
389  *
390  *    Revision 1.23  2001/07/23 13:40:12  oes
391  *    Fixed bug that caused document body to be dropped when pcrs joblist was empty.
392  *
393  *    Revision 1.22  2001/07/18 12:29:34  oes
394  *    - Made gif_deanimate_response respect
395  *      csp->action->string[ACTION_STRING_DEANIMATE]
396  *    - Logging cosmetics
397  *
398  *    Revision 1.21  2001/07/13 13:59:53  oes
399  *     - Introduced gif_deanimate_response which shares the
400  *       generic content modification interface of pcrs_filter_response
401  *       and acts as a wrapper to deanimate.c:gif_deanimate()
402  *     - Renamed re_process_buffer to pcrs_filter_response
403  *     - pcrs_filter_response now returns NULL on failiure
404  *     - Removed all #ifdef PCRS
405  *
406  *    Revision 1.20  2001/07/01 17:01:04  oes
407  *    Added comments and missing return statement in is_untrusted_url()
408  *
409  *    Revision 1.19  2001/06/29 21:45:41  oes
410  *    Indentation, CRLF->LF, Tab-> Space
411  *
412  *    Revision 1.18  2001/06/29 13:27:38  oes
413  *    - Cleaned up, renamed and reorderd functions
414  *      and improved comments
415  *
416  *    - block_url:
417  *      - Ported to CGI platform. Now delivers
418  *        http_response or NULL
419  *      - Unified HTML and GIF generation (moved image detection
420  *        and GIF generation here from jcc.c:chat())
421  *      - Fixed HTTP status to:
422  *       -  403 (Forbidden) for the "blocked" HTML message
423  *       -  200 (OK) for GIF answers
424  *       -  302 (Redirect) for redirect to GIF
425  *
426  *    - trust_url:
427  *      - Ported to CGI platform. Now delivers
428  *        http_response or NULL
429  *      - Separated detection of untrusted URL into
430  *        (bool)is_untrusted_url
431  *      - Added enforcement of untrusted requests
432  *
433  *    - Moved redirect_url() from cgi.c to here
434  *      and ported it to the CGI platform
435  *
436  *    - Removed logentry from cancelled commit
437  *
438  *    Revision 1.17  2001/06/09 10:55:28  jongfoster
439  *    Changing BUFSIZ ==> BUFFER_SIZE
440  *
441  *    Revision 1.16  2001/06/07 23:10:26  jongfoster
442  *    Allowing unanchored domain patterns to back off and retry
443  *    if they partially match.  Optimized right-anchored patterns.
444  *    Moving ACL and forward files into config file.
445  *    Replacing struct gateway with struct forward_spec
446  *
447  *    Revision 1.15  2001/06/03 19:12:00  oes
448  *    extracted-CGI relevant stuff
449  *
450  *    Revision 1.14  2001/06/01 10:30:55  oes
451  *    Added optional left-anchoring to domaincmp
452  *
453  *    Revision 1.13  2001/05/31 21:21:30  jongfoster
454  *    Permissionsfile / actions file changes:
455  *    - Changed "permission" to "action" throughout
456  *    - changes to file format to allow string parameters
457  *    - Moved helper functions to actions.c
458  *
459  *    Revision 1.12  2001/05/31 17:35:20  oes
460  *
461  *     - Enhanced domain part globbing with infix and prefix asterisk
462  *       matching and optional unanchored operation
463  *
464  *    Revision 1.11  2001/05/29 11:53:23  oes
465  *    "See why" link added to "blocked" page
466  *
467  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
468  *    Unified blocklist/imagelist/permissionslist.
469  *    File format is still under discussion, but the internal changes
470  *    are (mostly) done.
471  *
472  *    Also modified interceptor behaviour:
473  *    - We now intercept all URLs beginning with one of the following
474  *      prefixes (and *only* these prefixes):
475  *        * http://i.j.b/
476  *        * http://ijbswa.sf.net/config/
477  *        * http://ijbswa.sourceforge.net/config/
478  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
479  *    - Internal changes so that intercepted and fast redirect pages
480  *      are not replaced with an image.
481  *    - Interceptors now have the option to send a binary page direct
482  *      to the client. (i.e. ijb-send-banner uses this)
483  *    - Implemented show-url-info interceptor.  (Which is why I needed
484  *      the above interceptors changes - a typical URL is
485  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
486  *      The previous mechanism would not have intercepted that, and
487  *      if it had been intercepted then it then it would have replaced
488  *      it with an image.)
489  *
490  *    Revision 1.9  2001/05/27 22:17:04  oes
491  *
492  *    - re_process_buffer no longer writes the modified buffer
493  *      to the client, which was very ugly. It now returns the
494  *      buffer, which it is then written by chat.
495  *
496  *    - content_length now adjusts the Content-Length: header
497  *      for modified documents rather than crunch()ing it.
498  *      (Length info in csp->content_length, which is 0 for
499  *      unmodified documents)
500  *
501  *    - For this to work, sed() is called twice when filtering.
502  *
503  *    Revision 1.8  2001/05/26 17:13:28  jongfoster
504  *    Filled in a function comment.
505  *
506  *    Revision 1.7  2001/05/26 15:26:15  jongfoster
507  *    ACL feature now provides more security by immediately dropping
508  *    connections from untrusted hosts.
509  *
510  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
511  *    Automatic reloading of config file.
512  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
513  *    Most of the global variables have been moved to a new
514  *    struct configuration_spec, accessed through csp->config->globalname
515  *    Most of the globals remaining are used by the Win32 GUI.
516  *
517  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
518  *    Hard tabs->Spaces
519  *
520  *    Revision 1.4  2001/05/22 18:46:04  oes
521  *
522  *    - Enabled filtering banners by size rather than URL
523  *      by adding patterns that replace all standard banner
524  *      sizes with the "Junkbuster" gif to the re_filterfile
525  *
526  *    - Enabled filtering WebBugs by providing a pattern
527  *      which kills all 1x1 images
528  *
529  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
530  *      which is selected by the (nonstandard and therefore
531  *      capital) letter 'U' in the option string.
532  *      It causes the quantifiers to be ungreedy by default.
533  *      Appending a ? turns back to greedy (!).
534  *
535  *    - Added a new interceptor ijb-send-banner, which
536  *      sends back the "Junkbuster" gif. Without imagelist or
537  *      MSIE detection support, or if tinygif = 1, or the
538  *      URL isn't recognized as an imageurl, a lame HTML
539  *      explanation is sent instead.
540  *
541  *    - Added new feature, which permits blocking remote
542  *      script redirects and firing back a local redirect
543  *      to the browser.
544  *      The feature is conditionally compiled, i.e. it
545  *      can be disabled with --disable-fast-redirects,
546  *      plus it must be activated by a "fast-redirects"
547  *      line in the config file, has its own log level
548  *      and of course wants to be displayed by show-proxy-args
549  *      Note: Boy, all the #ifdefs in 1001 locations and
550  *      all the fumbling with configure.in and acconfig.h
551  *      were *way* more work than the feature itself :-(
552  *
553  *    - Because a generic redirect template was needed for
554  *      this, tinygif = 3 now uses the same.
555  *
556  *    - Moved GIFs, and other static HTTP response templates
557  *      to project.h
558  *
559  *    - Some minor fixes
560  *
561  *    - Removed some >400 CRs again (Jon, you really worked
562  *      a lot! ;-)
563  *
564  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
565  *    Removing last hardcoded Junkbusters.com URLs.
566  *
567  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
568  *    Version 2.9.4 checkin.
569  *    - Merged popupfile and cookiefile, and added control over PCRS
570  *      filtering, in new "permissionsfile".
571  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
572  *      file error you now get a message box (in the Win32 GUI) rather
573  *      than the program exiting with no explanation.
574  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
575  *      skipping.
576  *    - Removed tabs from "config"
577  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
578  *    - Bumped up version number.
579  *
580  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
581  *    Initial import of version 2.9.3 source tree
582  *
583  *
584  *********************************************************************/
585 \f
586
587 #include "config.h"
588
589 #include <stdio.h>
590 #include <sys/types.h>
591 #include <stdlib.h>
592 #include <ctype.h>
593 #include <string.h>
594 #include <assert.h>
595
596 #ifndef _WIN32
597 #ifndef __OS2__
598 #include <unistd.h>
599 #endif /* ndef __OS2__ */
600 #include <netinet/in.h>
601 #else
602 #include <winsock2.h>
603 #endif /* ndef _WIN32 */
604
605 #ifdef __OS2__
606 #include <utils.h>
607 #endif /* def __OS2__ */
608
609 #include "project.h"
610 #include "filters.h"
611 #include "encode.h"
612 #include "parsers.h"
613 #include "ssplit.h"
614 #include "errlog.h"
615 #include "jbsockets.h"
616 #include "miscutil.h"
617 #include "actions.h"
618 #include "cgi.h"
619 #include "list.h"
620 #include "deanimate.h"
621 #include "urlmatch.h"
622 #include "loaders.h"
623
624 #ifdef _WIN32
625 #include "win32.h"
626 #endif
627
628 const char filters_h_rcs[] = FILTERS_H_VERSION;
629
630 /* Fix a problem with Solaris.  There should be no effect on other
631  * platforms.
632  * Solaris's isspace() is a macro which uses it's argument directly
633  * as an array index.  Therefore we need to make sure that high-bit
634  * characters generate +ve values, and ideally we also want to make
635  * the argument match the declared parameter type of "int".
636  */
637 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
638
639 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
640 static jb_err prepare_for_filtering(struct client_state *csp);
641
642 #ifdef FEATURE_ACL
643 /*********************************************************************
644  *
645  * Function    :  block_acl
646  *
647  * Description :  Block this request?
648  *                Decide yes or no based on ACL file.
649  *
650  * Parameters  :
651  *          1  :  dst = The proxy or gateway address this is going to.
652  *                      Or NULL to check all possible targets.
653  *          2  :  csp = Current client state (buffers, headers, etc...)
654  *                      Also includes the client IP address.
655  *
656  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
657  *
658  *********************************************************************/
659 int block_acl(struct access_control_addr *dst, struct client_state *csp)
660 {
661    struct access_control_list *acl = csp->config->acl;
662
663    /* if not using an access control list, then permit the connection */
664    if (acl == NULL)
665    {
666       return(0);
667    }
668
669    /* search the list */
670    while (acl != NULL)
671    {
672       if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
673       {
674          if (dst == NULL)
675          {
676             /* Just want to check if they have any access */
677             if (acl->action == ACL_PERMIT)
678             {
679                return(0);
680             }
681          }
682          else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
683            && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
684          {
685             if (acl->action == ACL_PERMIT)
686             {
687                return(0);
688             }
689             else
690             {
691                return(1);
692             }
693          }
694       }
695       acl = acl->next;
696    }
697
698    return(1);
699
700 }
701
702
703 /*********************************************************************
704  *
705  * Function    :  acl_addr
706  *
707  * Description :  Called from `load_config' to parse an ACL address.
708  *
709  * Parameters  :
710  *          1  :  aspec = String specifying ACL address.
711  *          2  :  aca = struct access_control_addr to fill in.
712  *
713  * Returns     :  0 => Ok, everything else is an error.
714  *
715  *********************************************************************/
716 int acl_addr(const char *aspec, struct access_control_addr *aca)
717 {
718    int i, masklength;
719    long port;
720    char *p;
721    char *acl_spec = NULL;
722
723    masklength = 32;
724    port       =  0;
725
726    /*
727     * Use a temporary acl spec copy so we can log
728     * the unmodified original in case of parse errors.
729     */
730    acl_spec = strdup(aspec);
731    if (acl_spec == NULL)
732    {
733       /* XXX: This will be logged as parse error. */
734       return(-1);
735    }
736
737    if ((p = strchr(acl_spec, '/')) != NULL)
738    {
739       *p++ = '\0';
740       if (ijb_isdigit(*p) == 0)
741       {
742          free(acl_spec);
743          return(-1);
744       }
745       masklength = atoi(p);
746    }
747
748    if ((masklength < 0) || (masklength > 32))
749    {
750       free(acl_spec);
751       return(-1);
752    }
753
754    if ((p = strchr(acl_spec, ':')) != NULL)
755    {
756       char *endptr;
757
758       *p++ = '\0';
759       port = strtol(p, &endptr, 10);
760
761       if (port <= 0 || port > 65535 || *endptr != '\0')
762       {
763          free(acl_spec);
764          return(-1);
765       }
766    }
767
768    aca->port = (unsigned long)port;
769
770    aca->addr = ntohl(resolve_hostname_to_ip(acl_spec));
771    free(acl_spec);
772
773    if (aca->addr == INADDR_NONE)
774    {
775       /* XXX: This will be logged as parse error. */
776       return(-1);
777    }
778
779    /* build the netmask */
780    aca->mask = 0;
781    for (i=1; i <= masklength ; i++)
782    {
783       aca->mask |= (1 << (32 - i));
784    }
785
786    /* now mask off the host portion of the ip address
787     * (i.e. save on the network portion of the address).
788     */
789    aca->addr = aca->addr & aca->mask;
790
791    return(0);
792
793 }
794 #endif /* def FEATURE_ACL */
795
796
797 /*********************************************************************
798  *
799  * Function    :  block_url
800  *
801  * Description :  Called from `chat'.  Check to see if we need to block this.
802  *
803  * Parameters  :
804  *          1  :  csp = Current client state (buffers, headers, etc...)
805  *
806  * Returns     :  NULL => unblocked, else HTTP block response
807  *
808  *********************************************************************/
809 struct http_response *block_url(struct client_state *csp)
810 {
811    struct http_response *rsp;
812    const char *new_content_type = NULL;
813
814    /*
815     * If it's not blocked, don't block it ;-)
816     */
817    if ((csp->action->flags & ACTION_BLOCK) == 0)
818    {
819       return NULL;
820    }
821    if (csp->action->flags & ACTION_REDIRECT)
822    {
823       log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");     
824    }
825    /*
826     * Else, prepare a response
827     */
828    if (NULL == (rsp = alloc_http_response()))
829    {
830       return cgi_error_memory();
831    }
832
833    /*
834     * If it's an image-url, send back an image or redirect
835     * as specified by the relevant +image action
836     */
837 #ifdef FEATURE_IMAGE_BLOCKING
838    if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
839         && is_imageurl(csp))
840    {
841       char *p;
842       /* determine HOW images should be blocked */
843       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
844
845       if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
846       {
847          log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image.");
848       }
849 #if 1 /* Two alternative strategies, use this one for now: */
850
851       /* and handle accordingly: */
852       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
853       {
854          rsp->status = strdup("403 Request blocked by Privoxy");
855          if (rsp->status == NULL)
856          {
857             free_http_response(rsp);
858             return cgi_error_memory();
859          }
860          rsp->body = bindup(image_pattern_data, image_pattern_length);
861          if (rsp->body == NULL)
862          {
863             free_http_response(rsp);
864             return cgi_error_memory();
865          }
866          rsp->content_length = image_pattern_length;
867
868          if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
869          {
870             free_http_response(rsp);
871             return cgi_error_memory();
872          }
873       }
874
875       else if (0 == strcmpic(p, "blank"))
876       {
877          rsp->status = strdup("403 Request blocked by Privoxy");
878          if (rsp->status == NULL)
879          {
880             free_http_response(rsp);
881             return cgi_error_memory();
882          }
883          rsp->body = bindup(image_blank_data, image_blank_length);
884          if (rsp->body == NULL)
885          {
886             free_http_response(rsp);
887             return cgi_error_memory();
888          }
889          rsp->content_length = image_blank_length;
890
891          if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
892          {
893             free_http_response(rsp);
894             return cgi_error_memory();
895          }
896       }
897
898       else
899       {
900          rsp->status = strdup("302 Local Redirect from Privoxy");
901          if (rsp->status == NULL)
902          {
903             free_http_response(rsp);
904             return cgi_error_memory();
905          }
906
907          if (enlist_unique_header(rsp->headers, "Location", p))
908          {
909             free_http_response(rsp);
910             return cgi_error_memory();
911          }
912       }
913
914 #else /* Following code is disabled for now */
915
916       /* and handle accordingly: */
917       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
918       {
919          p = CGI_PREFIX "send-banner?type=pattern";
920       }
921       else if (0 == strcmpic(p, "blank"))
922       {
923          p = CGI_PREFIX "send-banner?type=blank";
924       }
925       rsp->status = strdup("302 Local Redirect from Privoxy");
926       if (rsp->status == NULL)
927       {
928          free_http_response(rsp);
929          return cgi_error_memory();
930       }
931
932       if (enlist_unique_header(rsp->headers, "Location", p))
933       {
934          free_http_response(rsp);
935          return cgi_error_memory();
936       }
937 #endif /* Preceeding code is disabled for now */
938    }
939    else if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
940    {
941      /*
942       *  Send empty document.               
943       */
944       new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
945
946       freez(rsp->body);
947       rsp->body = strdup(" ");
948       rsp->content_length = 1;
949
950       rsp->status = strdup("403 Request blocked by Privoxy");
951       if (rsp->status == NULL)
952       {
953          free_http_response(rsp);
954          return cgi_error_memory();
955       }
956       if (new_content_type != 0)
957       {
958          log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type);
959          if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type))
960          {
961             free_http_response(rsp);
962             return cgi_error_memory();
963          }
964       }
965    }
966    else
967 #endif /* def FEATURE_IMAGE_BLOCKING */
968
969    /*
970     * Else, generate an HTML "blocked" message:
971     */
972    {
973       jb_err err;
974       struct map * exports;
975       char *p;
976
977       /*
978        * Workaround for stupid Netscape bug which prevents
979        * pages from being displayed if loading a referenced
980        * JavaScript or style sheet fails. So make it appear
981        * as if it succeeded.
982        */
983       if ( NULL != (p = get_header_value(csp->headers, "User-Agent:"))
984            && !strncmpic(p, "mozilla", 7) /* Catch Netscape but */
985            && !strstr(p, "Gecko")         /* save Mozilla, */
986            && !strstr(p, "compatible")    /* MSIE */
987            && !strstr(p, "Opera"))        /* and Opera. */
988       {
989          rsp->status = strdup("200 Request for blocked URL");
990       }
991       else
992       {
993          rsp->status = strdup("403 Request for blocked URL");
994       }
995
996       if (rsp->status == NULL)
997       {
998          free_http_response(rsp);
999          return cgi_error_memory();
1000       }
1001
1002       exports = default_exports(csp, NULL);
1003       if (exports == NULL)
1004       {
1005          free_http_response(rsp);
1006          return cgi_error_memory();
1007       }
1008
1009 #ifdef FEATURE_FORCE_LOAD
1010       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
1011       /*
1012        * Export the force conditional block killer if
1013        *
1014        * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
1015        * - Privoxy is configured to enforce blocks, or
1016        * - it's a CONNECT request and enforcing wouldn't work anyway.
1017        */
1018       if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
1019        || (0 == strcmpic(csp->http->gpc, "connect")))
1020 #endif /* ndef FEATURE_FORCE_LOAD */
1021       {
1022          err = map_block_killer(exports, "force-support");
1023       }
1024
1025       if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
1026       if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
1027       if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
1028       if (!err) err = map(exports, "path-ue", 1, url_encode(csp->http->path), 0);
1029
1030       if (err)
1031       {
1032          free_map(exports);
1033          free_http_response(rsp);
1034          return cgi_error_memory();
1035       }
1036
1037       err = template_fill_for_cgi(csp, "blocked", exports, rsp);
1038       if (err)
1039       {
1040          free_http_response(rsp);
1041          return cgi_error_memory();
1042       }
1043    }
1044    rsp->reason = RSP_REASON_BLOCKED;
1045
1046    return finish_http_response(csp, rsp);
1047
1048 }
1049
1050
1051 #ifdef FEATURE_TRUST
1052 /*********************************************************************
1053  *
1054  * Function    :  trust_url FIXME: I should be called distrust_url
1055  *
1056  * Description :  Calls is_untrusted_url to determine if the URL is trusted
1057  *                and if not, returns a HTTP 403 response with a reject message.
1058  *
1059  * Parameters  :
1060  *          1  :  csp = Current client state (buffers, headers, etc...)
1061  *
1062  * Returns     :  NULL => trusted, else http_response.
1063  *
1064  *********************************************************************/
1065 struct http_response *trust_url(struct client_state *csp)
1066 {
1067    struct http_response *rsp;
1068    struct map * exports;
1069    char buf[BUFFER_SIZE];
1070    char *p;
1071    struct url_spec **tl;
1072    struct url_spec *t;
1073    jb_err err;
1074
1075    /*
1076     * Don't bother to work on trusted URLs
1077     */
1078    if (!is_untrusted_url(csp))
1079    {
1080       return NULL;
1081    }
1082
1083    /*
1084     * Else, prepare a response:
1085     */
1086    if (NULL == (rsp = alloc_http_response()))
1087    {
1088       return cgi_error_memory();
1089    }
1090
1091    rsp->status = strdup("403 Request blocked by Privoxy");
1092    exports = default_exports(csp, NULL);
1093    if (exports == NULL || rsp->status == NULL)
1094    {
1095       free_http_response(rsp);
1096       return cgi_error_memory();
1097    }
1098
1099    /*
1100     * Export the protocol, host, port, and referrer information
1101     */
1102    err = map(exports, "hostport", 1, csp->http->hostport, 1);
1103    if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); 
1104    if (!err) err = map(exports, "path", 1, csp->http->path, 1);
1105
1106    if (NULL != (p = get_header_value(csp->headers, "Referer:")))
1107    {
1108       if (!err) err = map(exports, "referrer", 1, html_encode(p), 0);
1109    }
1110    else
1111    {
1112       if (!err) err = map(exports, "referrer", 1, "none set", 1);
1113    }
1114
1115    if (err)
1116    {
1117       free_map(exports);
1118       free_http_response(rsp);
1119       return cgi_error_memory();
1120    }
1121
1122    /*
1123     * Export the trust list
1124     */
1125    p = strdup("");
1126    for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
1127    {
1128       snprintf(buf, sizeof(buf), "<li>%s</li>\n", t->spec);
1129       string_append(&p, buf);
1130    }
1131    err = map(exports, "trusted-referrers", 1, p, 0);
1132
1133    if (err)
1134    {
1135       free_map(exports);
1136       free_http_response(rsp);
1137       return cgi_error_memory();
1138    }
1139
1140    /*
1141     * Export the trust info, if available
1142     */
1143    if (csp->config->trust_info->first)
1144    {
1145       struct list_entry *l;
1146
1147       p = strdup("");
1148       for (l = csp->config->trust_info->first; l ; l = l->next)
1149       {
1150          snprintf(buf, sizeof(buf), "<li> <a href=\"%s\">%s</a><br>\n", l->str, l->str);
1151          string_append(&p, buf);
1152       }
1153       err = map(exports, "trust-info", 1, p, 0);
1154    }
1155    else
1156    {
1157       err = map_block_killer(exports, "have-trust-info");
1158    }
1159
1160    if (err)
1161    {
1162       free_map(exports);
1163       free_http_response(rsp);
1164       return cgi_error_memory();
1165    }
1166
1167    /*
1168     * Export the force conditional block killer if
1169     *
1170     * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
1171     * - Privoxy is configured to enforce blocks, or
1172     * - it's a CONNECT request and enforcing wouldn't work anyway.
1173     */
1174 #ifdef FEATURE_FORCE_LOAD
1175    if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
1176     || (0 == strcmpic(csp->http->gpc, "connect")))
1177    {
1178       err = map_block_killer(exports, "force-support");
1179    }
1180    else
1181    {
1182       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
1183    }
1184 #else /* ifndef FEATURE_FORCE_LOAD */
1185    err = map_block_killer(exports, "force-support");
1186 #endif /* ndef FEATURE_FORCE_LOAD */
1187
1188    if (err)
1189    {
1190       free_map(exports);
1191       free_http_response(rsp);
1192       return cgi_error_memory();
1193    }
1194
1195    /*
1196     * Build the response
1197     */
1198    err = template_fill_for_cgi(csp, "untrusted", exports, rsp);
1199    if (err)
1200    {
1201       free_http_response(rsp);
1202       return cgi_error_memory();
1203    }
1204    rsp->reason = RSP_REASON_UNTRUSTED;
1205
1206    return finish_http_response(csp, rsp);
1207 }
1208 #endif /* def FEATURE_TRUST */
1209
1210
1211 /*********************************************************************
1212  *
1213  * Function    :  compile_dynamic_pcrs_job_list
1214  *
1215  * Description :  Compiles a dynamic pcrs job list (one with variables
1216  *                resolved at request time)
1217  *
1218  * Parameters  :
1219  *          1  :  csp = Current client state (buffers, headers, etc...)
1220  *          2  :  b = The filter list to compile
1221  *
1222  * Returns     :  NULL in case of errors, otherwise the
1223  *                pcrs job list.  
1224  *
1225  *********************************************************************/
1226 pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
1227 {
1228    struct list_entry *pattern;
1229    pcrs_job *job_list = NULL;
1230    pcrs_job *dummy = NULL;
1231    pcrs_job *lastjob = NULL;
1232    int error = 0;
1233
1234    const struct pcrs_variable variables[] =
1235    {
1236       {"url",    csp->http->url,   1},
1237       {"path",   csp->http->path,  1},
1238       {"host",   csp->http->host,  1},
1239       {"origin", csp->ip_addr_str, 1},
1240       {NULL,     NULL,             1}
1241    };
1242
1243    for (pattern = b->patterns->first; pattern != NULL; pattern = pattern->next)
1244    {
1245       assert(pattern->str != NULL);
1246
1247       dummy = pcrs_compile_dynamic_command(pattern->str, variables, &error);
1248       if (NULL == dummy)
1249       {
1250          assert(error < 0);
1251          log_error(LOG_LEVEL_ERROR,
1252             "Adding filter job \'%s\' to dynamic filter %s failed: %s",
1253             pattern->str, b->name, pcrs_strerror(error));
1254          continue;
1255       }
1256       else
1257       {
1258          if (error == PCRS_WARN_TRUNCATION)
1259          {
1260             log_error(LOG_LEVEL_ERROR,
1261                "At least one of the variables in \'%s\' had to "
1262                "be truncated before compilation", pattern->str);
1263          }
1264          if (job_list == NULL)
1265          {
1266             job_list = dummy;
1267          }
1268          else
1269          {
1270             lastjob->next = dummy;
1271          }
1272          lastjob = dummy;
1273       }
1274    }
1275
1276    return job_list;
1277 }
1278
1279
1280 /*********************************************************************
1281  *
1282  * Function    :  rewrite_url
1283  *
1284  * Description :  Rewrites a URL with a single pcrs command
1285  *                and returns the result if it differs from the
1286  *                original and isn't obviously invalid.
1287  *
1288  * Parameters  :
1289  *          1  :  old_url = URL to rewrite.
1290  *          2  :  pcrs_command = pcrs command formatted as string (s@foo@bar@)
1291  *
1292  *
1293  * Returns     :  NULL if the pcrs_command didn't change the url, or 
1294  *                the result of the modification.
1295  *
1296  *********************************************************************/
1297 char *rewrite_url(char *old_url, const char *pcrs_command)
1298 {
1299    char *new_url = NULL;
1300    int hits;
1301
1302    assert(old_url);
1303    assert(pcrs_command);
1304
1305    new_url = pcrs_execute_single_command(old_url, pcrs_command, &hits);
1306
1307    if (hits == 0)
1308    {
1309       log_error(LOG_LEVEL_REDIRECTS,
1310          "pcrs command \"%s\" didn't change \"%s\".",
1311          pcrs_command, old_url);
1312       freez(new_url);
1313    }
1314    else if (hits < 0)
1315    {
1316       log_error(LOG_LEVEL_REDIRECTS,
1317          "executing pcrs command \"%s\" to rewrite %s failed: %s",
1318          pcrs_command, old_url, pcrs_strerror(hits));
1319       freez(new_url);
1320    }
1321    else if (strncmpic(new_url, "http://", 7) && strncmpic(new_url, "https://", 8))
1322    {
1323       log_error(LOG_LEVEL_ERROR,
1324          "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s), "
1325          "but the result doesn't look like a valid URL and will be ignored.",
1326          pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
1327       freez(new_url);
1328    }
1329    else
1330    {
1331       log_error(LOG_LEVEL_REDIRECTS,
1332          "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s).",
1333          pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
1334    }
1335
1336    return new_url;
1337
1338 }
1339
1340
1341 #ifdef FEATURE_FAST_REDIRECTS
1342 /*********************************************************************
1343  *
1344  * Function    :  get_last_url
1345  *
1346  * Description :  Search for the last URL inside a string.
1347  *                If the string already is a URL, it will
1348  *                be the first URL found.
1349  *
1350  * Parameters  :
1351  *          1  :  subject = the string to check
1352  *          2  :  redirect_mode = +fast-redirect{} mode 
1353  *
1354  * Returns     :  NULL if no URL was found, or
1355  *                the last URL found.
1356  *
1357  *********************************************************************/
1358 char *get_last_url(char *subject, const char *redirect_mode)
1359 {
1360    char *new_url = NULL;
1361    char *tmp;
1362
1363    assert(subject);
1364    assert(redirect_mode);
1365
1366    subject = strdup(subject);
1367    if (subject == NULL)
1368    {
1369       log_error(LOG_LEVEL_ERROR, "Out of memory while searching for redirects.");
1370       return NULL;
1371    }
1372
1373    if (0 == strcmpic(redirect_mode, "check-decoded-url"))
1374    {  
1375       log_error(LOG_LEVEL_REDIRECTS, "Decoding \"%s\" if necessary.", subject);
1376       new_url = url_decode(subject);
1377       if (new_url != NULL)
1378       {
1379          freez(subject);
1380          subject = new_url;
1381       }
1382       else
1383       {
1384          log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", subject);
1385       }
1386    }
1387
1388    log_error(LOG_LEVEL_REDIRECTS, "Checking \"%s\" for redirects.", subject);
1389
1390    /*
1391     * Find the last URL encoded in the request
1392     */
1393    tmp = subject;
1394    while ((tmp = strstr(tmp, "http://")) != NULL)
1395    {
1396       new_url = tmp++;
1397    }
1398    tmp = (new_url != NULL) ? new_url : subject;
1399    while ((tmp = strstr(tmp, "https://")) != NULL)
1400    {
1401       new_url = tmp++;
1402    }
1403
1404    if ((new_url != NULL)
1405       && (  (new_url != subject)
1406          || (0 == strncmpic(subject, "http://", 7))
1407          || (0 == strncmpic(subject, "https://", 8))
1408          ))
1409    {
1410       /*
1411        * Return new URL if we found a redirect 
1412        * or if the subject already was a URL.
1413        *
1414        * The second case makes sure that we can
1415        * chain get_last_url after another redirection check
1416        * (like rewrite_url) without losing earlier redirects.
1417        */
1418       new_url = strdup(new_url);
1419       freez(subject);
1420       return new_url;
1421    }
1422
1423    freez(subject);
1424    return NULL;
1425
1426 }
1427 #endif /* def FEATURE_FAST_REDIRECTS */
1428
1429
1430 /*********************************************************************
1431  *
1432  * Function    :  redirect_url
1433  *
1434  * Description :  Checks if Privoxy should answer the request with
1435  *                a HTTP redirect and generates the redirect if
1436  *                necessary.
1437  *
1438  * Parameters  :
1439  *          1  :  csp = Current client state (buffers, headers, etc...)
1440  *
1441  * Returns     :  NULL if the request can pass, HTTP redirect otherwise.
1442  *
1443  *********************************************************************/
1444 struct http_response *redirect_url(struct client_state *csp)
1445 {
1446    struct http_response *rsp;
1447 #ifdef FEATURE_FAST_REDIRECTS
1448    /*
1449     * XXX: Do we still need FEATURE_FAST_REDIRECTS
1450     * as compile-time option? The user can easily disable
1451     * it in his action file.
1452     */
1453    char * redirect_mode;
1454 #endif /* def FEATURE_FAST_REDIRECTS */
1455    char *old_url = NULL;
1456    char *new_url = NULL;
1457    char *redirection_string;
1458
1459    if ((csp->action->flags & ACTION_REDIRECT))
1460    {
1461       redirection_string = csp->action->string[ACTION_STRING_REDIRECT];
1462
1463       /*
1464        * If the redirection string begins with 's',
1465        * assume it's a pcrs command, otherwise treat it as
1466        * properly formatted URL and use it for the redirection
1467        * directly.
1468        *
1469        * According to RFC 2616 section 14.30 the URL
1470        * has to be absolute and if the user tries:
1471        * +redirect{shit/this/will/be/parsed/as/pcrs_command.html}
1472        * she would get undefined results anyway.
1473        *
1474        */
1475
1476       if (*redirection_string == 's')
1477       {
1478          old_url = csp->http->url;
1479          new_url = rewrite_url(old_url, redirection_string);
1480       }
1481       else
1482       {
1483          log_error(LOG_LEVEL_REDIRECTS,
1484             "No pcrs command recognized, assuming that \"%s\" is already properly formatted.",
1485             redirection_string);
1486          new_url = strdup(redirection_string);
1487       }
1488    }
1489
1490 #ifdef FEATURE_FAST_REDIRECTS
1491    if ((csp->action->flags & ACTION_FAST_REDIRECTS))
1492    {
1493       redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS];
1494
1495       /*
1496        * If it exists, use the previously rewritten URL as input
1497        * otherwise just use the old path.
1498        */
1499       old_url = (new_url != NULL) ? new_url : strdup(csp->http->path);
1500       new_url = get_last_url(old_url, redirect_mode);
1501       freez(old_url);
1502    }
1503
1504    /*
1505     * Disable redirect checkers, so that they
1506     * will be only run more than once if the user
1507     * also enables them through tags.
1508     *
1509     * From a performance point of view
1510     * it doesn't matter, but the duplicated
1511     * log messages are annoying.
1512     */
1513    csp->action->flags &= ~ACTION_FAST_REDIRECTS;
1514 #endif /* def FEATURE_FAST_REDIRECTS */
1515    csp->action->flags &= ~ACTION_REDIRECT;
1516
1517    /* Did any redirect action trigger? */   
1518    if (new_url)
1519    {
1520       if (0 == strcmpic(new_url, csp->http->url))
1521       {
1522          log_error(LOG_LEVEL_ERROR,
1523             "New URL \"%s\" and old URL \"%s\" are the same. Redirection loop prevented.",
1524             csp->http->url, new_url);
1525             freez(new_url);
1526       }
1527       else
1528       {
1529          log_error(LOG_LEVEL_REDIRECTS, "New URL is: %s", new_url);
1530
1531          if (NULL == (rsp = alloc_http_response()))
1532          {
1533             freez(new_url);
1534             return cgi_error_memory();
1535          }
1536
1537          if ( enlist_unique_header(rsp->headers, "Location", new_url)
1538            || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))) )
1539          {
1540             freez(new_url);
1541             free_http_response(rsp);
1542             return cgi_error_memory();
1543          }
1544          rsp->reason = RSP_REASON_REDIRECTED;
1545          freez(new_url);
1546
1547          return finish_http_response(csp, rsp);
1548       }
1549    }
1550
1551    /* Only reached if no redirect is required */
1552    return NULL;
1553
1554 }
1555
1556
1557 #ifdef FEATURE_IMAGE_BLOCKING
1558 /*********************************************************************
1559  *
1560  * Function    :  is_imageurl
1561  *
1562  * Description :  Given a URL, decide whether it is an image or not,
1563  *                using either the info from a previous +image action
1564  *                or, #ifdef FEATURE_IMAGE_DETECT_MSIE, and the browser
1565  *                is MSIE and not on a Mac, tell from the browser's accept
1566  *                header.
1567  *
1568  * Parameters  :
1569  *          1  :  csp = Current client state (buffers, headers, etc...)
1570  *
1571  * Returns     :  True (nonzero) if URL is an image, false (0)
1572  *                otherwise
1573  *
1574  *********************************************************************/
1575 int is_imageurl(const struct client_state *csp)
1576 {
1577 #ifdef FEATURE_IMAGE_DETECT_MSIE
1578    char *tmp;
1579
1580    tmp = get_header_value(csp->headers, "User-Agent:");
1581    if (tmp && strstr(tmp, "MSIE") && !strstr(tmp, "Mac_"))
1582    {
1583       tmp = get_header_value(csp->headers, "Accept:");
1584       if (tmp && strstr(tmp, "image/gif"))
1585       {
1586          /* Client will accept HTML.  If this seems counterintuitive,
1587           * blame Microsoft.
1588           */
1589          return(0);
1590       }
1591       else
1592       {
1593          return(1);
1594       }
1595    }
1596 #endif /* def FEATURE_IMAGE_DETECT_MSIE */
1597
1598    return ((csp->action->flags & ACTION_IMAGE) != 0);
1599
1600 }
1601 #endif /* def FEATURE_IMAGE_BLOCKING */
1602
1603
1604 #ifdef FEATURE_TRUST
1605 /*********************************************************************
1606  *
1607  * Function    :  is_untrusted_url
1608  *
1609  * Description :  Should we "distrust" this URL (and block it)?
1610  *
1611  *                Yes if it matches a line in the trustfile, or if the
1612  *                    referrer matches a line starting with "+" in the
1613  *                    trustfile.
1614  *                No  otherwise.
1615  *
1616  * Parameters  :
1617  *          1  :  csp = Current client state (buffers, headers, etc...)
1618  *
1619  * Returns     :  0 => trusted, 1 => untrusted
1620  *
1621  *********************************************************************/
1622 int is_untrusted_url(const struct client_state *csp)
1623 {
1624    struct file_list *fl;
1625    struct block_spec *b;
1626    struct url_spec **trusted_url;
1627    struct http_request rhttp[1];
1628    const char * referer;
1629    jb_err err;
1630
1631    /*
1632     * If we don't have a trustlist, we trust everybody
1633     */
1634    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
1635    {
1636       return 0;
1637    }
1638
1639    memset(rhttp, '\0', sizeof(*rhttp));
1640
1641    /*
1642     * Do we trust the request URL itself?
1643     */
1644    for (b = b->next; b ; b = b->next)
1645    {
1646       if (url_match(b->url, csp->http))
1647       {
1648          return b->reject;
1649       }
1650    }
1651
1652    if (NULL == (referer = get_header_value(csp->headers, "Referer:")))
1653    {
1654       /* no referrer was supplied */
1655       return 1;
1656    }
1657
1658
1659    /*
1660     * If not, do we maybe trust its referrer?
1661     */
1662    err = parse_http_url(referer, rhttp, csp);
1663    if (err)
1664    {
1665       return 1;
1666    }
1667
1668    for (trusted_url = csp->config->trust_list; *trusted_url != NULL; trusted_url++)
1669    {
1670       if (url_match(*trusted_url, rhttp))
1671       {
1672          /* if the URL's referrer is from a trusted referrer, then
1673           * add the target spec to the trustfile as an unblocked
1674           * domain and return 0 (which means it's OK).
1675           */
1676
1677          FILE *fp;
1678
1679          if (NULL != (fp = fopen(csp->config->trustfile, "a")))
1680          {
1681             char * path;
1682             char * path_end;
1683             char * new_entry = strdup("~");
1684
1685             string_append(&new_entry, csp->http->hostport);
1686
1687             path = csp->http->path;
1688             if ( (path[0] == '/')
1689               && (path[1] == '~')
1690               && ((path_end = strchr(path + 2, '/')) != NULL))
1691             {
1692                /* since this path points into a user's home space
1693                 * be sure to include this spec in the trustfile.
1694                 */
1695                int path_len = path_end - path; /* save offset */
1696                path = strdup(path); /* Copy string */
1697                if (path != NULL)
1698                {
1699                   path_end = path + path_len; /* regenerate ptr to new buffer */
1700                   *(path_end + 1) = '\0'; /* Truncate path after '/' */
1701                }
1702                string_join(&new_entry, path);
1703             }
1704
1705             /*
1706              * Give a reason for generating this entry.
1707              */
1708             string_append(&new_entry, " # Trusted referrer was: ");
1709             string_append(&new_entry, referer);
1710
1711             if (new_entry != NULL)
1712             {
1713                if (-1 == fprintf(fp, "%s\n", new_entry))
1714                {
1715                   log_error(LOG_LEVEL_ERROR, "Failed to append \'%s\' to trustfile \'%s\': %E",
1716                      new_entry, csp->config->trustfile);
1717                }
1718                free(new_entry);
1719             }
1720             else
1721             {
1722                /* FIXME: No way to handle out-of memory, so mostly ignoring it */
1723                log_error(LOG_LEVEL_ERROR, "Out of memory adding pattern to trust file");
1724             }
1725
1726             fclose(fp);
1727          }
1728          else
1729          {
1730             log_error(LOG_LEVEL_ERROR, "Failed to append new entry for \'%s\' to trustfile \'%s\': %E",
1731                csp->http->hostport, csp->config->trustfile);
1732          }
1733          return 0;
1734       }
1735    }
1736
1737    return 1;
1738 }
1739 #endif /* def FEATURE_TRUST */
1740
1741
1742 /*********************************************************************
1743  *
1744  * Function    :  pcrs_filter_response
1745  *
1746  * Description :  Execute all text substitutions from all applying
1747  *                +filter actions on the text buffer that's been
1748  *                accumulated in csp->iob->buf.
1749  *
1750  * Parameters  :
1751  *          1  :  csp = Current client state (buffers, headers, etc...)
1752  *
1753  * Returns     :  a pointer to the (newly allocated) modified buffer.
1754  *                or NULL if there were no hits or something went wrong
1755  *
1756  *********************************************************************/
1757 static char *pcrs_filter_response(struct client_state *csp)
1758 {
1759    int hits=0;
1760    size_t size, prev_size;
1761
1762    char *old = NULL;
1763    char *new = NULL;
1764    pcrs_job *job;
1765
1766    struct file_list *fl;
1767    struct re_filterfile_spec *b;
1768    struct list_entry *filtername;
1769
1770    int i, found_filters = 0;
1771
1772    /* 
1773     * Sanity first
1774     */
1775    if (csp->iob->cur >= csp->iob->eod)
1776    {
1777       return(NULL);
1778    }
1779
1780    /*
1781     * Need to check the set of re_filterfiles...
1782     */
1783    for (i = 0; i < MAX_AF_FILES; i++)
1784    {
1785       fl = csp->rlist[i];
1786       if (NULL != fl)
1787       {
1788          if (NULL != fl->f)
1789          {
1790            found_filters = 1;
1791            break;
1792          }
1793       }
1794    }
1795
1796    if (0 == found_filters)
1797    {
1798       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
1799       return(NULL);
1800    }
1801
1802    size = (size_t)(csp->iob->eod - csp->iob->cur);
1803    old = csp->iob->cur;
1804
1805    for (i = 0; i < MAX_AF_FILES; i++)
1806    {
1807      fl = csp->rlist[i];
1808      if ((NULL == fl) || (NULL == fl->f))
1809      {
1810         /*
1811          * Either there are no filter files
1812          * left, or this filter file just
1813          * contains no valid filters.
1814          *
1815          * Continue to be sure we don't miss
1816          * valid filter files that are chained
1817          * after empty or invalid ones.
1818          */
1819         continue;
1820      }
1821    /*
1822     * For all applying +filter actions, look if a filter by that
1823     * name exists and if yes, execute it's pcrs_joblist on the
1824     * buffer.
1825     */
1826    for (b = fl->f; b; b = b->next)
1827    {
1828       if (b->type != FT_CONTENT_FILTER)
1829       {
1830          /* Skip header filters */
1831          continue;
1832       }
1833
1834       for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
1835            filtername ; filtername = filtername->next)
1836       {
1837          if (strcmp(b->name, filtername->str) == 0)
1838          {
1839             int current_hits = 0; /* Number of hits caused by this filter */
1840             int job_number   = 0; /* Which job we're currently executing  */
1841             int job_hits     = 0; /* How many hits the current job caused */
1842             pcrs_job *joblist = b->joblist;
1843
1844             if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
1845
1846             if (NULL == joblist)
1847             {
1848                log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
1849                continue;
1850             }
1851
1852             prev_size = size;
1853             /* Apply all jobs from the joblist */
1854             for (job = joblist; NULL != job; job = job->next)
1855             {
1856                job_number++;
1857                job_hits = pcrs_execute(job, old, size, &new, &size);
1858
1859                if (job_hits >= 0)
1860                {
1861                   /*
1862                    * That went well. Continue filtering
1863                    * and use the result of this job as
1864                    * input for the next one.
1865                    */
1866                   current_hits += job_hits;
1867                   if (old != csp->iob->cur)
1868                   {
1869                      free(old);
1870                   }
1871                   old = new;
1872                }
1873                else
1874                {
1875                   /*
1876                    * This job caused an unexpected error. Inform the user
1877                    * and skip the rest of the jobs in this filter. We could
1878                    * continue with the next job, but usually the jobs
1879                    * depend on each other or are similar enough to
1880                    * fail for the same reason.
1881                    *
1882                    * At the moment our pcrs expects the error codes of pcre 3.4,
1883                    * but newer pcre versions can return additional error codes.
1884                    * As a result pcrs_strerror()'s error message might be
1885                    * "Unknown error ...", therefore we print the numerical value
1886                    * as well.
1887                    *
1888                    * XXX: Is this important enough for LOG_LEVEL_ERROR or
1889                    * should we use LOG_LEVEL_RE_FILTER instead?
1890                    */
1891                   log_error(LOG_LEVEL_ERROR, "Skipped filter \'%s\' after job number %u: %s (%d)",
1892                      b->name, job_number, pcrs_strerror(job_hits), job_hits);
1893                   break;
1894                }
1895             }
1896
1897             if (b->dynamic) pcrs_free_joblist(joblist);
1898
1899             log_error(LOG_LEVEL_RE_FILTER,
1900                "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
1901                csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
1902
1903             hits += current_hits;
1904          }
1905       }
1906    }
1907    }
1908
1909    /*
1910     * If there were no hits, destroy our copy and let
1911     * chat() use the original in csp->iob
1912     */
1913    if (!hits)
1914    {
1915       free(new);
1916       return(NULL);
1917    }
1918
1919    csp->flags |= CSP_FLAG_MODIFIED;
1920    csp->content_length = size;
1921    IOB_RESET(csp);
1922
1923    return(new);
1924
1925 }
1926
1927
1928 /*********************************************************************
1929  *
1930  * Function    :  gif_deanimate_response
1931  *
1932  * Description :  Deanimate the GIF image that has been accumulated in
1933  *                csp->iob->buf, set csp->content_length to the modified
1934  *                size and raise the CSP_FLAG_MODIFIED flag.
1935  *
1936  * Parameters  :
1937  *          1  :  csp = Current client state (buffers, headers, etc...)
1938  *
1939  * Returns     :  a pointer to the (newly allocated) modified buffer.
1940  *                or NULL in case something went wrong.
1941  *
1942  *********************************************************************/
1943 static char *gif_deanimate_response(struct client_state *csp)
1944 {
1945    struct binbuffer *in, *out;
1946    char *p;
1947    size_t size;
1948
1949    size = (size_t)(csp->iob->eod - csp->iob->cur);
1950
1951    if (  (NULL == (in =  (struct binbuffer *)zalloc(sizeof *in )))
1952       || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) )
1953    {
1954       log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)");
1955       return NULL;
1956    }
1957
1958    in->buffer = csp->iob->cur;
1959    in->size = size;
1960
1961    if (gif_deanimate(in, out, strncmp("last", csp->action->string[ACTION_STRING_DEANIMATE], 4)))
1962    {
1963       log_error(LOG_LEVEL_DEANIMATE, "failed! (gif parsing)");
1964       free(in);
1965       buf_free(out);
1966       return(NULL);
1967    }
1968    else
1969    {
1970       if ((int)size == out->offset)
1971       {
1972          log_error(LOG_LEVEL_DEANIMATE, "GIF not changed.");
1973       }
1974       else
1975       {
1976          log_error(LOG_LEVEL_DEANIMATE, "Success! GIF shrunk from %d bytes to %d.", size, out->offset);
1977       }
1978       csp->content_length = out->offset;
1979       csp->flags |= CSP_FLAG_MODIFIED;
1980       p = out->buffer;
1981       free(in);
1982       free(out);
1983       return(p);
1984    }
1985
1986 }
1987
1988
1989 /*********************************************************************
1990  *
1991  * Function    :  jpeg_inspect_response
1992  *
1993  * Description :  
1994  *
1995  * Parameters  :
1996  *          1  :  csp = Current client state (buffers, headers, etc...)
1997  *
1998  * Returns     :  a pointer to the (newly allocated) modified buffer
1999  *                or NULL in case something went wrong.
2000  *
2001  *********************************************************************/
2002 static char *jpeg_inspect_response(struct client_state *csp)
2003 {
2004    struct binbuffer  *in = NULL;
2005    struct binbuffer *out = NULL;
2006    char *p = NULL;
2007    size_t size;
2008
2009    size = (size_t)(csp->iob->eod - csp->iob->cur);
2010
2011    if (NULL == (in =  (struct binbuffer *)zalloc(sizeof *in )))
2012    {
2013       log_error(LOG_LEVEL_DEANIMATE, "failed! (jpeg no mem 1)");
2014       return NULL;
2015    }
2016
2017    if (NULL == (out = (struct binbuffer *)zalloc(sizeof *out)))
2018    {
2019       log_error(LOG_LEVEL_DEANIMATE, "failed! (jpeg no mem 2)");
2020       return NULL;
2021    }
2022
2023    in->buffer = csp->iob->cur;
2024    in->size = size;
2025
2026    /*
2027     * Calling jpeg_inspect has the side-effect of creating and 
2028     * modifying the image buffer of "out" directly.
2029     */
2030    if (jpeg_inspect(in, out))
2031    {
2032       log_error(LOG_LEVEL_DEANIMATE, "failed! (jpeg parsing)");
2033       free(in);
2034       buf_free(out);
2035       return(NULL);
2036
2037    }
2038    else
2039    {
2040       csp->content_length = out->offset;
2041       csp->flags |= CSP_FLAG_MODIFIED;
2042       p = out->buffer;
2043       free(in);
2044       free(out);
2045       return(p);
2046    }
2047
2048 }
2049
2050
2051 /*********************************************************************
2052  *
2053  * Function    :  get_filter_function
2054  *
2055  * Description :  Decides which content filter function has
2056  *                to be applied (if any).
2057  *
2058  *                XXX: Doesn't handle filter_popups()
2059  *                because of the different prototype. Probably
2060  *                we should ditch filter_popups() anyway, it's
2061  *                even less reliable than popup blocking based
2062  *                on pcrs filters.
2063  *
2064  * Parameters  :
2065  *          1  :  csp = Current client state (buffers, headers, etc...)
2066  *
2067  * Returns     :  The content filter function to run, or
2068  *                NULL if no content filter is active
2069  *
2070  *********************************************************************/
2071 filter_function_ptr get_filter_function(struct client_state *csp)
2072 {
2073    filter_function_ptr filter_function = NULL;
2074
2075    /*
2076     * Are we enabling text mode by force?
2077     */
2078    if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
2079    {
2080       /*
2081        * Do we really have to?
2082        */
2083       if (csp->content_type & CT_TEXT)
2084       {
2085          log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");   
2086       }
2087       else
2088       {
2089          csp->content_type |= CT_TEXT;
2090          log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");   
2091       }
2092    }
2093
2094    if (!(csp->content_type & CT_DECLARED))
2095    {
2096       /*
2097        * The server didn't bother to declare a MIME-Type.
2098        * Assume it's text that can be filtered.
2099        *
2100        * This also regulary happens with 304 responses,
2101        * therefore logging anything here would cause
2102        * too much noise.
2103        */
2104       csp->content_type |= CT_TEXT;
2105    }
2106
2107    /*
2108     * Choose the applying filter function based on
2109     * the content type and action settings.
2110     */
2111    if ((csp->content_type & CT_TEXT) &&
2112        (csp->rlist != NULL) &&
2113        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
2114    {
2115       filter_function = pcrs_filter_response;
2116    }
2117    else if ((csp->content_type & CT_GIF)  &&
2118             (csp->action->flags & ACTION_DEANIMATE))
2119    {
2120       filter_function = gif_deanimate_response;
2121    }
2122    else if ((csp->content_type & CT_JPEG)  &&
2123             (csp->action->flags & ACTION_JPEG_INSPECT))
2124    {
2125       filter_function = jpeg_inspect_response;
2126    }
2127
2128    return filter_function;
2129 }
2130
2131
2132 /*********************************************************************
2133  *
2134  * Function    :  remove_chunked_transfer_coding
2135  *
2136  * Description :  In-situ remove the "chunked" transfer coding as defined
2137  *                in rfc2616 from a buffer.
2138  *
2139  * Parameters  :
2140  *          1  :  buffer = Pointer to the text buffer
2141  *          2  :  size =  In: Number of bytes to be processed,
2142  *                       Out: Number of bytes after de-chunking.
2143  *                       (undefined in case of errors)
2144  *
2145  * Returns     :  JB_ERR_OK for success,
2146  *                JB_ERR_PARSE otherwise
2147  *
2148  *********************************************************************/
2149 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
2150 {
2151    size_t newsize = 0;
2152    unsigned int chunksize = 0;
2153    char *from_p, *to_p;
2154
2155    assert(buffer);
2156    from_p = to_p = buffer;
2157
2158    if (sscanf(buffer, "%x", &chunksize) != 1)
2159    {
2160       log_error(LOG_LEVEL_ERROR, "Invalid first chunksize while stripping \"chunked\" transfer coding");
2161       return JB_ERR_PARSE;
2162    }
2163
2164    while (chunksize > 0)
2165    {
2166       if (NULL == (from_p = strstr(from_p, "\r\n")))
2167       {
2168          log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding");
2169          return JB_ERR_PARSE;
2170       }
2171
2172       if ((newsize += chunksize) >= *size)
2173       {
2174          log_error(LOG_LEVEL_ERROR, "Chunksize exceeds buffer in  \"chunked\" transfer coding");
2175          return JB_ERR_PARSE;
2176       }
2177       from_p += 2;
2178
2179       memmove(to_p, from_p, (size_t) chunksize);
2180       to_p = buffer + newsize;
2181       from_p += chunksize + 2;
2182
2183       if (sscanf(from_p, "%x", &chunksize) != 1)
2184       {
2185          log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding");
2186          return JB_ERR_PARSE;
2187       }
2188    }
2189    
2190    /* XXX: Should get its own loglevel. */
2191    log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d", *size, newsize);
2192
2193    *size = newsize;
2194
2195    return JB_ERR_OK;
2196
2197 }
2198
2199
2200 /*********************************************************************
2201  *
2202  * Function    :  prepare_for_filtering
2203  *
2204  * Description :  If necessary, de-chunks and decompresses
2205  *                the content so it can get filterd.
2206  *
2207  * Parameters  :
2208  *          1  :  csp = Current client state (buffers, headers, etc...)
2209  *
2210  * Returns     :  JB_ERR_OK for success,
2211  *                JB_ERR_PARSE otherwise
2212  *
2213  *********************************************************************/
2214 static jb_err prepare_for_filtering(struct client_state *csp)
2215 {
2216    jb_err err = JB_ERR_OK;
2217
2218    /*
2219     * If the body has a "chunked" transfer-encoding,
2220     * get rid of it, adjusting size and iob->eod
2221     */
2222    if (csp->flags & CSP_FLAG_CHUNKED)
2223    {
2224       size_t size = (size_t)(csp->iob->eod - csp->iob->cur);
2225
2226       log_error(LOG_LEVEL_RE_FILTER, "Need to de-chunk first");
2227       err = remove_chunked_transfer_coding(csp->iob->cur, &size);
2228       if (JB_ERR_OK == err)
2229       {
2230          csp->iob->eod = csp->iob->cur + size;
2231          csp->flags |= CSP_FLAG_MODIFIED;
2232       }
2233       else
2234       {
2235          return JB_ERR_PARSE;
2236       }
2237    }
2238
2239 #ifdef FEATURE_ZLIB
2240    /*
2241     * If the body has a supported transfer-encoding,
2242     * decompress it, adjusting size and iob->eod.
2243     */
2244    if (csp->content_type & (CT_GZIP|CT_DEFLATE))
2245    {
2246       if (0 == csp->iob->eod - csp->iob->cur)
2247       {
2248          /* Nothing left after de-chunking. */
2249          return JB_ERR_OK;
2250       }
2251
2252       err = decompress_iob(csp);
2253
2254       if (JB_ERR_OK == err)
2255       {
2256          csp->flags |= CSP_FLAG_MODIFIED;
2257          csp->content_type &= ~CT_TABOO;
2258       }
2259       else
2260       {
2261          /*
2262           * Unset CT_GZIP and CT_DEFLATE to remember not
2263           * to modify the Content-Encoding header later.
2264           */
2265          csp->content_type &= ~CT_GZIP;
2266          csp->content_type &= ~CT_DEFLATE;
2267       }
2268    }
2269 #endif
2270
2271    return err;
2272 }
2273
2274
2275 /*********************************************************************
2276  *
2277  * Function    :  execute_content_filter
2278  *
2279  * Description :  Executes a given content filter.
2280  *
2281  * Parameters  :
2282  *          1  :  csp = Current client state (buffers, headers, etc...)
2283  *          2  :  content_filter = The filter function to execute
2284  *
2285  * Returns     :  Pointer to the modified buffer, or
2286  *                NULL if filtering failed or wasn't necessary.
2287  *
2288  *********************************************************************/
2289 char *execute_content_filter(struct client_state *csp, filter_function_ptr content_filter)
2290 {
2291    if (0 == csp->iob->eod - csp->iob->cur)
2292    {
2293       /*
2294        * No content (probably status code 301, 302 ...),
2295        * no filtering necessary.
2296        */
2297       return NULL;
2298    }
2299
2300    if (JB_ERR_OK != prepare_for_filtering(csp))
2301    {
2302       /*
2303        * failed to de-chunk or decompress.
2304        * XXX: if possible, we should continue anyway.
2305        */
2306       return NULL;
2307    }
2308
2309    if (0 == csp->iob->eod - csp->iob->cur)
2310    {
2311       /*
2312        * Clown alarm: chunked and/or compressed nothing delivered.
2313        */
2314       return NULL;
2315    }
2316
2317    return ((*content_filter)(csp));
2318 }
2319
2320
2321 /*********************************************************************
2322  *
2323  * Function    :  url_actions
2324  *
2325  * Description :  Gets the actions for this URL.
2326  *
2327  * Parameters  :
2328  *          1  :  http = http_request request for blocked URLs
2329  *          2  :  csp = Current client state (buffers, headers, etc...)
2330  *
2331  * Returns     :  N/A
2332  *
2333  *********************************************************************/
2334 void url_actions(struct http_request *http,
2335                  struct client_state *csp)
2336 {
2337    struct file_list *fl;
2338    struct url_actions *b;
2339    int i;
2340
2341    init_current_action(csp->action);
2342
2343    for (i = 0; i < MAX_AF_FILES; i++)
2344    {
2345       if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
2346       {
2347          return;
2348       }
2349
2350       apply_url_actions(csp->action, http, b);
2351    }
2352
2353    return;
2354 }
2355
2356
2357 /*********************************************************************
2358  *
2359  * Function    :  apply_url_actions
2360  *
2361  * Description :  Applies a list of URL actions.
2362  *
2363  * Parameters  :
2364  *          1  :  action = Destination.
2365  *          2  :  http = Current URL
2366  *          3  :  b = list of URL actions to apply
2367  *
2368  * Returns     :  N/A
2369  *
2370  *********************************************************************/
2371 void apply_url_actions(struct current_action_spec *action,
2372                        struct http_request *http,
2373                        struct url_actions *b)
2374 {
2375    if (b == NULL)
2376    {
2377       /* Should never happen */
2378       return;
2379    }
2380
2381    for (b = b->next; NULL != b; b = b->next)
2382    {
2383       if (url_match(b->url, http))
2384       {
2385          merge_current_action(action, b->action);
2386       }
2387    }
2388 }
2389
2390
2391 /*********************************************************************
2392  *
2393  * Function    :  get_forward_override_settings
2394  *
2395  * Description :  Returns forward settings as specified with the
2396  *                forward-override{} action. forward-override accepts
2397  *                forward lines similar to the one used in the
2398  *                configuration file, but without the URL pattern.
2399  *
2400  *                For example:
2401  *
2402  *                   forward / .
2403  *
2404  *                in the configuration file can be replaced with
2405  *                the action section:
2406  *
2407  *                 {+forward-override{forward .}}
2408  *                 /
2409  *
2410  * Parameters  :
2411  *          1  :  csp = Current client state (buffers, headers, etc...)
2412  *
2413  * Returns     :  Pointer to forwarding structure in case of success.
2414  *                Invalid syntax is fatal.
2415  *
2416  *********************************************************************/
2417 static const struct forward_spec *get_forward_override_settings(struct client_state *csp)
2418 {
2419    const char *forward_override_line = csp->action->string[ACTION_STRING_FORWARD_OVERRIDE];
2420    char forward_settings[BUFFER_SIZE];
2421    char *http_parent = NULL;
2422    /* variable names were chosen for consistency reasons. */
2423    struct forward_spec *fwd = NULL;
2424    int vec_count;
2425    char *vec[3];
2426
2427    assert(csp->action->flags & ACTION_FORWARD_OVERRIDE);
2428    /* Should be enforced by load_one_actions_file() */
2429    assert(strlen(forward_override_line) < sizeof(forward_settings) - 1);
2430
2431    /* Create a copy ssplit can modify */
2432    strlcpy(forward_settings, forward_override_line, sizeof(forward_settings));
2433
2434    if (NULL != csp->fwd)
2435    {
2436       /*
2437        * XXX: Currently necessary to prevent memory
2438        * leaks when the show-url-info cgi page is visited.
2439        */
2440       unload_forward_spec(csp->fwd);
2441    }
2442
2443    /*
2444     * allocate a new forward node, valid only for
2445     * the lifetime of this request. Save its location
2446     * in csp as well, so sweep() can free it later on.
2447     */
2448    fwd = csp->fwd = zalloc(sizeof(*fwd));
2449    if (NULL == fwd)
2450    {
2451       log_error(LOG_LEVEL_FATAL,
2452          "can't allocate memory for forward-override{%s}", forward_override_line);
2453       /* Never get here - LOG_LEVEL_FATAL causes program exit */
2454    }
2455
2456    vec_count = ssplit(forward_settings, " \t", vec, SZ(vec), 1, 1);
2457    if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
2458    {
2459       fwd->type = SOCKS_NONE;
2460
2461       /* Parse the parent HTTP proxy host:port */
2462       http_parent = vec[1];
2463
2464    }
2465    else if (vec_count == 3)
2466    {
2467       char *socks_proxy = NULL;
2468
2469       if  (!strcasecmp(vec[0], "forward-socks4"))
2470       {
2471          fwd->type = SOCKS_4;
2472          socks_proxy = vec[1];
2473       }
2474       else if (!strcasecmp(vec[0], "forward-socks4a"))
2475       {
2476          fwd->type = SOCKS_4A;
2477          socks_proxy = vec[1];
2478       }
2479
2480       if (NULL != socks_proxy)
2481       {
2482          /* Parse the SOCKS proxy host[:port] */
2483          fwd->gateway_host = strdup(socks_proxy);
2484
2485          if (NULL != (socks_proxy = strchr(fwd->gateway_host, ':')))
2486          {
2487             *socks_proxy++ = '\0';
2488             fwd->gateway_port = strtol(socks_proxy, NULL, 0);
2489          }
2490
2491          if (fwd->gateway_port <= 0)
2492          {
2493             fwd->gateway_port = 1080;
2494          }
2495
2496          http_parent = vec[2];
2497       }
2498    }
2499
2500    if (NULL == http_parent)
2501    {
2502       log_error(LOG_LEVEL_FATAL,
2503          "Invalid forward-override syntax in: %s", forward_override_line);
2504       /* Never get here - LOG_LEVEL_FATAL causes program exit */
2505    }
2506
2507    /* Parse http forwarding settings */
2508    if (strcmp(http_parent, ".") != 0)
2509    {
2510       fwd->forward_host = strdup(http_parent);
2511
2512       if (NULL != (http_parent = strchr(fwd->forward_host, ':')))
2513       {
2514          *http_parent++ = '\0';
2515          fwd->forward_port = strtol(http_parent, NULL, 0);
2516       }
2517
2518       if (fwd->forward_port <= 0)
2519       {
2520          fwd->forward_port = 8000;
2521       }
2522    }
2523
2524    assert (NULL != fwd);
2525
2526    log_error(LOG_LEVEL_CONNECT,
2527       "Overriding forwarding settings based on \'%s\'", forward_override_line);
2528
2529    return fwd;
2530 }
2531
2532
2533 /*********************************************************************
2534  *
2535  * Function    :  forward_url
2536  *
2537  * Description :  Should we forward this to another proxy?
2538  *
2539  *                XXX: Should be changed to make use of csp->fwd.
2540  *
2541  * Parameters  :
2542  *          1  :  http = http_request request for current URL
2543  *          2  :  csp = Current client state (buffers, headers, etc...)
2544  *
2545  * Returns     :  Pointer to forwarding information.
2546  *
2547  *********************************************************************/
2548 const struct forward_spec * forward_url(struct http_request *http,
2549                                         struct client_state *csp)
2550 {
2551    static const struct forward_spec fwd_default[1] = { FORWARD_SPEC_INITIALIZER };
2552    struct forward_spec *fwd = csp->config->forward;
2553
2554    if (csp->action->flags & ACTION_FORWARD_OVERRIDE)
2555    {
2556       return get_forward_override_settings(csp);
2557    }
2558
2559    if (fwd == NULL)
2560    {
2561       return fwd_default;
2562    }
2563
2564    while (fwd != NULL)
2565    {
2566       if (url_match(fwd->url, http))
2567       {
2568          return fwd;
2569       }
2570       fwd = fwd->next;
2571    }
2572
2573    return fwd_default;
2574 }
2575
2576
2577 /*********************************************************************
2578  *
2579  * Function    :  direct_response 
2580  *
2581  * Description :  Check if Max-Forwards == 0 for an OPTIONS or TRACE
2582  *                request and if so, return a HTTP 501 to the client.
2583  *
2584  *                FIXME: I have a stupid name and I should handle the
2585  *                requests properly. Still, what we do here is rfc-
2586  *                compliant, whereas ignoring or forwarding are not.
2587  *
2588  * Parameters  :  
2589  *          1  :  csp = Current client state (buffers, headers, etc...)
2590  *
2591  * Returns     :  http_response if , NULL if nonmatch or handler fail
2592  *
2593  *********************************************************************/
2594 struct http_response *direct_response(struct client_state *csp)
2595 {
2596    struct http_response *rsp;
2597    struct list_entry *p;
2598
2599    if ((0 == strcmpic(csp->http->gpc, "trace"))
2600       || (0 == strcmpic(csp->http->gpc, "options")))
2601    {
2602       for (p = csp->headers->first; (p != NULL) ; p = p->next)
2603       {
2604          if (!strncmpic("Max-Forwards:", p->str, 13))
2605          {
2606             unsigned int max_forwards;
2607
2608             /*
2609              * If it's a Max-Forwards value of zero,
2610              * we have to intercept the request.
2611              */
2612             if (1 == sscanf(p->str+12, ": %u", &max_forwards) && max_forwards == 0)
2613             {
2614                /*
2615                 * FIXME: We could handle at least TRACE here,
2616                 * but that would require a verbatim copy of
2617                 * the request which we don't have anymore
2618                 */
2619                 log_error(LOG_LEVEL_HEADER,
2620                   "Detected header \'%s\' in OPTIONS or TRACE request. Returning 501.",
2621                   p->str);
2622
2623                /* Get mem for response or fail*/
2624                if (NULL == (rsp = alloc_http_response()))
2625                {
2626                   return cgi_error_memory();
2627                }
2628             
2629                if (NULL == (rsp->status = strdup("501 Not Implemented")))
2630                {
2631                   free_http_response(rsp);
2632                   return cgi_error_memory();
2633                }
2634
2635                rsp->is_static = 1;
2636                rsp->reason = RSP_REASON_UNSUPPORTED;
2637
2638                return(finish_http_response(csp, rsp));
2639             }
2640          }
2641       }
2642    }
2643    return NULL;
2644 }
2645
2646
2647 /*
2648   Local Variables:
2649   tab-width: 3
2650   end:
2651 */