- Add patches from Debian release 3.0.3-2-2
authorRoland Rosenfeld <roland@spinnaker.de>
Sat, 9 Sep 2006 13:49:59 +0000 (13:49 +0000)
committerRoland Rosenfeld <roland@spinnaker.de>
Sat, 9 Sep 2006 13:49:59 +0000 (13:49 +0000)
- Adapt Debian patches to the upcoming 3.0.4 release

19 files changed:
debian/changelog
debian/control
debian/patches/00list
debian/patches/01_local_usermanual.dpatch
debian/patches/02_linkcolor.dpatch
debian/patches/03_ipv6.dpatch [new file with mode: 0755]
debian/patches/04_nomultiproxy.dpatch
debian/patches/05_defaut_action.dpatch
debian/patches/06_8bit_manual.dpatch
debian/patches/07_typos.dpatch
debian/patches/09_no_identity.dpatch
debian/patches/14_config.dpatch
debian/patches/15_mansection8.dpatch
debian/patches/16_gzip.dpatch [new file with mode: 0755]
debian/patches/17_502_no_such_domain.dpatch [new file with mode: 0755]
debian/patches/18_dns_retry.dpatch [new file with mode: 0755]
debian/patches/19_manpage_fixup.dpatch [new file with mode: 0755]
debian/patches/20_makefile_fixup.dpatch [new file with mode: 0755]
debian/patches/21_version_3.0.4.dpatch [new file with mode: 0755]

