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