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