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