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