Splitting fill_template() into 2 functions:
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.30 2001/09/16 11:00:10 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *                Functions declared include:
8  *                   `acl_addr', `add_stats', `block_acl', `block_imageurl',
9  *                   `block_url', `url_actions', `domaincmp', `dsplit',
10  *                   `filter_popups', `forward_url', 'redirect_url',
11  *                   `ij_untrusted_url', `intercept_url', `pcrs_filter_respose',
12  *                   `show_proxy_args', 'ijb_send_banner', and `trust_url'
13  *
14  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
15  *                IJBSWA team.  http://ijbswa.sourceforge.net
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.30  2001/09/16 11:00:10  jongfoster
42  *    New function alloc_http_response, for symmetry with free_http_response
43  *
44  *    Revision 1.29  2001/09/13 23:32:40  jongfoster
45  *    Moving image data to cgi.c rather than cgi.h
46  *    Fixing a GPF under Win32 (and any other OS that protects global
47  *    constants from being written to).
48  *
49  *    Revision 1.28  2001/09/10 10:18:51  oes
50  *    Silenced compiler warnings
51  *
52  *    Revision 1.27  2001/08/05 16:06:20  jongfoster
53  *    Modifiying "struct map" so that there are now separate header and
54  *    "map_entry" structures.  This means that functions which modify a
55  *    map no longer need to return a pointer to the modified map.
56  *    Also, it no longer reverses the order of the entries (which may be
57  *    important with some advanced template substitutions).
58  *
59  *    Revision 1.26  2001/07/30 22:08:36  jongfoster
60  *    Tidying up #defines:
61  *    - All feature #defines are now of the form FEATURE_xxx
62  *    - Permanently turned off WIN_GUI_EDIT
63  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
64  *
65  *    Revision 1.25  2001/07/26 10:09:46  oes
66  *    Made browser detection a little less naive
67  *
68  *    Revision 1.24  2001/07/25 17:22:51  oes
69  *    Added workaround for Netscape bug that prevents display of page when loading a component fails.
70  *
71  *    Revision 1.23  2001/07/23 13:40:12  oes
72  *    Fixed bug that caused document body to be dropped when pcrs joblist was empty.
73  *
74  *    Revision 1.22  2001/07/18 12:29:34  oes
75  *    - Made gif_deanimate_response respect
76  *      csp->action->string[ACTION_STRING_DEANIMATE]
77  *    - Logging cosmetics
78  *
79  *    Revision 1.21  2001/07/13 13:59:53  oes
80  *     - Introduced gif_deanimate_response which shares the
81  *       generic content modification interface of pcrs_filter_response
82  *       and acts as a wrapper to deanimate.c:gif_deanimate()
83  *     - Renamed re_process_buffer to pcrs_filter_response
84  *     - pcrs_filter_response now returns NULL on failiure
85  *     - Removed all #ifdef PCRS
86  *
87  *    Revision 1.20  2001/07/01 17:01:04  oes
88  *    Added comments and missing return statement in is_untrusted_url()
89  *
90  *    Revision 1.19  2001/06/29 21:45:41  oes
91  *    Indentation, CRLF->LF, Tab-> Space
92  *
93  *    Revision 1.18  2001/06/29 13:27:38  oes
94  *    - Cleaned up, renamed and reorderd functions
95  *      and improved comments
96  *
97  *    - block_url:
98  *      - Ported to CGI platform. Now delivers
99  *        http_response or NULL
100  *      - Unified HTML and GIF generation (moved image detection
101  *        and GIF generation here from jcc.c:chat())
102  *      - Fixed HTTP status to:
103  *       -  403 (Forbidden) for the "blocked" HTML message
104  *       -  200 (OK) for GIF answers
105  *       -  302 (Redirect) for redirect to GIF
106  *
107  *    - trust_url:
108  *      - Ported to CGI platform. Now delivers
109  *        http_response or NULL
110  *      - Separated detection of untrusted URL into
111  *        (bool)is_untrusted_url
112  *      - Added enforcement of untrusted requests
113  *
114  *    - Moved redirect_url() from cgi.c to here
115  *      and ported it to the CGI platform
116  *
117  *    - Removed logentry from cancelled commit
118  *
119  *    Revision 1.17  2001/06/09 10:55:28  jongfoster
120  *    Changing BUFSIZ ==> BUFFER_SIZE
121  *
122  *    Revision 1.16  2001/06/07 23:10:26  jongfoster
123  *    Allowing unanchored domain patterns to back off and retry
124  *    if they partially match.  Optimized right-anchored patterns.
125  *    Moving ACL and forward files into config file.
126  *    Replacing struct gateway with struct forward_spec
127  *
128  *    Revision 1.15  2001/06/03 19:12:00  oes
129  *    extracted-CGI relevant stuff
130  *
131  *    Revision 1.14  2001/06/01 10:30:55  oes
132  *    Added optional left-anchoring to domaincmp
133  *
134  *    Revision 1.13  2001/05/31 21:21:30  jongfoster
135  *    Permissionsfile / actions file changes:
136  *    - Changed "permission" to "action" throughout
137  *    - changes to file format to allow string parameters
138  *    - Moved helper functions to actions.c
139  *
140  *    Revision 1.12  2001/05/31 17:35:20  oes
141  *
142  *     - Enhanced domain part globbing with infix and prefix asterisk
143  *       matching and optional unanchored operation
144  *
145  *    Revision 1.11  2001/05/29 11:53:23  oes
146  *    "See why" link added to "blocked" page
147  *
148  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
149  *    Unified blocklist/imagelist/permissionslist.
150  *    File format is still under discussion, but the internal changes
151  *    are (mostly) done.
152  *
153  *    Also modified interceptor behaviour:
154  *    - We now intercept all URLs beginning with one of the following
155  *      prefixes (and *only* these prefixes):
156  *        * http://i.j.b/
157  *        * http://ijbswa.sf.net/config/
158  *        * http://ijbswa.sourceforge.net/config/
159  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
160  *    - Internal changes so that intercepted and fast redirect pages
161  *      are not replaced with an image.
162  *    - Interceptors now have the option to send a binary page direct
163  *      to the client. (i.e. ijb-send-banner uses this)
164  *    - Implemented show-url-info interceptor.  (Which is why I needed
165  *      the above interceptors changes - a typical URL is
166  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
167  *      The previous mechanism would not have intercepted that, and
168  *      if it had been intercepted then it then it would have replaced
169  *      it with an image.)
170  *
171  *    Revision 1.9  2001/05/27 22:17:04  oes
172  *
173  *    - re_process_buffer no longer writes the modified buffer
174  *      to the client, which was very ugly. It now returns the
175  *      buffer, which it is then written by chat.
176  *
177  *    - content_length now adjusts the Content-Length: header
178  *      for modified documents rather than crunch()ing it.
179  *      (Length info in csp->content_length, which is 0 for
180  *      unmodified documents)
181  *
182  *    - For this to work, sed() is called twice when filtering.
183  *
184  *    Revision 1.8  2001/05/26 17:13:28  jongfoster
185  *    Filled in a function comment.
186  *
187  *    Revision 1.7  2001/05/26 15:26:15  jongfoster
188  *    ACL feature now provides more security by immediately dropping
189  *    connections from untrusted hosts.
190  *
191  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
192  *    Automatic reloading of config file.
193  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
194  *    Most of the global variables have been moved to a new
195  *    struct configuration_spec, accessed through csp->config->globalname
196  *    Most of the globals remaining are used by the Win32 GUI.
197  *
198  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
199  *    Hard tabs->Spaces
200  *
201  *    Revision 1.4  2001/05/22 18:46:04  oes
202  *
203  *    - Enabled filtering banners by size rather than URL
204  *      by adding patterns that replace all standard banner
205  *      sizes with the "Junkbuster" gif to the re_filterfile
206  *
207  *    - Enabled filtering WebBugs by providing a pattern
208  *      which kills all 1x1 images
209  *
210  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
211  *      which is selected by the (nonstandard and therefore
212  *      capital) letter 'U' in the option string.
213  *      It causes the quantifiers to be ungreedy by default.
214  *      Appending a ? turns back to greedy (!).
215  *
216  *    - Added a new interceptor ijb-send-banner, which
217  *      sends back the "Junkbuster" gif. Without imagelist or
218  *      MSIE detection support, or if tinygif = 1, or the
219  *      URL isn't recognized as an imageurl, a lame HTML
220  *      explanation is sent instead.
221  *
222  *    - Added new feature, which permits blocking remote
223  *      script redirects and firing back a local redirect
224  *      to the browser.
225  *      The feature is conditionally compiled, i.e. it
226  *      can be disabled with --disable-fast-redirects,
227  *      plus it must be activated by a "fast-redirects"
228  *      line in the config file, has its own log level
229  *      and of course wants to be displayed by show-proxy-args
230  *      Note: Boy, all the #ifdefs in 1001 locations and
231  *      all the fumbling with configure.in and acconfig.h
232  *      were *way* more work than the feature itself :-(
233  *
234  *    - Because a generic redirect template was needed for
235  *      this, tinygif = 3 now uses the same.
236  *
237  *    - Moved GIFs, and other static HTTP response templates
238  *      to project.h
239  *
240  *    - Some minor fixes
241  *
242  *    - Removed some >400 CRs again (Jon, you really worked
243  *      a lot! ;-)
244  *
245  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
246  *    Removing last hardcoded JunkBusters.com URLs.
247  *
248  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
249  *    Version 2.9.4 checkin.
250  *    - Merged popupfile and cookiefile, and added control over PCRS
251  *      filtering, in new "permissionsfile".
252  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
253  *      file error you now get a message box (in the Win32 GUI) rather
254  *      than the program exiting with no explanation.
255  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
256  *      skipping.
257  *    - Removed tabs from "config"
258  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
259  *    - Bumped up version number.
260  *
261  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
262  *    Initial import of version 2.9.3 source tree
263  *
264  *
265  *********************************************************************/
266 \f
267
268 #include "config.h"
269
270 #include <stdio.h>
271 #include <sys/types.h>
272 #include <stdlib.h>
273 #include <ctype.h>
274 #include <string.h>
275
276 #ifndef _WIN32
277 #include <unistd.h>
278 #include <netinet/in.h>
279 #else
280 #include <winsock2.h>
281 #endif
282
283 #include "project.h"
284 #include "filters.h"
285 #include "encode.h"
286 #include "jcc.h"
287 #include "showargs.h"
288 #include "parsers.h"
289 #include "ssplit.h"
290 #include "gateway.h"
291 #include "jbsockets.h"
292 #include "errlog.h"
293 #include "jbsockets.h"
294 #include "miscutil.h"
295 #include "actions.h"
296 #include "cgi.h"
297 #include "list.h"
298 #include "deanimate.h"
299
300 #ifdef _WIN32
301 #include "win32.h"
302 #endif
303
304 const char filters_h_rcs[] = FILTERS_H_VERSION;
305
306 /* Fix a problem with Solaris.  There should be no effect on other
307  * platforms.
308  * Solaris's isspace() is a macro which uses it's argument directly
309  * as an array index.  Therefore we need to make sure that high-bit
310  * characters generate +ve values, and ideally we also want to make
311  * the argument match the declared parameter type of "int".
312  */
313 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
314
315
316 #ifdef FEATURE_ACL
317 /*********************************************************************
318  *
319  * Function    :  block_acl
320  *
321  * Description :  Block this request?
322  *                Decide yes or no based on ACL file.
323  *
324  * Parameters  :
325  *          1  :  dst = The proxy or gateway address this is going to.
326  *                      Or NULL to check all possible targets.
327  *          2  :  csp = Current client state (buffers, headers, etc...)
328  *                      Also includes the client IP address.
329  *
330  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
331  *
332  *********************************************************************/
333 int block_acl(struct access_control_addr *dst, struct client_state *csp)
334 {
335    struct access_control_list *acl = csp->config->acl;
336
337    /* if not using an access control list, then permit the connection */
338    if (acl == NULL)
339    {
340       return(0);
341    }
342
343    /* search the list */
344    while (acl != NULL)
345    {
346       if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
347       {
348          if (dst == NULL)
349          {
350             /* Just want to check if they have any access */
351             if (acl->action == ACL_PERMIT)
352             {
353                return(0);
354             }
355          }
356          else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
357            && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
358          {
359             if (acl->action == ACL_PERMIT)
360             {
361                return(0);
362             }
363             else
364             {
365                return(1);
366             }
367          }
368       }
369       acl = acl->next;
370    }
371
372    return(1);
373
374 }
375
376
377 /*********************************************************************
378  *
379  * Function    :  acl_addr
380  *
381  * Description :  Called from `load_aclfile' to parse an ACL address.
382  *
383  * Parameters  :
384  *          1  :  aspec = String specifying ACL address.
385  *          2  :  aca = struct access_control_addr to fill in.
386  *
387  * Returns     :  0 => Ok, everything else is an error.
388  *
389  *********************************************************************/
390 int acl_addr(char *aspec, struct access_control_addr *aca)
391 {
392    int i, masklength, port;
393    char *p;
394
395    masklength = 32;
396    port       =  0;
397
398    if ((p = strchr(aspec, '/')))
399    {
400       *p++ = '\0';
401
402       if (ijb_isdigit(*p) == 0)
403       {
404          return(-1);
405       }
406       masklength = atoi(p);
407    }
408
409    if ((masklength < 0) || (masklength > 32))
410    {
411       return(-1);
412    }
413
414    if ((p = strchr(aspec, ':')))
415    {
416       *p++ = '\0';
417
418       if (ijb_isdigit(*p) == 0)
419       {
420          return(-1);
421       }
422       port = atoi(p);
423    }
424
425    aca->port = port;
426
427    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
428
429    if (aca->addr == -1)
430    {
431       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
432       return(-1);
433    }
434
435    /* build the netmask */
436    aca->mask = 0;
437    for (i=1; i <= masklength ; i++)
438    {
439       aca->mask |= (1 << (32 - i));
440    }
441
442    /* now mask off the host portion of the ip address
443     * (i.e. save on the network portion of the address).
444     */
445    aca->addr = aca->addr & aca->mask;
446
447    return(0);
448
449 }
450 #endif /* def FEATURE_ACL */
451
452
453 /*********************************************************************
454  *
455  * Function    :  block_url
456  *
457  * Description :  Called from `chat'.  Check to see if we need to block this.
458  *
459  * Parameters  :
460  *          1  :  csp = Current client state (buffers, headers, etc...)
461  *
462  * Returns     :  NULL => unblocked, else HTTP block response
463  *
464  *********************************************************************/
465 struct http_response *block_url(struct client_state *csp)
466 {
467 #ifdef FEATURE_IMAGE_BLOCKING
468    char *p;
469 #endif /* def FEATURE_IMAGE_BLOCKING */
470    struct http_response *rsp;
471
472    /* 
473     * If it's not blocked, don't block it ;-)
474     */
475    if ((csp->action->flags & ACTION_BLOCK) == 0)
476    {
477       return(NULL);
478    }
479
480    /* 
481     * Else, prepare a response
482     */
483    if (NULL == (rsp = alloc_http_response()))
484    {
485       return NULL;
486    }
487
488    /*
489     * If it's an image-url, send back an image or redirect
490     * as specified by the relevant +image action
491     */
492 #ifdef FEATURE_IMAGE_BLOCKING
493    if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
494         && is_imageurl(csp))
495    {
496       /* determine HOW images should be blocked */
497       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
498
499       /* and handle accordingly: */
500       if ((p == NULL) || (0 == strcmpic(p, "logo")))
501       {
502          rsp->body = bindup(image_junkbuster_gif_data, image_junkbuster_gif_length);
503          rsp->content_length = image_junkbuster_gif_length;
504          enlist_unique_header(rsp->headers, "Content-Type", "image/gif");
505       }
506
507       else if (0 == strcmpic(p, "blank"))
508       {
509          rsp->body = bindup(image_blank_gif_data, image_blank_gif_length);
510          rsp->content_length = image_blank_gif_length;
511          enlist_unique_header(rsp->headers, "Content-Type", "image/gif");
512       }
513
514       else
515       {
516          rsp->status = strdup("302 Local Redirect from Junkbuster");
517          enlist_unique_header(rsp->headers, "Location", p);
518       }
519    }  
520    else
521 #endif /* def FEATURE_IMAGE_BLOCKING */
522
523    /* 
524     * Else, generate an HTML "blocked" message:
525     */
526    {
527       struct map * exports = default_exports(csp, NULL);
528 #ifdef FEATURE_FORCE_LOAD
529       map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
530 #else /* ifndef FEATURE_FORCE_LOAD */
531       map_block_killer(exports, "force-support");
532 #endif /* ndef FEATURE_FORCE_LOAD */
533
534       map(exports, "hostport", 1, csp->http->hostport, 1);
535       map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
536       map(exports, "path", 1, csp->http->path, 1);
537       map(exports, "path-html", 1, html_encode(csp->http->path), 0);
538
539       rsp->body = template_load(csp, "blocked");
540       template_fill(&rsp->body, exports);
541       free_map(exports);
542   
543       /*
544        * Workaround for stupid Netscape bug which prevents
545        * pages from being displayed if loading a referenced
546        * JavaScript or style sheet fails. So make it appear
547        * as if it succeeded.
548        */
549       if (csp->http->user_agent
550           && !strncmpic(csp->http->user_agent, "mozilla", 7)
551           && !strstr(csp->http->user_agent, "compatible")
552           && !strstr(csp->http->user_agent, "Opera"))
553       {
554          rsp->status = strdup("200 Request for blocked URL"); 
555       }
556       else
557       {
558          rsp->status = strdup("404 Request for blocked URL"); 
559       }
560
561    }
562
563    return(finish_http_response(rsp));
564
565 }
566
567
568 #ifdef FEATURE_TRUST
569 /*********************************************************************
570  *
571  * Function    :  trust_url FIXME: I should be called distrust_url
572  *
573  * Description :  Calls is_untrusted_url to determine if the URL is trusted
574  *                and if not, returns a HTTP 304 response with a reject message.
575  *
576  * Parameters  :
577  *          1  :  csp = Current client state (buffers, headers, etc...)
578  *
579  * Returns     :  NULL => trusted, else http_response.
580  *
581  *********************************************************************/
582 struct http_response *trust_url(struct client_state *csp)
583 {
584    struct http_response *rsp;
585    struct map * exports;
586    char buf[BUFFER_SIZE], *p = NULL;
587    struct url_spec **tl, *t;
588
589    /*
590     * Don't bother to work on trusted URLs
591     */
592    if (!is_untrusted_url(csp))
593    {
594       return NULL;
595    }
596
597    /* 
598     * Else, prepare a response:
599     */
600    if (NULL == (rsp = alloc_http_response()))
601    {
602       return NULL;
603    }
604
605    exports = default_exports(csp, NULL);
606
607    /* 
608     * Export the host, port, and referrer information
609     */
610    map(exports, "hostport", 1, csp->http->hostport, 1);
611    map(exports, "path", 1, csp->http->path, 1);
612    map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
613    map(exports, "path-html", 1, html_encode(csp->http->path), 0);
614
615    if (csp->referrer && strlen(csp->referrer) > 9)
616    {
617       map(exports, "referrer", 1, csp->referrer + 9, 1);
618       map(exports, "referrer-html", 1, html_encode(csp->referrer + 9), 0);
619    }
620    else
621    {
622       map(exports, "referrer", 1, "unknown", 1);
623       map(exports, "referrer-html", 1, "unknown", 1);
624    }
625
626    /*
627     * Export the trust list
628     */
629    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
630    {
631       sprintf(buf, "<li>%s</li>\n", t->spec);
632       p = strsav(p, buf);
633    }
634    map(exports, "trusted-referrers", 1, p, 0);
635    p = NULL;
636
637    /*
638     * Export the trust info, if available
639     */
640    if (csp->config->trust_info->next)
641    {
642       struct list *l;
643
644       for (l = csp->config->trust_info->next; l ; l = l->next)
645       {
646          sprintf(buf, "<li> <a href=%s>%s</a><br>\n",l->str, l->str);
647          p = strsav(p, buf);
648       }
649       map(exports, "trust-info", 1, p, 0);
650    }
651    else
652    {
653       map_block_killer(exports, "have-trust-info");
654    }
655    
656    /*
657     * Export the force prefix or the force conditional block killer
658     */
659 #ifdef FEATURE_FORCE_LOAD
660    map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
661 #else /* ifndef FEATURE_FORCE_LOAD */
662    map_block_killer(exports, "force-support");
663 #endif /* ndef FEATURE_FORCE_LOAD */
664
665    /*
666     * Build the response
667     */
668    rsp->body = template_load(csp, "untrusted");
669    template_fill(&rsp->body, exports);
670    free_map(exports);
671
672    return(finish_http_response(rsp));
673
674 }
675 #endif /* def FEATURE_TRUST */
676
677
678 #ifdef FEATURE_FAST_REDIRECTS
679 /*********************************************************************
680  *
681  * Function    :  redirect_url
682  *
683  * Description :  Checks for redirection URLs and returns a HTTP redirect
684  *                to the destination URL, if necessary
685  *
686  * Parameters  :
687  *          1  :  csp = Current client state (buffers, headers, etc...)
688  *
689  * Returns     :  NULL if URL was clean, HTTP redirect otherwise.
690  *
691  *********************************************************************/
692 struct http_response *redirect_url(struct client_state *csp)
693 {
694    char *p, *q;
695    struct http_response *rsp;
696
697    p = q = csp->http->path;
698    log_error(LOG_LEVEL_REDIRECTS, "checking path for redirects: %s", p);
699
700    /* 
701     * find the last URL encoded in the request
702     */
703    while ((p = strstr(p, "http://")))
704    {
705       q = p++;
706    }
707
708    /* 
709     * if there was any, generate and return a HTTP redirect
710     */
711    if (q != csp->http->path)
712    {
713       log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q);
714
715       if (NULL == (rsp = alloc_http_response()))
716       {
717          return NULL;
718       }
719
720       rsp->status = strdup("302 Local Redirect from Junkbuster");
721       enlist_unique_header(rsp->headers, "Location", q);
722
723       return(finish_http_response(rsp));
724    }
725    else
726    {
727       return(NULL);
728    }
729
730 }
731 #endif /* def FEATURE_FAST_REDIRECTS */
732
733
734 #ifdef FEATURE_IMAGE_BLOCKING
735 /*********************************************************************
736  *
737  * Function    :  is_imageurl
738  *
739  * Description :  Given a URL, decide whether it is an image or not,
740  *                using either the info from a previous +image action
741  *                or, #ifdef FEATURE_IMAGE_DETECT_MSIE, the info from
742  *                the browser's accept header.
743  *                
744  * Parameters  :
745  *          1  :  csp = Current client state (buffers, headers, etc...)
746  *
747  * Returns     :  True (nonzero) if URL is an image, false (0)
748  *                otherwise
749  *
750  *********************************************************************/
751 int is_imageurl(struct client_state *csp)
752 {
753 #ifdef FEATURE_IMAGE_DETECT_MSIE
754    if ((csp->accept_types 
755        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
756        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
757    {
758       return 1;
759    }
760    else if ((csp->accept_types 
761        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
762        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
763    {
764       return 0;
765    }
766 #endif /* def FEATURE_IMAGE_DETECT_MSIE */
767
768    return ((csp->action->flags & ACTION_IMAGE) != 0);
769
770 }
771 #endif /* def FEATURE_IMAGE_BLOCKING */
772
773
774 #ifdef FEATURE_COOKIE_JAR
775 /*********************************************************************
776  *
777  * Function    :  is_untrusted_url
778  *
779  * Description :  Should we "distrust" this URL (and block it)?
780  *
781  *                Yes if it matches a line in the trustfile, or if the
782  *                    referrer matches a line starting with "+" in the
783  *                    trustfile.
784  *                No  otherwise.
785  *
786  * Parameters  :
787  *          1  :  csp = Current client state (buffers, headers, etc...)
788  *
789  * Returns     :  0 => trusted, 1 => untrusted
790  *
791  *********************************************************************/
792 int is_untrusted_url(struct client_state *csp)
793 {
794    struct file_list *fl;
795    struct block_spec *b;
796    struct url_spec url[1], **tl, *t;
797    struct http_request rhttp[1];
798    char *p, *h;
799
800    /*
801     * If we don't have a trustlist, we trust everybody
802     */
803    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
804    {
805       return(0);
806    }
807
808
809    /*
810     * Do we trust the request URL itself?
811     */
812    *url = dsplit(csp->http->host);
813
814    /* if splitting the domain fails, punt */
815    if (url->dbuf == NULL) return(0);
816
817    memset(rhttp, '\0', sizeof(*rhttp));
818
819    for (b = b->next; b ; b = b->next)
820    {
821       if ((b->url->port == 0) || (b->url->port == csp->http->port))
822       {
823          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
824          {
825             if ((b->url->path == NULL) ||
826 #ifdef REGEX
827                (regexec(b->url->preg, csp->http->path, 0, NULL, 0) == 0)
828 #else
829                (strncmp(b->url->path, csp->http->path, b->url->pathlen) == 0)
830 #endif
831             )
832             {
833                freez(url->dbuf);
834                freez(url->dvec);
835
836                if (b->reject == 0) return(0);
837
838                return(1);
839             }
840          }
841       }
842    }
843
844    freez(url->dbuf);
845    freez(url->dvec);
846
847    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
848    {
849       /* no referrer was supplied */
850       return(1);
851    }
852
853    /* forge a URL from the referrer so we can use
854     * convert_url() to parse it into its components.
855     */
856
857    p = NULL;
858    p = strsav(p, "GET ");
859    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
860    p = strsav(p, " HTTP/1.0");
861
862    parse_http_request(p, rhttp, csp);
863    freez(p);
864
865    if (rhttp->cmd == NULL)
866    {
867       return(1);
868    }
869
870
871    /*
872     * If not, do we maybe trust its referrer?
873     */
874    *url = dsplit(rhttp->host);
875
876    /* if splitting the domain fails, punt */
877    if (url->dbuf == NULL) return(1);
878
879    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
880    {
881       if ((t->port == 0) || (t->port == rhttp->port))
882       {
883          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
884          {
885             if ((t->path == NULL) ||
886 #ifdef REGEX
887                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
888 #else
889                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
890 #endif
891             )
892             {
893                /* if the URL's referrer is from a trusted referrer, then
894                 * add the target spec to the trustfile as an unblocked
895                 * domain and return NULL (which means it's OK).
896                 */
897
898                FILE *fp;
899
900                freez(url->dbuf);
901                freez(url->dvec);
902
903                if ((fp = fopen(csp->config->trustfile, "a")))
904                {
905                   h = NULL;
906
907                   h = strsav(h, "~");
908                   h = strsav(h, csp->http->hostport);
909
910                   p = csp->http->path;
911                   if ((*p++ == '/')
912                   && (*p++ == '~'))
913                   {
914                   /* since this path points into a user's home space
915                    * be sure to include this spec in the trustfile.
916                    */
917                      if ((p = strchr(p, '/')))
918                      {
919                         *p = '\0';
920                         h = strsav(h, csp->http->path); /* FIXME: p?! */
921                         h = strsav(h, "/");
922                      }
923                   }
924
925                   fprintf(fp, "%s\n", h);
926                   freez(h);
927                   fclose(fp);
928                }
929                return(0);
930             }
931          }
932       }
933    }
934    return(1);
935 }
936 #endif /* def FEATURE_COOKIE_JAR */
937
938
939 /*********************************************************************
940  *
941  * Function    :  pcrs_filter_response
942  *
943  * Description :  Apply all the pcrs jobs from the joblist (re_filterfile)
944  *                to the text buffer that's been accumulated in 
945  *                csp->iob->buf and set csp->content_length to the modified
946  *                size.
947  *
948  * Parameters  :
949  *          1  :  csp = Current client state (buffers, headers, etc...)
950  *
951  * Returns     :  a pointer to the (newly allocated) modified buffer.
952  *                or NULL in case something went wrong
953  *                
954  *********************************************************************/
955 char *pcrs_filter_response(struct client_state *csp)
956 {
957    int hits=0;
958    int size = csp->iob->eod - csp->iob->cur;
959
960    char *old = csp->iob->cur, *new = NULL;
961    pcrs_job *job;
962
963    struct file_list *fl;
964    struct re_filterfile_spec *b;
965
966    /* Sanity first ;-) */
967    if (size <= 0)
968    {
969       return(NULL);
970    }
971
972    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
973    {
974       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
975       return(NULL);
976    }
977
978    if ( NULL == b->joblist )
979    {
980       log_error(LOG_LEVEL_RE_FILTER, "Empty joblist. Nothing to do.");
981       return(NULL);
982    }
983
984    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
985               csp->http->hostport, csp->http->path, size);
986
987    /* Apply all jobs from the joblist */
988    for (job = b->joblist; NULL != job; job = job->next)
989    {
990       hits += pcrs_execute(job, old, size, &new, &size);
991       if (old != csp->iob->cur) free(old);
992       old=new;
993    }
994
995    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
996
997    csp->content_length = size;
998
999    /* fwiw, reset the iob */
1000    IOB_RESET(csp);
1001    return(new);
1002
1003 }
1004
1005
1006 /*********************************************************************
1007  *
1008  * Function    :  gif_deanimate_response
1009  *
1010  * Description :  Deanimate the GIF image that has been accumulated in 
1011  *                csp->iob->buf and set csp->content_length to the modified
1012  *                size.
1013  *
1014  * Parameters  :
1015  *          1  :  csp = Current client state (buffers, headers, etc...)
1016  *
1017  * Returns     :  a pointer to the (newly allocated) modified buffer.
1018  *                or NULL in case something went wrong.
1019  *                
1020  *********************************************************************/
1021 char *gif_deanimate_response(struct client_state *csp)
1022 {
1023    struct binbuffer *in, *out;
1024    char *p;
1025    int size = csp->iob->eod - csp->iob->cur;
1026
1027    if (  (NULL == (in =  (struct binbuffer *)zalloc(sizeof *in )))
1028       || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) )
1029    {
1030       log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)");
1031       return NULL;
1032    }
1033
1034    in->buffer = csp->iob->cur;
1035    in->size = size;
1036
1037    if (gif_deanimate(in, out, strncmp("last", csp->action->string[ACTION_STRING_DEANIMATE], 4)))
1038    {
1039       log_error(LOG_LEVEL_DEANIMATE, "failed! (gif parsing)");
1040       free(in);
1041       buf_free(out);
1042       return(NULL);
1043    }
1044    else
1045    {
1046       log_error(LOG_LEVEL_DEANIMATE, "Success! GIF shrunk from %d bytes to %d.", size, out->offset);
1047       csp->content_length = out->offset;
1048       p = out->buffer;
1049       free(in);
1050       free(out);
1051       return(p);
1052    }  
1053
1054 }
1055
1056
1057 /*********************************************************************
1058  *
1059  * Function    :  url_actions
1060  *
1061  * Description :  Gets the actions for this URL.
1062  *
1063  * Parameters  :
1064  *          1  :  http = http_request request for blocked URLs
1065  *          2  :  csp = Current client state (buffers, headers, etc...)
1066  *
1067  * Returns     :  N/A
1068  *
1069  *********************************************************************/
1070 void url_actions(struct http_request *http, 
1071                  struct client_state *csp)
1072 {
1073    struct file_list *fl;
1074    struct url_actions *b;
1075
1076    init_current_action(csp->action);
1077
1078    if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
1079    {
1080       return;
1081    }
1082
1083    apply_url_actions(csp->action, http, b);
1084
1085 }
1086
1087
1088 /*********************************************************************
1089  *
1090  * Function    :  apply_url_actions
1091  *
1092  * Description :  Applies a list of URL actions.
1093  *
1094  * Parameters  :
1095  *          1  :  action = Destination.
1096  *          2  :  http = Current URL
1097  *          3  :  b = list of URL actions to apply
1098  *
1099  * Returns     :  N/A
1100  *
1101  *********************************************************************/
1102 void apply_url_actions(struct current_action_spec *action, 
1103                        struct http_request *http, 
1104                        struct url_actions *b)
1105 {
1106    struct url_spec url[1];
1107
1108    if (b == NULL)
1109    {
1110       /* Should never happen */
1111       return;
1112    }
1113
1114    *url = dsplit(http->host);
1115
1116    /* if splitting the domain fails, punt */
1117    if (url->dbuf == NULL)
1118    {
1119       return;
1120    }
1121
1122    for (b = b->next; NULL != b; b = b->next)
1123    {
1124       if ((b->url->port == 0) || (b->url->port == http->port))
1125       {
1126          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
1127          {
1128             if ((b->url->path == NULL) ||
1129 #ifdef REGEX
1130                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
1131 #else
1132                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
1133 #endif
1134             )
1135             {
1136                merge_current_action(action, b->action);
1137             }
1138          }
1139       }
1140    }
1141
1142    freez(url->dbuf);
1143    freez(url->dvec);
1144 }
1145
1146
1147 /*********************************************************************
1148  *
1149  * Function    :  forward_url
1150  *
1151  * Description :  Should we forward this to another proxy?
1152  *
1153  * Parameters  :
1154  *          1  :  http = http_request request for current URL
1155  *          2  :  csp = Current client state (buffers, headers, etc...)
1156  *
1157  * Returns     :  Pointer to forwarding information.
1158  *
1159  *********************************************************************/
1160 const struct forward_spec * forward_url(struct http_request *http,
1161                                         struct client_state *csp)
1162 {
1163    static const struct forward_spec fwd_default[1] = { FORWARD_SPEC_INITIALIZER };
1164    struct forward_spec *fwd = csp->config->forward;
1165    struct url_spec url[1];
1166
1167    if (fwd == NULL)
1168    {
1169       return(fwd_default);
1170    }
1171
1172    *url = dsplit(http->host);
1173
1174    /* if splitting the domain fails, punt */
1175    if (url->dbuf == NULL)
1176    {
1177       return(fwd_default);
1178    }
1179
1180    while (fwd != NULL)
1181    {
1182       if ((fwd->url->port == 0) || (fwd->url->port == http->port))
1183       {
1184          if ((fwd->url->domain[0] == '\0') || (domaincmp(fwd->url, url) == 0))
1185          {
1186             if ((fwd->url->path == NULL) ||
1187 #ifdef REGEX
1188                (regexec(fwd->url->preg, http->path, 0, NULL, 0) == 0)
1189 #else
1190                (strncmp(fwd->url->path, http->path, fwd->url->pathlen) == 0)
1191 #endif
1192             )
1193             {
1194                freez(url->dbuf);
1195                freez(url->dvec);
1196                return(fwd);
1197             }
1198          }
1199       }
1200       fwd = fwd->next;
1201    }
1202
1203    freez(url->dbuf);
1204    freez(url->dvec);
1205    return(fwd_default);
1206
1207 }
1208
1209
1210 /*********************************************************************
1211  *
1212  * Function    :  dsplit
1213  *
1214  * Description :  Takes a domain and returns a pointer to a url_spec
1215  *                structure populated with dbuf, dcnt and dvec.  The
1216  *                other fields in the structure that is returned are zero.
1217  *
1218  * Parameters  :
1219  *          1  :  domain = a URL address
1220  *
1221  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
1222  *
1223  *********************************************************************/
1224 struct url_spec dsplit(char *domain)
1225 {
1226    struct url_spec ret[1];
1227    char *v[BUFFER_SIZE];
1228    int size;
1229    char *p;
1230
1231    memset(ret, '\0', sizeof(*ret));
1232
1233    if (domain[strlen(domain) - 1] == '.')
1234    {
1235       ret->unanchored |= ANCHOR_RIGHT;
1236    }
1237
1238    if (domain[0] == '.')
1239    {
1240       ret->unanchored |= ANCHOR_LEFT;
1241    }
1242
1243    ret->dbuf = strdup(domain);
1244
1245    /* map to lower case */
1246    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
1247
1248    /* split the domain name into components */
1249    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
1250
1251    if (ret->dcnt <= 0)
1252    {
1253       memset(ret, '\0', sizeof(ret));
1254       return(*ret);
1255    }
1256
1257    /* save a copy of the pointers in dvec */
1258    size = ret->dcnt * sizeof(*ret->dvec);
1259
1260    if ((ret->dvec = (char **)malloc(size)))
1261    {
1262       memcpy(ret->dvec, v, size);
1263    }
1264
1265    return(*ret);
1266
1267 }
1268
1269
1270 /*********************************************************************
1271  *
1272  * Function    :  simple_domaincmp
1273  *
1274  * Description :  Domain-wise Compare fqdn's.  The comparison is 
1275  *                both left- and right-anchored.  The individual
1276  *                domain names are compared with simplematch().
1277  *                This is only used by domaincmp.
1278  *
1279  * Parameters  :
1280  *          1  :  pv = array of patterns to compare
1281  *          2  :  fv = array of domain components to compare
1282  *          3  :  len = length of the arrays (both arrays are the
1283  *                      same length - if they weren't, it couldn't
1284  *                      possibly be a match).
1285  *
1286  * Returns     :  0 => domains are equivalent, else no match.
1287  *
1288  *********************************************************************/
1289 static int simple_domaincmp(char **pv, char **fv, int len)
1290 {
1291    int n;
1292
1293    for (n = 0; n < len; n++)
1294    {
1295       if (simplematch(pv[n], fv[n]))
1296       {
1297          return 1;
1298       }
1299    }
1300
1301    return 0;
1302
1303 }
1304
1305
1306 /*********************************************************************
1307  *
1308  * Function    :  domaincmp
1309  *
1310  * Description :  Domain-wise Compare fqdn's. Governed by the bimap in
1311  *                pattern->unachored, the comparison is un-, left-,
1312  *                right-anchored, or both.
1313  *                The individual domain names are compared with
1314  *                simplematch().
1315  *
1316  * Parameters  :
1317  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
1318  *          2  :  fqdn = domain name against which the patterns are compared.
1319  *
1320  * Returns     :  0 => domains are equivalent, else no match.
1321  *
1322  *********************************************************************/
1323 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
1324 {
1325    char **pv, **fv;  /* vectors  */
1326    int    plen, flen;
1327    int unanchored = pattern->unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
1328
1329    plen = pattern->dcnt;
1330    flen = fqdn->dcnt;
1331
1332    if (flen < plen)
1333    {
1334       /* fqdn is too short to match this pattern */
1335       return 1;
1336    }
1337
1338    pv   = pattern->dvec;
1339    fv   = fqdn->dvec;
1340
1341    if (unanchored == ANCHOR_LEFT)
1342    {
1343       /*
1344        * Right anchored.
1345        *
1346        * Convert this into a fully anchored pattern with
1347        * the fqdn and pattern the same length
1348        */
1349       fv += (flen - plen); /* flen - plen >= 0 due to check above */
1350       return simple_domaincmp(pv, fv, plen);
1351    }
1352    else if (unanchored == 0)
1353    {
1354       /* Fully anchored, check length */
1355       if (flen != plen)
1356       {
1357          return 1;
1358       }
1359       return simple_domaincmp(pv, fv, plen);
1360    }
1361    else if (unanchored == ANCHOR_RIGHT)
1362    {
1363       /* Left anchored, ignore all extra in fqdn */
1364       return simple_domaincmp(pv, fv, plen);
1365    }
1366    else
1367    {
1368       /* Unanchored */
1369       int n;
1370       int maxn = flen - plen;
1371       for (n = 0; n <= maxn; n++)
1372       {
1373          if (!simple_domaincmp(pv, fv, plen))
1374          {
1375             return 0;
1376          }
1377          /*
1378           * Doesn't match from start of fqdn
1379           * Try skipping first part of fqdn
1380           */
1381          fv++;
1382       }
1383       return 1;
1384    }
1385
1386 }
1387
1388
1389 /*
1390   Local Variables:
1391   tab-width: 3
1392   end:
1393 */