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