LCOV - code coverage report
Current view: top level - fuzz - filters.c (source / functions) Hit Total Coverage
Test: trace.lcov_info_final Lines: 505 904 55.9 %
Date: 2021-02-22 04:51:02 Functions: 34 41 82.9 %

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

Generated by: LCOV version 1.14