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