index 804eefc..8e0fdb0 100644 (file)
@@ -1,8 +1,40 @@
+privoxy (3.0.4.cvs20060909-1) UNRELEASED; urgency=low
+
+  * New upstream CVS snapshot as of 2006-09-09.
+  * Adapted all patches to this version.
+  * The following patches are incorporated upstream now, so they are no
+    longer needed: 01_local_usermanual.dpatch, 08_log_pthread.dpatch,
+    09_no_identity.dpatch, 12_multiple-filters.dpatch, 13_memory.dpatch,
+    18_dns_retry.dpatch.
+  * 20_makefile_fixup.dpatch: Fix a syntax error in the GNUmakefile.in.
+
+ -- Roland Rosenfeld <roland@debian.org>  Sat,  9 Sep 2006 15:26:34 +0200
+
+privoxy (3.0.3-2-2) unstable; urgency=low
+
+  * Upgrade to Standards-Version 3.7.2 (no changes).
+  * 17_502_no_such_domain.dpatch: Changes the 404 HTTP status code of the
+    "No such Domain" template to 502 Bad Gateway, which seems to be more
+    correct according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+    (Closes: #380389).
+  * Disable filter{js-annoyances} and filter{unsolicited-popups} for
+    .cnrs.fr and blogs.msdn.com, because these sites consume very much CPU
+    time otherwise (Closes: #377843).
+  * 18_dns_retry.dpatch by Fabian Keil <fk@fabiankeil.de>: 10 retries
+    before giving up DNS lookup. This hopefully Closes: #335660.
+  * 19_manpage_fixup.dpatch:
+    - Convert Latin-1 char á to groff eqivalent in man page.
+    - Quote minus signs to differenciate them from hyphens.
+  * Do not filter{banners-by-size} on .w3.org pages, because these often
+    contain validator icons (Closes: #319025).
+
+ -- Roland Rosenfeld <roland@debian.org>  Sat,  5 Aug 2006 15:15:17 +0200
+
 privoxy (3.0.3-2-1) unstable; urgency=low
 
   * New upstream version 3.0.3-2.
   * Add debian/watch file.
-  * Remove parts of 13_memory.dpatch, which seem to be free too much
+  * Remove parts of 13_memory.dpatch, which seems to free too much
     memory (Closes: #353110, #354611).
 
  -- Roland Rosenfeld <roland@debian.org>  Mon, 27 Feb 2006 23:28:52 +0100
index cd43c6c..f33f074 100644 (file)
@@ -3,7 +3,7 @@ Section: web
 Priority: optional
 Maintainer: Roland Rosenfeld <roland@debian.org>
 Build-Depends: debhelper (>= 5.0.0), autoconf, autotools-dev, libpcre3-dev, jade, docbook-dsssl, w3m, sp, sgmlspl, groff, htmldoc (>= 1.8.25-1), man2html, dpatch
-Standards-Version: 3.6.2
+Standards-Version: 3.7.2
 
 Package: privoxy
 Architecture: any
index 6c2fb3f..c225495 100644 (file)
@@ -1,14 +1,21 @@
-01_local_usermanual.dpatch
+# 01_local_usermanual.dpatch # now upstream
 02_linkcolor.dpatch
+# 03_ipv6.dpatch # incompatible with 18_dns_retry.dpatch
 04_nomultiproxy.dpatch
 05_defaut_action.dpatch
 06_8bit_manual.dpatch
 07_typos.dpatch
-08_log_pthread.dpatch
-09_no_identity.dpatch
+# 08_log_pthread.dpatch # now upstream
+# 09_no_identity.dpatch # now upstream
 10_backup_doc.dpatch
 11_backup_autotools.dpatch
-12_multiple-filters.dpatch
-13_memory.dpatch
+# 12_multiple-filters.dpatch # now upstream
+# 13_memory.dpatch # new upstream
 14_config.dpatch
 15_mansection8.dpatch
+# 16_gzip.dpatch # still broken.
+17_502_no_such_domain.dpatch 
+# 18_dns_retry.dpatch # now upstream
+19_manpage_fixup.dpatch
+20_makefile_fixup.dpatch
+21_version_3.0.4.dpatch
index a5c51fa..faa5722 100644 (file)
@@ -7,9 +7,9 @@
 
 @DPATCH@
 diff -urNad privoxy~/cgi.c privoxy/cgi.c
---- privoxy~/cgi.c     2006-02-11 23:44:26.000000000 +0100
-+++ privoxy/cgi.c      2006-02-11 23:44:34.000000000 +0100
-@@ -609,6 +609,9 @@
+--- privoxy~/cgi.c
++++ privoxy/cgi.c
+@@ -616,6 +616,9 @@
     { "t",
           cgi_transparent_image, 
           NULL, TRUE /* Send a transparent image (short name) */ },
@@ -19,7 +19,7 @@ diff -urNad privoxy~/cgi.c privoxy/cgi.c
     { NULL, /* NULL Indicates end of list and default page */
           cgi_error_404,
           NULL, TRUE /* Unknown CGI page */ }
-@@ -821,21 +824,28 @@
+@@ -828,21 +831,28 @@
     {
        return cgi_error_memory();
     }
@@ -57,7 +57,7 @@ diff -urNad privoxy~/cgi.c privoxy/cgi.c
     }
  
     /*
-@@ -1597,7 +1607,13 @@
+@@ -1604,7 +1614,13 @@
     if (!item) return NULL;
  
     result = strdup("<a href=\"");
@@ -72,7 +72,7 @@ diff -urNad privoxy~/cgi.c privoxy/cgi.c
     string_append(&result, ACTIONS_HELP_PREFIX);
     string_join  (&result, string_toupper(item));
     string_append(&result, "\">");
-@@ -2171,7 +2187,11 @@
+@@ -2193,7 +2209,11 @@
     if (!err) err = map(exports, "default-cgi",   1, html_encode(CGI_PREFIX), 0);
     if (!err) err = map(exports, "menu",          1, make_menu(caller), 0);
     if (!err) err = map(exports, "code-status",   1, CODE_STATUS, 1);
@@ -86,9 +86,9 @@ diff -urNad privoxy~/cgi.c privoxy/cgi.c
  #ifdef FEATURE_TOGGLE
     if (!err) err = map_conditional(exports, "enabled-display", global_toggle_state);
 diff -urNad privoxy~/cgisimple.c privoxy/cgisimple.c
---- privoxy~/cgisimple.c       2006-02-11 23:44:26.000000000 +0100
-+++ privoxy/cgisimple.c        2006-02-11 23:44:34.000000000 +0100
-@@ -642,6 +642,89 @@
+--- privoxy~/cgisimple.c
++++ privoxy/cgisimple.c
+@@ -660,6 +660,89 @@
     return JB_ERR_OK;
  
  }
@@ -179,9 +179,9 @@ diff -urNad privoxy~/cgisimple.c privoxy/cgisimple.c
  
  /*********************************************************************
 diff -urNad privoxy~/cgisimple.h privoxy/cgisimple.h
---- privoxy~/cgisimple.h       2006-02-11 23:44:26.000000000 +0100
-+++ privoxy/cgisimple.h        2006-02-11 23:44:34.000000000 +0100
-@@ -128,6 +128,9 @@
+--- privoxy~/cgisimple.h
++++ privoxy/cgisimple.h
+@@ -132,6 +132,9 @@
  extern jb_err cgi_send_stylesheet(struct client_state *csp,
                                    struct http_response *rsp,
                                    const struct map *parameters);
@@ -192,9 +192,9 @@ diff -urNad privoxy~/cgisimple.h privoxy/cgisimple.h
  #ifdef FEATURE_GRACEFUL_TERMINATION
  extern jb_err cgi_die (struct client_state *csp,
 diff -urNad privoxy~/loadcfg.c privoxy/loadcfg.c
---- privoxy~/loadcfg.c 2006-02-11 23:44:26.000000000 +0100
-+++ privoxy/loadcfg.c  2006-02-11 23:44:34.000000000 +0100
-@@ -1579,7 +1579,13 @@
+--- privoxy~/loadcfg.c
++++ privoxy/loadcfg.c
+@@ -1605,7 +1605,13 @@
      * link to it's section in the user-manual
      */
     buf = strdup("\n<br><a href=\"");
index 160778d..966d576 100644 (file)
@@ -6,8 +6,8 @@
 
 @DPATCH@
 diff -urNad privoxy~/utils/ldp_print/ldp_print privoxy/utils/ldp_print/ldp_print
---- privoxy~/utils/ldp_print/ldp_print 2006-02-05 22:03:58.000000000 +0100
-+++ privoxy/utils/ldp_print/ldp_print  2006-02-05 22:12:43.000000000 +0100
+--- privoxy~/utils/ldp_print/ldp_print
++++ privoxy/utils/ldp_print/ldp_print
 @@ -54,7 +54,7 @@
  
  my($cmd) = "htmldoc --size universal --bodyfont helvetica --fontsize 8 " .
diff --git a/debian/patches/03_ipv6.dpatch b/debian/patches/03_ipv6.dpatch
new file mode 100755 (executable)
index 0000000..853c573
--- /dev/null
@@ -0,0 +1,3535 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 03_ipv6.dpatch by Lionel Elie Mamane <lionel@mamane.lu>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: privoxy_CVS_20030523_ipv6_5.patch.bz2 from
+## DP: ftp://ftp.deepspace6.net/pub/ds6/sources/privoxy/privoxy_CVS_20030523_ipv6_5.patch.bz2
+## DP: adapted to the 3.0 branch of privoxy by Roland Rosenfeld
+
+@DPATCH@
+diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
+--- privoxy~/GNUmakefile.in
++++ privoxy/GNUmakefile.in
+@@ -187,7 +187,7 @@
+ C_SRC  = actions.c cgi.c cgiedit.c cgisimple.c deanimate.c encode.c \
+          errlog.c filters.c gateway.c jbsockets.c jcc.c killpopup.c \
+          list.c loadcfg.c loaders.c miscutil.c parsers.c ssplit.c \
+-         urlmatch.c
++         urlmatch.c addrlist.c jb_socket_set.c
+ C_OBJS = $(C_SRC:.c=.@OBJEXT@)
+ C_HDRS = $(C_SRC:.c=.h) project.h actionlist.h
+@@ -241,7 +241,7 @@
+ SPECIAL_CFLAGS = @SPECIAL_CFLAGS@
+ # Add your flags here 
+-OTHER_CFLAGS =   
++OTHER_CFLAGS = -DINET6
+ CFLAGS = @CFLAGS@ @CPPFLAGS@ $(OTHER_CFLAGS) $(SPECIAL_CFLAGS) -Wall \
+          @STATIC_PCRE_ONLY@ -Ipcre 
+diff -urNad privoxy~/addrlist.c privoxy/addrlist.c
+--- privoxy~/addrlist.c
++++ privoxy/addrlist.c
+@@ -0,0 +1,198 @@
++const char addrlist_rcs[] = "$Id: $";
++/*********************************************************************
++ *
++ * File        :  $Source: $
++ *
++ * Purpose     :  Declares functions to handle lists of network addresses.
++ *                Functions declared include:
++ *                   `destroy_addr_list', head_addr_list and `tail_addr_list'
++ *
++ * Copyright   :  Written by and Copyright (C) 2002 Lionel Elie Mamane
++ *                <lionel@mamane.lu>
++ *
++ *                This program is free software; you can redistribute it
++ *                and/or modify it under the terms of the GNU General
++ *                Public License as published by the Free Software
++ *                Foundation; either version 2 of the License, or (at
++ *                your option) any later version.
++ *
++ *                This program is distributed in the hope that it will
++ *                be useful, but WITHOUT ANY WARRANTY; without even the
++ *                implied warranty of MERCHANTABILITY or FITNESS FOR A
++ *                PARTICULAR PURPOSE.  See the GNU General Public
++ *                License for more details.
++ *
++ *                The GNU General Public License should be included with
++ *                this file.  If not, you can view it at
++ *                http://www.gnu.org/copyleft/gpl.html
++ *                or write to the Free Software Foundation, Inc., 59
++ *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ *
++ * Revisions   :
++ *    $Log: addrlist.c,v $
++ *
++ *********************************************************************/
++
++#include "addrlist.h"
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h>
++#include <string.h>
++
++/*********************************************************************
++ *
++ * Function    :  acceptable
++ *
++ * Description :  Test wheter an address is acceptable for our use
++ *                Currently, this means either an IPv4 or an IPv6 address
++ *
++ * Parameters  :
++ *          0  :  addr = the address to test
++ *
++ * Returns     :  0             = false / no
++ *                anything else = true / yes
++ *
++ *********************************************************************/
++static int acceptable (struct sockaddr_storage *addr)
++{
++   switch(addr->ss_family)
++   {
++   case AF_INET:
++#ifdef INET6
++   case AF_INET6:
++#endif
++      return !0;
++   default:
++      return 0;
++   }
++}
++
++/*********************************************************************
++ *
++ * Function    :  skim
++ *
++ * Description :  Get the first acceptable address in head position
++ *                Assumes there is one
++ *
++ * Parameters  :
++ *          0  :  l = the list to skim
++ *
++ * Returns     :  the skimmed list
++ *
++ *********************************************************************/
++static addr_list *skim (addr_list *l)
++{
++   if (acceptable((struct sockaddr_storage*)l->ai_addr))
++      return l;
++   return skim(l->ai_next);
++}
++
++/*********************************************************************
++ *
++ * Function    :  tail_addr_list
++ *
++ * Description :  Get the tail of an address list
++ *
++ * Parameters  :
++ *          0  :  l = the list to get the tail of
++ *
++ * Returns     :  the tail of the list
++ *                If the list has no tail (i.e. is nil), unspecified
++ *                behaviour
++ *
++ *********************************************************************/
++addr_list *tail_addr_list(addr_list *l)
++{
++   return skim(l)->ai_next;
++}
++
++/*********************************************************************
++ *
++ * Function    :  head_addr_list
++ *
++ * Description :  Get the head of an address list
++ *
++ * Parameters  :
++ *          0  :  l = the list to get the head of
++ *
++ * Returns     :  the head of the list
++ *                If the list has no head (i.e. is nil), unspecified
++ *                behaviour
++ *
++ *********************************************************************/
++struct sockaddr_storage *head_addr_list(addr_list *l)
++{
++   return (struct sockaddr_storage *)skim(l)->ai_addr;
++}
++
++/*********************************************************************
++ *
++ * Function    :  cpy_head_addr_list
++ *
++ * Description :  Copy the head of an address list to the given destination
++ *
++ * Parameters  :
++ *          0  :  l = the list to get the head of
++ *          1  :  r = where to put the result
++ *
++ * Returns     :  Nothing
++ *                If the list has no head (i.e. is nil), unspecified
++ *                behaviour
++ *
++ *********************************************************************/
++void cpy_head_addr_list(addr_list *l, struct sockaddr_storage *r, size_t *addrlen)
++{
++   addr_list *sl = skim(l);
++   memcpy(r, sl->ai_addr, sl->ai_addrlen);
++   *addrlen = sl->ai_addrlen;
++}
++
++/*********************************************************************
++ *
++ * Function    :  destroy_addr_list
++ *
++ * Description :  Unallocate memory allocated to an address list
++ *
++ * Parameters  :
++ *          0  :  l = the list to unallocate
++ *
++ * Returns     :  nothing
++ *
++ *********************************************************************/
++void destroy_addr_list(addr_list *l)
++{
++   freeaddrinfo(l);
++}
++
++/*********************************************************************
++ *
++ * Function    :  is_nil_addr_list
++ *
++ * Description :  Test wheter a list is nil (empty)
++ *
++ * Parameters  :
++ *          0  :  l = the list to test
++ *
++ * Returns     :  0             = false if list has a head,
++ *                anything else = true if list is nil
++ *
++ *********************************************************************/
++int is_nil_addr_list(addr_list *l)
++{
++   /* We are searching for a witness of non-nilness (modulo acceptability)
++    * If none is found, the list is nil
++    */
++   if (l==NULL)
++      /* Empty list*/
++      return !0;
++   if (acceptable(head_addr_list(l)))
++      /* Witness found */
++      return 0;
++   return is_nil_addr_list(l->ai_next);
++}
++
++/*
++  Local Variables:
++  tab-width: 3
++  end:
++*/
+diff -urNad privoxy~/addrlist.h privoxy/addrlist.h
+--- privoxy~/addrlist.h
++++ privoxy/addrlist.h
+@@ -0,0 +1,56 @@
++#ifndef ADDR_LIST_H_INCLUDED
++#define ADDR_LIST_H_INCLUDED
++#define ADDR_LIST_H_VERSION "$Id: $"
++/*********************************************************************
++ *
++ * File        :  $Source: $
++ *
++ * Purpose     :  Declares functions to handle lists of network addresses.
++ *                Functions declared include:
++ *                   `destroy_addr_list', head_addr_list and `tail_addr_list'
++ *
++ * Copyright   :  Written by and Copyright (C) 2002 Lionel Elie Mamane
++ *                <lionel@mamane.lu>
++ *
++ *                This program is free software; you can redistribute it
++ *                and/or modify it under the terms of the GNU General
++ *                Public License as published by the Free Software
++ *                Foundation; either version 2 of the License, or (at
++ *                your option) any later version.
++ *
++ *                This program is distributed in the hope that it will
++ *                be useful, but WITHOUT ANY WARRANTY; without even the
++ *                implied warranty of MERCHANTABILITY or FITNESS FOR A
++ *                PARTICULAR PURPOSE.  See the GNU General Public
++ *                License for more details.
++ *
++ *                The GNU General Public License should be included with
++ *                this file.  If not, you can view it at
++ *                http://www.gnu.org/copyleft/gpl.html
++ *                or write to the Free Software Foundation, Inc., 59
++ *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ *
++ * Revisions   :
++ *    $Log: addrlist.h,v $
++ *
++ *********************************************************************/
++
++#include <sys/socket.h>
++
++typedef struct addrinfo addr_list;
++
++addr_list *tail_addr_list(addr_list *l);
++struct sockaddr_storage *head_addr_list(addr_list *l);
++void cpy_head_addr_list(addr_list *l, struct sockaddr_storage *r, size_t *addrlen);
++void destroy_addr_list(addr_list *l);
++int is_nil_addr_list(addr_list *l);
++
++#define freez_addr_list(X)  { if(X) { destroy_addr_list(X); X = NULL ; } }
++
++#endif /* ndef LIST_H_INCLUDED */
++
++/*
++  Local Variables:
++  tab-width: 3
++  end:
++*/
+diff -urNad privoxy~/cgi.c privoxy/cgi.c
+--- privoxy~/cgi.c
++++ privoxy/cgi.c
+@@ -14,6 +14,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and 
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -2167,7 +2170,6 @@
+  *********************************************************************/
+ struct map *default_exports(const struct client_state *csp, const char *caller)
+ {
+-   char buf[20];
+    jb_err err;
+    struct map * exports;
+    int local_help_exists = 0;
+@@ -2199,8 +2201,7 @@
+    if (!err) err = map_block_killer(exports, "can-toggle");
+ #endif
+-   snprintf(buf, 20, "%d", csp->config->hport);
+-   if (!err) err = map(exports, "my-port", 1, buf, 1);
++      if (!err) err = map(exports, "my-port", 1, csp->my_port_str, 1);
+    if(!strcmp(CODE_STATUS, "stable"))
+    {
+diff -urNad privoxy~/errlog.c privoxy/errlog.c
+--- privoxy~/errlog.c
++++ privoxy/errlog.c
+@@ -679,6 +679,13 @@
+             break;
+          case 'E':
+             /* Non-standard: Print error code from errno */
++            /* TODO
++             * This is not only not standard, but clashes
++             * with the E modifier on the GNU (and possibly
++             * other systems): It means double (floating point)
++             * number in exponential notation, with capital E
++             * for mantiss / exponenent separator
++             */
+ #ifdef _WIN32
+             ival = WSAGetLastError();
+             sval = w32_socket_strerr(ival, tempbuf);
+diff -urNad privoxy~/filters.c privoxy/filters.c
+--- privoxy~/filters.c
++++ privoxy/filters.c
+@@ -14,6 +14,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -427,6 +430,9 @@
+ #include <ctype.h>
+ #include <string.h>
+ #include <assert.h>
++#ifdef INET6
++#include <netdb.h>
++#endif
+ #ifndef _WIN32
+ #ifndef __OS2__
+@@ -461,17 +467,119 @@
+ const char filters_h_rcs[] = FILTERS_H_VERSION;
+-/* Fix a problem with Solaris.  There should be no effect on other
+- * platforms.
+- * Solaris's isspace() is a macro which uses it's argument directly
+- * as an array index.  Therefore we need to make sure that high-bit
+- * characters generate +ve values, and ideally we also want to make
+- * the argument match the declared parameter type of "int".
+- */
+-#define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
++#ifdef FEATURE_ACL
++/*********************************************************************
++ *
++ * Function    :  addr_equal_under_mask
++ *
++ * Description :  Are these addresses equal modulo this mask?
++ *                Assumes the second argument is already in
++ *                mask-normal form
++ *
++ * Parameters  :
++ *          0  :  addr1 = First address to compare
++ *          1  :  addr2 = Second address to compare
++ *                        MUST be in mask-normal form
++ *          2  :  mask = for IPv4 addresses, a bitmask
++ *                       for IPv6 addresses, a prefixlen in bits
++ *
++ * Returns     : 0 = FALSE (not equal) and non-zero = TRUE (equal)
++ *
++ *********************************************************************/
++static
++int
++addr_equal_under_mask(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2, unsigned long  mask)
++{
++   if (!mask)
++       return 1;
++   /* only identical families can be compared */
++   /* TODO: Should we code the special case of "IPv4 addresses as IPv6 addresses"? */
++   if (addr1->ss_family != addr2-> ss_family)
++     {
++       /*fprintf(stderr, "equal_under_mask: diff sa_family: %d %d\n",
++         sa1->sa_family, sa2-> sa_family); */
++       return 0;
++     }
++  
++   switch (addr1->ss_family)
++      {
++      case AF_INET:
++         {
++            /* IPv4 - mask is a bitmask */
++            struct sockaddr_in *sin1 = (struct sockaddr_in *)addr1;
++            struct sockaddr_in *sin2 = (struct sockaddr_in *)addr2;
++            
++            /*fprintf(stderr, "AF_INET: %08x %08x %08x\n", 
++              sin1->sin_addr.s_addr,
++              sin2->sin_addr.s_addr,
++              mask); */
++            return (sin1->sin_addr.s_addr & mask) == sin2->sin_addr.s_addr;
++         }
++         break;
++#ifdef INET6
++      case AF_INET6:
++         {
++            /* IPv6 - mask is a prefixlength in bits. */
++            struct sockaddr_in6 *sin1 = (struct sockaddr_in6 *)addr1;
++            struct sockaddr_in6 *sin2 = (struct sockaddr_in6 *)addr2;
++            char bitmask;
++            char *a1, *a2;
++            const int maskbytes = mask / 8;
++            static char m[] = { 0x00, 0x80, 0xC0, 0xE0,
++                                0xF0, 0xF8, 0xFC, 0xFE };
++/*             { */
++/*                int i; */
++/*                fprintf(stderr, "PF_INET6: "); */
++/*                for (i = 0; i < 16; i++) { */
++/*                   fprintf(stderr, "%02x ", sin1->sin6_addr.s6_addr[i]); */
++/*                } */
++/*                fprintf(stderr, "   "); */
++/*                for (i = 0; i < 16; i++) { */
++/*                   fprintf(stderr, "%02x ", sin2->sin6_addr.s6_addr[i]); */
++/*                } */
++/*                fprintf(stderr, "mask %d scopeids %x %x\n", mask, sin1->sin6_scope_id, sin2->sin6_scope_id); */
++/*             } */
++            /* should we compare scope ids and such too? */
++            /*
++             * LEM: I see no reason for this comparison
++             * Quite the contrary: A client coming to us with
++             * a small-scope address should be able to a bigger-scope
++             * address.
++             */
++/*             if (sin1->sin6_scope_id != sin2->sin6_scope_id) */
++/*                return 0; */
++
++            if (mask > 128ul)
++            {
++               log_error(LOG_LEVEL_ERROR, "%s%d", "Tried to compare IPv6 addresses with invalid prefixlen: ", mask);
++               return 0;
++            }
++
++            a1 = sin1->sin6_addr.s6_addr;
++            a2 = sin2->sin6_addr.s6_addr;
++
++            if (memcmp(a1, a2, maskbytes) != 0)
++               return 0;
++
++            mask %= 8;
++            /* This special case is necessary for when mask==128
++               else, we would go over the array size in a1/a2
++             */
++            if (mask==0)
++               return 1;
++
++            bitmask = m[mask];
++
++            return (a1[maskbytes] & bitmask) == a2[maskbytes];
++      }
++         break;
++#endif
++      default:
++         return 0;
++      }
++}
+-#ifdef FEATURE_ACL
+ /*********************************************************************
+  *
+  * Function    :  block_acl
+@@ -501,7 +609,7 @@
+    /* search the list */
+    while (acl != NULL)
+    {
+-      if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
++      if (addr_equal_under_mask(&csp->ip_addr_addr, &acl->src->addr, acl->src->mask))
+       {
+          if (dst == NULL)
+          {
+@@ -511,8 +619,8 @@
+                return(0);
+             }
+          }
+-         else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
+-           && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
++         else if ( addr_equal_under_mask(&dst->addr, &acl->dst->addr, acl->dst->mask)
++                   && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
+          {
+             if (acl->action == ACL_PERMIT)
+             {
+@@ -531,81 +639,249 @@
+ }
+-
+ /*********************************************************************
+  *
+- * Function    :  acl_addr
++ * Function    :  fill_acl_addr_mask
+  *
+- * Description :  Called from `load_config' to parse an ACL address.
++ * Description :  Fill in the mask-related members of a
++ *                struct access_control_addr
+  *
+  * Parameters  :
+- *          1  :  aspec = String specifying ACL address.
+- *          2  :  aca = struct access_control_addr to fill in.
++ *          0  :  aca = struct access_control_addr to fill in.
++ *          1  :  masklength = mask length.
+  *
+- * Returns     :  0 => Ok, everything else is an error.
++ * Returns     :  nothing
+  *
+  *********************************************************************/
+-int acl_addr(char *aspec, struct access_control_addr *aca)
++void fill_acl_addr_mask(struct access_control_addr *aca, int masklength)
+ {
+-   int i, masklength, port;
+-   char *p;
++   int pf;
+-   masklength = 32;
+-   port       =  0;
++   pf = aca->addr.ss_family;
+-   if ((p = strchr(aspec, '/')) != NULL)
++   switch (pf)
+    {
+-      *p++ = '\0';
+-
+-      if (ijb_isdigit(*p) == 0)
++   case PF_INET:
++      /* build the netmask */
++      if (masklength == -1)
++         masklength = 32;
++      aca->mask = 0;
++      for(pf=1; pf <= masklength ; ++pf)
+       {
+-         return(-1);
++         aca->mask |= (1 << (32 - pf));
+       }
+-      masklength = atoi(p);
+-   }
++      aca->mask = htonl(aca->mask);
++      
++      /* now mask off the host portion of the ip address
++       * (i.e. save on the network portion of the address).
++       */
++      ((struct sockaddr_in*) &aca->addr)->sin_addr.s_addr &= aca->mask;
++      aca->port = ((struct sockaddr_in*) &aca->addr)->sin_port;
++      break;
++#ifdef INET6
++   case PF_INET6:
++      {
++         static char m[] = { 0x00, 0x80, 0xC0, 0xE0,
++                             0xF0, 0xF8, 0xFC, 0xFE };
++         int i;
++         struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)&aca->addr;
+-   if ((masklength < 0) || (masklength > 32))
+-   {
+-      return(-1);
++         aca->mask = (masklength == -1) ? masklength : 128 ;
++         /* now mask off the host portion of the ip address
++          * (i.e. save on the network portion of the address).
++          */
++         i = aca->mask / 8;
++         if (i < 16)
++         {
++            sa6->sin6_addr.s6_addr[i] &= m[aca->mask % 8];
++            /* The following loop is not strictly necessary,
++               because of the way addr_equal_under_mask is
++               written. Better safe than sorry, though:
++               New code might make the full mask-normal
++               form assumption.
++             */
++            for(++i; i < 16 ; ++i)
++               sa6->sin6_addr.s6_addr[i] = 0;
++         }
++         aca -> port = sa6->sin6_port;
++         break;
++      }
++#endif
++   default:
++      /* FATAL because access_control_addr's are created only with adresses
++         deemed 'acceptable' by the addr_list stuff, thus currently IPv4 and
++         IPv6.
++       */
++      log_error(LOG_LEVEL_FATAL,"%s%d","Unknown address family in ACL address: ",pf);
+    }
++}
+-   if ((p = strchr(aspec, ':')) != NULL)
+-   {
+-      *p++ = '\0';
++/*********************************************************************
++ *
++ * Function    :  acl_addrs
++ *
++ * Description :  Parse an ACL address (adress + mask prefix)
++ *                Resolve the parsed address
++ *                Describe errors in *proxy_args.
++ *
++ * Parameters  :
++ *          0  :  aspec = the string containing the ACL address/mask
++ *          1  :  masklength = pointer used to return the mask
++ *          2  :  proxy_args = Pointer to string to append description of errors to.
++ *          3  :  type = type of ACL adress (source / destination).
++ *                       Used for error reporting.
++ *
++ * Returns     :  the list of adresses the ACL address resolves to
++ *
++ *********************************************************************/
++static addr_list *acl_addrs(char *aspec, int *masklength, char**proxy_args, const char *type)
++{
++   char *host;
++   char *port;
++   int pf;
+-      if (ijb_isdigit(*p) == 0)
+-      {
+-         return(-1);
+-      }
+-      port = atoi(p);
++   pf = -1;
++   if (parse_pf_ip_netmask(aspec, &host, &port, &pf, masklength) != 0)
++   {
++      log_error(LOG_LEVEL_ERROR, "Invalid %s IP for (deny|permit)-access "
++                "directive in configuration file: \"%s\"", type, aspec);
++      string_append(proxy_args,"<br>\nWARNING: Invalid ");
++      string_append(proxy_args, type);
++      string_append(proxy_args," IP for (deny|permit)-access directive"
++                    " in configuration file: \"");
++      string_append(proxy_args, aspec);
++      string_append(proxy_args,"\"<br><br>\n");
++      return NULL;
+    }
+-   aca->port = port;
++   return resolve_hostname_to_ip(host, port, pf);
++}
+-   aca->addr = ntohl(resolve_hostname_to_ip(aspec));
++/*********************************************************************
++ *
++ * Function    :  add_one_to_acl_list
++ *
++ * Description :  Add one entry to an access_control_list.
++ *
++ * Parameters  :
++ *          0  :  l = the list to add to
++ *          1  :  action = ACL_DENY or ACL_PERMIT
++ *          2  :  src_addrs = the head of this list will be used as source
++ *                            in the ACL entry.
++ *          3  :  dst_addrs = the head of this list will be used as destination
++ *                            in the ACL entry.
++ *                            NULL for none
++ *          4  :  src_masklength = mask length for the source
++ *          5  :  src_masklength = mask length for the destination
++ *
++ * Returns     :  the new list
++ *
++ *********************************************************************/
++struct access_control_list *add_one_to_acl_list(struct access_control_list *l, short action,
++                                                addr_list *src_addrs, addr_list *dst_addrs,
++                                                int src_masklength, int dst_masklength)
++{
++   struct access_control_list *cur_acl;
++   /* allocate a new node */
++   cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
+-   if (aca->addr == INADDR_NONE)
++   if (cur_acl == NULL)
+    {
+-      return(-1);
++      log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
++      /* Never get here - LOG_LEVEL_FATAL causes program exit */
++      return l;
+    }
+-   /* build the netmask */
+-   aca->mask = 0;
+-   for (i=1; i <= masklength ; i++)
++   cur_acl->action = action;
++
++   cpy_head_addr_list(src_addrs, &cur_acl->src->addr, &cur_acl->src->addrlen);
++   fill_acl_addr_mask(cur_acl->src, src_masklength);
++   if (dst_addrs != NULL)
+    {
+-      aca->mask |= (1 << (32 - i));
++      cpy_head_addr_list(dst_addrs, &cur_acl->dst->addr, &cur_acl->dst->addrlen);
++      fill_acl_addr_mask(cur_acl->src, src_masklength);
+    }
+-   /* now mask off the host portion of the ip address
+-    * (i.e. save on the network portion of the address).
++   /*
++    * Add it to the list.  Note we reverse the list to get the
++    * behaviour the user expects.  With both the ACL and
++    * actions file, the last match wins.  However, the internal
++    * implementations are different:  The actions file is stored
++    * in the same order as the file, and scanned completely.
++    * With the ACL, we reverse the order as we load it, then
++    * when we scan it we stop as soon as we get a match.
+     */
+-   aca->addr = aca->addr & aca->mask;
++   cur_acl->next  = l;
+-   return(0);
++   return cur_acl;
++}
++
++/*********************************************************************
++ *
++ * Function    :  add_to_acl_list
++ *
++ * Description :  Add entries to an access_control_list.
++ *                Describe errors in *proxy_args.
++ *
++ * Parameters  :
++ *          0  :  l = the list to add to
++ *          1  :  action = ACL_DENY or ACL_PERMIT
++ *          2  :  src_spec = String giving the source of the acl entry
++ *          3  :  dst_spec = String giving the destination of the acl entry,
++ *                           or NULL
++ *          4  :  proxy_args = Pointer to string to append description of errors to.
++ *
++ * Returns     :  the new list
++ *
++ *********************************************************************/
++struct access_control_list *add_to_acl_list(struct access_control_list *l,
++                                            short action,
++                                            char *src_spec,
++                                            char *dst_spec,
++                                            char **proxy_args)
++{
++   int src_masklength, dst_masklength;
++   addr_list *src_addrs, *dst_addrs;
++   addr_list *src_addrs_remaining, *dst_addrs_remaining;
++
++   src_addrs = acl_addrs(src_spec, &src_masklength, proxy_args, "source");
++   if (is_nil_addr_list(src_addrs))
++   {
++      log_error(LOG_LEVEL_ERROR, "Source of ACL resolves to no address",dst_spec);
++      return l;
++   }
++   if (dst_spec != NULL)
++   {
++      dst_addrs = acl_addrs(dst_spec, &dst_masklength, proxy_args, "destination");
++      if (is_nil_addr_list(dst_addrs))
++      {
++         log_error(LOG_LEVEL_ERROR, "Destination of ACL resolves to no address",dst_spec);
++         destroy_addr_list(src_addrs);
++         return l;
++      }
++   }
++   else
++      dst_addrs = NULL;
++
++   for(src_addrs_remaining = src_addrs;
++       is_nil_addr_list(src_addrs);
++       src_addrs_remaining=tail_addr_list(src_addrs_remaining))
++   {
++      if (dst_addrs == NULL)
++         l = add_one_to_acl_list(l, action, src_addrs_remaining, NULL, src_masklength,0);
++      else for(dst_addrs_remaining = dst_addrs;
++               is_nil_addr_list(dst_addrs);
++               dst_addrs_remaining=tail_addr_list(dst_addrs_remaining))
++         l = add_one_to_acl_list(l, action, src_addrs_remaining, dst_addrs_remaining,
++                                 src_masklength, dst_masklength);
++   }
++   destroy_addr_list(src_addrs);
++   destroy_addr_list(dst_addrs);
++   return l;
+ }
+-#endif /* def FEATURE_ACL */
++#endif /* def FEATURE_ACL */
+ /*********************************************************************
+  *
+@@ -1048,7 +1324,7 @@
+  *********************************************************************/
+ struct http_response *redirect_url(struct client_state *csp)
+ {
+-   char *p, *q;
++   const char *p, *q;
+    struct http_response *rsp;
+    p = q = csp->http->path;
+diff -urNad privoxy~/filters.h privoxy/filters.h
+--- privoxy~/filters.h
++++ privoxy/filters.h
+@@ -15,6 +15,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and 
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -221,7 +224,11 @@
+  */
+ #ifdef FEATURE_ACL
+ extern int block_acl(struct access_control_addr *dst, struct client_state *csp);
+-extern int acl_addr(char *aspec, struct access_control_addr *aca);
++extern struct access_control_list *add_to_acl_list(struct access_control_list *l,
++                                                   short action,
++                                                   char *src_spec,
++                                                   char *dst_spec,
++                                                   char **proxy_args);
+ #endif /* def FEATURE_ACL */
+ extern int match_portlist(const char *portlist, int port);
+diff -urNad privoxy~/gateway.c privoxy/gateway.c
+--- privoxy~/gateway.c
++++ privoxy/gateway.c
+@@ -10,6 +10,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -199,12 +202,14 @@
+  * Returns     :  JB_INVALID_SOCKET => failure, else it is the socket file descriptor.
+  *
+  *********************************************************************/
+-jb_socket forwarded_connect(const struct forward_spec * fwd,
++jb_socket forwarded_connect(const struct forward_spec *fwd,
+                             struct http_request *http,
+                             struct client_state *csp)
+ {
+    const char * dest_host;
+-   int dest_port;
++   const char * dest_port_str;
++   unsigned long dest_port;
++   int dest_pf;
+    /* Figure out if we need to connect to the web server or a HTTP proxy. */
+    if (fwd->forward_host)
+@@ -212,19 +217,23 @@
+       /* HTTP proxy */
+       dest_host = fwd->forward_host;
+       dest_port = fwd->forward_port;
++      dest_port_str = fwd->forward_port_str;
++      dest_pf = fwd->forward_family;
+    }
+    else
+    {
+       /* Web server */
+       dest_host = http->host;
+       dest_port = http->port;
++      dest_port_str = http->port_str;
++      dest_pf = PF_UNSPEC;
+    }
+    /* Connect, maybe using a SOCKS proxy */
+    switch (fwd->type)
+    {
+       case SOCKS_NONE:
+-         return (connect_to(dest_host, dest_port, csp));
++         return (connect_to(dest_host, dest_port_str, dest_port, dest_pf, csp));
+       case SOCKS_4:
+       case SOCKS_4A:
+@@ -258,74 +267,19 @@
+  * Returns     :  JB_INVALID_SOCKET => failure, else a socket file descriptor.
+  *
+  *********************************************************************/
+-static jb_socket socks4_connect(const struct forward_spec * fwd,
+-                                const char * target_host,
+-                                int target_port,
+-                                struct client_state *csp)
++static jb_socket socks4_connect_one_ip(const struct forward_spec * fwd,
++                                       unsigned long web_server_addr,
++                                       int target_port,
++                                       struct client_state *csp,
++                                       size_t csiz,
++                                       char *cbuf)
+ {
+-   int web_server_addr;
+-   char cbuf[BUFFER_SIZE];
+    char sbuf[BUFFER_SIZE];
+-   struct socks_op    *c = (struct socks_op    *)cbuf;
+    struct socks_reply *s = (struct socks_reply *)sbuf;
+-   size_t n;
+-   size_t csiz;
++   struct socks_reply *c = (struct socks_reply *)cbuf;
+    jb_socket sfd;
+-   int err = 0;
+    char *errstr;
+-   if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
+-   {
+-      log_error(LOG_LEVEL_CONNECT, "socks4_connect: NULL gateway host specified");
+-      err = 1;
+-   }
+-
+-   if (fwd->gateway_port <= 0)
+-   {
+-      log_error(LOG_LEVEL_CONNECT, "socks4_connect: invalid gateway port specified");
+-      err = 1;
+-   }
+-
+-   if (err)
+-   {
+-      errno = EINVAL;
+-      return(JB_INVALID_SOCKET);
+-   }
+-
+-   /* build a socks request for connection to the web server */
+-
+-   strcpy((char *)&(c->userid), socks_userid);
+-
+-   csiz = sizeof(*c) + sizeof(socks_userid) - 1;
+-
+-   switch (fwd->type)
+-   {
+-      case SOCKS_4:
+-         web_server_addr = htonl(resolve_hostname_to_ip(target_host));
+-         if (web_server_addr == INADDR_NONE)
+-         {
+-            log_error(LOG_LEVEL_CONNECT, "socks4_connect: could not resolve target host %s", target_host);
+-            return(JB_INVALID_SOCKET);
+-         }
+-         break;
+-      case SOCKS_4A:
+-         web_server_addr = 0x00000001;
+-         n = csiz + strlen(target_host) + 1;
+-         if (n > sizeof(cbuf))
+-         {
+-            errno = EINVAL;
+-            return(JB_INVALID_SOCKET);
+-         }
+-         strcpy(cbuf + csiz, target_host);
+-         csiz = n;
+-         break;
+-      default:
+-         /* Should never get here */
+-         log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type.");
+-         errno = EINVAL;
+-         return(JB_INVALID_SOCKET);
+-   }
+-
+    c->vn          = 4;
+    c->cd          = 1;
+    c->dstport[0]  = (target_port       >> 8  ) & 0xff;
+@@ -336,7 +290,7 @@
+    c->dstip[3]    = (web_server_addr         ) & 0xff;
+    /* pass the request to the socks server */
+-   sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
++   sfd = connect_to(fwd->gateway_host, fwd->gateway_port_str, fwd->gateway_port, PF_INET ,csp);
+    if (sfd == JB_INVALID_SOCKET)
+    {
+@@ -391,6 +345,92 @@
+ }
++static jb_socket socks4_connect(const struct forward_spec * fwd,
++                                const char * target_host,
++                                int target_port,
++                                struct client_state *csp)
++{
++   char cbuf[BUFFER_SIZE];
++   struct socks_op    *c = (struct socks_op    *)cbuf;
++   size_t csiz;
++   int err = 0;
++
++   /**
++    * SOCKS4 is IPv4-specific. At least I think so.
++    */
++   if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
++   {
++      log_error(LOG_LEVEL_CONNECT, "socks4_connect: NULL gateway host specified");
++      err = 1;
++   }
++
++   if (fwd->gateway_port <= 0)
++   {
++      log_error(LOG_LEVEL_CONNECT, "socks4_connect: invalid gateway port specified");
++      err = 1;
++   }
++
++   if (err)
++   {
++      errno = EINVAL;
++      return(JB_INVALID_SOCKET);
++   }
++
++   /* build a socks request for connection to the web server */
++
++   strcpy((char *)&(c->userid), socks_userid);
++
++   csiz = sizeof(*c) + sizeof(socks_userid) - 1;
++
++   switch (fwd->type)
++   {
++      case SOCKS_4:
++      {
++         addr_list *web_server_addrs = resolve_hostname_to_ip(target_host,NULL,PF_INET);
++         jb_socket return_value = JB_INVALID_SOCKET;
++         if (is_nil_addr_list(web_server_addrs))
++         {
++            log_error(LOG_LEVEL_CONNECT, "socks4_connect: could not resolve target host %s", target_host);
++         }
++         else
++         {
++            addr_list *addrs_to_try;
++            
++            for(addrs_to_try = web_server_addrs;
++                !is_nil_addr_list(addrs_to_try);
++                addrs_to_try = tail_addr_list(addrs_to_try))
++            {
++               const unsigned long web_server_addr = ((struct sockaddr_in*) head_addr_list(addrs_to_try))->sin_addr.s_addr;
++               return_value=socks4_connect_one_ip(fwd, web_server_addr, target_port, csp, csiz, cbuf);
++               if(return_value != JB_INVALID_SOCKET)
++                  break;
++            }
++         }
++         destroy_addr_list(web_server_addrs);
++         return return_value;
++         break;
++      }
++      case SOCKS_4A:
++      {
++         size_t n;
++         n = csiz + strlen(target_host) + 1;
++         if (n > sizeof(cbuf))
++         {
++            errno = EINVAL;
++            return(JB_INVALID_SOCKET);
++         }
++         strcpy(cbuf + csiz, target_host);
++         csiz = n;
++         return socks4_connect_one_ip(fwd, 0x00000001, target_port, csp, csiz, cbuf);
++         break;
++      }
++      default:
++         /* Should never get here */
++         log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type.");
++         errno = EINVAL;
++         return(JB_INVALID_SOCKET);
++   }
++}
+ /*
+   Local Variables:
+diff -urNad privoxy~/jb_socket_set.c privoxy/jb_socket_set.c
+--- privoxy~/jb_socket_set.c
++++ privoxy/jb_socket_set.c
+@@ -0,0 +1,174 @@
++const char jb_socket_set_rcs[] = "$Id: $";
++/*********************************************************************
++ *
++ * File        :  $Source: $
++ *
++ * Purpose     :  Declares functions to handle sets of sockets
++ *
++ * Copyright   :  Written by and Copyright (C) 2002 Lionel Elie Mamane
++ *                <lionel@mamane.lu>
++ *
++ *                This program is free software; you can redistribute it
++ *                and/or modify it under the terms of the GNU General
++ *                Public License as published by the Free Software
++ *                Foundation; either version 2 of the License, or (at
++ *                your option) any later version.
++ *
++ *                This program is distributed in the hope that it will
++ *                be useful, but WITHOUT ANY WARRANTY; without even the
++ *                implied warranty of MERCHANTABILITY or FITNESS FOR A
++ *                PARTICULAR PURPOSE.  See the GNU General Public
++ *                License for more details.
++ *
++ *                The GNU General Public License should be included with
++ *                this file.  If not, you can view it at
++ *                http://www.gnu.org/copyleft/gpl.html
++ *                or write to the Free Software Foundation, Inc., 59
++ *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ *
++ * Revisions   :
++ *    $Log: jb_socket_set.c,v $
++ *
++ *********************************************************************/
++
++#include "jb_socket_set.h"
++#include <sys/time.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include "project.h"
++
++/*********************************************************************
++ *
++ * Function    :  jb_socket_set_add
++ *
++ * Description :  Add a socket to the set
++ *
++ * Parameters  :
++ *          0  :  l = the set to add to
++ *          1  :  the elemen to add to the set
++ *
++ * Returns     :  0 on success
++ *                non-0 on failure
++ *
++ *********************************************************************/
++
++int jb_socket_set_add(jb_socket_set *l, jb_socket e)
++{
++   if (l->data==NULL)
++   {
++      l->size=2;
++      l->data=malloc(l->size * sizeof(jb_socket));
++      if (l->data==NULL)
++      {
++         l->size = 0;
++         return -1;
++      }
++      l->occupied=0;
++   }
++   l->data[(l->occupied)++] = e;
++   if (l->occupied == l->size)
++   {
++      jb_socket *new_data;
++      l->size *= 2;
++      new_data = realloc(l->data,l->size * sizeof(jb_socket));
++      if (new_data == NULL)
++      {
++         /* Not enough memory to continue. Cancel changes. */
++         l->data[--(l->occupied)] = JB_INVALID_SOCKET;
++         l->size /= 2;
++         return -1;
++      }
++      l->data = new_data;
++   }
++   l->data[l->occupied] = JB_INVALID_SOCKET;
++   return 0;
++}
++
++/*********************************************************************
++ *
++ * Function    :  destroy_jb_socket_set
++ *
++ * Description :  Unallocate memory allocated to a socket set
++ *
++ * Parameters  :
++ *          0  :  l = the set to unallocate
++ *
++ * Returns     :  nothing
++ *
++ *********************************************************************/
++void destroy_jb_socket_set(jb_socket_set *l)
++{
++   free(l->data);
++   init_jb_socket_set(l);
++}
++/*********************************************************************
++ *
++ * Function    :  is_empty_jb_socket_set
++ *
++ * Description :  Test wheter a set is empty
++ *
++ * Parameters  :
++ *          0  :  l = the set to test
++ *
++ * Returns     :  0             = false if set has a head,
++ *                anything else = true if set is nil
++ *
++ *********************************************************************/
++int is_nil_jb_socket_set(jb_socket_set *l)
++{
++   return (l->occupied == 0);
++}
++
++/*********************************************************************
++ *
++ * Function    :  init_jb_socket_set
++ *
++ * Description :  Init a set to empty
++ *
++ * Parameters  :
++ *          0  :  l = the set to init
++ *
++ *********************************************************************/
++void init_jb_socket_set(jb_socket_set *l)
++{
++   l->data=NULL;
++   l->size=0;
++   l->occupied=0;
++}
++
++/*********************************************************************
++ *
++ * Function    :  jb_socket_set_iteration_begin
++ *
++ * Description :  Return an iterator on the set
++ *
++ * Parameters  :
++ *          0  :  l = the set
++ *
++ *********************************************************************/
++jb_socket_set_iterate_state jb_socket_set_iteration_begin(jb_socket_set *l)
++{
++   return l->data;
++}
++
++/*********************************************************************
++ *
++ * Function    :  jb_socket_set_iteration_next
++ *
++ * Description :  Return value pointed to by iterator and step
++ *                iterator to next position
++ *
++ * Parameters  :
++ *          0  :  s = the iterator
++ *
++ *********************************************************************/
++jb_socket jb_socket_set_iteration_next(jb_socket_set_iterate_state*s)
++{
++   return(*((*s)++));
++}
++
++/*
++  Local Variables:
++  tab-width: 3
++  end:
++*/
+diff -urNad privoxy~/jb_socket_set.h privoxy/jb_socket_set.h
+--- privoxy~/jb_socket_set.h
++++ privoxy/jb_socket_set.h
+@@ -0,0 +1,71 @@
++#ifndef JB_SOCKET_SET_H_INCLUDED
++#define JB_SOCKET_SET_H_INCLUDED
++#define JB_SOCKET_SET_H_VERSION "$Id: $"
++/*********************************************************************
++ *
++ * File        :  $Source: $
++ *
++ * Purpose     :  Declares functions to handle sets of sockets
++ *
++ * Copyright   :  Written by and Copyright (C) 2002 Lionel Elie Mamane
++ *                <lionel@mamane.lu>
++ *
++ *                This program is free software; you can redistribute it
++ *                and/or modify it under the terms of the GNU General
++ *                Public License as published by the Free Software
++ *                Foundation; either version 2 of the License, or (at
++ *                your option) any later version.
++ *
++ *                This program is distributed in the hope that it will
++ *                be useful, but WITHOUT ANY WARRANTY; without even the
++ *                implied warranty of MERCHANTABILITY or FITNESS FOR A
++ *                PARTICULAR PURPOSE.  See the GNU General Public
++ *                License for more details.
++ *
++ *                The GNU General Public License should be included with
++ *                this file.  If not, you can view it at
++ *                http://www.gnu.org/copyleft/gpl.html
++ *                or write to the Free Software Foundation, Inc., 59
++ *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ *
++ * Revisions   :
++ *    $Log: jb_socket_set.h,v $
++ *
++ *********************************************************************/
++#include <sys/time.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include "project.h"
++
++struct jb_socket_set_struct
++{
++   unsigned int size; /* size allocated*/
++   unsigned int occupied; /* size occupied - 1 == index of final JB_INVALID_SOCKET
++                             == number of sockets in the set */
++   jb_socket *data; /* Array containing the sockets, JB_INVALID_SOCKET-terminated */
++};
++
++typedef struct jb_socket_set_struct jb_socket_set;
++typedef const jb_socket* jb_socket_set_iterate_state;
++
++void init_jb_socket_set(jb_socket_set*);
++
++jb_socket_set_iterate_state jb_socket_set_iteration_begin(jb_socket_set *);
++jb_socket jb_socket_set_iteration_next(jb_socket_set_iterate_state*);
++
++int jb_socket_set_add(jb_socket_set*, jb_socket);
++
++void destroy_jb_socket_set(jb_socket_set *l);
++int is_empty_jb_socket_set(jb_socket_set *l);
++
++#define freez_jb_socket_set(X)  { if(X) { destroy_jb_socket_set(X); X = NULL ; } }
++
++int jb_select(jb_socket_set *readfds, jb_socket_set *writefds, jb_socket_set *exceptfds, struct timeval *timeout);
++
++#endif /* ndef JB_SOCKET_SET_H_INCLUDED */
++
++/*
++  Local Variables:
++  tab-width: 3
++  end:
++*/
+diff -urNad privoxy~/jbsockets.c privoxy/jbsockets.c
+--- privoxy~/jbsockets.c
++++ privoxy/jbsockets.c
+@@ -11,6 +11,10 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8-9 December 2002, 24 January 2003,
++ *                13 February 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and 
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -258,6 +262,7 @@
+ #include "jbsockets.h"
+ #include "filters.h"
+ #include "errlog.h"
++#include "addrlist.h"
+ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION;
+@@ -270,141 +275,194 @@
+  *                that this is allowed according to ACL.
+  *
+  * Parameters  :
+- *          1  :  host = hostname to connect to
+- *          2  :  portnum = port to connent on
++ *          0  :  host = hostname to connect to
++ *          1  :  port = port to connect on, as string
++ *          2  :  portnum = port to connect on, as integer
+  *          3  :  csp = Current client state (buffers, headers, etc...)
+- *                      Not modified, only used for source IP and ACL.
+  *
+  * Returns     :  JB_INVALID_SOCKET => failure, else it is the socket
+  *                file descriptor.
+  *
+  *********************************************************************/
+-jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
++jb_socket connect_to_one_ip(struct sockaddr_storage *addr, size_t addrlen, const char *host, unsigned long portnum, struct client_state *csp)
+ {
+-   struct sockaddr_in inaddr;
+-   jb_socket fd;
+-   int addr;
+-   fd_set wfds;
+-   struct timeval tv[1];
+-#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
+-   int   flags;
+-#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
+-
+ #ifdef FEATURE_ACL
+-   struct access_control_addr dst[1];
+-#endif /* def FEATURE_ACL */
+-
+-   memset((char *)&inaddr, 0, sizeof inaddr);
+-
+-   if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE)
++   if (csp)
+    {
+-      csp->http->host_ip_addr_str = strdup("unknown");
+-      return(JB_INVALID_SOCKET);
+-   }
++      struct access_control_addr dst[1];
++      char hostname[NI_MAXHOST];
++      char port[NI_MAXSERV];
++      if (getnameinfo((struct sockaddr*)addr, addrlen, hostname, NI_MAXHOST,
++                      port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
++      {
++         log_error(LOG_LEVEL_ERROR, "connect: Could not get string address and port back from sockaddr because %E");
++         strncpy(hostname,"unknown",NI_MAXHOST);
++         strncpy(port,"unknown",NI_MAXSERV);
++      }
+-#ifdef FEATURE_ACL
+-   dst->addr = ntohl((unsigned long) addr);
+-   dst->port = portnum;
++      csp->http->host_ip_addr_str = strdup(hostname);
+-   if (block_acl(dst, csp))
+-   {
++      dst->addr = *addr;
++      dst->addrlen = addrlen;
++      dst->port = portnum;
++
++      if (block_acl(dst, csp))
++      {
+ #ifdef __OS2__
+-      errno = SOCEPERM;
++         errno = SOCEPERM;
+ #else
+-      errno = EPERM;
++         errno = EPERM;
+ #endif
+-      return(JB_INVALID_SOCKET);
+-   }
+-#endif /* def FEATURE_ACL */
++         return(JB_INVALID_SOCKET);
++      }
+-   inaddr.sin_addr.s_addr = addr;
+-   inaddr.sin_family      = AF_INET;
+-   csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr));
+-#ifndef _WIN32
+-   if (sizeof(inaddr.sin_port) == sizeof(short))
+-#endif /* ndef _WIN32 */
+-   {
+-      inaddr.sin_port = htons((unsigned short) portnum);
+    }
+-#ifndef _WIN32
+-   else
++#endif /* def FEATURE_ACL */
++
+    {
+-      inaddr.sin_port = htonl((unsigned long)portnum);
+-   }
+-#endif /* ndef _WIN32 */
++      jb_socket fd;
+ #ifdef _WIN32
+-   if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) == JB_INVALID_SOCKET)
++      if ((fd = socket(addr->ss_family, SOCK_STREAM, 0)) == JB_INVALID_SOCKET)
+ #else
+-   if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) < 0)
++      if ((fd = socket(addr->ss_family, SOCK_STREAM, 6)) < 0)
+ #endif
+-   {
+-      return(JB_INVALID_SOCKET);
+-   }
++      {
++         return(JB_INVALID_SOCKET);
++      }
+ #ifdef TCP_NODELAY
+-   {  /* turn off TCP coalescence */
+-      int mi = 1;
+-      setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int));
+-   }
++      {  /* turn off TCP coalescence */
++         int mi = 1;
++         setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int));
++      }
+ #endif /* def TCP_NODELAY */
+ #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
+-   if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
+-   {
+-      flags |= O_NDELAY;
+-      fcntl(fd, F_SETFL, flags);
+-   }
++      {
++         int flags;
++
++         if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
++         {
++            flags |= O_NDELAY;
++            fcntl(fd, F_SETFL, flags);
++         }
++      }
+ #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
+-   while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET)
+-   {
++      while (connect(fd, (struct sockaddr *) addr, addrlen) == JB_INVALID_SOCKET)
++      {
+ #ifdef _WIN32
+-      if (errno == WSAEINPROGRESS)
++         if (errno == WSAEINPROGRESS)
+ #elif __OS2__ 
+-      if (sock_errno() == EINPROGRESS)
++         if (sock_errno() == EINPROGRESS)
+ #else /* ifndef _WIN32 */
+-      if (errno == EINPROGRESS)
++         if (errno == EINPROGRESS)
+ #endif /* ndef _WIN32 || __OS2__ */
+-      {
+-         break;
+-      }
++         {
++            break;
++         }
+ #ifdef __OS2__ 
+-      if (sock_errno() != EINTR)
++         if (sock_errno() != EINTR)
+ #else
+-      if (errno != EINTR)
++         if (errno != EINTR)
+ #endif /* __OS2__ */
++         {
++            close_socket(fd);
++            return(JB_INVALID_SOCKET);
++         }
++      }
++
++#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
+       {
+-         close_socket(fd);
+-         return(JB_INVALID_SOCKET);
++         int flags;
++         if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
++         {
++            flags &= ~O_NDELAY;
++            fcntl(fd, F_SETFL, flags);
++         }
+       }
+-   }
++#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
++
++      {
++         fd_set wfds;
++         struct timeval tv[1];
++
++         /* wait for connection to complete */
++         FD_ZERO(&wfds);
++         FD_SET(fd, &wfds);
++         tv->tv_sec  = 30;
++         tv->tv_usec = 0;
++
++         /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */
++         if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
++         {
++            close_socket(fd);
++            return(JB_INVALID_SOCKET);
++         }
+ #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
+-   if (flags != -1)
+-   {
+-      flags &= ~O_NDELAY;
+-      fcntl(fd, F_SETFL, flags);
+-   }
++         else
++         {
++            int connect_result;
++            socklen_t connect_result_len = sizeof connect_result;
++
++            if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &connect_result, &connect_result_len) != 0)
++            {
++               log_error(LOG_LEVEL_ERROR, "Could not determine whether connection to %s port %d was successful because %E. Assuming failure.",
++                         csp->http->host_ip_addr_str, portnum);
++               close_socket(fd);
++               return(JB_INVALID_SOCKET);
++            }
++            else if( connect_result != 0 )
++            {
++               log_error(LOG_LEVEL_CONNECT, "Connection to %s port %d failed because %s.",
++                         csp->http->host_ip_addr_str, portnum, strerror(connect_result));
++               close_socket(fd);
++               return(JB_INVALID_SOCKET);
++            }
++         }
+ #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
++      }
++      return(fd);
++   }
++}
+-   /* wait for connection to complete */
+-   FD_ZERO(&wfds);
+-   FD_SET(fd, &wfds);
++jb_socket connect_to(const char *host, const char *port, unsigned long portnum, int pf, struct client_state *csp)
++{
++   jb_socket fd = JB_INVALID_SOCKET;
++   struct sockaddr_storage addr;
++   addr_list *addrs, *addrs_to_try;
+-   tv->tv_sec  = 30;
+-   tv->tv_usec = 0;
++   addrs = resolve_hostname_to_ip(host,port,pf);
+-   /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */
+-   if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
++   if (is_nil_addr_list(addrs))
+    {
+-      close_socket(fd);
+-      return(JB_INVALID_SOCKET);
++      errno = EINVAL;
++      return fd;
+    }
+-   return(fd);
++   for(addrs_to_try=addrs;
++       !is_nil_addr_list(addrs_to_try);
++       addrs_to_try = tail_addr_list(addrs_to_try))
++   {
++      size_t addrlen;
++      memset((char *)&addr, 0, sizeof addr);
++      cpy_head_addr_list(addrs_to_try, &addr,&addrlen);
++      fd = connect_to_one_ip(&addr, addrlen, host, portnum, csp);
++      if (fd != JB_INVALID_SOCKET)
++         break;
++   }
++
++   if (fd == JB_INVALID_SOCKET)
++   {
++      csp->http->host_ip_addr_str = strdup("unknown");
++   }
++
++   destroy_addr_list(addrs);
++   return fd;
+ }
+@@ -544,55 +602,30 @@
+ /*********************************************************************
+  *
+- * Function    :  bind_port
++ * Function    :  bind_port_one_ip
+  *
+  * Description :  Call socket, set socket options, and listen.
+- *                Called by listen_loop to "boot up" our proxy address.
+  *
+  * Parameters  :
+- *          1  :  hostnam = TCP/IP address to bind/listen to
+- *          2  :  portnum = port to listen on
+- *          3  :  pfd = pointer used to return file descriptor.
++ *          0  :  addr = TCP/IP address and port to bind/listen to
++ *          1  :  fds = jb_socket_set where the new socket should go
+  *
+- * Returns     :  if success, returns 0 and sets *pfd.
++ * Returns     :  if success returns 0 and adds sockets to fds.
+  *                if failure, returns -3 if address is in use,
+- *                                    -2 if address unresolvable,
++ *                                    -2 if memory error
+  *                                    -1 otherwise
+  *********************************************************************/
+-int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
++int bind_port_one_ip(struct sockaddr *addr, const socklen_t addr_len, jb_socket_set *fds)
+ {
+-   struct sockaddr_in inaddr;
+    jb_socket fd;
++   int flags;
+ #ifndef _WIN32
+    int one = 1;
+ #endif /* ndef _WIN32 */
+-   *pfd = JB_INVALID_SOCKET;
+-
+-   memset((char *)&inaddr, '\0', sizeof inaddr);
+-
+-   inaddr.sin_family      = AF_INET;
+-   inaddr.sin_addr.s_addr = resolve_hostname_to_ip(hostnam);
+-
+-   if (inaddr.sin_addr.s_addr == INADDR_NONE)
+-   {
+-      return(-2);
+-   }
+-
+-#ifndef _WIN32
+-   if (sizeof(inaddr.sin_port) == sizeof(short))
+-#endif /* ndef _WIN32 */
+-   {
+-      inaddr.sin_port = htons((unsigned short) portnum);
+-   }
+-#ifndef _WIN32
+-   else
+-   {
+-      inaddr.sin_port = htonl((unsigned long) portnum);
+-   }
+-#endif /* ndef _WIN32 */
++   fd = JB_INVALID_SOCKET;
+-   fd = socket(AF_INET, SOCK_STREAM, 0);
++   fd = socket(addr->sa_family, SOCK_STREAM, 6);
+ #ifdef _WIN32
+    if (fd == JB_INVALID_SOCKET)
+@@ -618,8 +651,17 @@
+     */
+    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
+ #endif /* ndef _WIN32 */
+-
+-   if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0)
++   /* As we are now listening on more than one socket,
++    * this is important: The only way to be sure accept
++    * won't block!!
++    */
++   if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
++   {
++      flags |= O_NONBLOCK;
++      fcntl(fd, F_SETFL, flags);
++   }
++   
++   if (bind (fd, addr, addr_len) < 0)
+    {
+ #ifdef _WIN32
+       errno = WSAGetLastError();
+@@ -638,7 +680,7 @@
+       }
+    }
+-   while (listen(fd, 5) == -1)
++   while (listen(fd, 25) == -1)
+    {
+       if (errno != EINTR)
+       {
+@@ -646,7 +688,11 @@
+       }
+    }
+-   *pfd = fd;
++   if (jb_socket_set_add(fds,fd) != 0)
++   {
++      close_socket(fd);
++      return -2;
++   }
+    return 0;
+ }
+@@ -654,6 +700,91 @@
+ /*********************************************************************
+  *
++ * Function    :  bind_port
++ *
++ * Description :  Call bind_port_one_ip on all addresses host resolves to
++ *                Called by listen_loop to "boot up" our proxy address.
++ *
++ * Parameters  :
++ *          0  :  host = TCP/IP hostname to bind/listen to
++ *          1  :  port = port to listen to, as string
++ *          2  :  fds = socket set the sockets should be added to
++ *
++ * Returns     :  if success on at least one address resolving from hostnam,
++ *                 returns 0 and adds sockets to fds.
++ *                if failure, returns non-zero
++ *********************************************************************/
++int bind_port(const char *host, const char *port, int pf, jb_socket_set *fds)
++{
++   int result;
++   int failure = 1;
++   struct sockaddr_storage addr;
++   struct sockaddr * const addr_addr = (struct sockaddr *)&addr;
++   addr_list *addrs, *addrs_to_try;
++
++   const char * const log_host = (host != NULL) ? host : "ADDR_ANY";
++
++   addrs = resolve_hostname_to_ip(host,port,pf);
++
++   if (is_nil_addr_list(addrs))
++      log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s: " 
++                "Name resolution didn't give any address",
++                log_host, port);
++
++   log_error(LOG_LEVEL_INFO, "Binding to %s:%s...",  log_host, port);
++
++   for(addrs_to_try=addrs;
++       !is_nil_addr_list(addrs_to_try);
++       addrs_to_try = tail_addr_list(addrs_to_try))
++   {
++      char numeric_hostname[NI_MAXHOST];
++      char numeric_port[NI_MAXSERV];
++      size_t addrlen;
++      memset((char *)addr_addr, 0, sizeof addr);
++      cpy_head_addr_list(addrs_to_try, &addr, &addrlen);
++      result = getnameinfo(addr_addr, addrlen, numeric_hostname, NI_MAXHOST,
++                           numeric_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
++      if (result != 0)
++      {
++         log_error(LOG_LEVEL_ERROR, "bind: Could not get string address and port back from sockaddr because %E");
++         strncpy(numeric_hostname,"unknown",NI_MAXHOST);
++         strncpy(numeric_port,"unknown",NI_MAXSERV);
++      }
++      result = bind_port_one_ip(addr_addr, addrlen, fds);
++      if( result == 0 )
++      {
++         failure = 0;
++         log_error(LOG_LEVEL_INFO, "Successfully bound to %s:%s", numeric_hostname, numeric_port);
++      }
++      else
++      {
++         switch(result)
++         {
++         case -3 :
++            log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s (%s:%s): "
++                      "There may be another Privoxy or some other "
++                      "proxy running on port %s",
++                      log_host, port, numeric_hostname, numeric_port, numeric_port);
++                              break;
++         case -2 :
++            log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s (%s:%s): "
++                      "Out of memory",
++                      log_host, port, numeric_hostname, numeric_port);
++                              break;
++         default :
++            log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s: because %E",
++                      log_host, numeric_port);
++         }
++      }
++   }
++
++   destroy_addr_list(addrs);
++   return failure;
++}
++
++
++/*********************************************************************
++ *
+  * Function    :  accept_connection
+  *
+  * Description :  Accepts a connection on a socket.  Socket must have
+@@ -670,8 +801,7 @@
+  *********************************************************************/
+ int accept_connection(struct client_state * csp, jb_socket fd)
+ {
+-   struct sockaddr_in client, server;
+-   struct hostent *host = NULL;
++   struct sockaddr_storage client, server;
+    jb_socket afd;
+ #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA)
+    /* Wierdness - fix a warning. */
+@@ -679,15 +809,7 @@
+ #else
+    socklen_t c_length, s_length;
+ #endif
+-#if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) ||  defined(HAVE_GETHOSTBYADDR_R_7_ARGS) || defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
+-   struct hostent result;
+-#if defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
+-   struct hostent_data hdata;
+-#else
+-   char hbuf[HOSTENT_BUFFER_SIZE];
+-   int thd_err;
+-#endif /* def HAVE_GETHOSTBYADDR_R_5_ARGS */
+-#endif /* def HAVE_GETHOSTBYADDR_R_(8|7|5)_ARGS */
++   int flags;
+    c_length = s_length = sizeof(client);
+@@ -707,6 +829,12 @@
+       return 0;
+    }
+ #endif
++   /* If we inherited O_NONBLOCK from the listening fd, unset it */
++   if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
++   {
++      flags &= ~O_NONBLOCK;
++      fcntl(fd, F_SETFL, flags);
++   }
+    /* 
+     * Determine the IP-Adress that the client used to reach us
+@@ -714,49 +842,50 @@
+     */
+    if (!getsockname(afd, (struct sockaddr *) &server, &s_length))
+    {
+-      csp->my_ip_addr_str = strdup(inet_ntoa(server.sin_addr));
+-#if defined(HAVE_GETHOSTBYADDR_R_8_ARGS)
+-      gethostbyaddr_r((const char *)&server.sin_addr,
+-                      sizeof(server.sin_addr), AF_INET,
+-                      &result, hbuf, HOSTENT_BUFFER_SIZE,
+-                      &host, &thd_err);
+-#elif defined(HAVE_GETHOSTBYADDR_R_7_ARGS)
+-      host = gethostbyaddr_r((const char *)&server.sin_addr,
+-                      sizeof(server.sin_addr), AF_INET,
+-                      &result, hbuf, HOSTENT_BUFFER_SIZE, &thd_err);
+-#elif defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
+-      if (0 == gethostbyaddr_r((const char *)&server.sin_addr,
+-                               sizeof(server.sin_addr), AF_INET,
+-                               &result, &hdata))
++      char hostname[NI_MAXHOST];
++      char port[NI_MAXSERV];
++
++      if (getnameinfo((struct sockaddr *)&server, s_length, hostname, NI_MAXHOST,
++                      port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+       {
+-         host = &result;
++         log_error(LOG_LEVEL_ERROR, "accept: Could not get string address and port back from server sockaddr because %E");
++         strncpy(hostname,"unknown IP",NI_MAXHOST);
++         strncpy(port,"unknown port",NI_MAXSERV);
+       }
+-      else
++      csp->my_ip_addr_str = strdup(hostname);
++      csp->my_port_str = strdup(port);
++
++      if (getnameinfo((struct sockaddr *)&server, s_length, hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD) != 0)
+       {
+-         host = NULL;
++         log_error(LOG_LEVEL_ERROR, "accept: Could not get my own hostname because %E");
++         strncpy(hostname,"unknown host",NI_MAXHOST);
+       }
+-#elif defined(OSX_DARWIN)
+-      pthread_mutex_lock(&gethostbyaddr_mutex);
+-      host = gethostbyaddr((const char *)&server.sin_addr, 
+-                           sizeof(server.sin_addr), AF_INET);
+-      pthread_mutex_unlock(&gethostbyaddr_mutex);
+-#else
+-      host = gethostbyaddr((const char *)&server.sin_addr, 
+-                           sizeof(server.sin_addr), AF_INET);
+-#endif
+-      if (host == NULL)
++      csp->my_hostname = strdup(hostname);
++   }
++   else
++   {
++      log_error(LOG_LEVEL_ERROR, "accept: Could not get sockaddr from socket fd because %E");
++   }
++
++   csp->cfd    = afd;
++   {
++      char hostname[NI_MAXHOST];
++
++      if (getnameinfo((struct sockaddr *)&client, c_length, hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0)
+       {
+-         log_error(LOG_LEVEL_ERROR, "Unable to get my own hostname: %E\n");
++         log_error(LOG_LEVEL_ERROR, "accept: Could not get client IP address string because %E");
++         strncpy(hostname,"unknown IP",NI_MAXHOST);
+       }
+-      else
++      csp->my_ip_addr_str = strdup(hostname);
++
++      if (getnameinfo((struct sockaddr *)&server, s_length, hostname, NI_MAXHOST, NULL, 0, 0) != 0)
+       {
+-         csp->my_hostname = strdup(host->h_name);
++         log_error(LOG_LEVEL_ERROR, "accept: Could not get my own hostname because %E");
++         strncpy(hostname,"unknown host",NI_MAXHOST);
+       }
++      csp->ip_addr_str = strdup(hostname);
+    }
+-
+-   csp->cfd    = afd;
+-   csp->ip_addr_str  = strdup(inet_ntoa(client.sin_addr));
+-   csp->ip_addr_long = ntohl(client.sin_addr.s_addr);
++   csp->ip_addr_addr = client;
+    return 1;
+@@ -767,90 +896,41 @@
+  *
+  * Function    :  resolve_hostname_to_ip
+  *
+- * Description :  Resolve a hostname to an internet tcp/ip address.
+- *                NULL or an empty string resolve to INADDR_ANY.
++ * Description :  Resolve a hostname to a list of internet tcp/ip addresses.
+  *
+  * Parameters  :
+- *          1  :  host = hostname to resolve
++ *          0  :  host = hostname to resolve
++ *          1  :  result = where to store the result
++ *          2  :  pf = preferred address family. PF_UNSPEC for no preference (recommended).
+  *
+- * Returns     :  INADDR_NONE => failure, INADDR_ANY or tcp/ip address if succesful.
++ * Returns     :  A (possibly empty) list of adresses
+  *
+  *********************************************************************/
+-unsigned long resolve_hostname_to_ip(const char *host)
++addr_list *resolve_hostname_to_ip(const char *host, const char *port, int pf)
+ {
+-   struct sockaddr_in inaddr;
+-   struct hostent *hostp;
+-#if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS)
+-   struct hostent result;
+-#if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
+-   char hbuf[HOSTENT_BUFFER_SIZE];
+-   int thd_err;
+-#else /* defined(HAVE_GETHOSTBYNAME_R_3_ARGS) */
+-   struct hostent_data hdata;
+-#endif /* def HAVE_GETHOSTBYNAME_R_(6|5)_ARGS */
+-#endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */
+-
+-   if ((host == NULL) || (*host == '\0'))
+-   {
+-      return(INADDR_ANY);
+-   }
+-
+-   memset((char *) &inaddr, 0, sizeof inaddr);
++   /* TODO 
++    * Do all supported platforms have "getaddrinfo"?
++    */
++    
++   struct addrinfo hints, *res0;
++   int result;
++   memset(&hints, 0, sizeof(hints));
++   hints.ai_family = pf;
++   hints.ai_socktype = SOCK_STREAM;
+-   if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1)
++   result = getaddrinfo(host, port, &hints, &res0);
++   if ( result != 0 )
+    {
+-#if defined(HAVE_GETHOSTBYNAME_R_6_ARGS)
+-      gethostbyname_r(host, &result, hbuf,
+-                      HOSTENT_BUFFER_SIZE, &hostp, &thd_err);
+-#elif defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
+-      hostp = gethostbyname_r(host, &result, hbuf,
+-                      HOSTENT_BUFFER_SIZE, &thd_err);
+-#elif defined(HAVE_GETHOSTBYNAME_R_3_ARGS)
+-      if (0 == gethostbyname_r(host, &result, &hdata))
+-      {
+-         hostp = &result;
+-      }
+-      else
+-      {
+-         hostp = NULL;
+-      }
+-#elif OSX_DARWIN
+-      pthread_mutex_lock(&gethostbyname_mutex);
+-      hostp = gethostbyname(host);
+-      pthread_mutex_unlock(&gethostbyname_mutex);
+-#else
+-      hostp = gethostbyname(host);
+-#endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */
+-      /*
+-       * On Mac OSX, if a domain exists but doesn't have a type A
+-       * record associated with it, the h_addr member of the struct
+-       * hostent returned by gethostbyname is NULL, even if h_length
+-       * is 4. Therefore the second test below.
+-       */
+-      if (hostp == NULL || hostp->h_addr == NULL)
+-      {
+-         errno = EINVAL;
+-         log_error(LOG_LEVEL_ERROR, "could not resolve hostname %s", host);
+-         return(INADDR_NONE);
+-      }
+-      if (hostp->h_addrtype != AF_INET)
+-      {
+-#ifdef _WIN32
+-         errno = WSAEPROTOTYPE;
+-#else
+-         errno = EPROTOTYPE;
+-#endif 
+-         log_error(LOG_LEVEL_ERROR, "hostname %s resolves to unknown address type.", host);
+-         return(INADDR_NONE);
+-      }
+-      memcpy(
+-         (char *) &inaddr.sin_addr,
+-         (char *) hostp->h_addr,
+-         sizeof(inaddr.sin_addr)
+-      );
++      log_error(LOG_LEVEL_ERROR, "could not resolve hostname %s because %s", host,gai_strerror(result));
++      if (result == EAI_SYSTEM)
++         log_error(LOG_LEVEL_ERROR, "The system error is %E");
++      return NULL;
+    }
+-   return(inaddr.sin_addr.s_addr);
++   else
++      if (res0==0)
++         log_error(LOG_LEVEL_ERROR, "Problem in resolving hostname %s: succeeded, but no information returned", host);
++   return res0;
+ }
+diff -urNad privoxy~/jbsockets.h privoxy/jbsockets.h
+--- privoxy~/jbsockets.h
++++ privoxy/jbsockets.h
+@@ -13,6 +13,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and 
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -100,9 +103,11 @@
+ extern "C" {
+ #endif
++#include "addrlist.h"
++
+ struct client_state;
+-extern jb_socket connect_to(const char *host, int portnum, struct client_state *csp);
++extern jb_socket connect_to(const char *host, const char *port, unsigned long portnum, int pf, struct client_state *csp);
+ #ifdef AMIGA
+ extern int write_socket(jb_socket fd, const char *buf, ssize_t n);
+ #else
+@@ -111,10 +116,10 @@
+ extern int read_socket(jb_socket fd, char *buf, int n);
+ extern void close_socket(jb_socket fd);
+-extern int bind_port(const char *hostnam, int portnum, jb_socket *pfd);
++extern int bind_port(const char *host, const char *port, int pf, jb_socket_set *fds);
+ extern int accept_connection(struct client_state * csp, jb_socket fd);
+-extern unsigned long resolve_hostname_to_ip(const char *host);
++extern addr_list *resolve_hostname_to_ip(const char *host, const char *port, int pf);
+ /* Revision control strings from this header and associated .c file */
+ extern const char jbsockets_rcs[];
+diff -urNad privoxy~/jcc.c privoxy/jcc.c
+--- privoxy~/jcc.c
++++ privoxy/jcc.c
+@@ -676,6 +676,7 @@
+ #include "cgi.h"
+ #include "loadcfg.h"
+ #include "urlmatch.h"
++#include "jb_socket_set.h"
+ const char jcc_h_rcs[] = JCC_H_VERSION;
+ const char project_h_rcs[] = PROJECT_H_VERSION;
+@@ -2112,61 +2113,78 @@
+  * Returns     :  Port that was opened.
+  *
+  *********************************************************************/
+-static jb_socket bind_port_helper(struct configuration_spec * config)
++static void bind_port_helper(struct configuration_spec * config, jb_socket_set *bfds)
+ {
+    int result;
+-   jb_socket bfd;
++   struct bind_spec *bs;
++   unsigned int bs_index;
++   int never_bound;
+-   if ( (config->haddr != NULL)
+-     && (config->haddr[0] == '1')
+-     && (config->haddr[1] == '2')
+-     && (config->haddr[2] == '7')
+-     && (config->haddr[3] == '.') )
+-   {
+-      log_error(LOG_LEVEL_INFO, "Listening on port %d for local connections only",
+-                config->hport);
+-   }
+-   else if (config->haddr == NULL)
+-   {
+-      log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses",
+-                config->hport);
+-   }
+-   else
++   never_bound = 1;
++   for (bs_index = 0; bs_index < config->hspecs_occupied;++bs_index)
+    {
+-      log_error(LOG_LEVEL_INFO, "Listening on port %d on IP address %s",
+-                config->hport, config->haddr);
+-   }
++      bs = &config->hspecs[bs_index];
++      
++      /* This check misses about a trillion zillion different manners to describe
++         the local interface in IPv6. Who cares? */
++      if ( (bs->haddr != NULL)
++           && (((bs->haddr[0] == '1')
++                && (bs->haddr[1] == '2')
++                && (bs->haddr[2] == '7')
++                && (bs->haddr[3] == '.'))
++               || ((bs->haddr[0] == ':')
++                   && (bs->haddr[1] == ':')
++                   && (bs->haddr[2] == '1'))))
++      {
++         log_error(LOG_LEVEL_INFO, "Listening on port %s for local connections only",
++                   bs->hport);
++      }
++      else if (bs->haddr == NULL || bs->haddr[0]=='\0')
++      {
++         log_error(LOG_LEVEL_INFO, "Listening on port %s on all IP addresses",
++                   bs->hport);
++      }
++      else
++      {
++         log_error(LOG_LEVEL_INFO, "Listening on port %s on IP address %s",
++                   bs->hport, bs->haddr);
++      }
+-   result = bind_port(config->haddr, config->hport, &bfd);
++      result = bind_port(bs->haddr, bs->hport, bs->pf, bfds);
+-   if (result < 0)
+-   {
+-      switch(result)
++      if (result != 0)
+       {
++         switch(result)
++         {
+          case -3 :
+-            log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: "
++            log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s: "
+                "There may be another Privoxy or some other "
+-               "proxy running on port %d",
+-               (NULL != config->haddr) ? config->haddr : "INADDR_ANY",
+-                      config->hport, config->hport);
++               "proxy running on port %s",
++               (NULL != bs->haddr) ? bs->haddr : "INADDR_ANY",
++                      bs->hport, bs->hport);
++                              break;
+          case -2 :
+-            log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: " 
++            log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s: " 
+                "The hostname is not resolvable",
+-               (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport);
++               (NULL != bs->haddr) ? bs->haddr : "INADDR_ANY", bs->hport);
++                              break;
+          default :
+-            log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: because %E",
+-               (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport);
++            log_error(LOG_LEVEL_ERROR, "can't bind to %s:%s: because %E",
++               (NULL != bs->haddr) ? bs->haddr : "INADDR_ANY", bs->hport);
++         }
+       }
++      else
++         never_bound = 0;
++   }
++   if(never_bound)
++   {
++      log_error(LOG_LEVEL_FATAL, "Couldn't bind at all - bailing out");
+       /* shouldn't get here */
+-      return JB_INVALID_SOCKET;
+    }
+-
+    config->need_bind = 0;
+-
+-   return bfd;
+ }
+@@ -2184,12 +2202,18 @@
+ static void listen_loop(void)
+ {
+    struct client_state *csp = NULL;
+-   jb_socket bfd;
++   jb_socket_set bfds;
++   fd_set bfds_fs;
++   jb_socket_set_iterate_state bfds_iterate_state;
++   jb_socket bfd_current;
+    struct configuration_spec * config;
++   init_jb_socket_set(&bfds);
++
+    config = load_config();
+-   bfd = bind_port_helper(config);
++   bind_port_helper(config,&bfds);
++   bfd_current=JB_INVALID_SOCKET;
+ #ifdef FEATURE_GRACEFUL_TERMINATION
+    while (!g_terminate)
+@@ -2263,14 +2287,55 @@
+           * that this will hurt people's feelings.
+           */
+-         close_socket(bfd);
++         jb_socket_set_iterate_state s;
++         jb_socket bfd;
++         s=jb_socket_set_iteration_begin(&bfds);
++         for(bfd=jb_socket_set_iteration_next(&s);bfd!=JB_INVALID_SOCKET;bfd=jb_socket_set_iteration_next(&s))
++         {
++            close_socket(bfd);
++         }
++         destroy_jb_socket_set(&bfds);
++         bind_port_helper(config,&bfds);
++         /* We have a new set of fd's to accept. Restart iteration over bfds. */
++         bfd_current = JB_INVALID_SOCKET;
++      }
+-         bfd = bind_port_helper(config);
++      /* Here: select call on listening sockets: bfd=sockets */
++      if (bfd_current == JB_INVALID_SOCKET)
++      {
++         jb_socket max;
++         log_error(LOG_LEVEL_CONNECT, "select connections ... ");
++         bfds_iterate_state=jb_socket_set_iteration_begin(&bfds);
++         FD_ZERO(&bfds_fs);
++         max = 0;
++         for(bfd_current=jb_socket_set_iteration_next(&bfds_iterate_state);
++             bfd_current!=JB_INVALID_SOCKET;
++             bfd_current=jb_socket_set_iteration_next(&bfds_iterate_state))
++         {
++            FD_SET(bfd_current,&bfds_fs);
++            if (bfd_current >= max)
++               max = bfd_current + 1;
++         }
++         if(!select(max,&bfds_fs,NULL,NULL,NULL))
++         {
++            log_error(LOG_LEVEL_CONNECT, "select failed: %E");
++            bfd_current=JB_INVALID_SOCKET;
++            continue;
++         }
++         log_error(LOG_LEVEL_CONNECT, "OK");
++         bfds_iterate_state=jb_socket_set_iteration_begin(&bfds);
++         bfd_current=jb_socket_set_iteration_next(&bfds_iterate_state);
++      }
++      if (!FD_ISSET(bfd_current,&bfds_fs))
++      {
++         bfd_current=jb_socket_set_iteration_next(&bfds_iterate_state);
++         freez(csp);
++         continue;
+       }
+       log_error(LOG_LEVEL_CONNECT, "accept connection ... ");
+-      if (!accept_connection(csp, bfd))
++      if (!accept_connection(csp, bfd_current))
+       {
+          log_error(LOG_LEVEL_CONNECT, "accept failed: %E");
+@@ -2288,6 +2353,8 @@
+          log_error(LOG_LEVEL_CONNECT, "OK");
+       }
++      bfd_current=jb_socket_set_iteration_next(&bfds_iterate_state);
++
+ #ifdef FEATURE_TOGGLE
+       if (global_toggle_state)
+       {
+diff -urNad privoxy~/loadcfg.c privoxy/loadcfg.c
+--- privoxy~/loadcfg.c
++++ privoxy/loadcfg.c
+@@ -11,6 +11,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -372,6 +375,7 @@
+ #include "encode.h"
+ #include "urlmatch.h"
+ #include "cgi.h"
++#include "parsers.h"
+ const char loadcfg_h_rcs[] = LOADCFG_H_VERSION;
+@@ -489,8 +493,8 @@
+       struct forward_spec * next_fwd = cur_fwd->next;
+       free_url_spec(cur_fwd->url);
+-      freez(cur_fwd->gateway_host);
+-      freez(cur_fwd->forward_host);
++      freez(cur_fwd->gateway_malloc);
++      freez(cur_fwd->forward_malloc);
+       free(cur_fwd);
+       cur_fwd = next_fwd;
+    }
+@@ -507,7 +511,16 @@
+    freez(config->confdir);
+    freez(config->logdir);
+-   freez(config->haddr);
++   if(config -> hspecs != NULL)
++   {
++      int i;
++      for (i=0; i < config->hspecs_occupied; ++i)
++      {
++         freez(config->hspecs[i].haddr);
++         freez(config->hspecs[i].hport);
++      }
++   }
++   freez(config->hspecs);
+    freez(config->logfile);
+    for (i = 0; i < MAX_ACTION_FILES; i++)
+@@ -570,6 +583,28 @@
+  * Returns     :  The configuration_spec, or NULL on error.
+  *
+  *********************************************************************/
++static void fail_load_config_memory(struct file_list *fs, struct configuration_spec *config)
++{
++   freez(fs->filename);
++   freez(fs);
++   if (config != NULL)
++   {
++      if(config -> hspecs != NULL)
++      {
++         int i;
++         for (i=0; i < config->hspecs_occupied; ++i)
++         {
++            freez(config->hspecs[i].haddr);
++            freez(config->hspecs[i].hport);
++         }
++      }
++      freez(config -> hspecs);
++   }
++   freez(config);
++   log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
++   /* Never get here - LOG_LEVEL_FATAL causes program exit */
++}
++
+ struct configuration_spec * load_config(void)
+ {
+    char buf[BUFFER_SIZE];
+@@ -601,12 +636,7 @@
+    fs->f = config = (struct configuration_spec *)zalloc(sizeof(*config));
+    if (config==NULL)
+-   {
+-      freez(fs->filename);
+-      freez(fs);
+-      log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+-      /* Never get here - LOG_LEVEL_FATAL causes program exit */
+-   }
++      fail_load_config_memory(fs,config);
+    /*
+     * This is backwards from how it's usually done.
+@@ -623,7 +653,7 @@
+     * Set to defaults
+     */
+    config->multi_threaded    = 1;
+-   config->hport             = HADDR_PORT;
++   config->hspecs            = NULL;
+    config->buffer_limit      = 4096 * 1024;
+    config->usermanual        = strdup(USER_MANUAL_URL);
+    config->proxy_args        = strdup("");
+@@ -640,9 +670,6 @@
+       char cmd[BUFFER_SIZE];
+       char arg[BUFFER_SIZE];
+       char tmp[BUFFER_SIZE];
+-#ifdef FEATURE_ACL
+-      struct access_control_list *cur_acl;
+-#endif /* def FEATURE_ACL */
+       struct forward_spec *cur_fwd;
+       int vec_count;
+       char *vec[3];
+@@ -753,74 +780,23 @@
+  * *************************************************************************/
+ #ifdef FEATURE_ACL
+          case hash_deny_access:
+-            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
+-
+-            if ((vec_count != 1) && (vec_count != 2))
+-            {
+-               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
+-                     "deny-access directive in configuration file.");
+-               string_append(&config->proxy_args,
+-                  "<br>\nWARNING: Wrong number of parameters for "
+-                  "deny-access directive in configuration file.<br><br>\n");
+-               continue;
+-            }
+-
+-            /* allocate a new node */
+-            cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
+-
+-            if (cur_acl == NULL)
+-            {
+-               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+-               /* Never get here - LOG_LEVEL_FATAL causes program exit */
+-               continue;
+-            }
+-            cur_acl->action = ACL_DENY;
+-
+-            if (acl_addr(vec[0], cur_acl->src) < 0)
+-            {
+-               log_error(LOG_LEVEL_ERROR, "Invalid source IP for deny-access "
+-                     "directive in configuration file: \"%s\"", vec[0]);
+-               string_append(&config->proxy_args,
+-                  "<br>\nWARNING: Invalid source IP for deny-access directive"
+-                  " in configuration file: \"");
+-               string_append(&config->proxy_args,
+-                  vec[0]);
+-               string_append(&config->proxy_args,
+-                  "\"<br><br>\n");
+-               freez(cur_acl);
+-               continue;
+-            }
+-            if (vec_count == 2)
++            switch (ssplit(arg, " \t", vec, SZ(vec), 1, 1))
+             {
+-               if (acl_addr(vec[1], cur_acl->dst) < 0)
+-               {
+-                  log_error(LOG_LEVEL_ERROR, "Invalid destination IP for deny-access "
+-                        "directive in configuration file: \"%s\"", vec[0]);
+-                  string_append(&config->proxy_args,
+-                     "<br>\nWARNING: Invalid destination IP for deny-access directive"
+-                     " in configuration file: \"");
+-                  string_append(&config->proxy_args,
+-                     vec[0]);
++               case 1:
++                  config->acl = add_to_acl_list(config->acl, ACL_DENY, vec[0], NULL, &config->proxy_args);
++                                              break;
++               case 2:
++                  config->acl = add_to_acl_list(config->acl, ACL_DENY, vec[0], vec[1], &config->proxy_args);
++                                              break;
++               default:
++                  log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
++                            "deny-access directive in configuration file.");
+                   string_append(&config->proxy_args,
+-                     "\"<br><br>\n");
+-                  freez(cur_acl);
+-                  continue;
+-               }
++                                "<br>\nWARNING: Wrong number of parameters for "
++                                "deny-access directive in configuration file.<br><br>\n");
+             }
+-
+-            /*
+-             * Add it to the list.  Note we reverse the list to get the
+-             * behaviour the user expects.  With both the ACL and
+-             * actions file, the last match wins.  However, the internal
+-             * implementations are different:  The actions file is stored
+-             * in the same order as the file, and scanned completely.
+-             * With the ACL, we reverse the order as we load it, then
+-             * when we scan it we stop as soon as we get a match.
+-             */
+-            cur_acl->next  = config->acl;
+-            config->acl = cur_acl;
+-
+             continue;
++
+ #endif /* def FEATURE_ACL */
+ /* *************************************************************************
+@@ -914,16 +890,18 @@
+             if (strcmp(p, ".") != 0)
+             {
+-               cur_fwd->forward_host = strdup(p);
++               cur_fwd->forward_malloc = strdup(p);
++               cur_fwd->forward_family = -1;
+-               if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+-               {
+-                  *p++ = '\0';
+-                  cur_fwd->forward_port = atoi(p);
+-               }
++               parse_pf_ip(cur_fwd->forward_malloc,
++                           &cur_fwd->forward_host,
++                           &cur_fwd->forward_port_str,
++                           &cur_fwd->forward_family);
++               cur_fwd->forward_port = atoi(cur_fwd->forward_port_str);
+                if (cur_fwd->forward_port <= 0)
+                {
++                  cur_fwd->forward_port_str = "8000";
+                   cur_fwd->forward_port = 8000;
+                }
+             }
+@@ -977,15 +955,27 @@
+             if (strcmp(p, ".") != 0)
+             {
+-               cur_fwd->gateway_host = strdup(p);
++               /* SOCKS is IPv4-specific */
++               int pf = PF_INET;
+-               if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
++               cur_fwd->gateway_malloc = strdup(p);
++               if (parse_pf_ip(cur_fwd->gateway_malloc,
++                               &cur_fwd->gateway_host,
++                               &cur_fwd->gateway_port_str,
++                               &pf) != 0)
+                {
+-                  *p++ = '\0';
+-                  cur_fwd->gateway_port = atoi(p);
++                 log_error(LOG_LEVEL_ERROR, "Could not parse forward-socks4 host: %s",p);
++                 cur_fwd->gateway_host = NULL;
++                 cur_fwd->gateway_port_str = NULL;
++                 freez(cur_fwd->gateway_malloc);
++                 continue;
+                }
++
++               cur_fwd->gateway_port = atoi(cur_fwd->gateway_port_str);
++
+                if (cur_fwd->gateway_port <= 0)
+                {
++                  cur_fwd->gateway_port_str = "1080";
+                   cur_fwd->gateway_port = 1080;
+                }
+             }
+@@ -995,16 +985,26 @@
+             if (strcmp(p, ".") != 0)
+             {
+-               cur_fwd->forward_host = strdup(p);
++               cur_fwd->forward_malloc = strdup(p);
++               cur_fwd->forward_family = -1;
+-               if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
++               parse_pf_ip(cur_fwd->forward_malloc,
++                           &cur_fwd->forward_host,
++                           &cur_fwd->forward_port_str,
++                           &cur_fwd->forward_family);
++               cur_fwd->forward_port = atoi(cur_fwd->forward_port_str);
++
++               if (cur_fwd->forward_port <= 0)
+                {
+-                  *p++ = '\0';
+-                  cur_fwd->forward_port = atoi(p);
++                  cur_fwd->forward_port_str = "8000";
++                  cur_fwd->forward_port = 8000;
+                }
++               cur_fwd->forward_port = atoi(p);
++
+                if (cur_fwd->forward_port <= 0)
+                {
++                  cur_fwd->forward_port_str = "8000";
+                   cur_fwd->forward_port = 8000;
+                }
+             }
+@@ -1056,16 +1056,30 @@
+             /* Parse the SOCKS proxy host[:port] */
+             p = vec[1];
+-            cur_fwd->gateway_host = strdup(p);
+-
+-            if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
+-            {
+-               *p++ = '\0';
+-               cur_fwd->gateway_port = atoi(p);
+-            }
+-            if (cur_fwd->gateway_port <= 0)
+             {
+-               cur_fwd->gateway_port = 1080;
++               /* SOCKS is IPv4-specific */
++               int pf = PF_INET;
++
++               cur_fwd->gateway_malloc = strdup(p);
++               if (parse_pf_ip(cur_fwd->gateway_malloc,
++                               &cur_fwd->gateway_host,
++                               &cur_fwd->gateway_port_str,
++                               &pf) != 0)
++               {
++                 log_error(LOG_LEVEL_ERROR, "Could not parse forward-socks4a host: %s",p);
++                 cur_fwd->gateway_host = NULL;
++                 cur_fwd->gateway_port_str = NULL;
++                 freez(cur_fwd->gateway_malloc);
++                 continue;
++               }
++
++               cur_fwd->gateway_port = atoi(cur_fwd->gateway_port_str);
++
++               if (cur_fwd->gateway_port <= 0)
++               {
++                  cur_fwd->gateway_port_str = "1080";
++                  cur_fwd->gateway_port = 1080;
++               }
+             }
+             /* Parse the parent HTTP proxy host[:port] */
+@@ -1073,16 +1087,26 @@
+             if (strcmp(p, ".") != 0)
+             {
+-               cur_fwd->forward_host = strdup(p);
++               cur_fwd->forward_malloc = strdup(p);
++               cur_fwd->forward_family = -1;
+-               if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
++               parse_pf_ip(cur_fwd->forward_malloc,
++                           &cur_fwd->forward_host,
++                           &cur_fwd->forward_port_str,
++                           &cur_fwd->forward_family);
++               cur_fwd->forward_port = atoi(cur_fwd->forward_port_str);
++
++               if (cur_fwd->forward_port <= 0)
+                {
+-                  *p++ = '\0';
+-                  cur_fwd->forward_port = atoi(p);
++                  cur_fwd->forward_port_str = "8000";
++                  cur_fwd->forward_port = 8000;
+                }
++               cur_fwd->forward_port = atoi(p);
++
+                if (cur_fwd->forward_port <= 0)
+                {
++                  cur_fwd->forward_port_str = "8000";
+                   cur_fwd->forward_port = 8000;
+                }
+             }
+@@ -1108,10 +1132,49 @@
+  * listen-address [ip][:port]
+  * *************************************************************************/
+          case hash_listen_address :
+-            freez(config->haddr);
+-            config->haddr = strdup(arg);
++         {
++            struct bind_spec *bs;
++            char *arg_cpy;
++            if (config->hspecs == NULL)
++            {
++               /* This is the first we'll bind to */
++               config->hspecs = calloc(2,sizeof(struct bind_spec));
++               if (config->hspecs == NULL)
++                  fail_load_config_memory(fs,config);
++               config->hspecs_size = 2;
++               config->hspecs_occupied = 0;
++            }
++            
++            arg_cpy = strdup(arg);
++            if (arg_cpy == NULL)
++               fail_load_config_memory(fs,config);
++            if (config->hspecs_occupied == config->hspecs_size)
++              {
++                struct bind_spec *new_hspecs;
++                config->hspecs_size *= 2;
++                new_hspecs = realloc(config->hspecs,config->hspecs_size * sizeof(struct bind_spec));
++                if (new_hspecs == NULL)
++                  {
++                    /* Not enough memory to continue. Cancel changes. */
++                    config->hspecs_size /= 2;
++                    fail_load_config_memory(fs,config);
++                  }
++                config->hspecs = new_hspecs;
++              }
++            bs = &config->hspecs[(config->hspecs_occupied)++];
++            bs->pf = -1;
++            parse_pf_ip(arg,&bs->haddr,&bs->hport,&bs->pf);
++            if (*bs->haddr == '\0')
++            {
++               bs->haddr = NULL;
++            }
++            else
++            {
++               (bs->haddr = strdup(bs->haddr));
++            }
++            bs->hport = strdup(bs->hport);
+             continue;
+-
++         }
+ /* *************************************************************************
+  * logdir directory-name
+  * *************************************************************************/
+@@ -1134,75 +1197,21 @@
+  * *************************************************************************/
+ #ifdef FEATURE_ACL
+          case hash_permit_access:
+-            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
+-
+-            if ((vec_count != 1) && (vec_count != 2))
+-            {
+-               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
+-                     "permit-access directive in configuration file.");
+-               string_append(&config->proxy_args,
+-                  "<br>\nWARNING: Wrong number of parameters for "
+-                  "permit-access directive in configuration file.<br><br>\n");
+-
+-               continue;
+-            }
+-
+-            /* allocate a new node */
+-            cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
+-
+-            if (cur_acl == NULL)
+-            {
+-               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+-               /* Never get here - LOG_LEVEL_FATAL causes program exit */
+-               continue;
+-            }
+-            cur_acl->action = ACL_PERMIT;
+-
+-            if (acl_addr(vec[0], cur_acl->src) < 0)
+-            {
+-               log_error(LOG_LEVEL_ERROR, "Invalid source IP for permit-access "
+-                     "directive in configuration file: \"%s\"", vec[0]);
+-               string_append(&config->proxy_args,
+-                  "<br>\nWARNING: Invalid source IP for permit-access directive"
+-                  " in configuration file: \"");
+-               string_append(&config->proxy_args,
+-                  vec[0]);
+-               string_append(&config->proxy_args,
+-                  "\"<br><br>\n");
+-               freez(cur_acl);
+-               continue;
+-            }
+-            if (vec_count == 2)
++            switch (ssplit(arg, " \t", vec, SZ(vec), 1, 1))
+             {
+-               if (acl_addr(vec[1], cur_acl->dst) < 0)
+-               {
+-                  log_error(LOG_LEVEL_ERROR, "Invalid destination IP for "
+-                        "permit-access directive in configuration file: \"%s\"",
+-                        vec[0]);
+-                  string_append(&config->proxy_args,
+-                     "<br>\nWARNING: Invalid destination IP for permit-access directive"
+-                     " in configuration file: \"");
+-                  string_append(&config->proxy_args,
+-                     vec[0]);
++               case 1:
++                  config->acl = add_to_acl_list(config->acl, ACL_PERMIT, vec[0], NULL, &config->proxy_args);
++                                              break;
++               case 2:
++                  config->acl = add_to_acl_list(config->acl, ACL_PERMIT, vec[0], vec[1], &config->proxy_args);
++                                              break;
++               default:
++                  log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
++                            "permit-access directive in configuration file.");
+                   string_append(&config->proxy_args,
+-                     "\"<br><br>\n");
+-                  freez(cur_acl);
+-                  continue;
+-               }
++                                "<br>\nWARNING: Wrong number of parameters for "
++                                "permit-access directive in configuration file.<br><br>\n");
+             }
+-
+-            /*
+-             * Add it to the list.  Note we reverse the list to get the
+-             * behaviour the user expects.  With both the ACL and
+-             * actions file, the last match wins.  However, the internal
+-             * implementations are different:  The actions file is stored
+-             * in the same order as the file, and scanned completely.
+-             * With the ACL, we reverse the order as we load it, then
+-             * when we scan it we stop as soon as we get a match.
+-             */
+-            cur_acl->next  = config->acl;
+-            config->acl = cur_acl;
+-
+             continue;
+ #endif /* def FEATURE_ACL */
+@@ -1442,32 +1451,33 @@
+    }
+ #endif /* def FEATURE_COOKIE_JAR */
+-   if ( NULL == config->haddr )
+-   {
+-      config->haddr = strdup( HADDR_DEFAULT );
+-   }
+-
+-   if ( NULL != config->haddr )
++   if ( config->hspecs == NULL )
+    {
+-      if (NULL != (p = strchr(config->haddr, ':')))
+-      {
+-         *p++ = '\0';
+-         if (*p)
+-         {
+-            config->hport = atoi(p);
+-         }
+-      }
+-
+-      if (config->hport <= 0)
+-      {
+-         *--p = ':';
+-         log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr);
+-         /* Never get here - LOG_LEVEL_FATAL causes program exit */
+-      }
+-      if (*config->haddr == '\0')
+-      {
+-         config->haddr = NULL;
+-      }
++      /* No listen-address set. The default is localhost on port 8118, on IPv4
++         and (if INET6 is defined) IPv6.
++       */
++      struct bind_spec *bs;
++#ifdef INET6
++      config->hspecs = calloc(2,sizeof(struct bind_spec));
++      if (config->hspecs == NULL)
++         fail_load_config_memory(fs,config);
++      config->hspecs_size=2;
++      config->hspecs_occupied=1;
++      bs = &config->hspecs[0];
++      bs->haddr = strdup("::1");
++      bs->hport = strdup("8118");
++      bs->pf = PF_UNSPEC;
++#else
++      config->hspecs = calloc(1,sizeof(struct bind_spec));
++      if (config->hspecs == NULL)
++         fail_load_config_memory(fs,config);
++      config->hspecs_size=1;
++      config->hspecs_occupied=0;
++#endif
++      bs = &config->hspecs[config->hspecs_occupied++];
++      bs->haddr = strdup("127.0.0.1");
++      bs->hport = strdup("8118");
++      bs->pf = PF_UNSPEC;
+    }
+    /*
+@@ -1509,31 +1519,29 @@
+       struct configuration_spec * oldcfg = (struct configuration_spec *)
+                                            current_configfile->f;
+       /*
+-       * Check if config->haddr,hport == oldcfg->haddr,hport
+-       *
+-       * The following could be written more compactly as a single,
+-       * (unreadably long) if statement.
++       * Check if the listening addresses have changed
+        */
+       config->need_bind = 0;
+-      if (config->hport != oldcfg->hport)
+-      {
+-         config->need_bind = 1;
+-      }
+-      else if (config->haddr == NULL)
++      if (config -> hspecs_occupied == oldcfg -> hspecs_occupied)
+       {
+-         if (oldcfg->haddr != NULL)
++         int bs_index;
++         struct bind_spec *hspec;
++         struct bind_spec *oldhspec;
++         hspec = config -> hspecs;
++         oldhspec = oldcfg -> hspecs;
++         for(bs_index = 0; bs_index < oldcfg->hspecs_occupied; ++bs_index)
+          {
+-            config->need_bind = 1;
++           if (strcmp(hspec[bs_index].haddr,oldhspec[bs_index].haddr) != 0
++               || strcmp(hspec[bs_index].hport,oldhspec[bs_index].hport) != 0
++               || hspec[bs_index].pf != hspec[bs_index].pf)
++           {
++              config -> need_bind = 1;
++              break;
++           }
+          }
+       }
+-      else if (oldcfg->haddr == NULL)
+-      {
+-         config->need_bind = 1;
+-      }
+-      else if (0 != strcmp(config->haddr, oldcfg->haddr))
+-      {
+-         config->need_bind = 1;
+-      }
++      else
++         config-> need_bind = 1;
+       current_configfile->unloader = unload_configfile;
+    }
+diff -urNad privoxy~/loaders.c privoxy/loaders.c
+--- privoxy~/loaders.c
++++ privoxy/loaders.c
+@@ -11,6 +11,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 8 December 2002, 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -427,6 +430,7 @@
+          freez(csp->ip_addr_str);
+          freez(csp->my_ip_addr_str);
++         freez(csp->my_port_str);
+          freez(csp->my_hostname);
+          freez(csp->x_forwarded);
+          freez(csp->iob->buf);
+diff -urNad privoxy~/miscutil.h privoxy/miscutil.h
+--- privoxy~/miscutil.h
++++ privoxy/miscutil.h
+@@ -142,6 +142,15 @@
+ #include "project.h"
++/* Fix a problem with Solaris.  There should be no effect on other
++ * platforms.
++ * Solaris's isspace() is a macro which uses it's argument directly
++ * as an array index.  Therefore we need to make sure that high-bit
++ * characters generate +ve values, and ideally we also want to make
++ * the argument match the declared parameter type of "int".
++ */
++#define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
++
+ #if defined(__cplusplus)
+ extern "C" {
+ #endif
+diff -urNad privoxy~/parsers.c privoxy/parsers.c
+--- privoxy~/parsers.c
++++ privoxy/parsers.c
+@@ -16,6 +16,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -1953,6 +1956,167 @@
+ }
+ #endif /* def FEATURE_FORCE_LOAD */
++/*********************************************************************
++ *
++ * Function    :  parse_pf_ip_netmask
++ *
++ * Description :  Parse an IPv{4,6} litteral or hostname
++ *                with optional port and optional explicit family
++ *                and optional netmask
++ *
++ * Parameters  :
++ *          0  :  string = the string to parse
++ *          1  :  host = Is set to point to the hostname or IP literal
++ *                       part
++ *          2  :  port = Is set to point to the port part,
++ *                       or NULL if no port in string
++ *          3  :  pf = pointer used to return the address family
++ *                     pf is a value-result argument:
++ *                     If it is set to -1, then parse_pf_ip will set it
++ *                      to the address family of the pf_ip string
++ *                     else, it won't touch it, and fail if the two
++ *                      cannot match
++ *          4  :  pointer used to return the mask length
++ *                Set to -1 if no mask
++ *
++ * Returns     :  0 on success
++ *
++ *********************************************************************/
++int parse_pf_ip_netmask(char *string, char **host, char **port, int *pf, int *masklength)
++{
++   int i;
++   char *p;
++
++   *masklength = -1;
++
++   if ((p = strchr(string, '/')) != NULL)
++   {
++      *p++ = '\0';
++
++      if (ijb_isdigit(*p) == 0)
++      {
++         return -1;
++      }
++      i = atoi(p);
++      if ( i < 0 )
++         return -1;
++      *masklength = i;
++   }
++
++   return parse_pf_ip(string, host, port, pf);
++}
++
++/*********************************************************************
++ *
++ * Function    :  parse_pf_ip
++ *
++ * Description :  Parse an IPv{4,6} litteral or hostname
++ *                with optional port and optional explicit family
++ *
++ * Parameters  :
++ *          0  :  string = the string to parse
++ *          1  :  host = Is set to point to the hostname or IP literal
++ *                       part
++ *          2  :  port = Is set to point to the port part,
++ *                       or NULL if no port in string
++ *          3  :  pf = pointer used to return the address family
++ *                     pf is a value-result argument:
++ *                     If it is set to -1, then parse_pf_ip will set it
++ *                      to the address family of the pf_ip string
++ *                     else, it won't touch it, and fail if the two
++ *                      cannot match
++ *
++ * Returns     :  0 on success
++ *
++ *********************************************************************/
++int parse_pf_ip(char *string, char **host, char **port, int *pf)
++{
++   if (pf != NULL && *pf == -1)
++      *pf = PF_UNSPEC;
++
++   /* See if we want to override the default protocol family */
++   if (strncmpic(string, "ipv4:", 5) == 0)
++   {
++      string += 5;
++      if (pf!=NULL)
++      {
++         if(*pf==PF_INET || *pf==PF_UNSPEC)
++            *pf = AF_INET;
++         else
++         {
++            log_error(LOG_LEVEL_ERROR,"%s","IPv4 address found where other awaited");
++            return -2;
++         }
++      }
++   }
++   else if (strncmpic(string, "ipv6:", 5) == 0)
++   {
++#ifdef INET6
++      string += 5;
++      if(*pf==PF_INET6 || *pf==PF_UNSPEC)
++         *pf = AF_INET6;
++      else
++      {
++        log_error(LOG_LEVEL_ERROR,"%s","IPv6 address found where other awaited");
++        return -2;
++      }
++#else
++      log_error(LOG_LEVEL_ERROR,"%s","This privoxy hasn't IPv6 support");
++      return -1;
++#endif
++   }
++   return parse_ip(string, host, port);
++}
++
++/*********************************************************************
++ *
++ * Function    :  parse_ip
++ *
++ * Description :  Parse an IPv{4,6} litteral or hostname
++ *                with optional port
++ *
++ * Parameters  :
++ *          0  :  string = the string to parse
++ *          1  :  host = Is set to point to the hostname or IP literal
++ *                       part
++ *          2  :  port = Is set to point to the port part,
++ *                       or NULL if no port in string
++ * Returns     :  0 on success
++ *
++ *********************************************************************/
++int parse_ip(char *string, char **host, char **port)
++{
++   char *p;
++   int skip;
++
++   /* allow IPv6 address literal: [numbers:with:colons]:port/mask */
++   if (string[0] == '[' && (p = strchr(string, ']')))
++   {
++      *p++ = '\0';
++      skip = 1;
++   }
++   else
++   {
++      p = string;
++      skip = 0;
++   }
++
++   if (host != NULL)
++      *host = string + skip;
++
++   for(;*p != '\0'; ++p)
++   {
++      if (*p == ':')
++      {
++         *p++ = '\0';
++         break;
++      }
++   }
++   if (port != NULL)
++      *port = p;
++   return 0;
++}
++
+ /*
+   Local Variables:
+diff -urNad privoxy~/parsers.h privoxy/parsers.h
+--- privoxy~/parsers.h
++++ privoxy/parsers.h
+@@ -19,6 +19,9 @@
+  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+  *                Privoxy team. http://www.privoxy.org/
+  *
++ *                Modified by Lionel Elie Mamane <lionel@mamane.lu>
++ *                for IPv6 support on 24 January 2003.
++ *
+  *                Based on the Internet Junkbuster originally written
+  *                by and Copyright (C) 1997 Anonymous Coders and 
+  *                Junkbusters Corporation.  http://www.junkbusters.com
+@@ -227,6 +230,10 @@
+ extern jb_err server_transfer_coding (struct client_state *csp, char **header);
+ extern jb_err server_http            (struct client_state *csp, char **header);
++extern int parse_pf_ip_netmask(char *string, char **host, char **port, int *pf, int *masklength);
++extern int parse_pf_ip(char *string, char ** host, char ** port, int *pf);
++extern int parse_ip(char *string, char ** host, char** port);
++
+ #ifdef FEATURE_FORCE_LOAD
+ extern int strclean(const char *string, const char *substring);
+ #endif /* def FEATURE_FORCE_LOAD */
+diff -urNad privoxy~/project.h privoxy/project.h
+--- privoxy~/project.h
++++ privoxy/project.h
+@@ -549,6 +549,20 @@
+ #endif /* ndef _WIN32 */
++#include "jb_socket_set.h"
++
++#ifdef INET6
++/**
++ * Get from the operating system structures big enough
++ * to put a network address in, namely sockaddr_storage
++ */
++#include <sys/socket.h> 
++/**
++ * If no IPv6 support, just use the old sockaddr
++ */
++#else
++#define sockaddr_storage   sockaddr
++#endif
+ /**
+  * A standard error code.  This should be JB_ERR_OK or one of the JB_ERR_xxx
+@@ -618,19 +632,6 @@
+  */
+ #define FOREVER 1
+-/**
+- * Default IP address to listen on, as a string.
+- * Set to "127.0.0.1".
+- */
+-#define HADDR_DEFAULT   "127.0.0.1"
+-
+-/**
+- * Default port to listen on, as a number.
+- * Set to 8118.
+- */
+-#define HADDR_PORT      8118
+-
+-
+ /* Forward def for struct client_state */
+ struct configuration_spec;
+@@ -709,13 +710,16 @@
+    char *ver;      /**< Protocol version */
+    int status;     /**< HTTP Status */
++   char *host_port_malloc;     /**< malloc used for place wher host and port_str are */
+    char *host;     /**< Host part of URL */
+    int   port;     /**< Port of URL or 80 (default) */
++   char *port_str; /**< Port of URL, as string */
+    char *path;     /**< Path of URL */
+    char *hostport; /**< host[:port] */
+    int   ssl;      /**< Flag if protocol is https */
+-   char *host_ip_addr_str; /**< String with dotted decimal representation
++   char *host_ip_addr_str; /**< String with dotted decimal representation (IPv4)
++                                or hexadecimal colon-separated (IPv6)
+                                 of host's IP. NULL before connect_to() */
+    char  *dbuffer; /**< Buffer with '\0'-delimited domain name.           */
+@@ -1039,13 +1043,16 @@
+        As a string. */
+    char *ip_addr_str;
+    /** Client PC's IP address, as reported by the accept() function.
+-       As a number. */
+-   long  ip_addr_long;
++       As an address. */
++   struct sockaddr_storage ip_addr_addr;
+    /** Our IP address. I.e. the IP address that the client used to reach us,
+        as a string. */
+    char *my_ip_addr_str;
++   /** Our  port. I.e. the port the client used to reach us */
++   char *my_port_str;
++
+    /** Our hostname. I.e. the reverse DNS of the IP address that the client
+        used to reach us, as a string. */
+    char *my_hostname;
+@@ -1214,18 +1221,33 @@
+    /** Connection type.  Must be SOCKS_NONE, SOCKS_4, or SOCKS_4A. */
+    int   type;
++   /** pointer returned by the malloc used for gateway_host and gateway_port_str */
++   char *gateway_malloc;
++
+    /** SOCKS server hostname.  Only valid if "type" is SOCKS_4 or SOCKS_4A. */
+    char *gateway_host;
+    /** SOCKS server port. */
+    int   gateway_port;
++   /** SOCKS server port, as string. */
++   char *gateway_port_str;
++
++   /** pointer returned by the malloc used for forward_host and forward_port_str */
++   char *forward_malloc;
++
++   /** Parent HTTP proxy address family. */
++   int forward_family;
++
+    /** Parent HTTP proxy hostname, or NULL for none. */
+    char *forward_host;
+    /** Parent HTTP proxy port. */
+    int   forward_port;
++   /** Parent HTTP proxy port as string. */
++   char *forward_port_str;
++
+    /** Next entry in the linked list. */
+    struct forward_spec *next;
+ };
+@@ -1234,7 +1256,7 @@
+ /**
+  * Initializer for a static struct forward_spec.
+  */
+-#define FORWARD_SPEC_INITIALIZER { { URL_SPEC_INITIALIZER }, 0, NULL, 0, NULL, 0, NULL }
++#define FORWARD_SPEC_INITIALIZER { { URL_SPEC_INITIALIZER }, 0, NULL, NULL, 0, NULL, NULL, 0, NULL, 0, NULL, NULL}
+ /**
+@@ -1263,7 +1285,8 @@
+  */
+ struct access_control_addr
+ {
+-   unsigned long addr;  /**< The IP address as an integer. */
++   struct sockaddr_storage addr; /**< The IP address. */
++   size_t addrlen;
+    unsigned long mask;  /**< The network mask as an integer. */
+    unsigned long port;  /**< The port number. */
+ };
+@@ -1295,6 +1318,17 @@
+ /** configuration_spec::feature_flags: Web-based toggle. */
+ #define RUNTIME_FEATURE_CGI_TOGGLE        2
++struct bind_spec
++{
++   /** IP address to bind to. */
++   char *haddr;
++
++   /** Port to bind to. */
++   char *hport;
++
++   /** Address family */
++   int pf;
++};
+ /**
+  * Data loaded from the configuration file.
+@@ -1355,11 +1389,13 @@
+ #endif /* def FEATURE_COOKIE_JAR */
+-   /** IP address to bind to.  Defaults to HADDR_DEFAULT == 127.0.0.1. */
+-   const char *haddr;
+-
+-   /** Port to bind to.  Defaults to HADDR_PORT == 8118. */
+-   int         hport;
++   /* IP addresses and ports to bind to.
++       Defaults to HSPECS_DEFAULT == {ipv4:127.0.0.1:8118, ipv6:[::1]:8118}. */
++   struct bind_spec *hspecs;
++   /* size allocated */
++   unsigned int hspecs_size;
++   /* number of entries */
++   unsigned int hspecs_occupied;
+    /** Size limit for IOB */
+    size_t buffer_limit;
+diff -urNad privoxy~/urlmatch.c privoxy/urlmatch.c
+--- privoxy~/urlmatch.c
++++ privoxy/urlmatch.c
+@@ -133,6 +133,7 @@
+ #include "ssplit.h"
+ #include "miscutil.h"
+ #include "errlog.h"
++#include "parsers.h"
+ const char urlmatch_h_rcs[] = URLMATCH_H_VERSION;
+@@ -156,7 +157,7 @@
+    freez(http->cmd);
+    freez(http->ocmd);
+    freez(http->gpc);
+-   freez(http->host);
++   freez(http->host_port_malloc);
+    freez(http->url);
+    freez(http->hostport);
+    freez(http->path);
+@@ -298,8 +299,6 @@
+     */
+    {
+       char *buf;
+-      char *host;
+-      char *port;
+       buf = strdup(http->hostport);
+       if (buf == NULL)
+@@ -307,38 +306,34 @@
+          return JB_ERR_MEMORY;
+       }
++              http->host_port_malloc = buf;
++
+       /* check if url contains username and/or password */
+-      host = strchr(buf, '@');
+-      if (host != NULL)
++      buf = strchr(buf, '@');
++      if (buf != NULL)
+       {
+          /* Contains username/password, skip it and the @ sign. */
+-         host++;
++         buf++;
+       }
+       else
+       {
+          /* No username or password. */
+-         host = buf;
++         buf = http->host_port_malloc;
+       }
+-      /* check if url contains port */
+-      port = strchr(host, ':');
+-      if (port != NULL)
++      parse_ip(buf,&http->host,&http->port_str);
++
++      if (*http->port_str != '\0')
+       {
+-         /* Contains port */
+-         /* Terminate hostname and point to start of port string */
+-         *port++ = '\0';
+-         http->port = atoi(port);
++         http->port = atoi(http->port_str);
+       }
+       else
+       {
+          /* No port specified. */
+-         http->port = (http->ssl ? 443 : 80);
++         http->port_str = (http->ssl ? "143" : "80");
++                      http->port = (http->ssl ? 443 : 80);
+       }
+-      http->host = strdup(host);
+-
+-      free(buf);
+-
+       if (http->host == NULL)
+       {
+          return JB_ERR_MEMORY;
+@@ -662,9 +657,8 @@
+  *                               written to system log)
+  *
+  *********************************************************************/
+-jb_err create_url_spec(struct url_spec * url, const char * buf)
++jb_err create_url_spec(struct url_spec * url, char * buf)
+ {
+-   char *p;
+    assert(url);
+    assert(buf);
+@@ -681,22 +675,25 @@
+    {
+       return JB_ERR_MEMORY;
+    }
+-
+-   if ((p = strchr(buf, '/')) != NULL)
+    {
+-      if (NULL == (url->path = strdup(p)))
++      char *p;
++
++      if ((p = strchr(buf, '/')) != NULL)
+       {
+-         freez(url->spec);
+-         return JB_ERR_MEMORY;
++         if (NULL == (url->path = strdup(p)))
++         {
++            freez(url->spec);
++            return JB_ERR_MEMORY;
++         }
++         url->pathlen = strlen(url->path);
++         *p = '\0';
+       }
+-      url->pathlen = strlen(url->path);
+-      *p = '\0';
+-   }
+-   else
+-   {
+-      url->path    = NULL;
+-      url->pathlen = 0;
+-   }
++      else
++      {
++         url->path    = NULL;
++         url->pathlen = 0;
++      }
++      }
+    if (url->path)
+    {
+       int errcode;
+@@ -735,15 +732,12 @@
+          return JB_ERR_PARSE;
+       }
+    }
+-   if ((p = strchr(buf, ':')) == NULL)
+-   {
+-      url->port = 0;
+-   }
+-   else
++
+    {
+-      *p++ = '\0';
+-      url->port = atoi(p);
+-   }
++     char *p;
++     parse_ip(buf,&buf,&p);
++     url->port = atoi(p);
++      }
+    if (buf[0] != '\0')
+    {
+@@ -775,12 +769,13 @@
+          return JB_ERR_MEMORY;
+       }
+-      /* 
+-       * Map to lower case
+-       */
+-      for (p = url->dbuffer; *p ; p++)
+       {
+-         *p = tolower((int)(unsigned char)*p);
++         char* p;
++         /* map to lower case */
++         for (p = url->dbuffer; *p ; p++)
++         {
++            *p = tolower((int)(unsigned char)*p);
++         }
+       }
+       /* 
+diff -urNad privoxy~/urlmatch.h privoxy/urlmatch.h
+--- privoxy~/urlmatch.h
++++ privoxy/urlmatch.h
+@@ -79,7 +79,7 @@
+ extern int url_match(const struct url_spec *pattern,
+                      const struct http_request *url);
+-extern jb_err create_url_spec(struct url_spec * url, const char * buf);
++extern jb_err create_url_spec(struct url_spec * url, char * buf);
+ extern void free_url_spec(struct url_spec *url);
index 2a451a1..5f7fd28 100644 (file)
@@ -7,9 +7,9 @@
 
 @DPATCH@
 diff -urNad privoxy~/config privoxy/config
---- privoxy~/config    2006-02-05 16:23:12.000000000 +0100
-+++ privoxy/config     2006-02-05 16:25:45.000000000 +0100
-@@ -889,11 +889,10 @@
+--- privoxy~/config
++++ privoxy/config
+@@ -931,11 +931,10 @@
  #  This feature allows routing of HTTP requests through a chain
  #  of multiple proxies. It can be used to better protect privacy
  #  and confidentiality when accessing specific domains by routing
@@ -18,34 +18,17 @@ diff -urNad privoxy~/config privoxy/config
 -#  proxy to speed up browsing. Or chaining to a parent proxy may be
 -#  necessary because the machine that Privoxy runs on has no direct
 -#  Internet access.
-+#  requests to those domains through an anonymous public proxy 
-+#  or to use a caching proxy to speed up browsing. Or chaining to
-+#  a parent proxy may be necessary because the machine that Privoxy
-+#  runs on has no direct Internet access.
- #  
++#  requests to those domains through an anonymous public proxy or 
++#  use a caching proxy to speed up browsing. Or chaining to a parent
++#  proxy may be necessary because the machine that Privoxy runs on has
++#  no direct Internet access.
+ #
  #  Also specified here are SOCKS proxies. Privoxy supports the SOCKS
  #  4 and SOCKS 4A protocols.
-diff -urNad privoxy~/doc/source/faq.sgml privoxy/doc/source/faq.sgml
---- privoxy~/doc/source/faq.sgml       2006-02-05 16:23:12.000000000 +0100
-+++ privoxy/doc/source/faq.sgml        2006-02-05 16:25:45.000000000 +0100
-@@ -1255,13 +1255,6 @@
-  on (those suspicious) people with a more than average preference for privacy.
- </para>
- <para>
-- You can find a list of anonymous public proxies at <ulink
-- url="http://www.multiproxy.org/anon_list.htm">multiproxy.org</ulink> and many
-- more through Google. A particularly interesting project is the JAP service
-- offered by the Technical University of Dresden (<ulink
-- url="http://anon.inf.tu-dresden.de/index_en.html">http://anon.inf.tu-dresden.de/index_en.html</ulink>).
--</para>
--<para>
-  There is, however, even in the single-machine case the possibility to make the
-  server believe that your machine is in fact a shared proxy serving a whole big
-  LAN, and we are looking into that.
 diff -urNad privoxy~/doc/source/p-config.sgml privoxy/doc/source/p-config.sgml
---- privoxy~/doc/source/p-config.sgml  2006-02-05 16:23:12.000000000 +0100
-+++ privoxy/doc/source/p-config.sgml   2006-02-05 16:25:45.000000000 +0100
-@@ -1519,9 +1519,8 @@
+--- privoxy~/doc/source/p-config.sgml
++++ privoxy/doc/source/p-config.sgml
+@@ -1606,9 +1606,8 @@
   multiple proxies.
   It can be used to better protect privacy and confidentiality when
   accessing specific domains by routing requests to those domains
index cf779e0..716d81c 100644 (file)
@@ -6,32 +6,38 @@
 
 @DPATCH@
 diff -urNad privoxy~/default.action.master privoxy/default.action.master
---- privoxy~/default.action.master     2006-02-05 16:23:12.000000000 +0100
-+++ privoxy/default.action.master      2006-02-05 16:25:47.000000000 +0100
-@@ -1444,6 +1444,8 @@
- .microsoft.com
- #MASTER# PROBLEM-URL: http://javabog.dk/ijk/
- javabog.dk/ijk/
-+#MASTER# PROBLEM-URL: http://validator.w3.org/
-+/valid-
- #----------------------------------------------------------------------------
- # These don't work without the referrer information:
-@@ -1464,6 +1466,8 @@
- #MASTER# PROBLEM-URL: http://www.mandrakelinux.com/en/ftp.php3
- #MASTER# REMARKS: Link to download page breaks
+--- privoxy~/default.action.master
++++ privoxy/default.action.master
+@@ -1593,6 +1593,8 @@
  www.mandrakelinux.com/en/ftp.php3
+ #MASTER# REMARKS: Actions Tracker 1313157
+ validator.w3.org/check\?uri=referer
 +#MASTER# PROBLEM-URL: http://www.petitiononline.com/mod_perl/signed.cgi?eldred&2901
 +www.petitiononline.com/mod_perl/signed.cgi
  
  #----------------------------------------------------------------------------
  # These animated gifs are either useful or nice:
-@@ -1601,6 +1605,8 @@
+@@ -1714,6 +1716,10 @@
+ #MASTER# PROBLEM-URL: http://www.nasa.gov
+ #MASTER# REMARKS: No progress past main page without js-annoyances
+ .nasa.gov
++#MASTER# PROBLEM-URL: http://www2.cnrs.fr/presse/communique/900.htm
++.cnrs.fr
++#MASTER# PROBLEM-URL: http://blogs.msdn.com/wga/archive/2006/07/16/667063.aspx
++blogs.msdn.com
+ {-filter{unsolicited-popups}}
+ #MASTER# DONT-VERIFY
+@@ -1733,6 +1739,12 @@
  cf.nbc4.com/
  #MASTER# PROBLEM-URL: http://www.hh.schule.de/ak/nt/
  www.hh.schule.de/ak/nt/
 +#MASTER# PROBLEM-URL: http://www.perl.com/language/newdocs/pod/perlop.html#Regexp_Quote_Like_Operators
 +www.perl.com/language/newdocs/pod/
++#MASTER# PROBLEM-URL: http://www2.cnrs.fr/presse/communique/900.htm
++.cnrs.fr
++#MASTER# PROBLEM-URL: http://blogs.msdn.com/wga/archive/2006/07/16/667063.aspx
++blogs.msdn.com
  
- {+fast-redirects -block}
+ {+fast-redirects{check-decoded-url} -block}
  #MASTER# PROBLEM-URL: http://isbn.nu/0596001088/price/2.html
index 3ddc586..e3ac205 100644 (file)
@@ -5,32 +5,32 @@
 ## DP: Stop converting 8bit chars in the documentation (#203697)
 
 @DPATCH@
-diff -urNad privoxy-dpatch~/doc/source/ldp.dsl.in privoxy-dpatch/doc/source/ldp.dsl.in
---- privoxy-dpatch~/doc/source/ldp.dsl.in      2006-02-05 16:17:43.000000000 +0100
-+++ privoxy-dpatch/doc/source/ldp.dsl.in       2006-02-05 16:17:46.000000000 +0100
+diff -urNad privoxy~/doc/source/ldp.dsl.in privoxy/doc/source/ldp.dsl.in
+--- privoxy~/doc/source/ldp.dsl.in
++++ privoxy/doc/source/ldp.dsl.in
 @@ -47,9 +47,9 @@
- ;; this is necessary because right now jadetex does not understand
- ;; symbolic entities, whereas things work well with numeric entities.
--(declare-characteristic preserve-sdata?
--  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"
--  #f)
-+;(declare-characteristic preserve-sdata?
-+;  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"
-+;  #f)
- ;; put the legal notice in a separate file
- (define %generate-legalnotice-link%
\r
+ ;; this is necessary because right now jadetex does not understand\r
+ ;; symbolic entities, whereas things work well with numeric entities.\r
+-(declare-characteristic preserve-sdata?\r
+-  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"\r
+-  #f)\r
++;(declare-characteristic preserve-sdata?\r
++;  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"\r
++;  #f)\r
\r
+ ;; put the legal notice in a separate file\r
+ (define %generate-legalnotice-link%\r
 @@ -257,9 +257,9 @@
- ;; this is necessary because right now jadetex does not understand
- ;; symbolic entities, whereas things work well with numeric entities.
--(declare-characteristic preserve-sdata?
--  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"
--  #f)
-+;(declare-characteristic preserve-sdata?
-+;  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"
-+;  #f)
- ;; put the legal notice in a separate file
- (define %generate-legalnotice-link%
\r
+ ;; this is necessary because right now jadetex does not understand\r
+ ;; symbolic entities, whereas things work well with numeric entities.\r
+-(declare-characteristic preserve-sdata?\r
+-  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"\r
+-  #f)\r
++;(declare-characteristic preserve-sdata?\r
++;  "UNREGISTERED::James Clark//Characteristic::preserve-sdata?"\r
++;  #f)\r
\r
+ ;; put the legal notice in a separate file\r
+ (define %generate-legalnotice-link%\r
index 32669d1..7b53410 100644 (file)
@@ -7,9 +7,9 @@
 
 @DPATCH@
 diff -urNad privoxy~/doc/source/privoxy-man-page.sgml privoxy/doc/source/privoxy-man-page.sgml
---- privoxy~/doc/source/privoxy-man-page.sgml  2006-02-05 22:03:58.000000000 +0100
-+++ privoxy/doc/source/privoxy-man-page.sgml   2006-02-11 18:32:40.000000000 +0100
-@@ -333,7 +333,7 @@
+--- privoxy~/doc/source/privoxy-man-page.sgml
++++ privoxy/doc/source/privoxy-man-page.sgml
+@@ -367,7 +367,7 @@
   
   # Set personal exceptions to the policies in default.action #######
  
@@ -18,33 +18,3 @@ diff -urNad privoxy~/doc/source/privoxy-man-page.sgml privoxy/doc/source/privoxy
   {-crunch-cookies -session-cookies-only}
    .redhat.com
    .sun.com
-diff -urNad privoxy~/doc/source/user-manual.sgml privoxy/doc/source/user-manual.sgml
---- privoxy~/doc/source/user-manual.sgml       2004-01-31 10:49:48.000000000 +0100
-+++ privoxy/doc/source/user-manual.sgml        2006-02-11 18:34:24.000000000 +0100
-@@ -368,7 +368,7 @@
- </para>
- <para>
-- In order not to loose your personal changes and adjustments when updating
-+ In order not to lose your personal changes and adjustments when updating
-  to the latest <literal>default.action</literal> file we <emphasis>strongly
-  recommend</emphasis> that you use <literal>user.action</literal> for your
-  customization of <application>Privoxy</application>. See the <link
-@@ -1720,7 +1720,7 @@
- <para>
-  To determine which actions apply to a request, the URL of the request is
-- compared to all patterns in each action file file. Every time it matches, the list of
-+ compared to all patterns in each <quote>action file</quote> file. Every time it matches, the list of
-  applicable actions for the URL is incrementally updated, using the heading
-  of the section in which the pattern is located. If multiple matches for
-  the same URL set the same action differently, the last match wins. If not, 
-@@ -5251,7 +5251,7 @@
-  blocks of HTML code disappear when a specific symbol is set. We use this
-  for many purposes, one of them being to include the beta warning in all
-  our user interface (CGI) pages when <application>Privoxy</application>
-- in in an alpha or beta development stage:
-+ is in an alpha or beta development stage:
- </para>
- <para>
index 95129c7..b3458fd 100644 (file)
@@ -7,9 +7,9 @@
 
 @DPATCH@
 diff -urNad privoxy~/parsers.c privoxy/parsers.c
---- privoxy~/parsers.c 2006-02-05 16:23:12.000000000 +0100
-+++ privoxy/parsers.c  2006-02-05 16:25:49.000000000 +0100
-@@ -871,8 +871,7 @@
+--- privoxy~/parsers.c
++++ privoxy/parsers.c
+@@ -1248,8 +1248,7 @@
   *
   * Description :  - Prohibit filtering (CT_TABOO) if transfer coding compresses
   *                - Raise the CSP_FLAG_CHUNKED flag if coding is "chunked"
@@ -19,7 +19,7 @@ diff -urNad privoxy~/parsers.c privoxy/parsers.c
   *
   * Parameters  :
   *          1  :  csp = Current client state (buffers, headers, etc...)
-@@ -904,13 +903,13 @@
+@@ -1281,14 +1280,13 @@
  
        /*
         * If the body was modified, it has been
@@ -32,6 +32,7 @@ diff -urNad privoxy~/parsers.c privoxy/parsers.c
        {
           freez(*header);
 -         *header = strdup("Transfer-Encoding: identity");
+-         log_error(LOG_LEVEL_HEADER, "Set: %s", *header);
 -         return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
        }
     }
index b1b9695..8390de2 100644 (file)
@@ -6,67 +6,67 @@
 
 @DPATCH@
 diff -urNad privoxy~/config privoxy/config
---- privoxy~/config    2006-02-11 20:29:59.000000000 +0100
-+++ privoxy/config     2006-02-11 20:30:28.000000000 +0100
+--- privoxy~/config
++++ privoxy/config
 @@ -1,4 +1,4 @@
--#        Sample Configuration File for Privoxy v3.0.x
+-#        Sample Configuration File for Privoxy v3.0.4
 +#        Sample Configuration File for Privoxy
- #  
- #  Copyright (C) 2001-2004 Privoxy Developers http://privoxy.org
- #  
-@@ -104,7 +104,7 @@
- #      flat, except for confdir/templates, where the HTML templates
- #      for CGI output reside (e.g. Privoxy's 404 error page).
- #  
--confdir .
-+confdir /etc/privoxy
- #  
- #  1.2. logdir
-@@ -131,7 +131,7 @@
- #  
- #      No trailing "/", please
- #  
--logdir .
-+logdir /var/log/privoxy
- #  
- #  1.3. actionsfile
-@@ -406,7 +406,7 @@
+ #
+ #  $Id: config,v 1.53 2006/09/07 02:02:56 hal9 Exp $
+ #
+@@ -123,7 +123,7 @@
  #          If set, this option should be the first option in the config
  #          file, because it is used while the config file is being read.
- #  
+ #
 -#user-manual http://www.privoxy.org/user-manual/
 +user-manual /usr/share/doc/privoxy/user-manual
  
- #  
- #  2.2. trust-info-url
-@@ -442,8 +442,8 @@
+ #
+ #  1.2. trust-info-url
+@@ -159,8 +159,8 @@
  #      don't end up locked out from the information on why they were
  #      locked out in the first place!
- #  
+ #
 -trust-info-url  http://www.example.com/why_we_block.html
 -trust-info-url  http://www.example.com/what_we_allow.html
 +#trust-info-url  http://www.example.com/why_we_block.html
 +#trust-info-url  http://www.example.com/what_we_allow.html
  
- #  
- #  2.3. admin-address
-@@ -730,7 +730,7 @@
+ #
+ #  1.3. admin-address
+@@ -266,7 +266,7 @@
+ #      flat, except for confdir/templates, where the HTML templates
+ #      for CGI output reside (e.g. Privoxy's 404 error page).
+ #
+-confdir .
++confdir /etc/privoxy
+ #
+ #  2.2. logdir
+@@ -293,7 +293,7 @@
+ #
+ #      No trailing "/", please
+ #
+-logdir .
++logdir /var/log/privoxy
+ #
+ #  2.3. actionsfile
+@@ -735,7 +735,7 @@
  #      Note that you must have compiled Privoxy with support for this
  #      feature, otherwise this option has no effect.
- #  
+ #
 -enable-remote-toggle  1
 +enable-remote-toggle  0
  
- #  
- #  4.4. enable-edit-actions
-@@ -763,7 +763,7 @@
+ #
+ #  4.4. enable-remote-http-toggle
+@@ -803,7 +803,7 @@
  #      Note that you must have compiled Privoxy with support for this
  #      feature, otherwise this option has no effect.
- #  
+ #
 -enable-edit-actions 1
 +enable-edit-actions 0
  
- #  
- #  4.5. ACLs: permit-access and deny-access
+ #
+ #  4.6. ACLs: permit-access and deny-access
index 8311c66..79e0081 100644 (file)
@@ -5,21 +5,9 @@
 ## DP: Move man page from man section 1 to man section 8.
 
 @DPATCH@
-diff -urNad privoxy~/doc/source/privoxy-man-page.sgml privoxy/doc/source/privoxy-man-page.sgml
---- privoxy~/doc/source/privoxy-man-page.sgml  2006-02-11 23:44:40.000000000 +0100
-+++ privoxy/doc/source/privoxy-man-page.sgml   2006-02-11 23:44:40.000000000 +0100
-@@ -57,7 +57,7 @@
- </refentryinfo>
- <refmeta>
-  <refentrytitle>privoxy</refentrytitle> 
-- <manvolnum>1</manvolnum>
-+ <manvolnum>8</manvolnum>
-  <refmiscinfo>
-   Privoxy &p-version;<![%p-not-stable;[ &p-status;]]>
-  </refmiscinfo>
 diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
---- privoxy~/GNUmakefile.in    2006-02-11 23:44:25.000000000 +0100
-+++ privoxy/GNUmakefile.in     2006-02-11 23:44:40.000000000 +0100
+--- privoxy~/GNUmakefile.in
++++ privoxy/GNUmakefile.in
 @@ -177,7 +177,7 @@
                `find doc/text/ -type f | grep -v "CVS" | grep -v "\.\#" | grep -v ".*~" | grep -v ".cvsignore" | grep -v "TAGS"` \
                `find doc/webserver/ -name "*.html" | grep -v "\(webserver\|team\)\/index\.html"` \
@@ -29,7 +17,7 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
                doc/pdf/*.pdf
  
  #############################################################################
-@@ -520,7 +520,7 @@
+@@ -521,7 +521,7 @@
        $(RM) /etc/init.d/privoxy
        $(RM) /usr/sbin/privoxy
        $(RM) /usr/sbin/rcprivoxy
@@ -38,7 +26,7 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
  
  #############################################################################
  # generic distribution
-@@ -692,7 +692,7 @@
+@@ -697,7 +697,7 @@
        $(RM) doc/man/* doc/webserver/man-page/*.html
  ifneq ($(MAN2HTML),false)
        $(ECHO) "<html><head><title>Privoxy Man page</title><link rel=\"stylesheet\" type=\"text/css\" href=\"../p_web.css\"></head><body><H2>NAME</H2>" > doc/webserver/man-page/privoxy-man-page.html
@@ -47,7 +35,7 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
        $(ECHO) "</body></html>" >> doc/webserver/man-page/privoxy-man-page.html
  else
        $(MAKE) groff2html
-@@ -704,14 +704,14 @@
+@@ -709,14 +709,14 @@
  man: dok-release
        mkdir -p doc/source/temp && cd doc/source/temp && $(RM) * ;\
        nsgmls ../privoxy-man-page.sgml  | sgmlspl ../../../utils/docbook2man/docbook2man-spec.pl &&\
@@ -65,7 +53,7 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
        $(PERL) -pi.bak -e 's/<A .*Contents<\/A>//; s/<A .*man2html<\/A>/man2html/' tmp.html
        $(PERL) -pi.bak -e 's/(<\/HEAD>)/<LINK REL=\"STYLESHEET\" TYPE=\"text\/css\" HREF=\"..\/p_doc.css\"><\/HEAD>/' tmp.html
  # Twice because my version of man2html is pulling in commas and periods in URLs.
-@@ -726,7 +726,7 @@
+@@ -731,7 +731,7 @@
  
  # Otherwise we get plain groff conversion.
  groff2html:
@@ -74,8 +62,8 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
  
  
  # readme page and INSTALL file
-@@ -1122,8 +1122,8 @@
-               $(INSTALL) $(INSTALL_T) $(DOK_WEB)/p_doc.css $$DOC;\
+@@ -1129,8 +1129,8 @@
+               $(INSTALL) $(INSTALL_T) $(DOK_WEB)/p_doc.css $$DOC/user-manual;\
        fi
        @# Not all platforms support gzipped man pages.
 -      @$(ECHO) Installing man page to $(MAN_DEST)/privoxy.1
@@ -85,7 +73,7 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
  
        @# Change the config file default directories according to the configured ones
        @$(ECHO) Rewriting config for this installation
-@@ -1273,7 +1273,7 @@
+@@ -1286,7 +1286,7 @@
  
        @# man page and docs
        @$(ECHO) Removing $(PROGRAM) docs
@@ -94,3 +82,15 @@ diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
        -$(RM) -r $(DOC_DEST) || $(RM) -r $(prefix)/doc/privoxy
  
        @# Log and jarfile and pidfile
+diff -urNad privoxy~/doc/source/privoxy-man-page.sgml privoxy/doc/source/privoxy-man-page.sgml
+--- privoxy~/doc/source/privoxy-man-page.sgml
++++ privoxy/doc/source/privoxy-man-page.sgml
+@@ -57,7 +57,7 @@
+ </refentryinfo>
+ <refmeta>
+  <refentrytitle>privoxy</refentrytitle> 
+- <manvolnum>1</manvolnum>
++ <manvolnum>8</manvolnum>
+  <refmiscinfo>
+   Privoxy &p-version;<![%p-not-stable;[ &p-status;]]>
+  </refmiscinfo>
diff --git a/debian/patches/16_gzip.dpatch b/debian/patches/16_gzip.dpatch
new file mode 100755 (executable)
index 0000000..9d6cc72
--- /dev/null
@@ -0,0 +1,527 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 16_gzip.dpatch by Wil Mahan <wmahan@users.sourceforge.net>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Add deflate-filter support to privoxy, see sf bug id 895531
+## DP: Adapted from privoxy 3.1 to 3.0.x by Roland Rosenfeld.
+## DP: This requires --enable-zlib as configure option in debian/rules
+## DP: and a build dependeny on zlib1g-dev.
+
+@DPATCH@
+diff -urNad privoxy~/actionlist.h privoxy/actionlist.h
+--- privoxy~/actionlist.h
++++ privoxy/actionlist.h
+@@ -116,6 +116,7 @@
+ DEFINE_ACTION_STRING     ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE)
+ DEFINE_CGI_PARAM_RADIO   ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE,     "first", 0)
+ DEFINE_CGI_PARAM_RADIO   ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE,     "last",  1)
++DEFINE_ACTION_BOOL       ("decompress-from-server",     ACTION_DECOMPRESS_IN)
+ DEFINE_ACTION_BOOL       ("downgrade-http-version",     ACTION_DOWNGRADE)
+ DEFINE_ACTION_BOOL       ("fast-redirects",             ACTION_FAST_REDIRECTS)
+ DEFINE_ACTION_MULTI      ("filter",                     ACTION_MULTI_FILTER)
+diff -urNad privoxy~/configure.in privoxy/configure.in
+--- privoxy~/configure.in
++++ privoxy/configure.in
+@@ -1234,6 +1234,20 @@
+                                 libpcrs is available],
+ [ if test $enableval = "no"; then have_pcrs=no; fi ])
++AC_ARG_ENABLE(zlib,
++[  --enable-zlib            Use the zlib library to allow compressing or
++                             decompressing data on the fly.],
++[enableval2=$enableval],
++[enableval2=no])
++if test $enableval2 = yes; then
++  AC_CHECK_LIB(z, zlibVersion, , [
++    AC_MSG_ERROR([Unable to find a copy of zlib.  The zlib library
++is necessary to enable compresion support. ])
++  ]) 
++  AC_DEFINE(FEATURE_ZLIB,1,
++    [ Define to 1 to use compression through the zlib library. ])
++fi
++
+ # If we have libpcre and either we also have pcreposix or
+ # we don't need pcreposix, then link pcre dynamically; else
+diff -urNad privoxy~/default.action.master privoxy/default.action.master
+--- privoxy~/default.action.master
++++ privoxy/default.action.master
+@@ -452,7 +452,7 @@
+ -hide-user-agent \
+ -kill-popups \
+ -limit-connect \
+-+prevent-compression \
++-prevent-compression \
+ -send-vanilla-wafer \
+ -send-wafer \
+ +session-cookies-only \
+diff -urNad privoxy~/filters.c privoxy/filters.c
+--- privoxy~/filters.c
++++ privoxy/filters.c
+@@ -1325,6 +1325,38 @@
+          return(NULL);
+    }
++#ifdef FEATURE_ZLIB
++   /* If the body has a compressed transfer-encoding, uncompress
++    * it first, adjusting size and iob->eod.  Note that
++    * decompression occurs after de-chunking.
++    */
++   if (csp->content_type & CT_GZIP || csp->content_type & CT_DEFLATE)
++   {
++      /* Notice that we at least tried to decompress. */
++      if (JB_ERR_OK != decompress_iob(csp))
++      {
++       /* We failed to decompress the data; there's no point
++        * in continuing since we can't filter.  This is
++        * slightly tricky because we need to remember not to
++        * modify the Content-Encoding header later; using
++        * CT_TABOO flag is a kludge for this purpose.
++        */
++         csp->content_type |= CT_TABOO;
++         return(NULL);
++      }
++      log_error(LOG_LEVEL_RE_FILTER, "Decompressing successful");
++
++      /* Decompression gives us a completely new iob, so we
++       * need to update.
++       */
++      size = csp->iob->eod - csp->iob->cur;
++      old  = csp->iob->cur;
++
++      csp->flags |= CSP_FLAG_MODIFIED;
++   }
++#endif
++
++
+    /*
+     * If the body has a "chunked" transfer-encoding,
+     * get rid of it first, adjusting size and iob->eod
+diff -urNad privoxy~/jcc.c privoxy/jcc.c
+--- privoxy~/jcc.c
++++ privoxy/jcc.c
+@@ -659,6 +659,10 @@
+ #  include <select.h>
+ # endif
++#ifdef FEATURE_ZLIB
++#include <zlib.h>
++#endif
++
+ #endif
+ #include "project.h"
+@@ -1609,6 +1613,8 @@
+             if ((csp->content_type & CT_TEXT) &&  /* It's a text / * MIME-Type */
+                 !http->ssl    &&                  /* We talk plaintext */
++                                       !(csp->content_type & CT_GZIP)    && 
++                                       !(csp->content_type & CT_DEFLATE) && 
+                 block_popups)                     /* Policy allows */
+             {
+                block_popups_now = 1;
+diff -urNad privoxy~/parsers.c privoxy/parsers.c
+--- privoxy~/parsers.c
++++ privoxy/parsers.c
+@@ -433,6 +433,10 @@
+ #include <assert.h>
+ #include <string.h>
++#ifdef FEATURE_ZLIB
++#include <zlib.h>
++#endif
++
+ #if !defined(_WIN32) && !defined(__OS2__)
+ #include <unistd.h>
+ #endif
+@@ -632,6 +636,281 @@
+ }
++#ifdef FEATURE_ZLIB
++/*********************************************************************
++ *
++ * Function    :  decompress_iob
++ *
++ * Description :  Decompress buffered page, expanding the
++ *                buffer as necessary.  csp->iob->cur
++ *                should point to the the beginning of the
++ *                compressed data block.
++ *
++ * Parameters  :
++ *          1  :  csp = Current client state (buffers, headers, etc...)
++ *
++ * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY if out-of-memory
++ *                limit reached, JB_ERR_COMPRESS if error decompressing
++ *                buffer.
++ *
++ *********************************************************************/
++jb_err decompress_iob(struct client_state *csp)
++{
++   char *buf;                    /* new, uncompressed buffer */
++   int bufsize = csp->iob->size; /* allocated size of the new buffer */
++                                 /* Number of bytes at the beginning
++                                * of the iob that we should NOT
++                                * decompress.
++                                */
++   int skip_size = csp->iob->cur - csp->iob->buf;
++   int status;                   /* return status of the inflate() call */
++   z_stream zstr;           /* used by calls to zlib */
++
++   /* This is to protect the parsing of gzipped data, but it should(?)
++    * be valid for deflated data also.
++    */
++   if (bufsize < 10)
++   {
++      log_error (LOG_LEVEL_ERROR, "Buffer too small decompressing iob");
++      return JB_ERR_COMPRESS;
++   }
++
++   if (csp->content_type & CT_GZIP)
++   {
++      /* Our task is slightly complicated by the facts that data
++       * compressed by gzip does not include a zlib header, and
++       * that there is no easily accessible interface in zlib to
++       * handle a gzip header.  We strip off the gzip header by
++       * hand, and later inform zlib not to expect a header.
++       */
++
++      /* Strip off the gzip header.  Please see RFC 1952 for more
++       * explanation of the appropriate fields.
++       */
++      if ((*csp->iob->cur++ != (char)0x1f)
++           || (*csp->iob->cur++ != (char)0x8b)
++         || (*csp->iob->cur++ != Z_DEFLATED))
++      {
++         log_error (LOG_LEVEL_ERROR,
++                   "Invalid gzip header when decompressing");
++         return JB_ERR_COMPRESS;
++      }
++      else {
++         int flags = *csp->iob->cur++;
++         if (flags & 0xe0)
++         {
++            /* The gzip header has reserved bits set; bail out. */
++            log_error (LOG_LEVEL_ERROR,
++                      "Invalid gzip header when decompressing");
++            return JB_ERR_COMPRESS;
++         }
++         csp->iob->cur += 6;
++
++         /* Skip extra fields if necessary. */
++         if (flags & 0x04)
++         {
++            /* Skip a given number of bytes, specified as a 16-bit
++           * little-endian value.
++           */
++            csp->iob->cur += *csp->iob->cur++ + (*csp->iob->cur++ << 8);
++         }
++
++         /* Skip the filename if necessary. */
++         if (flags & 0x08)
++         {
++            /* A null-terminated string follows. */
++            while (*csp->iob->cur++);
++         }
++
++         /* Skip the comment if necessary. */
++         if (flags & 0x10)
++         {
++            while (*csp->iob->cur++);
++         }
++
++         /* Skip the CRC if necessary. */
++         if (flags & 0x02)
++         {
++            csp->iob->cur += 2;
++         }
++      }
++   }
++   else if (csp->content_type & CT_DEFLATE)
++   {
++   log_error (LOG_LEVEL_INFO, "Decompressing deflated iob: %d", *csp->iob->cur);
++      /* In theory (that is, according to RFC 1950), deflate-compressed
++       * data should begin with a two-byte zlib header and have an
++       * adler32 checksum at the end.  It seems that in practice the
++       * only the raw compressed data is sent.  Note that this means that
++       * we are not RFC 1950-compliant here, but the advantage is that
++       * this actually works. :)
++       *
++       * We add a dummy null byte to tell zlib where the data ends,
++       * and later inform it not to expect a header.
++       *
++       * Fortunately, add_to_iob() has thoughtfully null-terminated
++       * the buffer; we can just increment the end pointer to include
++       * the dummy byte.  
++       */
++      csp->iob->eod++;
++   }
++   else
++   {
++      log_error (LOG_LEVEL_ERROR,
++                                        "Unable to determine compression format for decompression");
++      return JB_ERR_COMPRESS;
++   }
++
++   /* Set up the fields required by zlib. */
++   zstr.next_in  = csp->iob->cur;
++   zstr.avail_in = csp->iob->eod - csp->iob->cur;
++   zstr.zalloc   = Z_NULL;
++   zstr.zfree    = Z_NULL;
++   zstr.opaque   = Z_NULL;
++
++   /* Passing -MAX_WBITS to inflateInit2 tells the library
++    * that there is no zlib header.
++    */
++   if (inflateInit2 (&zstr, -MAX_WBITS) != Z_OK)
++   {
++      log_error (LOG_LEVEL_ERROR,
++                "Error initializing decompression");
++      return JB_ERR_COMPRESS;
++   }
++
++   /* Next, we allocate new storage for the inflated data.
++    * We don't modify the existing iob yet, so in case there
++    * is error in decompression we can recover gracefully.
++    */
++   buf = zalloc (bufsize);
++   if (NULL == buf)
++   {
++      log_error (LOG_LEVEL_ERROR,
++                "Out of memory decompressing iob");
++      return JB_ERR_MEMORY;
++   }
++
++   assert(bufsize >= skip_size);
++   memcpy(buf, csp->iob->buf, skip_size);
++   zstr.avail_out = bufsize - skip_size;
++   zstr.next_out  = buf + skip_size;
++
++   /* Try to decompress the whole stream in one shot. */
++   while (Z_BUF_ERROR == (status = inflate(&zstr, Z_FINISH)))
++   { 
++      /* We need to allocate more memory for the output buffer. */
++
++      char *tmpbuf;             /* used for realloc'ing the buffer */
++      int oldbufsize = bufsize; /* keep track of the old bufsize */
++
++      /* If zlib wants more data then there's a problem, because
++       * the complete compressed file should have been buffered.
++       */
++      if (0 == zstr.avail_in)
++      {
++         log_error(LOG_LEVEL_ERROR,
++                  "Unexpected end of compressed iob");
++         return JB_ERR_COMPRESS;
++      }
++
++      /* If we tried the limit and still didn't have enough
++       * memory, just give up.
++       */
++      if (bufsize == csp->config->buffer_limit)
++      {
++         log_error(LOG_LEVEL_ERROR, "Out of memory decompressing iob");
++         return JB_ERR_MEMORY;
++      }
++
++      /* Try doubling the buffer size each time. */
++      bufsize *= 2;
++
++      /* Don't exceed the buffer limit. */
++      if (bufsize > csp->config->buffer_limit)
++      {
++         bufsize = csp->config->buffer_limit;
++      }
++    
++      /* Try to allocate the new buffer. */
++      tmpbuf = realloc(buf, bufsize);
++      if (NULL == tmpbuf)
++      {
++         log_error(LOG_LEVEL_ERROR, "Out of memory decompressing iob");
++       freez(buf);
++         return JB_ERR_MEMORY;
++      }
++      else
++      {
++         char *oldnext_out = zstr.next_out;
++
++         /* Update the fields for inflate() to use the new
++        * buffer, which may be in a different location from
++        * the old one.
++        */
++         zstr.avail_out += bufsize - oldbufsize;
++       zstr.next_out = tmpbuf + bufsize - zstr.avail_out;
++
++         /* Compare with an uglier method of calculating these values
++        * that doesn't require the extra oldbufsize variable.
++        */
++         assert(zstr.avail_out ==
++                tmpbuf + bufsize - (char *)zstr.next_out);
++         assert((char *)zstr.next_out ==
++                tmpbuf + ((char *)oldnext_out - buf));
++         assert(zstr.avail_out > 0);
++
++         buf = tmpbuf;
++      }
++   }
++
++   inflateEnd(&zstr);
++   if (status != Z_STREAM_END)
++   {
++      /* We failed to decompress the stream. */
++      log_error(LOG_LEVEL_ERROR,
++               "Error in decompressing to the buffer (iob): %s",
++             zstr.msg);
++      return JB_ERR_COMPRESS;
++   }
++
++   /* Finally, we can actually update the iob, since the
++    * decompression was successful.  First, free the old
++    * buffer.
++    */
++   freez(csp->iob->buf);
++
++   /* Now, update the iob to use the new buffer. */
++   csp->iob->buf  = buf;
++   csp->iob->cur  = csp->iob->buf + skip_size;
++   csp->iob->eod  = zstr.next_out;
++   csp->iob->size = bufsize;
++  
++   /* Make sure the new uncompressed iob obeys some minimal
++    * consistency conditions.
++    */
++   if ((csp->iob->buf < csp->iob->cur)
++       && (csp->iob->cur <= csp->iob->eod)
++       && (csp->iob->eod <= csp->iob->buf + csp->iob->size))
++   {
++         char t = csp->iob->cur[100];
++         csp->iob->cur[100] = 0;
++      log_error(LOG_LEVEL_INFO,
++               "Sucessfully decompressed: %s", csp->iob->cur);
++         csp->iob->cur[100] = t;
++      return JB_ERR_OK;
++   }
++   else
++   {
++      /* It seems that zlib did something weird. */
++      log_error(LOG_LEVEL_ERROR,
++               "Unexpected error decompressing the buffer (iob): %d==%d, %d>%d, %d<%d", csp->iob->cur,  csp->iob->buf + skip_size, csp->iob->eod, csp->iob->buf, csp->iob->eod, csp->iob->buf + csp->iob->size);
++      return JB_ERR_COMPRESS;
++   }
++
++}
++#endif /* defined(FEATURE_ZLIB) */
++
++
+ /*********************************************************************
+  *
+  * Function    :  get_header
+@@ -936,13 +1215,59 @@
+  *********************************************************************/
+ jb_err server_content_encoding(struct client_state *csp, char **header)
+ {
++#ifdef FEATURE_ZLIB
++   if (strstr(*header, "gzip"))
++   {
++      /*
++       * If the body was modified, we have tried to
++       * decompress it, so adjust the header if necessary.
++       */
++      if ((csp->flags & CSP_FLAG_MODIFIED) /* we attempted to decompress   */
++      && !(csp->content_type & CT_TABOO))  /* decompression was successful */
++      {
++         freez(*header);
++         *header = strdup("Content-Encoding: identity");
++         return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
++      }
++      else
++      {
++        csp->content_type |= CT_GZIP;
++      }
++   }
++   else if (strstr(*header, "deflate"))
++   {
++      /*
++       * If the body was modified, we have tried to
++       * decompress it, so adjust the header if necessary.
++       */
++      if ((csp->flags & CSP_FLAG_MODIFIED) /* we attempted to decompress   */
++      && !(csp->content_type & CT_TABOO))  /* decompression was successful */
++      {
++         freez(*header);
++         *header = strdup("Content-Encoding: identity");
++         return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
++      }
++      else
++      {
++        csp->content_type |= CT_DEFLATE;
++      }
++   }
++   else if (strstr(*header, "compress"))
++   {
++      /* We can't decompress this; therefore we can't filter
++       * it either.
++       */
++      csp->content_type |= CT_TABOO;
++   }
++#else /* !defined(FEATURE_GZIP) */
+    /*
+     * Turn off pcrs and gif filtering if body compressed
+     */
+    if (strstr(*header, "gzip") || strstr(*header, "compress") || strstr(*header, "deflate"))
+    {
+-      csp->content_type = CT_TABOO;
++      csp->content_type |= CT_TABOO;
+    }
++#endif /* !defined(FEATURE_GZIP) */
+    return JB_ERR_OK;
+diff -urNad privoxy~/parsers.h privoxy/parsers.h
+--- privoxy~/parsers.h
++++ privoxy/parsers.h
+@@ -194,6 +194,7 @@
+ extern int flush_socket(jb_socket fd, struct client_state *csp);
+ extern jb_err add_to_iob(struct client_state *csp, char *buf, int n);
++extern jb_err decompress_iob(struct client_state *csp);
+ extern char *get_header(struct client_state *csp);
+ extern char *get_header_value(const struct list *header_list, const char *header_name);
+ extern char *sed(const struct parsers pats[], const add_header_func_ptr more_headers[], struct client_state *csp);
+diff -urNad privoxy~/project.h privoxy/project.h
+--- privoxy~/project.h
++++ privoxy/project.h
+@@ -563,7 +563,7 @@
+ #define JB_ERR_PARSE      4 /**< Error parsing file                       */
+ #define JB_ERR_MODIFIED   5 /**< File has been modified outside of the  
+                                  CGI actions editor.                      */
+-
++#define JB_ERR_COMPRESS   6 /**< Error on decompression                   */
+ /**
+  * This macro is used to free a pointer that may be NULL.
+@@ -818,6 +818,15 @@
+ #define CT_TABOO  4 /**< csp->content_type bitmask:
+                          DO NOT filter, irrespective of other flags. */
++/* Although these are not, strictly speaking, content types
++ * (they are content encodings), it is simple to handle
++ * them as such.
++ */
++#define CT_GZIP     8 /**< csp->content_type bitmask:
++                         gzip-compressed data. */
++#define CT_DEFLATE 16 /**< csp->content_type bitmask:
++                         zlib-compressed data. */
++
+ /**
+  * The mask which includes all actions.
+  */
+@@ -862,6 +871,8 @@
+ #define ACTION_VANILLA_WAFER   0x00008000UL
+ /** Action bitmap: Limit CONNECT requests to safe ports. */
+ #define ACTION_LIMIT_CONNECT   0x00010000UL
++/** Action bitmap: Uncompress incoming text for filtering. */
++#define ACTION_DECOMPRESS_IN   0x00020000UL
+ /** Action string index: How to deanimate GIFs */
+ #define ACTION_STRING_DEANIMATE     0
diff --git a/debian/patches/17_502_no_such_domain.dpatch b/debian/patches/17_502_no_such_domain.dpatch
new file mode 100755 (executable)
index 0000000..6eafee7
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 17_502_no_such_domain.dpatch by Roland Rosenfeld <roland@debian.org>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Changes the 404 HTTP status code of the "No such Domain" template
+## DP: to 502 Bad Gateway, which seems to be more correct according to 
+## DP: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+
+@DPATCH@
+diff -urNad privoxy~/templates/no-such-domain privoxy/templates/no-such-domain
+--- privoxy~/templates/no-such-domain
++++ privoxy/templates/no-such-domain
+@@ -79,7 +79,7 @@
+ <html>
+ <head>
+-  <title>404 - No such Domain (Privoxy@@my-hostname@)</title>
++  <title>502 - Bad Gateway (Privoxy@@my-hostname@)</title>
+   <meta http-equiv="Content-Style-Type" content="text/css">
+   <meta http-equiv="Content-Script-Type" content="text/javascript">
+   <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+@@ -92,7 +92,7 @@
+   <table cellpadding="20" cellspacing="10" border="0" width="100%">
+     <tr>
+       <td class="status">
+-        404
++        502
+       </td>     
+       <td class="title" style="width: 100%">
diff --git a/debian/patches/18_dns_retry.dpatch b/debian/patches/18_dns_retry.dpatch
new file mode 100755 (executable)
index 0000000..cff2cbb
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 18_dns_retry.dpatch by Fabian Keil <fk@fabiankeil.de>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Retries DNS queries 10 times before giving up.
+## DP: Extracted from http://www.fabiankeil.de/sourcecode/privoxy/
+
+@DPATCH@
+diff -urNad privoxy~/jbsockets.c privoxy/jbsockets.c
+--- privoxy~/jbsockets.c
++++ privoxy/jbsockets.c
+@@ -780,6 +780,7 @@
+ {
+    struct sockaddr_in inaddr;
+    struct hostent *hostp;
++   unsigned int dns_retries = 0;
+ #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS)
+    struct hostent result;
+ #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
+@@ -800,8 +801,13 @@
+    if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1)
+    {
+ #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS)
+-      gethostbyname_r(host, &result, hbuf,
+-                      HOSTENT_BUFFER_SIZE, &hostp, &thd_err);
++              while ( gethostbyname_r(host, &result, hbuf,
++                                                                              HOSTENT_BUFFER_SIZE, &hostp, &thd_err)
++                                && (thd_err == TRY_AGAIN) && (dns_retries++ < 10) )
++              {   
++                log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.",
++                                              dns_retries, host);
++      }
+ #elif defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
+       hostp = gethostbyname_r(host, &result, hbuf,
+                       HOSTENT_BUFFER_SIZE, &thd_err);
+@@ -816,10 +822,20 @@
+       }
+ #elif OSX_DARWIN
+       pthread_mutex_lock(&gethostbyname_mutex);
+-      hostp = gethostbyname(host);
++              while ( NULL == (hostp = gethostbyname(host))
++                                && (h_errno == TRY_AGAIN) && (dns_retries++ < 10) )
++              {   
++                log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.",
++                                              dns_retries, host);
++      }
+       pthread_mutex_unlock(&gethostbyname_mutex);
+ #else
+-      hostp = gethostbyname(host);
++      while ( NULL == (hostp = gethostbyname(host))
++            && (h_errno == TRY_AGAIN) && (dns_retries++ < 10) )
++      {
++         log_error(LOG_LEVEL_ERROR, "%u. timeout while trying to resolve %s. Trying again.",
++                                                dns_retries, host);
++      }
+ #endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */
+       /*
+        * On Mac OSX, if a domain exists but doesn't have a type A
diff --git a/debian/patches/19_manpage_fixup.dpatch b/debian/patches/19_manpage_fixup.dpatch
new file mode 100755 (executable)
index 0000000..bdb3c11
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 19_manpage_fixup.dpatch by Roland Rosenfeld <roland@debian.org>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Convert Latin-1 char á to groff eqivalent in man page.
+## DP: Quote minus signs to differenciate them from hyphens.
+
+@DPATCH@
+diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
+--- privoxy~/GNUmakefile.in
++++ privoxy/GNUmakefile.in
+@@ -710,6 +710,10 @@
+       mkdir -p doc/source/temp && cd doc/source/temp && $(RM) * ;\
+       nsgmls ../privoxy-man-page.sgml  | sgmlspl ../../../utils/docbook2man/docbook2man-spec.pl &&\
+       perl -pi.bak -e 's/ <URL:.*>//; s/\[ /\[/g' privoxy.8 ;\
++      perl -pi.bak -e "s/\[ /\[/g;s/á/\\\\['a]/g" privoxy.8; \
++      perl -pi.bak -e 's/([ {])-([a-z])/$$1\\-$$2/g' privoxy.8; \
++      perl -pi.bak -e 's/ --([a-z])/ \\-\\-$$1/g' privoxy.8; \
++      perl -pi.bak -e 's/\\fB--/\\fB\\-\\-/g' privoxy.8; \
+       $(DB) ../privoxy-man-page.sgml && $(MV) -f privoxy.8 ../../../privoxy.8
+ # For those with man2html ala RH7s.
+@@ -722,6 +726,7 @@
+ # Twice because my version of man2html is pulling in commas and periods in URLs.
+       $(PERL) -pi.bak -e 's/(<A.*),(">)/$$1$$2/g' tmp.html
+       $(PERL) -pi.bak -e 's,\.">,">,g' tmp.html
++      $(PERL) -pi.bak -e "s/\['a\]/\&aacute;/g" tmp.html
+ # Get rid of spurious \a from conversion. (How to do this with perl?)
+       $(SED) -e 's/\a//g' tmp.html > doc/webserver/man-page/privoxy-man-page.html && $(RM) tmp.*
+ else
diff --git a/debian/patches/20_makefile_fixup.dpatch b/debian/patches/20_makefile_fixup.dpatch
new file mode 100755 (executable)
index 0000000..2717bb9
--- /dev/null
@@ -0,0 +1,23 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 20_makefile_fixup.dpatch by Roland Rosenfeld <roland@debian.org>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Fix syntax error in GNUmakefile.in
+
+@DPATCH@
+diff -urNad privoxy~/GNUmakefile.in privoxy/GNUmakefile.in
+--- privoxy~/GNUmakefile.in
++++ privoxy/GNUmakefile.in
+@@ -678,9 +678,9 @@
+       $(RM) -r doc/source/user-manual/
+       mkdir -p doc/text doc/source/user-manual
+       cd doc/source/user-manual && $(DB) -iuser-man ../user-manual.sgml && cd .. && cp user-manual/*.html ../webserver/user-manual/
+-      @#FIXME: temp fix so same stylesheet gets in more than one place so it works \
+-      @# for all doc set-ups, including the 'user manual' config option in local \
+-      @#system where it MUST be in same directory as html.
++      #FIXME: temp fix so same stylesheet gets in more than one place so it works \
++      # for all doc set-ups, including the 'user manual' config option in local \
++      #system where it MUST be in same directory as html.
+       $(PERL) -pi.bak -e 's/<\/head/\n<LINK REL=\"STYLESHEET\" TYPE=\"text\/css\" HREF=\"p_doc.css\">\n<\/head/i' doc/webserver/user-manual/*html
+       cd doc/source && $(DB) -iuser-man -V nochunks user-manual.sgml > tmp.html && $(WDUMP) tmp.html > ../text/user-manual.txt && $(RM) -r tmp.html user-manual
diff --git a/debian/patches/21_version_3.0.4.dpatch b/debian/patches/21_version_3.0.4.dpatch
new file mode 100755 (executable)
index 0000000..821f5ef
--- /dev/null
@@ -0,0 +1,22 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 21_version_3.0.4.dpatch by Roland Rosenfeld <roland@debian.org>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Bump version to 3.0.4 otherwise privoxy complains in the version check.
+
+@DPATCH@
+diff -urNad privoxy~/configure.in privoxy/configure.in
+--- privoxy~/configure.in
++++ privoxy/configure.in
+@@ -488,9 +488,9 @@
+ dnl Application version number
+ dnl =================================================================
+-VERSION_MAJOR=0
++VERSION_MAJOR=3
+ VERSION_MINOR=0
+-VERSION_POINT=0
++VERSION_POINT=4
+ CODE_STATUS="UNRELEASED"
+ dnl CODE_STATUS can be "alpha", "beta", or "stable", and will be