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