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