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