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