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