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