Update #41: Note that txt2tags seems worth evaluating as well
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.200 2016/02/26 12:29:38 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 remove_chunked_transfer_coding(char *buffer, size_t *size);
86 static jb_err prepare_for_filtering(struct client_state *csp);
87 static void apply_url_actions(struct current_action_spec *action,
88                               struct http_request *http,
89 #ifdef FEATURE_CLIENT_TAGS
90                               const struct list *client_tags,
91 #endif
92                               struct url_actions *b);
93
94 #ifdef FEATURE_ACL
95 #ifdef HAVE_RFC2553
96 /*********************************************************************
97  *
98  * Function    :  sockaddr_storage_to_ip
99  *
100  * Description :  Access internal structure of sockaddr_storage
101  *
102  * Parameters  :
103  *          1  :  addr = socket address
104  *          2  :  ip   = IP address as array of octets in network order
105  *                       (it points into addr)
106  *          3  :  len  = length of IP address in octets
107  *          4  :  port = port number in network order;
108  *
109  * Returns     :  0 = no errror; -1 otherwise.
110  *
111  *********************************************************************/
112 static int sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
113                                   uint8_t **ip, unsigned int *len,
114                                   in_port_t **port)
115 {
116    if (NULL == addr)
117    {
118       return(-1);
119    }
120
121    switch (addr->ss_family)
122    {
123       case AF_INET:
124          if (NULL != len)
125          {
126             *len = 4;
127          }
128          if (NULL != ip)
129          {
130             *ip = (uint8_t *)
131                &(((struct sockaddr_in *)addr)->sin_addr.s_addr);
132          }
133          if (NULL != port)
134          {
135             *port = &((struct sockaddr_in *)addr)->sin_port;
136          }
137          break;
138
139       case AF_INET6:
140          if (NULL != len)
141          {
142             *len = 16;
143          }
144          if (NULL != ip)
145          {
146             *ip = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr;
147          }
148          if (NULL != port)
149          {
150             *port = &((struct sockaddr_in6 *)addr)->sin6_port;
151          }
152          break;
153
154       default:
155          /* Unsupported address family */
156          return(-1);
157    }
158
159    return(0);
160 }
161
162
163 /*********************************************************************
164  *
165  * Function    :  match_sockaddr
166  *
167  * Description :  Check whether address matches network (IP address and port)
168  *
169  * Parameters  :
170  *          1  :  network = socket address of subnework
171  *          2  :  netmask = network mask as socket address
172  *          3  :  address = checked socket address against given network
173  *
174  * Returns     :  0 = doesn't match; 1 = does match
175  *
176  *********************************************************************/
177 static int match_sockaddr(const struct sockaddr_storage *network,
178                           const struct sockaddr_storage *netmask,
179                           const struct sockaddr_storage *address)
180 {
181    uint8_t *network_addr, *netmask_addr, *address_addr;
182    unsigned int addr_len;
183    in_port_t *network_port, *netmask_port, *address_port;
184    int i;
185
186    if (network->ss_family != netmask->ss_family)
187    {
188       /* This should never happen */
189       assert(network->ss_family == netmask->ss_family);
190       log_error(LOG_LEVEL_FATAL, "Network and netmask differ in family.");
191    }
192
193    sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port);
194    sockaddr_storage_to_ip(netmask, &netmask_addr, NULL, &netmask_port);
195    sockaddr_storage_to_ip(address, &address_addr, NULL, &address_port);
196
197    /* Check for family */
198    if ((network->ss_family == AF_INET) && (address->ss_family == AF_INET6)
199       && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)address_addr))
200    {
201       /* Map AF_INET6 V4MAPPED address into AF_INET */
202       address_addr += 12;
203       addr_len = 4;
204    }
205    else if ((network->ss_family == AF_INET6) && (address->ss_family == AF_INET)
206       && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)network_addr))
207    {
208       /* Map AF_INET6 V4MAPPED network into AF_INET */
209       network_addr += 12;
210       netmask_addr += 12;
211       addr_len = 4;
212    }
213
214    /* XXX: Port check is signaled in netmask */
215    if (*netmask_port && *network_port != *address_port)
216    {
217       return 0;
218    }
219
220    /* TODO: Optimize by checking by words insted of octets */
221    for (i = 0; (i < addr_len) && netmask_addr[i]; i++)
222    {
223       if ((network_addr[i] & netmask_addr[i]) !=
224           (address_addr[i] & netmask_addr[i]))
225       {
226          return 0;
227       }
228    }
229
230    return 1;
231 }
232 #endif /* def HAVE_RFC2553 */
233
234
235 /*********************************************************************
236  *
237  * Function    :  block_acl
238  *
239  * Description :  Block this request?
240  *                Decide yes or no based on ACL file.
241  *
242  * Parameters  :
243  *          1  :  dst = The proxy or gateway address this is going to.
244  *                      Or NULL to check all possible targets.
245  *          2  :  csp = Current client state (buffers, headers, etc...)
246  *                      Also includes the client IP address.
247  *
248  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
249  *
250  *********************************************************************/
251 int block_acl(const struct access_control_addr *dst, const struct client_state *csp)
252 {
253    struct access_control_list *acl = csp->config->acl;
254
255    /* if not using an access control list, then permit the connection */
256    if (acl == NULL)
257    {
258       return(0);
259    }
260
261    /* search the list */
262    while (acl != NULL)
263    {
264       if (
265 #ifdef HAVE_RFC2553
266             match_sockaddr(&acl->src->addr, &acl->src->mask, &csp->tcp_addr)
267 #else
268             (csp->ip_addr_long & acl->src->mask) == acl->src->addr
269 #endif
270             )
271       {
272          if (dst == NULL)
273          {
274             /* Just want to check if they have any access */
275             if (acl->action == ACL_PERMIT)
276             {
277                return(0);
278             }
279             else
280             {
281                return(1);
282             }
283          }
284          else if (
285 #ifdef HAVE_RFC2553
286                /*
287                 * XXX: An undefined acl->dst is full of zeros and should be
288                 * considered a wildcard address. sockaddr_storage_to_ip()
289                 * fails on such destinations because of unknown sa_familly
290                 * (glibc only?). However this test is not portable.
291                 *
292                 * So, we signal the acl->dst is wildcard in wildcard_dst.
293                 */
294                acl->wildcard_dst ||
295                   match_sockaddr(&acl->dst->addr, &acl->dst->mask, &dst->addr)
296 #else
297                ((dst->addr & acl->dst->mask) == acl->dst->addr)
298            && ((dst->port == acl->dst->port) || (acl->dst->port == 0))
299 #endif
300            )
301          {
302             if (acl->action == ACL_PERMIT)
303             {
304                return(0);
305             }
306             else
307             {
308                return(1);
309             }
310          }
311       }
312       acl = acl->next;
313    }
314
315    return(1);
316
317 }
318
319
320 /*********************************************************************
321  *
322  * Function    :  acl_addr
323  *
324  * Description :  Called from `load_config' to parse an ACL address.
325  *
326  * Parameters  :
327  *          1  :  aspec = String specifying ACL address.
328  *          2  :  aca = struct access_control_addr to fill in.
329  *
330  * Returns     :  0 => Ok, everything else is an error.
331  *
332  *********************************************************************/
333 int acl_addr(const char *aspec, struct access_control_addr *aca)
334 {
335    int i, masklength;
336 #ifdef HAVE_RFC2553
337    struct addrinfo hints, *result;
338    uint8_t *mask_data;
339    in_port_t *mask_port;
340    unsigned int addr_len;
341 #else
342    long port;
343 #endif /* def HAVE_RFC2553 */
344    char *p;
345    char *acl_spec = NULL;
346
347 #ifdef HAVE_RFC2553
348    /* XXX: Depend on ai_family */
349    masklength = 128;
350 #else
351    masklength = 32;
352    port       =  0;
353 #endif
354
355    /*
356     * Use a temporary acl spec copy so we can log
357     * the unmodified original in case of parse errors.
358     */
359    acl_spec = strdup_or_die(aspec);
360
361    if ((p = strchr(acl_spec, '/')) != NULL)
362    {
363       *p++ = '\0';
364       if (privoxy_isdigit(*p) == 0)
365       {
366          freez(acl_spec);
367          return(-1);
368       }
369       masklength = atoi(p);
370    }
371
372    if ((masklength < 0) ||
373 #ifdef HAVE_RFC2553
374          (masklength > 128)
375 #else
376          (masklength > 32)
377 #endif
378          )
379    {
380       freez(acl_spec);
381       return(-1);
382    }
383
384    if ((*acl_spec == '[') && (NULL != (p = strchr(acl_spec, ']'))))
385    {
386       *p = '\0';
387       memmove(acl_spec, acl_spec + 1, (size_t)(p - acl_spec));
388
389       if (*++p != ':')
390       {
391          p = NULL;
392       }
393    }
394    else
395    {
396       p = strchr(acl_spec, ':');
397    }
398    if (p != NULL)
399    {
400       assert(*p == ':');
401       *p = '\0';
402       p++;
403    }
404
405 #ifdef HAVE_RFC2553
406    memset(&hints, 0, sizeof(struct addrinfo));
407    hints.ai_family = AF_UNSPEC;
408    hints.ai_socktype = SOCK_STREAM;
409
410    i = getaddrinfo(acl_spec, p, &hints, &result);
411
412    if (i != 0)
413    {
414       log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s",
415          acl_spec, p, gai_strerror(i));
416       freez(acl_spec);
417       return(-1);
418    }
419    freez(acl_spec);
420
421    /* TODO: Allow multihomed hostnames */
422    memcpy(&(aca->addr), result->ai_addr, result->ai_addrlen);
423    freeaddrinfo(result);
424 #else
425    if (p != NULL)
426    {
427       char *endptr;
428
429       port = strtol(p, &endptr, 10);
430
431       if (port <= 0 || port > 65535 || *endptr != '\0')
432       {
433          freez(acl_spec);
434          return(-1);
435       }
436    }
437
438    aca->port = (unsigned long)port;
439
440    aca->addr = ntohl(resolve_hostname_to_ip(acl_spec));
441    freez(acl_spec);
442
443    if (aca->addr == INADDR_NONE)
444    {
445       /* XXX: This will be logged as parse error. */
446       return(-1);
447    }
448 #endif /* def HAVE_RFC2553 */
449
450    /* build the netmask */
451 #ifdef HAVE_RFC2553
452    /* Clip masklength according to current family. */
453    if ((aca->addr.ss_family == AF_INET) && (masklength > 32))
454    {
455       masklength = 32;
456    }
457
458    aca->mask.ss_family = aca->addr.ss_family;
459    if (sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port))
460    {
461       return(-1);
462    }
463
464    if (p)
465    {
466       /* ACL contains a port number, check ports in the future. */
467       *mask_port = 1;
468    }
469
470    /*
471     * XXX: This could be optimized to operate on whole words instead
472     * of octets (128-bit CPU could do it in one iteration).
473     */
474    /*
475     * Octets after prefix can be omitted because of
476     * previous initialization to zeros.
477     */
478    for (i = 0; (i < addr_len) && masklength; i++)
479    {
480       if (masklength >= 8)
481       {
482          mask_data[i] = 0xFF;
483          masklength -= 8;
484       }
485       else
486       {
487          /*
488           * XXX: This assumes MSB of octet is on the left side.
489           * This should be true for all architectures or solved
490           * by the link layer.
491           */
492          mask_data[i] = (uint8_t)~((1 << (8 - masklength)) - 1);
493          masklength = 0;
494       }
495    }
496
497 #else
498    aca->mask = 0;
499    for (i=1; i <= masklength ; i++)
500    {
501       aca->mask |= (1U << (32 - i));
502    }
503
504    /* now mask off the host portion of the ip address
505     * (i.e. save on the network portion of the address).
506     */
507    aca->addr = aca->addr & aca->mask;
508 #endif /* def HAVE_RFC2553 */
509
510    return(0);
511
512 }
513 #endif /* def FEATURE_ACL */
514
515
516 /*********************************************************************
517  *
518  * Function    :  connect_port_is_forbidden
519  *
520  * Description :  Check to see if CONNECT requests to the destination
521  *                port of this request are forbidden. The check is
522  *                independend of the actual request method.
523  *
524  * Parameters  :
525  *          1  :  csp = Current client state (buffers, headers, etc...)
526  *
527  * Returns     :  True if yes, false otherwise.
528  *
529  *********************************************************************/
530 int connect_port_is_forbidden(const struct client_state *csp)
531 {
532    return ((csp->action->flags & ACTION_LIMIT_CONNECT) &&
533      !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT],
534         csp->http->port));
535 }
536
537
538 /*********************************************************************
539  *
540  * Function    :  block_url
541  *
542  * Description :  Called from `chat'.  Check to see if we need to block this.
543  *
544  * Parameters  :
545  *          1  :  csp = Current client state (buffers, headers, etc...)
546  *
547  * Returns     :  NULL => unblocked, else HTTP block response
548  *
549  *********************************************************************/
550 struct http_response *block_url(struct client_state *csp)
551 {
552    struct http_response *rsp;
553    const char *new_content_type = NULL;
554
555    /*
556     * If it's not blocked, don't block it ;-)
557     */
558    if ((csp->action->flags & ACTION_BLOCK) == 0)
559    {
560       return NULL;
561    }
562    if (csp->action->flags & ACTION_REDIRECT)
563    {
564       log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");
565    }
566    /*
567     * Else, prepare a response
568     */
569    if (NULL == (rsp = alloc_http_response()))
570    {
571       return cgi_error_memory();
572    }
573
574    /*
575     * If it's an image-url, send back an image or redirect
576     * as specified by the relevant +image action
577     */
578 #ifdef FEATURE_IMAGE_BLOCKING
579    if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
580         && is_imageurl(csp))
581    {
582       char *p;
583       /* determine HOW images should be blocked */
584       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
585
586       if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
587       {
588          log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image.");
589       }
590
591       /* and handle accordingly: */
592       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
593       {
594          rsp->status = strdup_or_die("403 Request blocked by Privoxy");
595          rsp->body = bindup(image_pattern_data, image_pattern_length);
596          if (rsp->body == NULL)
597          {
598             free_http_response(rsp);
599             return cgi_error_memory();
600          }
601          rsp->content_length = image_pattern_length;
602
603          if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
604          {
605             free_http_response(rsp);
606             return cgi_error_memory();
607          }
608       }
609       else if (0 == strcmpic(p, "blank"))
610       {
611          rsp->status = strdup_or_die("403 Request blocked by Privoxy");
612          rsp->body = bindup(image_blank_data, image_blank_length);
613          if (rsp->body == NULL)
614          {
615             free_http_response(rsp);
616             return cgi_error_memory();
617          }
618          rsp->content_length = image_blank_length;
619
620          if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
621          {
622             free_http_response(rsp);
623             return cgi_error_memory();
624          }
625       }
626       else
627       {
628          rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
629
630          if (enlist_unique_header(rsp->headers, "Location", p))
631          {
632             free_http_response(rsp);
633             return cgi_error_memory();
634          }
635       }
636
637    }
638    else
639 #endif /* def FEATURE_IMAGE_BLOCKING */
640    if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
641    {
642      /*
643       *  Send empty document.
644       */
645       new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
646
647       freez(rsp->body);
648       rsp->body = strdup_or_die(" ");
649       rsp->content_length = 1;
650
651       if (csp->config->feature_flags & RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK)
652       {
653          /*
654           * Workaround for firefox bug 492459
655           *   https://bugzilla.mozilla.org/show_bug.cgi?id=492459
656           * Return a 200 OK status for pages blocked with +handle-as-empty-document
657           * if the "handle-as-empty-doc-returns-ok" runtime config option is set.
658           */
659          rsp->status = strdup_or_die("200 Request blocked by Privoxy");
660       }
661       else
662       {
663          rsp->status = strdup_or_die("403 Request blocked by Privoxy");
664       }
665
666       if (new_content_type != 0)
667       {
668          log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type);
669          if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type))
670          {
671             free_http_response(rsp);
672             return cgi_error_memory();
673          }
674       }
675    }
676    else
677
678    /*
679     * Else, generate an HTML "blocked" message:
680     */
681    {
682       jb_err err;
683       struct map * exports;
684
685       rsp->status = strdup_or_die("403 Request blocked by Privoxy");
686
687       exports = default_exports(csp, NULL);
688       if (exports == NULL)
689       {
690          free_http_response(rsp);
691          return cgi_error_memory();
692       }
693
694 #ifdef FEATURE_FORCE_LOAD
695       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
696       /*
697        * Export the force conditional block killer if
698        *
699        * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
700        * - Privoxy is configured to enforce blocks, or
701        * - it's a CONNECT request and enforcing wouldn't work anyway.
702        */
703       if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
704        || (0 == strcmpic(csp->http->gpc, "connect")))
705 #endif /* ndef FEATURE_FORCE_LOAD */
706       {
707          err = map_block_killer(exports, "force-support");
708       }
709
710       if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
711       if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
712       if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
713       if (!err) err = map(exports, "path-ue", 1, url_encode(csp->http->path), 0);
714       if (!err)
715       {
716          const char *block_reason;
717          if (csp->action->string[ACTION_STRING_BLOCK] != NULL)
718          {
719             block_reason = csp->action->string[ACTION_STRING_BLOCK];
720          }
721          else
722          {
723             assert(connect_port_is_forbidden(csp));
724             block_reason = "Forbidden CONNECT port.";
725          }
726          err = map(exports, "block-reason", 1, html_encode(block_reason), 0);
727       }
728       if (err)
729       {
730          free_map(exports);
731          free_http_response(rsp);
732          return cgi_error_memory();
733       }
734
735       err = template_fill_for_cgi(csp, "blocked", exports, rsp);
736       if (err)
737       {
738          free_http_response(rsp);
739          return cgi_error_memory();
740       }
741    }
742    rsp->crunch_reason = BLOCKED;
743
744    return finish_http_response(csp, rsp);
745
746 }
747
748
749 #ifdef FEATURE_TRUST
750 /*********************************************************************
751  *
752  * Function    :  trust_url FIXME: I should be called distrust_url
753  *
754  * Description :  Calls is_untrusted_url to determine if the URL is trusted
755  *                and if not, returns a HTTP 403 response with a reject message.
756  *
757  * Parameters  :
758  *          1  :  csp = Current client state (buffers, headers, etc...)
759  *
760  * Returns     :  NULL => trusted, else http_response.
761  *
762  *********************************************************************/
763 struct http_response *trust_url(struct client_state *csp)
764 {
765    struct http_response *rsp;
766    struct map * exports;
767    char buf[BUFFER_SIZE];
768    char *p;
769    struct pattern_spec **tl;
770    struct pattern_spec *t;
771    jb_err err;
772
773    /*
774     * Don't bother to work on trusted URLs
775     */
776    if (!is_untrusted_url(csp))
777    {
778       return NULL;
779    }
780
781    /*
782     * Else, prepare a response:
783     */
784    if (NULL == (rsp = alloc_http_response()))
785    {
786       return cgi_error_memory();
787    }
788
789    rsp->status = strdup_or_die("403 Request blocked by Privoxy");
790    exports = default_exports(csp, NULL);
791    if (exports == NULL)
792    {
793       free_http_response(rsp);
794       return cgi_error_memory();
795    }
796
797    /*
798     * Export the protocol, host, port, and referrer information
799     */
800    err = map(exports, "hostport", 1, csp->http->hostport, 1);
801    if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
802    if (!err) err = map(exports, "path", 1, csp->http->path, 1);
803
804    if (NULL != (p = get_header_value(csp->headers, "Referer:")))
805    {
806       if (!err) err = map(exports, "referrer", 1, html_encode(p), 0);
807    }
808    else
809    {
810       if (!err) err = map(exports, "referrer", 1, "none set", 1);
811    }
812
813    if (err)
814    {
815       free_map(exports);
816       free_http_response(rsp);
817       return cgi_error_memory();
818    }
819
820    /*
821     * Export the trust list
822     */
823    p = strdup_or_die("");
824    for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
825    {
826       snprintf(buf, sizeof(buf), "<li>%s</li>\n", t->spec);
827       string_append(&p, buf);
828    }
829    err = map(exports, "trusted-referrers", 1, p, 0);
830
831    if (err)
832    {
833       free_map(exports);
834       free_http_response(rsp);
835       return cgi_error_memory();
836    }
837
838    /*
839     * Export the trust info, if available
840     */
841    if (csp->config->trust_info->first)
842    {
843       struct list_entry *l;
844
845       p = strdup_or_die("");
846       for (l = csp->config->trust_info->first; l ; l = l->next)
847       {
848          snprintf(buf, sizeof(buf), "<li> <a href=\"%s\">%s</a><br>\n", l->str, l->str);
849          string_append(&p, buf);
850       }
851       err = map(exports, "trust-info", 1, p, 0);
852    }
853    else
854    {
855       err = map_block_killer(exports, "have-trust-info");
856    }
857
858    if (err)
859    {
860       free_map(exports);
861       free_http_response(rsp);
862       return cgi_error_memory();
863    }
864
865    /*
866     * Export the force conditional block killer if
867     *
868     * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
869     * - Privoxy is configured to enforce blocks, or
870     * - it's a CONNECT request and enforcing wouldn't work anyway.
871     */
872 #ifdef FEATURE_FORCE_LOAD
873    if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
874     || (0 == strcmpic(csp->http->gpc, "connect")))
875    {
876       err = map_block_killer(exports, "force-support");
877    }
878    else
879    {
880       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
881    }
882 #else /* ifndef FEATURE_FORCE_LOAD */
883    err = map_block_killer(exports, "force-support");
884 #endif /* ndef FEATURE_FORCE_LOAD */
885
886    if (err)
887    {
888       free_map(exports);
889       free_http_response(rsp);
890       return cgi_error_memory();
891    }
892
893    /*
894     * Build the response
895     */
896    err = template_fill_for_cgi(csp, "untrusted", exports, rsp);
897    if (err)
898    {
899       free_http_response(rsp);
900       return cgi_error_memory();
901    }
902    rsp->crunch_reason = UNTRUSTED;
903
904    return finish_http_response(csp, rsp);
905 }
906 #endif /* def FEATURE_TRUST */
907
908
909 /*********************************************************************
910  *
911  * Function    :  compile_dynamic_pcrs_job_list
912  *
913  * Description :  Compiles a dynamic pcrs job list (one with variables
914  *                resolved at request time)
915  *
916  * Parameters  :
917  *          1  :  csp = Current client state (buffers, headers, etc...)
918  *          2  :  b = The filter list to compile
919  *
920  * Returns     :  NULL in case of errors, otherwise the
921  *                pcrs job list.
922  *
923  *********************************************************************/
924 pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
925 {
926    struct list_entry *pattern;
927    pcrs_job *job_list = NULL;
928    pcrs_job *dummy = NULL;
929    pcrs_job *lastjob = NULL;
930    int error = 0;
931
932    const struct pcrs_variable variables[] =
933    {
934       {"url",    csp->http->url,   1},
935       {"path",   csp->http->path,  1},
936       {"host",   csp->http->host,  1},
937       {"origin", csp->ip_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    };
1775
1776    for (i = 0; i < SZ(env); i++)
1777    {
1778       if (setenv(env[i].name, env[i].value, 1))
1779       {
1780          log_error(LOG_LEVEL_ERROR, "Failed to set %s=%s: %E",
1781             env[i].name, env[i].value);
1782       }
1783    }
1784 }
1785
1786
1787 /*********************************************************************
1788  *
1789  * Function    :  execute_external_filter
1790  *
1791  * Description :  Pipe content into external filter and return the output
1792  *
1793  * Parameters  :
1794  *          1  :  csp = Current client state (buffers, headers, etc...)
1795  *          2  :  name = Name of the external filter to execute
1796  *          3  :  content = The original content to filter
1797  *          4  :  size = The size of the content buffer
1798  *
1799  * Returns     :  a pointer to the (newly allocated) modified buffer.
1800  *                or NULL if there were no hits or something went wrong
1801  *
1802  *********************************************************************/
1803 static char *execute_external_filter(const struct client_state *csp,
1804    const char *name, char *content, size_t *size)
1805 {
1806    char cmd[200];
1807    char file_name[FILENAME_MAX];
1808    FILE *fp;
1809    char *filter_output;
1810    int fd;
1811    int ret;
1812    size_t new_size;
1813    const char *external_filter;
1814
1815    if (csp->config->temporary_directory == NULL)
1816    {
1817       log_error(LOG_LEVEL_ERROR,
1818          "No temporary-directory configured. Can't execute filter: %s",
1819          name);
1820       return NULL;
1821    }
1822
1823    external_filter = get_external_filter(csp, name);
1824
1825    if (sizeof(file_name) < snprintf(file_name, sizeof(file_name),
1826          "%s/privoxy-XXXXXXXX", csp->config->temporary_directory))
1827    {
1828       log_error(LOG_LEVEL_ERROR, "temporary-directory path too long");
1829       return NULL;
1830    }
1831
1832    fd = mkstemp(file_name);
1833    if (fd == -1)
1834    {
1835       log_error(LOG_LEVEL_ERROR, "mkstemp() failed to create %s: %E", file_name);
1836       return NULL;
1837    }
1838
1839    fp = fdopen(fd, "w");
1840    if (fp == NULL)
1841    {
1842       log_error(LOG_LEVEL_ERROR, "fdopen() failed: %E");
1843       unlink(file_name);
1844       return NULL;
1845    }
1846
1847    /*
1848     * The size may be zero if a previous filter discarded everything.
1849     *
1850     * This isn't necessary unintentional, so we just don't try
1851     * to fwrite() nothing and let the user deal with the rest.
1852     */
1853    if ((*size != 0) && fwrite(content, *size, 1, fp) != 1)
1854    {
1855       log_error(LOG_LEVEL_ERROR, "fwrite(..., %d, 1, ..) failed: %E", *size);
1856       unlink(file_name);
1857       fclose(fp);
1858       return NULL;
1859    }
1860    fclose(fp);
1861
1862    if (sizeof(cmd) < snprintf(cmd, sizeof(cmd), "%s < %s", external_filter, file_name))
1863    {
1864       log_error(LOG_LEVEL_ERROR,
1865          "temporary-directory or external filter path too long");
1866       unlink(file_name);
1867       return NULL;
1868    }
1869
1870    log_error(LOG_LEVEL_RE_FILTER, "Executing '%s': %s", name, cmd);
1871
1872    /*
1873     * The locking is necessary to prevent other threads
1874     * from overwriting the environment variables before
1875     * the popen fork. Afterwards this no longer matters.
1876     */
1877    privoxy_mutex_lock(&external_filter_mutex);
1878    set_privoxy_variables(csp);
1879    fp = popen(cmd, "r");
1880    privoxy_mutex_unlock(&external_filter_mutex);
1881    if (fp == NULL)
1882    {
1883       log_error(LOG_LEVEL_ERROR, "popen(\"%s\", \"r\") failed: %E", cmd);
1884       unlink(file_name);
1885       return NULL;
1886    }
1887
1888    /* Allocate at least one byte */
1889    filter_output = malloc_or_die(*size + 1);
1890
1891    new_size = 0;
1892    while (!feof(fp) && !ferror(fp))
1893    {
1894       size_t len;
1895       /* Could be bigger ... */
1896       enum { READ_LENGTH = 2048 };
1897
1898       if (new_size + READ_LENGTH >= *size)
1899       {
1900          char *p;
1901
1902          /* Could be considered wasteful if the content is 'large'. */
1903          *size += (*size >= READ_LENGTH) ? *size : READ_LENGTH;
1904
1905          p = realloc(filter_output, *size);
1906          if (p == NULL)
1907          {
1908             log_error(LOG_LEVEL_ERROR, "Out of memory while reading "
1909                "external filter output. Using what we got so far.");
1910             break;
1911          }
1912          filter_output = p;
1913       }
1914       assert(new_size + READ_LENGTH < *size);
1915       len = fread(&filter_output[new_size], 1, READ_LENGTH, fp);
1916       if (len > 0)
1917       {
1918          new_size += len;
1919       }
1920    }
1921
1922    ret = pclose(fp);
1923    if (ret == -1)
1924    {
1925       log_error(LOG_LEVEL_ERROR, "Executing %s failed: %E", cmd);
1926    }
1927    else
1928    {
1929       log_error(LOG_LEVEL_RE_FILTER,
1930          "Executing '%s' resulted in return value %d. "
1931          "Read %d of up to %d bytes.", name, (ret >> 8), new_size, *size);
1932    }
1933
1934    unlink(file_name);
1935    *size = new_size;
1936
1937    return filter_output;
1938
1939 }
1940 #endif /* def FEATURE_EXTERNAL_FILTERS */
1941
1942
1943 /*********************************************************************
1944  *
1945  * Function    :  gif_deanimate_response
1946  *
1947  * Description :  Deanimate the GIF image that has been accumulated in
1948  *                csp->iob->buf, set csp->content_length to the modified
1949  *                size and raise the CSP_FLAG_MODIFIED flag.
1950  *
1951  * Parameters  :
1952  *          1  :  csp = Current client state (buffers, headers, etc...)
1953  *
1954  * Returns     :  a pointer to the (newly allocated) modified buffer.
1955  *                or NULL in case something went wrong.
1956  *
1957  *********************************************************************/
1958 static char *gif_deanimate_response(struct client_state *csp)
1959 {
1960    struct binbuffer *in, *out;
1961    char *p;
1962    size_t size;
1963
1964    size = (size_t)(csp->iob->eod - csp->iob->cur);
1965
1966    in =  zalloc_or_die(sizeof(*in));
1967    out = zalloc_or_die(sizeof(*out));
1968
1969    in->buffer = csp->iob->cur;
1970    in->size = size;
1971
1972    if (gif_deanimate(in, out, strncmp("last", csp->action->string[ACTION_STRING_DEANIMATE], 4)))
1973    {
1974       log_error(LOG_LEVEL_DEANIMATE, "failed! (gif parsing)");
1975       freez(in);
1976       buf_free(out);
1977       return(NULL);
1978    }
1979    else
1980    {
1981       if ((int)size == out->offset)
1982       {
1983          log_error(LOG_LEVEL_DEANIMATE, "GIF not changed.");
1984       }
1985       else
1986       {
1987          log_error(LOG_LEVEL_DEANIMATE, "Success! GIF shrunk from %d bytes to %d.", size, out->offset);
1988       }
1989       csp->content_length = out->offset;
1990       csp->flags |= CSP_FLAG_MODIFIED;
1991       p = out->buffer;
1992       freez(in);
1993       freez(out);
1994       return(p);
1995    }
1996
1997 }
1998
1999
2000 /*********************************************************************
2001  *
2002  * Function    :  get_filter_function
2003  *
2004  * Description :  Decides which content filter function has
2005  *                to be applied (if any). Only considers functions
2006  *                for internal filters which are mutually-exclusive.
2007  *
2008  * Parameters  :
2009  *          1  :  csp = Current client state (buffers, headers, etc...)
2010  *
2011  * Returns     :  The content filter function to run, or
2012  *                NULL if no content filter is active
2013  *
2014  *********************************************************************/
2015 static filter_function_ptr get_filter_function(const struct client_state *csp)
2016 {
2017    filter_function_ptr filter_function = NULL;
2018
2019    /*
2020     * Choose the applying filter function based on
2021     * the content type and action settings.
2022     */
2023    if ((csp->content_type & CT_TEXT) &&
2024        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
2025    {
2026       filter_function = pcrs_filter_response;
2027    }
2028    else if ((csp->content_type & CT_GIF) &&
2029             (csp->action->flags & ACTION_DEANIMATE))
2030    {
2031       filter_function = gif_deanimate_response;
2032    }
2033
2034    return filter_function;
2035 }
2036
2037
2038 /*********************************************************************
2039  *
2040  * Function    :  remove_chunked_transfer_coding
2041  *
2042  * Description :  In-situ remove the "chunked" transfer coding as defined
2043  *                in RFC 7230 4.1 from a buffer. XXX: The implementation
2044  *                is neither complete nor compliant (TODO #129).
2045  *
2046  * Parameters  :
2047  *          1  :  buffer = Pointer to the text buffer
2048  *          2  :  size =  In: Number of bytes to be processed,
2049  *                       Out: Number of bytes after de-chunking.
2050  *                       (undefined in case of errors)
2051  *
2052  * Returns     :  JB_ERR_OK for success,
2053  *                JB_ERR_PARSE otherwise
2054  *
2055  *********************************************************************/
2056 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
2057 {
2058    size_t newsize = 0;
2059    unsigned int chunksize = 0;
2060    char *from_p, *to_p;
2061    const char *end_of_buffer = buffer + *size;
2062
2063    assert(buffer);
2064    from_p = to_p = buffer;
2065
2066    if (sscanf(buffer, "%x", &chunksize) != 1)
2067    {
2068       log_error(LOG_LEVEL_ERROR, "Invalid first chunksize while stripping \"chunked\" transfer coding");
2069       return JB_ERR_PARSE;
2070    }
2071
2072    while (chunksize > 0U)
2073    {
2074       /*
2075        * If the chunk-size is valid, we should have at least
2076        * chunk-size bytes of chunk-data and five bytes of
2077        * meta data (chunk-size, CRLF, CRLF) left in the buffer.
2078        */
2079       if (chunksize + 5 >= *size - newsize)
2080       {
2081          log_error(LOG_LEVEL_ERROR,
2082             "Chunk size %u exceeds buffered data left. "
2083             "Already digested %u of %u buffered bytes.",
2084             chunksize, (unsigned int)newsize, (unsigned int)*size);
2085          return JB_ERR_PARSE;
2086       }
2087
2088       /*
2089        * Skip the chunk-size, the optional chunk-ext and the CRLF
2090        * that is supposed to be located directly before the start
2091        * of chunk-data.
2092        */
2093       if (NULL == (from_p = strstr(from_p, "\r\n")))
2094       {
2095          log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding");
2096          return JB_ERR_PARSE;
2097       }
2098       from_p += 2;
2099
2100       /*
2101        * The previous strstr() does not enforce chunk-validity
2102        * and is sattisfied as long a CRLF is left in the buffer.
2103        *
2104        * Make sure the bytes we consider chunk-data are within
2105        * the valid range.
2106        */
2107       if (from_p + chunksize >= end_of_buffer)
2108       {
2109          log_error(LOG_LEVEL_ERROR,
2110             "End of chunk is beyond the end of the buffer.");
2111          return JB_ERR_PARSE;
2112       }
2113
2114       memmove(to_p, from_p, (size_t) chunksize);
2115       newsize += chunksize;
2116       to_p = buffer + newsize;
2117       from_p += chunksize;
2118
2119       /*
2120        * Not merging this check with the previous one allows us
2121        * to keep chunks without trailing CRLF. It's not clear
2122        * if we actually have to care about those, though.
2123        */
2124       if (from_p + 2 >= end_of_buffer)
2125       {
2126          log_error(LOG_LEVEL_ERROR, "Not enough room for trailing CRLF.");
2127          return JB_ERR_PARSE;
2128       }
2129       from_p += 2;
2130       if (sscanf(from_p, "%x", &chunksize) != 1)
2131       {
2132          log_error(LOG_LEVEL_INFO, "Invalid \"chunked\" transfer encoding detected and ignored.");
2133          break;
2134       }
2135    }
2136
2137    /* XXX: Should get its own loglevel. */
2138    log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d", *size, newsize);
2139
2140    *size = newsize;
2141
2142    return JB_ERR_OK;
2143
2144 }
2145
2146
2147 /*********************************************************************
2148  *
2149  * Function    :  prepare_for_filtering
2150  *
2151  * Description :  If necessary, de-chunks and decompresses
2152  *                the content so it can get filterd.
2153  *
2154  * Parameters  :
2155  *          1  :  csp = Current client state (buffers, headers, etc...)
2156  *
2157  * Returns     :  JB_ERR_OK for success,
2158  *                JB_ERR_PARSE otherwise
2159  *
2160  *********************************************************************/
2161 static jb_err prepare_for_filtering(struct client_state *csp)
2162 {
2163    jb_err err = JB_ERR_OK;
2164
2165    /*
2166     * If the body has a "chunked" transfer-encoding,
2167     * get rid of it, adjusting size and iob->eod
2168     */
2169    if (csp->flags & CSP_FLAG_CHUNKED)
2170    {
2171       size_t size = (size_t)(csp->iob->eod - csp->iob->cur);
2172
2173       log_error(LOG_LEVEL_RE_FILTER, "Need to de-chunk first");
2174       err = remove_chunked_transfer_coding(csp->iob->cur, &size);
2175       if (JB_ERR_OK == err)
2176       {
2177          csp->iob->eod = csp->iob->cur + size;
2178          csp->flags |= CSP_FLAG_MODIFIED;
2179       }
2180       else
2181       {
2182          return JB_ERR_PARSE;
2183       }
2184    }
2185
2186 #ifdef FEATURE_ZLIB
2187    /*
2188     * If the body has a supported transfer-encoding,
2189     * decompress it, adjusting size and iob->eod.
2190     */
2191    if (csp->content_type & (CT_GZIP|CT_DEFLATE))
2192    {
2193       if (0 == csp->iob->eod - csp->iob->cur)
2194       {
2195          /* Nothing left after de-chunking. */
2196          return JB_ERR_OK;
2197       }
2198
2199       err = decompress_iob(csp);
2200
2201       if (JB_ERR_OK == err)
2202       {
2203          csp->flags |= CSP_FLAG_MODIFIED;
2204          csp->content_type &= ~CT_TABOO;
2205       }
2206       else
2207       {
2208          /*
2209           * Unset CT_GZIP and CT_DEFLATE to remember not
2210           * to modify the Content-Encoding header later.
2211           */
2212          csp->content_type &= ~CT_GZIP;
2213          csp->content_type &= ~CT_DEFLATE;
2214       }
2215    }
2216 #endif
2217
2218    return err;
2219 }
2220
2221
2222 /*********************************************************************
2223  *
2224  * Function    :  execute_content_filters
2225  *
2226  * Description :  Executes a given content filter.
2227  *
2228  * Parameters  :
2229  *          1  :  csp = Current client state (buffers, headers, etc...)
2230  *
2231  * Returns     :  Pointer to the modified buffer, or
2232  *                NULL if filtering failed or wasn't necessary.
2233  *
2234  *********************************************************************/
2235 char *execute_content_filters(struct client_state *csp)
2236 {
2237    char *content;
2238    filter_function_ptr content_filter;
2239
2240    assert(content_filters_enabled(csp->action));
2241
2242    if (0 == csp->iob->eod - csp->iob->cur)
2243    {
2244       /*
2245        * No content (probably status code 301, 302 ...),
2246        * no filtering necessary.
2247        */
2248       return NULL;
2249    }
2250
2251    if (JB_ERR_OK != prepare_for_filtering(csp))
2252    {
2253       /*
2254        * failed to de-chunk or decompress.
2255        */
2256       return NULL;
2257    }
2258
2259    if (0 == csp->iob->eod - csp->iob->cur)
2260    {
2261       /*
2262        * Clown alarm: chunked and/or compressed nothing delivered.
2263        */
2264       return NULL;
2265    }
2266
2267    content_filter = get_filter_function(csp);
2268    content = (content_filter != NULL) ? (*content_filter)(csp) : NULL;
2269
2270 #ifdef FEATURE_EXTERNAL_FILTERS
2271    if ((csp->content_type & CT_TEXT) &&
2272        !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]))
2273    {
2274       struct list_entry *filtername;
2275       size_t size = (size_t)csp->content_length;
2276
2277       if (content == NULL)
2278       {
2279          content = csp->iob->cur;
2280          size = (size_t)(csp->iob->eod - csp->iob->cur);
2281       }
2282
2283       for (filtername = csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]->first;
2284            filtername ; filtername = filtername->next)
2285       {
2286          char *result = execute_external_filter(csp, filtername->str, content, &size);
2287          if (result != NULL)
2288          {
2289             if (content != csp->iob->cur)
2290             {
2291                free(content);
2292             }
2293             content = result;
2294          }
2295       }
2296       csp->flags |= CSP_FLAG_MODIFIED;
2297       csp->content_length = size;
2298    }
2299 #endif /* def FEATURE_EXTERNAL_FILTERS */
2300
2301    return content;
2302
2303 }
2304
2305
2306 /*********************************************************************
2307  *
2308  * Function    :  get_url_actions
2309  *
2310  * Description :  Gets the actions for this URL.
2311  *
2312  * Parameters  :
2313  *          1  :  csp = Current client state (buffers, headers, etc...)
2314  *          2  :  http = http_request request for blocked URLs
2315  *
2316  * Returns     :  N/A
2317  *
2318  *********************************************************************/
2319 void get_url_actions(struct client_state *csp, struct http_request *http)
2320 {
2321    struct file_list *fl;
2322    struct url_actions *b;
2323    int i;
2324
2325    init_current_action(csp->action);
2326
2327    for (i = 0; i < MAX_AF_FILES; i++)
2328    {
2329       if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
2330       {
2331          return;
2332       }
2333
2334 #ifdef FEATURE_CLIENT_TAGS
2335       apply_url_actions(csp->action, http, csp->client_tags, b);
2336 #else
2337       apply_url_actions(csp->action, http, b);
2338 #endif
2339    }
2340
2341    return;
2342 }
2343
2344 /*********************************************************************
2345  *
2346  * Function    :  apply_url_actions
2347  *
2348  * Description :  Applies a list of URL actions.
2349  *
2350  * Parameters  :
2351  *          1  :  action = Destination.
2352  *          2  :  http = Current URL
2353  *          3  :  client_tags = list of client tags
2354  *          4  :  b = list of URL actions to apply
2355  *
2356  * Returns     :  N/A
2357  *
2358  *********************************************************************/
2359 static void apply_url_actions(struct current_action_spec *action,
2360                               struct http_request *http,
2361 #ifdef FEATURE_CLIENT_TAGS
2362                               const struct list *client_tags,
2363 #endif
2364                               struct url_actions *b)
2365 {
2366    if (b == NULL)
2367    {
2368       /* Should never happen */
2369       return;
2370    }
2371
2372    for (b = b->next; NULL != b; b = b->next)
2373    {
2374       if (url_match(b->url, http))
2375       {
2376          merge_current_action(action, b->action);
2377       }
2378 #ifdef FEATURE_CLIENT_TAGS
2379       if (client_tag_match(b->url, client_tags))
2380       {
2381          merge_current_action(action, b->action);
2382       }
2383 #endif
2384    }
2385 }
2386
2387
2388 /*********************************************************************
2389  *
2390  * Function    :  get_forward_override_settings
2391  *
2392  * Description :  Returns forward settings as specified with the
2393  *                forward-override{} action. forward-override accepts
2394  *                forward lines similar to the one used in the
2395  *                configuration file, but without the URL pattern.
2396  *
2397  *                For example:
2398  *
2399  *                   forward / .
2400  *
2401  *                in the configuration file can be replaced with
2402  *                the action section:
2403  *
2404  *                 {+forward-override{forward .}}
2405  *                 /
2406  *
2407  * Parameters  :
2408  *          1  :  csp = Current client state (buffers, headers, etc...)
2409  *
2410  * Returns     :  Pointer to forwarding structure in case of success.
2411  *                Invalid syntax is fatal.
2412  *
2413  *********************************************************************/
2414 static const struct forward_spec *get_forward_override_settings(struct client_state *csp)
2415 {
2416    const char *forward_override_line = csp->action->string[ACTION_STRING_FORWARD_OVERRIDE];
2417    char forward_settings[BUFFER_SIZE];
2418    char *http_parent = NULL;
2419    /* variable names were chosen for consistency reasons. */
2420    struct forward_spec *fwd = NULL;
2421    int vec_count;
2422    char *vec[3];
2423
2424    assert(csp->action->flags & ACTION_FORWARD_OVERRIDE);
2425    /* Should be enforced by load_one_actions_file() */
2426    assert(strlen(forward_override_line) < sizeof(forward_settings) - 1);
2427
2428    /* Create a copy ssplit can modify */
2429    strlcpy(forward_settings, forward_override_line, sizeof(forward_settings));
2430
2431    if (NULL != csp->fwd)
2432    {
2433       /*
2434        * XXX: Currently necessary to prevent memory
2435        * leaks when the show-url-info cgi page is visited.
2436        */
2437       unload_forward_spec(csp->fwd);
2438    }
2439
2440    /*
2441     * allocate a new forward node, valid only for
2442     * the lifetime of this request. Save its location
2443     * in csp as well, so sweep() can free it later on.
2444     */
2445    fwd = csp->fwd = zalloc_or_die(sizeof(*fwd));
2446
2447    vec_count = ssplit(forward_settings, " \t", vec, SZ(vec));
2448    if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
2449    {
2450       fwd->type = SOCKS_NONE;
2451
2452       /* Parse the parent HTTP proxy host:port */
2453       http_parent = vec[1];
2454
2455    }
2456    else if ((vec_count == 2) && !strcasecmp(vec[0], "forward-webserver"))
2457    {
2458       fwd->type = FORWARD_WEBSERVER;
2459
2460       /* Parse the parent HTTP server host:port */
2461       http_parent = vec[1];
2462
2463    }
2464    else if (vec_count == 3)
2465    {
2466       char *socks_proxy = NULL;
2467
2468       if  (!strcasecmp(vec[0], "forward-socks4"))
2469       {
2470          fwd->type = SOCKS_4;
2471          socks_proxy = vec[1];
2472       }
2473       else if (!strcasecmp(vec[0], "forward-socks4a"))
2474       {
2475          fwd->type = SOCKS_4A;
2476          socks_proxy = vec[1];
2477       }
2478       else if (!strcasecmp(vec[0], "forward-socks5"))
2479       {
2480          fwd->type = SOCKS_5;
2481          socks_proxy = vec[1];
2482       }
2483       else if (!strcasecmp(vec[0], "forward-socks5t"))
2484       {
2485          fwd->type = SOCKS_5T;
2486          socks_proxy = vec[1];
2487       }
2488
2489       if (NULL != socks_proxy)
2490       {
2491          /* Parse the SOCKS proxy host[:port] */
2492          fwd->gateway_port = 1080;
2493          parse_forwarder_address(socks_proxy,
2494             &fwd->gateway_host, &fwd->gateway_port);
2495
2496          http_parent = vec[2];
2497       }
2498    }
2499
2500    if (NULL == http_parent)
2501    {
2502       log_error(LOG_LEVEL_FATAL,
2503          "Invalid forward-override syntax in: %s", forward_override_line);
2504       /* Never get here - LOG_LEVEL_FATAL causes program exit */
2505    }
2506
2507    /* Parse http forwarding settings */
2508    if (strcmp(http_parent, ".") != 0)
2509    {
2510       fwd->forward_port = 8000;
2511       parse_forwarder_address(http_parent,
2512          &fwd->forward_host, &fwd->forward_port);
2513    }
2514
2515    assert (NULL != fwd);
2516
2517    log_error(LOG_LEVEL_CONNECT,
2518       "Overriding forwarding settings based on \'%s\'", forward_override_line);
2519
2520    return fwd;
2521 }
2522
2523
2524 /*********************************************************************
2525  *
2526  * Function    :  forward_url
2527  *
2528  * Description :  Should we forward this to another proxy?
2529  *
2530  * Parameters  :
2531  *          1  :  csp = Current client state (buffers, headers, etc...)
2532  *          2  :  http = http_request request for current URL
2533  *
2534  * Returns     :  Pointer to forwarding information.
2535  *
2536  *********************************************************************/
2537 const struct forward_spec *forward_url(struct client_state *csp,
2538                                        const struct http_request *http)
2539 {
2540    static const struct forward_spec fwd_default[1]; /* Zero'ed due to being static. */
2541    struct forward_spec *fwd = csp->config->forward;
2542
2543    if (csp->action->flags & ACTION_FORWARD_OVERRIDE)
2544    {
2545       return get_forward_override_settings(csp);
2546    }
2547
2548    if (fwd == NULL)
2549    {
2550       return fwd_default;
2551    }
2552
2553    while (fwd != NULL)
2554    {
2555       if (url_match(fwd->url, http))
2556       {
2557          return fwd;
2558       }
2559       fwd = fwd->next;
2560    }
2561
2562    return fwd_default;
2563 }
2564
2565
2566 /*********************************************************************
2567  *
2568  * Function    :  direct_response
2569  *
2570  * Description :  Check if Max-Forwards == 0 for an OPTIONS or TRACE
2571  *                request and if so, return a HTTP 501 to the client.
2572  *
2573  *                FIXME: I have a stupid name and I should handle the
2574  *                requests properly. Still, what we do here is rfc-
2575  *                compliant, whereas ignoring or forwarding are not.
2576  *
2577  * Parameters  :
2578  *          1  :  csp = Current client state (buffers, headers, etc...)
2579  *
2580  * Returns     :  http_response if , NULL if nonmatch or handler fail
2581  *
2582  *********************************************************************/
2583 struct http_response *direct_response(struct client_state *csp)
2584 {
2585    struct http_response *rsp;
2586    struct list_entry *p;
2587
2588    if ((0 == strcmpic(csp->http->gpc, "trace"))
2589       || (0 == strcmpic(csp->http->gpc, "options")))
2590    {
2591       for (p = csp->headers->first; (p != NULL) ; p = p->next)
2592       {
2593          if (!strncmpic(p->str, "Max-Forwards:", 13))
2594          {
2595             unsigned int max_forwards;
2596
2597             /*
2598              * If it's a Max-Forwards value of zero,
2599              * we have to intercept the request.
2600              */
2601             if (1 == sscanf(p->str+12, ": %u", &max_forwards) && max_forwards == 0)
2602             {
2603                /*
2604                 * FIXME: We could handle at least TRACE here,
2605                 * but that would require a verbatim copy of
2606                 * the request which we don't have anymore
2607                 */
2608                 log_error(LOG_LEVEL_HEADER,
2609                   "Detected header \'%s\' in OPTIONS or TRACE request. Returning 501.",
2610                   p->str);
2611
2612                /* Get mem for response or fail*/
2613                if (NULL == (rsp = alloc_http_response()))
2614                {
2615                   return cgi_error_memory();
2616                }
2617
2618                rsp->status = strdup_or_die("501 Not Implemented");
2619                rsp->is_static = 1;
2620                rsp->crunch_reason = UNSUPPORTED;
2621
2622                return(finish_http_response(csp, rsp));
2623             }
2624          }
2625       }
2626    }
2627    return NULL;
2628 }
2629
2630
2631 /*********************************************************************
2632  *
2633  * Function    :  content_requires_filtering
2634  *
2635  * Description :  Checks whether there are any content filters
2636  *                enabled for the current request and if they
2637  *                can actually be applied..
2638  *
2639  * Parameters  :
2640  *          1  :  csp = Current client state (buffers, headers, etc...)
2641  *
2642  * Returns     :  TRUE for yes, FALSE otherwise
2643  *
2644  *********************************************************************/
2645 int content_requires_filtering(struct client_state *csp)
2646 {
2647    if ((csp->content_type & CT_TABOO)
2648       && !(csp->action->flags & ACTION_FORCE_TEXT_MODE))
2649    {
2650       return FALSE;
2651    }
2652
2653    /*
2654     * Are we enabling text mode by force?
2655     */
2656    if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
2657    {
2658       /*
2659        * Do we really have to?
2660        */
2661       if (csp->content_type & CT_TEXT)
2662       {
2663          log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");
2664       }
2665       else
2666       {
2667          csp->content_type |= CT_TEXT;
2668          log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");
2669       }
2670    }
2671
2672    if (!(csp->content_type & CT_DECLARED))
2673    {
2674       /*
2675        * The server didn't bother to declare a MIME-Type.
2676        * Assume it's text that can be filtered.
2677        *
2678        * This also regulary happens with 304 responses,
2679        * therefore logging anything here would cause
2680        * too much noise.
2681        */
2682       csp->content_type |= CT_TEXT;
2683    }
2684
2685    /*
2686     * Choose the applying filter function based on
2687     * the content type and action settings.
2688     */
2689    if ((csp->content_type & CT_TEXT) &&
2690        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]) ||
2691         !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])))
2692    {
2693       return TRUE;
2694    }
2695    else if ((csp->content_type & CT_GIF)  &&
2696             (csp->action->flags & ACTION_DEANIMATE))
2697    {
2698       return TRUE;
2699    }
2700
2701    return FALSE;
2702
2703 }
2704
2705
2706 /*********************************************************************
2707  *
2708  * Function    :  content_filters_enabled
2709  *
2710  * Description :  Checks whether there are any content filters
2711  *                enabled for the current request.
2712  *
2713  * Parameters  :
2714  *          1  :  action = Action spec to check.
2715  *
2716  * Returns     :  TRUE for yes, FALSE otherwise
2717  *
2718  *********************************************************************/
2719 int content_filters_enabled(const struct current_action_spec *action)
2720 {
2721    return ((action->flags & ACTION_DEANIMATE) ||
2722       !list_is_empty(action->multi[ACTION_MULTI_FILTER]) ||
2723       !list_is_empty(action->multi[ACTION_MULTI_EXTERNAL_FILTER]));
2724 }
2725
2726
2727 /*********************************************************************
2728  *
2729  * Function    :  filters_available
2730  *
2731  * Description :  Checks whether there are any filters available.
2732  *
2733  * Parameters  :
2734  *          1  :  csp = Current client state (buffers, headers, etc...)
2735  *
2736  * Returns     :  TRUE for yes, FALSE otherwise.
2737  *
2738  *********************************************************************/
2739 int filters_available(const struct client_state *csp)
2740 {
2741    int i;
2742    for (i = 0; i < MAX_AF_FILES; i++)
2743    {
2744       const struct file_list *fl = csp->rlist[i];
2745       if ((NULL != fl) && (NULL != fl->f))
2746       {
2747          return TRUE;
2748       }
2749    }
2750    return FALSE;
2751 }
2752
2753
2754 /*
2755   Local Variables:
2756   tab-width: 3
2757   end:
2758 */