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