Turn family mismatches in match_sockaddr() into fatal errors
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.164 2011/12/31 14:47:44 fabiankeil Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *
8  * Copyright   :  Written by and Copyright (C) 2001-2011 the
9  *                Privoxy team. http://www.privoxy.org/
10  *
11  *                Based on the Internet Junkbuster originally written
12  *                by and Copyright (C) 1997 Anonymous Coders and
13  *                Junkbusters Corporation.  http://www.junkbusters.com
14  *
15  *                This program is free software; you can redistribute it
16  *                and/or modify it under the terms of the GNU General
17  *                Public License as published by the Free Software
18  *                Foundation; either version 2 of the License, or (at
19  *                your option) any later version.
20  *
21  *                This program is distributed in the hope that it will
22  *                be useful, but WITHOUT ANY WARRANTY; without even the
23  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
24  *                PARTICULAR PURPOSE.  See the GNU General Public
25  *                License for more details.
26  *
27  *                The GNU General Public License should be included with
28  *                this file.  If not, you can view it at
29  *                http://www.gnu.org/copyleft/gpl.html
30  *                or write to the Free Software Foundation, Inc., 59
31  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32  *
33  *********************************************************************/
34
35
36 #include "config.h"
37
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <assert.h>
44
45 #ifndef _WIN32
46 #ifndef __OS2__
47 #include <unistd.h>
48 #endif /* ndef __OS2__ */
49 #include <netinet/in.h>
50 #else
51 #include <winsock2.h>
52 #endif /* ndef _WIN32 */
53
54 #ifdef __OS2__
55 #include <utils.h>
56 #endif /* def __OS2__ */
57
58 #include "project.h"
59 #include "filters.h"
60 #include "encode.h"
61 #include "parsers.h"
62 #include "ssplit.h"
63 #include "errlog.h"
64 #include "jbsockets.h"
65 #include "miscutil.h"
66 #include "actions.h"
67 #include "cgi.h"
68 #include "list.h"
69 #include "deanimate.h"
70 #include "urlmatch.h"
71 #include "loaders.h"
72
73 #ifdef _WIN32
74 #include "win32.h"
75 #endif
76
77 const char filters_h_rcs[] = FILTERS_H_VERSION;
78
79 /* Fix a problem with Solaris.  There should be no effect on other
80  * platforms.
81  * Solaris's isspace() is a macro which uses it's argument directly
82  * as an array index.  Therefore we need to make sure that high-bit
83  * characters generate +ve values, and ideally we also want to make
84  * the argument match the declared parameter type of "int".
85  */
86 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
87
88 typedef char *(*filter_function_ptr)();
89 static filter_function_ptr get_filter_function(const struct client_state *csp);
90 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
91 static jb_err prepare_for_filtering(struct client_state *csp);
92
93 #ifdef FEATURE_ACL
94 #ifdef HAVE_RFC2553
95 /*********************************************************************
96  *
97  * Function    :  sockaddr_storage_to_ip
98  *
99  * Description :  Access internal structure of sockaddr_storage
100  *
101  * Parameters  :
102  *          1  :  addr = socket address
103  *          2  :  ip   = IP address as array of octets in network order
104  *                       (it points into addr)
105  *          3  :  len  = length of IP address in octets
106  *          4  :  port = port number in network order;
107  *
108  * Returns     :  0 = no errror; -1 otherwise.
109  *
110  *********************************************************************/
111 static int sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
112                                   uint8_t **ip, unsigned int *len,
113                                   in_port_t **port)
114 {
115    if (NULL == addr)
116    {
117       return(-1);
118    }
119
120    switch (addr->ss_family)
121    {
122       case AF_INET:
123          if (NULL != len)
124          {
125             *len = 4;
126          }
127          if (NULL != ip)
128          {
129             *ip = (uint8_t *)
130                &(((struct sockaddr_in *)addr)->sin_addr.s_addr);
131          }
132          if (NULL != port)
133          {
134             *port = &((struct sockaddr_in *)addr)->sin_port;
135          }
136          break;
137
138       case AF_INET6:
139          if (NULL != len)
140          {
141             *len = 16;
142          }
143          if (NULL != ip)
144          {
145             *ip = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr;
146          }
147          if (NULL != port)
148          {
149             *port = &((struct sockaddr_in6 *)addr)->sin6_port;
150          }
151          break;
152
153       default:
154          /* Unsupported address family */
155          return(-1);
156    }
157
158    return(0);
159 }
160
161
162 /*********************************************************************
163  *
164  * Function    :  match_sockaddr
165  *
166  * Description :  Check whether address matches network (IP address and port)
167  *
168  * Parameters  :
169  *          1  :  network = socket address of subnework
170  *          2  :  netmask = network mask as socket address
171  *          3  :  address = checked socket address against given network
172  *
173  * Returns     :  0 = doesn't match; 1 = does match
174  *
175  *********************************************************************/
176 static int match_sockaddr(const struct sockaddr_storage *network,
177                           const struct sockaddr_storage *netmask,
178                           const struct sockaddr_storage *address)
179 {
180    uint8_t *network_addr, *netmask_addr, *address_addr;
181    unsigned int addr_len;
182    in_port_t *network_port, *netmask_port, *address_port;
183    int i;
184
185    if (network->ss_family != netmask->ss_family)
186    {
187       /* This should never happen */
188       assert(network->ss_family == netmask->ss_family);
189       log_error(LOG_LEVEL_FATAL, "Network and netmask differ in family.");
190    }
191
192    sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port);
193    sockaddr_storage_to_ip(netmask, &netmask_addr, NULL, &netmask_port);
194    sockaddr_storage_to_ip(address, &address_addr, NULL, &address_port);
195
196    /* Check for family */
197    if ((network->ss_family == AF_INET) && (address->ss_family == AF_INET6)
198       && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)address_addr))
199    {
200       /* Map AF_INET6 V4MAPPED address into AF_INET */
201       address_addr += 12;
202       addr_len = 4;
203    }
204    else if ((network->ss_family == AF_INET6) && (address->ss_family == AF_INET)
205       && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)network_addr))
206    {
207       /* Map AF_INET6 V4MAPPED network into AF_INET */
208       network_addr += 12;
209       netmask_addr += 12;
210       addr_len = 4;
211    }
212    else if (network->ss_family != address->ss_family)
213    {
214       return 0;
215    }
216
217    /* XXX: Port check is signaled in netmask */
218    if (*netmask_port && *network_port != *address_port)
219    {
220       return 0;
221    }
222
223    /* TODO: Optimize by checking by words insted of octets */
224    for (i = 0; (i < addr_len) && netmask_addr[i]; i++)
225    {
226       if ((network_addr[i] & netmask_addr[i]) !=
227           (address_addr[i] & netmask_addr[i]))
228       {
229          return 0;
230       }
231    }
232
233    return 1;
234 }
235 #endif /* def HAVE_RFC2553 */
236
237
238 /*********************************************************************
239  *
240  * Function    :  block_acl
241  *
242  * Description :  Block this request?
243  *                Decide yes or no based on ACL file.
244  *
245  * Parameters  :
246  *          1  :  dst = The proxy or gateway address this is going to.
247  *                      Or NULL to check all possible targets.
248  *          2  :  csp = Current client state (buffers, headers, etc...)
249  *                      Also includes the client IP address.
250  *
251  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
252  *
253  *********************************************************************/
254 int block_acl(const struct access_control_addr *dst, const struct client_state *csp)
255 {
256    struct access_control_list *acl = csp->config->acl;
257
258    /* if not using an access control list, then permit the connection */
259    if (acl == NULL)
260    {
261       return(0);
262    }
263
264    /* search the list */
265    while (acl != NULL)
266    {
267       if (
268 #ifdef HAVE_RFC2553
269             match_sockaddr(&acl->src->addr, &acl->src->mask, &csp->tcp_addr)
270 #else
271             (csp->ip_addr_long & acl->src->mask) == acl->src->addr
272 #endif
273             )
274       {
275          if (dst == NULL)
276          {
277             /* Just want to check if they have any access */
278             if (acl->action == ACL_PERMIT)
279             {
280                return(0);
281             }
282             else
283             {
284                return(1);
285             }
286          }
287          else if (
288 #ifdef HAVE_RFC2553
289                /*
290                 * XXX: An undefined acl->dst is full of zeros and should be
291                 * considered a wildcard address. sockaddr_storage_to_ip()
292                 * fails on such destinations because of unknown sa_familly
293                 * (glibc only?). However this test is not portable.
294                 *
295                 * So, we signal the acl->dst is wildcard in wildcard_dst.
296                 */
297                acl->wildcard_dst ||
298                   match_sockaddr(&acl->dst->addr, &acl->dst->mask, &dst->addr)
299 #else
300                ((dst->addr & acl->dst->mask) == acl->dst->addr)
301            && ((dst->port == acl->dst->port) || (acl->dst->port == 0))
302 #endif
303            )
304          {
305             if (acl->action == ACL_PERMIT)
306             {
307                return(0);
308             }
309             else
310             {
311                return(1);
312             }
313          }
314       }
315       acl = acl->next;
316    }
317
318    return(1);
319
320 }
321
322
323 /*********************************************************************
324  *
325  * Function    :  acl_addr
326  *
327  * Description :  Called from `load_config' to parse an ACL address.
328  *
329  * Parameters  :
330  *          1  :  aspec = String specifying ACL address.
331  *          2  :  aca = struct access_control_addr to fill in.
332  *
333  * Returns     :  0 => Ok, everything else is an error.
334  *
335  *********************************************************************/
336 int acl_addr(const char *aspec, struct access_control_addr *aca)
337 {
338    int i, masklength;
339 #ifdef HAVE_RFC2553
340    struct addrinfo hints, *result;
341    uint8_t *mask_data;
342    in_port_t *mask_port;
343    unsigned int addr_len;
344 #else
345    long port;
346 #endif /* def HAVE_RFC2553 */
347    char *p;
348    char *acl_spec = NULL;
349
350 #ifdef HAVE_RFC2553
351    /* XXX: Depend on ai_family */
352    masklength = 128;
353 #else
354    masklength = 32;
355    port       =  0;
356 #endif
357
358    /*
359     * Use a temporary acl spec copy so we can log
360     * the unmodified original in case of parse errors.
361     */
362    acl_spec = strdup(aspec);
363    if (acl_spec == NULL)
364    {
365       /* XXX: This will be logged as parse error. */
366       return(-1);
367    }
368
369    if ((p = strchr(acl_spec, '/')) != NULL)
370    {
371       *p++ = '\0';
372       if (ijb_isdigit(*p) == 0)
373       {
374          freez(acl_spec);
375          return(-1);
376       }
377       masklength = atoi(p);
378    }
379
380    if ((masklength < 0) ||
381 #ifdef HAVE_RFC2553
382          (masklength > 128)
383 #else
384          (masklength > 32)
385 #endif
386          )
387    {
388       freez(acl_spec);
389       return(-1);
390    }
391
392    if ((*acl_spec == '[') && (NULL != (p = strchr(acl_spec, ']'))))
393    {
394       *p = '\0';
395       memmove(acl_spec, acl_spec + 1, (size_t)(p - acl_spec));
396
397       if (*++p != ':')
398       {
399          p = NULL;
400       }
401    }
402    else
403    {
404       p = strchr(acl_spec, ':');
405    }
406
407 #ifdef HAVE_RFC2553
408    memset(&hints, 0, sizeof(struct addrinfo));
409    hints.ai_family = AF_UNSPEC;
410    hints.ai_socktype = SOCK_STREAM;
411
412    i = getaddrinfo(acl_spec, ((p) ? ++p : NULL), &hints, &result);
413
414    if (i != 0)
415    {
416       log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s",
417          acl_spec, p, gai_strerror(i));
418       freez(acl_spec);
419       return(-1);
420    }
421    freez(acl_spec);
422
423    /* TODO: Allow multihomed hostnames */
424    memcpy(&(aca->addr), result->ai_addr, result->ai_addrlen);
425    freeaddrinfo(result);
426 #else
427    if (p != NULL)
428    {
429       char *endptr;
430
431       *p++ = '\0';
432       port = strtol(p, &endptr, 10);
433
434       if (port <= 0 || port > 65535 || *endptr != '\0')
435       {
436          freez(acl_spec);
437          return(-1);
438       }
439    }
440
441    aca->port = (unsigned long)port;
442
443    aca->addr = ntohl(resolve_hostname_to_ip(acl_spec));
444    freez(acl_spec);
445
446    if (aca->addr == INADDR_NONE)
447    {
448       /* XXX: This will be logged as parse error. */
449       return(-1);
450    }
451 #endif /* def HAVE_RFC2553 */
452
453    /* build the netmask */
454 #ifdef HAVE_RFC2553
455    /* Clip masklength according to current family. */
456    if ((aca->addr.ss_family == AF_INET) && (masklength > 32))
457    {
458       masklength = 32;
459    }
460
461    aca->mask.ss_family = aca->addr.ss_family;
462    if (sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port))
463    {
464       return(-1);
465    }
466
467    if (p)
468    {
469       /* ACL contains a port number, check ports in the future. */
470       *mask_port = 1;
471    }
472
473    /*
474     * XXX: This could be optimized to operate on whole words instead
475     * of octets (128-bit CPU could do it in one iteration).
476     */
477    /*
478     * Octets after prefix can be omitted because of
479     * previous initialization to zeros.
480     */
481    for (i = 0; (i < addr_len) && masklength; i++)
482    {
483       if (masklength >= 8)
484       {
485          mask_data[i] = 0xFF;
486          masklength -= 8;
487       }
488       else
489       {
490          /*
491           * XXX: This assumes MSB of octet is on the left side.
492           * This should be true for all architectures or solved
493           * by the link layer.
494           */
495          mask_data[i] = (uint8_t)~((1 << (8 - masklength)) - 1);
496          masklength = 0;
497       }
498    }
499
500 #else
501    aca->mask = 0;
502    for (i=1; i <= masklength ; i++)
503    {
504       aca->mask |= (1U << (32 - i));
505    }
506
507    /* now mask off the host portion of the ip address
508     * (i.e. save on the network portion of the address).
509     */
510    aca->addr = aca->addr & aca->mask;
511 #endif /* def HAVE_RFC2553 */
512
513    return(0);
514
515 }
516 #endif /* def FEATURE_ACL */
517
518
519 /*********************************************************************
520  *
521  * Function    :  connect_port_is_forbidden
522  *
523  * Description :  Check to see if CONNECT requests to the destination
524  *                port of this request are forbidden. The check is
525  *                independend of the actual request method.
526  *
527  * Parameters  :
528  *          1  :  csp = Current client state (buffers, headers, etc...)
529  *
530  * Returns     :  True if yes, false otherwise.
531  *
532  *********************************************************************/
533 int connect_port_is_forbidden(const struct client_state *csp)
534 {
535    return ((csp->action->flags & ACTION_LIMIT_CONNECT) &&
536      !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT],
537         csp->http->port));
538 }
539
540
541 /*********************************************************************
542  *
543  * Function    :  block_url
544  *
545  * Description :  Called from `chat'.  Check to see if we need to block this.
546  *
547  * Parameters  :
548  *          1  :  csp = Current client state (buffers, headers, etc...)
549  *
550  * Returns     :  NULL => unblocked, else HTTP block response
551  *
552  *********************************************************************/
553 struct http_response *block_url(struct client_state *csp)
554 {
555    struct http_response *rsp;
556    const char *new_content_type = NULL;
557
558    /*
559     * If it's not blocked, don't block it ;-)
560     */
561    if ((csp->action->flags & ACTION_BLOCK) == 0)
562    {
563       return NULL;
564    }
565    if (csp->action->flags & ACTION_REDIRECT)
566    {
567       log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");
568    }
569    /*
570     * Else, prepare a response
571     */
572    if (NULL == (rsp = alloc_http_response()))
573    {
574       return cgi_error_memory();
575    }
576
577    /*
578     * If it's an image-url, send back an image or redirect
579     * as specified by the relevant +image action
580     */
581 #ifdef FEATURE_IMAGE_BLOCKING
582    if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
583         && is_imageurl(csp))
584    {
585       char *p;
586       /* determine HOW images should be blocked */
587       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
588
589       if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
590       {
591          log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image.");
592       }
593
594       /* and handle accordingly: */
595       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
596       {
597          rsp->status = strdup("403 Request blocked by Privoxy");
598          if (rsp->status == NULL)
599          {
600             free_http_response(rsp);
601             return cgi_error_memory();
602          }
603          rsp->body = bindup(image_pattern_data, image_pattern_length);
604          if (rsp->body == NULL)
605          {
606             free_http_response(rsp);
607             return cgi_error_memory();
608          }
609          rsp->content_length = image_pattern_length;
610
611          if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
612          {
613             free_http_response(rsp);
614             return cgi_error_memory();
615          }
616       }
617       else if (0 == strcmpic(p, "blank"))
618       {
619          rsp->status = strdup("403 Request blocked by Privoxy");
620          if (rsp->status == NULL)
621          {
622             free_http_response(rsp);
623             return cgi_error_memory();
624          }
625          rsp->body = bindup(image_blank_data, image_blank_length);
626          if (rsp->body == NULL)
627          {
628             free_http_response(rsp);
629             return cgi_error_memory();
630          }
631          rsp->content_length = image_blank_length;
632
633          if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
634          {
635             free_http_response(rsp);
636             return cgi_error_memory();
637          }
638       }
639       else
640       {
641          rsp->status = strdup("302 Local Redirect from Privoxy");
642          if (rsp->status == NULL)
643          {
644             free_http_response(rsp);
645             return cgi_error_memory();
646          }
647
648          if (enlist_unique_header(rsp->headers, "Location", p))
649          {
650             free_http_response(rsp);
651             return cgi_error_memory();
652          }
653       }
654
655    }
656    else
657 #endif /* def FEATURE_IMAGE_BLOCKING */
658    if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
659    {
660      /*
661       *  Send empty document.
662       */
663       new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
664
665       freez(rsp->body);
666       rsp->body = strdup(" ");
667       rsp->content_length = 1;
668
669       if (csp->config->feature_flags & RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK)
670       {
671          /*
672           * Workaround for firefox bug 492459
673           *   https://bugzilla.mozilla.org/show_bug.cgi?id=492459
674           * Return a 200 OK status for pages blocked with +handle-as-empty-document
675           * if the "handle-as-empty-doc-returns-ok" runtime config option is set.
676           */
677          rsp->status = strdup("200 Request blocked by Privoxy");
678       }
679       else
680       {
681          rsp->status = strdup("403 Request blocked by Privoxy");
682       }
683
684       if (rsp->status == NULL)
685       {
686          free_http_response(rsp);
687          return cgi_error_memory();
688       }
689       if (new_content_type != 0)
690       {
691          log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type);
692          if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type))
693          {
694             free_http_response(rsp);
695             return cgi_error_memory();
696          }
697       }
698    }
699    else
700
701    /*
702     * Else, generate an HTML "blocked" message:
703     */
704    {
705       jb_err err;
706       struct map * exports;
707
708       rsp->status = strdup("403 Request blocked by Privoxy");
709       if (rsp->status == NULL)
710       {
711          free_http_response(rsp);
712          return cgi_error_memory();
713       }
714
715       exports = default_exports(csp, NULL);
716       if (exports == NULL)
717       {
718          free_http_response(rsp);
719          return cgi_error_memory();
720       }
721
722 #ifdef FEATURE_FORCE_LOAD
723       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
724       /*
725        * Export the force conditional block killer if
726        *
727        * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
728        * - Privoxy is configured to enforce blocks, or
729        * - it's a CONNECT request and enforcing wouldn't work anyway.
730        */
731       if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
732        || (0 == strcmpic(csp->http->gpc, "connect")))
733 #endif /* ndef FEATURE_FORCE_LOAD */
734       {
735          err = map_block_killer(exports, "force-support");
736       }
737
738       if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
739       if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
740       if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
741       if (!err) err = map(exports, "path-ue", 1, url_encode(csp->http->path), 0);
742       if (!err)
743       {
744          const char *block_reason;
745          if (csp->action->string[ACTION_STRING_BLOCK] != NULL)
746          {
747             block_reason = csp->action->string[ACTION_STRING_BLOCK];
748          }
749          else
750          {
751             assert(connect_port_is_forbidden(csp));
752             block_reason = "Forbidden CONNECT port.";
753          }
754          err = map(exports, "block-reason", 1, html_encode(block_reason), 0);
755       }
756       if (err)
757       {
758          free_map(exports);
759          free_http_response(rsp);
760          return cgi_error_memory();
761       }
762
763       err = template_fill_for_cgi(csp, "blocked", exports, rsp);
764       if (err)
765       {
766          free_http_response(rsp);
767          return cgi_error_memory();
768       }
769    }
770    rsp->crunch_reason = BLOCKED;
771
772    return finish_http_response(csp, rsp);
773
774 }
775
776
777 #ifdef FEATURE_TRUST
778 /*********************************************************************
779  *
780  * Function    :  trust_url FIXME: I should be called distrust_url
781  *
782  * Description :  Calls is_untrusted_url to determine if the URL is trusted
783  *                and if not, returns a HTTP 403 response with a reject message.
784  *
785  * Parameters  :
786  *          1  :  csp = Current client state (buffers, headers, etc...)
787  *
788  * Returns     :  NULL => trusted, else http_response.
789  *
790  *********************************************************************/
791 struct http_response *trust_url(struct client_state *csp)
792 {
793    struct http_response *rsp;
794    struct map * exports;
795    char buf[BUFFER_SIZE];
796    char *p;
797    struct url_spec **tl;
798    struct url_spec *t;
799    jb_err err;
800
801    /*
802     * Don't bother to work on trusted URLs
803     */
804    if (!is_untrusted_url(csp))
805    {
806       return NULL;
807    }
808
809    /*
810     * Else, prepare a response:
811     */
812    if (NULL == (rsp = alloc_http_response()))
813    {
814       return cgi_error_memory();
815    }
816
817    rsp->status = strdup("403 Request blocked by Privoxy");
818    exports = default_exports(csp, NULL);
819    if (exports == NULL || rsp->status == NULL)
820    {
821       free_http_response(rsp);
822       return cgi_error_memory();
823    }
824
825    /*
826     * Export the protocol, host, port, and referrer information
827     */
828    err = map(exports, "hostport", 1, csp->http->hostport, 1);
829    if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
830    if (!err) err = map(exports, "path", 1, csp->http->path, 1);
831
832    if (NULL != (p = get_header_value(csp->headers, "Referer:")))
833    {
834       if (!err) err = map(exports, "referrer", 1, html_encode(p), 0);
835    }
836    else
837    {
838       if (!err) err = map(exports, "referrer", 1, "none set", 1);
839    }
840
841    if (err)
842    {
843       free_map(exports);
844       free_http_response(rsp);
845       return cgi_error_memory();
846    }
847
848    /*
849     * Export the trust list
850     */
851    p = strdup("");
852    for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
853    {
854       snprintf(buf, sizeof(buf), "<li>%s</li>\n", t->spec);
855       string_append(&p, buf);
856    }
857    err = map(exports, "trusted-referrers", 1, p, 0);
858
859    if (err)
860    {
861       free_map(exports);
862       free_http_response(rsp);
863       return cgi_error_memory();
864    }
865
866    /*
867     * Export the trust info, if available
868     */
869    if (csp->config->trust_info->first)
870    {
871       struct list_entry *l;
872
873       p = strdup("");
874       for (l = csp->config->trust_info->first; l ; l = l->next)
875       {
876          snprintf(buf, sizeof(buf), "<li> <a href=\"%s\">%s</a><br>\n", l->str, l->str);
877          string_append(&p, buf);
878       }
879       err = map(exports, "trust-info", 1, p, 0);
880    }
881    else
882    {
883       err = map_block_killer(exports, "have-trust-info");
884    }
885
886    if (err)
887    {
888       free_map(exports);
889       free_http_response(rsp);
890       return cgi_error_memory();
891    }
892
893    /*
894     * Export the force conditional block killer if
895     *
896     * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
897     * - Privoxy is configured to enforce blocks, or
898     * - it's a CONNECT request and enforcing wouldn't work anyway.
899     */
900 #ifdef FEATURE_FORCE_LOAD
901    if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
902     || (0 == strcmpic(csp->http->gpc, "connect")))
903    {
904       err = map_block_killer(exports, "force-support");
905    }
906    else
907    {
908       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
909    }
910 #else /* ifndef FEATURE_FORCE_LOAD */
911    err = map_block_killer(exports, "force-support");
912 #endif /* ndef FEATURE_FORCE_LOAD */
913
914    if (err)
915    {
916       free_map(exports);
917       free_http_response(rsp);
918       return cgi_error_memory();
919    }
920
921    /*
922     * Build the response
923     */
924    err = template_fill_for_cgi(csp, "untrusted", exports, rsp);
925    if (err)
926    {
927       free_http_response(rsp);
928       return cgi_error_memory();
929    }
930    rsp->crunch_reason = UNTRUSTED;
931
932    return finish_http_response(csp, rsp);
933 }
934 #endif /* def FEATURE_TRUST */
935
936
937 /*********************************************************************
938  *
939  * Function    :  compile_dynamic_pcrs_job_list
940  *
941  * Description :  Compiles a dynamic pcrs job list (one with variables
942  *                resolved at request time)
943  *
944  * Parameters  :
945  *          1  :  csp = Current client state (buffers, headers, etc...)
946  *          2  :  b = The filter list to compile
947  *
948  * Returns     :  NULL in case of errors, otherwise the
949  *                pcrs job list.
950  *
951  *********************************************************************/
952 pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
953 {
954    struct list_entry *pattern;
955    pcrs_job *job_list = NULL;
956    pcrs_job *dummy = NULL;
957    pcrs_job *lastjob = NULL;
958    int error = 0;
959
960    const struct pcrs_variable variables[] =
961    {
962       {"url",    csp->http->url,   1},
963       {"path",   csp->http->path,  1},
964       {"host",   csp->http->host,  1},
965       {"origin", csp->ip_addr_str, 1},
966       {NULL,     NULL,             1}
967    };
968
969    for (pattern = b->patterns->first; pattern != NULL; pattern = pattern->next)
970    {
971       assert(pattern->str != NULL);
972
973       dummy = pcrs_compile_dynamic_command(pattern->str, variables, &error);
974       if (NULL == dummy)
975       {
976          log_error(LOG_LEVEL_ERROR,
977             "Compiling dynamic pcrs job '%s' for '%s' failed with error code %d: %s",
978             pattern->str, b->name, error, pcrs_strerror(error));
979          continue;
980       }
981       else
982       {
983          if (error == PCRS_WARN_TRUNCATION)
984          {
985             log_error(LOG_LEVEL_ERROR,
986                "At least one of the variables in \'%s\' had to "
987                "be truncated before compilation", pattern->str);
988          }
989          if (job_list == NULL)
990          {
991             job_list = dummy;
992          }
993          else
994          {
995             lastjob->next = dummy;
996          }
997          lastjob = dummy;
998       }
999    }
1000
1001    return job_list;
1002 }
1003
1004
1005 /*********************************************************************
1006  *
1007  * Function    :  rewrite_url
1008  *
1009  * Description :  Rewrites a URL with a single pcrs command
1010  *                and returns the result if it differs from the
1011  *                original and isn't obviously invalid.
1012  *
1013  * Parameters  :
1014  *          1  :  old_url = URL to rewrite.
1015  *          2  :  pcrs_command = pcrs command formatted as string (s@foo@bar@)
1016  *
1017  *
1018  * Returns     :  NULL if the pcrs_command didn't change the url, or
1019  *                the result of the modification.
1020  *
1021  *********************************************************************/
1022 char *rewrite_url(char *old_url, const char *pcrs_command)
1023 {
1024    char *new_url = NULL;
1025    int hits;
1026
1027    assert(old_url);
1028    assert(pcrs_command);
1029
1030    new_url = pcrs_execute_single_command(old_url, pcrs_command, &hits);
1031
1032    if (hits == 0)
1033    {
1034       log_error(LOG_LEVEL_REDIRECTS,
1035          "pcrs command \"%s\" didn't change \"%s\".",
1036          pcrs_command, old_url);
1037       freez(new_url);
1038    }
1039    else if (hits < 0)
1040    {
1041       log_error(LOG_LEVEL_REDIRECTS,
1042          "executing pcrs command \"%s\" to rewrite %s failed: %s",
1043          pcrs_command, old_url, pcrs_strerror(hits));
1044       freez(new_url);
1045    }
1046    else if (strncmpic(new_url, "http://", 7) && strncmpic(new_url, "https://", 8))
1047    {
1048       log_error(LOG_LEVEL_ERROR,
1049          "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s), "
1050          "but the result doesn't look like a valid URL and will be ignored.",
1051          pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
1052       freez(new_url);
1053    }
1054    else
1055    {
1056       log_error(LOG_LEVEL_REDIRECTS,
1057          "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s).",
1058          pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
1059    }
1060
1061    return new_url;
1062
1063 }
1064
1065
1066 #ifdef FEATURE_FAST_REDIRECTS
1067 /*********************************************************************
1068  *
1069  * Function    :  get_last_url
1070  *
1071  * Description :  Search for the last URL inside a string.
1072  *                If the string already is a URL, it will
1073  *                be the first URL found.
1074  *
1075  * Parameters  :
1076  *          1  :  subject = the string to check
1077  *          2  :  redirect_mode = +fast-redirect{} mode
1078  *
1079  * Returns     :  NULL if no URL was found, or
1080  *                the last URL found.
1081  *
1082  *********************************************************************/
1083 char *get_last_url(char *subject, const char *redirect_mode)
1084 {
1085    char *new_url = NULL;
1086    char *tmp;
1087
1088    assert(subject);
1089    assert(redirect_mode);
1090
1091    subject = strdup(subject);
1092    if (subject == NULL)
1093    {
1094       log_error(LOG_LEVEL_ERROR, "Out of memory while searching for redirects.");
1095       return NULL;
1096    }
1097
1098    if (0 == strcmpic(redirect_mode, "check-decoded-url") && strchr(subject, '%'))
1099    {  
1100       log_error(LOG_LEVEL_REDIRECTS,
1101          "Checking \"%s\" for encoded redirects.", subject);
1102
1103       /*
1104        * Check each parameter in the URL separately.
1105        * Sectionize the URL at "?" and "&",
1106        * go backwards through the segments, URL-decode them
1107        * and look for a URL in the decoded result.
1108        * Stop the search after the first match.
1109        */
1110       char *url_segment = NULL;
1111       /*
1112        * XXX: This estimate is guaranteed to be high enough as we
1113        *      let ssplit() ignore empty fields, but also a bit wasteful.
1114        */
1115       size_t max_segments = strlen(subject) / 2;
1116       char **url_segments = malloc(max_segments * sizeof(char *));
1117       int segments;
1118
1119       if (NULL == url_segments)
1120       {
1121          log_error(LOG_LEVEL_ERROR, "Out of memory while decoding URL: %s", new_url);
1122          freez(subject);
1123          return NULL;
1124       }
1125
1126       segments = ssplit(subject, "?&", url_segments, max_segments, 1, 1);
1127
1128       while (segments-- > 0)
1129       {
1130          char *dtoken = url_decode(url_segments[segments]);
1131          if (NULL == dtoken)
1132          {
1133             log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", url_segments[segments]);
1134             continue;
1135          }
1136          url_segment = strstr(dtoken, "http://");
1137          if (NULL == url_segment)
1138          {
1139             url_segment = strstr(dtoken, "https://");
1140          }
1141          if (NULL != url_segment)
1142          {
1143             url_segment = strdup(url_segment);
1144             freez(dtoken);
1145             if (url_segment == NULL)
1146             {
1147                log_error(LOG_LEVEL_ERROR,
1148                   "Out of memory while searching for redirects.");
1149                return NULL;
1150             }
1151             break;
1152          }
1153          freez(dtoken);
1154       }
1155       freez(subject);
1156       freez(url_segments);
1157
1158       if (url_segment == NULL)
1159       {
1160          return NULL;
1161       }
1162       subject = url_segment;
1163    }
1164    else
1165    {
1166       /* Look for a URL inside this one, without decoding anything. */
1167       log_error(LOG_LEVEL_REDIRECTS,
1168          "Checking \"%s\" for unencoded redirects.", subject);
1169    }
1170
1171    /*
1172     * Find the last URL encoded in the request
1173     */
1174    tmp = subject;
1175    while ((tmp = strstr(tmp, "http://")) != NULL)
1176    {
1177       new_url = tmp++;
1178    }
1179    tmp = (new_url != NULL) ? new_url : subject;
1180    while ((tmp = strstr(tmp, "https://")) != NULL)
1181    {
1182       new_url = tmp++;
1183    }
1184
1185    if ((new_url != NULL)
1186       && (  (new_url != subject)
1187          || (0 == strncmpic(subject, "http://", 7))
1188          || (0 == strncmpic(subject, "https://", 8))
1189          ))
1190    {
1191       /*
1192        * Return new URL if we found a redirect
1193        * or if the subject already was a URL.
1194        *
1195        * The second case makes sure that we can
1196        * chain get_last_url after another redirection check
1197        * (like rewrite_url) without losing earlier redirects.
1198        */
1199       new_url = strdup(new_url);
1200       freez(subject);
1201       return new_url;
1202    }
1203
1204    freez(subject);
1205    return NULL;
1206
1207 }
1208 #endif /* def FEATURE_FAST_REDIRECTS */
1209
1210
1211 /*********************************************************************
1212  *
1213  * Function    :  redirect_url
1214  *
1215  * Description :  Checks if Privoxy should answer the request with
1216  *                a HTTP redirect and generates the redirect if
1217  *                necessary.
1218  *
1219  * Parameters  :
1220  *          1  :  csp = Current client state (buffers, headers, etc...)
1221  *
1222  * Returns     :  NULL if the request can pass, HTTP redirect otherwise.
1223  *
1224  *********************************************************************/
1225 struct http_response *redirect_url(struct client_state *csp)
1226 {
1227    struct http_response *rsp;
1228 #ifdef FEATURE_FAST_REDIRECTS
1229    /*
1230     * XXX: Do we still need FEATURE_FAST_REDIRECTS
1231     * as compile-time option? The user can easily disable
1232     * it in his action file.
1233     */
1234    char * redirect_mode;
1235 #endif /* def FEATURE_FAST_REDIRECTS */
1236    char *old_url = NULL;
1237    char *new_url = NULL;
1238    char *redirection_string;
1239
1240    if ((csp->action->flags & ACTION_REDIRECT))
1241    {
1242       redirection_string = csp->action->string[ACTION_STRING_REDIRECT];
1243
1244       /*
1245        * If the redirection string begins with 's',
1246        * assume it's a pcrs command, otherwise treat it as
1247        * properly formatted URL and use it for the redirection
1248        * directly.
1249        *
1250        * According to RFC 2616 section 14.30 the URL
1251        * has to be absolute and if the user tries:
1252        * +redirect{shit/this/will/be/parsed/as/pcrs_command.html}
1253        * she would get undefined results anyway.
1254        *
1255        */
1256
1257       if (*redirection_string == 's')
1258       {
1259          old_url = csp->http->url;
1260          new_url = rewrite_url(old_url, redirection_string);
1261       }
1262       else
1263       {
1264          log_error(LOG_LEVEL_REDIRECTS,
1265             "No pcrs command recognized, assuming that \"%s\" is already properly formatted.",
1266             redirection_string);
1267          new_url = strdup(redirection_string);
1268       }
1269    }
1270
1271 #ifdef FEATURE_FAST_REDIRECTS
1272    if ((csp->action->flags & ACTION_FAST_REDIRECTS))
1273    {
1274       redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS];
1275
1276       /*
1277        * If it exists, use the previously rewritten URL as input
1278        * otherwise just use the old path.
1279        */
1280       old_url = (new_url != NULL) ? new_url : strdup(csp->http->path);
1281       new_url = get_last_url(old_url, redirect_mode);
1282       freez(old_url);
1283    }
1284
1285    /*
1286     * Disable redirect checkers, so that they
1287     * will be only run more than once if the user
1288     * also enables them through tags.
1289     *
1290     * From a performance point of view
1291     * it doesn't matter, but the duplicated
1292     * log messages are annoying.
1293     */
1294    csp->action->flags &= ~ACTION_FAST_REDIRECTS;
1295 #endif /* def FEATURE_FAST_REDIRECTS */
1296    csp->action->flags &= ~ACTION_REDIRECT;
1297
1298    /* Did any redirect action trigger? */
1299    if (new_url)
1300    {
1301       if (url_requires_percent_encoding(new_url))
1302       {
1303          char *encoded_url;
1304          log_error(LOG_LEVEL_REDIRECTS, "Percent-encoding redirect URL: %N",
1305             strlen(new_url), new_url);
1306          encoded_url = percent_encode_url(new_url);
1307          freez(new_url);
1308          if (encoded_url == NULL)
1309          {
1310             return cgi_error_memory();
1311          }
1312          new_url = encoded_url;
1313          assert(FALSE == url_requires_percent_encoding(new_url));
1314       }
1315
1316       if (0 == strcmpic(new_url, csp->http->url))
1317       {
1318          log_error(LOG_LEVEL_ERROR,
1319             "New URL \"%s\" and old URL \"%s\" are the same. Redirection loop prevented.",
1320             csp->http->url, new_url);
1321             freez(new_url);
1322       }
1323       else
1324       {
1325          log_error(LOG_LEVEL_REDIRECTS, "New URL is: %s", new_url);
1326
1327          if (NULL == (rsp = alloc_http_response()))
1328          {
1329             freez(new_url);
1330             return cgi_error_memory();
1331          }
1332
1333          if (enlist_unique_header(rsp->headers, "Location", new_url)
1334            || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))))
1335          {
1336             freez(new_url);
1337             free_http_response(rsp);
1338             return cgi_error_memory();
1339          }
1340          rsp->crunch_reason = REDIRECTED;
1341          freez(new_url);
1342
1343          return finish_http_response(csp, rsp);
1344       }
1345    }
1346
1347    /* Only reached if no redirect is required */
1348    return NULL;
1349
1350 }
1351
1352
1353 #ifdef FEATURE_IMAGE_BLOCKING
1354 /*********************************************************************
1355  *
1356  * Function    :  is_imageurl
1357  *
1358  * Description :  Given a URL, decide whether it is an image or not,
1359  *                using either the info from a previous +image action
1360  *                or, #ifdef FEATURE_IMAGE_DETECT_MSIE, and the browser
1361  *                is MSIE and not on a Mac, tell from the browser's accept
1362  *                header.
1363  *
1364  * Parameters  :
1365  *          1  :  csp = Current client state (buffers, headers, etc...)
1366  *
1367  * Returns     :  True (nonzero) if URL is an image, false (0)
1368  *                otherwise
1369  *
1370  *********************************************************************/
1371 int is_imageurl(const struct client_state *csp)
1372 {
1373 #ifdef FEATURE_IMAGE_DETECT_MSIE
1374    char *tmp;
1375
1376    tmp = get_header_value(csp->headers, "User-Agent:");
1377    if (tmp && strstr(tmp, "MSIE") && !strstr(tmp, "Mac_"))
1378    {
1379       tmp = get_header_value(csp->headers, "Accept:");
1380       if (tmp && strstr(tmp, "image/gif"))
1381       {
1382          /* Client will accept HTML.  If this seems counterintuitive,
1383           * blame Microsoft.
1384           */
1385          return(0);
1386       }
1387       else
1388       {
1389          return(1);
1390       }
1391    }
1392 #endif /* def FEATURE_IMAGE_DETECT_MSIE */
1393
1394    return ((csp->action->flags & ACTION_IMAGE) != 0);
1395
1396 }
1397 #endif /* def FEATURE_IMAGE_BLOCKING */
1398
1399
1400 #ifdef FEATURE_TRUST
1401 /*********************************************************************
1402  *
1403  * Function    :  is_untrusted_url
1404  *
1405  * Description :  Should we "distrust" this URL (and block it)?
1406  *
1407  *                Yes if it matches a line in the trustfile, or if the
1408  *                    referrer matches a line starting with "+" in the
1409  *                    trustfile.
1410  *                No  otherwise.
1411  *
1412  * Parameters  :
1413  *          1  :  csp = Current client state (buffers, headers, etc...)
1414  *
1415  * Returns     :  0 => trusted, 1 => untrusted
1416  *
1417  *********************************************************************/
1418 int is_untrusted_url(const struct client_state *csp)
1419 {
1420    struct file_list *fl;
1421    struct block_spec *b;
1422    struct url_spec **trusted_url;
1423    struct http_request rhttp[1];
1424    const char * referer;
1425    jb_err err;
1426
1427    /*
1428     * If we don't have a trustlist, we trust everybody
1429     */
1430    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
1431    {
1432       return 0;
1433    }
1434
1435    memset(rhttp, '\0', sizeof(*rhttp));
1436
1437    /*
1438     * Do we trust the request URL itself?
1439     */
1440    for (b = b->next; b ; b = b->next)
1441    {
1442       if (url_match(b->url, csp->http))
1443       {
1444          return b->reject;
1445       }
1446    }
1447
1448    if (NULL == (referer = get_header_value(csp->headers, "Referer:")))
1449    {
1450       /* no referrer was supplied */
1451       return 1;
1452    }
1453
1454
1455    /*
1456     * If not, do we maybe trust its referrer?
1457     */
1458    err = parse_http_url(referer, rhttp, REQUIRE_PROTOCOL);
1459    if (err)
1460    {
1461       return 1;
1462    }
1463
1464    for (trusted_url = csp->config->trust_list; *trusted_url != NULL; trusted_url++)
1465    {
1466       if (url_match(*trusted_url, rhttp))
1467       {
1468          /* if the URL's referrer is from a trusted referrer, then
1469           * add the target spec to the trustfile as an unblocked
1470           * domain and return 0 (which means it's OK).
1471           */
1472
1473          FILE *fp;
1474
1475          if (NULL != (fp = fopen(csp->config->trustfile, "a")))
1476          {
1477             char * path;
1478             char * path_end;
1479             char * new_entry = strdup("~");
1480
1481             string_append(&new_entry, csp->http->hostport);
1482
1483             path = csp->http->path;
1484             if ( (path[0] == '/')
1485               && (path[1] == '~')
1486               && ((path_end = strchr(path + 2, '/')) != NULL))
1487             {
1488                /* since this path points into a user's home space
1489                 * be sure to include this spec in the trustfile.
1490                 */
1491                long path_len = path_end - path; /* save offset */
1492                path = strdup(path); /* Copy string */
1493                if (path != NULL)
1494                {
1495                   path_end = path + path_len; /* regenerate ptr to new buffer */
1496                   *(path_end + 1) = '\0'; /* Truncate path after '/' */
1497                }
1498                string_join(&new_entry, path);
1499             }
1500
1501             /*
1502              * Give a reason for generating this entry.
1503              */
1504             string_append(&new_entry, " # Trusted referrer was: ");
1505             string_append(&new_entry, referer);
1506
1507             if (new_entry != NULL)
1508             {
1509                if (-1 == fprintf(fp, "%s\n", new_entry))
1510                {
1511                   log_error(LOG_LEVEL_ERROR, "Failed to append \'%s\' to trustfile \'%s\': %E",
1512                      new_entry, csp->config->trustfile);
1513                }
1514                freez(new_entry);
1515             }
1516             else
1517             {
1518                /* FIXME: No way to handle out-of memory, so mostly ignoring it */
1519                log_error(LOG_LEVEL_ERROR, "Out of memory adding pattern to trust file");
1520             }
1521
1522             fclose(fp);
1523          }
1524          else
1525          {
1526             log_error(LOG_LEVEL_ERROR, "Failed to append new entry for \'%s\' to trustfile \'%s\': %E",
1527                csp->http->hostport, csp->config->trustfile);
1528          }
1529          return 0;
1530       }
1531    }
1532
1533    return 1;
1534 }
1535 #endif /* def FEATURE_TRUST */
1536
1537
1538 /*********************************************************************
1539  *
1540  * Function    :  pcrs_filter_response
1541  *
1542  * Description :  Execute all text substitutions from all applying
1543  *                +filter actions on the text buffer that's been
1544  *                accumulated in csp->iob->buf.
1545  *
1546  * Parameters  :
1547  *          1  :  csp = Current client state (buffers, headers, etc...)
1548  *
1549  * Returns     :  a pointer to the (newly allocated) modified buffer.
1550  *                or NULL if there were no hits or something went wrong
1551  *
1552  *********************************************************************/
1553 static char *pcrs_filter_response(struct client_state *csp)
1554 {
1555    int hits = 0;
1556    int i;
1557    size_t size, prev_size;
1558
1559    char *old = NULL;
1560    char *new = NULL;
1561    pcrs_job *job;
1562
1563    struct file_list *fl;
1564    struct re_filterfile_spec *b;
1565    struct list_entry *filtername;
1566
1567    /*
1568     * Sanity first
1569     */
1570    if (csp->iob->cur >= csp->iob->eod)
1571    {
1572       return(NULL);
1573    }
1574
1575    if (filters_available(csp) == FALSE)
1576    {
1577       log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: "
1578          "content filtering enabled, but no content filters available.");
1579       return(NULL);
1580    }
1581
1582    size = (size_t)(csp->iob->eod - csp->iob->cur);
1583    old = csp->iob->cur;
1584
1585    for (i = 0; i < MAX_AF_FILES; i++)
1586    {
1587      fl = csp->rlist[i];
1588      if ((NULL == fl) || (NULL == fl->f))
1589      {
1590         /*
1591          * Either there are no filter files
1592          * left, or this filter file just
1593          * contains no valid filters.
1594          *
1595          * Continue to be sure we don't miss
1596          * valid filter files that are chained
1597          * after empty or invalid ones.
1598          */
1599         continue;
1600      }
1601    /*
1602     * For all applying +filter actions, look if a filter by that
1603     * name exists and if yes, execute it's pcrs_joblist on the
1604     * buffer.
1605     */
1606    for (b = fl->f; b; b = b->next)
1607    {
1608       if (b->type != FT_CONTENT_FILTER)
1609       {
1610          /* Skip header filters */
1611          continue;
1612       }
1613
1614       for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
1615            filtername ; filtername = filtername->next)
1616       {
1617          if (strcmp(b->name, filtername->str) == 0)
1618          {
1619             int current_hits = 0; /* Number of hits caused by this filter */
1620             int job_number   = 0; /* Which job we're currently executing  */
1621             int job_hits     = 0; /* How many hits the current job caused */
1622             pcrs_job *joblist = b->joblist;
1623
1624             if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
1625
1626             if (NULL == joblist)
1627             {
1628                log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
1629                continue;
1630             }
1631
1632             prev_size = size;
1633             /* Apply all jobs from the joblist */
1634             for (job = joblist; NULL != job; job = job->next)
1635             {
1636                job_number++;
1637                job_hits = pcrs_execute(job, old, size, &new, &size);
1638
1639                if (job_hits >= 0)
1640                {
1641                   /*
1642                    * That went well. Continue filtering
1643                    * and use the result of this job as
1644                    * input for the next one.
1645                    */
1646                   current_hits += job_hits;
1647                   if (old != csp->iob->cur)
1648                   {
1649                      freez(old);
1650                   }
1651                   old = new;
1652                }
1653                else
1654                {
1655                   /*
1656                    * This job caused an unexpected error. Inform the user
1657                    * and skip the rest of the jobs in this filter. We could
1658                    * continue with the next job, but usually the jobs
1659                    * depend on each other or are similar enough to
1660                    * fail for the same reason.
1661                    *
1662                    * At the moment our pcrs expects the error codes of pcre 3.4,
1663                    * but newer pcre versions can return additional error codes.
1664                    * As a result pcrs_strerror()'s error message might be
1665                    * "Unknown error ...", therefore we print the numerical value
1666                    * as well.
1667                    *
1668                    * XXX: Is this important enough for LOG_LEVEL_ERROR or
1669                    * should we use LOG_LEVEL_RE_FILTER instead?
1670                    */
1671                   log_error(LOG_LEVEL_ERROR, "Skipped filter \'%s\' after job number %u: %s (%d)",
1672                      b->name, job_number, pcrs_strerror(job_hits), job_hits);
1673                   break;
1674                }
1675             }
1676
1677             if (b->dynamic) pcrs_free_joblist(joblist);
1678
1679             log_error(LOG_LEVEL_RE_FILTER,
1680                "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
1681                csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
1682
1683             hits += current_hits;
1684          }
1685       }
1686    }
1687    }
1688
1689    /*
1690     * If there were no hits, destroy our copy and let
1691     * chat() use the original in csp->iob
1692     */
1693    if (!hits)
1694    {
1695       freez(new);
1696       return(NULL);
1697    }
1698
1699    csp->flags |= CSP_FLAG_MODIFIED;
1700    csp->content_length = size;
1701    IOB_RESET(csp);
1702
1703    return(new);
1704
1705 }
1706
1707
1708 /*********************************************************************
1709  *
1710  * Function    :  gif_deanimate_response
1711  *
1712  * Description :  Deanimate the GIF image that has been accumulated in
1713  *                csp->iob->buf, set csp->content_length to the modified
1714  *                size and raise the CSP_FLAG_MODIFIED flag.
1715  *
1716  * Parameters  :
1717  *          1  :  csp = Current client state (buffers, headers, etc...)
1718  *
1719  * Returns     :  a pointer to the (newly allocated) modified buffer.
1720  *                or NULL in case something went wrong.
1721  *
1722  *********************************************************************/
1723 static char *gif_deanimate_response(struct client_state *csp)
1724 {
1725    struct binbuffer *in, *out;
1726    char *p;
1727    size_t size;
1728
1729    size = (size_t)(csp->iob->eod - csp->iob->cur);
1730
1731    if (  (NULL == (in =  (struct binbuffer *)zalloc(sizeof *in )))
1732       || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) )
1733    {
1734       log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)");
1735       return NULL;
1736    }
1737
1738    in->buffer = csp->iob->cur;
1739    in->size = size;
1740
1741    if (gif_deanimate(in, out, strncmp("last", csp->action->string[ACTION_STRING_DEANIMATE], 4)))
1742    {
1743       log_error(LOG_LEVEL_DEANIMATE, "failed! (gif parsing)");
1744       freez(in);
1745       buf_free(out);
1746       return(NULL);
1747    }
1748    else
1749    {
1750       if ((int)size == out->offset)
1751       {
1752          log_error(LOG_LEVEL_DEANIMATE, "GIF not changed.");
1753       }
1754       else
1755       {
1756          log_error(LOG_LEVEL_DEANIMATE, "Success! GIF shrunk from %d bytes to %d.", size, out->offset);
1757       }
1758       csp->content_length = out->offset;
1759       csp->flags |= CSP_FLAG_MODIFIED;
1760       p = out->buffer;
1761       freez(in);
1762       freez(out);
1763       return(p);
1764    }
1765
1766 }
1767
1768
1769 /*********************************************************************
1770  *
1771  * Function    :  get_filter_function
1772  *
1773  * Description :  Decides which content filter function has
1774  *                to be applied (if any).
1775  *
1776  * Parameters  :
1777  *          1  :  csp = Current client state (buffers, headers, etc...)
1778  *
1779  * Returns     :  The content filter function to run, or
1780  *                NULL if no content filter is active
1781  *
1782  *********************************************************************/
1783 static filter_function_ptr get_filter_function(const struct client_state *csp)
1784 {
1785    filter_function_ptr filter_function = NULL;
1786
1787    /*
1788     * Choose the applying filter function based on
1789     * the content type and action settings.
1790     */
1791    if ((csp->content_type & CT_TEXT) &&
1792        (csp->rlist != NULL) &&
1793        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
1794    {
1795       filter_function = pcrs_filter_response;
1796    }
1797    else if ((csp->content_type & CT_GIF)  &&
1798             (csp->action->flags & ACTION_DEANIMATE))
1799    {
1800       filter_function = gif_deanimate_response;
1801    }
1802
1803    return filter_function;
1804 }
1805
1806
1807 /*********************************************************************
1808  *
1809  * Function    :  remove_chunked_transfer_coding
1810  *
1811  * Description :  In-situ remove the "chunked" transfer coding as defined
1812  *                in rfc2616 from a buffer.
1813  *
1814  * Parameters  :
1815  *          1  :  buffer = Pointer to the text buffer
1816  *          2  :  size =  In: Number of bytes to be processed,
1817  *                       Out: Number of bytes after de-chunking.
1818  *                       (undefined in case of errors)
1819  *
1820  * Returns     :  JB_ERR_OK for success,
1821  *                JB_ERR_PARSE otherwise
1822  *
1823  *********************************************************************/
1824 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
1825 {
1826    size_t newsize = 0;
1827    unsigned int chunksize = 0;
1828    char *from_p, *to_p;
1829
1830    assert(buffer);
1831    from_p = to_p = buffer;
1832
1833    if (sscanf(buffer, "%x", &chunksize) != 1)
1834    {
1835       log_error(LOG_LEVEL_ERROR, "Invalid first chunksize while stripping \"chunked\" transfer coding");
1836       return JB_ERR_PARSE;
1837    }
1838
1839    while (chunksize > 0U)
1840    {
1841       if (NULL == (from_p = strstr(from_p, "\r\n")))
1842       {
1843          log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding");
1844          return JB_ERR_PARSE;
1845       }
1846
1847       if (chunksize >= *size - newsize)
1848       {
1849          log_error(LOG_LEVEL_ERROR,
1850             "Chunk size %u exceeds buffered data left. "
1851             "Already digested %u of %u buffered bytes.",
1852             chunksize, (unsigned int)newsize, (unsigned int)*size);
1853          return JB_ERR_PARSE;
1854       }
1855       newsize += chunksize;
1856       from_p += 2;
1857
1858       memmove(to_p, from_p, (size_t) chunksize);
1859       to_p = buffer + newsize;
1860       from_p += chunksize + 2;
1861
1862       if (sscanf(from_p, "%x", &chunksize) != 1)
1863       {
1864          log_error(LOG_LEVEL_INFO, "Invalid \"chunked\" transfer encoding detected and ignored.");
1865          break;
1866       }
1867    }
1868
1869    /* XXX: Should get its own loglevel. */
1870    log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d", *size, newsize);
1871
1872    *size = newsize;
1873
1874    return JB_ERR_OK;
1875
1876 }
1877
1878
1879 /*********************************************************************
1880  *
1881  * Function    :  prepare_for_filtering
1882  *
1883  * Description :  If necessary, de-chunks and decompresses
1884  *                the content so it can get filterd.
1885  *
1886  * Parameters  :
1887  *          1  :  csp = Current client state (buffers, headers, etc...)
1888  *
1889  * Returns     :  JB_ERR_OK for success,
1890  *                JB_ERR_PARSE otherwise
1891  *
1892  *********************************************************************/
1893 static jb_err prepare_for_filtering(struct client_state *csp)
1894 {
1895    jb_err err = JB_ERR_OK;
1896
1897    /*
1898     * If the body has a "chunked" transfer-encoding,
1899     * get rid of it, adjusting size and iob->eod
1900     */
1901    if (csp->flags & CSP_FLAG_CHUNKED)
1902    {
1903       size_t size = (size_t)(csp->iob->eod - csp->iob->cur);
1904
1905       log_error(LOG_LEVEL_RE_FILTER, "Need to de-chunk first");
1906       err = remove_chunked_transfer_coding(csp->iob->cur, &size);
1907       if (JB_ERR_OK == err)
1908       {
1909          csp->iob->eod = csp->iob->cur + size;
1910          csp->flags |= CSP_FLAG_MODIFIED;
1911       }
1912       else
1913       {
1914          return JB_ERR_PARSE;
1915       }
1916    }
1917
1918 #ifdef FEATURE_ZLIB
1919    /*
1920     * If the body has a supported transfer-encoding,
1921     * decompress it, adjusting size and iob->eod.
1922     */
1923    if (csp->content_type & (CT_GZIP|CT_DEFLATE))
1924    {
1925       if (0 == csp->iob->eod - csp->iob->cur)
1926       {
1927          /* Nothing left after de-chunking. */
1928          return JB_ERR_OK;
1929       }
1930
1931       err = decompress_iob(csp);
1932
1933       if (JB_ERR_OK == err)
1934       {
1935          csp->flags |= CSP_FLAG_MODIFIED;
1936          csp->content_type &= ~CT_TABOO;
1937       }
1938       else
1939       {
1940          /*
1941           * Unset CT_GZIP and CT_DEFLATE to remember not
1942           * to modify the Content-Encoding header later.
1943           */
1944          csp->content_type &= ~CT_GZIP;
1945          csp->content_type &= ~CT_DEFLATE;
1946       }
1947    }
1948 #endif
1949
1950    return err;
1951 }
1952
1953
1954 /*********************************************************************
1955  *
1956  * Function    :  execute_content_filters
1957  *
1958  * Description :  Executes a given content filter.
1959  *
1960  * Parameters  :
1961  *          1  :  csp = Current client state (buffers, headers, etc...)
1962  *
1963  * Returns     :  Pointer to the modified buffer, or
1964  *                NULL if filtering failed or wasn't necessary.
1965  *
1966  *********************************************************************/
1967 char *execute_content_filters(struct client_state *csp)
1968 {
1969    filter_function_ptr content_filter;
1970
1971    assert(content_filters_enabled(csp->action));
1972
1973    if (0 == csp->iob->eod - csp->iob->cur)
1974    {
1975       /*
1976        * No content (probably status code 301, 302 ...),
1977        * no filtering necessary.
1978        */
1979       return NULL;
1980    }
1981
1982    if (JB_ERR_OK != prepare_for_filtering(csp))
1983    {
1984       /*
1985        * failed to de-chunk or decompress.
1986        */
1987       return NULL;
1988    }
1989
1990    if (0 == csp->iob->eod - csp->iob->cur)
1991    {
1992       /*
1993        * Clown alarm: chunked and/or compressed nothing delivered.
1994        */
1995       return NULL;
1996    }
1997
1998    content_filter = get_filter_function(csp);
1999
2000    return ((*content_filter)(csp));
2001 }
2002
2003
2004 /*********************************************************************
2005  *
2006  * Function    :  get_url_actions
2007  *
2008  * Description :  Gets the actions for this URL.
2009  *
2010  * Parameters  :
2011  *          1  :  csp = Current client state (buffers, headers, etc...)
2012  *          2  :  http = http_request request for blocked URLs
2013  *
2014  * Returns     :  N/A
2015  *
2016  *********************************************************************/
2017 void get_url_actions(struct client_state *csp, struct http_request *http)
2018 {
2019    struct file_list *fl;
2020    struct url_actions *b;
2021    int i;
2022
2023    init_current_action(csp->action);
2024
2025    for (i = 0; i < MAX_AF_FILES; i++)
2026    {
2027       if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
2028       {
2029          return;
2030       }
2031
2032       apply_url_actions(csp->action, http, b);
2033    }
2034
2035    return;
2036 }
2037
2038
2039 /*********************************************************************
2040  *
2041  * Function    :  apply_url_actions
2042  *
2043  * Description :  Applies a list of URL actions.
2044  *
2045  * Parameters  :
2046  *          1  :  action = Destination.
2047  *          2  :  http = Current URL
2048  *          3  :  b = list of URL actions to apply
2049  *
2050  * Returns     :  N/A
2051  *
2052  *********************************************************************/
2053 void apply_url_actions(struct current_action_spec *action,
2054                        struct http_request *http,
2055                        struct url_actions *b)
2056 {
2057    if (b == NULL)
2058    {
2059       /* Should never happen */
2060       return;
2061    }
2062
2063    for (b = b->next; NULL != b; b = b->next)
2064    {
2065       if (url_match(b->url, http))
2066       {
2067          merge_current_action(action, b->action);
2068       }
2069    }
2070 }
2071
2072
2073 /*********************************************************************
2074  *
2075  * Function    :  get_forward_override_settings
2076  *
2077  * Description :  Returns forward settings as specified with the
2078  *                forward-override{} action. forward-override accepts
2079  *                forward lines similar to the one used in the
2080  *                configuration file, but without the URL pattern.
2081  *
2082  *                For example:
2083  *
2084  *                   forward / .
2085  *
2086  *                in the configuration file can be replaced with
2087  *                the action section:
2088  *
2089  *                 {+forward-override{forward .}}
2090  *                 /
2091  *
2092  * Parameters  :
2093  *          1  :  csp = Current client state (buffers, headers, etc...)
2094  *
2095  * Returns     :  Pointer to forwarding structure in case of success.
2096  *                Invalid syntax is fatal.
2097  *
2098  *********************************************************************/
2099 const static struct forward_spec *get_forward_override_settings(struct client_state *csp)
2100 {
2101    const char *forward_override_line = csp->action->string[ACTION_STRING_FORWARD_OVERRIDE];
2102    char forward_settings[BUFFER_SIZE];
2103    char *http_parent = NULL;
2104    /* variable names were chosen for consistency reasons. */
2105    struct forward_spec *fwd = NULL;
2106    int vec_count;
2107    char *vec[3];
2108
2109    assert(csp->action->flags & ACTION_FORWARD_OVERRIDE);
2110    /* Should be enforced by load_one_actions_file() */
2111    assert(strlen(forward_override_line) < sizeof(forward_settings) - 1);
2112
2113    /* Create a copy ssplit can modify */
2114    strlcpy(forward_settings, forward_override_line, sizeof(forward_settings));
2115
2116    if (NULL != csp->fwd)
2117    {
2118       /*
2119        * XXX: Currently necessary to prevent memory
2120        * leaks when the show-url-info cgi page is visited.
2121        */
2122       unload_forward_spec(csp->fwd);
2123    }
2124
2125    /*
2126     * allocate a new forward node, valid only for
2127     * the lifetime of this request. Save its location
2128     * in csp as well, so sweep() can free it later on.
2129     */
2130    fwd = csp->fwd = zalloc(sizeof(*fwd));
2131    if (NULL == fwd)
2132    {
2133       log_error(LOG_LEVEL_FATAL,
2134          "can't allocate memory for forward-override{%s}", forward_override_line);
2135       /* Never get here - LOG_LEVEL_FATAL causes program exit */
2136       return NULL;
2137    }
2138
2139    vec_count = ssplit(forward_settings, " \t", vec, SZ(vec), 1, 1);
2140    if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
2141    {
2142       fwd->type = SOCKS_NONE;
2143
2144       /* Parse the parent HTTP proxy host:port */
2145       http_parent = vec[1];
2146
2147    }
2148    else if (vec_count == 3)
2149    {
2150       char *socks_proxy = NULL;
2151
2152       if  (!strcasecmp(vec[0], "forward-socks4"))
2153       {
2154          fwd->type = SOCKS_4;
2155          socks_proxy = vec[1];
2156       }
2157       else if (!strcasecmp(vec[0], "forward-socks4a"))
2158       {
2159          fwd->type = SOCKS_4A;
2160          socks_proxy = vec[1];
2161       }
2162       else if (!strcasecmp(vec[0], "forward-socks5"))
2163       {
2164          fwd->type = SOCKS_5;
2165          socks_proxy = vec[1];
2166       }
2167
2168       if (NULL != socks_proxy)
2169       {
2170          /* Parse the SOCKS proxy host[:port] */
2171          fwd->gateway_port = 1080;
2172          parse_forwarder_address(socks_proxy,
2173             &fwd->gateway_host, &fwd->gateway_port);
2174
2175          http_parent = vec[2];
2176       }
2177    }
2178
2179    if (NULL == http_parent)
2180    {
2181       log_error(LOG_LEVEL_FATAL,
2182          "Invalid forward-override syntax in: %s", forward_override_line);
2183       /* Never get here - LOG_LEVEL_FATAL causes program exit */
2184    }
2185
2186    /* Parse http forwarding settings */
2187    if (strcmp(http_parent, ".") != 0)
2188    {
2189       fwd->forward_port = 8000;
2190       parse_forwarder_address(http_parent,
2191          &fwd->forward_host, &fwd->forward_port);
2192    }
2193
2194    assert (NULL != fwd);
2195
2196    log_error(LOG_LEVEL_CONNECT,
2197       "Overriding forwarding settings based on \'%s\'", forward_override_line);
2198
2199    return fwd;
2200 }
2201
2202
2203 /*********************************************************************
2204  *
2205  * Function    :  forward_url
2206  *
2207  * Description :  Should we forward this to another proxy?
2208  *
2209  * Parameters  :
2210  *          1  :  csp = Current client state (buffers, headers, etc...)
2211  *          2  :  http = http_request request for current URL
2212  *
2213  * Returns     :  Pointer to forwarding information.
2214  *
2215  *********************************************************************/
2216 const struct forward_spec *forward_url(struct client_state *csp,
2217                                        const struct http_request *http)
2218 {
2219    static const struct forward_spec fwd_default[1] = { FORWARD_SPEC_INITIALIZER };
2220    struct forward_spec *fwd = csp->config->forward;
2221
2222    if (csp->action->flags & ACTION_FORWARD_OVERRIDE)
2223    {
2224       return get_forward_override_settings(csp);
2225    }
2226
2227    if (fwd == NULL)
2228    {
2229       return fwd_default;
2230    }
2231
2232    while (fwd != NULL)
2233    {
2234       if (url_match(fwd->url, http))
2235       {
2236          return fwd;
2237       }
2238       fwd = fwd->next;
2239    }
2240
2241    return fwd_default;
2242 }
2243
2244
2245 /*********************************************************************
2246  *
2247  * Function    :  direct_response
2248  *
2249  * Description :  Check if Max-Forwards == 0 for an OPTIONS or TRACE
2250  *                request and if so, return a HTTP 501 to the client.
2251  *
2252  *                FIXME: I have a stupid name and I should handle the
2253  *                requests properly. Still, what we do here is rfc-
2254  *                compliant, whereas ignoring or forwarding are not.
2255  *
2256  * Parameters  :
2257  *          1  :  csp = Current client state (buffers, headers, etc...)
2258  *
2259  * Returns     :  http_response if , NULL if nonmatch or handler fail
2260  *
2261  *********************************************************************/
2262 struct http_response *direct_response(struct client_state *csp)
2263 {
2264    struct http_response *rsp;
2265    struct list_entry *p;
2266
2267    if ((0 == strcmpic(csp->http->gpc, "trace"))
2268       || (0 == strcmpic(csp->http->gpc, "options")))
2269    {
2270       for (p = csp->headers->first; (p != NULL) ; p = p->next)
2271       {
2272          if (!strncmpic(p->str, "Max-Forwards:", 13))
2273          {
2274             unsigned int max_forwards;
2275
2276             /*
2277              * If it's a Max-Forwards value of zero,
2278              * we have to intercept the request.
2279              */
2280             if (1 == sscanf(p->str+12, ": %u", &max_forwards) && max_forwards == 0)
2281             {
2282                /*
2283                 * FIXME: We could handle at least TRACE here,
2284                 * but that would require a verbatim copy of
2285                 * the request which we don't have anymore
2286                 */
2287                 log_error(LOG_LEVEL_HEADER,
2288                   "Detected header \'%s\' in OPTIONS or TRACE request. Returning 501.",
2289                   p->str);
2290
2291                /* Get mem for response or fail*/
2292                if (NULL == (rsp = alloc_http_response()))
2293                {
2294                   return cgi_error_memory();
2295                }
2296
2297                if (NULL == (rsp->status = strdup("501 Not Implemented")))
2298                {
2299                   free_http_response(rsp);
2300                   return cgi_error_memory();
2301                }
2302
2303                rsp->is_static = 1;
2304                rsp->crunch_reason = UNSUPPORTED;
2305
2306                return(finish_http_response(csp, rsp));
2307             }
2308          }
2309       }
2310    }
2311    return NULL;
2312 }
2313
2314
2315 /*********************************************************************
2316  *
2317  * Function    :  content_requires_filtering
2318  *
2319  * Description :  Checks whether there are any content filters
2320  *                enabled for the current request and if they
2321  *                can actually be applied..
2322  *
2323  * Parameters  :
2324  *          1  :  csp = Current client state (buffers, headers, etc...)
2325  *
2326  * Returns     :  TRUE for yes, FALSE otherwise
2327  *
2328  *********************************************************************/
2329 int content_requires_filtering(struct client_state *csp)
2330 {
2331    if ((csp->content_type & CT_TABOO)
2332       && !(csp->action->flags & ACTION_FORCE_TEXT_MODE))
2333    {
2334       return FALSE;
2335    }
2336
2337    /*
2338     * Are we enabling text mode by force?
2339     */
2340    if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
2341    {
2342       /*
2343        * Do we really have to?
2344        */
2345       if (csp->content_type & CT_TEXT)
2346       {
2347          log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");
2348       }
2349       else
2350       {
2351          csp->content_type |= CT_TEXT;
2352          log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");
2353       }
2354    }
2355
2356    if (!(csp->content_type & CT_DECLARED))
2357    {
2358       /*
2359        * The server didn't bother to declare a MIME-Type.
2360        * Assume it's text that can be filtered.
2361        *
2362        * This also regulary happens with 304 responses,
2363        * therefore logging anything here would cause
2364        * too much noise.
2365        */
2366       csp->content_type |= CT_TEXT;
2367    }
2368
2369    /*
2370     * Choose the applying filter function based on
2371     * the content type and action settings.
2372     */
2373    if ((csp->content_type & CT_TEXT) &&
2374        (csp->rlist != NULL) &&
2375        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
2376    {
2377       return TRUE;
2378    }
2379    else if ((csp->content_type & CT_GIF)  &&
2380             (csp->action->flags & ACTION_DEANIMATE))
2381    {
2382       return TRUE;
2383    }
2384
2385    return FALSE;
2386
2387 }
2388
2389
2390 /*********************************************************************
2391  *
2392  * Function    :  content_filters_enabled
2393  *
2394  * Description :  Checks whether there are any content filters
2395  *                enabled for the current request.
2396  *
2397  * Parameters  :
2398  *          1  :  action = Action spec to check.
2399  *
2400  * Returns     :  TRUE for yes, FALSE otherwise
2401  *
2402  *********************************************************************/
2403 int content_filters_enabled(const struct current_action_spec *action)
2404 {
2405    return ((action->flags & ACTION_DEANIMATE) ||
2406       !list_is_empty(action->multi[ACTION_MULTI_FILTER]));
2407 }
2408
2409
2410 /*********************************************************************
2411  *
2412  * Function    :  filters_available
2413  *
2414  * Description :  Checks whether there are any filters available.
2415  *
2416  * Parameters  :
2417  *          1  :  csp = Current client state (buffers, headers, etc...)
2418  *
2419  * Returns     :  TRUE for yes, FALSE otherwise.
2420  *
2421  *********************************************************************/
2422 int filters_available(const struct client_state *csp)
2423 {
2424    int i;
2425    for (i = 0; i < MAX_AF_FILES; i++)
2426    {
2427       const struct file_list *fl = csp->rlist[i];
2428       if ((NULL != fl) && (NULL != fl->f))
2429       {
2430          return TRUE;
2431       }
2432    }
2433    return FALSE;
2434 }
2435
2436
2437 /*
2438   Local Variables:
2439   tab-width: 3
2440   end:
2441 */