diff options
author | garga <garga@FreeBSD.org> | 2005-12-21 00:01:15 +0800 |
---|---|---|
committer | garga <garga@FreeBSD.org> | 2005-12-21 00:01:15 +0800 |
commit | 8079a638474d94568e8ebf903f7eba1b64f33af8 (patch) | |
tree | b4c566cb3d469539361a50838b6b88eec94aaa9e | |
parent | 0599f2ccf0366c4966935c9287d78128f14906fb (diff) | |
download | freebsd-ports-gnome-8079a638474d94568e8ebf903f7eba1b64f33af8.tar.gz freebsd-ports-gnome-8079a638474d94568e8ebf903f7eba1b64f33af8.tar.zst freebsd-ports-gnome-8079a638474d94568e8ebf903f7eba1b64f33af8.zip |
- Integrate vendor patch to fix a problem with the SMB helper when
--enable-ntlm-fail-open was specified as an additional configuration
option (squid bug #1022).
The port does not enable this option by default; document it, while at it.
- Add SHA256 checksum for the squid tarball
- Integrate ICAP client support based upon the icap project's CVS repository,
turned off by default.
To activate it, build the port with WITH_SQUID_ICAP defined or rerun
'make config'.
- Bump PORTREVISION
PR: ports/90688
Submitted by: maintainer
24 files changed, 44982 insertions, 0 deletions
diff --git a/www/squid/Makefile b/www/squid/Makefile index 31916d4cb34e..1f75bf5d90f8 100644 --- a/www/squid/Makefile +++ b/www/squid/Makefile @@ -66,10 +66,14 @@ # Override the maximum number of filedescriptors. Useful if you # build as another user who is not privileged to use the amount # of filedescriptors the resulting binary is expected to support. +# --enable-ntlm-fail-open +# Enable NTLM fail open, where a helper that fails one of the +# Authentication steps can allow squid to still authenticate the user # PORTNAME= squid PORTVERSION= 2.5.12 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= \ ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ @@ -82,6 +86,7 @@ DISTNAME= squid-2.5.STABLE12 DIST_SUBDIR= squid2.5 PATCH_SITES= http://www.squid-cache.org/Versions/v2/2.5/bugs/ +PATCHFILES= squid-2.5.STABLE12-SMB_BadFetch.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -120,6 +125,7 @@ OPTIONS= SQUID_LDAP_AUTH "Install LDAP authentication helpers" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ + SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ SQUID_COSS "Enable the COSS storage scheme" off \ SQUID_LARGEFILE "Support log and cache files >2GB" off \ @@ -293,6 +299,12 @@ EXTRA_PATCHES+= ${PATCHDIR}/follow_xff-2.5.patch \ ${PATCHDIR}/follow_xff-configure.patch CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif +.if defined(WITH_SQUID_ICAP) +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.5-core.patch \ + ${PATCHDIR}/icap-2.5-bootstrap.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE +.endif .if defined(WITH_SQUID_LARGEFILE) CONFIGURE_ARGS+= --with-large-files --enable-large-cache-files .endif diff --git a/www/squid/distinfo b/www/squid/distinfo index 5d1b5428ba58..3e55ac8d1717 100644 --- a/www/squid/distinfo +++ b/www/squid/distinfo @@ -1,2 +1,6 @@ MD5 (squid2.5/squid-2.5.STABLE12.tar.bz2) = 7354255015b3772a1e024dfac173e48c +SHA256 (squid2.5/squid-2.5.STABLE12.tar.bz2) = ba0ccd956323f0dad46c19aa8d40c537846fedfc3778b5730e5610f16c0d9af1 SIZE (squid2.5/squid-2.5.STABLE12.tar.bz2) = 1075111 +MD5 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 8e83b776c0d015bd4137cc1ca08f6d38 +SHA256 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 9ca8427c2eb9e5cbdb5a49fb5cb94fc00853ad965f87666f8fc35236e98bc0ae +SIZE (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 826 diff --git a/www/squid/files/icap-2.5-bootstrap.patch b/www/squid/files/icap-2.5-bootstrap.patch new file mode 100644 index 000000000000..247ca0c94cbc --- /dev/null +++ b/www/squid/files/icap-2.5-bootstrap.patch @@ -0,0 +1,422 @@ +Patch 2 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.5-core.patch for further information. + +Patch last updated: 2005-12-17 + +--- configure.orig Sat Oct 22 11:56:01 2005 ++++ configure Sat Dec 17 17:45:21 2005 +@@ -70,6 +70,8 @@ + ac_help="$ac_help + --enable-delay-pools Enable delay pools to limit bandwidth usage" + ac_help="$ac_help ++ --enable-icap-support Enable iCAP client capability" ++ac_help="$ac_help + --enable-useragent-log Enable logging of User-Agent header" + ac_help="$ac_help + --enable-referer-log Enable logging of Referer header" +@@ -2170,6 +2172,38 @@ + + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ cat >> confdefs.h <<\EOF ++#define HS_FEAT_ICAP 1 ++EOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ fi ++ ++fi ++ ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -7428,14 +7462,14 @@ + fi + ;; + esac +- echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 +-echo "configure:7433: checking for main in -lpthread" >&5 ++ echo $ac_n "checking for main in -pthread""... $ac_c" 1>&6 ++echo "configure:7433: checking for main in -pthread" >&5 + ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 + else + ac_save_LIBS="$LIBS" +-LIBS="-lpthread $LIBS" ++LIBS="-pthread $LIBS" + cat > conftest.$ac_ext <<EOF + #line 7441 "configure" + #include "confdefs.h" +@@ -7465,7 +7499,7 @@ + #define $ac_tr_lib 1 + EOF + +- LIBS="-lpthread $LIBS" ++ LIBS="-pthread $LIBS" + + else + echo "$ac_t""no" 1>&6 +@@ -7769,6 +7803,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strtoll \ + sysconf \ + syslog \ +@@ -7898,6 +7934,50 @@ + fi + fi + ++ ++if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++fi ++ ++ ++ ++ + echo $ac_n "checking if va_copy is implemented""... $ac_c" 1>&6 + echo "configure:7903: checking if va_copy is implemented" >&5 + if eval "test \"`echo '$''{'ac_cv_func_va_copy'+set}'`\" = set"; then +@@ -9072,6 +9152,8 @@ + s%@ENABLE_PINGER_FALSE@%$ENABLE_PINGER_FALSE%g + s%@USE_DELAY_POOLS_TRUE@%$USE_DELAY_POOLS_TRUE%g + s%@USE_DELAY_POOLS_FALSE@%$USE_DELAY_POOLS_FALSE%g ++s%@USE_ICAP_TRUE@%$USE_ICAP_TRUE%g ++s%@USE_ICAP_FALSE@%$USE_ICAP_FALSE%g + s%@USE_SNMP_TRUE@%$USE_SNMP_TRUE%g + s%@USE_SNMP_FALSE@%$USE_SNMP_FALSE%g + s%@SNMPLIB@%$SNMPLIB%g +@@ -9118,6 +9200,10 @@ + s%@LIB_LBER@%$LIB_LBER%g + s%@NEED_OWN_SNPRINTF_TRUE@%$NEED_OWN_SNPRINTF_TRUE%g + s%@NEED_OWN_SNPRINTF_FALSE@%$NEED_OWN_SNPRINTF_FALSE%g ++s%@NEED_OWN_STRNSTR_TRUE@%$NEED_OWN_STRNSTR_TRUE%g ++s%@NEED_OWN_STRNSTR_FALSE@%$NEED_OWN_STRNSTR_FALSE%g ++s%@NEED_OWN_STRCASESTR_TRUE@%$NEED_OWN_STRCASESTR_TRUE%g ++s%@NEED_OWN_STRCASESTR_FALSE@%$NEED_OWN_STRCASESTR_FALSE%g + s%@REGEXLIB@%$REGEXLIB%g + s%@LIBREGEX@%$LIBREGEX%g + s%@LIBOBJS@%$LIBOBJS%g +--- include/autoconf.h.in.orig Tue Sep 13 02:12:34 2005 ++++ include/autoconf.h.in Sat Dec 17 17:45:21 2005 +@@ -124,6 +124,11 @@ + */ + #undef DELAY_POOLS + ++/* ++ * ICAP - Internet Content Adaptation Protocol ++ */ ++#undef HS_FEAT_ICAP ++ + /* + * If you want to log User-Agent request header values, define this. + * By default, they are written to useragent.log in the Squid log +@@ -574,6 +579,12 @@ + + /* Define if you have the statfs function. */ + #undef HAVE_STATFS ++ ++/* Define if you have the strcasestr function. */ ++#undef HAVE_STRCASESTR ++ ++/* Define if you have the strnstr function. */ ++#undef HAVE_STRNSTR + + /* Define if you have the strerror function. */ + #undef HAVE_STRERROR +--- lib/Makefile.in.orig Wed Sep 28 22:57:20 2005 ++++ lib/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -123,6 +123,13 @@ + + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = ++ ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++ ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++ + @NEED_OWN_MD5_TRUE@MD5SOURCE = md5.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = + +@@ -158,6 +165,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +@@ -196,13 +205,18 @@ + @NEED_OWN_MD5_FALSE@am__objects_1 = + @NEED_OWN_SNPRINTF_FALSE@am__objects_2 = + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) ++@NEED_OWN_STRNSTR_FALSE@am__objects_3 = ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_FALSE@am__objects_4 = + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) stub_memaccount.$(OBJEXT) \ ++ util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) cru + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -224,15 +238,16 @@ + @AMDEP_TRUE@ $(DEPDIR)/dlmalloc.Po $(DEPDIR)/drand48.Po \ + @AMDEP_TRUE@ $(DEPDIR)/getfullhostname.Po $(DEPDIR)/hash.Po \ + @AMDEP_TRUE@ $(DEPDIR)/heap.Po $(DEPDIR)/html_quote.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/initgroups.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/iso3307.Po $(DEPDIR)/md5.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/ntlmauth.Po $(DEPDIR)/radix.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1035.Po $(DEPDIR)/rfc1123.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1738.Po $(DEPDIR)/rfc2617.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/safe_inet_addr.Po $(DEPDIR)/snprintf.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/splay.Po $(DEPDIR)/strerror.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/stub_memaccount.Po $(DEPDIR)/tempnam.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uudecode.Po ++@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/iso3307.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/md5.Po $(DEPDIR)/ntlmauth.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/radix.Po $(DEPDIR)/rfc1035.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc1123.Po $(DEPDIR)/rfc1738.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc2617.Po $(DEPDIR)/safe_inet_addr.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/snprintf.Po $(DEPDIR)/splay.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strcasestr.Po $(DEPDIR)/strerror.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strnstr.Po $(DEPDIR)/stub_memaccount.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/tempnam.Po $(DEPDIR)/util.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/uudecode.Po + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -241,8 +256,8 @@ + DIST_SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) \ + $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) \ + $(libregex_a_SOURCES) +-DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c \ +- initgroups.c strerror.c tempnam.c ++DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c strerror.c \ ++ tempnam.c + SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) $(libregex_a_SOURCES) + + all: all-am +@@ -295,7 +310,6 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/heap.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/html_quote.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/inet_ntoa.Po@am__quote@ +-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/initgroups.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iso3307.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/md5.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ntlmauth.Po@am__quote@ +@@ -307,7 +321,9 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/safe_inet_addr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splay.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerror.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tempnam.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Wed Sep 28 22:57:21 2005 ++++ src/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -125,6 +125,9 @@ + install_sh = @install_sh@ + makesnmplib = @makesnmplib@ + ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c ++@USE_ICAP_FALSE@ICAPSOURCE = ++ + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver +@@ -249,6 +252,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -468,54 +472,58 @@ + pinger_LDADD = $(LDADD) + pinger_DEPENDENCIES = + pinger_LDFLAGS = +-@USE_DELAY_POOLS_TRUE@am__objects_3 = delay_pools.$(OBJEXT) +-@USE_DELAY_POOLS_FALSE@am__objects_3 = +-@USE_DNSSERVER_FALSE@am__objects_4 = dns_internal.$(OBJEXT) +-@USE_DNSSERVER_TRUE@am__objects_4 = dns.$(OBJEXT) +-@ENABLE_HTCP_TRUE@am__objects_5 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_FALSE@am__objects_6 = +-@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) +-@USE_SNMP_FALSE@am__objects_7 = +-@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) +-@ENABLE_SSL_FALSE@am__objects_8 = +-@ENABLE_UNLINKD_FALSE@am__objects_9 = +-@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_10 = ++@USE_DELAY_POOLS_FALSE@am__objects_5 = ++@USE_DELAY_POOLS_TRUE@am__objects_5 = delay_pools.$(OBJEXT) ++@USE_DNSSERVER_FALSE@am__objects_6 = dns_internal.$(OBJEXT) ++@USE_DNSSERVER_TRUE@am__objects_6 = dns.$(OBJEXT) ++@ENABLE_HTCP_TRUE@am__objects_7 = htcp.$(OBJEXT) ++@USE_ICAP_TRUE@am__objects_8 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@USE_ICAP_FALSE@am__objects_8 = ++@MAKE_LEAKFINDER_TRUE@am__objects_9 = leakfinder.$(OBJEXT) ++@MAKE_LEAKFINDER_FALSE@am__objects_9 = ++@USE_SNMP_TRUE@am__objects_10 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) ++@USE_SNMP_FALSE@am__objects_10 = ++@ENABLE_SSL_FALSE@am__objects_11 = ++@ENABLE_SSL_TRUE@am__objects_11 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_12 = unlinkd.$(OBJEXT) ++@ENABLE_UNLINKD_FALSE@am__objects_12 = ++@ENABLE_WIN32SPECIFIC_FALSE@am__objects_13 = ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_13 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \ + cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \ + client_db.$(OBJEXT) client_side.$(OBJEXT) comm.$(OBJEXT) \ +- comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_3) \ +- disk.$(OBJEXT) $(am__objects_4) errorpage.$(OBJEXT) \ ++ comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \ ++ disk.$(OBJEXT) $(am__objects_6) errorpage.$(OBJEXT) \ + ETag.$(OBJEXT) event.$(OBJEXT) external_acl.$(OBJEXT) \ + fd.$(OBJEXT) filemap.$(OBJEXT) forward.$(OBJEXT) \ + fqdncache.$(OBJEXT) ftp.$(OBJEXT) gopher.$(OBJEXT) \ +- helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ ++ helper.$(OBJEXT) $(am__objects_7) http.$(OBJEXT) \ + HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_6) \ +- logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_8) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_9) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ + MemPool.$(OBJEXT) MemBuf.$(OBJEXT) mime.$(OBJEXT) \ + multicast.$(OBJEXT) neighbors.$(OBJEXT) net_db.$(OBJEXT) \ + Packer.$(OBJEXT) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \ + peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ +- ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_10) \ ++ ssl.$(OBJEXT) $(am__objects_11) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_12) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- whois.$(OBJEXT) $(am__objects_10) ++ whois.$(OBJEXT) $(am__objects_13) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -563,7 +571,9 @@ + @AMDEP_TRUE@ $(DEPDIR)/fqdncache.Po $(DEPDIR)/ftp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/globals.Po $(DEPDIR)/gopher.Po \ + @AMDEP_TRUE@ $(DEPDIR)/helper.Po $(DEPDIR)/htcp.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icmp.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icap_common.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_opt.Po $(DEPDIR)/icap_reqmod.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_respmod.Po $(DEPDIR)/icmp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/icp_v2.Po $(DEPDIR)/icp_v3.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ident.Po $(DEPDIR)/internal.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ipc.Po $(DEPDIR)/ipcache.Po \ +@@ -777,6 +787,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v3.Po@am__quote@ diff --git a/www/squid/files/icap-2.5-core.patch b/www/squid/files/icap-2.5-core.patch new file mode 100644 index 000000000000..22d209c18fb4 --- /dev/null +++ b/www/squid/files/icap-2.5-core.patch @@ -0,0 +1,7059 @@ +Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch only contains the parts of the original patchset that +actually implement the ICAP client functionality. The updates to +the build infrastructure are omitted to avoid the need to run an +autotools bootstrap. Instead, we simulate said bootstrapping with +a second patch, icap-2.5-bootstrap.patch. + +The patchset was pulled from the project's CVS repository +at cvs.devel.squid-cache.org using + +cvs diff -u -b -N -kk -rs2_5 -ricap-2_5 + +See also +<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5> +for the "official" auto-generated patchset. + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2005-12-17 + +Index: errors/Bulgarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Bulgarian/ERR_ICAP_FAILURE +diff -N errors/Bulgarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Catalan/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Catalan/ERR_ICAP_FAILURE +diff -N errors/Catalan/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Czech/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Czech/ERR_ICAP_FAILURE +diff -N errors/Czech/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Danish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Danish/ERR_ICAP_FAILURE +diff -N errors/Danish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Dutch/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Dutch/ERR_ICAP_FAILURE +diff -N errors/Dutch/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/English/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/English/ERR_ICAP_FAILURE +diff -N errors/English/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Estonian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Estonian/ERR_ICAP_FAILURE +diff -N errors/Estonian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Finnish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Finnish/ERR_ICAP_FAILURE +diff -N errors/Finnish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/French/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/French/ERR_ICAP_FAILURE +diff -N errors/French/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/German/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/German/ERR_ICAP_FAILURE +diff -N errors/German/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2 +@@ -0,0 +1,33 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>FEHLER</H1> ++<H2>Der angeforderte URL konnte nicht geholt werden</H2> ++<HR noshade size="1px"> ++<P> ++Während des Versuches, den URL<BR> ++<A HREF="%U">%U</A> ++ ++<BR> ++zu laden, trat der folgende Fehler auf: ++<UL> ++<LI> ++<STRONG> ++ICAP-Protokollfehler ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: ++<UL> ++<LI>Nicht erreichbarer ICAP-Server ++<LI>Ungültige Antwort vom ICAP-Server ++ ++</UL> ++</P> ++ ++<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Greek/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Greek/ERR_ICAP_FAILURE +diff -N errors/Greek/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hebrew/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hebrew/ERR_ICAP_FAILURE +diff -N errors/Hebrew/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hungarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hungarian/ERR_ICAP_FAILURE +diff -N errors/Hungarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Italian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Italian/ERR_ICAP_FAILURE +diff -N errors/Italian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Japanese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Japanese/ERR_ICAP_FAILURE +diff -N errors/Japanese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Korean/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Korean/ERR_ICAP_FAILURE +diff -N errors/Korean/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Lithuanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Lithuanian/ERR_ICAP_FAILURE +diff -N errors/Lithuanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Polish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Polish/ERR_ICAP_FAILURE +diff -N errors/Polish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Portuguese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Portuguese/ERR_ICAP_FAILURE +diff -N errors/Portuguese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Romanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Romanian/ERR_ICAP_FAILURE +diff -N errors/Romanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-1251/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-1251/ERR_ICAP_FAILURE +diff -N errors/Russian-1251/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-koi8-r/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE +diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Serbian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Serbian/ERR_ICAP_FAILURE +diff -N errors/Serbian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Simplify_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE +diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Slovak/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Slovak/ERR_ICAP_FAILURE +diff -N errors/Slovak/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Spanish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Spanish/ERR_ICAP_FAILURE +diff -N errors/Spanish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Swedish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Swedish/ERR_ICAP_FAILURE +diff -N errors/Swedish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Traditional_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE +diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Turkish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Turkish/ERR_ICAP_FAILURE +diff -N errors/Turkish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.10 +retrieving revision 1.10.30.2 +diff -p -u -b -r1.10 -r1.10.30.2 +--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10 ++++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2 +@@ -132,4 +132,12 @@ double drand48(void); + */ + int statMemoryAccounted(void); + ++#ifndef HAVE_STRNSTR ++extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); ++#endif ++ ++#ifndef HAVE_STRCASESTR ++extern char *strcasestr(const char *haystack, const char *needle); ++#endif ++ + #endif /* SQUID_UTIL_H */ +Index: lib/Makefile.am +=================================================================== +RCS file: /cvsroot/squid/squid/lib/Makefile.am,v +retrieving revision 1.4 +retrieving revision 1.4.26.2 +diff -p -u -b -r1.4 -r1.4.26.2 +--- lib/Makefile.am 21 Nov 2001 23:48:57 -0000 1.4 ++++ lib/Makefile.am 6 Apr 2004 13:04:38 -0000 1.4.26.2 +@@ -8,6 +8,19 @@ SNPRINTFSOURCE=snprintf.c + else + SNPRINTFSOURCE= + endif ++ ++if NEED_OWN_STRNSTR ++STRNSTRSOURCE=strnstr.c ++else ++STRNSTRSOURCE= ++endif ++ ++if NEED_OWN_STRCASESTR ++STRCASESTRSOURCE=strcasestr.c ++else ++STRCASESTRSOURCE= ++endif ++ + if NEED_OWN_MD5 + MD5SOURCE=md5.c + else +@@ -43,6 +56,8 @@ libmiscutil_a_SOURCES = \ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +Index: lib/strcasestr.c +=================================================================== +RCS file: lib/strcasestr.c +diff -N lib/strcasestr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1 +@@ -0,0 +1,126 @@ ++/* Return the offset of one string within another. ++ Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* ++ * My personal strstr() implementation that beats most other algorithms. ++ * Until someone tells me otherwise, I assume that this is the ++ * fastest implementation of strstr() in C. ++ * I deliberately chose not to comment it. You should have at least ++ * as much fun trying to understand it, as I had to write it :-). ++ * ++ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ ++ ++/* ++ * modified to work outside of glibc (rhorstmann, 06/04/2004) ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRCASESTR ++#include <ctype.h> ++ ++typedef unsigned chartype; ++ ++char * ++strcasestr (phaystack, pneedle) ++ const char *phaystack; ++ const char *pneedle; ++{ ++ register const unsigned char *haystack, *needle; ++ register chartype b, c; ++ ++ haystack = (const unsigned char *) phaystack; ++ needle = (const unsigned char *) pneedle; ++ ++ b = tolower (*needle); ++ if (b != '\0') ++ { ++ haystack--; /* possible ANSI violation */ ++ do ++ { ++ c = *++haystack; ++ if (c == '\0') ++ goto ret0; ++ } ++ while (tolower (c) != (int) b); ++ ++ c = tolower (*++needle); ++ if (c == '\0') ++ goto foundneedle; ++ ++needle; ++ goto jin; ++ ++ for (;;) ++ { ++ register chartype a; ++ register const unsigned char *rhaystack, *rneedle; ++ ++ do ++ { ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ if (tolower (a) == (int) b) ++ break; ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++shloop: ++ ; ++ } ++ while (tolower (a) != (int) b); ++ ++jin: a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ ++ if (tolower (a) != (int) c) ++ goto shloop; ++ ++ rhaystack = haystack-- + 1; ++ rneedle = needle; ++ a = tolower (*rneedle); ++ ++ if (tolower (*rhaystack) == (int) a) ++ do ++ { ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ if (tolower (*rhaystack) != (int) a) ++ break; ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ } ++ while (tolower (*rhaystack) == (int) a); ++ ++ needle = rneedle; /* took the register-poor approach */ ++ ++ if (a == '\0') ++ break; ++ } ++ } ++foundneedle: ++ return (char*) haystack; ++ret0: ++ return 0; ++} ++#endif +Index: lib/strnstr.c +=================================================================== +RCS file: lib/strnstr.c +diff -N lib/strnstr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include <string.h> ++#include <util.h> ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.5.30.3 +retrieving revision 1.5.44.8 +diff -p -u -b -r1.5.30.3 -r1.5.44.8 +--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3 ++++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8 +@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb) + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); + } ++ ++int ++memBufRead(int fd, MemBuf * mb) ++{ ++ int len; ++ if (mb->capacity == mb->size) ++ memBufGrow(mb, SQUID_TCP_SO_RCVBUF); ++ len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); ++ if (len) ++ mb->size += len; ++ return len; ++} +Index: src/cache_cf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cache_cf.c,v +retrieving revision 1.38.6.29 +retrieving revision 1.38.6.11.2.22 +diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22 +--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29 ++++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22 +@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli + return bodylist.head == NULL; + } + ++#ifdef HS_FEAT_ICAP ++ ++/*************************************************** ++ * prototypes ++ */ ++static int icap_service_process(icap_service * s); ++static void icap_service_init(icap_service * s); ++static void icap_service_destroy(icap_service * s); ++icap_service *icap_service_lookup(char *name); ++static int icap_class_process(icap_class * c); ++static void icap_class_destroy(icap_class * c); ++static void icap_access_destroy(icap_access * a); ++static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access <servicename> {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.14.6.1 +retrieving revision 1.14.32.2 +diff -p -u -b -r1.14.6.1 -r1.14.32.2 +--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1 ++++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2 +@@ -144,6 +144,10 @@ cbdataInit(void) + CREATE_CBDATA(statefulhelper); + CREATE_CBDATA(helper_stateful_server); + CREATE_CBDATA(HttpStateData); ++#ifdef HS_FEAT_ICAP ++ CREATE_CBDATA(IcapStateData); ++ CREATE_CBDATA(icap_service); ++#endif + CREATE_CBDATA_FREE(peer, peerDestroy); + CREATE_CBDATA(ps_state); + CREATE_CBDATA(RemovalPolicy); +Index: src/cf.data.pre +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf.data.pre,v +retrieving revision 1.49.2.84 +retrieving revision 1.49.2.33.2.32 +diff -p -u -b -r1.49.2.84 -r1.49.2.33.2.32 +--- src/cf.data.pre 21 Oct 2005 02:13:47 -0000 1.49.2.84 ++++ src/cf.data.pre 24 Oct 2005 17:07:42 -0000 1.49.2.33.2.32 +@@ -2397,7 +2397,6 @@ DOC_START + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -2716,6 +2715,177 @@ DOC_START + DOC_END + + COMMENT_START ++ ICAP OPTIONS ++ ----------------------------------------------------------------------------- ++COMMENT_END ++ ++NAME: icap_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.onoff ++DEFAULT: off ++DOC_START ++ If you want to enable the ICAP client module, set this to on. ++DOC_END ++ ++NAME: icap_preview_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.preview_enable ++DEFAULT: off ++DOC_START ++ Set this to 'on' if you want to enable the ICAP preview ++ feature in Squid. ++DOC_END ++ ++NAME: icap_preview_size ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.preview_size ++DEFAULT: -1 ++DOC_START ++ The default size of preview data to be sent to the ICAP server. ++ -1 means no preview. This value might be overwritten on a per server ++ basis by OPTIONS requests. ++DOC_END ++ ++NAME: icap_check_interval ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.check_interval ++DEFAULT: 300 ++DOC_START ++ If an ICAP server does not respond, it gets marked as unreachable. Squid ++ will try again to reach it after this time. ++DOC_END ++ ++NAME: icap_send_client_ip ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_client_ip ++DEFAULT: off ++DOC_START ++ This adds the header "X-Client-IP" to ICAP requests. Can also be ++ set from the server's response to OPTIONS. ++DOC_END ++ ++NAME: icap_send_auth_user ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_auth_user ++DEFAULT: off ++DOC_START ++ This adds the header "X-Authenticated-User" to ICAP requests ++ if proxy access is authentified. Can also be set from the server's ++ response to OPTIONS. ++DOC_END ++ ++NAME: icap_auth_scheme ++TYPE: string ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.auth_scheme ++DEFAULT: Local://%u ++DOC_START ++ Authentification scheme to pass to ICAP requests if ++ icap_send_auth_user is enabled. The first occurence of "%u" ++ is replaced by the authentified user name. If no "%u" is found, ++ the username is added at the end of the scheme. ++ ++ See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, ++ section 3.4 for details on this. ++ ++ Examples: ++ ++ icap_auth_scheme Local://%u ++ icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com ++ icap_auth_scheme WinNT://nt-domain/%u ++ icap_auth_scheme Radius://radius-server/%u ++DOC_END ++ ++NAME: icap_service ++TYPE: icap_service_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines a single ICAP service ++ ++ icap_service servicename vectoring_point bypass service_url [options ...] ++ ++ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache ++ This specifies at which point of request processing the ICAP ++ service should be plugged in. ++ bypass = 1|0 ++ If set to 1 and the ICAP server cannot be reached, the request will go ++ through without being processed by an ICAP server ++ service_url = icap://servername:port/service ++ ++ Options: ++ ++ no-keep-alive To always close the connection to icap server ++ after the transaction completes ++ ++ ++ Note: reqmod_precache and respmod_postcache is not yet implemented ++ ++ Load-balancing and high availability: ++ You can obtain load-balancing and high availability by defining a ++ named service with different definitions. Then, the client ++ loops through the different entities of the service providing ++ load-balancing. If an entity is marked as unreachable, the client goes ++ one step further to the next entity: you have the high-availability. ++ See the service_1 definition below ++ ++Example: ++icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod ++icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive ++icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod ++DOC_END ++ ++NAME: icap_class ++TYPE: icap_class_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines an ICAP service chain. If there are multiple services per ++ vectoring point, they are processed in the specified order. ++ ++ icap_class classname servicename... ++ ++Example: ++icap_class class_1 service_1 service_2 ++icap class class_2 service_1 service_3 ++DOC_END ++ ++NAME: icap_access ++TYPE: icap_access_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Redirects a request through an ICAP service class, depending ++ on given acls ++ ++ icap_access classname allow|deny [!]aclname... ++ ++ The icap_access statements are processed in the order they appear in ++ this configuration file. If an access list matches, the processing stops. ++ For an "allow" rule, the specified class is used for the request. A "deny" ++ rule simply stops processing without using the class. You can also use the ++ special classname "None". ++ ++ For backward compatibility, it is also possible to use services ++ directly here. ++Example: ++icap_access class_1 allow all ++DOC_END ++ ++COMMENT_START + MISCELLANEOUS + ----------------------------------------------------------------------------- + COMMENT_END +Index: src/cf_gen_defines +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v +retrieving revision 1.5 +retrieving revision 1.5.48.3 +diff -p -u -b -r1.5 -r1.5.48.3 +--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5 ++++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3 +@@ -18,12 +18,13 @@ BEGIN { + define["USE_UNLINKD"]="--enable-unlinkd" + define["USE_USERAGENT_LOG"]="--enable-useragent-log" + define["USE_WCCP"]="--enable-wccp" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +- DEFINE=define[$2] ++ DEFINE = define[$2] + else +- DEFINE="-D" $2 ++ DEFINE = "-D" $2 + print "{\"" $2 "\", \"" DEFINE "\", " + print "#if " $2 + print "1" +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.47.2.71 +retrieving revision 1.47.2.28.2.40 +diff -p -u -b -r1.47.2.71 -r1.47.2.28.2.40 +--- src/client_side.c 19 Oct 2005 02:13:20 -0000 1.47.2.71 ++++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40 +@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n"; + static CWCB clientWriteComplete; + static CWCB clientWriteBodyComplete; + static PF clientReadRequest; +-static PF connStateFree; ++PF connStateFree; + static PF requestTimeout; + static PF clientLifetimeTimeout; + static int clientCheckTransferDone(clientHttpRequest *); +@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien + static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); + static void clientPackTermBound(String boundary, MemBuf * mb); + static void clientInterpretRequestHeaders(clientHttpRequest *); +-static void clientProcessRequest(clientHttpRequest *); ++void clientProcessRequest(clientHttpRequest *); + static void clientProcessExpired(void *data); + static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); +-static int clientCachable(clientHttpRequest * http); +-static int clientHierarchical(clientHttpRequest * http); +-static int clientCheckContentLength(request_t * r); ++int clientCachable(clientHttpRequest * http); ++int clientHierarchical(clientHttpRequest * http); ++int clientCheckContentLength(request_t * r); + static DEFER httpAcceptDefer; + static log_type clientProcessRequest2(clientHttpRequest * http); + static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); + static int clientRequestBodyTooLarge(squid_off_t clen); + static void clientProcessBody(ConnStateData * conn); + static void clientEatRequestBody(clientHttpRequest *); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + static int + checkAccelOnly(clientHttpRequest * http) +@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res + http->request = requestLink(new_request); + } + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -931,11 +938,22 @@ httpRequestFree(void *data) + *H = http->next; + http->next = NULL; + dlinkDelete(&http->active, &ClientActiveRequests); ++#if HS_FEAT_ICAP ++ /*In the case that the upload of data breaks, we need this code here .... */ ++ if (NULL != http->icap_reqmod) { ++ if (cbdataValid(http->icap_reqmod)) ++ if (http->icap_reqmod->icap_fd > -1) { ++ comm_close(http->icap_reqmod->icap_fd); ++ } ++ cbdataUnlock(http->icap_reqmod); ++ http->icap_reqmod = NULL; ++ } ++#endif + cbdataFree(http); + } + + /* This is a handler normally called by comm_close() */ +-static void ++void + connStateFree(int fd, void *data) + { + ConnStateData *connState = data; +@@ -958,7 +976,6 @@ connStateFree(int fd, void *data) + } else + safe_free(connState->in.buf); + /* XXX account connState->in.buf */ +- pconnHistCount(0, connState->nrequests); + cbdataFree(connState); + #ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ +@@ -1103,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1122,7 +1139,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1148,7 +1165,7 @@ clientCachable(clientHttpRequest * http) + } + + /* Return true if we can query our neighbors for this object */ +-static int ++int + clientHierarchical(clientHttpRequest * http) + { + const char *url = http->uri; +@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -2449,6 +2466,11 @@ clientProcessRequest(clientHttpRequest * + debug(33, 4) ("clientProcessRequest: %s '%s'\n", + RequestMethodStr[r->method], + url); ++#if HS_FEAT_ICAP ++ if (clientIcapReqMod(http)) { ++ return; ++ } ++#endif + if (r->method == METHOD_CONNECT && !http->redirect.status) { + http->log_type = LOG_TCP_MISS; + sslStart(http, &http->out.size, &http->al.http.code); +@@ -2993,6 +3015,20 @@ clientReadRequest(int fd, void *data) + (long) conn->in.offset, (long) conn->in.size); + len = conn->in.size - conn->in.offset - 1; + } ++#if HS_FEAT_ICAP ++ /* ++ * This check exists because ICAP doesn't always work well ++ * with persistent (reused) connections. One version of the ++ * REQMOD code creates a fake ConnStateData, which doesn't have ++ * an in.buf. We want to make sure that the fake ConnStateData ++ * doesn't get used here. ++ */ ++ if (NULL == conn->in.buf) { ++ debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); ++ comm_close(fd); ++ return; ++ } ++#endif + statCounter.syscalls.sock.reads++; + size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); + if (size > 0) { +@@ -3096,7 +3132,8 @@ clientReadRequest(int fd, void *data) + /* add to the client request queue */ + for (H = &conn->chr; *H; H = &(*H)->next); + *H = http; +- conn->nrequests++; ++ F->pconn.uses++; ++ F->pconn.type = 0; + /* + * I wanted to lock 'http' here since its callback data for + * clientLifetimeTimeout(), but there's no logical place to +@@ -3266,7 +3303,7 @@ clientReadRequest(int fd, void *data) + } + + /* file_read like function, for reading body content */ +-static void ++void + clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) + { + ConnStateData *conn = request->body_reader_data; +@@ -3390,7 +3427,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data) + * Some data has been sent to the client, just close the FD + */ + comm_close(fd); +- } else if (conn->nrequests) { ++ } else if (fd_table[fd].pconn.uses) { + /* + * assume its a persistent connection; just close it + */ +@@ -3948,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re + } + } + } ++ ++#if HS_FEAT_ICAP ++static int ++clientIcapReqMod(clientHttpRequest * http) ++{ ++ ErrorState *err; ++ icap_service *service; ++ if (http->flags.did_icap_reqmod) ++ return 0; ++ if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) ++ return 0; ++ debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); ++ /* ++ * Note, we pass 'start' and 'log_addr' to ICAP so the access.log ++ * entry comes out right. The 'clientHttpRequest' created by ++ * the ICAP side is the one that gets logged. The first ++ * 'clientHttpRequest' does not get logged because its out.size ++ * is zero and log_type is unset. ++ */ ++ http->icap_reqmod = icapReqModStart(service, ++ http->uri, ++ http->request, ++ http->conn->fd, ++ http->start, ++ http->conn->log_addr, ++ (void *) http->conn); ++ if (NULL == http->icap_reqmod) { ++ return 0; ++ } else if (-1 == (int) http->icap_reqmod) { ++ /* produce error */ ++ http->icap_reqmod = NULL; ++ debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); ++ http->log_type = LOG_TCP_DENIED; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = ETIMEDOUT; ++ err->request = requestLink(http->request); ++ err->src_addr = http->conn->peer.sin_addr; ++ http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return 1; ++ } ++ cbdataLock(http->icap_reqmod); ++ http->flags.did_icap_reqmod = 1; ++ return 1; ++} ++#endif +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.18.6.6 +retrieving revision 1.18.6.2.12.9 +diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9 +--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6 ++++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9 +@@ -653,8 +653,8 @@ comm_close(int fd) + #endif + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + SSL_free(F->ssl); +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.29.2.18 +retrieving revision 1.29.2.8.2.17 +diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17 +--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18 ++++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17 +@@ -93,6 +93,7 @@ typedef enum { + ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ + ERR_TOO_BIG, + TCP_RESET, ++ ERR_ICAP_FAILURE, + ERR_INVALID_RESP, + ERR_MAX + } err_type; +@@ -438,6 +439,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -610,6 +614,12 @@ typedef enum { + MEM_TLV, + MEM_SWAP_LOG_DATA, + MEM_CLIENT_REQ_BUF, ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -709,9 +719,14 @@ typedef enum { + CBDATA_RemovalPolicyWalker, + CBDATA_RemovalPurgeWalker, + CBDATA_store_client, ++#ifdef HS_FEAT_ICAP ++ CBDATA_IcapStateData, ++ CBDATA_icap_service, ++#endif + CBDATA_FIRST_CUSTOM_TYPE = 1000 + } cbdata_type; + ++ + /* + * Return codes from checkVary(request) + */ +@@ -742,4 +757,68 @@ enum { + + #endif + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.13.6.15 +retrieving revision 1.13.6.3.2.15 +diff -p -u -b -r1.13.6.15 -r1.13.6.3.2.15 +--- src/forward.c 2 Sep 2005 02:13:43 -0000 1.13.6.15 ++++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15 +@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status + else + hierarchyNote(&fwdState->request->hier, fs->code, request->host); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; + if (fs->peer) + peerConnectSucceded(fs->peer); + fwdDispatch(fwdState); +@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data) + void + fwdFail(FwdState * fwdState, ErrorState * errorState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", + err_type_str[errorState->type], + httpStatusString(errorState->http_status), +@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data) + void + fwdUnregister(int fd, FwdState * fwdState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); + assert(fd == fwdState->server_fd); + assert(fd > -1); +@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat + void + fwdComplete(FwdState * fwdState) + { +- StoreEntry *e = fwdState->entry; ++ StoreEntry *e; ++ if (NULL == fwdState) ++ return; ++ e = fwdState->entry; + assert(e->store_status == STORE_PENDING); + debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), + e->mem_obj->reply->sline.status); +Index: src/globals.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/globals.h,v +retrieving revision 1.14.6.7 +retrieving revision 1.14.6.3.2.5 +diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5 +--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7 ++++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5 +@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */ + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.17.6.32 +retrieving revision 1.17.6.3.6.39 +diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.39 +--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32 ++++ src/http.c 23 Nov 2005 20:33:07 -0000 1.17.6.3.6.39 +@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry; + + static PF httpReadReply; + static void httpSendRequest(HttpStateData *); +-static PF httpStateFree; ++PF httpStateFree; + static PF httpTimeout; + static void httpCacheNegatively(StoreEntry *); + static void httpMakePrivate(StoreEntry *); +@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *) + static int httpCachableReply(HttpStateData *); + static void httpMaybeRemovePublic(StoreEntry *, http_status); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data) + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; ++#if HS_FEAT_ICAP ++ cbdataUnlock(httpState->icap_writer); ++#endif + cbdataFree(httpState); + } + +@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht + } + + /* rewrite this later using new interfaces @?@ */ +-static void ++void + httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) + { + StoreEntry *entry = httpState->entry; +@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; ++ squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ +- if (httpState->reply_hdr_state < 2) ++ if (httpState->reply_hdr_state < 2) { ++ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", ++ httpState->reply_hdr_state); + return 0; ++ } + clen = httpReplyBodySize(httpState->request->method, reply); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ content_bytes_read = httpState->icap_writer->fake_content_length; ++ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); ++ } else ++#endif ++ content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ +- if (mem->inmem_hi > clen + reply->hdr_sz) ++ if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ +- if (mem->inmem_hi < clen + reply->hdr_sz) ++ if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data) + delay_id delay_id; + #endif + ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ /*The folowing entry can not be marked as aborted. ++ * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; +@@ -579,6 +605,33 @@ httpReadReply(int fd, void *data) + else + delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); + #endif ++ ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ IcapStateData *icap = httpState->icap_writer; ++ /* ++ * Ok we have a received a response from the web server, so try to ++ * connect the icap server if it's the first attemps. If we try ++ * to connect to the icap server, defer this request (do not read ++ * the buffer), and defer until icapConnectOver() is not called. ++ */ ++ if (icap->flags.connect_requested == 0) { ++ debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); ++ if (!icapConnect(icap, icapConnectOver)) { ++ debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); ++ icap->flags.connect_requested = 1; ++ /* Wait for more data or EOF condition */ ++ commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ } ++#endif ++ + errno = 0; + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, read_sz); +@@ -595,7 +648,13 @@ httpReadReply(int fd, void *data) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +- if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ (void) 0; ++ else ++#endif ++ ++ if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { + /* Skip whitespace */ + while (len > 0 && xisspace(*buf)) + xmemmove(buf, buf + 1, len--); +@@ -625,6 +684,12 @@ httpReadReply(int fd, void *data) + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { ++ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); ++ icapSendRespMod(httpState->icap_writer, buf, len, 1); ++ } ++#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we +@@ -677,7 +742,28 @@ httpReadReply(int fd, void *data) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ if (cbdataValid(httpState->icap_writer)) { ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } ++ } else ++#endif + storeAppend(entry, buf, len); ++ ++ ++ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + /* + * the above storeAppend() call could ABORT this entry, +@@ -724,10 +810,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); +@@ -766,6 +863,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -776,6 +877,34 @@ httpReadReply(int fd, void *data) + } + } + ++#ifdef HS_FEAT_ICAP ++static int ++httpReadReplyWaitForIcap(int fd, void *data) ++{ ++ HttpStateData *httpState = data; ++ if (NULL == httpState->icap_writer) ++ return 0; ++ /* ++ * Do not defer when we are not connected to the icap server. ++ * Defer when the icap server connection is not established but pending ++ * Defer when the icap server is busy (writing on the socket) ++ */ ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", ++ fd, httpState->icap_writer->flags.connect_requested); ++ if (!httpState->icap_writer->flags.connect_requested) ++ return 0; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", ++ fd, httpState->icap_writer->flags.connect_pending); ++ if (httpState->icap_writer->flags.connect_pending) ++ return 1; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", ++ fd, httpState->icap_writer->flags.write_pending); ++ if (httpState->icap_writer->flags.write_pending) ++ return 1; ++ return 0; ++} ++#endif ++ + /* This will be called when request write is complete. Schedule read of + * reply. */ + static void +@@ -803,6 +932,63 @@ httpSendComplete(int fd, char *bufnotuse + comm_close(fd); + return; + } else { ++ /* Schedule read reply. */ ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart( ++ ICAP_SERVICE_RESPMOD_PRECACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (-1 == (int) httpState->icap_writer) { ++ /* TODO: send error here and exit */ ++ ErrorState *err; ++ httpState->icap_writer = 0; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(httpState->orig_request); ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ return; ++ } else if (httpState->icap_writer) { ++ request_flags fake_flags = httpState->orig_request->flags; ++ method_t fake_method = entry->mem_obj->method; ++ const char *fake_msg = "this is a fake entry for " ++ " response sent to an ICAP RESPMOD server"; ++ cbdataLock(httpState->icap_writer); ++ /* ++ * this httpState will give the data it reads to ++ * the icap server, rather than put it into ++ * a StoreEntry ++ */ ++ storeUnlockObject(httpState->entry); ++ storeUnregisterAbort(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -811,8 +997,18 @@ httpSendComplete(int fd, char *bufnotuse + * the timeout for POST/PUT requests that have very large + * request bodies. + */ ++ ++ /* removed in stable5: ++ * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ */ + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +- commSetDefer(fd, fwdCheckDeferRead, entry); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); ++ commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); ++ } else ++#endif ++ commSetDefer(httpState->fd, fwdCheckDeferRead, entry); + } + httpState->flags.request_sent = 1; + } +@@ -1010,8 +1206,11 @@ httpBuildRequestHeader(request_t * reque + if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { + const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); + httpHdrCcSetMaxAge(cc, getMaxAge(url)); ++#ifndef HS_FEAT_ICAP ++ /* Don;t bother - if the url you want to cache is redirected? */ + if (strLen(request->urlpath)) + assert(strstr(url, strBuf(request->urlpath))); ++#endif + } + /* Set no-cache if determined needed but not found */ + if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) +@@ -1119,6 +1318,7 @@ httpStart(FwdState * fwd) + int fd = fwd->server_fd; + HttpStateData *httpState; + request_t *proxy_req; ++ /* ErrorState *err; */ + request_t *orig_req = fwd->request; + debug(11, 3) ("httpStart: \"%s %s\"\n", + RequestMethodStr[orig_req->method], +@@ -1156,12 +1356,22 @@ httpStart(FwdState * fwd) + httpState->request = requestLink(orig_req); + httpState->orig_request = requestLink(orig_req); + } ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (httpState->icap_writer) { ++ return; ++ } ++ } ++#endif + /* + * register the handler to free HTTP state data when the FD closes + */ + comm_add_close_handler(fd, httpStateFree, httpState); + statCounter.server.all.requests++; + statCounter.server.http.requests++; ++ + httpSendRequest(httpState); + /* + * We used to set the read timeout here, but not any more. +Index: src/icap_common.c +=================================================================== +RCS file: src/icap_common.c +diff -N src/icap_common.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_common.c 22 Nov 2005 22:41:48 -0000 1.1.2.39 +@@ -0,0 +1,785 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} +Index: src/icap_opt.c +=================================================================== +RCS file: src/icap_opt.c +diff -N src/icap_opt.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_opt.c 22 Nov 2005 22:41:48 -0000 1.1.2.17 +@@ -0,0 +1,519 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: src/icap_reqmod.c +=================================================================== +RCS file: src/icap_reqmod.c +diff -N src/icap_reqmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_reqmod.c 6 Dec 2005 21:53:44 -0000 1.1.2.58 +@@ -0,0 +1,976 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++#define ICAP_PROXY_KEEP_ALIVE 0 ++ ++/* ++ * These once-static functions are required to be global for ICAP ++ */ ++ ++PF clientReadRequest; ++PF connStateFree; ++int clientReadDefer(int fd, void *data); ++int clientCheckContentLength(request_t * r); ++void clientProcessRequest(clientHttpRequest *); ++int clientCachable(clientHttpRequest *); ++int clientHierarchical(clientHttpRequest *); ++void clientReadBody(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++ ++static PF icapReqModReadHttpHdrs; ++static PF icapReqModReadHttpBody; ++static CWCB icapReqModSendBodyChunk; ++static CBCB icapReqModBodyHandler; ++static BODY_HANDLER icapReqModBodyReader; ++static STRCB icapReqModMemBufAppend; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++static const char *crlf = "\r\n"; ++ ++/* ++ * icapExpectedHttpReqHdrSize ++ * ++ * calculate the size of the HTTP headers that we expect ++ * to read from the ICAP server. ++ */ ++static int ++icapExpectedHttpReqHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) ++ return (icap->enc.req_body - icap->enc.req_hdr); ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ fatal("icapExpectedHttpReqHdrSize: unexpected case"); ++ return 0; ++} ++ ++/* ++ * icapReqModCreateClientState ++ * ++ * Creates fake client_side data structures so we can use ++ * that module to read/parse the HTTP request that we read ++ * from the ICAP server. ++ */ ++static clientHttpRequest * ++icapReqModCreateClientState(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http; ++ if (!cbdataValid(icap->reqmod.client_cookie)) { ++ debug(81, 3) ("Whups, client cookie invalid\n"); ++ icap->reqmod.client_fd = -1; ++ return NULL; ++ } ++ http = cbdataAlloc(clientHttpRequest); ++ /* ++ * use our own urlCanonicalClean here, because urlCanonicalClean ++ * may strip everything after a question-mark. As http->uri ++ * is used when doing a request to a parent proxy, we need the full ++ * url here. ++ */ ++ http->uri = xstrdup(urlCanonical(icap->request)); ++ http->log_uri = xstrndup(http->uri, MAX_URL); ++ http->range_iter.boundary = StringNull; ++ http->request = requestLink(request ? request : icap->request); ++ http->flags.did_icap_reqmod = 1; ++ http->start = icap->reqmod.start; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ ++ http->conn->chr = http; ++ ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ err->request = requestLink(request); ++ request->flags.proxy_keepalive = 0; ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ if (!clientCheckContentLength(request)) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED); ++ err->request = requestLink(request); ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ /* Do we expect a request-body? */ ++ if (request->content_length > 0) { ++ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); ++ if (request->body_reader_data) ++ cbdataUnlock(request->body_reader_data); ++ request->body_reader = icapReqModBodyReader; ++ request->body_reader_data = icap; /* XXX cbdataLock? */ ++ cbdataLock(icap); /*Yes sure ..... */ ++ memBufDefInit(&icap->reqmod.http_entity.buf); ++ } ++ if (clientCachable(http)) ++ request->flags.cachable = 1; ++ if (clientHierarchical(http)) ++ request->flags.hierarchical = 1; ++ clientProcessRequest(http); ++} ++ ++/* ++ * icapReqModParseHttpError ++ * ++ * Handle an error when parsing the new HTTP request we read ++ * from the ICAP server. ++ */ ++static void ++icapReqModParseHttpError(IcapStateData * icap, const char *reason) ++{ ++ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); ++} ++ ++/* ++ * icapEntryError ++ * ++ * A wrapper for errorCon() and errorAppendEntry(). ++ */ ++static void ++icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) ++{ ++ ErrorState *err; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, null_request_flags); ++ err = errorCon(et, hs); ++ err->xerrno = xerrno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(http->entry, err); ++} ++ ++/* ++ * icapReqModParseHttpRequest ++ * ++ * Parse the HTTP request that we read from the ICAP server. ++ * Creates and fills in the request_t structure. ++ */ ++static void ++icapReqModParseHttpRequest(IcapStateData * icap) ++{ ++ char *mstr; ++ char *uri; ++ char *inbuf; ++ char *t; ++ char *token; ++ char *headers; ++ method_t method; ++ request_t *request; ++ http_version_t http_ver; ++ int reqlen = icap->reqmod.hdr_buf.size; ++ int hdrlen; ++ ++ /* ++ * Lazy, make a copy of the buf so I can chop it up with strtok() ++ */ ++ inbuf = xcalloc(reqlen + 1, 1); ++ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); ++ ++ if ((mstr = strtok(inbuf, "\t ")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); ++ icapReqModParseHttpError(icap, "error:invalid-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ method = urlParseMethod(mstr); ++ if (method == METHOD_NONE) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", ++ mstr); ++ icapReqModParseHttpError(icap, "error:unsupported-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ /* look for URL+HTTP/x.x */ ++ if ((uri = strtok(NULL, "\n")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); ++ icapReqModParseHttpError(icap, "error:missing-url"); ++ xfree(inbuf); ++ return; ++ } ++ while (xisspace(*uri)) ++ uri++; ++ t = uri + strlen(uri); ++ assert(*t == '\0'); ++ token = NULL; ++ while (t > uri) { ++ t--; ++ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { ++ token = t + 1; ++ break; ++ } ++ } ++ while (t > uri && xisspace(*t)) ++ *(t--) = '\0'; ++ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); ++ if (token == NULL) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); ++ icapReqModParseHttpError(icap, "error:missing-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); ++ icapReqModParseHttpError(icap, "error:invalid-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", ++ http_ver.major, http_ver.minor); ++ ++ headers = strtok(NULL, null_string); ++ hdrlen = inbuf + reqlen - headers; ++ ++ if ((request = urlParse(method, uri)) == NULL) { ++ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ /* compile headers */ ++ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { ++ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", ++ uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, ++ 3) ++ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); ++ request->http_ver = http_ver; ++ request->client_addr = icap->request->client_addr; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request != NULL) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service *service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->reqmod.http_entity.bytes_read >= icap->request->content_length) ++ icap->flags.reqmod_http_entity_eof = 1; ++ ++ if (!icap->flags.reqmod_http_entity_eof) ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ /* ++ * Notify the other side if it is waiting for data from us ++ */ ++ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.callback); ++ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.callback && icap->reqmod.http_entity.buf.size) { ++ icapReqModPassHttpBody(icap, ++ icap->reqmod.http_entity.callback_buf, ++ icap->reqmod.http_entity.callback_bufsize, ++ icap->reqmod.http_entity.callback, ++ icap->reqmod.http_entity.callback_data); ++ icap->reqmod.http_entity.callback = NULL; ++ cbdataUnlock(icap->reqmod.http_entity.callback_data); ++ ++ } ++} ++ ++/* ++ * icapReqModPassHttpBody ++ * ++ * Called from http.c after request headers have been sent. ++ * This function feeds the http.c module chunks of the request ++ * body that were stored in the http_entity.buf MemBuf. ++ */ ++static void ++icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ debug(81, 3) ("icapReqModPassHttpBody: called\n"); ++ if (!buf) { ++ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", ++ icap->icap_fd, buf, (int) size, cbdata); ++ comm_close(icap->icap_fd); ++ return; ++ } ++ if (!cbdataValid(cbdata)) { ++ debug(81, ++ 1) ++ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", ++ icap->icap_fd); ++ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ ++ /*icapReqModKeepAliveOrClose(icap); */ ++ return; ++ } ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.buf.size) { ++ int copy_sz = icap->reqmod.http_entity.buf.size; ++ if (copy_sz > size) ++ copy_sz = size; ++ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); ++ /* XXX don't let Alex see this ugliness */ ++ xmemmove(icap->reqmod.http_entity.buf.buf, ++ icap->reqmod.http_entity.buf.buf + copy_sz, ++ icap->reqmod.http_entity.buf.size - copy_sz); ++ icap->reqmod.http_entity.buf.size -= copy_sz; ++ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", ++ copy_sz); ++ callback(buf, copy_sz, cbdata); ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ return; ++ } ++ if (icap->flags.reqmod_http_entity_eof) { ++ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); ++ callback(buf, 0, cbdata); ++ icapReqModKeepAliveOrClose(icap); ++ return; ++ } ++ /* ++ * We have no data for the other side at this point. Save all ++ * these values and use them when we do have data. ++ */ ++ assert(NULL == icap->reqmod.http_entity.callback); ++ icap->reqmod.http_entity.callback = callback; ++ icap->reqmod.http_entity.callback_data = cbdata; ++ icap->reqmod.http_entity.callback_buf = buf; ++ icap->reqmod.http_entity.callback_bufsize = size; ++ cbdataLock(icap->reqmod.http_entity.callback_data); ++} ++ ++/* ++ * Body reader handler for use with request->body_reader function ++ * Simple a wrapper for icapReqModPassHttpBody function ++ */ ++ ++static void ++icapReqModBodyReader(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ IcapStateData *icap = request->body_reader_data; ++ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); ++} ++ ++/* ++ * icapReqModMemBufAppend ++ * ++ * stupid wrapper to eliminate compiler warnings ++ */ ++static void ++icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) ++{ ++ memBufAppend(data, buf, size); ++} +Index: src/icap_respmod.c +=================================================================== +RCS file: src/icap_respmod.c +diff -N src/icap_respmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_respmod.c 23 Nov 2005 20:34:34 -0000 1.1.2.60 +@@ -0,0 +1,1039 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ ++ memBufPrintf(mb, crlf); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++ if (service->flags.allow_204) ++ memBufPrintf(mb, "Allow: 204\r\n"); ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ } ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ /* No data sent yet. Start with headers */ ++ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { ++ buf += icap->sc; ++ len -= icap->sc; ++ } ++ /* ++ * Then we do not have http responce headers. All data (previous and those in buf) ++ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... ++ */ ++ if (icap->sc < 0) { ++ memBufAppend(&icap->respmod.buffer, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->sc = icap->respmod.req_hdr_copy.size; ++ icap->respmod.req_hdr_copy.size = 0; ++ buf = NULL; ++ len = 0; ++ } ++ } ++ if (0 == icap->sc) { ++ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ ++ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); ++ memBufClean(&mb); ++ return; ++ } ++#if ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ ++ icap->flags.preview_done = 1; ++ ++ if (!icap->flags.preview_done) { ++ /* preview not yet sent */ ++ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size ++ && len > 0) { ++ /* Try to collect at least preview_size+1 bytes */ ++ /* By collecting one more byte than needed for preview we know best */ ++ /* whether we have to send the ieof chunk extension */ ++ size = icap->respmod.buffer.size + len; ++ if (size > preview_size + 1) ++ size = preview_size + 1; ++ size -= icap->respmod.buffer.size; ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", ++ icap->icap_fd, size); ++ memBufAppend(&icap->respmod.buffer, buf, size); ++ buf = ((char *) buf) + size; ++ len -= size; ++ } ++ if (icap->respmod.buffer.size > preview_size || theEnd) { ++ /* we got enough bytes for preview or this is the last call */ ++ /* add preview preview now */ ++ if (icap->respmod.buffer.size > 0) { ++ size = icap->respmod.buffer.size; ++ if (size > preview_size) ++ size = preview_size; ++ memBufPrintf(&mb, "%x\r\n", size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += size; ++ } ++ if (icap->respmod.buffer.size <= preview_size) { ++ /* content length is less than preview size+1 */ ++ if (icap->respmod.res_body_sz) ++ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ } else { ++ char ch; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ /* end of preview, wait for continue or 204 signal */ ++ /* copy the extra byte and all other data to the icap buffer */ ++ /* so that it can be handled next time */ ++ ch = icap->respmod.buffer.buf[preview_size]; ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ memBufAppend(&icap->respmod.buffer, &ch, 1); ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", ++ icap->icap_fd, len + 1); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ } ++ icap->flags.preview_done = 1; ++ icap->flags.wait_for_preview_reply = 1; ++ } ++ } else if (icap->flags.wait_for_preview_reply) { ++ /* received new data while waiting for preview response */ ++ /* add data to internal buffer and send later */ ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", ++ icap->icap_fd, len); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ /* do not send any data now while waiting for preview response */ ++ /* but prepare for read more data on the HTTP connection */ ++ memBufClean(&mb); ++ return; ++ } else ++#endif ++ { ++ /* after preview completed and ICAP preview response received */ ++ /* there may still be some data in the buffer */ ++ if (icap->respmod.buffer.size > 0) { ++ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, ++ icap->respmod.buffer.size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += icap->respmod.buffer.size; ++ memBufReset(&icap->respmod.buffer); ++ } ++ if (len > 0) { ++ memBufPrintf(&mb, "%x\r\n", len); ++ memBufAppend(&mb, buf, len); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += len; ++ } ++ if (icap->flags.send_zero_chunk) { ++ /* send zero end chunk */ ++ icap->flags.send_zero_chunk = 0; ++ icap->flags.http_server_eof = 1; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ } ++ /* wait for data coming from ICAP server as soon as we sent something */ ++ /* but of course only until we got the response header */ ++ if (!icap->flags.got_reply) ++ icap->flags.wait_for_reply = 1; ++ } ++ commSetTimeout(icap->icap_fd, -1, NULL, NULL); ++ ++ if (!mb.size) { ++ memBufClean(&mb); ++ return; ++ } ++ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ icap->flags.write_pending = 1; ++ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); ++} ++ ++static void ++icapRespModReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ int status = 0; ++ int isIcap = 0; ++ int directResponse = 0; ++ ErrorState *err; ++ const char *start; ++ const char *end; ++ ++ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ }; ++ /* OK here we have responce. Lets stop filling the ++ * icap->respmod.resp_copy buffer .... ++ */ ++ icap->flags.copy_response = 0; ++ ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++#if ICAP_PREVIEW ++ if (icap->flags.wait_for_preview_reply) { ++ if (100 == status) { ++ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ /* if http_server_eof ++ * call again icapSendRespMod to handle data that ++ * was received while waiting for this ICAP response ++ * else let http to call icapSendRespMod when new data arrived ++ */ ++ if (icap->flags.http_server_eof) ++ icapSendRespMod(icap, NULL, 0, 0); ++ /* ++ * reset the header to send the rest of the preview ++ */ ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufReset(&icap->icap_hdr); ++ ++ /*We do n't need it any more ....... */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ ++ return; ++ } ++ if (204 == status) { ++ debug(81, ++ 5) ("icapRespModReadReply: 204 No modification received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ } ++ } ++#endif /*ICAP_PREVIEW */ ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ if (204 == status) { ++ debug(81, 3) ("got 204 status from ICAP server\n"); ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ ++ /* ++ * "directResponse" is the normal case here. If we don't have ++ * a response header or body, it is an error. ++ */ ++ if (!directResponse) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ /* Next, gobble any data before the HTTP response starts */ ++ if (icap->enc.res_hdr > -1) ++ icap->bytes_to_gobble = icap->enc.res_hdr; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++} ++ ++ ++/* ++ * Gobble up (read) some bytes until we get to the start of the body ++ */ ++static void ++icapRespModGobble(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); ++ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, ++ icap->bytes_to_gobble); ++ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); ++ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); ++ if (len < 0) { ++ /* XXX error */ ++ abort(); ++ } ++ icap->bytes_to_gobble -= len; ++ if (icap->bytes_to_gobble) ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++ else ++ icapReadReply(fd, icap); ++} ++ ++ ++static void ++icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ErrorState *err; ++ ++ icap->flags.write_pending = 0; ++ debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ if (cbdataValid(icap)) ++ err->request = requestLink(icap->request); ++ storeEntryReset(icap->respmod.entry); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); ++ comm_close(fd); ++ return; ++ } ++ if (icap->flags.send_zero_chunk) { ++ debug(81, ++ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); ++ icap->flags.send_zero_chunk = 0; ++ icapSendRespMod(icap, NULL, 0, 1); ++ return; ++ } ++ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { ++ /* Schedule reading the ICAP response */ ++ debug(81, ++ 3) ++ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", ++ fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++#if 1 ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++#else ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ /* ++ * Set the read timeout only after all data has been sent ++ * or we are waiting for a preview response ++ * If the ICAP server does not return any data till all data ++ * has been sent, we are likely to hit the timeout for large ++ * HTTP bodies ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ } ++#endif ++ } ++} ++ ++void ++icapConnectOver(int fd, int status, void *data) ++{ ++ ErrorState *err; ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ if (status < 0) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); ++ icapOptSetUnreachable(icap->current_service); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++} ++ ++ ++ ++IcapStateData * ++icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, ++ http_state_flags http_flags) ++{ ++ IcapStateData *icap = NULL; ++ CNCB *theCallback = NULL; ++ icap_service *service = NULL; ++ ++ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); ++ assert(type >= 0 && type < ICAP_SERVICE_MAX); ++ ++ service = icapService(type, request); ++ if (!service) { ++ debug(81, 3) ("icapRespModStart: no service found\n"); ++ return NULL; /* no service found */ ++ } ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ++ ("icapRespModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ++ ("icapRespModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ switch (type) { ++ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change ++ * this switch, because callbacks isn't keep */ ++ case ICAP_SERVICE_RESPMOD_PRECACHE: ++ theCallback = icapConnectOver; ++ break; ++ default: ++ fatalf("icapRespModStart: unsupported service type '%s'\n", ++ icap_service_type_str[type]); ++ break; ++ } ++ ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->request = requestLink(request); ++ icap->respmod.entry = entry; ++ if (entry) ++ storeLockObject(entry); ++ icap->http_flags = http_flags; ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ ++ /* ++ * Don't create socket to the icap server now, but only for the first ++ * packet receive from the http server. This will resolve all timeout ++ * between the web server and icap server. ++ */ ++ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); ++ icap->flags.connect_requested = 0; ++ ++ /* ++ * make a copy the HTTP response that we send to the ICAP server in ++ * case it turns out to be a 204 ++ */ ++#ifdef SUPPORT_ICAP_204 ++ icap->flags.copy_response = 1; ++#elif ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) ++ icap->flags.copy_response = 0; ++ else ++ icap->flags.copy_response = 1; ++#else ++ icap->flags.copy_response = 0; ++#endif ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapRespModStart: returning %p\n", icap); ++ return icap; ++} ++ ++static int ++icapHttpReplyHdrState(IcapStateData * icap) ++{ ++ assert(icap); ++ if (NULL == icap->httpState) ++ return 0; ++ return icap->httpState->reply_hdr_state; ++} ++ ++static void ++icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) ++{ ++ if (NULL == icap->httpState) { ++ icap->httpState = cbdataAlloc(HttpStateData); ++ icap->httpState->request = requestLink(icap->request); ++ icap->httpState->orig_request = requestLink(icap->request); ++ icap->httpState->entry = icap->respmod.entry; ++ storeLockObject(icap->httpState->entry); /* lock it */ ++ } ++ httpProcessReplyHeader(icap->httpState, buf, size); ++ if (2 == icap->httpState->reply_hdr_state) ++ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); ++} ++ ++/* ++ * icapRespModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapRespModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink((request_t *) request); ++ err->xerrno = errno; ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ } else { ++ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", ++ fd); ++ comm_close(fd); ++ } ++ return; ++ } ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++} ++ ++static int ++icapReadReply2(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ debug(81, 3) ("icapReadReply2\n"); ++ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); ++ err->xerrno = errno; ++ err->request = requestLink((request_t *) request); ++ errorAppendEntry(entry, err); ++ icap->flags.http_server_eof = 1; ++ return -1; ++ } ++ if (icap->chunk_buf.size == 0) { ++ /* Retrieval done. */ ++ if (icapHttpReplyHdrState(icap) < 2) ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ icap->flags.http_server_eof = 1; ++ icapReadReply3(icap); ++ return 0; ++ } ++ if (icapHttpReplyHdrState(icap) == 0) { ++ int expect = icapExpectedHttpReplyHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed < 0 || needed >= 0); ++ if (0 > expect) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ } else if (0 == expect) { ++ /* ++ * this icap reply doesn't give us new HTTP headers ++ * so we must copy them from our copy ++ */ ++ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, ++ __LINE__); ++ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ ++ storeAppend(entry, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ } ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ ++ } else if (needed) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ if (icap->chunk_buf.size >= needed) { ++ storeAppend(entry, icap->chunk_buf.buf, needed); ++ so_far += needed; ++ xmemmove(icap->chunk_buf.buf, ++ icap->chunk_buf.buf + needed, ++ icap->chunk_buf.size - needed); ++ icap->chunk_buf.size -= needed; ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; ++ } else { ++ /* ++ * We don't have the full HTTP reply headers yet, so keep ++ * the partial reply buffered in 'chunk_buf' and wait ++ * for more. ++ */ ++ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); ++ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } ++ } ++ icap->http_header_bytes_read_so_far = so_far; ++ } ++ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, ++ (int) icap->chunk_buf.size); ++ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, ++ icap->flags.no_content); ++ if (icap->flags.no_content) { ++ /* data from http.c is not chunked */ ++ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", ++ icap->chunk_buf.size); ++ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); ++ icap->chunk_buf.size = 0; ++ } ++ } else if (2 == icapHttpReplyHdrState(icap)) { ++ if (icap->chunk_buf.size) ++ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); ++ } ++ icapReadReply3(icap); ++ return 0; ++} ++ ++static void ++icapReadReply3(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ int fd = icap->icap_fd; ++ debug(81, 3) ("icapReadReply3\n"); ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ icapRespModKeepAliveOrClose(icap); ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.28.6.25 +retrieving revision 1.28.6.8.2.11 +diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11 +--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25 ++++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11 +@@ -350,6 +350,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + authenticateShutdown(); + externalAclShutdown(); +@@ -378,6 +381,9 @@ mainReconfigure(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -507,6 +513,9 @@ mainInitialize(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); +Index: src/mem.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/mem.c,v +retrieving revision 1.13 +retrieving revision 1.13.28.2 +diff -p -u -b -r1.13 -r1.13.28.2 +--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13 ++++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2 +@@ -243,6 +243,13 @@ memInit(void) + memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.140.1 +diff -p -u -b -r1.2 -r1.2.140.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1 +@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str"; + $pat{'icp_opcode'} = "icp_opcode_str"; + $pat{'swap_log_op'} = "swap_log_op_str"; + $pat{'lookup_t'} = "lookup_t_str"; ++$pat{'icap_service_t'} = "icap_service_type_str"; + + $state = 0; # start state + while (<>) { +Index: src/pconn.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/pconn.c,v +retrieving revision 1.6.38.2 +retrieving revision 1.6.60.2 +diff -p -u -b -r1.6.38.2 -r1.6.60.2 +--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2 ++++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2 +@@ -46,6 +46,9 @@ struct _pconn { + #define PCONN_HIST_SZ (1<<16) + int client_pconn_hist[PCONN_HIST_SZ]; + int server_pconn_hist[PCONN_HIST_SZ]; ++#ifdef HS_FEAT_ICAP ++int icap_server_pconn_hist[PCONN_HIST_SZ]; ++#endif + + static PF pconnRead; + static PF pconnTimeout; +@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); + } ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(e, ++ "\n" ++ "ICAP-server persistent connection counts:\n" ++ "\n" ++ "\treq/\n" ++ "\tconn count\n" ++ "\t---- ---------\n"); ++ for (i = 0; i < PCONN_HIST_SZ; i++) { ++ if (icap_server_pconn_hist[i] == 0) ++ continue; ++ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); ++ } ++#endif + } + + /* ========== PUBLIC FUNCTIONS ============================================ */ +@@ -173,6 +190,9 @@ pconnInit(void) + for (i = 0; i < PCONN_HIST_SZ; i++) { + client_pconn_hist[i] = 0; + server_pconn_hist[i] = 0; ++#ifdef HS_FEAT_ICAP ++ icap_server_pconn_hist[i] = 0; ++#endif + } + pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); + pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); +@@ -248,11 +268,15 @@ pconnHistCount(int what, int i) + { + if (i >= PCONN_HIST_SZ) + i = PCONN_HIST_SZ - 1; +- /* what == 0 for client, 1 for server */ ++ /* what == 0 for client, 1 for server, 2 for ICAP server */ + if (what == 0) + client_pconn_hist[i]++; + else if (what == 1) + server_pconn_hist[i]++; ++#ifdef HS_FEAT_ICAP ++ else if (what == 2) ++ icap_server_pconn_hist[i]++; ++#endif + else + assert(0); + } +Index: src/protos.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/protos.h,v +retrieving revision 1.41.6.33 +retrieving revision 1.41.6.13.2.37 +diff -p -u -b -r1.41.6.33 -r1.41.6.13.2.37 +--- src/protos.h 16 Sep 2005 02:13:25 -0000 1.41.6.33 ++++ src/protos.h 6 Dec 2005 21:53:44 -0000 1.41.6.13.2.37 +@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *); + /* http.c */ + extern int httpCachable(method_t); + extern void httpStart(FwdState *); ++extern void httpParseReplyHeaders(const char *, http_reply *); ++extern void httpProcessReplyHeader(HttpStateData *, const char *, int); + extern int httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, +@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c + extern FREE *memBufFreeFunc(MemBuf * mb); + /* puts report on MemBuf _module_ usage into mb */ + extern void memBufReport(MemBuf * mb); ++extern int memBufRead(int fd, MemBuf * mb); + + extern char *mime_get_header(const char *mime, const char *header); + extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); +@@ -1341,4 +1344,49 @@ extern void externalAclShutdown(void); + extern int externalAclRequiresAuth(void *acl_data); + extern char *strtokFile(void); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.13.6.8 +retrieving revision 1.13.6.6.2.11 +diff -p -u -b -r1.13.6.8 -r1.13.6.6.2.11 +--- src/squid.h 26 Mar 2005 03:15:58 -0000 1.13.6.8 ++++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11 +@@ -38,6 +38,14 @@ + #include "config.h" + + /* ++ * experimental defines for ICAP ++ */ ++#ifdef HS_FEAT_ICAP ++#define ICAP_PREVIEW 1 ++#define SUPPORT_ICAP_204 0 ++#endif ++ ++/* + * On some systems, FD_SETSIZE is set to something lower than the + * actual number of files which can be opened. IRIX is one case, + * NetBSD is another. So here we increase FD_SETSIZE to our +Index: src/stat.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/stat.c,v +retrieving revision 1.13.6.14 +retrieving revision 1.13.6.7.2.7 +diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7 +--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14 ++++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7 +@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min + storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", + XAVG(server.other.kbytes_out.kb)); + ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", ++ XAVG(icap.all.requests)); ++ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", ++ XAVG(icap.all.errors)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", ++ XAVG(icap.all.kbytes_in.kb)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", ++ XAVG(icap.all.kbytes_out.kb)); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", + XAVG(icp.pkts_sent)); + storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", +@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry) + storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", + (int) f->server.other.kbytes_out.kb); + ++#if HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %d\n", ++ (int) f->icap.all.requests); ++ storeAppendPrintf(sentry, "icap.all.errors = %d\n", ++ (int) f->icap.all.errors); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", ++ (int) f->icap.all.kbytes_in.kb); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", ++ (int) f->icap.all.kbytes_out.kb); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", + f->icp.pkts_sent); + storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", +@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s) + storeAppendPrintf(s, "\tme: %s:%d\n", + inet_ntoa(conn->me.sin_addr), + ntohs(conn->me.sin_port)); +- storeAppendPrintf(s, "\tnrequests: %d\n", +- conn->nrequests); + storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", + conn->defer.n, (long int) conn->defer.until); + } +Index: src/store.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/store.c,v +retrieving revision 1.16.6.9 +retrieving revision 1.16.6.2.2.8 +diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8 +--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9 ++++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8 +@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.48.2.43 +retrieving revision 1.48.2.9.2.48 +diff -p -u -b -r1.48.2.43 -r1.48.2.9.2.48 +--- src/structs.h 4 Sep 2005 02:13:28 -0000 1.48.2.43 ++++ src/structs.h 30 Nov 2005 21:52:15 -0000 1.48.2.9.2.48 +@@ -384,6 +384,22 @@ struct _RemovalPolicySettings { + wordlist *args; + }; + ++#if HS_FEAT_ICAP ++struct _IcapConfig { ++ int onoff; ++ int preview_enable; ++ icap_service *service_head; ++ icap_class *class_head; ++ icap_access *access_head; ++ int preview_size; ++ int check_interval; ++ int send_client_ip; ++ int send_auth_user; ++ char *auth_scheme; ++}; ++ ++#endif /* HS_FEAT_ICAP */ ++ + struct _SquidConfig { + struct { + squid_off_t maxSize; +@@ -714,6 +730,9 @@ struct _SquidConfig { + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + external_acl *externalAclHelperList; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -787,7 +806,10 @@ struct _fde { + } flags; + squid_off_t bytes_read; + squid_off_t bytes_written; +- int uses; /* ie # req's over persistent conn */ ++ struct { ++ int uses; ++ int type; ++ } pconn; + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; +@@ -982,6 +1004,130 @@ struct _http_state_flags { + unsigned int request_sent:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -993,10 +1139,14 @@ struct _HttpStateData { + int fd; + http_state_flags flags; + FwdState *fwd; ++#ifdef HS_FEAT_ICAP ++ struct _IcapStateData *icap_writer; ++#endif + char *body_buf; + int body_buf_sz; + }; + ++ + struct _icpUdpData { + struct sockaddr_in address; + void *msg; +@@ -1092,6 +1242,7 @@ struct _clientHttpRequest { + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; ++ unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { +@@ -1100,6 +1251,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1127,7 +1281,6 @@ struct _ConnStateData { + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; +- int nrequests; + struct { + int n; + time_t until; +@@ -1678,6 +1831,9 @@ struct _request_t { + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + }; +@@ -1784,7 +1940,11 @@ struct _StatCounters { + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; +- } server; ++ } ++#if HS_FEAT_ICAP ++ icap, ++#endif ++ server; + struct { + int pkts_sent; + int queries_sent; +Index: src/typedefs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/typedefs.h,v +retrieving revision 1.25.6.8 +retrieving revision 1.25.6.1.6.13 +diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13 +--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8 ++++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13 +@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade + typedef struct _HttpBody HttpBody; + typedef struct _HttpReply HttpReply; + typedef struct _HttpStateData HttpStateData; ++#ifdef HS_FEAT_ICAP ++typedef struct _IcapStateData IcapStateData; ++typedef struct _IcapConfig IcapConfig; ++typedef struct _icap_service icap_service; ++typedef struct _icap_service_list icap_service_list; ++typedef struct _icap_class icap_class; ++typedef struct _icap_access icap_access; ++typedef struct _IcapOptData IcapOptData; ++#endif + typedef struct _icpUdpData icpUdpData; + typedef struct _clientHttpRequest clientHttpRequest; + typedef struct _ConnStateData ConnStateData; +Index: src/url.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/url.c,v +retrieving revision 1.7.6.6 +retrieving revision 1.7.6.5.2.2 +diff -p -u -b -r1.7.6.6 -r1.7.6.5.2.2 +--- src/url.c 12 Nov 2005 03:13:48 -0000 1.7.6.6 ++++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -221,6 +224,10 @@ urlParseProtocol(const char *s) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; ++#ifdef HS_FEAT_ICAP ++ if (strcasecmp(s, "icap") == 0) ++ return PROTO_ICAP; ++#endif + return PROTO_NONE; + } + +@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p) + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; ++#ifdef HS_FEAT_ICAP ++ case PROTO_ICAP: ++ return 1344; ++#endif + default: + return 0; + } diff --git a/www/squid25/Makefile b/www/squid25/Makefile index 31916d4cb34e..1f75bf5d90f8 100644 --- a/www/squid25/Makefile +++ b/www/squid25/Makefile @@ -66,10 +66,14 @@ # Override the maximum number of filedescriptors. Useful if you # build as another user who is not privileged to use the amount # of filedescriptors the resulting binary is expected to support. +# --enable-ntlm-fail-open +# Enable NTLM fail open, where a helper that fails one of the +# Authentication steps can allow squid to still authenticate the user # PORTNAME= squid PORTVERSION= 2.5.12 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= \ ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ @@ -82,6 +86,7 @@ DISTNAME= squid-2.5.STABLE12 DIST_SUBDIR= squid2.5 PATCH_SITES= http://www.squid-cache.org/Versions/v2/2.5/bugs/ +PATCHFILES= squid-2.5.STABLE12-SMB_BadFetch.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -120,6 +125,7 @@ OPTIONS= SQUID_LDAP_AUTH "Install LDAP authentication helpers" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ + SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ SQUID_COSS "Enable the COSS storage scheme" off \ SQUID_LARGEFILE "Support log and cache files >2GB" off \ @@ -293,6 +299,12 @@ EXTRA_PATCHES+= ${PATCHDIR}/follow_xff-2.5.patch \ ${PATCHDIR}/follow_xff-configure.patch CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif +.if defined(WITH_SQUID_ICAP) +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.5-core.patch \ + ${PATCHDIR}/icap-2.5-bootstrap.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE +.endif .if defined(WITH_SQUID_LARGEFILE) CONFIGURE_ARGS+= --with-large-files --enable-large-cache-files .endif diff --git a/www/squid25/distinfo b/www/squid25/distinfo index 5d1b5428ba58..3e55ac8d1717 100644 --- a/www/squid25/distinfo +++ b/www/squid25/distinfo @@ -1,2 +1,6 @@ MD5 (squid2.5/squid-2.5.STABLE12.tar.bz2) = 7354255015b3772a1e024dfac173e48c +SHA256 (squid2.5/squid-2.5.STABLE12.tar.bz2) = ba0ccd956323f0dad46c19aa8d40c537846fedfc3778b5730e5610f16c0d9af1 SIZE (squid2.5/squid-2.5.STABLE12.tar.bz2) = 1075111 +MD5 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 8e83b776c0d015bd4137cc1ca08f6d38 +SHA256 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 9ca8427c2eb9e5cbdb5a49fb5cb94fc00853ad965f87666f8fc35236e98bc0ae +SIZE (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 826 diff --git a/www/squid25/files/icap-2.5-bootstrap.patch b/www/squid25/files/icap-2.5-bootstrap.patch new file mode 100644 index 000000000000..247ca0c94cbc --- /dev/null +++ b/www/squid25/files/icap-2.5-bootstrap.patch @@ -0,0 +1,422 @@ +Patch 2 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.5-core.patch for further information. + +Patch last updated: 2005-12-17 + +--- configure.orig Sat Oct 22 11:56:01 2005 ++++ configure Sat Dec 17 17:45:21 2005 +@@ -70,6 +70,8 @@ + ac_help="$ac_help + --enable-delay-pools Enable delay pools to limit bandwidth usage" + ac_help="$ac_help ++ --enable-icap-support Enable iCAP client capability" ++ac_help="$ac_help + --enable-useragent-log Enable logging of User-Agent header" + ac_help="$ac_help + --enable-referer-log Enable logging of Referer header" +@@ -2170,6 +2172,38 @@ + + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ cat >> confdefs.h <<\EOF ++#define HS_FEAT_ICAP 1 ++EOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ fi ++ ++fi ++ ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -7428,14 +7462,14 @@ + fi + ;; + esac +- echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 +-echo "configure:7433: checking for main in -lpthread" >&5 ++ echo $ac_n "checking for main in -pthread""... $ac_c" 1>&6 ++echo "configure:7433: checking for main in -pthread" >&5 + ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 + else + ac_save_LIBS="$LIBS" +-LIBS="-lpthread $LIBS" ++LIBS="-pthread $LIBS" + cat > conftest.$ac_ext <<EOF + #line 7441 "configure" + #include "confdefs.h" +@@ -7465,7 +7499,7 @@ + #define $ac_tr_lib 1 + EOF + +- LIBS="-lpthread $LIBS" ++ LIBS="-pthread $LIBS" + + else + echo "$ac_t""no" 1>&6 +@@ -7769,6 +7803,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strtoll \ + sysconf \ + syslog \ +@@ -7898,6 +7934,50 @@ + fi + fi + ++ ++if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++fi ++ ++ ++ ++ + echo $ac_n "checking if va_copy is implemented""... $ac_c" 1>&6 + echo "configure:7903: checking if va_copy is implemented" >&5 + if eval "test \"`echo '$''{'ac_cv_func_va_copy'+set}'`\" = set"; then +@@ -9072,6 +9152,8 @@ + s%@ENABLE_PINGER_FALSE@%$ENABLE_PINGER_FALSE%g + s%@USE_DELAY_POOLS_TRUE@%$USE_DELAY_POOLS_TRUE%g + s%@USE_DELAY_POOLS_FALSE@%$USE_DELAY_POOLS_FALSE%g ++s%@USE_ICAP_TRUE@%$USE_ICAP_TRUE%g ++s%@USE_ICAP_FALSE@%$USE_ICAP_FALSE%g + s%@USE_SNMP_TRUE@%$USE_SNMP_TRUE%g + s%@USE_SNMP_FALSE@%$USE_SNMP_FALSE%g + s%@SNMPLIB@%$SNMPLIB%g +@@ -9118,6 +9200,10 @@ + s%@LIB_LBER@%$LIB_LBER%g + s%@NEED_OWN_SNPRINTF_TRUE@%$NEED_OWN_SNPRINTF_TRUE%g + s%@NEED_OWN_SNPRINTF_FALSE@%$NEED_OWN_SNPRINTF_FALSE%g ++s%@NEED_OWN_STRNSTR_TRUE@%$NEED_OWN_STRNSTR_TRUE%g ++s%@NEED_OWN_STRNSTR_FALSE@%$NEED_OWN_STRNSTR_FALSE%g ++s%@NEED_OWN_STRCASESTR_TRUE@%$NEED_OWN_STRCASESTR_TRUE%g ++s%@NEED_OWN_STRCASESTR_FALSE@%$NEED_OWN_STRCASESTR_FALSE%g + s%@REGEXLIB@%$REGEXLIB%g + s%@LIBREGEX@%$LIBREGEX%g + s%@LIBOBJS@%$LIBOBJS%g +--- include/autoconf.h.in.orig Tue Sep 13 02:12:34 2005 ++++ include/autoconf.h.in Sat Dec 17 17:45:21 2005 +@@ -124,6 +124,11 @@ + */ + #undef DELAY_POOLS + ++/* ++ * ICAP - Internet Content Adaptation Protocol ++ */ ++#undef HS_FEAT_ICAP ++ + /* + * If you want to log User-Agent request header values, define this. + * By default, they are written to useragent.log in the Squid log +@@ -574,6 +579,12 @@ + + /* Define if you have the statfs function. */ + #undef HAVE_STATFS ++ ++/* Define if you have the strcasestr function. */ ++#undef HAVE_STRCASESTR ++ ++/* Define if you have the strnstr function. */ ++#undef HAVE_STRNSTR + + /* Define if you have the strerror function. */ + #undef HAVE_STRERROR +--- lib/Makefile.in.orig Wed Sep 28 22:57:20 2005 ++++ lib/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -123,6 +123,13 @@ + + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = ++ ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++ ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++ + @NEED_OWN_MD5_TRUE@MD5SOURCE = md5.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = + +@@ -158,6 +165,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +@@ -196,13 +205,18 @@ + @NEED_OWN_MD5_FALSE@am__objects_1 = + @NEED_OWN_SNPRINTF_FALSE@am__objects_2 = + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) ++@NEED_OWN_STRNSTR_FALSE@am__objects_3 = ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_FALSE@am__objects_4 = + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) stub_memaccount.$(OBJEXT) \ ++ util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) cru + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -224,15 +238,16 @@ + @AMDEP_TRUE@ $(DEPDIR)/dlmalloc.Po $(DEPDIR)/drand48.Po \ + @AMDEP_TRUE@ $(DEPDIR)/getfullhostname.Po $(DEPDIR)/hash.Po \ + @AMDEP_TRUE@ $(DEPDIR)/heap.Po $(DEPDIR)/html_quote.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/initgroups.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/iso3307.Po $(DEPDIR)/md5.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/ntlmauth.Po $(DEPDIR)/radix.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1035.Po $(DEPDIR)/rfc1123.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1738.Po $(DEPDIR)/rfc2617.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/safe_inet_addr.Po $(DEPDIR)/snprintf.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/splay.Po $(DEPDIR)/strerror.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/stub_memaccount.Po $(DEPDIR)/tempnam.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uudecode.Po ++@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/iso3307.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/md5.Po $(DEPDIR)/ntlmauth.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/radix.Po $(DEPDIR)/rfc1035.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc1123.Po $(DEPDIR)/rfc1738.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc2617.Po $(DEPDIR)/safe_inet_addr.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/snprintf.Po $(DEPDIR)/splay.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strcasestr.Po $(DEPDIR)/strerror.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strnstr.Po $(DEPDIR)/stub_memaccount.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/tempnam.Po $(DEPDIR)/util.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/uudecode.Po + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -241,8 +256,8 @@ + DIST_SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) \ + $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) \ + $(libregex_a_SOURCES) +-DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c \ +- initgroups.c strerror.c tempnam.c ++DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c strerror.c \ ++ tempnam.c + SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) $(libregex_a_SOURCES) + + all: all-am +@@ -295,7 +310,6 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/heap.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/html_quote.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/inet_ntoa.Po@am__quote@ +-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/initgroups.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iso3307.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/md5.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ntlmauth.Po@am__quote@ +@@ -307,7 +321,9 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/safe_inet_addr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splay.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerror.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tempnam.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Wed Sep 28 22:57:21 2005 ++++ src/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -125,6 +125,9 @@ + install_sh = @install_sh@ + makesnmplib = @makesnmplib@ + ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c ++@USE_ICAP_FALSE@ICAPSOURCE = ++ + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver +@@ -249,6 +252,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -468,54 +472,58 @@ + pinger_LDADD = $(LDADD) + pinger_DEPENDENCIES = + pinger_LDFLAGS = +-@USE_DELAY_POOLS_TRUE@am__objects_3 = delay_pools.$(OBJEXT) +-@USE_DELAY_POOLS_FALSE@am__objects_3 = +-@USE_DNSSERVER_FALSE@am__objects_4 = dns_internal.$(OBJEXT) +-@USE_DNSSERVER_TRUE@am__objects_4 = dns.$(OBJEXT) +-@ENABLE_HTCP_TRUE@am__objects_5 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_FALSE@am__objects_6 = +-@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) +-@USE_SNMP_FALSE@am__objects_7 = +-@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) +-@ENABLE_SSL_FALSE@am__objects_8 = +-@ENABLE_UNLINKD_FALSE@am__objects_9 = +-@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_10 = ++@USE_DELAY_POOLS_FALSE@am__objects_5 = ++@USE_DELAY_POOLS_TRUE@am__objects_5 = delay_pools.$(OBJEXT) ++@USE_DNSSERVER_FALSE@am__objects_6 = dns_internal.$(OBJEXT) ++@USE_DNSSERVER_TRUE@am__objects_6 = dns.$(OBJEXT) ++@ENABLE_HTCP_TRUE@am__objects_7 = htcp.$(OBJEXT) ++@USE_ICAP_TRUE@am__objects_8 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@USE_ICAP_FALSE@am__objects_8 = ++@MAKE_LEAKFINDER_TRUE@am__objects_9 = leakfinder.$(OBJEXT) ++@MAKE_LEAKFINDER_FALSE@am__objects_9 = ++@USE_SNMP_TRUE@am__objects_10 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) ++@USE_SNMP_FALSE@am__objects_10 = ++@ENABLE_SSL_FALSE@am__objects_11 = ++@ENABLE_SSL_TRUE@am__objects_11 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_12 = unlinkd.$(OBJEXT) ++@ENABLE_UNLINKD_FALSE@am__objects_12 = ++@ENABLE_WIN32SPECIFIC_FALSE@am__objects_13 = ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_13 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \ + cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \ + client_db.$(OBJEXT) client_side.$(OBJEXT) comm.$(OBJEXT) \ +- comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_3) \ +- disk.$(OBJEXT) $(am__objects_4) errorpage.$(OBJEXT) \ ++ comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \ ++ disk.$(OBJEXT) $(am__objects_6) errorpage.$(OBJEXT) \ + ETag.$(OBJEXT) event.$(OBJEXT) external_acl.$(OBJEXT) \ + fd.$(OBJEXT) filemap.$(OBJEXT) forward.$(OBJEXT) \ + fqdncache.$(OBJEXT) ftp.$(OBJEXT) gopher.$(OBJEXT) \ +- helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ ++ helper.$(OBJEXT) $(am__objects_7) http.$(OBJEXT) \ + HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_6) \ +- logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_8) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_9) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ + MemPool.$(OBJEXT) MemBuf.$(OBJEXT) mime.$(OBJEXT) \ + multicast.$(OBJEXT) neighbors.$(OBJEXT) net_db.$(OBJEXT) \ + Packer.$(OBJEXT) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \ + peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ +- ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_10) \ ++ ssl.$(OBJEXT) $(am__objects_11) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_12) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- whois.$(OBJEXT) $(am__objects_10) ++ whois.$(OBJEXT) $(am__objects_13) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -563,7 +571,9 @@ + @AMDEP_TRUE@ $(DEPDIR)/fqdncache.Po $(DEPDIR)/ftp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/globals.Po $(DEPDIR)/gopher.Po \ + @AMDEP_TRUE@ $(DEPDIR)/helper.Po $(DEPDIR)/htcp.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icmp.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icap_common.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_opt.Po $(DEPDIR)/icap_reqmod.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_respmod.Po $(DEPDIR)/icmp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/icp_v2.Po $(DEPDIR)/icp_v3.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ident.Po $(DEPDIR)/internal.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ipc.Po $(DEPDIR)/ipcache.Po \ +@@ -777,6 +787,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v3.Po@am__quote@ diff --git a/www/squid25/files/icap-2.5-core.patch b/www/squid25/files/icap-2.5-core.patch new file mode 100644 index 000000000000..22d209c18fb4 --- /dev/null +++ b/www/squid25/files/icap-2.5-core.patch @@ -0,0 +1,7059 @@ +Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch only contains the parts of the original patchset that +actually implement the ICAP client functionality. The updates to +the build infrastructure are omitted to avoid the need to run an +autotools bootstrap. Instead, we simulate said bootstrapping with +a second patch, icap-2.5-bootstrap.patch. + +The patchset was pulled from the project's CVS repository +at cvs.devel.squid-cache.org using + +cvs diff -u -b -N -kk -rs2_5 -ricap-2_5 + +See also +<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5> +for the "official" auto-generated patchset. + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2005-12-17 + +Index: errors/Bulgarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Bulgarian/ERR_ICAP_FAILURE +diff -N errors/Bulgarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Catalan/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Catalan/ERR_ICAP_FAILURE +diff -N errors/Catalan/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Czech/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Czech/ERR_ICAP_FAILURE +diff -N errors/Czech/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Danish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Danish/ERR_ICAP_FAILURE +diff -N errors/Danish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Dutch/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Dutch/ERR_ICAP_FAILURE +diff -N errors/Dutch/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/English/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/English/ERR_ICAP_FAILURE +diff -N errors/English/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Estonian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Estonian/ERR_ICAP_FAILURE +diff -N errors/Estonian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Finnish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Finnish/ERR_ICAP_FAILURE +diff -N errors/Finnish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/French/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/French/ERR_ICAP_FAILURE +diff -N errors/French/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/German/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/German/ERR_ICAP_FAILURE +diff -N errors/German/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2 +@@ -0,0 +1,33 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>FEHLER</H1> ++<H2>Der angeforderte URL konnte nicht geholt werden</H2> ++<HR noshade size="1px"> ++<P> ++Während des Versuches, den URL<BR> ++<A HREF="%U">%U</A> ++ ++<BR> ++zu laden, trat der folgende Fehler auf: ++<UL> ++<LI> ++<STRONG> ++ICAP-Protokollfehler ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: ++<UL> ++<LI>Nicht erreichbarer ICAP-Server ++<LI>Ungültige Antwort vom ICAP-Server ++ ++</UL> ++</P> ++ ++<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Greek/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Greek/ERR_ICAP_FAILURE +diff -N errors/Greek/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hebrew/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hebrew/ERR_ICAP_FAILURE +diff -N errors/Hebrew/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hungarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hungarian/ERR_ICAP_FAILURE +diff -N errors/Hungarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Italian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Italian/ERR_ICAP_FAILURE +diff -N errors/Italian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Japanese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Japanese/ERR_ICAP_FAILURE +diff -N errors/Japanese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Korean/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Korean/ERR_ICAP_FAILURE +diff -N errors/Korean/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Lithuanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Lithuanian/ERR_ICAP_FAILURE +diff -N errors/Lithuanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Polish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Polish/ERR_ICAP_FAILURE +diff -N errors/Polish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Portuguese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Portuguese/ERR_ICAP_FAILURE +diff -N errors/Portuguese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Romanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Romanian/ERR_ICAP_FAILURE +diff -N errors/Romanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-1251/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-1251/ERR_ICAP_FAILURE +diff -N errors/Russian-1251/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-koi8-r/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE +diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Serbian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Serbian/ERR_ICAP_FAILURE +diff -N errors/Serbian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Simplify_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE +diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Slovak/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Slovak/ERR_ICAP_FAILURE +diff -N errors/Slovak/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Spanish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Spanish/ERR_ICAP_FAILURE +diff -N errors/Spanish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Swedish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Swedish/ERR_ICAP_FAILURE +diff -N errors/Swedish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Traditional_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE +diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Turkish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Turkish/ERR_ICAP_FAILURE +diff -N errors/Turkish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.10 +retrieving revision 1.10.30.2 +diff -p -u -b -r1.10 -r1.10.30.2 +--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10 ++++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2 +@@ -132,4 +132,12 @@ double drand48(void); + */ + int statMemoryAccounted(void); + ++#ifndef HAVE_STRNSTR ++extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); ++#endif ++ ++#ifndef HAVE_STRCASESTR ++extern char *strcasestr(const char *haystack, const char *needle); ++#endif ++ + #endif /* SQUID_UTIL_H */ +Index: lib/Makefile.am +=================================================================== +RCS file: /cvsroot/squid/squid/lib/Makefile.am,v +retrieving revision 1.4 +retrieving revision 1.4.26.2 +diff -p -u -b -r1.4 -r1.4.26.2 +--- lib/Makefile.am 21 Nov 2001 23:48:57 -0000 1.4 ++++ lib/Makefile.am 6 Apr 2004 13:04:38 -0000 1.4.26.2 +@@ -8,6 +8,19 @@ SNPRINTFSOURCE=snprintf.c + else + SNPRINTFSOURCE= + endif ++ ++if NEED_OWN_STRNSTR ++STRNSTRSOURCE=strnstr.c ++else ++STRNSTRSOURCE= ++endif ++ ++if NEED_OWN_STRCASESTR ++STRCASESTRSOURCE=strcasestr.c ++else ++STRCASESTRSOURCE= ++endif ++ + if NEED_OWN_MD5 + MD5SOURCE=md5.c + else +@@ -43,6 +56,8 @@ libmiscutil_a_SOURCES = \ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +Index: lib/strcasestr.c +=================================================================== +RCS file: lib/strcasestr.c +diff -N lib/strcasestr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1 +@@ -0,0 +1,126 @@ ++/* Return the offset of one string within another. ++ Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* ++ * My personal strstr() implementation that beats most other algorithms. ++ * Until someone tells me otherwise, I assume that this is the ++ * fastest implementation of strstr() in C. ++ * I deliberately chose not to comment it. You should have at least ++ * as much fun trying to understand it, as I had to write it :-). ++ * ++ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ ++ ++/* ++ * modified to work outside of glibc (rhorstmann, 06/04/2004) ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRCASESTR ++#include <ctype.h> ++ ++typedef unsigned chartype; ++ ++char * ++strcasestr (phaystack, pneedle) ++ const char *phaystack; ++ const char *pneedle; ++{ ++ register const unsigned char *haystack, *needle; ++ register chartype b, c; ++ ++ haystack = (const unsigned char *) phaystack; ++ needle = (const unsigned char *) pneedle; ++ ++ b = tolower (*needle); ++ if (b != '\0') ++ { ++ haystack--; /* possible ANSI violation */ ++ do ++ { ++ c = *++haystack; ++ if (c == '\0') ++ goto ret0; ++ } ++ while (tolower (c) != (int) b); ++ ++ c = tolower (*++needle); ++ if (c == '\0') ++ goto foundneedle; ++ ++needle; ++ goto jin; ++ ++ for (;;) ++ { ++ register chartype a; ++ register const unsigned char *rhaystack, *rneedle; ++ ++ do ++ { ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ if (tolower (a) == (int) b) ++ break; ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++shloop: ++ ; ++ } ++ while (tolower (a) != (int) b); ++ ++jin: a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ ++ if (tolower (a) != (int) c) ++ goto shloop; ++ ++ rhaystack = haystack-- + 1; ++ rneedle = needle; ++ a = tolower (*rneedle); ++ ++ if (tolower (*rhaystack) == (int) a) ++ do ++ { ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ if (tolower (*rhaystack) != (int) a) ++ break; ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ } ++ while (tolower (*rhaystack) == (int) a); ++ ++ needle = rneedle; /* took the register-poor approach */ ++ ++ if (a == '\0') ++ break; ++ } ++ } ++foundneedle: ++ return (char*) haystack; ++ret0: ++ return 0; ++} ++#endif +Index: lib/strnstr.c +=================================================================== +RCS file: lib/strnstr.c +diff -N lib/strnstr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include <string.h> ++#include <util.h> ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.5.30.3 +retrieving revision 1.5.44.8 +diff -p -u -b -r1.5.30.3 -r1.5.44.8 +--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3 ++++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8 +@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb) + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); + } ++ ++int ++memBufRead(int fd, MemBuf * mb) ++{ ++ int len; ++ if (mb->capacity == mb->size) ++ memBufGrow(mb, SQUID_TCP_SO_RCVBUF); ++ len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); ++ if (len) ++ mb->size += len; ++ return len; ++} +Index: src/cache_cf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cache_cf.c,v +retrieving revision 1.38.6.29 +retrieving revision 1.38.6.11.2.22 +diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22 +--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29 ++++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22 +@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli + return bodylist.head == NULL; + } + ++#ifdef HS_FEAT_ICAP ++ ++/*************************************************** ++ * prototypes ++ */ ++static int icap_service_process(icap_service * s); ++static void icap_service_init(icap_service * s); ++static void icap_service_destroy(icap_service * s); ++icap_service *icap_service_lookup(char *name); ++static int icap_class_process(icap_class * c); ++static void icap_class_destroy(icap_class * c); ++static void icap_access_destroy(icap_access * a); ++static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access <servicename> {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.14.6.1 +retrieving revision 1.14.32.2 +diff -p -u -b -r1.14.6.1 -r1.14.32.2 +--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1 ++++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2 +@@ -144,6 +144,10 @@ cbdataInit(void) + CREATE_CBDATA(statefulhelper); + CREATE_CBDATA(helper_stateful_server); + CREATE_CBDATA(HttpStateData); ++#ifdef HS_FEAT_ICAP ++ CREATE_CBDATA(IcapStateData); ++ CREATE_CBDATA(icap_service); ++#endif + CREATE_CBDATA_FREE(peer, peerDestroy); + CREATE_CBDATA(ps_state); + CREATE_CBDATA(RemovalPolicy); +Index: src/cf.data.pre +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf.data.pre,v +retrieving revision 1.49.2.84 +retrieving revision 1.49.2.33.2.32 +diff -p -u -b -r1.49.2.84 -r1.49.2.33.2.32 +--- src/cf.data.pre 21 Oct 2005 02:13:47 -0000 1.49.2.84 ++++ src/cf.data.pre 24 Oct 2005 17:07:42 -0000 1.49.2.33.2.32 +@@ -2397,7 +2397,6 @@ DOC_START + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -2716,6 +2715,177 @@ DOC_START + DOC_END + + COMMENT_START ++ ICAP OPTIONS ++ ----------------------------------------------------------------------------- ++COMMENT_END ++ ++NAME: icap_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.onoff ++DEFAULT: off ++DOC_START ++ If you want to enable the ICAP client module, set this to on. ++DOC_END ++ ++NAME: icap_preview_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.preview_enable ++DEFAULT: off ++DOC_START ++ Set this to 'on' if you want to enable the ICAP preview ++ feature in Squid. ++DOC_END ++ ++NAME: icap_preview_size ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.preview_size ++DEFAULT: -1 ++DOC_START ++ The default size of preview data to be sent to the ICAP server. ++ -1 means no preview. This value might be overwritten on a per server ++ basis by OPTIONS requests. ++DOC_END ++ ++NAME: icap_check_interval ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.check_interval ++DEFAULT: 300 ++DOC_START ++ If an ICAP server does not respond, it gets marked as unreachable. Squid ++ will try again to reach it after this time. ++DOC_END ++ ++NAME: icap_send_client_ip ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_client_ip ++DEFAULT: off ++DOC_START ++ This adds the header "X-Client-IP" to ICAP requests. Can also be ++ set from the server's response to OPTIONS. ++DOC_END ++ ++NAME: icap_send_auth_user ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_auth_user ++DEFAULT: off ++DOC_START ++ This adds the header "X-Authenticated-User" to ICAP requests ++ if proxy access is authentified. Can also be set from the server's ++ response to OPTIONS. ++DOC_END ++ ++NAME: icap_auth_scheme ++TYPE: string ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.auth_scheme ++DEFAULT: Local://%u ++DOC_START ++ Authentification scheme to pass to ICAP requests if ++ icap_send_auth_user is enabled. The first occurence of "%u" ++ is replaced by the authentified user name. If no "%u" is found, ++ the username is added at the end of the scheme. ++ ++ See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, ++ section 3.4 for details on this. ++ ++ Examples: ++ ++ icap_auth_scheme Local://%u ++ icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com ++ icap_auth_scheme WinNT://nt-domain/%u ++ icap_auth_scheme Radius://radius-server/%u ++DOC_END ++ ++NAME: icap_service ++TYPE: icap_service_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines a single ICAP service ++ ++ icap_service servicename vectoring_point bypass service_url [options ...] ++ ++ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache ++ This specifies at which point of request processing the ICAP ++ service should be plugged in. ++ bypass = 1|0 ++ If set to 1 and the ICAP server cannot be reached, the request will go ++ through without being processed by an ICAP server ++ service_url = icap://servername:port/service ++ ++ Options: ++ ++ no-keep-alive To always close the connection to icap server ++ after the transaction completes ++ ++ ++ Note: reqmod_precache and respmod_postcache is not yet implemented ++ ++ Load-balancing and high availability: ++ You can obtain load-balancing and high availability by defining a ++ named service with different definitions. Then, the client ++ loops through the different entities of the service providing ++ load-balancing. If an entity is marked as unreachable, the client goes ++ one step further to the next entity: you have the high-availability. ++ See the service_1 definition below ++ ++Example: ++icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod ++icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive ++icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod ++DOC_END ++ ++NAME: icap_class ++TYPE: icap_class_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines an ICAP service chain. If there are multiple services per ++ vectoring point, they are processed in the specified order. ++ ++ icap_class classname servicename... ++ ++Example: ++icap_class class_1 service_1 service_2 ++icap class class_2 service_1 service_3 ++DOC_END ++ ++NAME: icap_access ++TYPE: icap_access_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Redirects a request through an ICAP service class, depending ++ on given acls ++ ++ icap_access classname allow|deny [!]aclname... ++ ++ The icap_access statements are processed in the order they appear in ++ this configuration file. If an access list matches, the processing stops. ++ For an "allow" rule, the specified class is used for the request. A "deny" ++ rule simply stops processing without using the class. You can also use the ++ special classname "None". ++ ++ For backward compatibility, it is also possible to use services ++ directly here. ++Example: ++icap_access class_1 allow all ++DOC_END ++ ++COMMENT_START + MISCELLANEOUS + ----------------------------------------------------------------------------- + COMMENT_END +Index: src/cf_gen_defines +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v +retrieving revision 1.5 +retrieving revision 1.5.48.3 +diff -p -u -b -r1.5 -r1.5.48.3 +--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5 ++++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3 +@@ -18,12 +18,13 @@ BEGIN { + define["USE_UNLINKD"]="--enable-unlinkd" + define["USE_USERAGENT_LOG"]="--enable-useragent-log" + define["USE_WCCP"]="--enable-wccp" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +- DEFINE=define[$2] ++ DEFINE = define[$2] + else +- DEFINE="-D" $2 ++ DEFINE = "-D" $2 + print "{\"" $2 "\", \"" DEFINE "\", " + print "#if " $2 + print "1" +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.47.2.71 +retrieving revision 1.47.2.28.2.40 +diff -p -u -b -r1.47.2.71 -r1.47.2.28.2.40 +--- src/client_side.c 19 Oct 2005 02:13:20 -0000 1.47.2.71 ++++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40 +@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n"; + static CWCB clientWriteComplete; + static CWCB clientWriteBodyComplete; + static PF clientReadRequest; +-static PF connStateFree; ++PF connStateFree; + static PF requestTimeout; + static PF clientLifetimeTimeout; + static int clientCheckTransferDone(clientHttpRequest *); +@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien + static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); + static void clientPackTermBound(String boundary, MemBuf * mb); + static void clientInterpretRequestHeaders(clientHttpRequest *); +-static void clientProcessRequest(clientHttpRequest *); ++void clientProcessRequest(clientHttpRequest *); + static void clientProcessExpired(void *data); + static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); +-static int clientCachable(clientHttpRequest * http); +-static int clientHierarchical(clientHttpRequest * http); +-static int clientCheckContentLength(request_t * r); ++int clientCachable(clientHttpRequest * http); ++int clientHierarchical(clientHttpRequest * http); ++int clientCheckContentLength(request_t * r); + static DEFER httpAcceptDefer; + static log_type clientProcessRequest2(clientHttpRequest * http); + static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); + static int clientRequestBodyTooLarge(squid_off_t clen); + static void clientProcessBody(ConnStateData * conn); + static void clientEatRequestBody(clientHttpRequest *); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + static int + checkAccelOnly(clientHttpRequest * http) +@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res + http->request = requestLink(new_request); + } + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -931,11 +938,22 @@ httpRequestFree(void *data) + *H = http->next; + http->next = NULL; + dlinkDelete(&http->active, &ClientActiveRequests); ++#if HS_FEAT_ICAP ++ /*In the case that the upload of data breaks, we need this code here .... */ ++ if (NULL != http->icap_reqmod) { ++ if (cbdataValid(http->icap_reqmod)) ++ if (http->icap_reqmod->icap_fd > -1) { ++ comm_close(http->icap_reqmod->icap_fd); ++ } ++ cbdataUnlock(http->icap_reqmod); ++ http->icap_reqmod = NULL; ++ } ++#endif + cbdataFree(http); + } + + /* This is a handler normally called by comm_close() */ +-static void ++void + connStateFree(int fd, void *data) + { + ConnStateData *connState = data; +@@ -958,7 +976,6 @@ connStateFree(int fd, void *data) + } else + safe_free(connState->in.buf); + /* XXX account connState->in.buf */ +- pconnHistCount(0, connState->nrequests); + cbdataFree(connState); + #ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ +@@ -1103,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1122,7 +1139,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1148,7 +1165,7 @@ clientCachable(clientHttpRequest * http) + } + + /* Return true if we can query our neighbors for this object */ +-static int ++int + clientHierarchical(clientHttpRequest * http) + { + const char *url = http->uri; +@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -2449,6 +2466,11 @@ clientProcessRequest(clientHttpRequest * + debug(33, 4) ("clientProcessRequest: %s '%s'\n", + RequestMethodStr[r->method], + url); ++#if HS_FEAT_ICAP ++ if (clientIcapReqMod(http)) { ++ return; ++ } ++#endif + if (r->method == METHOD_CONNECT && !http->redirect.status) { + http->log_type = LOG_TCP_MISS; + sslStart(http, &http->out.size, &http->al.http.code); +@@ -2993,6 +3015,20 @@ clientReadRequest(int fd, void *data) + (long) conn->in.offset, (long) conn->in.size); + len = conn->in.size - conn->in.offset - 1; + } ++#if HS_FEAT_ICAP ++ /* ++ * This check exists because ICAP doesn't always work well ++ * with persistent (reused) connections. One version of the ++ * REQMOD code creates a fake ConnStateData, which doesn't have ++ * an in.buf. We want to make sure that the fake ConnStateData ++ * doesn't get used here. ++ */ ++ if (NULL == conn->in.buf) { ++ debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); ++ comm_close(fd); ++ return; ++ } ++#endif + statCounter.syscalls.sock.reads++; + size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); + if (size > 0) { +@@ -3096,7 +3132,8 @@ clientReadRequest(int fd, void *data) + /* add to the client request queue */ + for (H = &conn->chr; *H; H = &(*H)->next); + *H = http; +- conn->nrequests++; ++ F->pconn.uses++; ++ F->pconn.type = 0; + /* + * I wanted to lock 'http' here since its callback data for + * clientLifetimeTimeout(), but there's no logical place to +@@ -3266,7 +3303,7 @@ clientReadRequest(int fd, void *data) + } + + /* file_read like function, for reading body content */ +-static void ++void + clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) + { + ConnStateData *conn = request->body_reader_data; +@@ -3390,7 +3427,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data) + * Some data has been sent to the client, just close the FD + */ + comm_close(fd); +- } else if (conn->nrequests) { ++ } else if (fd_table[fd].pconn.uses) { + /* + * assume its a persistent connection; just close it + */ +@@ -3948,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re + } + } + } ++ ++#if HS_FEAT_ICAP ++static int ++clientIcapReqMod(clientHttpRequest * http) ++{ ++ ErrorState *err; ++ icap_service *service; ++ if (http->flags.did_icap_reqmod) ++ return 0; ++ if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) ++ return 0; ++ debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); ++ /* ++ * Note, we pass 'start' and 'log_addr' to ICAP so the access.log ++ * entry comes out right. The 'clientHttpRequest' created by ++ * the ICAP side is the one that gets logged. The first ++ * 'clientHttpRequest' does not get logged because its out.size ++ * is zero and log_type is unset. ++ */ ++ http->icap_reqmod = icapReqModStart(service, ++ http->uri, ++ http->request, ++ http->conn->fd, ++ http->start, ++ http->conn->log_addr, ++ (void *) http->conn); ++ if (NULL == http->icap_reqmod) { ++ return 0; ++ } else if (-1 == (int) http->icap_reqmod) { ++ /* produce error */ ++ http->icap_reqmod = NULL; ++ debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); ++ http->log_type = LOG_TCP_DENIED; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = ETIMEDOUT; ++ err->request = requestLink(http->request); ++ err->src_addr = http->conn->peer.sin_addr; ++ http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return 1; ++ } ++ cbdataLock(http->icap_reqmod); ++ http->flags.did_icap_reqmod = 1; ++ return 1; ++} ++#endif +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.18.6.6 +retrieving revision 1.18.6.2.12.9 +diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9 +--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6 ++++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9 +@@ -653,8 +653,8 @@ comm_close(int fd) + #endif + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + SSL_free(F->ssl); +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.29.2.18 +retrieving revision 1.29.2.8.2.17 +diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17 +--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18 ++++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17 +@@ -93,6 +93,7 @@ typedef enum { + ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ + ERR_TOO_BIG, + TCP_RESET, ++ ERR_ICAP_FAILURE, + ERR_INVALID_RESP, + ERR_MAX + } err_type; +@@ -438,6 +439,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -610,6 +614,12 @@ typedef enum { + MEM_TLV, + MEM_SWAP_LOG_DATA, + MEM_CLIENT_REQ_BUF, ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -709,9 +719,14 @@ typedef enum { + CBDATA_RemovalPolicyWalker, + CBDATA_RemovalPurgeWalker, + CBDATA_store_client, ++#ifdef HS_FEAT_ICAP ++ CBDATA_IcapStateData, ++ CBDATA_icap_service, ++#endif + CBDATA_FIRST_CUSTOM_TYPE = 1000 + } cbdata_type; + ++ + /* + * Return codes from checkVary(request) + */ +@@ -742,4 +757,68 @@ enum { + + #endif + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.13.6.15 +retrieving revision 1.13.6.3.2.15 +diff -p -u -b -r1.13.6.15 -r1.13.6.3.2.15 +--- src/forward.c 2 Sep 2005 02:13:43 -0000 1.13.6.15 ++++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15 +@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status + else + hierarchyNote(&fwdState->request->hier, fs->code, request->host); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; + if (fs->peer) + peerConnectSucceded(fs->peer); + fwdDispatch(fwdState); +@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data) + void + fwdFail(FwdState * fwdState, ErrorState * errorState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", + err_type_str[errorState->type], + httpStatusString(errorState->http_status), +@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data) + void + fwdUnregister(int fd, FwdState * fwdState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); + assert(fd == fwdState->server_fd); + assert(fd > -1); +@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat + void + fwdComplete(FwdState * fwdState) + { +- StoreEntry *e = fwdState->entry; ++ StoreEntry *e; ++ if (NULL == fwdState) ++ return; ++ e = fwdState->entry; + assert(e->store_status == STORE_PENDING); + debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), + e->mem_obj->reply->sline.status); +Index: src/globals.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/globals.h,v +retrieving revision 1.14.6.7 +retrieving revision 1.14.6.3.2.5 +diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5 +--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7 ++++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5 +@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */ + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.17.6.32 +retrieving revision 1.17.6.3.6.39 +diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.39 +--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32 ++++ src/http.c 23 Nov 2005 20:33:07 -0000 1.17.6.3.6.39 +@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry; + + static PF httpReadReply; + static void httpSendRequest(HttpStateData *); +-static PF httpStateFree; ++PF httpStateFree; + static PF httpTimeout; + static void httpCacheNegatively(StoreEntry *); + static void httpMakePrivate(StoreEntry *); +@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *) + static int httpCachableReply(HttpStateData *); + static void httpMaybeRemovePublic(StoreEntry *, http_status); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data) + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; ++#if HS_FEAT_ICAP ++ cbdataUnlock(httpState->icap_writer); ++#endif + cbdataFree(httpState); + } + +@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht + } + + /* rewrite this later using new interfaces @?@ */ +-static void ++void + httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) + { + StoreEntry *entry = httpState->entry; +@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; ++ squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ +- if (httpState->reply_hdr_state < 2) ++ if (httpState->reply_hdr_state < 2) { ++ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", ++ httpState->reply_hdr_state); + return 0; ++ } + clen = httpReplyBodySize(httpState->request->method, reply); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ content_bytes_read = httpState->icap_writer->fake_content_length; ++ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); ++ } else ++#endif ++ content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ +- if (mem->inmem_hi > clen + reply->hdr_sz) ++ if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ +- if (mem->inmem_hi < clen + reply->hdr_sz) ++ if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data) + delay_id delay_id; + #endif + ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ /*The folowing entry can not be marked as aborted. ++ * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; +@@ -579,6 +605,33 @@ httpReadReply(int fd, void *data) + else + delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); + #endif ++ ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ IcapStateData *icap = httpState->icap_writer; ++ /* ++ * Ok we have a received a response from the web server, so try to ++ * connect the icap server if it's the first attemps. If we try ++ * to connect to the icap server, defer this request (do not read ++ * the buffer), and defer until icapConnectOver() is not called. ++ */ ++ if (icap->flags.connect_requested == 0) { ++ debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); ++ if (!icapConnect(icap, icapConnectOver)) { ++ debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); ++ icap->flags.connect_requested = 1; ++ /* Wait for more data or EOF condition */ ++ commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ } ++#endif ++ + errno = 0; + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, read_sz); +@@ -595,7 +648,13 @@ httpReadReply(int fd, void *data) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +- if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ (void) 0; ++ else ++#endif ++ ++ if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { + /* Skip whitespace */ + while (len > 0 && xisspace(*buf)) + xmemmove(buf, buf + 1, len--); +@@ -625,6 +684,12 @@ httpReadReply(int fd, void *data) + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { ++ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); ++ icapSendRespMod(httpState->icap_writer, buf, len, 1); ++ } ++#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we +@@ -677,7 +742,28 @@ httpReadReply(int fd, void *data) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ if (cbdataValid(httpState->icap_writer)) { ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } ++ } else ++#endif + storeAppend(entry, buf, len); ++ ++ ++ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + /* + * the above storeAppend() call could ABORT this entry, +@@ -724,10 +810,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); +@@ -766,6 +863,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -776,6 +877,34 @@ httpReadReply(int fd, void *data) + } + } + ++#ifdef HS_FEAT_ICAP ++static int ++httpReadReplyWaitForIcap(int fd, void *data) ++{ ++ HttpStateData *httpState = data; ++ if (NULL == httpState->icap_writer) ++ return 0; ++ /* ++ * Do not defer when we are not connected to the icap server. ++ * Defer when the icap server connection is not established but pending ++ * Defer when the icap server is busy (writing on the socket) ++ */ ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", ++ fd, httpState->icap_writer->flags.connect_requested); ++ if (!httpState->icap_writer->flags.connect_requested) ++ return 0; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", ++ fd, httpState->icap_writer->flags.connect_pending); ++ if (httpState->icap_writer->flags.connect_pending) ++ return 1; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", ++ fd, httpState->icap_writer->flags.write_pending); ++ if (httpState->icap_writer->flags.write_pending) ++ return 1; ++ return 0; ++} ++#endif ++ + /* This will be called when request write is complete. Schedule read of + * reply. */ + static void +@@ -803,6 +932,63 @@ httpSendComplete(int fd, char *bufnotuse + comm_close(fd); + return; + } else { ++ /* Schedule read reply. */ ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart( ++ ICAP_SERVICE_RESPMOD_PRECACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (-1 == (int) httpState->icap_writer) { ++ /* TODO: send error here and exit */ ++ ErrorState *err; ++ httpState->icap_writer = 0; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(httpState->orig_request); ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ return; ++ } else if (httpState->icap_writer) { ++ request_flags fake_flags = httpState->orig_request->flags; ++ method_t fake_method = entry->mem_obj->method; ++ const char *fake_msg = "this is a fake entry for " ++ " response sent to an ICAP RESPMOD server"; ++ cbdataLock(httpState->icap_writer); ++ /* ++ * this httpState will give the data it reads to ++ * the icap server, rather than put it into ++ * a StoreEntry ++ */ ++ storeUnlockObject(httpState->entry); ++ storeUnregisterAbort(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -811,8 +997,18 @@ httpSendComplete(int fd, char *bufnotuse + * the timeout for POST/PUT requests that have very large + * request bodies. + */ ++ ++ /* removed in stable5: ++ * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ */ + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +- commSetDefer(fd, fwdCheckDeferRead, entry); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); ++ commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); ++ } else ++#endif ++ commSetDefer(httpState->fd, fwdCheckDeferRead, entry); + } + httpState->flags.request_sent = 1; + } +@@ -1010,8 +1206,11 @@ httpBuildRequestHeader(request_t * reque + if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { + const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); + httpHdrCcSetMaxAge(cc, getMaxAge(url)); ++#ifndef HS_FEAT_ICAP ++ /* Don;t bother - if the url you want to cache is redirected? */ + if (strLen(request->urlpath)) + assert(strstr(url, strBuf(request->urlpath))); ++#endif + } + /* Set no-cache if determined needed but not found */ + if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) +@@ -1119,6 +1318,7 @@ httpStart(FwdState * fwd) + int fd = fwd->server_fd; + HttpStateData *httpState; + request_t *proxy_req; ++ /* ErrorState *err; */ + request_t *orig_req = fwd->request; + debug(11, 3) ("httpStart: \"%s %s\"\n", + RequestMethodStr[orig_req->method], +@@ -1156,12 +1356,22 @@ httpStart(FwdState * fwd) + httpState->request = requestLink(orig_req); + httpState->orig_request = requestLink(orig_req); + } ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (httpState->icap_writer) { ++ return; ++ } ++ } ++#endif + /* + * register the handler to free HTTP state data when the FD closes + */ + comm_add_close_handler(fd, httpStateFree, httpState); + statCounter.server.all.requests++; + statCounter.server.http.requests++; ++ + httpSendRequest(httpState); + /* + * We used to set the read timeout here, but not any more. +Index: src/icap_common.c +=================================================================== +RCS file: src/icap_common.c +diff -N src/icap_common.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_common.c 22 Nov 2005 22:41:48 -0000 1.1.2.39 +@@ -0,0 +1,785 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} +Index: src/icap_opt.c +=================================================================== +RCS file: src/icap_opt.c +diff -N src/icap_opt.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_opt.c 22 Nov 2005 22:41:48 -0000 1.1.2.17 +@@ -0,0 +1,519 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: src/icap_reqmod.c +=================================================================== +RCS file: src/icap_reqmod.c +diff -N src/icap_reqmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_reqmod.c 6 Dec 2005 21:53:44 -0000 1.1.2.58 +@@ -0,0 +1,976 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++#define ICAP_PROXY_KEEP_ALIVE 0 ++ ++/* ++ * These once-static functions are required to be global for ICAP ++ */ ++ ++PF clientReadRequest; ++PF connStateFree; ++int clientReadDefer(int fd, void *data); ++int clientCheckContentLength(request_t * r); ++void clientProcessRequest(clientHttpRequest *); ++int clientCachable(clientHttpRequest *); ++int clientHierarchical(clientHttpRequest *); ++void clientReadBody(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++ ++static PF icapReqModReadHttpHdrs; ++static PF icapReqModReadHttpBody; ++static CWCB icapReqModSendBodyChunk; ++static CBCB icapReqModBodyHandler; ++static BODY_HANDLER icapReqModBodyReader; ++static STRCB icapReqModMemBufAppend; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++static const char *crlf = "\r\n"; ++ ++/* ++ * icapExpectedHttpReqHdrSize ++ * ++ * calculate the size of the HTTP headers that we expect ++ * to read from the ICAP server. ++ */ ++static int ++icapExpectedHttpReqHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) ++ return (icap->enc.req_body - icap->enc.req_hdr); ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ fatal("icapExpectedHttpReqHdrSize: unexpected case"); ++ return 0; ++} ++ ++/* ++ * icapReqModCreateClientState ++ * ++ * Creates fake client_side data structures so we can use ++ * that module to read/parse the HTTP request that we read ++ * from the ICAP server. ++ */ ++static clientHttpRequest * ++icapReqModCreateClientState(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http; ++ if (!cbdataValid(icap->reqmod.client_cookie)) { ++ debug(81, 3) ("Whups, client cookie invalid\n"); ++ icap->reqmod.client_fd = -1; ++ return NULL; ++ } ++ http = cbdataAlloc(clientHttpRequest); ++ /* ++ * use our own urlCanonicalClean here, because urlCanonicalClean ++ * may strip everything after a question-mark. As http->uri ++ * is used when doing a request to a parent proxy, we need the full ++ * url here. ++ */ ++ http->uri = xstrdup(urlCanonical(icap->request)); ++ http->log_uri = xstrndup(http->uri, MAX_URL); ++ http->range_iter.boundary = StringNull; ++ http->request = requestLink(request ? request : icap->request); ++ http->flags.did_icap_reqmod = 1; ++ http->start = icap->reqmod.start; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ ++ http->conn->chr = http; ++ ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ err->request = requestLink(request); ++ request->flags.proxy_keepalive = 0; ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ if (!clientCheckContentLength(request)) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED); ++ err->request = requestLink(request); ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ /* Do we expect a request-body? */ ++ if (request->content_length > 0) { ++ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); ++ if (request->body_reader_data) ++ cbdataUnlock(request->body_reader_data); ++ request->body_reader = icapReqModBodyReader; ++ request->body_reader_data = icap; /* XXX cbdataLock? */ ++ cbdataLock(icap); /*Yes sure ..... */ ++ memBufDefInit(&icap->reqmod.http_entity.buf); ++ } ++ if (clientCachable(http)) ++ request->flags.cachable = 1; ++ if (clientHierarchical(http)) ++ request->flags.hierarchical = 1; ++ clientProcessRequest(http); ++} ++ ++/* ++ * icapReqModParseHttpError ++ * ++ * Handle an error when parsing the new HTTP request we read ++ * from the ICAP server. ++ */ ++static void ++icapReqModParseHttpError(IcapStateData * icap, const char *reason) ++{ ++ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); ++} ++ ++/* ++ * icapEntryError ++ * ++ * A wrapper for errorCon() and errorAppendEntry(). ++ */ ++static void ++icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) ++{ ++ ErrorState *err; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, null_request_flags); ++ err = errorCon(et, hs); ++ err->xerrno = xerrno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(http->entry, err); ++} ++ ++/* ++ * icapReqModParseHttpRequest ++ * ++ * Parse the HTTP request that we read from the ICAP server. ++ * Creates and fills in the request_t structure. ++ */ ++static void ++icapReqModParseHttpRequest(IcapStateData * icap) ++{ ++ char *mstr; ++ char *uri; ++ char *inbuf; ++ char *t; ++ char *token; ++ char *headers; ++ method_t method; ++ request_t *request; ++ http_version_t http_ver; ++ int reqlen = icap->reqmod.hdr_buf.size; ++ int hdrlen; ++ ++ /* ++ * Lazy, make a copy of the buf so I can chop it up with strtok() ++ */ ++ inbuf = xcalloc(reqlen + 1, 1); ++ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); ++ ++ if ((mstr = strtok(inbuf, "\t ")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); ++ icapReqModParseHttpError(icap, "error:invalid-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ method = urlParseMethod(mstr); ++ if (method == METHOD_NONE) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", ++ mstr); ++ icapReqModParseHttpError(icap, "error:unsupported-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ /* look for URL+HTTP/x.x */ ++ if ((uri = strtok(NULL, "\n")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); ++ icapReqModParseHttpError(icap, "error:missing-url"); ++ xfree(inbuf); ++ return; ++ } ++ while (xisspace(*uri)) ++ uri++; ++ t = uri + strlen(uri); ++ assert(*t == '\0'); ++ token = NULL; ++ while (t > uri) { ++ t--; ++ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { ++ token = t + 1; ++ break; ++ } ++ } ++ while (t > uri && xisspace(*t)) ++ *(t--) = '\0'; ++ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); ++ if (token == NULL) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); ++ icapReqModParseHttpError(icap, "error:missing-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); ++ icapReqModParseHttpError(icap, "error:invalid-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", ++ http_ver.major, http_ver.minor); ++ ++ headers = strtok(NULL, null_string); ++ hdrlen = inbuf + reqlen - headers; ++ ++ if ((request = urlParse(method, uri)) == NULL) { ++ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ /* compile headers */ ++ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { ++ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", ++ uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, ++ 3) ++ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); ++ request->http_ver = http_ver; ++ request->client_addr = icap->request->client_addr; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request != NULL) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service *service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->reqmod.http_entity.bytes_read >= icap->request->content_length) ++ icap->flags.reqmod_http_entity_eof = 1; ++ ++ if (!icap->flags.reqmod_http_entity_eof) ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ /* ++ * Notify the other side if it is waiting for data from us ++ */ ++ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.callback); ++ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.callback && icap->reqmod.http_entity.buf.size) { ++ icapReqModPassHttpBody(icap, ++ icap->reqmod.http_entity.callback_buf, ++ icap->reqmod.http_entity.callback_bufsize, ++ icap->reqmod.http_entity.callback, ++ icap->reqmod.http_entity.callback_data); ++ icap->reqmod.http_entity.callback = NULL; ++ cbdataUnlock(icap->reqmod.http_entity.callback_data); ++ ++ } ++} ++ ++/* ++ * icapReqModPassHttpBody ++ * ++ * Called from http.c after request headers have been sent. ++ * This function feeds the http.c module chunks of the request ++ * body that were stored in the http_entity.buf MemBuf. ++ */ ++static void ++icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ debug(81, 3) ("icapReqModPassHttpBody: called\n"); ++ if (!buf) { ++ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", ++ icap->icap_fd, buf, (int) size, cbdata); ++ comm_close(icap->icap_fd); ++ return; ++ } ++ if (!cbdataValid(cbdata)) { ++ debug(81, ++ 1) ++ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", ++ icap->icap_fd); ++ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ ++ /*icapReqModKeepAliveOrClose(icap); */ ++ return; ++ } ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.buf.size) { ++ int copy_sz = icap->reqmod.http_entity.buf.size; ++ if (copy_sz > size) ++ copy_sz = size; ++ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); ++ /* XXX don't let Alex see this ugliness */ ++ xmemmove(icap->reqmod.http_entity.buf.buf, ++ icap->reqmod.http_entity.buf.buf + copy_sz, ++ icap->reqmod.http_entity.buf.size - copy_sz); ++ icap->reqmod.http_entity.buf.size -= copy_sz; ++ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", ++ copy_sz); ++ callback(buf, copy_sz, cbdata); ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ return; ++ } ++ if (icap->flags.reqmod_http_entity_eof) { ++ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); ++ callback(buf, 0, cbdata); ++ icapReqModKeepAliveOrClose(icap); ++ return; ++ } ++ /* ++ * We have no data for the other side at this point. Save all ++ * these values and use them when we do have data. ++ */ ++ assert(NULL == icap->reqmod.http_entity.callback); ++ icap->reqmod.http_entity.callback = callback; ++ icap->reqmod.http_entity.callback_data = cbdata; ++ icap->reqmod.http_entity.callback_buf = buf; ++ icap->reqmod.http_entity.callback_bufsize = size; ++ cbdataLock(icap->reqmod.http_entity.callback_data); ++} ++ ++/* ++ * Body reader handler for use with request->body_reader function ++ * Simple a wrapper for icapReqModPassHttpBody function ++ */ ++ ++static void ++icapReqModBodyReader(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ IcapStateData *icap = request->body_reader_data; ++ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); ++} ++ ++/* ++ * icapReqModMemBufAppend ++ * ++ * stupid wrapper to eliminate compiler warnings ++ */ ++static void ++icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) ++{ ++ memBufAppend(data, buf, size); ++} +Index: src/icap_respmod.c +=================================================================== +RCS file: src/icap_respmod.c +diff -N src/icap_respmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_respmod.c 23 Nov 2005 20:34:34 -0000 1.1.2.60 +@@ -0,0 +1,1039 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ ++ memBufPrintf(mb, crlf); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++ if (service->flags.allow_204) ++ memBufPrintf(mb, "Allow: 204\r\n"); ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ } ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ /* No data sent yet. Start with headers */ ++ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { ++ buf += icap->sc; ++ len -= icap->sc; ++ } ++ /* ++ * Then we do not have http responce headers. All data (previous and those in buf) ++ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... ++ */ ++ if (icap->sc < 0) { ++ memBufAppend(&icap->respmod.buffer, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->sc = icap->respmod.req_hdr_copy.size; ++ icap->respmod.req_hdr_copy.size = 0; ++ buf = NULL; ++ len = 0; ++ } ++ } ++ if (0 == icap->sc) { ++ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ ++ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); ++ memBufClean(&mb); ++ return; ++ } ++#if ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ ++ icap->flags.preview_done = 1; ++ ++ if (!icap->flags.preview_done) { ++ /* preview not yet sent */ ++ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size ++ && len > 0) { ++ /* Try to collect at least preview_size+1 bytes */ ++ /* By collecting one more byte than needed for preview we know best */ ++ /* whether we have to send the ieof chunk extension */ ++ size = icap->respmod.buffer.size + len; ++ if (size > preview_size + 1) ++ size = preview_size + 1; ++ size -= icap->respmod.buffer.size; ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", ++ icap->icap_fd, size); ++ memBufAppend(&icap->respmod.buffer, buf, size); ++ buf = ((char *) buf) + size; ++ len -= size; ++ } ++ if (icap->respmod.buffer.size > preview_size || theEnd) { ++ /* we got enough bytes for preview or this is the last call */ ++ /* add preview preview now */ ++ if (icap->respmod.buffer.size > 0) { ++ size = icap->respmod.buffer.size; ++ if (size > preview_size) ++ size = preview_size; ++ memBufPrintf(&mb, "%x\r\n", size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += size; ++ } ++ if (icap->respmod.buffer.size <= preview_size) { ++ /* content length is less than preview size+1 */ ++ if (icap->respmod.res_body_sz) ++ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ } else { ++ char ch; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ /* end of preview, wait for continue or 204 signal */ ++ /* copy the extra byte and all other data to the icap buffer */ ++ /* so that it can be handled next time */ ++ ch = icap->respmod.buffer.buf[preview_size]; ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ memBufAppend(&icap->respmod.buffer, &ch, 1); ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", ++ icap->icap_fd, len + 1); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ } ++ icap->flags.preview_done = 1; ++ icap->flags.wait_for_preview_reply = 1; ++ } ++ } else if (icap->flags.wait_for_preview_reply) { ++ /* received new data while waiting for preview response */ ++ /* add data to internal buffer and send later */ ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", ++ icap->icap_fd, len); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ /* do not send any data now while waiting for preview response */ ++ /* but prepare for read more data on the HTTP connection */ ++ memBufClean(&mb); ++ return; ++ } else ++#endif ++ { ++ /* after preview completed and ICAP preview response received */ ++ /* there may still be some data in the buffer */ ++ if (icap->respmod.buffer.size > 0) { ++ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, ++ icap->respmod.buffer.size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += icap->respmod.buffer.size; ++ memBufReset(&icap->respmod.buffer); ++ } ++ if (len > 0) { ++ memBufPrintf(&mb, "%x\r\n", len); ++ memBufAppend(&mb, buf, len); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += len; ++ } ++ if (icap->flags.send_zero_chunk) { ++ /* send zero end chunk */ ++ icap->flags.send_zero_chunk = 0; ++ icap->flags.http_server_eof = 1; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ } ++ /* wait for data coming from ICAP server as soon as we sent something */ ++ /* but of course only until we got the response header */ ++ if (!icap->flags.got_reply) ++ icap->flags.wait_for_reply = 1; ++ } ++ commSetTimeout(icap->icap_fd, -1, NULL, NULL); ++ ++ if (!mb.size) { ++ memBufClean(&mb); ++ return; ++ } ++ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ icap->flags.write_pending = 1; ++ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); ++} ++ ++static void ++icapRespModReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ int status = 0; ++ int isIcap = 0; ++ int directResponse = 0; ++ ErrorState *err; ++ const char *start; ++ const char *end; ++ ++ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ }; ++ /* OK here we have responce. Lets stop filling the ++ * icap->respmod.resp_copy buffer .... ++ */ ++ icap->flags.copy_response = 0; ++ ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++#if ICAP_PREVIEW ++ if (icap->flags.wait_for_preview_reply) { ++ if (100 == status) { ++ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ /* if http_server_eof ++ * call again icapSendRespMod to handle data that ++ * was received while waiting for this ICAP response ++ * else let http to call icapSendRespMod when new data arrived ++ */ ++ if (icap->flags.http_server_eof) ++ icapSendRespMod(icap, NULL, 0, 0); ++ /* ++ * reset the header to send the rest of the preview ++ */ ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufReset(&icap->icap_hdr); ++ ++ /*We do n't need it any more ....... */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ ++ return; ++ } ++ if (204 == status) { ++ debug(81, ++ 5) ("icapRespModReadReply: 204 No modification received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ } ++ } ++#endif /*ICAP_PREVIEW */ ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ if (204 == status) { ++ debug(81, 3) ("got 204 status from ICAP server\n"); ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ ++ /* ++ * "directResponse" is the normal case here. If we don't have ++ * a response header or body, it is an error. ++ */ ++ if (!directResponse) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ /* Next, gobble any data before the HTTP response starts */ ++ if (icap->enc.res_hdr > -1) ++ icap->bytes_to_gobble = icap->enc.res_hdr; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++} ++ ++ ++/* ++ * Gobble up (read) some bytes until we get to the start of the body ++ */ ++static void ++icapRespModGobble(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); ++ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, ++ icap->bytes_to_gobble); ++ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); ++ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); ++ if (len < 0) { ++ /* XXX error */ ++ abort(); ++ } ++ icap->bytes_to_gobble -= len; ++ if (icap->bytes_to_gobble) ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++ else ++ icapReadReply(fd, icap); ++} ++ ++ ++static void ++icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ErrorState *err; ++ ++ icap->flags.write_pending = 0; ++ debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ if (cbdataValid(icap)) ++ err->request = requestLink(icap->request); ++ storeEntryReset(icap->respmod.entry); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); ++ comm_close(fd); ++ return; ++ } ++ if (icap->flags.send_zero_chunk) { ++ debug(81, ++ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); ++ icap->flags.send_zero_chunk = 0; ++ icapSendRespMod(icap, NULL, 0, 1); ++ return; ++ } ++ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { ++ /* Schedule reading the ICAP response */ ++ debug(81, ++ 3) ++ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", ++ fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++#if 1 ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++#else ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ /* ++ * Set the read timeout only after all data has been sent ++ * or we are waiting for a preview response ++ * If the ICAP server does not return any data till all data ++ * has been sent, we are likely to hit the timeout for large ++ * HTTP bodies ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ } ++#endif ++ } ++} ++ ++void ++icapConnectOver(int fd, int status, void *data) ++{ ++ ErrorState *err; ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ if (status < 0) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); ++ icapOptSetUnreachable(icap->current_service); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++} ++ ++ ++ ++IcapStateData * ++icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, ++ http_state_flags http_flags) ++{ ++ IcapStateData *icap = NULL; ++ CNCB *theCallback = NULL; ++ icap_service *service = NULL; ++ ++ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); ++ assert(type >= 0 && type < ICAP_SERVICE_MAX); ++ ++ service = icapService(type, request); ++ if (!service) { ++ debug(81, 3) ("icapRespModStart: no service found\n"); ++ return NULL; /* no service found */ ++ } ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ++ ("icapRespModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ++ ("icapRespModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ switch (type) { ++ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change ++ * this switch, because callbacks isn't keep */ ++ case ICAP_SERVICE_RESPMOD_PRECACHE: ++ theCallback = icapConnectOver; ++ break; ++ default: ++ fatalf("icapRespModStart: unsupported service type '%s'\n", ++ icap_service_type_str[type]); ++ break; ++ } ++ ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->request = requestLink(request); ++ icap->respmod.entry = entry; ++ if (entry) ++ storeLockObject(entry); ++ icap->http_flags = http_flags; ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ ++ /* ++ * Don't create socket to the icap server now, but only for the first ++ * packet receive from the http server. This will resolve all timeout ++ * between the web server and icap server. ++ */ ++ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); ++ icap->flags.connect_requested = 0; ++ ++ /* ++ * make a copy the HTTP response that we send to the ICAP server in ++ * case it turns out to be a 204 ++ */ ++#ifdef SUPPORT_ICAP_204 ++ icap->flags.copy_response = 1; ++#elif ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) ++ icap->flags.copy_response = 0; ++ else ++ icap->flags.copy_response = 1; ++#else ++ icap->flags.copy_response = 0; ++#endif ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapRespModStart: returning %p\n", icap); ++ return icap; ++} ++ ++static int ++icapHttpReplyHdrState(IcapStateData * icap) ++{ ++ assert(icap); ++ if (NULL == icap->httpState) ++ return 0; ++ return icap->httpState->reply_hdr_state; ++} ++ ++static void ++icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) ++{ ++ if (NULL == icap->httpState) { ++ icap->httpState = cbdataAlloc(HttpStateData); ++ icap->httpState->request = requestLink(icap->request); ++ icap->httpState->orig_request = requestLink(icap->request); ++ icap->httpState->entry = icap->respmod.entry; ++ storeLockObject(icap->httpState->entry); /* lock it */ ++ } ++ httpProcessReplyHeader(icap->httpState, buf, size); ++ if (2 == icap->httpState->reply_hdr_state) ++ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); ++} ++ ++/* ++ * icapRespModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapRespModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink((request_t *) request); ++ err->xerrno = errno; ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ } else { ++ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", ++ fd); ++ comm_close(fd); ++ } ++ return; ++ } ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++} ++ ++static int ++icapReadReply2(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ debug(81, 3) ("icapReadReply2\n"); ++ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); ++ err->xerrno = errno; ++ err->request = requestLink((request_t *) request); ++ errorAppendEntry(entry, err); ++ icap->flags.http_server_eof = 1; ++ return -1; ++ } ++ if (icap->chunk_buf.size == 0) { ++ /* Retrieval done. */ ++ if (icapHttpReplyHdrState(icap) < 2) ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ icap->flags.http_server_eof = 1; ++ icapReadReply3(icap); ++ return 0; ++ } ++ if (icapHttpReplyHdrState(icap) == 0) { ++ int expect = icapExpectedHttpReplyHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed < 0 || needed >= 0); ++ if (0 > expect) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ } else if (0 == expect) { ++ /* ++ * this icap reply doesn't give us new HTTP headers ++ * so we must copy them from our copy ++ */ ++ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, ++ __LINE__); ++ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ ++ storeAppend(entry, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ } ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ ++ } else if (needed) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ if (icap->chunk_buf.size >= needed) { ++ storeAppend(entry, icap->chunk_buf.buf, needed); ++ so_far += needed; ++ xmemmove(icap->chunk_buf.buf, ++ icap->chunk_buf.buf + needed, ++ icap->chunk_buf.size - needed); ++ icap->chunk_buf.size -= needed; ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; ++ } else { ++ /* ++ * We don't have the full HTTP reply headers yet, so keep ++ * the partial reply buffered in 'chunk_buf' and wait ++ * for more. ++ */ ++ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); ++ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } ++ } ++ icap->http_header_bytes_read_so_far = so_far; ++ } ++ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, ++ (int) icap->chunk_buf.size); ++ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, ++ icap->flags.no_content); ++ if (icap->flags.no_content) { ++ /* data from http.c is not chunked */ ++ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", ++ icap->chunk_buf.size); ++ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); ++ icap->chunk_buf.size = 0; ++ } ++ } else if (2 == icapHttpReplyHdrState(icap)) { ++ if (icap->chunk_buf.size) ++ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); ++ } ++ icapReadReply3(icap); ++ return 0; ++} ++ ++static void ++icapReadReply3(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ int fd = icap->icap_fd; ++ debug(81, 3) ("icapReadReply3\n"); ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ icapRespModKeepAliveOrClose(icap); ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.28.6.25 +retrieving revision 1.28.6.8.2.11 +diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11 +--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25 ++++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11 +@@ -350,6 +350,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + authenticateShutdown(); + externalAclShutdown(); +@@ -378,6 +381,9 @@ mainReconfigure(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -507,6 +513,9 @@ mainInitialize(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); +Index: src/mem.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/mem.c,v +retrieving revision 1.13 +retrieving revision 1.13.28.2 +diff -p -u -b -r1.13 -r1.13.28.2 +--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13 ++++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2 +@@ -243,6 +243,13 @@ memInit(void) + memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.140.1 +diff -p -u -b -r1.2 -r1.2.140.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1 +@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str"; + $pat{'icp_opcode'} = "icp_opcode_str"; + $pat{'swap_log_op'} = "swap_log_op_str"; + $pat{'lookup_t'} = "lookup_t_str"; ++$pat{'icap_service_t'} = "icap_service_type_str"; + + $state = 0; # start state + while (<>) { +Index: src/pconn.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/pconn.c,v +retrieving revision 1.6.38.2 +retrieving revision 1.6.60.2 +diff -p -u -b -r1.6.38.2 -r1.6.60.2 +--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2 ++++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2 +@@ -46,6 +46,9 @@ struct _pconn { + #define PCONN_HIST_SZ (1<<16) + int client_pconn_hist[PCONN_HIST_SZ]; + int server_pconn_hist[PCONN_HIST_SZ]; ++#ifdef HS_FEAT_ICAP ++int icap_server_pconn_hist[PCONN_HIST_SZ]; ++#endif + + static PF pconnRead; + static PF pconnTimeout; +@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); + } ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(e, ++ "\n" ++ "ICAP-server persistent connection counts:\n" ++ "\n" ++ "\treq/\n" ++ "\tconn count\n" ++ "\t---- ---------\n"); ++ for (i = 0; i < PCONN_HIST_SZ; i++) { ++ if (icap_server_pconn_hist[i] == 0) ++ continue; ++ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); ++ } ++#endif + } + + /* ========== PUBLIC FUNCTIONS ============================================ */ +@@ -173,6 +190,9 @@ pconnInit(void) + for (i = 0; i < PCONN_HIST_SZ; i++) { + client_pconn_hist[i] = 0; + server_pconn_hist[i] = 0; ++#ifdef HS_FEAT_ICAP ++ icap_server_pconn_hist[i] = 0; ++#endif + } + pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); + pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); +@@ -248,11 +268,15 @@ pconnHistCount(int what, int i) + { + if (i >= PCONN_HIST_SZ) + i = PCONN_HIST_SZ - 1; +- /* what == 0 for client, 1 for server */ ++ /* what == 0 for client, 1 for server, 2 for ICAP server */ + if (what == 0) + client_pconn_hist[i]++; + else if (what == 1) + server_pconn_hist[i]++; ++#ifdef HS_FEAT_ICAP ++ else if (what == 2) ++ icap_server_pconn_hist[i]++; ++#endif + else + assert(0); + } +Index: src/protos.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/protos.h,v +retrieving revision 1.41.6.33 +retrieving revision 1.41.6.13.2.37 +diff -p -u -b -r1.41.6.33 -r1.41.6.13.2.37 +--- src/protos.h 16 Sep 2005 02:13:25 -0000 1.41.6.33 ++++ src/protos.h 6 Dec 2005 21:53:44 -0000 1.41.6.13.2.37 +@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *); + /* http.c */ + extern int httpCachable(method_t); + extern void httpStart(FwdState *); ++extern void httpParseReplyHeaders(const char *, http_reply *); ++extern void httpProcessReplyHeader(HttpStateData *, const char *, int); + extern int httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, +@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c + extern FREE *memBufFreeFunc(MemBuf * mb); + /* puts report on MemBuf _module_ usage into mb */ + extern void memBufReport(MemBuf * mb); ++extern int memBufRead(int fd, MemBuf * mb); + + extern char *mime_get_header(const char *mime, const char *header); + extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); +@@ -1341,4 +1344,49 @@ extern void externalAclShutdown(void); + extern int externalAclRequiresAuth(void *acl_data); + extern char *strtokFile(void); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.13.6.8 +retrieving revision 1.13.6.6.2.11 +diff -p -u -b -r1.13.6.8 -r1.13.6.6.2.11 +--- src/squid.h 26 Mar 2005 03:15:58 -0000 1.13.6.8 ++++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11 +@@ -38,6 +38,14 @@ + #include "config.h" + + /* ++ * experimental defines for ICAP ++ */ ++#ifdef HS_FEAT_ICAP ++#define ICAP_PREVIEW 1 ++#define SUPPORT_ICAP_204 0 ++#endif ++ ++/* + * On some systems, FD_SETSIZE is set to something lower than the + * actual number of files which can be opened. IRIX is one case, + * NetBSD is another. So here we increase FD_SETSIZE to our +Index: src/stat.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/stat.c,v +retrieving revision 1.13.6.14 +retrieving revision 1.13.6.7.2.7 +diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7 +--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14 ++++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7 +@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min + storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", + XAVG(server.other.kbytes_out.kb)); + ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", ++ XAVG(icap.all.requests)); ++ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", ++ XAVG(icap.all.errors)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", ++ XAVG(icap.all.kbytes_in.kb)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", ++ XAVG(icap.all.kbytes_out.kb)); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", + XAVG(icp.pkts_sent)); + storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", +@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry) + storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", + (int) f->server.other.kbytes_out.kb); + ++#if HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %d\n", ++ (int) f->icap.all.requests); ++ storeAppendPrintf(sentry, "icap.all.errors = %d\n", ++ (int) f->icap.all.errors); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", ++ (int) f->icap.all.kbytes_in.kb); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", ++ (int) f->icap.all.kbytes_out.kb); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", + f->icp.pkts_sent); + storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", +@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s) + storeAppendPrintf(s, "\tme: %s:%d\n", + inet_ntoa(conn->me.sin_addr), + ntohs(conn->me.sin_port)); +- storeAppendPrintf(s, "\tnrequests: %d\n", +- conn->nrequests); + storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", + conn->defer.n, (long int) conn->defer.until); + } +Index: src/store.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/store.c,v +retrieving revision 1.16.6.9 +retrieving revision 1.16.6.2.2.8 +diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8 +--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9 ++++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8 +@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.48.2.43 +retrieving revision 1.48.2.9.2.48 +diff -p -u -b -r1.48.2.43 -r1.48.2.9.2.48 +--- src/structs.h 4 Sep 2005 02:13:28 -0000 1.48.2.43 ++++ src/structs.h 30 Nov 2005 21:52:15 -0000 1.48.2.9.2.48 +@@ -384,6 +384,22 @@ struct _RemovalPolicySettings { + wordlist *args; + }; + ++#if HS_FEAT_ICAP ++struct _IcapConfig { ++ int onoff; ++ int preview_enable; ++ icap_service *service_head; ++ icap_class *class_head; ++ icap_access *access_head; ++ int preview_size; ++ int check_interval; ++ int send_client_ip; ++ int send_auth_user; ++ char *auth_scheme; ++}; ++ ++#endif /* HS_FEAT_ICAP */ ++ + struct _SquidConfig { + struct { + squid_off_t maxSize; +@@ -714,6 +730,9 @@ struct _SquidConfig { + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + external_acl *externalAclHelperList; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -787,7 +806,10 @@ struct _fde { + } flags; + squid_off_t bytes_read; + squid_off_t bytes_written; +- int uses; /* ie # req's over persistent conn */ ++ struct { ++ int uses; ++ int type; ++ } pconn; + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; +@@ -982,6 +1004,130 @@ struct _http_state_flags { + unsigned int request_sent:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -993,10 +1139,14 @@ struct _HttpStateData { + int fd; + http_state_flags flags; + FwdState *fwd; ++#ifdef HS_FEAT_ICAP ++ struct _IcapStateData *icap_writer; ++#endif + char *body_buf; + int body_buf_sz; + }; + ++ + struct _icpUdpData { + struct sockaddr_in address; + void *msg; +@@ -1092,6 +1242,7 @@ struct _clientHttpRequest { + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; ++ unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { +@@ -1100,6 +1251,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1127,7 +1281,6 @@ struct _ConnStateData { + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; +- int nrequests; + struct { + int n; + time_t until; +@@ -1678,6 +1831,9 @@ struct _request_t { + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + }; +@@ -1784,7 +1940,11 @@ struct _StatCounters { + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; +- } server; ++ } ++#if HS_FEAT_ICAP ++ icap, ++#endif ++ server; + struct { + int pkts_sent; + int queries_sent; +Index: src/typedefs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/typedefs.h,v +retrieving revision 1.25.6.8 +retrieving revision 1.25.6.1.6.13 +diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13 +--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8 ++++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13 +@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade + typedef struct _HttpBody HttpBody; + typedef struct _HttpReply HttpReply; + typedef struct _HttpStateData HttpStateData; ++#ifdef HS_FEAT_ICAP ++typedef struct _IcapStateData IcapStateData; ++typedef struct _IcapConfig IcapConfig; ++typedef struct _icap_service icap_service; ++typedef struct _icap_service_list icap_service_list; ++typedef struct _icap_class icap_class; ++typedef struct _icap_access icap_access; ++typedef struct _IcapOptData IcapOptData; ++#endif + typedef struct _icpUdpData icpUdpData; + typedef struct _clientHttpRequest clientHttpRequest; + typedef struct _ConnStateData ConnStateData; +Index: src/url.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/url.c,v +retrieving revision 1.7.6.6 +retrieving revision 1.7.6.5.2.2 +diff -p -u -b -r1.7.6.6 -r1.7.6.5.2.2 +--- src/url.c 12 Nov 2005 03:13:48 -0000 1.7.6.6 ++++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -221,6 +224,10 @@ urlParseProtocol(const char *s) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; ++#ifdef HS_FEAT_ICAP ++ if (strcasecmp(s, "icap") == 0) ++ return PROTO_ICAP; ++#endif + return PROTO_NONE; + } + +@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p) + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; ++#ifdef HS_FEAT_ICAP ++ case PROTO_ICAP: ++ return 1344; ++#endif + default: + return 0; + } diff --git a/www/squid26/Makefile b/www/squid26/Makefile index 31916d4cb34e..1f75bf5d90f8 100644 --- a/www/squid26/Makefile +++ b/www/squid26/Makefile @@ -66,10 +66,14 @@ # Override the maximum number of filedescriptors. Useful if you # build as another user who is not privileged to use the amount # of filedescriptors the resulting binary is expected to support. +# --enable-ntlm-fail-open +# Enable NTLM fail open, where a helper that fails one of the +# Authentication steps can allow squid to still authenticate the user # PORTNAME= squid PORTVERSION= 2.5.12 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= \ ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ @@ -82,6 +86,7 @@ DISTNAME= squid-2.5.STABLE12 DIST_SUBDIR= squid2.5 PATCH_SITES= http://www.squid-cache.org/Versions/v2/2.5/bugs/ +PATCHFILES= squid-2.5.STABLE12-SMB_BadFetch.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -120,6 +125,7 @@ OPTIONS= SQUID_LDAP_AUTH "Install LDAP authentication helpers" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ + SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ SQUID_COSS "Enable the COSS storage scheme" off \ SQUID_LARGEFILE "Support log and cache files >2GB" off \ @@ -293,6 +299,12 @@ EXTRA_PATCHES+= ${PATCHDIR}/follow_xff-2.5.patch \ ${PATCHDIR}/follow_xff-configure.patch CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif +.if defined(WITH_SQUID_ICAP) +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.5-core.patch \ + ${PATCHDIR}/icap-2.5-bootstrap.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE +.endif .if defined(WITH_SQUID_LARGEFILE) CONFIGURE_ARGS+= --with-large-files --enable-large-cache-files .endif diff --git a/www/squid26/distinfo b/www/squid26/distinfo index 5d1b5428ba58..3e55ac8d1717 100644 --- a/www/squid26/distinfo +++ b/www/squid26/distinfo @@ -1,2 +1,6 @@ MD5 (squid2.5/squid-2.5.STABLE12.tar.bz2) = 7354255015b3772a1e024dfac173e48c +SHA256 (squid2.5/squid-2.5.STABLE12.tar.bz2) = ba0ccd956323f0dad46c19aa8d40c537846fedfc3778b5730e5610f16c0d9af1 SIZE (squid2.5/squid-2.5.STABLE12.tar.bz2) = 1075111 +MD5 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 8e83b776c0d015bd4137cc1ca08f6d38 +SHA256 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 9ca8427c2eb9e5cbdb5a49fb5cb94fc00853ad965f87666f8fc35236e98bc0ae +SIZE (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 826 diff --git a/www/squid26/files/icap-2.5-bootstrap.patch b/www/squid26/files/icap-2.5-bootstrap.patch new file mode 100644 index 000000000000..247ca0c94cbc --- /dev/null +++ b/www/squid26/files/icap-2.5-bootstrap.patch @@ -0,0 +1,422 @@ +Patch 2 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.5-core.patch for further information. + +Patch last updated: 2005-12-17 + +--- configure.orig Sat Oct 22 11:56:01 2005 ++++ configure Sat Dec 17 17:45:21 2005 +@@ -70,6 +70,8 @@ + ac_help="$ac_help + --enable-delay-pools Enable delay pools to limit bandwidth usage" + ac_help="$ac_help ++ --enable-icap-support Enable iCAP client capability" ++ac_help="$ac_help + --enable-useragent-log Enable logging of User-Agent header" + ac_help="$ac_help + --enable-referer-log Enable logging of Referer header" +@@ -2170,6 +2172,38 @@ + + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ cat >> confdefs.h <<\EOF ++#define HS_FEAT_ICAP 1 ++EOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ fi ++ ++fi ++ ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -7428,14 +7462,14 @@ + fi + ;; + esac +- echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 +-echo "configure:7433: checking for main in -lpthread" >&5 ++ echo $ac_n "checking for main in -pthread""... $ac_c" 1>&6 ++echo "configure:7433: checking for main in -pthread" >&5 + ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 + else + ac_save_LIBS="$LIBS" +-LIBS="-lpthread $LIBS" ++LIBS="-pthread $LIBS" + cat > conftest.$ac_ext <<EOF + #line 7441 "configure" + #include "confdefs.h" +@@ -7465,7 +7499,7 @@ + #define $ac_tr_lib 1 + EOF + +- LIBS="-lpthread $LIBS" ++ LIBS="-pthread $LIBS" + + else + echo "$ac_t""no" 1>&6 +@@ -7769,6 +7803,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strtoll \ + sysconf \ + syslog \ +@@ -7898,6 +7934,50 @@ + fi + fi + ++ ++if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++fi ++ ++ ++ ++ + echo $ac_n "checking if va_copy is implemented""... $ac_c" 1>&6 + echo "configure:7903: checking if va_copy is implemented" >&5 + if eval "test \"`echo '$''{'ac_cv_func_va_copy'+set}'`\" = set"; then +@@ -9072,6 +9152,8 @@ + s%@ENABLE_PINGER_FALSE@%$ENABLE_PINGER_FALSE%g + s%@USE_DELAY_POOLS_TRUE@%$USE_DELAY_POOLS_TRUE%g + s%@USE_DELAY_POOLS_FALSE@%$USE_DELAY_POOLS_FALSE%g ++s%@USE_ICAP_TRUE@%$USE_ICAP_TRUE%g ++s%@USE_ICAP_FALSE@%$USE_ICAP_FALSE%g + s%@USE_SNMP_TRUE@%$USE_SNMP_TRUE%g + s%@USE_SNMP_FALSE@%$USE_SNMP_FALSE%g + s%@SNMPLIB@%$SNMPLIB%g +@@ -9118,6 +9200,10 @@ + s%@LIB_LBER@%$LIB_LBER%g + s%@NEED_OWN_SNPRINTF_TRUE@%$NEED_OWN_SNPRINTF_TRUE%g + s%@NEED_OWN_SNPRINTF_FALSE@%$NEED_OWN_SNPRINTF_FALSE%g ++s%@NEED_OWN_STRNSTR_TRUE@%$NEED_OWN_STRNSTR_TRUE%g ++s%@NEED_OWN_STRNSTR_FALSE@%$NEED_OWN_STRNSTR_FALSE%g ++s%@NEED_OWN_STRCASESTR_TRUE@%$NEED_OWN_STRCASESTR_TRUE%g ++s%@NEED_OWN_STRCASESTR_FALSE@%$NEED_OWN_STRCASESTR_FALSE%g + s%@REGEXLIB@%$REGEXLIB%g + s%@LIBREGEX@%$LIBREGEX%g + s%@LIBOBJS@%$LIBOBJS%g +--- include/autoconf.h.in.orig Tue Sep 13 02:12:34 2005 ++++ include/autoconf.h.in Sat Dec 17 17:45:21 2005 +@@ -124,6 +124,11 @@ + */ + #undef DELAY_POOLS + ++/* ++ * ICAP - Internet Content Adaptation Protocol ++ */ ++#undef HS_FEAT_ICAP ++ + /* + * If you want to log User-Agent request header values, define this. + * By default, they are written to useragent.log in the Squid log +@@ -574,6 +579,12 @@ + + /* Define if you have the statfs function. */ + #undef HAVE_STATFS ++ ++/* Define if you have the strcasestr function. */ ++#undef HAVE_STRCASESTR ++ ++/* Define if you have the strnstr function. */ ++#undef HAVE_STRNSTR + + /* Define if you have the strerror function. */ + #undef HAVE_STRERROR +--- lib/Makefile.in.orig Wed Sep 28 22:57:20 2005 ++++ lib/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -123,6 +123,13 @@ + + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = ++ ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++ ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++ + @NEED_OWN_MD5_TRUE@MD5SOURCE = md5.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = + +@@ -158,6 +165,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +@@ -196,13 +205,18 @@ + @NEED_OWN_MD5_FALSE@am__objects_1 = + @NEED_OWN_SNPRINTF_FALSE@am__objects_2 = + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) ++@NEED_OWN_STRNSTR_FALSE@am__objects_3 = ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_FALSE@am__objects_4 = + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) stub_memaccount.$(OBJEXT) \ ++ util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) cru + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -224,15 +238,16 @@ + @AMDEP_TRUE@ $(DEPDIR)/dlmalloc.Po $(DEPDIR)/drand48.Po \ + @AMDEP_TRUE@ $(DEPDIR)/getfullhostname.Po $(DEPDIR)/hash.Po \ + @AMDEP_TRUE@ $(DEPDIR)/heap.Po $(DEPDIR)/html_quote.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/initgroups.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/iso3307.Po $(DEPDIR)/md5.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/ntlmauth.Po $(DEPDIR)/radix.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1035.Po $(DEPDIR)/rfc1123.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1738.Po $(DEPDIR)/rfc2617.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/safe_inet_addr.Po $(DEPDIR)/snprintf.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/splay.Po $(DEPDIR)/strerror.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/stub_memaccount.Po $(DEPDIR)/tempnam.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uudecode.Po ++@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/iso3307.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/md5.Po $(DEPDIR)/ntlmauth.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/radix.Po $(DEPDIR)/rfc1035.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc1123.Po $(DEPDIR)/rfc1738.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc2617.Po $(DEPDIR)/safe_inet_addr.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/snprintf.Po $(DEPDIR)/splay.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strcasestr.Po $(DEPDIR)/strerror.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strnstr.Po $(DEPDIR)/stub_memaccount.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/tempnam.Po $(DEPDIR)/util.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/uudecode.Po + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -241,8 +256,8 @@ + DIST_SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) \ + $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) \ + $(libregex_a_SOURCES) +-DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c \ +- initgroups.c strerror.c tempnam.c ++DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c strerror.c \ ++ tempnam.c + SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) $(libregex_a_SOURCES) + + all: all-am +@@ -295,7 +310,6 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/heap.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/html_quote.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/inet_ntoa.Po@am__quote@ +-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/initgroups.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iso3307.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/md5.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ntlmauth.Po@am__quote@ +@@ -307,7 +321,9 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/safe_inet_addr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splay.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerror.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tempnam.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Wed Sep 28 22:57:21 2005 ++++ src/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -125,6 +125,9 @@ + install_sh = @install_sh@ + makesnmplib = @makesnmplib@ + ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c ++@USE_ICAP_FALSE@ICAPSOURCE = ++ + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver +@@ -249,6 +252,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -468,54 +472,58 @@ + pinger_LDADD = $(LDADD) + pinger_DEPENDENCIES = + pinger_LDFLAGS = +-@USE_DELAY_POOLS_TRUE@am__objects_3 = delay_pools.$(OBJEXT) +-@USE_DELAY_POOLS_FALSE@am__objects_3 = +-@USE_DNSSERVER_FALSE@am__objects_4 = dns_internal.$(OBJEXT) +-@USE_DNSSERVER_TRUE@am__objects_4 = dns.$(OBJEXT) +-@ENABLE_HTCP_TRUE@am__objects_5 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_FALSE@am__objects_6 = +-@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) +-@USE_SNMP_FALSE@am__objects_7 = +-@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) +-@ENABLE_SSL_FALSE@am__objects_8 = +-@ENABLE_UNLINKD_FALSE@am__objects_9 = +-@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_10 = ++@USE_DELAY_POOLS_FALSE@am__objects_5 = ++@USE_DELAY_POOLS_TRUE@am__objects_5 = delay_pools.$(OBJEXT) ++@USE_DNSSERVER_FALSE@am__objects_6 = dns_internal.$(OBJEXT) ++@USE_DNSSERVER_TRUE@am__objects_6 = dns.$(OBJEXT) ++@ENABLE_HTCP_TRUE@am__objects_7 = htcp.$(OBJEXT) ++@USE_ICAP_TRUE@am__objects_8 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@USE_ICAP_FALSE@am__objects_8 = ++@MAKE_LEAKFINDER_TRUE@am__objects_9 = leakfinder.$(OBJEXT) ++@MAKE_LEAKFINDER_FALSE@am__objects_9 = ++@USE_SNMP_TRUE@am__objects_10 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) ++@USE_SNMP_FALSE@am__objects_10 = ++@ENABLE_SSL_FALSE@am__objects_11 = ++@ENABLE_SSL_TRUE@am__objects_11 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_12 = unlinkd.$(OBJEXT) ++@ENABLE_UNLINKD_FALSE@am__objects_12 = ++@ENABLE_WIN32SPECIFIC_FALSE@am__objects_13 = ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_13 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \ + cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \ + client_db.$(OBJEXT) client_side.$(OBJEXT) comm.$(OBJEXT) \ +- comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_3) \ +- disk.$(OBJEXT) $(am__objects_4) errorpage.$(OBJEXT) \ ++ comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \ ++ disk.$(OBJEXT) $(am__objects_6) errorpage.$(OBJEXT) \ + ETag.$(OBJEXT) event.$(OBJEXT) external_acl.$(OBJEXT) \ + fd.$(OBJEXT) filemap.$(OBJEXT) forward.$(OBJEXT) \ + fqdncache.$(OBJEXT) ftp.$(OBJEXT) gopher.$(OBJEXT) \ +- helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ ++ helper.$(OBJEXT) $(am__objects_7) http.$(OBJEXT) \ + HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_6) \ +- logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_8) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_9) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ + MemPool.$(OBJEXT) MemBuf.$(OBJEXT) mime.$(OBJEXT) \ + multicast.$(OBJEXT) neighbors.$(OBJEXT) net_db.$(OBJEXT) \ + Packer.$(OBJEXT) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \ + peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ +- ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_10) \ ++ ssl.$(OBJEXT) $(am__objects_11) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_12) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- whois.$(OBJEXT) $(am__objects_10) ++ whois.$(OBJEXT) $(am__objects_13) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -563,7 +571,9 @@ + @AMDEP_TRUE@ $(DEPDIR)/fqdncache.Po $(DEPDIR)/ftp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/globals.Po $(DEPDIR)/gopher.Po \ + @AMDEP_TRUE@ $(DEPDIR)/helper.Po $(DEPDIR)/htcp.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icmp.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icap_common.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_opt.Po $(DEPDIR)/icap_reqmod.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_respmod.Po $(DEPDIR)/icmp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/icp_v2.Po $(DEPDIR)/icp_v3.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ident.Po $(DEPDIR)/internal.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ipc.Po $(DEPDIR)/ipcache.Po \ +@@ -777,6 +787,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v3.Po@am__quote@ diff --git a/www/squid26/files/icap-2.5-core.patch b/www/squid26/files/icap-2.5-core.patch new file mode 100644 index 000000000000..22d209c18fb4 --- /dev/null +++ b/www/squid26/files/icap-2.5-core.patch @@ -0,0 +1,7059 @@ +Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch only contains the parts of the original patchset that +actually implement the ICAP client functionality. The updates to +the build infrastructure are omitted to avoid the need to run an +autotools bootstrap. Instead, we simulate said bootstrapping with +a second patch, icap-2.5-bootstrap.patch. + +The patchset was pulled from the project's CVS repository +at cvs.devel.squid-cache.org using + +cvs diff -u -b -N -kk -rs2_5 -ricap-2_5 + +See also +<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5> +for the "official" auto-generated patchset. + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2005-12-17 + +Index: errors/Bulgarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Bulgarian/ERR_ICAP_FAILURE +diff -N errors/Bulgarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Catalan/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Catalan/ERR_ICAP_FAILURE +diff -N errors/Catalan/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Czech/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Czech/ERR_ICAP_FAILURE +diff -N errors/Czech/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Danish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Danish/ERR_ICAP_FAILURE +diff -N errors/Danish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Dutch/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Dutch/ERR_ICAP_FAILURE +diff -N errors/Dutch/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/English/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/English/ERR_ICAP_FAILURE +diff -N errors/English/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Estonian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Estonian/ERR_ICAP_FAILURE +diff -N errors/Estonian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Finnish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Finnish/ERR_ICAP_FAILURE +diff -N errors/Finnish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/French/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/French/ERR_ICAP_FAILURE +diff -N errors/French/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/German/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/German/ERR_ICAP_FAILURE +diff -N errors/German/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2 +@@ -0,0 +1,33 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>FEHLER</H1> ++<H2>Der angeforderte URL konnte nicht geholt werden</H2> ++<HR noshade size="1px"> ++<P> ++Während des Versuches, den URL<BR> ++<A HREF="%U">%U</A> ++ ++<BR> ++zu laden, trat der folgende Fehler auf: ++<UL> ++<LI> ++<STRONG> ++ICAP-Protokollfehler ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: ++<UL> ++<LI>Nicht erreichbarer ICAP-Server ++<LI>Ungültige Antwort vom ICAP-Server ++ ++</UL> ++</P> ++ ++<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Greek/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Greek/ERR_ICAP_FAILURE +diff -N errors/Greek/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hebrew/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hebrew/ERR_ICAP_FAILURE +diff -N errors/Hebrew/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hungarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hungarian/ERR_ICAP_FAILURE +diff -N errors/Hungarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Italian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Italian/ERR_ICAP_FAILURE +diff -N errors/Italian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Japanese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Japanese/ERR_ICAP_FAILURE +diff -N errors/Japanese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Korean/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Korean/ERR_ICAP_FAILURE +diff -N errors/Korean/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Lithuanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Lithuanian/ERR_ICAP_FAILURE +diff -N errors/Lithuanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Polish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Polish/ERR_ICAP_FAILURE +diff -N errors/Polish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Portuguese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Portuguese/ERR_ICAP_FAILURE +diff -N errors/Portuguese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Romanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Romanian/ERR_ICAP_FAILURE +diff -N errors/Romanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-1251/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-1251/ERR_ICAP_FAILURE +diff -N errors/Russian-1251/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-koi8-r/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE +diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Serbian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Serbian/ERR_ICAP_FAILURE +diff -N errors/Serbian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Simplify_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE +diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Slovak/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Slovak/ERR_ICAP_FAILURE +diff -N errors/Slovak/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Spanish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Spanish/ERR_ICAP_FAILURE +diff -N errors/Spanish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Swedish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Swedish/ERR_ICAP_FAILURE +diff -N errors/Swedish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Traditional_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE +diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Turkish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Turkish/ERR_ICAP_FAILURE +diff -N errors/Turkish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.10 +retrieving revision 1.10.30.2 +diff -p -u -b -r1.10 -r1.10.30.2 +--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10 ++++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2 +@@ -132,4 +132,12 @@ double drand48(void); + */ + int statMemoryAccounted(void); + ++#ifndef HAVE_STRNSTR ++extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); ++#endif ++ ++#ifndef HAVE_STRCASESTR ++extern char *strcasestr(const char *haystack, const char *needle); ++#endif ++ + #endif /* SQUID_UTIL_H */ +Index: lib/Makefile.am +=================================================================== +RCS file: /cvsroot/squid/squid/lib/Makefile.am,v +retrieving revision 1.4 +retrieving revision 1.4.26.2 +diff -p -u -b -r1.4 -r1.4.26.2 +--- lib/Makefile.am 21 Nov 2001 23:48:57 -0000 1.4 ++++ lib/Makefile.am 6 Apr 2004 13:04:38 -0000 1.4.26.2 +@@ -8,6 +8,19 @@ SNPRINTFSOURCE=snprintf.c + else + SNPRINTFSOURCE= + endif ++ ++if NEED_OWN_STRNSTR ++STRNSTRSOURCE=strnstr.c ++else ++STRNSTRSOURCE= ++endif ++ ++if NEED_OWN_STRCASESTR ++STRCASESTRSOURCE=strcasestr.c ++else ++STRCASESTRSOURCE= ++endif ++ + if NEED_OWN_MD5 + MD5SOURCE=md5.c + else +@@ -43,6 +56,8 @@ libmiscutil_a_SOURCES = \ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +Index: lib/strcasestr.c +=================================================================== +RCS file: lib/strcasestr.c +diff -N lib/strcasestr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1 +@@ -0,0 +1,126 @@ ++/* Return the offset of one string within another. ++ Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* ++ * My personal strstr() implementation that beats most other algorithms. ++ * Until someone tells me otherwise, I assume that this is the ++ * fastest implementation of strstr() in C. ++ * I deliberately chose not to comment it. You should have at least ++ * as much fun trying to understand it, as I had to write it :-). ++ * ++ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ ++ ++/* ++ * modified to work outside of glibc (rhorstmann, 06/04/2004) ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRCASESTR ++#include <ctype.h> ++ ++typedef unsigned chartype; ++ ++char * ++strcasestr (phaystack, pneedle) ++ const char *phaystack; ++ const char *pneedle; ++{ ++ register const unsigned char *haystack, *needle; ++ register chartype b, c; ++ ++ haystack = (const unsigned char *) phaystack; ++ needle = (const unsigned char *) pneedle; ++ ++ b = tolower (*needle); ++ if (b != '\0') ++ { ++ haystack--; /* possible ANSI violation */ ++ do ++ { ++ c = *++haystack; ++ if (c == '\0') ++ goto ret0; ++ } ++ while (tolower (c) != (int) b); ++ ++ c = tolower (*++needle); ++ if (c == '\0') ++ goto foundneedle; ++ ++needle; ++ goto jin; ++ ++ for (;;) ++ { ++ register chartype a; ++ register const unsigned char *rhaystack, *rneedle; ++ ++ do ++ { ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ if (tolower (a) == (int) b) ++ break; ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++shloop: ++ ; ++ } ++ while (tolower (a) != (int) b); ++ ++jin: a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ ++ if (tolower (a) != (int) c) ++ goto shloop; ++ ++ rhaystack = haystack-- + 1; ++ rneedle = needle; ++ a = tolower (*rneedle); ++ ++ if (tolower (*rhaystack) == (int) a) ++ do ++ { ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ if (tolower (*rhaystack) != (int) a) ++ break; ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ } ++ while (tolower (*rhaystack) == (int) a); ++ ++ needle = rneedle; /* took the register-poor approach */ ++ ++ if (a == '\0') ++ break; ++ } ++ } ++foundneedle: ++ return (char*) haystack; ++ret0: ++ return 0; ++} ++#endif +Index: lib/strnstr.c +=================================================================== +RCS file: lib/strnstr.c +diff -N lib/strnstr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include <string.h> ++#include <util.h> ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.5.30.3 +retrieving revision 1.5.44.8 +diff -p -u -b -r1.5.30.3 -r1.5.44.8 +--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3 ++++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8 +@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb) + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); + } ++ ++int ++memBufRead(int fd, MemBuf * mb) ++{ ++ int len; ++ if (mb->capacity == mb->size) ++ memBufGrow(mb, SQUID_TCP_SO_RCVBUF); ++ len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); ++ if (len) ++ mb->size += len; ++ return len; ++} +Index: src/cache_cf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cache_cf.c,v +retrieving revision 1.38.6.29 +retrieving revision 1.38.6.11.2.22 +diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22 +--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29 ++++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22 +@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli + return bodylist.head == NULL; + } + ++#ifdef HS_FEAT_ICAP ++ ++/*************************************************** ++ * prototypes ++ */ ++static int icap_service_process(icap_service * s); ++static void icap_service_init(icap_service * s); ++static void icap_service_destroy(icap_service * s); ++icap_service *icap_service_lookup(char *name); ++static int icap_class_process(icap_class * c); ++static void icap_class_destroy(icap_class * c); ++static void icap_access_destroy(icap_access * a); ++static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access <servicename> {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.14.6.1 +retrieving revision 1.14.32.2 +diff -p -u -b -r1.14.6.1 -r1.14.32.2 +--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1 ++++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2 +@@ -144,6 +144,10 @@ cbdataInit(void) + CREATE_CBDATA(statefulhelper); + CREATE_CBDATA(helper_stateful_server); + CREATE_CBDATA(HttpStateData); ++#ifdef HS_FEAT_ICAP ++ CREATE_CBDATA(IcapStateData); ++ CREATE_CBDATA(icap_service); ++#endif + CREATE_CBDATA_FREE(peer, peerDestroy); + CREATE_CBDATA(ps_state); + CREATE_CBDATA(RemovalPolicy); +Index: src/cf.data.pre +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf.data.pre,v +retrieving revision 1.49.2.84 +retrieving revision 1.49.2.33.2.32 +diff -p -u -b -r1.49.2.84 -r1.49.2.33.2.32 +--- src/cf.data.pre 21 Oct 2005 02:13:47 -0000 1.49.2.84 ++++ src/cf.data.pre 24 Oct 2005 17:07:42 -0000 1.49.2.33.2.32 +@@ -2397,7 +2397,6 @@ DOC_START + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -2716,6 +2715,177 @@ DOC_START + DOC_END + + COMMENT_START ++ ICAP OPTIONS ++ ----------------------------------------------------------------------------- ++COMMENT_END ++ ++NAME: icap_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.onoff ++DEFAULT: off ++DOC_START ++ If you want to enable the ICAP client module, set this to on. ++DOC_END ++ ++NAME: icap_preview_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.preview_enable ++DEFAULT: off ++DOC_START ++ Set this to 'on' if you want to enable the ICAP preview ++ feature in Squid. ++DOC_END ++ ++NAME: icap_preview_size ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.preview_size ++DEFAULT: -1 ++DOC_START ++ The default size of preview data to be sent to the ICAP server. ++ -1 means no preview. This value might be overwritten on a per server ++ basis by OPTIONS requests. ++DOC_END ++ ++NAME: icap_check_interval ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.check_interval ++DEFAULT: 300 ++DOC_START ++ If an ICAP server does not respond, it gets marked as unreachable. Squid ++ will try again to reach it after this time. ++DOC_END ++ ++NAME: icap_send_client_ip ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_client_ip ++DEFAULT: off ++DOC_START ++ This adds the header "X-Client-IP" to ICAP requests. Can also be ++ set from the server's response to OPTIONS. ++DOC_END ++ ++NAME: icap_send_auth_user ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_auth_user ++DEFAULT: off ++DOC_START ++ This adds the header "X-Authenticated-User" to ICAP requests ++ if proxy access is authentified. Can also be set from the server's ++ response to OPTIONS. ++DOC_END ++ ++NAME: icap_auth_scheme ++TYPE: string ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.auth_scheme ++DEFAULT: Local://%u ++DOC_START ++ Authentification scheme to pass to ICAP requests if ++ icap_send_auth_user is enabled. The first occurence of "%u" ++ is replaced by the authentified user name. If no "%u" is found, ++ the username is added at the end of the scheme. ++ ++ See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, ++ section 3.4 for details on this. ++ ++ Examples: ++ ++ icap_auth_scheme Local://%u ++ icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com ++ icap_auth_scheme WinNT://nt-domain/%u ++ icap_auth_scheme Radius://radius-server/%u ++DOC_END ++ ++NAME: icap_service ++TYPE: icap_service_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines a single ICAP service ++ ++ icap_service servicename vectoring_point bypass service_url [options ...] ++ ++ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache ++ This specifies at which point of request processing the ICAP ++ service should be plugged in. ++ bypass = 1|0 ++ If set to 1 and the ICAP server cannot be reached, the request will go ++ through without being processed by an ICAP server ++ service_url = icap://servername:port/service ++ ++ Options: ++ ++ no-keep-alive To always close the connection to icap server ++ after the transaction completes ++ ++ ++ Note: reqmod_precache and respmod_postcache is not yet implemented ++ ++ Load-balancing and high availability: ++ You can obtain load-balancing and high availability by defining a ++ named service with different definitions. Then, the client ++ loops through the different entities of the service providing ++ load-balancing. If an entity is marked as unreachable, the client goes ++ one step further to the next entity: you have the high-availability. ++ See the service_1 definition below ++ ++Example: ++icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod ++icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive ++icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod ++DOC_END ++ ++NAME: icap_class ++TYPE: icap_class_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines an ICAP service chain. If there are multiple services per ++ vectoring point, they are processed in the specified order. ++ ++ icap_class classname servicename... ++ ++Example: ++icap_class class_1 service_1 service_2 ++icap class class_2 service_1 service_3 ++DOC_END ++ ++NAME: icap_access ++TYPE: icap_access_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Redirects a request through an ICAP service class, depending ++ on given acls ++ ++ icap_access classname allow|deny [!]aclname... ++ ++ The icap_access statements are processed in the order they appear in ++ this configuration file. If an access list matches, the processing stops. ++ For an "allow" rule, the specified class is used for the request. A "deny" ++ rule simply stops processing without using the class. You can also use the ++ special classname "None". ++ ++ For backward compatibility, it is also possible to use services ++ directly here. ++Example: ++icap_access class_1 allow all ++DOC_END ++ ++COMMENT_START + MISCELLANEOUS + ----------------------------------------------------------------------------- + COMMENT_END +Index: src/cf_gen_defines +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v +retrieving revision 1.5 +retrieving revision 1.5.48.3 +diff -p -u -b -r1.5 -r1.5.48.3 +--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5 ++++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3 +@@ -18,12 +18,13 @@ BEGIN { + define["USE_UNLINKD"]="--enable-unlinkd" + define["USE_USERAGENT_LOG"]="--enable-useragent-log" + define["USE_WCCP"]="--enable-wccp" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +- DEFINE=define[$2] ++ DEFINE = define[$2] + else +- DEFINE="-D" $2 ++ DEFINE = "-D" $2 + print "{\"" $2 "\", \"" DEFINE "\", " + print "#if " $2 + print "1" +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.47.2.71 +retrieving revision 1.47.2.28.2.40 +diff -p -u -b -r1.47.2.71 -r1.47.2.28.2.40 +--- src/client_side.c 19 Oct 2005 02:13:20 -0000 1.47.2.71 ++++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40 +@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n"; + static CWCB clientWriteComplete; + static CWCB clientWriteBodyComplete; + static PF clientReadRequest; +-static PF connStateFree; ++PF connStateFree; + static PF requestTimeout; + static PF clientLifetimeTimeout; + static int clientCheckTransferDone(clientHttpRequest *); +@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien + static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); + static void clientPackTermBound(String boundary, MemBuf * mb); + static void clientInterpretRequestHeaders(clientHttpRequest *); +-static void clientProcessRequest(clientHttpRequest *); ++void clientProcessRequest(clientHttpRequest *); + static void clientProcessExpired(void *data); + static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); +-static int clientCachable(clientHttpRequest * http); +-static int clientHierarchical(clientHttpRequest * http); +-static int clientCheckContentLength(request_t * r); ++int clientCachable(clientHttpRequest * http); ++int clientHierarchical(clientHttpRequest * http); ++int clientCheckContentLength(request_t * r); + static DEFER httpAcceptDefer; + static log_type clientProcessRequest2(clientHttpRequest * http); + static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); + static int clientRequestBodyTooLarge(squid_off_t clen); + static void clientProcessBody(ConnStateData * conn); + static void clientEatRequestBody(clientHttpRequest *); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + static int + checkAccelOnly(clientHttpRequest * http) +@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res + http->request = requestLink(new_request); + } + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -931,11 +938,22 @@ httpRequestFree(void *data) + *H = http->next; + http->next = NULL; + dlinkDelete(&http->active, &ClientActiveRequests); ++#if HS_FEAT_ICAP ++ /*In the case that the upload of data breaks, we need this code here .... */ ++ if (NULL != http->icap_reqmod) { ++ if (cbdataValid(http->icap_reqmod)) ++ if (http->icap_reqmod->icap_fd > -1) { ++ comm_close(http->icap_reqmod->icap_fd); ++ } ++ cbdataUnlock(http->icap_reqmod); ++ http->icap_reqmod = NULL; ++ } ++#endif + cbdataFree(http); + } + + /* This is a handler normally called by comm_close() */ +-static void ++void + connStateFree(int fd, void *data) + { + ConnStateData *connState = data; +@@ -958,7 +976,6 @@ connStateFree(int fd, void *data) + } else + safe_free(connState->in.buf); + /* XXX account connState->in.buf */ +- pconnHistCount(0, connState->nrequests); + cbdataFree(connState); + #ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ +@@ -1103,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1122,7 +1139,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1148,7 +1165,7 @@ clientCachable(clientHttpRequest * http) + } + + /* Return true if we can query our neighbors for this object */ +-static int ++int + clientHierarchical(clientHttpRequest * http) + { + const char *url = http->uri; +@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -2449,6 +2466,11 @@ clientProcessRequest(clientHttpRequest * + debug(33, 4) ("clientProcessRequest: %s '%s'\n", + RequestMethodStr[r->method], + url); ++#if HS_FEAT_ICAP ++ if (clientIcapReqMod(http)) { ++ return; ++ } ++#endif + if (r->method == METHOD_CONNECT && !http->redirect.status) { + http->log_type = LOG_TCP_MISS; + sslStart(http, &http->out.size, &http->al.http.code); +@@ -2993,6 +3015,20 @@ clientReadRequest(int fd, void *data) + (long) conn->in.offset, (long) conn->in.size); + len = conn->in.size - conn->in.offset - 1; + } ++#if HS_FEAT_ICAP ++ /* ++ * This check exists because ICAP doesn't always work well ++ * with persistent (reused) connections. One version of the ++ * REQMOD code creates a fake ConnStateData, which doesn't have ++ * an in.buf. We want to make sure that the fake ConnStateData ++ * doesn't get used here. ++ */ ++ if (NULL == conn->in.buf) { ++ debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); ++ comm_close(fd); ++ return; ++ } ++#endif + statCounter.syscalls.sock.reads++; + size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); + if (size > 0) { +@@ -3096,7 +3132,8 @@ clientReadRequest(int fd, void *data) + /* add to the client request queue */ + for (H = &conn->chr; *H; H = &(*H)->next); + *H = http; +- conn->nrequests++; ++ F->pconn.uses++; ++ F->pconn.type = 0; + /* + * I wanted to lock 'http' here since its callback data for + * clientLifetimeTimeout(), but there's no logical place to +@@ -3266,7 +3303,7 @@ clientReadRequest(int fd, void *data) + } + + /* file_read like function, for reading body content */ +-static void ++void + clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) + { + ConnStateData *conn = request->body_reader_data; +@@ -3390,7 +3427,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data) + * Some data has been sent to the client, just close the FD + */ + comm_close(fd); +- } else if (conn->nrequests) { ++ } else if (fd_table[fd].pconn.uses) { + /* + * assume its a persistent connection; just close it + */ +@@ -3948,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re + } + } + } ++ ++#if HS_FEAT_ICAP ++static int ++clientIcapReqMod(clientHttpRequest * http) ++{ ++ ErrorState *err; ++ icap_service *service; ++ if (http->flags.did_icap_reqmod) ++ return 0; ++ if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) ++ return 0; ++ debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); ++ /* ++ * Note, we pass 'start' and 'log_addr' to ICAP so the access.log ++ * entry comes out right. The 'clientHttpRequest' created by ++ * the ICAP side is the one that gets logged. The first ++ * 'clientHttpRequest' does not get logged because its out.size ++ * is zero and log_type is unset. ++ */ ++ http->icap_reqmod = icapReqModStart(service, ++ http->uri, ++ http->request, ++ http->conn->fd, ++ http->start, ++ http->conn->log_addr, ++ (void *) http->conn); ++ if (NULL == http->icap_reqmod) { ++ return 0; ++ } else if (-1 == (int) http->icap_reqmod) { ++ /* produce error */ ++ http->icap_reqmod = NULL; ++ debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); ++ http->log_type = LOG_TCP_DENIED; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = ETIMEDOUT; ++ err->request = requestLink(http->request); ++ err->src_addr = http->conn->peer.sin_addr; ++ http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return 1; ++ } ++ cbdataLock(http->icap_reqmod); ++ http->flags.did_icap_reqmod = 1; ++ return 1; ++} ++#endif +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.18.6.6 +retrieving revision 1.18.6.2.12.9 +diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9 +--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6 ++++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9 +@@ -653,8 +653,8 @@ comm_close(int fd) + #endif + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + SSL_free(F->ssl); +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.29.2.18 +retrieving revision 1.29.2.8.2.17 +diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17 +--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18 ++++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17 +@@ -93,6 +93,7 @@ typedef enum { + ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ + ERR_TOO_BIG, + TCP_RESET, ++ ERR_ICAP_FAILURE, + ERR_INVALID_RESP, + ERR_MAX + } err_type; +@@ -438,6 +439,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -610,6 +614,12 @@ typedef enum { + MEM_TLV, + MEM_SWAP_LOG_DATA, + MEM_CLIENT_REQ_BUF, ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -709,9 +719,14 @@ typedef enum { + CBDATA_RemovalPolicyWalker, + CBDATA_RemovalPurgeWalker, + CBDATA_store_client, ++#ifdef HS_FEAT_ICAP ++ CBDATA_IcapStateData, ++ CBDATA_icap_service, ++#endif + CBDATA_FIRST_CUSTOM_TYPE = 1000 + } cbdata_type; + ++ + /* + * Return codes from checkVary(request) + */ +@@ -742,4 +757,68 @@ enum { + + #endif + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.13.6.15 +retrieving revision 1.13.6.3.2.15 +diff -p -u -b -r1.13.6.15 -r1.13.6.3.2.15 +--- src/forward.c 2 Sep 2005 02:13:43 -0000 1.13.6.15 ++++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15 +@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status + else + hierarchyNote(&fwdState->request->hier, fs->code, request->host); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; + if (fs->peer) + peerConnectSucceded(fs->peer); + fwdDispatch(fwdState); +@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data) + void + fwdFail(FwdState * fwdState, ErrorState * errorState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", + err_type_str[errorState->type], + httpStatusString(errorState->http_status), +@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data) + void + fwdUnregister(int fd, FwdState * fwdState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); + assert(fd == fwdState->server_fd); + assert(fd > -1); +@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat + void + fwdComplete(FwdState * fwdState) + { +- StoreEntry *e = fwdState->entry; ++ StoreEntry *e; ++ if (NULL == fwdState) ++ return; ++ e = fwdState->entry; + assert(e->store_status == STORE_PENDING); + debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), + e->mem_obj->reply->sline.status); +Index: src/globals.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/globals.h,v +retrieving revision 1.14.6.7 +retrieving revision 1.14.6.3.2.5 +diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5 +--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7 ++++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5 +@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */ + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.17.6.32 +retrieving revision 1.17.6.3.6.39 +diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.39 +--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32 ++++ src/http.c 23 Nov 2005 20:33:07 -0000 1.17.6.3.6.39 +@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry; + + static PF httpReadReply; + static void httpSendRequest(HttpStateData *); +-static PF httpStateFree; ++PF httpStateFree; + static PF httpTimeout; + static void httpCacheNegatively(StoreEntry *); + static void httpMakePrivate(StoreEntry *); +@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *) + static int httpCachableReply(HttpStateData *); + static void httpMaybeRemovePublic(StoreEntry *, http_status); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data) + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; ++#if HS_FEAT_ICAP ++ cbdataUnlock(httpState->icap_writer); ++#endif + cbdataFree(httpState); + } + +@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht + } + + /* rewrite this later using new interfaces @?@ */ +-static void ++void + httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) + { + StoreEntry *entry = httpState->entry; +@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; ++ squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ +- if (httpState->reply_hdr_state < 2) ++ if (httpState->reply_hdr_state < 2) { ++ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", ++ httpState->reply_hdr_state); + return 0; ++ } + clen = httpReplyBodySize(httpState->request->method, reply); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ content_bytes_read = httpState->icap_writer->fake_content_length; ++ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); ++ } else ++#endif ++ content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ +- if (mem->inmem_hi > clen + reply->hdr_sz) ++ if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ +- if (mem->inmem_hi < clen + reply->hdr_sz) ++ if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data) + delay_id delay_id; + #endif + ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ /*The folowing entry can not be marked as aborted. ++ * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; +@@ -579,6 +605,33 @@ httpReadReply(int fd, void *data) + else + delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); + #endif ++ ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ IcapStateData *icap = httpState->icap_writer; ++ /* ++ * Ok we have a received a response from the web server, so try to ++ * connect the icap server if it's the first attemps. If we try ++ * to connect to the icap server, defer this request (do not read ++ * the buffer), and defer until icapConnectOver() is not called. ++ */ ++ if (icap->flags.connect_requested == 0) { ++ debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); ++ if (!icapConnect(icap, icapConnectOver)) { ++ debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); ++ icap->flags.connect_requested = 1; ++ /* Wait for more data or EOF condition */ ++ commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ } ++#endif ++ + errno = 0; + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, read_sz); +@@ -595,7 +648,13 @@ httpReadReply(int fd, void *data) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +- if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ (void) 0; ++ else ++#endif ++ ++ if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { + /* Skip whitespace */ + while (len > 0 && xisspace(*buf)) + xmemmove(buf, buf + 1, len--); +@@ -625,6 +684,12 @@ httpReadReply(int fd, void *data) + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { ++ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); ++ icapSendRespMod(httpState->icap_writer, buf, len, 1); ++ } ++#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we +@@ -677,7 +742,28 @@ httpReadReply(int fd, void *data) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ if (cbdataValid(httpState->icap_writer)) { ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } ++ } else ++#endif + storeAppend(entry, buf, len); ++ ++ ++ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + /* + * the above storeAppend() call could ABORT this entry, +@@ -724,10 +810,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); +@@ -766,6 +863,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -776,6 +877,34 @@ httpReadReply(int fd, void *data) + } + } + ++#ifdef HS_FEAT_ICAP ++static int ++httpReadReplyWaitForIcap(int fd, void *data) ++{ ++ HttpStateData *httpState = data; ++ if (NULL == httpState->icap_writer) ++ return 0; ++ /* ++ * Do not defer when we are not connected to the icap server. ++ * Defer when the icap server connection is not established but pending ++ * Defer when the icap server is busy (writing on the socket) ++ */ ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", ++ fd, httpState->icap_writer->flags.connect_requested); ++ if (!httpState->icap_writer->flags.connect_requested) ++ return 0; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", ++ fd, httpState->icap_writer->flags.connect_pending); ++ if (httpState->icap_writer->flags.connect_pending) ++ return 1; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", ++ fd, httpState->icap_writer->flags.write_pending); ++ if (httpState->icap_writer->flags.write_pending) ++ return 1; ++ return 0; ++} ++#endif ++ + /* This will be called when request write is complete. Schedule read of + * reply. */ + static void +@@ -803,6 +932,63 @@ httpSendComplete(int fd, char *bufnotuse + comm_close(fd); + return; + } else { ++ /* Schedule read reply. */ ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart( ++ ICAP_SERVICE_RESPMOD_PRECACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (-1 == (int) httpState->icap_writer) { ++ /* TODO: send error here and exit */ ++ ErrorState *err; ++ httpState->icap_writer = 0; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(httpState->orig_request); ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ return; ++ } else if (httpState->icap_writer) { ++ request_flags fake_flags = httpState->orig_request->flags; ++ method_t fake_method = entry->mem_obj->method; ++ const char *fake_msg = "this is a fake entry for " ++ " response sent to an ICAP RESPMOD server"; ++ cbdataLock(httpState->icap_writer); ++ /* ++ * this httpState will give the data it reads to ++ * the icap server, rather than put it into ++ * a StoreEntry ++ */ ++ storeUnlockObject(httpState->entry); ++ storeUnregisterAbort(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -811,8 +997,18 @@ httpSendComplete(int fd, char *bufnotuse + * the timeout for POST/PUT requests that have very large + * request bodies. + */ ++ ++ /* removed in stable5: ++ * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ */ + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +- commSetDefer(fd, fwdCheckDeferRead, entry); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); ++ commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); ++ } else ++#endif ++ commSetDefer(httpState->fd, fwdCheckDeferRead, entry); + } + httpState->flags.request_sent = 1; + } +@@ -1010,8 +1206,11 @@ httpBuildRequestHeader(request_t * reque + if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { + const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); + httpHdrCcSetMaxAge(cc, getMaxAge(url)); ++#ifndef HS_FEAT_ICAP ++ /* Don;t bother - if the url you want to cache is redirected? */ + if (strLen(request->urlpath)) + assert(strstr(url, strBuf(request->urlpath))); ++#endif + } + /* Set no-cache if determined needed but not found */ + if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) +@@ -1119,6 +1318,7 @@ httpStart(FwdState * fwd) + int fd = fwd->server_fd; + HttpStateData *httpState; + request_t *proxy_req; ++ /* ErrorState *err; */ + request_t *orig_req = fwd->request; + debug(11, 3) ("httpStart: \"%s %s\"\n", + RequestMethodStr[orig_req->method], +@@ -1156,12 +1356,22 @@ httpStart(FwdState * fwd) + httpState->request = requestLink(orig_req); + httpState->orig_request = requestLink(orig_req); + } ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (httpState->icap_writer) { ++ return; ++ } ++ } ++#endif + /* + * register the handler to free HTTP state data when the FD closes + */ + comm_add_close_handler(fd, httpStateFree, httpState); + statCounter.server.all.requests++; + statCounter.server.http.requests++; ++ + httpSendRequest(httpState); + /* + * We used to set the read timeout here, but not any more. +Index: src/icap_common.c +=================================================================== +RCS file: src/icap_common.c +diff -N src/icap_common.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_common.c 22 Nov 2005 22:41:48 -0000 1.1.2.39 +@@ -0,0 +1,785 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} +Index: src/icap_opt.c +=================================================================== +RCS file: src/icap_opt.c +diff -N src/icap_opt.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_opt.c 22 Nov 2005 22:41:48 -0000 1.1.2.17 +@@ -0,0 +1,519 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: src/icap_reqmod.c +=================================================================== +RCS file: src/icap_reqmod.c +diff -N src/icap_reqmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_reqmod.c 6 Dec 2005 21:53:44 -0000 1.1.2.58 +@@ -0,0 +1,976 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++#define ICAP_PROXY_KEEP_ALIVE 0 ++ ++/* ++ * These once-static functions are required to be global for ICAP ++ */ ++ ++PF clientReadRequest; ++PF connStateFree; ++int clientReadDefer(int fd, void *data); ++int clientCheckContentLength(request_t * r); ++void clientProcessRequest(clientHttpRequest *); ++int clientCachable(clientHttpRequest *); ++int clientHierarchical(clientHttpRequest *); ++void clientReadBody(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++ ++static PF icapReqModReadHttpHdrs; ++static PF icapReqModReadHttpBody; ++static CWCB icapReqModSendBodyChunk; ++static CBCB icapReqModBodyHandler; ++static BODY_HANDLER icapReqModBodyReader; ++static STRCB icapReqModMemBufAppend; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++static const char *crlf = "\r\n"; ++ ++/* ++ * icapExpectedHttpReqHdrSize ++ * ++ * calculate the size of the HTTP headers that we expect ++ * to read from the ICAP server. ++ */ ++static int ++icapExpectedHttpReqHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) ++ return (icap->enc.req_body - icap->enc.req_hdr); ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ fatal("icapExpectedHttpReqHdrSize: unexpected case"); ++ return 0; ++} ++ ++/* ++ * icapReqModCreateClientState ++ * ++ * Creates fake client_side data structures so we can use ++ * that module to read/parse the HTTP request that we read ++ * from the ICAP server. ++ */ ++static clientHttpRequest * ++icapReqModCreateClientState(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http; ++ if (!cbdataValid(icap->reqmod.client_cookie)) { ++ debug(81, 3) ("Whups, client cookie invalid\n"); ++ icap->reqmod.client_fd = -1; ++ return NULL; ++ } ++ http = cbdataAlloc(clientHttpRequest); ++ /* ++ * use our own urlCanonicalClean here, because urlCanonicalClean ++ * may strip everything after a question-mark. As http->uri ++ * is used when doing a request to a parent proxy, we need the full ++ * url here. ++ */ ++ http->uri = xstrdup(urlCanonical(icap->request)); ++ http->log_uri = xstrndup(http->uri, MAX_URL); ++ http->range_iter.boundary = StringNull; ++ http->request = requestLink(request ? request : icap->request); ++ http->flags.did_icap_reqmod = 1; ++ http->start = icap->reqmod.start; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ ++ http->conn->chr = http; ++ ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ err->request = requestLink(request); ++ request->flags.proxy_keepalive = 0; ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ if (!clientCheckContentLength(request)) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED); ++ err->request = requestLink(request); ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ /* Do we expect a request-body? */ ++ if (request->content_length > 0) { ++ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); ++ if (request->body_reader_data) ++ cbdataUnlock(request->body_reader_data); ++ request->body_reader = icapReqModBodyReader; ++ request->body_reader_data = icap; /* XXX cbdataLock? */ ++ cbdataLock(icap); /*Yes sure ..... */ ++ memBufDefInit(&icap->reqmod.http_entity.buf); ++ } ++ if (clientCachable(http)) ++ request->flags.cachable = 1; ++ if (clientHierarchical(http)) ++ request->flags.hierarchical = 1; ++ clientProcessRequest(http); ++} ++ ++/* ++ * icapReqModParseHttpError ++ * ++ * Handle an error when parsing the new HTTP request we read ++ * from the ICAP server. ++ */ ++static void ++icapReqModParseHttpError(IcapStateData * icap, const char *reason) ++{ ++ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); ++} ++ ++/* ++ * icapEntryError ++ * ++ * A wrapper for errorCon() and errorAppendEntry(). ++ */ ++static void ++icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) ++{ ++ ErrorState *err; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, null_request_flags); ++ err = errorCon(et, hs); ++ err->xerrno = xerrno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(http->entry, err); ++} ++ ++/* ++ * icapReqModParseHttpRequest ++ * ++ * Parse the HTTP request that we read from the ICAP server. ++ * Creates and fills in the request_t structure. ++ */ ++static void ++icapReqModParseHttpRequest(IcapStateData * icap) ++{ ++ char *mstr; ++ char *uri; ++ char *inbuf; ++ char *t; ++ char *token; ++ char *headers; ++ method_t method; ++ request_t *request; ++ http_version_t http_ver; ++ int reqlen = icap->reqmod.hdr_buf.size; ++ int hdrlen; ++ ++ /* ++ * Lazy, make a copy of the buf so I can chop it up with strtok() ++ */ ++ inbuf = xcalloc(reqlen + 1, 1); ++ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); ++ ++ if ((mstr = strtok(inbuf, "\t ")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); ++ icapReqModParseHttpError(icap, "error:invalid-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ method = urlParseMethod(mstr); ++ if (method == METHOD_NONE) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", ++ mstr); ++ icapReqModParseHttpError(icap, "error:unsupported-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ /* look for URL+HTTP/x.x */ ++ if ((uri = strtok(NULL, "\n")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); ++ icapReqModParseHttpError(icap, "error:missing-url"); ++ xfree(inbuf); ++ return; ++ } ++ while (xisspace(*uri)) ++ uri++; ++ t = uri + strlen(uri); ++ assert(*t == '\0'); ++ token = NULL; ++ while (t > uri) { ++ t--; ++ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { ++ token = t + 1; ++ break; ++ } ++ } ++ while (t > uri && xisspace(*t)) ++ *(t--) = '\0'; ++ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); ++ if (token == NULL) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); ++ icapReqModParseHttpError(icap, "error:missing-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); ++ icapReqModParseHttpError(icap, "error:invalid-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", ++ http_ver.major, http_ver.minor); ++ ++ headers = strtok(NULL, null_string); ++ hdrlen = inbuf + reqlen - headers; ++ ++ if ((request = urlParse(method, uri)) == NULL) { ++ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ /* compile headers */ ++ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { ++ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", ++ uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, ++ 3) ++ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); ++ request->http_ver = http_ver; ++ request->client_addr = icap->request->client_addr; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request != NULL) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service *service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->reqmod.http_entity.bytes_read >= icap->request->content_length) ++ icap->flags.reqmod_http_entity_eof = 1; ++ ++ if (!icap->flags.reqmod_http_entity_eof) ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ /* ++ * Notify the other side if it is waiting for data from us ++ */ ++ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.callback); ++ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.callback && icap->reqmod.http_entity.buf.size) { ++ icapReqModPassHttpBody(icap, ++ icap->reqmod.http_entity.callback_buf, ++ icap->reqmod.http_entity.callback_bufsize, ++ icap->reqmod.http_entity.callback, ++ icap->reqmod.http_entity.callback_data); ++ icap->reqmod.http_entity.callback = NULL; ++ cbdataUnlock(icap->reqmod.http_entity.callback_data); ++ ++ } ++} ++ ++/* ++ * icapReqModPassHttpBody ++ * ++ * Called from http.c after request headers have been sent. ++ * This function feeds the http.c module chunks of the request ++ * body that were stored in the http_entity.buf MemBuf. ++ */ ++static void ++icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ debug(81, 3) ("icapReqModPassHttpBody: called\n"); ++ if (!buf) { ++ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", ++ icap->icap_fd, buf, (int) size, cbdata); ++ comm_close(icap->icap_fd); ++ return; ++ } ++ if (!cbdataValid(cbdata)) { ++ debug(81, ++ 1) ++ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", ++ icap->icap_fd); ++ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ ++ /*icapReqModKeepAliveOrClose(icap); */ ++ return; ++ } ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.buf.size) { ++ int copy_sz = icap->reqmod.http_entity.buf.size; ++ if (copy_sz > size) ++ copy_sz = size; ++ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); ++ /* XXX don't let Alex see this ugliness */ ++ xmemmove(icap->reqmod.http_entity.buf.buf, ++ icap->reqmod.http_entity.buf.buf + copy_sz, ++ icap->reqmod.http_entity.buf.size - copy_sz); ++ icap->reqmod.http_entity.buf.size -= copy_sz; ++ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", ++ copy_sz); ++ callback(buf, copy_sz, cbdata); ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ return; ++ } ++ if (icap->flags.reqmod_http_entity_eof) { ++ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); ++ callback(buf, 0, cbdata); ++ icapReqModKeepAliveOrClose(icap); ++ return; ++ } ++ /* ++ * We have no data for the other side at this point. Save all ++ * these values and use them when we do have data. ++ */ ++ assert(NULL == icap->reqmod.http_entity.callback); ++ icap->reqmod.http_entity.callback = callback; ++ icap->reqmod.http_entity.callback_data = cbdata; ++ icap->reqmod.http_entity.callback_buf = buf; ++ icap->reqmod.http_entity.callback_bufsize = size; ++ cbdataLock(icap->reqmod.http_entity.callback_data); ++} ++ ++/* ++ * Body reader handler for use with request->body_reader function ++ * Simple a wrapper for icapReqModPassHttpBody function ++ */ ++ ++static void ++icapReqModBodyReader(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ IcapStateData *icap = request->body_reader_data; ++ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); ++} ++ ++/* ++ * icapReqModMemBufAppend ++ * ++ * stupid wrapper to eliminate compiler warnings ++ */ ++static void ++icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) ++{ ++ memBufAppend(data, buf, size); ++} +Index: src/icap_respmod.c +=================================================================== +RCS file: src/icap_respmod.c +diff -N src/icap_respmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_respmod.c 23 Nov 2005 20:34:34 -0000 1.1.2.60 +@@ -0,0 +1,1039 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ ++ memBufPrintf(mb, crlf); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++ if (service->flags.allow_204) ++ memBufPrintf(mb, "Allow: 204\r\n"); ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ } ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ /* No data sent yet. Start with headers */ ++ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { ++ buf += icap->sc; ++ len -= icap->sc; ++ } ++ /* ++ * Then we do not have http responce headers. All data (previous and those in buf) ++ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... ++ */ ++ if (icap->sc < 0) { ++ memBufAppend(&icap->respmod.buffer, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->sc = icap->respmod.req_hdr_copy.size; ++ icap->respmod.req_hdr_copy.size = 0; ++ buf = NULL; ++ len = 0; ++ } ++ } ++ if (0 == icap->sc) { ++ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ ++ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); ++ memBufClean(&mb); ++ return; ++ } ++#if ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ ++ icap->flags.preview_done = 1; ++ ++ if (!icap->flags.preview_done) { ++ /* preview not yet sent */ ++ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size ++ && len > 0) { ++ /* Try to collect at least preview_size+1 bytes */ ++ /* By collecting one more byte than needed for preview we know best */ ++ /* whether we have to send the ieof chunk extension */ ++ size = icap->respmod.buffer.size + len; ++ if (size > preview_size + 1) ++ size = preview_size + 1; ++ size -= icap->respmod.buffer.size; ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", ++ icap->icap_fd, size); ++ memBufAppend(&icap->respmod.buffer, buf, size); ++ buf = ((char *) buf) + size; ++ len -= size; ++ } ++ if (icap->respmod.buffer.size > preview_size || theEnd) { ++ /* we got enough bytes for preview or this is the last call */ ++ /* add preview preview now */ ++ if (icap->respmod.buffer.size > 0) { ++ size = icap->respmod.buffer.size; ++ if (size > preview_size) ++ size = preview_size; ++ memBufPrintf(&mb, "%x\r\n", size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += size; ++ } ++ if (icap->respmod.buffer.size <= preview_size) { ++ /* content length is less than preview size+1 */ ++ if (icap->respmod.res_body_sz) ++ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ } else { ++ char ch; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ /* end of preview, wait for continue or 204 signal */ ++ /* copy the extra byte and all other data to the icap buffer */ ++ /* so that it can be handled next time */ ++ ch = icap->respmod.buffer.buf[preview_size]; ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ memBufAppend(&icap->respmod.buffer, &ch, 1); ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", ++ icap->icap_fd, len + 1); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ } ++ icap->flags.preview_done = 1; ++ icap->flags.wait_for_preview_reply = 1; ++ } ++ } else if (icap->flags.wait_for_preview_reply) { ++ /* received new data while waiting for preview response */ ++ /* add data to internal buffer and send later */ ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", ++ icap->icap_fd, len); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ /* do not send any data now while waiting for preview response */ ++ /* but prepare for read more data on the HTTP connection */ ++ memBufClean(&mb); ++ return; ++ } else ++#endif ++ { ++ /* after preview completed and ICAP preview response received */ ++ /* there may still be some data in the buffer */ ++ if (icap->respmod.buffer.size > 0) { ++ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, ++ icap->respmod.buffer.size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += icap->respmod.buffer.size; ++ memBufReset(&icap->respmod.buffer); ++ } ++ if (len > 0) { ++ memBufPrintf(&mb, "%x\r\n", len); ++ memBufAppend(&mb, buf, len); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += len; ++ } ++ if (icap->flags.send_zero_chunk) { ++ /* send zero end chunk */ ++ icap->flags.send_zero_chunk = 0; ++ icap->flags.http_server_eof = 1; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ } ++ /* wait for data coming from ICAP server as soon as we sent something */ ++ /* but of course only until we got the response header */ ++ if (!icap->flags.got_reply) ++ icap->flags.wait_for_reply = 1; ++ } ++ commSetTimeout(icap->icap_fd, -1, NULL, NULL); ++ ++ if (!mb.size) { ++ memBufClean(&mb); ++ return; ++ } ++ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ icap->flags.write_pending = 1; ++ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); ++} ++ ++static void ++icapRespModReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ int status = 0; ++ int isIcap = 0; ++ int directResponse = 0; ++ ErrorState *err; ++ const char *start; ++ const char *end; ++ ++ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ }; ++ /* OK here we have responce. Lets stop filling the ++ * icap->respmod.resp_copy buffer .... ++ */ ++ icap->flags.copy_response = 0; ++ ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++#if ICAP_PREVIEW ++ if (icap->flags.wait_for_preview_reply) { ++ if (100 == status) { ++ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ /* if http_server_eof ++ * call again icapSendRespMod to handle data that ++ * was received while waiting for this ICAP response ++ * else let http to call icapSendRespMod when new data arrived ++ */ ++ if (icap->flags.http_server_eof) ++ icapSendRespMod(icap, NULL, 0, 0); ++ /* ++ * reset the header to send the rest of the preview ++ */ ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufReset(&icap->icap_hdr); ++ ++ /*We do n't need it any more ....... */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ ++ return; ++ } ++ if (204 == status) { ++ debug(81, ++ 5) ("icapRespModReadReply: 204 No modification received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ } ++ } ++#endif /*ICAP_PREVIEW */ ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ if (204 == status) { ++ debug(81, 3) ("got 204 status from ICAP server\n"); ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ ++ /* ++ * "directResponse" is the normal case here. If we don't have ++ * a response header or body, it is an error. ++ */ ++ if (!directResponse) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ /* Next, gobble any data before the HTTP response starts */ ++ if (icap->enc.res_hdr > -1) ++ icap->bytes_to_gobble = icap->enc.res_hdr; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++} ++ ++ ++/* ++ * Gobble up (read) some bytes until we get to the start of the body ++ */ ++static void ++icapRespModGobble(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); ++ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, ++ icap->bytes_to_gobble); ++ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); ++ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); ++ if (len < 0) { ++ /* XXX error */ ++ abort(); ++ } ++ icap->bytes_to_gobble -= len; ++ if (icap->bytes_to_gobble) ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++ else ++ icapReadReply(fd, icap); ++} ++ ++ ++static void ++icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ErrorState *err; ++ ++ icap->flags.write_pending = 0; ++ debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ if (cbdataValid(icap)) ++ err->request = requestLink(icap->request); ++ storeEntryReset(icap->respmod.entry); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); ++ comm_close(fd); ++ return; ++ } ++ if (icap->flags.send_zero_chunk) { ++ debug(81, ++ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); ++ icap->flags.send_zero_chunk = 0; ++ icapSendRespMod(icap, NULL, 0, 1); ++ return; ++ } ++ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { ++ /* Schedule reading the ICAP response */ ++ debug(81, ++ 3) ++ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", ++ fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++#if 1 ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++#else ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ /* ++ * Set the read timeout only after all data has been sent ++ * or we are waiting for a preview response ++ * If the ICAP server does not return any data till all data ++ * has been sent, we are likely to hit the timeout for large ++ * HTTP bodies ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ } ++#endif ++ } ++} ++ ++void ++icapConnectOver(int fd, int status, void *data) ++{ ++ ErrorState *err; ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ if (status < 0) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); ++ icapOptSetUnreachable(icap->current_service); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++} ++ ++ ++ ++IcapStateData * ++icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, ++ http_state_flags http_flags) ++{ ++ IcapStateData *icap = NULL; ++ CNCB *theCallback = NULL; ++ icap_service *service = NULL; ++ ++ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); ++ assert(type >= 0 && type < ICAP_SERVICE_MAX); ++ ++ service = icapService(type, request); ++ if (!service) { ++ debug(81, 3) ("icapRespModStart: no service found\n"); ++ return NULL; /* no service found */ ++ } ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ++ ("icapRespModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ++ ("icapRespModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ switch (type) { ++ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change ++ * this switch, because callbacks isn't keep */ ++ case ICAP_SERVICE_RESPMOD_PRECACHE: ++ theCallback = icapConnectOver; ++ break; ++ default: ++ fatalf("icapRespModStart: unsupported service type '%s'\n", ++ icap_service_type_str[type]); ++ break; ++ } ++ ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->request = requestLink(request); ++ icap->respmod.entry = entry; ++ if (entry) ++ storeLockObject(entry); ++ icap->http_flags = http_flags; ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ ++ /* ++ * Don't create socket to the icap server now, but only for the first ++ * packet receive from the http server. This will resolve all timeout ++ * between the web server and icap server. ++ */ ++ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); ++ icap->flags.connect_requested = 0; ++ ++ /* ++ * make a copy the HTTP response that we send to the ICAP server in ++ * case it turns out to be a 204 ++ */ ++#ifdef SUPPORT_ICAP_204 ++ icap->flags.copy_response = 1; ++#elif ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) ++ icap->flags.copy_response = 0; ++ else ++ icap->flags.copy_response = 1; ++#else ++ icap->flags.copy_response = 0; ++#endif ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapRespModStart: returning %p\n", icap); ++ return icap; ++} ++ ++static int ++icapHttpReplyHdrState(IcapStateData * icap) ++{ ++ assert(icap); ++ if (NULL == icap->httpState) ++ return 0; ++ return icap->httpState->reply_hdr_state; ++} ++ ++static void ++icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) ++{ ++ if (NULL == icap->httpState) { ++ icap->httpState = cbdataAlloc(HttpStateData); ++ icap->httpState->request = requestLink(icap->request); ++ icap->httpState->orig_request = requestLink(icap->request); ++ icap->httpState->entry = icap->respmod.entry; ++ storeLockObject(icap->httpState->entry); /* lock it */ ++ } ++ httpProcessReplyHeader(icap->httpState, buf, size); ++ if (2 == icap->httpState->reply_hdr_state) ++ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); ++} ++ ++/* ++ * icapRespModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapRespModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink((request_t *) request); ++ err->xerrno = errno; ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ } else { ++ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", ++ fd); ++ comm_close(fd); ++ } ++ return; ++ } ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++} ++ ++static int ++icapReadReply2(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ debug(81, 3) ("icapReadReply2\n"); ++ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); ++ err->xerrno = errno; ++ err->request = requestLink((request_t *) request); ++ errorAppendEntry(entry, err); ++ icap->flags.http_server_eof = 1; ++ return -1; ++ } ++ if (icap->chunk_buf.size == 0) { ++ /* Retrieval done. */ ++ if (icapHttpReplyHdrState(icap) < 2) ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ icap->flags.http_server_eof = 1; ++ icapReadReply3(icap); ++ return 0; ++ } ++ if (icapHttpReplyHdrState(icap) == 0) { ++ int expect = icapExpectedHttpReplyHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed < 0 || needed >= 0); ++ if (0 > expect) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ } else if (0 == expect) { ++ /* ++ * this icap reply doesn't give us new HTTP headers ++ * so we must copy them from our copy ++ */ ++ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, ++ __LINE__); ++ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ ++ storeAppend(entry, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ } ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ ++ } else if (needed) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ if (icap->chunk_buf.size >= needed) { ++ storeAppend(entry, icap->chunk_buf.buf, needed); ++ so_far += needed; ++ xmemmove(icap->chunk_buf.buf, ++ icap->chunk_buf.buf + needed, ++ icap->chunk_buf.size - needed); ++ icap->chunk_buf.size -= needed; ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; ++ } else { ++ /* ++ * We don't have the full HTTP reply headers yet, so keep ++ * the partial reply buffered in 'chunk_buf' and wait ++ * for more. ++ */ ++ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); ++ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } ++ } ++ icap->http_header_bytes_read_so_far = so_far; ++ } ++ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, ++ (int) icap->chunk_buf.size); ++ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, ++ icap->flags.no_content); ++ if (icap->flags.no_content) { ++ /* data from http.c is not chunked */ ++ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", ++ icap->chunk_buf.size); ++ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); ++ icap->chunk_buf.size = 0; ++ } ++ } else if (2 == icapHttpReplyHdrState(icap)) { ++ if (icap->chunk_buf.size) ++ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); ++ } ++ icapReadReply3(icap); ++ return 0; ++} ++ ++static void ++icapReadReply3(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ int fd = icap->icap_fd; ++ debug(81, 3) ("icapReadReply3\n"); ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ icapRespModKeepAliveOrClose(icap); ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.28.6.25 +retrieving revision 1.28.6.8.2.11 +diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11 +--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25 ++++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11 +@@ -350,6 +350,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + authenticateShutdown(); + externalAclShutdown(); +@@ -378,6 +381,9 @@ mainReconfigure(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -507,6 +513,9 @@ mainInitialize(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); +Index: src/mem.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/mem.c,v +retrieving revision 1.13 +retrieving revision 1.13.28.2 +diff -p -u -b -r1.13 -r1.13.28.2 +--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13 ++++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2 +@@ -243,6 +243,13 @@ memInit(void) + memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.140.1 +diff -p -u -b -r1.2 -r1.2.140.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1 +@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str"; + $pat{'icp_opcode'} = "icp_opcode_str"; + $pat{'swap_log_op'} = "swap_log_op_str"; + $pat{'lookup_t'} = "lookup_t_str"; ++$pat{'icap_service_t'} = "icap_service_type_str"; + + $state = 0; # start state + while (<>) { +Index: src/pconn.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/pconn.c,v +retrieving revision 1.6.38.2 +retrieving revision 1.6.60.2 +diff -p -u -b -r1.6.38.2 -r1.6.60.2 +--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2 ++++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2 +@@ -46,6 +46,9 @@ struct _pconn { + #define PCONN_HIST_SZ (1<<16) + int client_pconn_hist[PCONN_HIST_SZ]; + int server_pconn_hist[PCONN_HIST_SZ]; ++#ifdef HS_FEAT_ICAP ++int icap_server_pconn_hist[PCONN_HIST_SZ]; ++#endif + + static PF pconnRead; + static PF pconnTimeout; +@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); + } ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(e, ++ "\n" ++ "ICAP-server persistent connection counts:\n" ++ "\n" ++ "\treq/\n" ++ "\tconn count\n" ++ "\t---- ---------\n"); ++ for (i = 0; i < PCONN_HIST_SZ; i++) { ++ if (icap_server_pconn_hist[i] == 0) ++ continue; ++ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); ++ } ++#endif + } + + /* ========== PUBLIC FUNCTIONS ============================================ */ +@@ -173,6 +190,9 @@ pconnInit(void) + for (i = 0; i < PCONN_HIST_SZ; i++) { + client_pconn_hist[i] = 0; + server_pconn_hist[i] = 0; ++#ifdef HS_FEAT_ICAP ++ icap_server_pconn_hist[i] = 0; ++#endif + } + pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); + pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); +@@ -248,11 +268,15 @@ pconnHistCount(int what, int i) + { + if (i >= PCONN_HIST_SZ) + i = PCONN_HIST_SZ - 1; +- /* what == 0 for client, 1 for server */ ++ /* what == 0 for client, 1 for server, 2 for ICAP server */ + if (what == 0) + client_pconn_hist[i]++; + else if (what == 1) + server_pconn_hist[i]++; ++#ifdef HS_FEAT_ICAP ++ else if (what == 2) ++ icap_server_pconn_hist[i]++; ++#endif + else + assert(0); + } +Index: src/protos.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/protos.h,v +retrieving revision 1.41.6.33 +retrieving revision 1.41.6.13.2.37 +diff -p -u -b -r1.41.6.33 -r1.41.6.13.2.37 +--- src/protos.h 16 Sep 2005 02:13:25 -0000 1.41.6.33 ++++ src/protos.h 6 Dec 2005 21:53:44 -0000 1.41.6.13.2.37 +@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *); + /* http.c */ + extern int httpCachable(method_t); + extern void httpStart(FwdState *); ++extern void httpParseReplyHeaders(const char *, http_reply *); ++extern void httpProcessReplyHeader(HttpStateData *, const char *, int); + extern int httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, +@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c + extern FREE *memBufFreeFunc(MemBuf * mb); + /* puts report on MemBuf _module_ usage into mb */ + extern void memBufReport(MemBuf * mb); ++extern int memBufRead(int fd, MemBuf * mb); + + extern char *mime_get_header(const char *mime, const char *header); + extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); +@@ -1341,4 +1344,49 @@ extern void externalAclShutdown(void); + extern int externalAclRequiresAuth(void *acl_data); + extern char *strtokFile(void); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.13.6.8 +retrieving revision 1.13.6.6.2.11 +diff -p -u -b -r1.13.6.8 -r1.13.6.6.2.11 +--- src/squid.h 26 Mar 2005 03:15:58 -0000 1.13.6.8 ++++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11 +@@ -38,6 +38,14 @@ + #include "config.h" + + /* ++ * experimental defines for ICAP ++ */ ++#ifdef HS_FEAT_ICAP ++#define ICAP_PREVIEW 1 ++#define SUPPORT_ICAP_204 0 ++#endif ++ ++/* + * On some systems, FD_SETSIZE is set to something lower than the + * actual number of files which can be opened. IRIX is one case, + * NetBSD is another. So here we increase FD_SETSIZE to our +Index: src/stat.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/stat.c,v +retrieving revision 1.13.6.14 +retrieving revision 1.13.6.7.2.7 +diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7 +--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14 ++++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7 +@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min + storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", + XAVG(server.other.kbytes_out.kb)); + ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", ++ XAVG(icap.all.requests)); ++ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", ++ XAVG(icap.all.errors)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", ++ XAVG(icap.all.kbytes_in.kb)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", ++ XAVG(icap.all.kbytes_out.kb)); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", + XAVG(icp.pkts_sent)); + storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", +@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry) + storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", + (int) f->server.other.kbytes_out.kb); + ++#if HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %d\n", ++ (int) f->icap.all.requests); ++ storeAppendPrintf(sentry, "icap.all.errors = %d\n", ++ (int) f->icap.all.errors); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", ++ (int) f->icap.all.kbytes_in.kb); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", ++ (int) f->icap.all.kbytes_out.kb); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", + f->icp.pkts_sent); + storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", +@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s) + storeAppendPrintf(s, "\tme: %s:%d\n", + inet_ntoa(conn->me.sin_addr), + ntohs(conn->me.sin_port)); +- storeAppendPrintf(s, "\tnrequests: %d\n", +- conn->nrequests); + storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", + conn->defer.n, (long int) conn->defer.until); + } +Index: src/store.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/store.c,v +retrieving revision 1.16.6.9 +retrieving revision 1.16.6.2.2.8 +diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8 +--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9 ++++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8 +@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.48.2.43 +retrieving revision 1.48.2.9.2.48 +diff -p -u -b -r1.48.2.43 -r1.48.2.9.2.48 +--- src/structs.h 4 Sep 2005 02:13:28 -0000 1.48.2.43 ++++ src/structs.h 30 Nov 2005 21:52:15 -0000 1.48.2.9.2.48 +@@ -384,6 +384,22 @@ struct _RemovalPolicySettings { + wordlist *args; + }; + ++#if HS_FEAT_ICAP ++struct _IcapConfig { ++ int onoff; ++ int preview_enable; ++ icap_service *service_head; ++ icap_class *class_head; ++ icap_access *access_head; ++ int preview_size; ++ int check_interval; ++ int send_client_ip; ++ int send_auth_user; ++ char *auth_scheme; ++}; ++ ++#endif /* HS_FEAT_ICAP */ ++ + struct _SquidConfig { + struct { + squid_off_t maxSize; +@@ -714,6 +730,9 @@ struct _SquidConfig { + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + external_acl *externalAclHelperList; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -787,7 +806,10 @@ struct _fde { + } flags; + squid_off_t bytes_read; + squid_off_t bytes_written; +- int uses; /* ie # req's over persistent conn */ ++ struct { ++ int uses; ++ int type; ++ } pconn; + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; +@@ -982,6 +1004,130 @@ struct _http_state_flags { + unsigned int request_sent:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -993,10 +1139,14 @@ struct _HttpStateData { + int fd; + http_state_flags flags; + FwdState *fwd; ++#ifdef HS_FEAT_ICAP ++ struct _IcapStateData *icap_writer; ++#endif + char *body_buf; + int body_buf_sz; + }; + ++ + struct _icpUdpData { + struct sockaddr_in address; + void *msg; +@@ -1092,6 +1242,7 @@ struct _clientHttpRequest { + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; ++ unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { +@@ -1100,6 +1251,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1127,7 +1281,6 @@ struct _ConnStateData { + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; +- int nrequests; + struct { + int n; + time_t until; +@@ -1678,6 +1831,9 @@ struct _request_t { + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + }; +@@ -1784,7 +1940,11 @@ struct _StatCounters { + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; +- } server; ++ } ++#if HS_FEAT_ICAP ++ icap, ++#endif ++ server; + struct { + int pkts_sent; + int queries_sent; +Index: src/typedefs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/typedefs.h,v +retrieving revision 1.25.6.8 +retrieving revision 1.25.6.1.6.13 +diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13 +--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8 ++++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13 +@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade + typedef struct _HttpBody HttpBody; + typedef struct _HttpReply HttpReply; + typedef struct _HttpStateData HttpStateData; ++#ifdef HS_FEAT_ICAP ++typedef struct _IcapStateData IcapStateData; ++typedef struct _IcapConfig IcapConfig; ++typedef struct _icap_service icap_service; ++typedef struct _icap_service_list icap_service_list; ++typedef struct _icap_class icap_class; ++typedef struct _icap_access icap_access; ++typedef struct _IcapOptData IcapOptData; ++#endif + typedef struct _icpUdpData icpUdpData; + typedef struct _clientHttpRequest clientHttpRequest; + typedef struct _ConnStateData ConnStateData; +Index: src/url.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/url.c,v +retrieving revision 1.7.6.6 +retrieving revision 1.7.6.5.2.2 +diff -p -u -b -r1.7.6.6 -r1.7.6.5.2.2 +--- src/url.c 12 Nov 2005 03:13:48 -0000 1.7.6.6 ++++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -221,6 +224,10 @@ urlParseProtocol(const char *s) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; ++#ifdef HS_FEAT_ICAP ++ if (strcasecmp(s, "icap") == 0) ++ return PROTO_ICAP; ++#endif + return PROTO_NONE; + } + +@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p) + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; ++#ifdef HS_FEAT_ICAP ++ case PROTO_ICAP: ++ return 1344; ++#endif + default: + return 0; + } diff --git a/www/squid27/Makefile b/www/squid27/Makefile index 31916d4cb34e..1f75bf5d90f8 100644 --- a/www/squid27/Makefile +++ b/www/squid27/Makefile @@ -66,10 +66,14 @@ # Override the maximum number of filedescriptors. Useful if you # build as another user who is not privileged to use the amount # of filedescriptors the resulting binary is expected to support. +# --enable-ntlm-fail-open +# Enable NTLM fail open, where a helper that fails one of the +# Authentication steps can allow squid to still authenticate the user # PORTNAME= squid PORTVERSION= 2.5.12 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= \ ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ @@ -82,6 +86,7 @@ DISTNAME= squid-2.5.STABLE12 DIST_SUBDIR= squid2.5 PATCH_SITES= http://www.squid-cache.org/Versions/v2/2.5/bugs/ +PATCHFILES= squid-2.5.STABLE12-SMB_BadFetch.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -120,6 +125,7 @@ OPTIONS= SQUID_LDAP_AUTH "Install LDAP authentication helpers" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ + SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ SQUID_COSS "Enable the COSS storage scheme" off \ SQUID_LARGEFILE "Support log and cache files >2GB" off \ @@ -293,6 +299,12 @@ EXTRA_PATCHES+= ${PATCHDIR}/follow_xff-2.5.patch \ ${PATCHDIR}/follow_xff-configure.patch CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif +.if defined(WITH_SQUID_ICAP) +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.5-core.patch \ + ${PATCHDIR}/icap-2.5-bootstrap.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE +.endif .if defined(WITH_SQUID_LARGEFILE) CONFIGURE_ARGS+= --with-large-files --enable-large-cache-files .endif diff --git a/www/squid27/distinfo b/www/squid27/distinfo index 5d1b5428ba58..3e55ac8d1717 100644 --- a/www/squid27/distinfo +++ b/www/squid27/distinfo @@ -1,2 +1,6 @@ MD5 (squid2.5/squid-2.5.STABLE12.tar.bz2) = 7354255015b3772a1e024dfac173e48c +SHA256 (squid2.5/squid-2.5.STABLE12.tar.bz2) = ba0ccd956323f0dad46c19aa8d40c537846fedfc3778b5730e5610f16c0d9af1 SIZE (squid2.5/squid-2.5.STABLE12.tar.bz2) = 1075111 +MD5 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 8e83b776c0d015bd4137cc1ca08f6d38 +SHA256 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 9ca8427c2eb9e5cbdb5a49fb5cb94fc00853ad965f87666f8fc35236e98bc0ae +SIZE (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 826 diff --git a/www/squid27/files/icap-2.5-bootstrap.patch b/www/squid27/files/icap-2.5-bootstrap.patch new file mode 100644 index 000000000000..247ca0c94cbc --- /dev/null +++ b/www/squid27/files/icap-2.5-bootstrap.patch @@ -0,0 +1,422 @@ +Patch 2 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.5-core.patch for further information. + +Patch last updated: 2005-12-17 + +--- configure.orig Sat Oct 22 11:56:01 2005 ++++ configure Sat Dec 17 17:45:21 2005 +@@ -70,6 +70,8 @@ + ac_help="$ac_help + --enable-delay-pools Enable delay pools to limit bandwidth usage" + ac_help="$ac_help ++ --enable-icap-support Enable iCAP client capability" ++ac_help="$ac_help + --enable-useragent-log Enable logging of User-Agent header" + ac_help="$ac_help + --enable-referer-log Enable logging of Referer header" +@@ -2170,6 +2172,38 @@ + + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ cat >> confdefs.h <<\EOF ++#define HS_FEAT_ICAP 1 ++EOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ fi ++ ++fi ++ ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -7428,14 +7462,14 @@ + fi + ;; + esac +- echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 +-echo "configure:7433: checking for main in -lpthread" >&5 ++ echo $ac_n "checking for main in -pthread""... $ac_c" 1>&6 ++echo "configure:7433: checking for main in -pthread" >&5 + ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 + else + ac_save_LIBS="$LIBS" +-LIBS="-lpthread $LIBS" ++LIBS="-pthread $LIBS" + cat > conftest.$ac_ext <<EOF + #line 7441 "configure" + #include "confdefs.h" +@@ -7465,7 +7499,7 @@ + #define $ac_tr_lib 1 + EOF + +- LIBS="-lpthread $LIBS" ++ LIBS="-pthread $LIBS" + + else + echo "$ac_t""no" 1>&6 +@@ -7769,6 +7803,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strtoll \ + sysconf \ + syslog \ +@@ -7898,6 +7934,50 @@ + fi + fi + ++ ++if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++fi ++ ++ ++ ++ + echo $ac_n "checking if va_copy is implemented""... $ac_c" 1>&6 + echo "configure:7903: checking if va_copy is implemented" >&5 + if eval "test \"`echo '$''{'ac_cv_func_va_copy'+set}'`\" = set"; then +@@ -9072,6 +9152,8 @@ + s%@ENABLE_PINGER_FALSE@%$ENABLE_PINGER_FALSE%g + s%@USE_DELAY_POOLS_TRUE@%$USE_DELAY_POOLS_TRUE%g + s%@USE_DELAY_POOLS_FALSE@%$USE_DELAY_POOLS_FALSE%g ++s%@USE_ICAP_TRUE@%$USE_ICAP_TRUE%g ++s%@USE_ICAP_FALSE@%$USE_ICAP_FALSE%g + s%@USE_SNMP_TRUE@%$USE_SNMP_TRUE%g + s%@USE_SNMP_FALSE@%$USE_SNMP_FALSE%g + s%@SNMPLIB@%$SNMPLIB%g +@@ -9118,6 +9200,10 @@ + s%@LIB_LBER@%$LIB_LBER%g + s%@NEED_OWN_SNPRINTF_TRUE@%$NEED_OWN_SNPRINTF_TRUE%g + s%@NEED_OWN_SNPRINTF_FALSE@%$NEED_OWN_SNPRINTF_FALSE%g ++s%@NEED_OWN_STRNSTR_TRUE@%$NEED_OWN_STRNSTR_TRUE%g ++s%@NEED_OWN_STRNSTR_FALSE@%$NEED_OWN_STRNSTR_FALSE%g ++s%@NEED_OWN_STRCASESTR_TRUE@%$NEED_OWN_STRCASESTR_TRUE%g ++s%@NEED_OWN_STRCASESTR_FALSE@%$NEED_OWN_STRCASESTR_FALSE%g + s%@REGEXLIB@%$REGEXLIB%g + s%@LIBREGEX@%$LIBREGEX%g + s%@LIBOBJS@%$LIBOBJS%g +--- include/autoconf.h.in.orig Tue Sep 13 02:12:34 2005 ++++ include/autoconf.h.in Sat Dec 17 17:45:21 2005 +@@ -124,6 +124,11 @@ + */ + #undef DELAY_POOLS + ++/* ++ * ICAP - Internet Content Adaptation Protocol ++ */ ++#undef HS_FEAT_ICAP ++ + /* + * If you want to log User-Agent request header values, define this. + * By default, they are written to useragent.log in the Squid log +@@ -574,6 +579,12 @@ + + /* Define if you have the statfs function. */ + #undef HAVE_STATFS ++ ++/* Define if you have the strcasestr function. */ ++#undef HAVE_STRCASESTR ++ ++/* Define if you have the strnstr function. */ ++#undef HAVE_STRNSTR + + /* Define if you have the strerror function. */ + #undef HAVE_STRERROR +--- lib/Makefile.in.orig Wed Sep 28 22:57:20 2005 ++++ lib/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -123,6 +123,13 @@ + + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = ++ ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++ ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++ + @NEED_OWN_MD5_TRUE@MD5SOURCE = md5.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = + +@@ -158,6 +165,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +@@ -196,13 +205,18 @@ + @NEED_OWN_MD5_FALSE@am__objects_1 = + @NEED_OWN_SNPRINTF_FALSE@am__objects_2 = + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) ++@NEED_OWN_STRNSTR_FALSE@am__objects_3 = ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_FALSE@am__objects_4 = + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) stub_memaccount.$(OBJEXT) \ ++ util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) cru + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -224,15 +238,16 @@ + @AMDEP_TRUE@ $(DEPDIR)/dlmalloc.Po $(DEPDIR)/drand48.Po \ + @AMDEP_TRUE@ $(DEPDIR)/getfullhostname.Po $(DEPDIR)/hash.Po \ + @AMDEP_TRUE@ $(DEPDIR)/heap.Po $(DEPDIR)/html_quote.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/initgroups.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/iso3307.Po $(DEPDIR)/md5.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/ntlmauth.Po $(DEPDIR)/radix.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1035.Po $(DEPDIR)/rfc1123.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1738.Po $(DEPDIR)/rfc2617.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/safe_inet_addr.Po $(DEPDIR)/snprintf.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/splay.Po $(DEPDIR)/strerror.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/stub_memaccount.Po $(DEPDIR)/tempnam.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uudecode.Po ++@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/iso3307.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/md5.Po $(DEPDIR)/ntlmauth.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/radix.Po $(DEPDIR)/rfc1035.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc1123.Po $(DEPDIR)/rfc1738.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc2617.Po $(DEPDIR)/safe_inet_addr.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/snprintf.Po $(DEPDIR)/splay.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strcasestr.Po $(DEPDIR)/strerror.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strnstr.Po $(DEPDIR)/stub_memaccount.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/tempnam.Po $(DEPDIR)/util.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/uudecode.Po + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -241,8 +256,8 @@ + DIST_SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) \ + $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) \ + $(libregex_a_SOURCES) +-DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c \ +- initgroups.c strerror.c tempnam.c ++DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c strerror.c \ ++ tempnam.c + SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) $(libregex_a_SOURCES) + + all: all-am +@@ -295,7 +310,6 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/heap.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/html_quote.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/inet_ntoa.Po@am__quote@ +-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/initgroups.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iso3307.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/md5.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ntlmauth.Po@am__quote@ +@@ -307,7 +321,9 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/safe_inet_addr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splay.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerror.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tempnam.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Wed Sep 28 22:57:21 2005 ++++ src/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -125,6 +125,9 @@ + install_sh = @install_sh@ + makesnmplib = @makesnmplib@ + ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c ++@USE_ICAP_FALSE@ICAPSOURCE = ++ + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver +@@ -249,6 +252,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -468,54 +472,58 @@ + pinger_LDADD = $(LDADD) + pinger_DEPENDENCIES = + pinger_LDFLAGS = +-@USE_DELAY_POOLS_TRUE@am__objects_3 = delay_pools.$(OBJEXT) +-@USE_DELAY_POOLS_FALSE@am__objects_3 = +-@USE_DNSSERVER_FALSE@am__objects_4 = dns_internal.$(OBJEXT) +-@USE_DNSSERVER_TRUE@am__objects_4 = dns.$(OBJEXT) +-@ENABLE_HTCP_TRUE@am__objects_5 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_FALSE@am__objects_6 = +-@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) +-@USE_SNMP_FALSE@am__objects_7 = +-@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) +-@ENABLE_SSL_FALSE@am__objects_8 = +-@ENABLE_UNLINKD_FALSE@am__objects_9 = +-@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_10 = ++@USE_DELAY_POOLS_FALSE@am__objects_5 = ++@USE_DELAY_POOLS_TRUE@am__objects_5 = delay_pools.$(OBJEXT) ++@USE_DNSSERVER_FALSE@am__objects_6 = dns_internal.$(OBJEXT) ++@USE_DNSSERVER_TRUE@am__objects_6 = dns.$(OBJEXT) ++@ENABLE_HTCP_TRUE@am__objects_7 = htcp.$(OBJEXT) ++@USE_ICAP_TRUE@am__objects_8 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@USE_ICAP_FALSE@am__objects_8 = ++@MAKE_LEAKFINDER_TRUE@am__objects_9 = leakfinder.$(OBJEXT) ++@MAKE_LEAKFINDER_FALSE@am__objects_9 = ++@USE_SNMP_TRUE@am__objects_10 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) ++@USE_SNMP_FALSE@am__objects_10 = ++@ENABLE_SSL_FALSE@am__objects_11 = ++@ENABLE_SSL_TRUE@am__objects_11 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_12 = unlinkd.$(OBJEXT) ++@ENABLE_UNLINKD_FALSE@am__objects_12 = ++@ENABLE_WIN32SPECIFIC_FALSE@am__objects_13 = ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_13 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \ + cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \ + client_db.$(OBJEXT) client_side.$(OBJEXT) comm.$(OBJEXT) \ +- comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_3) \ +- disk.$(OBJEXT) $(am__objects_4) errorpage.$(OBJEXT) \ ++ comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \ ++ disk.$(OBJEXT) $(am__objects_6) errorpage.$(OBJEXT) \ + ETag.$(OBJEXT) event.$(OBJEXT) external_acl.$(OBJEXT) \ + fd.$(OBJEXT) filemap.$(OBJEXT) forward.$(OBJEXT) \ + fqdncache.$(OBJEXT) ftp.$(OBJEXT) gopher.$(OBJEXT) \ +- helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ ++ helper.$(OBJEXT) $(am__objects_7) http.$(OBJEXT) \ + HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_6) \ +- logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_8) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_9) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ + MemPool.$(OBJEXT) MemBuf.$(OBJEXT) mime.$(OBJEXT) \ + multicast.$(OBJEXT) neighbors.$(OBJEXT) net_db.$(OBJEXT) \ + Packer.$(OBJEXT) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \ + peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ +- ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_10) \ ++ ssl.$(OBJEXT) $(am__objects_11) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_12) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- whois.$(OBJEXT) $(am__objects_10) ++ whois.$(OBJEXT) $(am__objects_13) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -563,7 +571,9 @@ + @AMDEP_TRUE@ $(DEPDIR)/fqdncache.Po $(DEPDIR)/ftp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/globals.Po $(DEPDIR)/gopher.Po \ + @AMDEP_TRUE@ $(DEPDIR)/helper.Po $(DEPDIR)/htcp.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icmp.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icap_common.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_opt.Po $(DEPDIR)/icap_reqmod.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_respmod.Po $(DEPDIR)/icmp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/icp_v2.Po $(DEPDIR)/icp_v3.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ident.Po $(DEPDIR)/internal.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ipc.Po $(DEPDIR)/ipcache.Po \ +@@ -777,6 +787,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v3.Po@am__quote@ diff --git a/www/squid27/files/icap-2.5-core.patch b/www/squid27/files/icap-2.5-core.patch new file mode 100644 index 000000000000..22d209c18fb4 --- /dev/null +++ b/www/squid27/files/icap-2.5-core.patch @@ -0,0 +1,7059 @@ +Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch only contains the parts of the original patchset that +actually implement the ICAP client functionality. The updates to +the build infrastructure are omitted to avoid the need to run an +autotools bootstrap. Instead, we simulate said bootstrapping with +a second patch, icap-2.5-bootstrap.patch. + +The patchset was pulled from the project's CVS repository +at cvs.devel.squid-cache.org using + +cvs diff -u -b -N -kk -rs2_5 -ricap-2_5 + +See also +<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5> +for the "official" auto-generated patchset. + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2005-12-17 + +Index: errors/Bulgarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Bulgarian/ERR_ICAP_FAILURE +diff -N errors/Bulgarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Catalan/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Catalan/ERR_ICAP_FAILURE +diff -N errors/Catalan/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Czech/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Czech/ERR_ICAP_FAILURE +diff -N errors/Czech/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Danish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Danish/ERR_ICAP_FAILURE +diff -N errors/Danish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Dutch/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Dutch/ERR_ICAP_FAILURE +diff -N errors/Dutch/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/English/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/English/ERR_ICAP_FAILURE +diff -N errors/English/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Estonian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Estonian/ERR_ICAP_FAILURE +diff -N errors/Estonian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Finnish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Finnish/ERR_ICAP_FAILURE +diff -N errors/Finnish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/French/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/French/ERR_ICAP_FAILURE +diff -N errors/French/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/German/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/German/ERR_ICAP_FAILURE +diff -N errors/German/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2 +@@ -0,0 +1,33 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>FEHLER</H1> ++<H2>Der angeforderte URL konnte nicht geholt werden</H2> ++<HR noshade size="1px"> ++<P> ++Während des Versuches, den URL<BR> ++<A HREF="%U">%U</A> ++ ++<BR> ++zu laden, trat der folgende Fehler auf: ++<UL> ++<LI> ++<STRONG> ++ICAP-Protokollfehler ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: ++<UL> ++<LI>Nicht erreichbarer ICAP-Server ++<LI>Ungültige Antwort vom ICAP-Server ++ ++</UL> ++</P> ++ ++<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Greek/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Greek/ERR_ICAP_FAILURE +diff -N errors/Greek/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hebrew/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hebrew/ERR_ICAP_FAILURE +diff -N errors/Hebrew/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hungarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hungarian/ERR_ICAP_FAILURE +diff -N errors/Hungarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Italian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Italian/ERR_ICAP_FAILURE +diff -N errors/Italian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Japanese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Japanese/ERR_ICAP_FAILURE +diff -N errors/Japanese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Korean/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Korean/ERR_ICAP_FAILURE +diff -N errors/Korean/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Lithuanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Lithuanian/ERR_ICAP_FAILURE +diff -N errors/Lithuanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Polish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Polish/ERR_ICAP_FAILURE +diff -N errors/Polish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Portuguese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Portuguese/ERR_ICAP_FAILURE +diff -N errors/Portuguese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Romanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Romanian/ERR_ICAP_FAILURE +diff -N errors/Romanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-1251/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-1251/ERR_ICAP_FAILURE +diff -N errors/Russian-1251/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-koi8-r/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE +diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Serbian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Serbian/ERR_ICAP_FAILURE +diff -N errors/Serbian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Simplify_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE +diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Slovak/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Slovak/ERR_ICAP_FAILURE +diff -N errors/Slovak/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Spanish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Spanish/ERR_ICAP_FAILURE +diff -N errors/Spanish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Swedish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Swedish/ERR_ICAP_FAILURE +diff -N errors/Swedish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Traditional_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE +diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Turkish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Turkish/ERR_ICAP_FAILURE +diff -N errors/Turkish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.10 +retrieving revision 1.10.30.2 +diff -p -u -b -r1.10 -r1.10.30.2 +--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10 ++++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2 +@@ -132,4 +132,12 @@ double drand48(void); + */ + int statMemoryAccounted(void); + ++#ifndef HAVE_STRNSTR ++extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); ++#endif ++ ++#ifndef HAVE_STRCASESTR ++extern char *strcasestr(const char *haystack, const char *needle); ++#endif ++ + #endif /* SQUID_UTIL_H */ +Index: lib/Makefile.am +=================================================================== +RCS file: /cvsroot/squid/squid/lib/Makefile.am,v +retrieving revision 1.4 +retrieving revision 1.4.26.2 +diff -p -u -b -r1.4 -r1.4.26.2 +--- lib/Makefile.am 21 Nov 2001 23:48:57 -0000 1.4 ++++ lib/Makefile.am 6 Apr 2004 13:04:38 -0000 1.4.26.2 +@@ -8,6 +8,19 @@ SNPRINTFSOURCE=snprintf.c + else + SNPRINTFSOURCE= + endif ++ ++if NEED_OWN_STRNSTR ++STRNSTRSOURCE=strnstr.c ++else ++STRNSTRSOURCE= ++endif ++ ++if NEED_OWN_STRCASESTR ++STRCASESTRSOURCE=strcasestr.c ++else ++STRCASESTRSOURCE= ++endif ++ + if NEED_OWN_MD5 + MD5SOURCE=md5.c + else +@@ -43,6 +56,8 @@ libmiscutil_a_SOURCES = \ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +Index: lib/strcasestr.c +=================================================================== +RCS file: lib/strcasestr.c +diff -N lib/strcasestr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1 +@@ -0,0 +1,126 @@ ++/* Return the offset of one string within another. ++ Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* ++ * My personal strstr() implementation that beats most other algorithms. ++ * Until someone tells me otherwise, I assume that this is the ++ * fastest implementation of strstr() in C. ++ * I deliberately chose not to comment it. You should have at least ++ * as much fun trying to understand it, as I had to write it :-). ++ * ++ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ ++ ++/* ++ * modified to work outside of glibc (rhorstmann, 06/04/2004) ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRCASESTR ++#include <ctype.h> ++ ++typedef unsigned chartype; ++ ++char * ++strcasestr (phaystack, pneedle) ++ const char *phaystack; ++ const char *pneedle; ++{ ++ register const unsigned char *haystack, *needle; ++ register chartype b, c; ++ ++ haystack = (const unsigned char *) phaystack; ++ needle = (const unsigned char *) pneedle; ++ ++ b = tolower (*needle); ++ if (b != '\0') ++ { ++ haystack--; /* possible ANSI violation */ ++ do ++ { ++ c = *++haystack; ++ if (c == '\0') ++ goto ret0; ++ } ++ while (tolower (c) != (int) b); ++ ++ c = tolower (*++needle); ++ if (c == '\0') ++ goto foundneedle; ++ ++needle; ++ goto jin; ++ ++ for (;;) ++ { ++ register chartype a; ++ register const unsigned char *rhaystack, *rneedle; ++ ++ do ++ { ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ if (tolower (a) == (int) b) ++ break; ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++shloop: ++ ; ++ } ++ while (tolower (a) != (int) b); ++ ++jin: a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ ++ if (tolower (a) != (int) c) ++ goto shloop; ++ ++ rhaystack = haystack-- + 1; ++ rneedle = needle; ++ a = tolower (*rneedle); ++ ++ if (tolower (*rhaystack) == (int) a) ++ do ++ { ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ if (tolower (*rhaystack) != (int) a) ++ break; ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ } ++ while (tolower (*rhaystack) == (int) a); ++ ++ needle = rneedle; /* took the register-poor approach */ ++ ++ if (a == '\0') ++ break; ++ } ++ } ++foundneedle: ++ return (char*) haystack; ++ret0: ++ return 0; ++} ++#endif +Index: lib/strnstr.c +=================================================================== +RCS file: lib/strnstr.c +diff -N lib/strnstr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include <string.h> ++#include <util.h> ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.5.30.3 +retrieving revision 1.5.44.8 +diff -p -u -b -r1.5.30.3 -r1.5.44.8 +--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3 ++++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8 +@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb) + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); + } ++ ++int ++memBufRead(int fd, MemBuf * mb) ++{ ++ int len; ++ if (mb->capacity == mb->size) ++ memBufGrow(mb, SQUID_TCP_SO_RCVBUF); ++ len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); ++ if (len) ++ mb->size += len; ++ return len; ++} +Index: src/cache_cf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cache_cf.c,v +retrieving revision 1.38.6.29 +retrieving revision 1.38.6.11.2.22 +diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22 +--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29 ++++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22 +@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli + return bodylist.head == NULL; + } + ++#ifdef HS_FEAT_ICAP ++ ++/*************************************************** ++ * prototypes ++ */ ++static int icap_service_process(icap_service * s); ++static void icap_service_init(icap_service * s); ++static void icap_service_destroy(icap_service * s); ++icap_service *icap_service_lookup(char *name); ++static int icap_class_process(icap_class * c); ++static void icap_class_destroy(icap_class * c); ++static void icap_access_destroy(icap_access * a); ++static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access <servicename> {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.14.6.1 +retrieving revision 1.14.32.2 +diff -p -u -b -r1.14.6.1 -r1.14.32.2 +--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1 ++++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2 +@@ -144,6 +144,10 @@ cbdataInit(void) + CREATE_CBDATA(statefulhelper); + CREATE_CBDATA(helper_stateful_server); + CREATE_CBDATA(HttpStateData); ++#ifdef HS_FEAT_ICAP ++ CREATE_CBDATA(IcapStateData); ++ CREATE_CBDATA(icap_service); ++#endif + CREATE_CBDATA_FREE(peer, peerDestroy); + CREATE_CBDATA(ps_state); + CREATE_CBDATA(RemovalPolicy); +Index: src/cf.data.pre +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf.data.pre,v +retrieving revision 1.49.2.84 +retrieving revision 1.49.2.33.2.32 +diff -p -u -b -r1.49.2.84 -r1.49.2.33.2.32 +--- src/cf.data.pre 21 Oct 2005 02:13:47 -0000 1.49.2.84 ++++ src/cf.data.pre 24 Oct 2005 17:07:42 -0000 1.49.2.33.2.32 +@@ -2397,7 +2397,6 @@ DOC_START + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -2716,6 +2715,177 @@ DOC_START + DOC_END + + COMMENT_START ++ ICAP OPTIONS ++ ----------------------------------------------------------------------------- ++COMMENT_END ++ ++NAME: icap_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.onoff ++DEFAULT: off ++DOC_START ++ If you want to enable the ICAP client module, set this to on. ++DOC_END ++ ++NAME: icap_preview_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.preview_enable ++DEFAULT: off ++DOC_START ++ Set this to 'on' if you want to enable the ICAP preview ++ feature in Squid. ++DOC_END ++ ++NAME: icap_preview_size ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.preview_size ++DEFAULT: -1 ++DOC_START ++ The default size of preview data to be sent to the ICAP server. ++ -1 means no preview. This value might be overwritten on a per server ++ basis by OPTIONS requests. ++DOC_END ++ ++NAME: icap_check_interval ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.check_interval ++DEFAULT: 300 ++DOC_START ++ If an ICAP server does not respond, it gets marked as unreachable. Squid ++ will try again to reach it after this time. ++DOC_END ++ ++NAME: icap_send_client_ip ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_client_ip ++DEFAULT: off ++DOC_START ++ This adds the header "X-Client-IP" to ICAP requests. Can also be ++ set from the server's response to OPTIONS. ++DOC_END ++ ++NAME: icap_send_auth_user ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_auth_user ++DEFAULT: off ++DOC_START ++ This adds the header "X-Authenticated-User" to ICAP requests ++ if proxy access is authentified. Can also be set from the server's ++ response to OPTIONS. ++DOC_END ++ ++NAME: icap_auth_scheme ++TYPE: string ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.auth_scheme ++DEFAULT: Local://%u ++DOC_START ++ Authentification scheme to pass to ICAP requests if ++ icap_send_auth_user is enabled. The first occurence of "%u" ++ is replaced by the authentified user name. If no "%u" is found, ++ the username is added at the end of the scheme. ++ ++ See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, ++ section 3.4 for details on this. ++ ++ Examples: ++ ++ icap_auth_scheme Local://%u ++ icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com ++ icap_auth_scheme WinNT://nt-domain/%u ++ icap_auth_scheme Radius://radius-server/%u ++DOC_END ++ ++NAME: icap_service ++TYPE: icap_service_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines a single ICAP service ++ ++ icap_service servicename vectoring_point bypass service_url [options ...] ++ ++ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache ++ This specifies at which point of request processing the ICAP ++ service should be plugged in. ++ bypass = 1|0 ++ If set to 1 and the ICAP server cannot be reached, the request will go ++ through without being processed by an ICAP server ++ service_url = icap://servername:port/service ++ ++ Options: ++ ++ no-keep-alive To always close the connection to icap server ++ after the transaction completes ++ ++ ++ Note: reqmod_precache and respmod_postcache is not yet implemented ++ ++ Load-balancing and high availability: ++ You can obtain load-balancing and high availability by defining a ++ named service with different definitions. Then, the client ++ loops through the different entities of the service providing ++ load-balancing. If an entity is marked as unreachable, the client goes ++ one step further to the next entity: you have the high-availability. ++ See the service_1 definition below ++ ++Example: ++icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod ++icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive ++icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod ++DOC_END ++ ++NAME: icap_class ++TYPE: icap_class_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines an ICAP service chain. If there are multiple services per ++ vectoring point, they are processed in the specified order. ++ ++ icap_class classname servicename... ++ ++Example: ++icap_class class_1 service_1 service_2 ++icap class class_2 service_1 service_3 ++DOC_END ++ ++NAME: icap_access ++TYPE: icap_access_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Redirects a request through an ICAP service class, depending ++ on given acls ++ ++ icap_access classname allow|deny [!]aclname... ++ ++ The icap_access statements are processed in the order they appear in ++ this configuration file. If an access list matches, the processing stops. ++ For an "allow" rule, the specified class is used for the request. A "deny" ++ rule simply stops processing without using the class. You can also use the ++ special classname "None". ++ ++ For backward compatibility, it is also possible to use services ++ directly here. ++Example: ++icap_access class_1 allow all ++DOC_END ++ ++COMMENT_START + MISCELLANEOUS + ----------------------------------------------------------------------------- + COMMENT_END +Index: src/cf_gen_defines +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v +retrieving revision 1.5 +retrieving revision 1.5.48.3 +diff -p -u -b -r1.5 -r1.5.48.3 +--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5 ++++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3 +@@ -18,12 +18,13 @@ BEGIN { + define["USE_UNLINKD"]="--enable-unlinkd" + define["USE_USERAGENT_LOG"]="--enable-useragent-log" + define["USE_WCCP"]="--enable-wccp" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +- DEFINE=define[$2] ++ DEFINE = define[$2] + else +- DEFINE="-D" $2 ++ DEFINE = "-D" $2 + print "{\"" $2 "\", \"" DEFINE "\", " + print "#if " $2 + print "1" +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.47.2.71 +retrieving revision 1.47.2.28.2.40 +diff -p -u -b -r1.47.2.71 -r1.47.2.28.2.40 +--- src/client_side.c 19 Oct 2005 02:13:20 -0000 1.47.2.71 ++++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40 +@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n"; + static CWCB clientWriteComplete; + static CWCB clientWriteBodyComplete; + static PF clientReadRequest; +-static PF connStateFree; ++PF connStateFree; + static PF requestTimeout; + static PF clientLifetimeTimeout; + static int clientCheckTransferDone(clientHttpRequest *); +@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien + static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); + static void clientPackTermBound(String boundary, MemBuf * mb); + static void clientInterpretRequestHeaders(clientHttpRequest *); +-static void clientProcessRequest(clientHttpRequest *); ++void clientProcessRequest(clientHttpRequest *); + static void clientProcessExpired(void *data); + static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); +-static int clientCachable(clientHttpRequest * http); +-static int clientHierarchical(clientHttpRequest * http); +-static int clientCheckContentLength(request_t * r); ++int clientCachable(clientHttpRequest * http); ++int clientHierarchical(clientHttpRequest * http); ++int clientCheckContentLength(request_t * r); + static DEFER httpAcceptDefer; + static log_type clientProcessRequest2(clientHttpRequest * http); + static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); + static int clientRequestBodyTooLarge(squid_off_t clen); + static void clientProcessBody(ConnStateData * conn); + static void clientEatRequestBody(clientHttpRequest *); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + static int + checkAccelOnly(clientHttpRequest * http) +@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res + http->request = requestLink(new_request); + } + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -931,11 +938,22 @@ httpRequestFree(void *data) + *H = http->next; + http->next = NULL; + dlinkDelete(&http->active, &ClientActiveRequests); ++#if HS_FEAT_ICAP ++ /*In the case that the upload of data breaks, we need this code here .... */ ++ if (NULL != http->icap_reqmod) { ++ if (cbdataValid(http->icap_reqmod)) ++ if (http->icap_reqmod->icap_fd > -1) { ++ comm_close(http->icap_reqmod->icap_fd); ++ } ++ cbdataUnlock(http->icap_reqmod); ++ http->icap_reqmod = NULL; ++ } ++#endif + cbdataFree(http); + } + + /* This is a handler normally called by comm_close() */ +-static void ++void + connStateFree(int fd, void *data) + { + ConnStateData *connState = data; +@@ -958,7 +976,6 @@ connStateFree(int fd, void *data) + } else + safe_free(connState->in.buf); + /* XXX account connState->in.buf */ +- pconnHistCount(0, connState->nrequests); + cbdataFree(connState); + #ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ +@@ -1103,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1122,7 +1139,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1148,7 +1165,7 @@ clientCachable(clientHttpRequest * http) + } + + /* Return true if we can query our neighbors for this object */ +-static int ++int + clientHierarchical(clientHttpRequest * http) + { + const char *url = http->uri; +@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -2449,6 +2466,11 @@ clientProcessRequest(clientHttpRequest * + debug(33, 4) ("clientProcessRequest: %s '%s'\n", + RequestMethodStr[r->method], + url); ++#if HS_FEAT_ICAP ++ if (clientIcapReqMod(http)) { ++ return; ++ } ++#endif + if (r->method == METHOD_CONNECT && !http->redirect.status) { + http->log_type = LOG_TCP_MISS; + sslStart(http, &http->out.size, &http->al.http.code); +@@ -2993,6 +3015,20 @@ clientReadRequest(int fd, void *data) + (long) conn->in.offset, (long) conn->in.size); + len = conn->in.size - conn->in.offset - 1; + } ++#if HS_FEAT_ICAP ++ /* ++ * This check exists because ICAP doesn't always work well ++ * with persistent (reused) connections. One version of the ++ * REQMOD code creates a fake ConnStateData, which doesn't have ++ * an in.buf. We want to make sure that the fake ConnStateData ++ * doesn't get used here. ++ */ ++ if (NULL == conn->in.buf) { ++ debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); ++ comm_close(fd); ++ return; ++ } ++#endif + statCounter.syscalls.sock.reads++; + size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); + if (size > 0) { +@@ -3096,7 +3132,8 @@ clientReadRequest(int fd, void *data) + /* add to the client request queue */ + for (H = &conn->chr; *H; H = &(*H)->next); + *H = http; +- conn->nrequests++; ++ F->pconn.uses++; ++ F->pconn.type = 0; + /* + * I wanted to lock 'http' here since its callback data for + * clientLifetimeTimeout(), but there's no logical place to +@@ -3266,7 +3303,7 @@ clientReadRequest(int fd, void *data) + } + + /* file_read like function, for reading body content */ +-static void ++void + clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) + { + ConnStateData *conn = request->body_reader_data; +@@ -3390,7 +3427,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data) + * Some data has been sent to the client, just close the FD + */ + comm_close(fd); +- } else if (conn->nrequests) { ++ } else if (fd_table[fd].pconn.uses) { + /* + * assume its a persistent connection; just close it + */ +@@ -3948,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re + } + } + } ++ ++#if HS_FEAT_ICAP ++static int ++clientIcapReqMod(clientHttpRequest * http) ++{ ++ ErrorState *err; ++ icap_service *service; ++ if (http->flags.did_icap_reqmod) ++ return 0; ++ if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) ++ return 0; ++ debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); ++ /* ++ * Note, we pass 'start' and 'log_addr' to ICAP so the access.log ++ * entry comes out right. The 'clientHttpRequest' created by ++ * the ICAP side is the one that gets logged. The first ++ * 'clientHttpRequest' does not get logged because its out.size ++ * is zero and log_type is unset. ++ */ ++ http->icap_reqmod = icapReqModStart(service, ++ http->uri, ++ http->request, ++ http->conn->fd, ++ http->start, ++ http->conn->log_addr, ++ (void *) http->conn); ++ if (NULL == http->icap_reqmod) { ++ return 0; ++ } else if (-1 == (int) http->icap_reqmod) { ++ /* produce error */ ++ http->icap_reqmod = NULL; ++ debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); ++ http->log_type = LOG_TCP_DENIED; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = ETIMEDOUT; ++ err->request = requestLink(http->request); ++ err->src_addr = http->conn->peer.sin_addr; ++ http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return 1; ++ } ++ cbdataLock(http->icap_reqmod); ++ http->flags.did_icap_reqmod = 1; ++ return 1; ++} ++#endif +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.18.6.6 +retrieving revision 1.18.6.2.12.9 +diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9 +--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6 ++++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9 +@@ -653,8 +653,8 @@ comm_close(int fd) + #endif + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + SSL_free(F->ssl); +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.29.2.18 +retrieving revision 1.29.2.8.2.17 +diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17 +--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18 ++++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17 +@@ -93,6 +93,7 @@ typedef enum { + ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ + ERR_TOO_BIG, + TCP_RESET, ++ ERR_ICAP_FAILURE, + ERR_INVALID_RESP, + ERR_MAX + } err_type; +@@ -438,6 +439,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -610,6 +614,12 @@ typedef enum { + MEM_TLV, + MEM_SWAP_LOG_DATA, + MEM_CLIENT_REQ_BUF, ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -709,9 +719,14 @@ typedef enum { + CBDATA_RemovalPolicyWalker, + CBDATA_RemovalPurgeWalker, + CBDATA_store_client, ++#ifdef HS_FEAT_ICAP ++ CBDATA_IcapStateData, ++ CBDATA_icap_service, ++#endif + CBDATA_FIRST_CUSTOM_TYPE = 1000 + } cbdata_type; + ++ + /* + * Return codes from checkVary(request) + */ +@@ -742,4 +757,68 @@ enum { + + #endif + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.13.6.15 +retrieving revision 1.13.6.3.2.15 +diff -p -u -b -r1.13.6.15 -r1.13.6.3.2.15 +--- src/forward.c 2 Sep 2005 02:13:43 -0000 1.13.6.15 ++++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15 +@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status + else + hierarchyNote(&fwdState->request->hier, fs->code, request->host); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; + if (fs->peer) + peerConnectSucceded(fs->peer); + fwdDispatch(fwdState); +@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data) + void + fwdFail(FwdState * fwdState, ErrorState * errorState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", + err_type_str[errorState->type], + httpStatusString(errorState->http_status), +@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data) + void + fwdUnregister(int fd, FwdState * fwdState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); + assert(fd == fwdState->server_fd); + assert(fd > -1); +@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat + void + fwdComplete(FwdState * fwdState) + { +- StoreEntry *e = fwdState->entry; ++ StoreEntry *e; ++ if (NULL == fwdState) ++ return; ++ e = fwdState->entry; + assert(e->store_status == STORE_PENDING); + debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), + e->mem_obj->reply->sline.status); +Index: src/globals.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/globals.h,v +retrieving revision 1.14.6.7 +retrieving revision 1.14.6.3.2.5 +diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5 +--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7 ++++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5 +@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */ + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.17.6.32 +retrieving revision 1.17.6.3.6.39 +diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.39 +--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32 ++++ src/http.c 23 Nov 2005 20:33:07 -0000 1.17.6.3.6.39 +@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry; + + static PF httpReadReply; + static void httpSendRequest(HttpStateData *); +-static PF httpStateFree; ++PF httpStateFree; + static PF httpTimeout; + static void httpCacheNegatively(StoreEntry *); + static void httpMakePrivate(StoreEntry *); +@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *) + static int httpCachableReply(HttpStateData *); + static void httpMaybeRemovePublic(StoreEntry *, http_status); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data) + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; ++#if HS_FEAT_ICAP ++ cbdataUnlock(httpState->icap_writer); ++#endif + cbdataFree(httpState); + } + +@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht + } + + /* rewrite this later using new interfaces @?@ */ +-static void ++void + httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) + { + StoreEntry *entry = httpState->entry; +@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; ++ squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ +- if (httpState->reply_hdr_state < 2) ++ if (httpState->reply_hdr_state < 2) { ++ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", ++ httpState->reply_hdr_state); + return 0; ++ } + clen = httpReplyBodySize(httpState->request->method, reply); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ content_bytes_read = httpState->icap_writer->fake_content_length; ++ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); ++ } else ++#endif ++ content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ +- if (mem->inmem_hi > clen + reply->hdr_sz) ++ if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ +- if (mem->inmem_hi < clen + reply->hdr_sz) ++ if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data) + delay_id delay_id; + #endif + ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ /*The folowing entry can not be marked as aborted. ++ * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; +@@ -579,6 +605,33 @@ httpReadReply(int fd, void *data) + else + delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); + #endif ++ ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ IcapStateData *icap = httpState->icap_writer; ++ /* ++ * Ok we have a received a response from the web server, so try to ++ * connect the icap server if it's the first attemps. If we try ++ * to connect to the icap server, defer this request (do not read ++ * the buffer), and defer until icapConnectOver() is not called. ++ */ ++ if (icap->flags.connect_requested == 0) { ++ debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); ++ if (!icapConnect(icap, icapConnectOver)) { ++ debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); ++ icap->flags.connect_requested = 1; ++ /* Wait for more data or EOF condition */ ++ commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ } ++#endif ++ + errno = 0; + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, read_sz); +@@ -595,7 +648,13 @@ httpReadReply(int fd, void *data) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +- if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ (void) 0; ++ else ++#endif ++ ++ if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { + /* Skip whitespace */ + while (len > 0 && xisspace(*buf)) + xmemmove(buf, buf + 1, len--); +@@ -625,6 +684,12 @@ httpReadReply(int fd, void *data) + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { ++ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); ++ icapSendRespMod(httpState->icap_writer, buf, len, 1); ++ } ++#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we +@@ -677,7 +742,28 @@ httpReadReply(int fd, void *data) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ if (cbdataValid(httpState->icap_writer)) { ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } ++ } else ++#endif + storeAppend(entry, buf, len); ++ ++ ++ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + /* + * the above storeAppend() call could ABORT this entry, +@@ -724,10 +810,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); +@@ -766,6 +863,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -776,6 +877,34 @@ httpReadReply(int fd, void *data) + } + } + ++#ifdef HS_FEAT_ICAP ++static int ++httpReadReplyWaitForIcap(int fd, void *data) ++{ ++ HttpStateData *httpState = data; ++ if (NULL == httpState->icap_writer) ++ return 0; ++ /* ++ * Do not defer when we are not connected to the icap server. ++ * Defer when the icap server connection is not established but pending ++ * Defer when the icap server is busy (writing on the socket) ++ */ ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", ++ fd, httpState->icap_writer->flags.connect_requested); ++ if (!httpState->icap_writer->flags.connect_requested) ++ return 0; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", ++ fd, httpState->icap_writer->flags.connect_pending); ++ if (httpState->icap_writer->flags.connect_pending) ++ return 1; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", ++ fd, httpState->icap_writer->flags.write_pending); ++ if (httpState->icap_writer->flags.write_pending) ++ return 1; ++ return 0; ++} ++#endif ++ + /* This will be called when request write is complete. Schedule read of + * reply. */ + static void +@@ -803,6 +932,63 @@ httpSendComplete(int fd, char *bufnotuse + comm_close(fd); + return; + } else { ++ /* Schedule read reply. */ ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart( ++ ICAP_SERVICE_RESPMOD_PRECACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (-1 == (int) httpState->icap_writer) { ++ /* TODO: send error here and exit */ ++ ErrorState *err; ++ httpState->icap_writer = 0; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(httpState->orig_request); ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ return; ++ } else if (httpState->icap_writer) { ++ request_flags fake_flags = httpState->orig_request->flags; ++ method_t fake_method = entry->mem_obj->method; ++ const char *fake_msg = "this is a fake entry for " ++ " response sent to an ICAP RESPMOD server"; ++ cbdataLock(httpState->icap_writer); ++ /* ++ * this httpState will give the data it reads to ++ * the icap server, rather than put it into ++ * a StoreEntry ++ */ ++ storeUnlockObject(httpState->entry); ++ storeUnregisterAbort(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -811,8 +997,18 @@ httpSendComplete(int fd, char *bufnotuse + * the timeout for POST/PUT requests that have very large + * request bodies. + */ ++ ++ /* removed in stable5: ++ * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ */ + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +- commSetDefer(fd, fwdCheckDeferRead, entry); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); ++ commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); ++ } else ++#endif ++ commSetDefer(httpState->fd, fwdCheckDeferRead, entry); + } + httpState->flags.request_sent = 1; + } +@@ -1010,8 +1206,11 @@ httpBuildRequestHeader(request_t * reque + if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { + const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); + httpHdrCcSetMaxAge(cc, getMaxAge(url)); ++#ifndef HS_FEAT_ICAP ++ /* Don;t bother - if the url you want to cache is redirected? */ + if (strLen(request->urlpath)) + assert(strstr(url, strBuf(request->urlpath))); ++#endif + } + /* Set no-cache if determined needed but not found */ + if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) +@@ -1119,6 +1318,7 @@ httpStart(FwdState * fwd) + int fd = fwd->server_fd; + HttpStateData *httpState; + request_t *proxy_req; ++ /* ErrorState *err; */ + request_t *orig_req = fwd->request; + debug(11, 3) ("httpStart: \"%s %s\"\n", + RequestMethodStr[orig_req->method], +@@ -1156,12 +1356,22 @@ httpStart(FwdState * fwd) + httpState->request = requestLink(orig_req); + httpState->orig_request = requestLink(orig_req); + } ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (httpState->icap_writer) { ++ return; ++ } ++ } ++#endif + /* + * register the handler to free HTTP state data when the FD closes + */ + comm_add_close_handler(fd, httpStateFree, httpState); + statCounter.server.all.requests++; + statCounter.server.http.requests++; ++ + httpSendRequest(httpState); + /* + * We used to set the read timeout here, but not any more. +Index: src/icap_common.c +=================================================================== +RCS file: src/icap_common.c +diff -N src/icap_common.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_common.c 22 Nov 2005 22:41:48 -0000 1.1.2.39 +@@ -0,0 +1,785 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} +Index: src/icap_opt.c +=================================================================== +RCS file: src/icap_opt.c +diff -N src/icap_opt.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_opt.c 22 Nov 2005 22:41:48 -0000 1.1.2.17 +@@ -0,0 +1,519 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: src/icap_reqmod.c +=================================================================== +RCS file: src/icap_reqmod.c +diff -N src/icap_reqmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_reqmod.c 6 Dec 2005 21:53:44 -0000 1.1.2.58 +@@ -0,0 +1,976 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++#define ICAP_PROXY_KEEP_ALIVE 0 ++ ++/* ++ * These once-static functions are required to be global for ICAP ++ */ ++ ++PF clientReadRequest; ++PF connStateFree; ++int clientReadDefer(int fd, void *data); ++int clientCheckContentLength(request_t * r); ++void clientProcessRequest(clientHttpRequest *); ++int clientCachable(clientHttpRequest *); ++int clientHierarchical(clientHttpRequest *); ++void clientReadBody(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++ ++static PF icapReqModReadHttpHdrs; ++static PF icapReqModReadHttpBody; ++static CWCB icapReqModSendBodyChunk; ++static CBCB icapReqModBodyHandler; ++static BODY_HANDLER icapReqModBodyReader; ++static STRCB icapReqModMemBufAppend; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++static const char *crlf = "\r\n"; ++ ++/* ++ * icapExpectedHttpReqHdrSize ++ * ++ * calculate the size of the HTTP headers that we expect ++ * to read from the ICAP server. ++ */ ++static int ++icapExpectedHttpReqHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) ++ return (icap->enc.req_body - icap->enc.req_hdr); ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ fatal("icapExpectedHttpReqHdrSize: unexpected case"); ++ return 0; ++} ++ ++/* ++ * icapReqModCreateClientState ++ * ++ * Creates fake client_side data structures so we can use ++ * that module to read/parse the HTTP request that we read ++ * from the ICAP server. ++ */ ++static clientHttpRequest * ++icapReqModCreateClientState(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http; ++ if (!cbdataValid(icap->reqmod.client_cookie)) { ++ debug(81, 3) ("Whups, client cookie invalid\n"); ++ icap->reqmod.client_fd = -1; ++ return NULL; ++ } ++ http = cbdataAlloc(clientHttpRequest); ++ /* ++ * use our own urlCanonicalClean here, because urlCanonicalClean ++ * may strip everything after a question-mark. As http->uri ++ * is used when doing a request to a parent proxy, we need the full ++ * url here. ++ */ ++ http->uri = xstrdup(urlCanonical(icap->request)); ++ http->log_uri = xstrndup(http->uri, MAX_URL); ++ http->range_iter.boundary = StringNull; ++ http->request = requestLink(request ? request : icap->request); ++ http->flags.did_icap_reqmod = 1; ++ http->start = icap->reqmod.start; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ ++ http->conn->chr = http; ++ ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ err->request = requestLink(request); ++ request->flags.proxy_keepalive = 0; ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ if (!clientCheckContentLength(request)) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED); ++ err->request = requestLink(request); ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ /* Do we expect a request-body? */ ++ if (request->content_length > 0) { ++ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); ++ if (request->body_reader_data) ++ cbdataUnlock(request->body_reader_data); ++ request->body_reader = icapReqModBodyReader; ++ request->body_reader_data = icap; /* XXX cbdataLock? */ ++ cbdataLock(icap); /*Yes sure ..... */ ++ memBufDefInit(&icap->reqmod.http_entity.buf); ++ } ++ if (clientCachable(http)) ++ request->flags.cachable = 1; ++ if (clientHierarchical(http)) ++ request->flags.hierarchical = 1; ++ clientProcessRequest(http); ++} ++ ++/* ++ * icapReqModParseHttpError ++ * ++ * Handle an error when parsing the new HTTP request we read ++ * from the ICAP server. ++ */ ++static void ++icapReqModParseHttpError(IcapStateData * icap, const char *reason) ++{ ++ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); ++} ++ ++/* ++ * icapEntryError ++ * ++ * A wrapper for errorCon() and errorAppendEntry(). ++ */ ++static void ++icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) ++{ ++ ErrorState *err; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, null_request_flags); ++ err = errorCon(et, hs); ++ err->xerrno = xerrno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(http->entry, err); ++} ++ ++/* ++ * icapReqModParseHttpRequest ++ * ++ * Parse the HTTP request that we read from the ICAP server. ++ * Creates and fills in the request_t structure. ++ */ ++static void ++icapReqModParseHttpRequest(IcapStateData * icap) ++{ ++ char *mstr; ++ char *uri; ++ char *inbuf; ++ char *t; ++ char *token; ++ char *headers; ++ method_t method; ++ request_t *request; ++ http_version_t http_ver; ++ int reqlen = icap->reqmod.hdr_buf.size; ++ int hdrlen; ++ ++ /* ++ * Lazy, make a copy of the buf so I can chop it up with strtok() ++ */ ++ inbuf = xcalloc(reqlen + 1, 1); ++ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); ++ ++ if ((mstr = strtok(inbuf, "\t ")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); ++ icapReqModParseHttpError(icap, "error:invalid-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ method = urlParseMethod(mstr); ++ if (method == METHOD_NONE) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", ++ mstr); ++ icapReqModParseHttpError(icap, "error:unsupported-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ /* look for URL+HTTP/x.x */ ++ if ((uri = strtok(NULL, "\n")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); ++ icapReqModParseHttpError(icap, "error:missing-url"); ++ xfree(inbuf); ++ return; ++ } ++ while (xisspace(*uri)) ++ uri++; ++ t = uri + strlen(uri); ++ assert(*t == '\0'); ++ token = NULL; ++ while (t > uri) { ++ t--; ++ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { ++ token = t + 1; ++ break; ++ } ++ } ++ while (t > uri && xisspace(*t)) ++ *(t--) = '\0'; ++ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); ++ if (token == NULL) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); ++ icapReqModParseHttpError(icap, "error:missing-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); ++ icapReqModParseHttpError(icap, "error:invalid-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", ++ http_ver.major, http_ver.minor); ++ ++ headers = strtok(NULL, null_string); ++ hdrlen = inbuf + reqlen - headers; ++ ++ if ((request = urlParse(method, uri)) == NULL) { ++ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ /* compile headers */ ++ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { ++ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", ++ uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, ++ 3) ++ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); ++ request->http_ver = http_ver; ++ request->client_addr = icap->request->client_addr; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request != NULL) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service *service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->reqmod.http_entity.bytes_read >= icap->request->content_length) ++ icap->flags.reqmod_http_entity_eof = 1; ++ ++ if (!icap->flags.reqmod_http_entity_eof) ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ /* ++ * Notify the other side if it is waiting for data from us ++ */ ++ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.callback); ++ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.callback && icap->reqmod.http_entity.buf.size) { ++ icapReqModPassHttpBody(icap, ++ icap->reqmod.http_entity.callback_buf, ++ icap->reqmod.http_entity.callback_bufsize, ++ icap->reqmod.http_entity.callback, ++ icap->reqmod.http_entity.callback_data); ++ icap->reqmod.http_entity.callback = NULL; ++ cbdataUnlock(icap->reqmod.http_entity.callback_data); ++ ++ } ++} ++ ++/* ++ * icapReqModPassHttpBody ++ * ++ * Called from http.c after request headers have been sent. ++ * This function feeds the http.c module chunks of the request ++ * body that were stored in the http_entity.buf MemBuf. ++ */ ++static void ++icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ debug(81, 3) ("icapReqModPassHttpBody: called\n"); ++ if (!buf) { ++ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", ++ icap->icap_fd, buf, (int) size, cbdata); ++ comm_close(icap->icap_fd); ++ return; ++ } ++ if (!cbdataValid(cbdata)) { ++ debug(81, ++ 1) ++ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", ++ icap->icap_fd); ++ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ ++ /*icapReqModKeepAliveOrClose(icap); */ ++ return; ++ } ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.buf.size) { ++ int copy_sz = icap->reqmod.http_entity.buf.size; ++ if (copy_sz > size) ++ copy_sz = size; ++ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); ++ /* XXX don't let Alex see this ugliness */ ++ xmemmove(icap->reqmod.http_entity.buf.buf, ++ icap->reqmod.http_entity.buf.buf + copy_sz, ++ icap->reqmod.http_entity.buf.size - copy_sz); ++ icap->reqmod.http_entity.buf.size -= copy_sz; ++ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", ++ copy_sz); ++ callback(buf, copy_sz, cbdata); ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ return; ++ } ++ if (icap->flags.reqmod_http_entity_eof) { ++ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); ++ callback(buf, 0, cbdata); ++ icapReqModKeepAliveOrClose(icap); ++ return; ++ } ++ /* ++ * We have no data for the other side at this point. Save all ++ * these values and use them when we do have data. ++ */ ++ assert(NULL == icap->reqmod.http_entity.callback); ++ icap->reqmod.http_entity.callback = callback; ++ icap->reqmod.http_entity.callback_data = cbdata; ++ icap->reqmod.http_entity.callback_buf = buf; ++ icap->reqmod.http_entity.callback_bufsize = size; ++ cbdataLock(icap->reqmod.http_entity.callback_data); ++} ++ ++/* ++ * Body reader handler for use with request->body_reader function ++ * Simple a wrapper for icapReqModPassHttpBody function ++ */ ++ ++static void ++icapReqModBodyReader(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ IcapStateData *icap = request->body_reader_data; ++ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); ++} ++ ++/* ++ * icapReqModMemBufAppend ++ * ++ * stupid wrapper to eliminate compiler warnings ++ */ ++static void ++icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) ++{ ++ memBufAppend(data, buf, size); ++} +Index: src/icap_respmod.c +=================================================================== +RCS file: src/icap_respmod.c +diff -N src/icap_respmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_respmod.c 23 Nov 2005 20:34:34 -0000 1.1.2.60 +@@ -0,0 +1,1039 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ ++ memBufPrintf(mb, crlf); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++ if (service->flags.allow_204) ++ memBufPrintf(mb, "Allow: 204\r\n"); ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ } ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ /* No data sent yet. Start with headers */ ++ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { ++ buf += icap->sc; ++ len -= icap->sc; ++ } ++ /* ++ * Then we do not have http responce headers. All data (previous and those in buf) ++ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... ++ */ ++ if (icap->sc < 0) { ++ memBufAppend(&icap->respmod.buffer, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->sc = icap->respmod.req_hdr_copy.size; ++ icap->respmod.req_hdr_copy.size = 0; ++ buf = NULL; ++ len = 0; ++ } ++ } ++ if (0 == icap->sc) { ++ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ ++ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); ++ memBufClean(&mb); ++ return; ++ } ++#if ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ ++ icap->flags.preview_done = 1; ++ ++ if (!icap->flags.preview_done) { ++ /* preview not yet sent */ ++ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size ++ && len > 0) { ++ /* Try to collect at least preview_size+1 bytes */ ++ /* By collecting one more byte than needed for preview we know best */ ++ /* whether we have to send the ieof chunk extension */ ++ size = icap->respmod.buffer.size + len; ++ if (size > preview_size + 1) ++ size = preview_size + 1; ++ size -= icap->respmod.buffer.size; ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", ++ icap->icap_fd, size); ++ memBufAppend(&icap->respmod.buffer, buf, size); ++ buf = ((char *) buf) + size; ++ len -= size; ++ } ++ if (icap->respmod.buffer.size > preview_size || theEnd) { ++ /* we got enough bytes for preview or this is the last call */ ++ /* add preview preview now */ ++ if (icap->respmod.buffer.size > 0) { ++ size = icap->respmod.buffer.size; ++ if (size > preview_size) ++ size = preview_size; ++ memBufPrintf(&mb, "%x\r\n", size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += size; ++ } ++ if (icap->respmod.buffer.size <= preview_size) { ++ /* content length is less than preview size+1 */ ++ if (icap->respmod.res_body_sz) ++ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ } else { ++ char ch; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ /* end of preview, wait for continue or 204 signal */ ++ /* copy the extra byte and all other data to the icap buffer */ ++ /* so that it can be handled next time */ ++ ch = icap->respmod.buffer.buf[preview_size]; ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ memBufAppend(&icap->respmod.buffer, &ch, 1); ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", ++ icap->icap_fd, len + 1); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ } ++ icap->flags.preview_done = 1; ++ icap->flags.wait_for_preview_reply = 1; ++ } ++ } else if (icap->flags.wait_for_preview_reply) { ++ /* received new data while waiting for preview response */ ++ /* add data to internal buffer and send later */ ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", ++ icap->icap_fd, len); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ /* do not send any data now while waiting for preview response */ ++ /* but prepare for read more data on the HTTP connection */ ++ memBufClean(&mb); ++ return; ++ } else ++#endif ++ { ++ /* after preview completed and ICAP preview response received */ ++ /* there may still be some data in the buffer */ ++ if (icap->respmod.buffer.size > 0) { ++ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, ++ icap->respmod.buffer.size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += icap->respmod.buffer.size; ++ memBufReset(&icap->respmod.buffer); ++ } ++ if (len > 0) { ++ memBufPrintf(&mb, "%x\r\n", len); ++ memBufAppend(&mb, buf, len); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += len; ++ } ++ if (icap->flags.send_zero_chunk) { ++ /* send zero end chunk */ ++ icap->flags.send_zero_chunk = 0; ++ icap->flags.http_server_eof = 1; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ } ++ /* wait for data coming from ICAP server as soon as we sent something */ ++ /* but of course only until we got the response header */ ++ if (!icap->flags.got_reply) ++ icap->flags.wait_for_reply = 1; ++ } ++ commSetTimeout(icap->icap_fd, -1, NULL, NULL); ++ ++ if (!mb.size) { ++ memBufClean(&mb); ++ return; ++ } ++ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ icap->flags.write_pending = 1; ++ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); ++} ++ ++static void ++icapRespModReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ int status = 0; ++ int isIcap = 0; ++ int directResponse = 0; ++ ErrorState *err; ++ const char *start; ++ const char *end; ++ ++ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ }; ++ /* OK here we have responce. Lets stop filling the ++ * icap->respmod.resp_copy buffer .... ++ */ ++ icap->flags.copy_response = 0; ++ ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++#if ICAP_PREVIEW ++ if (icap->flags.wait_for_preview_reply) { ++ if (100 == status) { ++ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ /* if http_server_eof ++ * call again icapSendRespMod to handle data that ++ * was received while waiting for this ICAP response ++ * else let http to call icapSendRespMod when new data arrived ++ */ ++ if (icap->flags.http_server_eof) ++ icapSendRespMod(icap, NULL, 0, 0); ++ /* ++ * reset the header to send the rest of the preview ++ */ ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufReset(&icap->icap_hdr); ++ ++ /*We do n't need it any more ....... */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ ++ return; ++ } ++ if (204 == status) { ++ debug(81, ++ 5) ("icapRespModReadReply: 204 No modification received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ } ++ } ++#endif /*ICAP_PREVIEW */ ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ if (204 == status) { ++ debug(81, 3) ("got 204 status from ICAP server\n"); ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ ++ /* ++ * "directResponse" is the normal case here. If we don't have ++ * a response header or body, it is an error. ++ */ ++ if (!directResponse) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ /* Next, gobble any data before the HTTP response starts */ ++ if (icap->enc.res_hdr > -1) ++ icap->bytes_to_gobble = icap->enc.res_hdr; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++} ++ ++ ++/* ++ * Gobble up (read) some bytes until we get to the start of the body ++ */ ++static void ++icapRespModGobble(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); ++ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, ++ icap->bytes_to_gobble); ++ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); ++ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); ++ if (len < 0) { ++ /* XXX error */ ++ abort(); ++ } ++ icap->bytes_to_gobble -= len; ++ if (icap->bytes_to_gobble) ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++ else ++ icapReadReply(fd, icap); ++} ++ ++ ++static void ++icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ErrorState *err; ++ ++ icap->flags.write_pending = 0; ++ debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ if (cbdataValid(icap)) ++ err->request = requestLink(icap->request); ++ storeEntryReset(icap->respmod.entry); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); ++ comm_close(fd); ++ return; ++ } ++ if (icap->flags.send_zero_chunk) { ++ debug(81, ++ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); ++ icap->flags.send_zero_chunk = 0; ++ icapSendRespMod(icap, NULL, 0, 1); ++ return; ++ } ++ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { ++ /* Schedule reading the ICAP response */ ++ debug(81, ++ 3) ++ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", ++ fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++#if 1 ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++#else ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ /* ++ * Set the read timeout only after all data has been sent ++ * or we are waiting for a preview response ++ * If the ICAP server does not return any data till all data ++ * has been sent, we are likely to hit the timeout for large ++ * HTTP bodies ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ } ++#endif ++ } ++} ++ ++void ++icapConnectOver(int fd, int status, void *data) ++{ ++ ErrorState *err; ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ if (status < 0) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); ++ icapOptSetUnreachable(icap->current_service); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++} ++ ++ ++ ++IcapStateData * ++icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, ++ http_state_flags http_flags) ++{ ++ IcapStateData *icap = NULL; ++ CNCB *theCallback = NULL; ++ icap_service *service = NULL; ++ ++ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); ++ assert(type >= 0 && type < ICAP_SERVICE_MAX); ++ ++ service = icapService(type, request); ++ if (!service) { ++ debug(81, 3) ("icapRespModStart: no service found\n"); ++ return NULL; /* no service found */ ++ } ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ++ ("icapRespModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ++ ("icapRespModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ switch (type) { ++ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change ++ * this switch, because callbacks isn't keep */ ++ case ICAP_SERVICE_RESPMOD_PRECACHE: ++ theCallback = icapConnectOver; ++ break; ++ default: ++ fatalf("icapRespModStart: unsupported service type '%s'\n", ++ icap_service_type_str[type]); ++ break; ++ } ++ ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->request = requestLink(request); ++ icap->respmod.entry = entry; ++ if (entry) ++ storeLockObject(entry); ++ icap->http_flags = http_flags; ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ ++ /* ++ * Don't create socket to the icap server now, but only for the first ++ * packet receive from the http server. This will resolve all timeout ++ * between the web server and icap server. ++ */ ++ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); ++ icap->flags.connect_requested = 0; ++ ++ /* ++ * make a copy the HTTP response that we send to the ICAP server in ++ * case it turns out to be a 204 ++ */ ++#ifdef SUPPORT_ICAP_204 ++ icap->flags.copy_response = 1; ++#elif ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) ++ icap->flags.copy_response = 0; ++ else ++ icap->flags.copy_response = 1; ++#else ++ icap->flags.copy_response = 0; ++#endif ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapRespModStart: returning %p\n", icap); ++ return icap; ++} ++ ++static int ++icapHttpReplyHdrState(IcapStateData * icap) ++{ ++ assert(icap); ++ if (NULL == icap->httpState) ++ return 0; ++ return icap->httpState->reply_hdr_state; ++} ++ ++static void ++icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) ++{ ++ if (NULL == icap->httpState) { ++ icap->httpState = cbdataAlloc(HttpStateData); ++ icap->httpState->request = requestLink(icap->request); ++ icap->httpState->orig_request = requestLink(icap->request); ++ icap->httpState->entry = icap->respmod.entry; ++ storeLockObject(icap->httpState->entry); /* lock it */ ++ } ++ httpProcessReplyHeader(icap->httpState, buf, size); ++ if (2 == icap->httpState->reply_hdr_state) ++ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); ++} ++ ++/* ++ * icapRespModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapRespModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink((request_t *) request); ++ err->xerrno = errno; ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ } else { ++ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", ++ fd); ++ comm_close(fd); ++ } ++ return; ++ } ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++} ++ ++static int ++icapReadReply2(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ debug(81, 3) ("icapReadReply2\n"); ++ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); ++ err->xerrno = errno; ++ err->request = requestLink((request_t *) request); ++ errorAppendEntry(entry, err); ++ icap->flags.http_server_eof = 1; ++ return -1; ++ } ++ if (icap->chunk_buf.size == 0) { ++ /* Retrieval done. */ ++ if (icapHttpReplyHdrState(icap) < 2) ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ icap->flags.http_server_eof = 1; ++ icapReadReply3(icap); ++ return 0; ++ } ++ if (icapHttpReplyHdrState(icap) == 0) { ++ int expect = icapExpectedHttpReplyHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed < 0 || needed >= 0); ++ if (0 > expect) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ } else if (0 == expect) { ++ /* ++ * this icap reply doesn't give us new HTTP headers ++ * so we must copy them from our copy ++ */ ++ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, ++ __LINE__); ++ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ ++ storeAppend(entry, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ } ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ ++ } else if (needed) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ if (icap->chunk_buf.size >= needed) { ++ storeAppend(entry, icap->chunk_buf.buf, needed); ++ so_far += needed; ++ xmemmove(icap->chunk_buf.buf, ++ icap->chunk_buf.buf + needed, ++ icap->chunk_buf.size - needed); ++ icap->chunk_buf.size -= needed; ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; ++ } else { ++ /* ++ * We don't have the full HTTP reply headers yet, so keep ++ * the partial reply buffered in 'chunk_buf' and wait ++ * for more. ++ */ ++ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); ++ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } ++ } ++ icap->http_header_bytes_read_so_far = so_far; ++ } ++ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, ++ (int) icap->chunk_buf.size); ++ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, ++ icap->flags.no_content); ++ if (icap->flags.no_content) { ++ /* data from http.c is not chunked */ ++ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", ++ icap->chunk_buf.size); ++ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); ++ icap->chunk_buf.size = 0; ++ } ++ } else if (2 == icapHttpReplyHdrState(icap)) { ++ if (icap->chunk_buf.size) ++ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); ++ } ++ icapReadReply3(icap); ++ return 0; ++} ++ ++static void ++icapReadReply3(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ int fd = icap->icap_fd; ++ debug(81, 3) ("icapReadReply3\n"); ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ icapRespModKeepAliveOrClose(icap); ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.28.6.25 +retrieving revision 1.28.6.8.2.11 +diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11 +--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25 ++++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11 +@@ -350,6 +350,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + authenticateShutdown(); + externalAclShutdown(); +@@ -378,6 +381,9 @@ mainReconfigure(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -507,6 +513,9 @@ mainInitialize(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); +Index: src/mem.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/mem.c,v +retrieving revision 1.13 +retrieving revision 1.13.28.2 +diff -p -u -b -r1.13 -r1.13.28.2 +--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13 ++++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2 +@@ -243,6 +243,13 @@ memInit(void) + memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.140.1 +diff -p -u -b -r1.2 -r1.2.140.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1 +@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str"; + $pat{'icp_opcode'} = "icp_opcode_str"; + $pat{'swap_log_op'} = "swap_log_op_str"; + $pat{'lookup_t'} = "lookup_t_str"; ++$pat{'icap_service_t'} = "icap_service_type_str"; + + $state = 0; # start state + while (<>) { +Index: src/pconn.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/pconn.c,v +retrieving revision 1.6.38.2 +retrieving revision 1.6.60.2 +diff -p -u -b -r1.6.38.2 -r1.6.60.2 +--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2 ++++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2 +@@ -46,6 +46,9 @@ struct _pconn { + #define PCONN_HIST_SZ (1<<16) + int client_pconn_hist[PCONN_HIST_SZ]; + int server_pconn_hist[PCONN_HIST_SZ]; ++#ifdef HS_FEAT_ICAP ++int icap_server_pconn_hist[PCONN_HIST_SZ]; ++#endif + + static PF pconnRead; + static PF pconnTimeout; +@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); + } ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(e, ++ "\n" ++ "ICAP-server persistent connection counts:\n" ++ "\n" ++ "\treq/\n" ++ "\tconn count\n" ++ "\t---- ---------\n"); ++ for (i = 0; i < PCONN_HIST_SZ; i++) { ++ if (icap_server_pconn_hist[i] == 0) ++ continue; ++ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); ++ } ++#endif + } + + /* ========== PUBLIC FUNCTIONS ============================================ */ +@@ -173,6 +190,9 @@ pconnInit(void) + for (i = 0; i < PCONN_HIST_SZ; i++) { + client_pconn_hist[i] = 0; + server_pconn_hist[i] = 0; ++#ifdef HS_FEAT_ICAP ++ icap_server_pconn_hist[i] = 0; ++#endif + } + pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); + pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); +@@ -248,11 +268,15 @@ pconnHistCount(int what, int i) + { + if (i >= PCONN_HIST_SZ) + i = PCONN_HIST_SZ - 1; +- /* what == 0 for client, 1 for server */ ++ /* what == 0 for client, 1 for server, 2 for ICAP server */ + if (what == 0) + client_pconn_hist[i]++; + else if (what == 1) + server_pconn_hist[i]++; ++#ifdef HS_FEAT_ICAP ++ else if (what == 2) ++ icap_server_pconn_hist[i]++; ++#endif + else + assert(0); + } +Index: src/protos.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/protos.h,v +retrieving revision 1.41.6.33 +retrieving revision 1.41.6.13.2.37 +diff -p -u -b -r1.41.6.33 -r1.41.6.13.2.37 +--- src/protos.h 16 Sep 2005 02:13:25 -0000 1.41.6.33 ++++ src/protos.h 6 Dec 2005 21:53:44 -0000 1.41.6.13.2.37 +@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *); + /* http.c */ + extern int httpCachable(method_t); + extern void httpStart(FwdState *); ++extern void httpParseReplyHeaders(const char *, http_reply *); ++extern void httpProcessReplyHeader(HttpStateData *, const char *, int); + extern int httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, +@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c + extern FREE *memBufFreeFunc(MemBuf * mb); + /* puts report on MemBuf _module_ usage into mb */ + extern void memBufReport(MemBuf * mb); ++extern int memBufRead(int fd, MemBuf * mb); + + extern char *mime_get_header(const char *mime, const char *header); + extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); +@@ -1341,4 +1344,49 @@ extern void externalAclShutdown(void); + extern int externalAclRequiresAuth(void *acl_data); + extern char *strtokFile(void); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.13.6.8 +retrieving revision 1.13.6.6.2.11 +diff -p -u -b -r1.13.6.8 -r1.13.6.6.2.11 +--- src/squid.h 26 Mar 2005 03:15:58 -0000 1.13.6.8 ++++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11 +@@ -38,6 +38,14 @@ + #include "config.h" + + /* ++ * experimental defines for ICAP ++ */ ++#ifdef HS_FEAT_ICAP ++#define ICAP_PREVIEW 1 ++#define SUPPORT_ICAP_204 0 ++#endif ++ ++/* + * On some systems, FD_SETSIZE is set to something lower than the + * actual number of files which can be opened. IRIX is one case, + * NetBSD is another. So here we increase FD_SETSIZE to our +Index: src/stat.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/stat.c,v +retrieving revision 1.13.6.14 +retrieving revision 1.13.6.7.2.7 +diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7 +--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14 ++++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7 +@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min + storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", + XAVG(server.other.kbytes_out.kb)); + ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", ++ XAVG(icap.all.requests)); ++ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", ++ XAVG(icap.all.errors)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", ++ XAVG(icap.all.kbytes_in.kb)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", ++ XAVG(icap.all.kbytes_out.kb)); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", + XAVG(icp.pkts_sent)); + storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", +@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry) + storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", + (int) f->server.other.kbytes_out.kb); + ++#if HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %d\n", ++ (int) f->icap.all.requests); ++ storeAppendPrintf(sentry, "icap.all.errors = %d\n", ++ (int) f->icap.all.errors); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", ++ (int) f->icap.all.kbytes_in.kb); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", ++ (int) f->icap.all.kbytes_out.kb); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", + f->icp.pkts_sent); + storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", +@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s) + storeAppendPrintf(s, "\tme: %s:%d\n", + inet_ntoa(conn->me.sin_addr), + ntohs(conn->me.sin_port)); +- storeAppendPrintf(s, "\tnrequests: %d\n", +- conn->nrequests); + storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", + conn->defer.n, (long int) conn->defer.until); + } +Index: src/store.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/store.c,v +retrieving revision 1.16.6.9 +retrieving revision 1.16.6.2.2.8 +diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8 +--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9 ++++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8 +@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.48.2.43 +retrieving revision 1.48.2.9.2.48 +diff -p -u -b -r1.48.2.43 -r1.48.2.9.2.48 +--- src/structs.h 4 Sep 2005 02:13:28 -0000 1.48.2.43 ++++ src/structs.h 30 Nov 2005 21:52:15 -0000 1.48.2.9.2.48 +@@ -384,6 +384,22 @@ struct _RemovalPolicySettings { + wordlist *args; + }; + ++#if HS_FEAT_ICAP ++struct _IcapConfig { ++ int onoff; ++ int preview_enable; ++ icap_service *service_head; ++ icap_class *class_head; ++ icap_access *access_head; ++ int preview_size; ++ int check_interval; ++ int send_client_ip; ++ int send_auth_user; ++ char *auth_scheme; ++}; ++ ++#endif /* HS_FEAT_ICAP */ ++ + struct _SquidConfig { + struct { + squid_off_t maxSize; +@@ -714,6 +730,9 @@ struct _SquidConfig { + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + external_acl *externalAclHelperList; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -787,7 +806,10 @@ struct _fde { + } flags; + squid_off_t bytes_read; + squid_off_t bytes_written; +- int uses; /* ie # req's over persistent conn */ ++ struct { ++ int uses; ++ int type; ++ } pconn; + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; +@@ -982,6 +1004,130 @@ struct _http_state_flags { + unsigned int request_sent:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -993,10 +1139,14 @@ struct _HttpStateData { + int fd; + http_state_flags flags; + FwdState *fwd; ++#ifdef HS_FEAT_ICAP ++ struct _IcapStateData *icap_writer; ++#endif + char *body_buf; + int body_buf_sz; + }; + ++ + struct _icpUdpData { + struct sockaddr_in address; + void *msg; +@@ -1092,6 +1242,7 @@ struct _clientHttpRequest { + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; ++ unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { +@@ -1100,6 +1251,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1127,7 +1281,6 @@ struct _ConnStateData { + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; +- int nrequests; + struct { + int n; + time_t until; +@@ -1678,6 +1831,9 @@ struct _request_t { + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + }; +@@ -1784,7 +1940,11 @@ struct _StatCounters { + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; +- } server; ++ } ++#if HS_FEAT_ICAP ++ icap, ++#endif ++ server; + struct { + int pkts_sent; + int queries_sent; +Index: src/typedefs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/typedefs.h,v +retrieving revision 1.25.6.8 +retrieving revision 1.25.6.1.6.13 +diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13 +--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8 ++++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13 +@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade + typedef struct _HttpBody HttpBody; + typedef struct _HttpReply HttpReply; + typedef struct _HttpStateData HttpStateData; ++#ifdef HS_FEAT_ICAP ++typedef struct _IcapStateData IcapStateData; ++typedef struct _IcapConfig IcapConfig; ++typedef struct _icap_service icap_service; ++typedef struct _icap_service_list icap_service_list; ++typedef struct _icap_class icap_class; ++typedef struct _icap_access icap_access; ++typedef struct _IcapOptData IcapOptData; ++#endif + typedef struct _icpUdpData icpUdpData; + typedef struct _clientHttpRequest clientHttpRequest; + typedef struct _ConnStateData ConnStateData; +Index: src/url.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/url.c,v +retrieving revision 1.7.6.6 +retrieving revision 1.7.6.5.2.2 +diff -p -u -b -r1.7.6.6 -r1.7.6.5.2.2 +--- src/url.c 12 Nov 2005 03:13:48 -0000 1.7.6.6 ++++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -221,6 +224,10 @@ urlParseProtocol(const char *s) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; ++#ifdef HS_FEAT_ICAP ++ if (strcasecmp(s, "icap") == 0) ++ return PROTO_ICAP; ++#endif + return PROTO_NONE; + } + +@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p) + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; ++#ifdef HS_FEAT_ICAP ++ case PROTO_ICAP: ++ return 1344; ++#endif + default: + return 0; + } diff --git a/www/squid30/Makefile b/www/squid30/Makefile index 31916d4cb34e..1f75bf5d90f8 100644 --- a/www/squid30/Makefile +++ b/www/squid30/Makefile @@ -66,10 +66,14 @@ # Override the maximum number of filedescriptors. Useful if you # build as another user who is not privileged to use the amount # of filedescriptors the resulting binary is expected to support. +# --enable-ntlm-fail-open +# Enable NTLM fail open, where a helper that fails one of the +# Authentication steps can allow squid to still authenticate the user # PORTNAME= squid PORTVERSION= 2.5.12 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= \ ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ @@ -82,6 +86,7 @@ DISTNAME= squid-2.5.STABLE12 DIST_SUBDIR= squid2.5 PATCH_SITES= http://www.squid-cache.org/Versions/v2/2.5/bugs/ +PATCHFILES= squid-2.5.STABLE12-SMB_BadFetch.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -120,6 +125,7 @@ OPTIONS= SQUID_LDAP_AUTH "Install LDAP authentication helpers" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ + SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ SQUID_COSS "Enable the COSS storage scheme" off \ SQUID_LARGEFILE "Support log and cache files >2GB" off \ @@ -293,6 +299,12 @@ EXTRA_PATCHES+= ${PATCHDIR}/follow_xff-2.5.patch \ ${PATCHDIR}/follow_xff-configure.patch CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif +.if defined(WITH_SQUID_ICAP) +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.5-core.patch \ + ${PATCHDIR}/icap-2.5-bootstrap.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE +.endif .if defined(WITH_SQUID_LARGEFILE) CONFIGURE_ARGS+= --with-large-files --enable-large-cache-files .endif diff --git a/www/squid30/distinfo b/www/squid30/distinfo index 5d1b5428ba58..3e55ac8d1717 100644 --- a/www/squid30/distinfo +++ b/www/squid30/distinfo @@ -1,2 +1,6 @@ MD5 (squid2.5/squid-2.5.STABLE12.tar.bz2) = 7354255015b3772a1e024dfac173e48c +SHA256 (squid2.5/squid-2.5.STABLE12.tar.bz2) = ba0ccd956323f0dad46c19aa8d40c537846fedfc3778b5730e5610f16c0d9af1 SIZE (squid2.5/squid-2.5.STABLE12.tar.bz2) = 1075111 +MD5 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 8e83b776c0d015bd4137cc1ca08f6d38 +SHA256 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 9ca8427c2eb9e5cbdb5a49fb5cb94fc00853ad965f87666f8fc35236e98bc0ae +SIZE (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 826 diff --git a/www/squid30/files/icap-2.5-bootstrap.patch b/www/squid30/files/icap-2.5-bootstrap.patch new file mode 100644 index 000000000000..247ca0c94cbc --- /dev/null +++ b/www/squid30/files/icap-2.5-bootstrap.patch @@ -0,0 +1,422 @@ +Patch 2 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.5-core.patch for further information. + +Patch last updated: 2005-12-17 + +--- configure.orig Sat Oct 22 11:56:01 2005 ++++ configure Sat Dec 17 17:45:21 2005 +@@ -70,6 +70,8 @@ + ac_help="$ac_help + --enable-delay-pools Enable delay pools to limit bandwidth usage" + ac_help="$ac_help ++ --enable-icap-support Enable iCAP client capability" ++ac_help="$ac_help + --enable-useragent-log Enable logging of User-Agent header" + ac_help="$ac_help + --enable-referer-log Enable logging of Referer header" +@@ -2170,6 +2172,38 @@ + + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ cat >> confdefs.h <<\EOF ++#define HS_FEAT_ICAP 1 ++EOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ fi ++ ++fi ++ ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -7428,14 +7462,14 @@ + fi + ;; + esac +- echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 +-echo "configure:7433: checking for main in -lpthread" >&5 ++ echo $ac_n "checking for main in -pthread""... $ac_c" 1>&6 ++echo "configure:7433: checking for main in -pthread" >&5 + ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 + else + ac_save_LIBS="$LIBS" +-LIBS="-lpthread $LIBS" ++LIBS="-pthread $LIBS" + cat > conftest.$ac_ext <<EOF + #line 7441 "configure" + #include "confdefs.h" +@@ -7465,7 +7499,7 @@ + #define $ac_tr_lib 1 + EOF + +- LIBS="-lpthread $LIBS" ++ LIBS="-pthread $LIBS" + + else + echo "$ac_t""no" 1>&6 +@@ -7769,6 +7803,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strtoll \ + sysconf \ + syslog \ +@@ -7898,6 +7934,50 @@ + fi + fi + ++ ++if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++fi ++ ++ ++ ++ + echo $ac_n "checking if va_copy is implemented""... $ac_c" 1>&6 + echo "configure:7903: checking if va_copy is implemented" >&5 + if eval "test \"`echo '$''{'ac_cv_func_va_copy'+set}'`\" = set"; then +@@ -9072,6 +9152,8 @@ + s%@ENABLE_PINGER_FALSE@%$ENABLE_PINGER_FALSE%g + s%@USE_DELAY_POOLS_TRUE@%$USE_DELAY_POOLS_TRUE%g + s%@USE_DELAY_POOLS_FALSE@%$USE_DELAY_POOLS_FALSE%g ++s%@USE_ICAP_TRUE@%$USE_ICAP_TRUE%g ++s%@USE_ICAP_FALSE@%$USE_ICAP_FALSE%g + s%@USE_SNMP_TRUE@%$USE_SNMP_TRUE%g + s%@USE_SNMP_FALSE@%$USE_SNMP_FALSE%g + s%@SNMPLIB@%$SNMPLIB%g +@@ -9118,6 +9200,10 @@ + s%@LIB_LBER@%$LIB_LBER%g + s%@NEED_OWN_SNPRINTF_TRUE@%$NEED_OWN_SNPRINTF_TRUE%g + s%@NEED_OWN_SNPRINTF_FALSE@%$NEED_OWN_SNPRINTF_FALSE%g ++s%@NEED_OWN_STRNSTR_TRUE@%$NEED_OWN_STRNSTR_TRUE%g ++s%@NEED_OWN_STRNSTR_FALSE@%$NEED_OWN_STRNSTR_FALSE%g ++s%@NEED_OWN_STRCASESTR_TRUE@%$NEED_OWN_STRCASESTR_TRUE%g ++s%@NEED_OWN_STRCASESTR_FALSE@%$NEED_OWN_STRCASESTR_FALSE%g + s%@REGEXLIB@%$REGEXLIB%g + s%@LIBREGEX@%$LIBREGEX%g + s%@LIBOBJS@%$LIBOBJS%g +--- include/autoconf.h.in.orig Tue Sep 13 02:12:34 2005 ++++ include/autoconf.h.in Sat Dec 17 17:45:21 2005 +@@ -124,6 +124,11 @@ + */ + #undef DELAY_POOLS + ++/* ++ * ICAP - Internet Content Adaptation Protocol ++ */ ++#undef HS_FEAT_ICAP ++ + /* + * If you want to log User-Agent request header values, define this. + * By default, they are written to useragent.log in the Squid log +@@ -574,6 +579,12 @@ + + /* Define if you have the statfs function. */ + #undef HAVE_STATFS ++ ++/* Define if you have the strcasestr function. */ ++#undef HAVE_STRCASESTR ++ ++/* Define if you have the strnstr function. */ ++#undef HAVE_STRNSTR + + /* Define if you have the strerror function. */ + #undef HAVE_STRERROR +--- lib/Makefile.in.orig Wed Sep 28 22:57:20 2005 ++++ lib/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -123,6 +123,13 @@ + + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = ++ ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++ ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++ + @NEED_OWN_MD5_TRUE@MD5SOURCE = md5.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = + +@@ -158,6 +165,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +@@ -196,13 +205,18 @@ + @NEED_OWN_MD5_FALSE@am__objects_1 = + @NEED_OWN_SNPRINTF_FALSE@am__objects_2 = + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) ++@NEED_OWN_STRNSTR_FALSE@am__objects_3 = ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_FALSE@am__objects_4 = + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) stub_memaccount.$(OBJEXT) \ ++ util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) cru + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -224,15 +238,16 @@ + @AMDEP_TRUE@ $(DEPDIR)/dlmalloc.Po $(DEPDIR)/drand48.Po \ + @AMDEP_TRUE@ $(DEPDIR)/getfullhostname.Po $(DEPDIR)/hash.Po \ + @AMDEP_TRUE@ $(DEPDIR)/heap.Po $(DEPDIR)/html_quote.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/initgroups.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/iso3307.Po $(DEPDIR)/md5.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/ntlmauth.Po $(DEPDIR)/radix.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1035.Po $(DEPDIR)/rfc1123.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1738.Po $(DEPDIR)/rfc2617.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/safe_inet_addr.Po $(DEPDIR)/snprintf.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/splay.Po $(DEPDIR)/strerror.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/stub_memaccount.Po $(DEPDIR)/tempnam.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uudecode.Po ++@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/iso3307.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/md5.Po $(DEPDIR)/ntlmauth.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/radix.Po $(DEPDIR)/rfc1035.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc1123.Po $(DEPDIR)/rfc1738.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc2617.Po $(DEPDIR)/safe_inet_addr.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/snprintf.Po $(DEPDIR)/splay.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strcasestr.Po $(DEPDIR)/strerror.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strnstr.Po $(DEPDIR)/stub_memaccount.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/tempnam.Po $(DEPDIR)/util.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/uudecode.Po + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -241,8 +256,8 @@ + DIST_SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) \ + $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) \ + $(libregex_a_SOURCES) +-DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c \ +- initgroups.c strerror.c tempnam.c ++DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c strerror.c \ ++ tempnam.c + SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) $(libregex_a_SOURCES) + + all: all-am +@@ -295,7 +310,6 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/heap.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/html_quote.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/inet_ntoa.Po@am__quote@ +-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/initgroups.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iso3307.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/md5.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ntlmauth.Po@am__quote@ +@@ -307,7 +321,9 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/safe_inet_addr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splay.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerror.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tempnam.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Wed Sep 28 22:57:21 2005 ++++ src/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -125,6 +125,9 @@ + install_sh = @install_sh@ + makesnmplib = @makesnmplib@ + ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c ++@USE_ICAP_FALSE@ICAPSOURCE = ++ + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver +@@ -249,6 +252,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -468,54 +472,58 @@ + pinger_LDADD = $(LDADD) + pinger_DEPENDENCIES = + pinger_LDFLAGS = +-@USE_DELAY_POOLS_TRUE@am__objects_3 = delay_pools.$(OBJEXT) +-@USE_DELAY_POOLS_FALSE@am__objects_3 = +-@USE_DNSSERVER_FALSE@am__objects_4 = dns_internal.$(OBJEXT) +-@USE_DNSSERVER_TRUE@am__objects_4 = dns.$(OBJEXT) +-@ENABLE_HTCP_TRUE@am__objects_5 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_FALSE@am__objects_6 = +-@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) +-@USE_SNMP_FALSE@am__objects_7 = +-@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) +-@ENABLE_SSL_FALSE@am__objects_8 = +-@ENABLE_UNLINKD_FALSE@am__objects_9 = +-@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_10 = ++@USE_DELAY_POOLS_FALSE@am__objects_5 = ++@USE_DELAY_POOLS_TRUE@am__objects_5 = delay_pools.$(OBJEXT) ++@USE_DNSSERVER_FALSE@am__objects_6 = dns_internal.$(OBJEXT) ++@USE_DNSSERVER_TRUE@am__objects_6 = dns.$(OBJEXT) ++@ENABLE_HTCP_TRUE@am__objects_7 = htcp.$(OBJEXT) ++@USE_ICAP_TRUE@am__objects_8 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@USE_ICAP_FALSE@am__objects_8 = ++@MAKE_LEAKFINDER_TRUE@am__objects_9 = leakfinder.$(OBJEXT) ++@MAKE_LEAKFINDER_FALSE@am__objects_9 = ++@USE_SNMP_TRUE@am__objects_10 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) ++@USE_SNMP_FALSE@am__objects_10 = ++@ENABLE_SSL_FALSE@am__objects_11 = ++@ENABLE_SSL_TRUE@am__objects_11 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_12 = unlinkd.$(OBJEXT) ++@ENABLE_UNLINKD_FALSE@am__objects_12 = ++@ENABLE_WIN32SPECIFIC_FALSE@am__objects_13 = ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_13 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \ + cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \ + client_db.$(OBJEXT) client_side.$(OBJEXT) comm.$(OBJEXT) \ +- comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_3) \ +- disk.$(OBJEXT) $(am__objects_4) errorpage.$(OBJEXT) \ ++ comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \ ++ disk.$(OBJEXT) $(am__objects_6) errorpage.$(OBJEXT) \ + ETag.$(OBJEXT) event.$(OBJEXT) external_acl.$(OBJEXT) \ + fd.$(OBJEXT) filemap.$(OBJEXT) forward.$(OBJEXT) \ + fqdncache.$(OBJEXT) ftp.$(OBJEXT) gopher.$(OBJEXT) \ +- helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ ++ helper.$(OBJEXT) $(am__objects_7) http.$(OBJEXT) \ + HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_6) \ +- logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_8) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_9) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ + MemPool.$(OBJEXT) MemBuf.$(OBJEXT) mime.$(OBJEXT) \ + multicast.$(OBJEXT) neighbors.$(OBJEXT) net_db.$(OBJEXT) \ + Packer.$(OBJEXT) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \ + peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ +- ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_10) \ ++ ssl.$(OBJEXT) $(am__objects_11) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_12) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- whois.$(OBJEXT) $(am__objects_10) ++ whois.$(OBJEXT) $(am__objects_13) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -563,7 +571,9 @@ + @AMDEP_TRUE@ $(DEPDIR)/fqdncache.Po $(DEPDIR)/ftp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/globals.Po $(DEPDIR)/gopher.Po \ + @AMDEP_TRUE@ $(DEPDIR)/helper.Po $(DEPDIR)/htcp.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icmp.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icap_common.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_opt.Po $(DEPDIR)/icap_reqmod.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_respmod.Po $(DEPDIR)/icmp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/icp_v2.Po $(DEPDIR)/icp_v3.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ident.Po $(DEPDIR)/internal.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ipc.Po $(DEPDIR)/ipcache.Po \ +@@ -777,6 +787,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v3.Po@am__quote@ diff --git a/www/squid30/files/icap-2.5-core.patch b/www/squid30/files/icap-2.5-core.patch new file mode 100644 index 000000000000..22d209c18fb4 --- /dev/null +++ b/www/squid30/files/icap-2.5-core.patch @@ -0,0 +1,7059 @@ +Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch only contains the parts of the original patchset that +actually implement the ICAP client functionality. The updates to +the build infrastructure are omitted to avoid the need to run an +autotools bootstrap. Instead, we simulate said bootstrapping with +a second patch, icap-2.5-bootstrap.patch. + +The patchset was pulled from the project's CVS repository +at cvs.devel.squid-cache.org using + +cvs diff -u -b -N -kk -rs2_5 -ricap-2_5 + +See also +<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5> +for the "official" auto-generated patchset. + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2005-12-17 + +Index: errors/Bulgarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Bulgarian/ERR_ICAP_FAILURE +diff -N errors/Bulgarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Catalan/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Catalan/ERR_ICAP_FAILURE +diff -N errors/Catalan/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Czech/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Czech/ERR_ICAP_FAILURE +diff -N errors/Czech/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Danish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Danish/ERR_ICAP_FAILURE +diff -N errors/Danish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Dutch/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Dutch/ERR_ICAP_FAILURE +diff -N errors/Dutch/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/English/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/English/ERR_ICAP_FAILURE +diff -N errors/English/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Estonian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Estonian/ERR_ICAP_FAILURE +diff -N errors/Estonian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Finnish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Finnish/ERR_ICAP_FAILURE +diff -N errors/Finnish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/French/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/French/ERR_ICAP_FAILURE +diff -N errors/French/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/German/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/German/ERR_ICAP_FAILURE +diff -N errors/German/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2 +@@ -0,0 +1,33 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>FEHLER</H1> ++<H2>Der angeforderte URL konnte nicht geholt werden</H2> ++<HR noshade size="1px"> ++<P> ++Während des Versuches, den URL<BR> ++<A HREF="%U">%U</A> ++ ++<BR> ++zu laden, trat der folgende Fehler auf: ++<UL> ++<LI> ++<STRONG> ++ICAP-Protokollfehler ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: ++<UL> ++<LI>Nicht erreichbarer ICAP-Server ++<LI>Ungültige Antwort vom ICAP-Server ++ ++</UL> ++</P> ++ ++<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Greek/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Greek/ERR_ICAP_FAILURE +diff -N errors/Greek/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hebrew/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hebrew/ERR_ICAP_FAILURE +diff -N errors/Hebrew/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hungarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hungarian/ERR_ICAP_FAILURE +diff -N errors/Hungarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Italian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Italian/ERR_ICAP_FAILURE +diff -N errors/Italian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Japanese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Japanese/ERR_ICAP_FAILURE +diff -N errors/Japanese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Korean/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Korean/ERR_ICAP_FAILURE +diff -N errors/Korean/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Lithuanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Lithuanian/ERR_ICAP_FAILURE +diff -N errors/Lithuanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Polish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Polish/ERR_ICAP_FAILURE +diff -N errors/Polish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Portuguese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Portuguese/ERR_ICAP_FAILURE +diff -N errors/Portuguese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Romanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Romanian/ERR_ICAP_FAILURE +diff -N errors/Romanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-1251/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-1251/ERR_ICAP_FAILURE +diff -N errors/Russian-1251/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-koi8-r/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE +diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Serbian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Serbian/ERR_ICAP_FAILURE +diff -N errors/Serbian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Simplify_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE +diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Slovak/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Slovak/ERR_ICAP_FAILURE +diff -N errors/Slovak/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Spanish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Spanish/ERR_ICAP_FAILURE +diff -N errors/Spanish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Swedish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Swedish/ERR_ICAP_FAILURE +diff -N errors/Swedish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Traditional_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE +diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Turkish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Turkish/ERR_ICAP_FAILURE +diff -N errors/Turkish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.10 +retrieving revision 1.10.30.2 +diff -p -u -b -r1.10 -r1.10.30.2 +--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10 ++++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2 +@@ -132,4 +132,12 @@ double drand48(void); + */ + int statMemoryAccounted(void); + ++#ifndef HAVE_STRNSTR ++extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); ++#endif ++ ++#ifndef HAVE_STRCASESTR ++extern char *strcasestr(const char *haystack, const char *needle); ++#endif ++ + #endif /* SQUID_UTIL_H */ +Index: lib/Makefile.am +=================================================================== +RCS file: /cvsroot/squid/squid/lib/Makefile.am,v +retrieving revision 1.4 +retrieving revision 1.4.26.2 +diff -p -u -b -r1.4 -r1.4.26.2 +--- lib/Makefile.am 21 Nov 2001 23:48:57 -0000 1.4 ++++ lib/Makefile.am 6 Apr 2004 13:04:38 -0000 1.4.26.2 +@@ -8,6 +8,19 @@ SNPRINTFSOURCE=snprintf.c + else + SNPRINTFSOURCE= + endif ++ ++if NEED_OWN_STRNSTR ++STRNSTRSOURCE=strnstr.c ++else ++STRNSTRSOURCE= ++endif ++ ++if NEED_OWN_STRCASESTR ++STRCASESTRSOURCE=strcasestr.c ++else ++STRCASESTRSOURCE= ++endif ++ + if NEED_OWN_MD5 + MD5SOURCE=md5.c + else +@@ -43,6 +56,8 @@ libmiscutil_a_SOURCES = \ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +Index: lib/strcasestr.c +=================================================================== +RCS file: lib/strcasestr.c +diff -N lib/strcasestr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1 +@@ -0,0 +1,126 @@ ++/* Return the offset of one string within another. ++ Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* ++ * My personal strstr() implementation that beats most other algorithms. ++ * Until someone tells me otherwise, I assume that this is the ++ * fastest implementation of strstr() in C. ++ * I deliberately chose not to comment it. You should have at least ++ * as much fun trying to understand it, as I had to write it :-). ++ * ++ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ ++ ++/* ++ * modified to work outside of glibc (rhorstmann, 06/04/2004) ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRCASESTR ++#include <ctype.h> ++ ++typedef unsigned chartype; ++ ++char * ++strcasestr (phaystack, pneedle) ++ const char *phaystack; ++ const char *pneedle; ++{ ++ register const unsigned char *haystack, *needle; ++ register chartype b, c; ++ ++ haystack = (const unsigned char *) phaystack; ++ needle = (const unsigned char *) pneedle; ++ ++ b = tolower (*needle); ++ if (b != '\0') ++ { ++ haystack--; /* possible ANSI violation */ ++ do ++ { ++ c = *++haystack; ++ if (c == '\0') ++ goto ret0; ++ } ++ while (tolower (c) != (int) b); ++ ++ c = tolower (*++needle); ++ if (c == '\0') ++ goto foundneedle; ++ ++needle; ++ goto jin; ++ ++ for (;;) ++ { ++ register chartype a; ++ register const unsigned char *rhaystack, *rneedle; ++ ++ do ++ { ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ if (tolower (a) == (int) b) ++ break; ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++shloop: ++ ; ++ } ++ while (tolower (a) != (int) b); ++ ++jin: a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ ++ if (tolower (a) != (int) c) ++ goto shloop; ++ ++ rhaystack = haystack-- + 1; ++ rneedle = needle; ++ a = tolower (*rneedle); ++ ++ if (tolower (*rhaystack) == (int) a) ++ do ++ { ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ if (tolower (*rhaystack) != (int) a) ++ break; ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ } ++ while (tolower (*rhaystack) == (int) a); ++ ++ needle = rneedle; /* took the register-poor approach */ ++ ++ if (a == '\0') ++ break; ++ } ++ } ++foundneedle: ++ return (char*) haystack; ++ret0: ++ return 0; ++} ++#endif +Index: lib/strnstr.c +=================================================================== +RCS file: lib/strnstr.c +diff -N lib/strnstr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include <string.h> ++#include <util.h> ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.5.30.3 +retrieving revision 1.5.44.8 +diff -p -u -b -r1.5.30.3 -r1.5.44.8 +--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3 ++++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8 +@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb) + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); + } ++ ++int ++memBufRead(int fd, MemBuf * mb) ++{ ++ int len; ++ if (mb->capacity == mb->size) ++ memBufGrow(mb, SQUID_TCP_SO_RCVBUF); ++ len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); ++ if (len) ++ mb->size += len; ++ return len; ++} +Index: src/cache_cf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cache_cf.c,v +retrieving revision 1.38.6.29 +retrieving revision 1.38.6.11.2.22 +diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22 +--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29 ++++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22 +@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli + return bodylist.head == NULL; + } + ++#ifdef HS_FEAT_ICAP ++ ++/*************************************************** ++ * prototypes ++ */ ++static int icap_service_process(icap_service * s); ++static void icap_service_init(icap_service * s); ++static void icap_service_destroy(icap_service * s); ++icap_service *icap_service_lookup(char *name); ++static int icap_class_process(icap_class * c); ++static void icap_class_destroy(icap_class * c); ++static void icap_access_destroy(icap_access * a); ++static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access <servicename> {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.14.6.1 +retrieving revision 1.14.32.2 +diff -p -u -b -r1.14.6.1 -r1.14.32.2 +--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1 ++++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2 +@@ -144,6 +144,10 @@ cbdataInit(void) + CREATE_CBDATA(statefulhelper); + CREATE_CBDATA(helper_stateful_server); + CREATE_CBDATA(HttpStateData); ++#ifdef HS_FEAT_ICAP ++ CREATE_CBDATA(IcapStateData); ++ CREATE_CBDATA(icap_service); ++#endif + CREATE_CBDATA_FREE(peer, peerDestroy); + CREATE_CBDATA(ps_state); + CREATE_CBDATA(RemovalPolicy); +Index: src/cf.data.pre +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf.data.pre,v +retrieving revision 1.49.2.84 +retrieving revision 1.49.2.33.2.32 +diff -p -u -b -r1.49.2.84 -r1.49.2.33.2.32 +--- src/cf.data.pre 21 Oct 2005 02:13:47 -0000 1.49.2.84 ++++ src/cf.data.pre 24 Oct 2005 17:07:42 -0000 1.49.2.33.2.32 +@@ -2397,7 +2397,6 @@ DOC_START + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -2716,6 +2715,177 @@ DOC_START + DOC_END + + COMMENT_START ++ ICAP OPTIONS ++ ----------------------------------------------------------------------------- ++COMMENT_END ++ ++NAME: icap_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.onoff ++DEFAULT: off ++DOC_START ++ If you want to enable the ICAP client module, set this to on. ++DOC_END ++ ++NAME: icap_preview_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.preview_enable ++DEFAULT: off ++DOC_START ++ Set this to 'on' if you want to enable the ICAP preview ++ feature in Squid. ++DOC_END ++ ++NAME: icap_preview_size ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.preview_size ++DEFAULT: -1 ++DOC_START ++ The default size of preview data to be sent to the ICAP server. ++ -1 means no preview. This value might be overwritten on a per server ++ basis by OPTIONS requests. ++DOC_END ++ ++NAME: icap_check_interval ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.check_interval ++DEFAULT: 300 ++DOC_START ++ If an ICAP server does not respond, it gets marked as unreachable. Squid ++ will try again to reach it after this time. ++DOC_END ++ ++NAME: icap_send_client_ip ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_client_ip ++DEFAULT: off ++DOC_START ++ This adds the header "X-Client-IP" to ICAP requests. Can also be ++ set from the server's response to OPTIONS. ++DOC_END ++ ++NAME: icap_send_auth_user ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_auth_user ++DEFAULT: off ++DOC_START ++ This adds the header "X-Authenticated-User" to ICAP requests ++ if proxy access is authentified. Can also be set from the server's ++ response to OPTIONS. ++DOC_END ++ ++NAME: icap_auth_scheme ++TYPE: string ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.auth_scheme ++DEFAULT: Local://%u ++DOC_START ++ Authentification scheme to pass to ICAP requests if ++ icap_send_auth_user is enabled. The first occurence of "%u" ++ is replaced by the authentified user name. If no "%u" is found, ++ the username is added at the end of the scheme. ++ ++ See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, ++ section 3.4 for details on this. ++ ++ Examples: ++ ++ icap_auth_scheme Local://%u ++ icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com ++ icap_auth_scheme WinNT://nt-domain/%u ++ icap_auth_scheme Radius://radius-server/%u ++DOC_END ++ ++NAME: icap_service ++TYPE: icap_service_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines a single ICAP service ++ ++ icap_service servicename vectoring_point bypass service_url [options ...] ++ ++ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache ++ This specifies at which point of request processing the ICAP ++ service should be plugged in. ++ bypass = 1|0 ++ If set to 1 and the ICAP server cannot be reached, the request will go ++ through without being processed by an ICAP server ++ service_url = icap://servername:port/service ++ ++ Options: ++ ++ no-keep-alive To always close the connection to icap server ++ after the transaction completes ++ ++ ++ Note: reqmod_precache and respmod_postcache is not yet implemented ++ ++ Load-balancing and high availability: ++ You can obtain load-balancing and high availability by defining a ++ named service with different definitions. Then, the client ++ loops through the different entities of the service providing ++ load-balancing. If an entity is marked as unreachable, the client goes ++ one step further to the next entity: you have the high-availability. ++ See the service_1 definition below ++ ++Example: ++icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod ++icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive ++icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod ++DOC_END ++ ++NAME: icap_class ++TYPE: icap_class_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines an ICAP service chain. If there are multiple services per ++ vectoring point, they are processed in the specified order. ++ ++ icap_class classname servicename... ++ ++Example: ++icap_class class_1 service_1 service_2 ++icap class class_2 service_1 service_3 ++DOC_END ++ ++NAME: icap_access ++TYPE: icap_access_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Redirects a request through an ICAP service class, depending ++ on given acls ++ ++ icap_access classname allow|deny [!]aclname... ++ ++ The icap_access statements are processed in the order they appear in ++ this configuration file. If an access list matches, the processing stops. ++ For an "allow" rule, the specified class is used for the request. A "deny" ++ rule simply stops processing without using the class. You can also use the ++ special classname "None". ++ ++ For backward compatibility, it is also possible to use services ++ directly here. ++Example: ++icap_access class_1 allow all ++DOC_END ++ ++COMMENT_START + MISCELLANEOUS + ----------------------------------------------------------------------------- + COMMENT_END +Index: src/cf_gen_defines +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v +retrieving revision 1.5 +retrieving revision 1.5.48.3 +diff -p -u -b -r1.5 -r1.5.48.3 +--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5 ++++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3 +@@ -18,12 +18,13 @@ BEGIN { + define["USE_UNLINKD"]="--enable-unlinkd" + define["USE_USERAGENT_LOG"]="--enable-useragent-log" + define["USE_WCCP"]="--enable-wccp" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +- DEFINE=define[$2] ++ DEFINE = define[$2] + else +- DEFINE="-D" $2 ++ DEFINE = "-D" $2 + print "{\"" $2 "\", \"" DEFINE "\", " + print "#if " $2 + print "1" +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.47.2.71 +retrieving revision 1.47.2.28.2.40 +diff -p -u -b -r1.47.2.71 -r1.47.2.28.2.40 +--- src/client_side.c 19 Oct 2005 02:13:20 -0000 1.47.2.71 ++++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40 +@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n"; + static CWCB clientWriteComplete; + static CWCB clientWriteBodyComplete; + static PF clientReadRequest; +-static PF connStateFree; ++PF connStateFree; + static PF requestTimeout; + static PF clientLifetimeTimeout; + static int clientCheckTransferDone(clientHttpRequest *); +@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien + static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); + static void clientPackTermBound(String boundary, MemBuf * mb); + static void clientInterpretRequestHeaders(clientHttpRequest *); +-static void clientProcessRequest(clientHttpRequest *); ++void clientProcessRequest(clientHttpRequest *); + static void clientProcessExpired(void *data); + static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); +-static int clientCachable(clientHttpRequest * http); +-static int clientHierarchical(clientHttpRequest * http); +-static int clientCheckContentLength(request_t * r); ++int clientCachable(clientHttpRequest * http); ++int clientHierarchical(clientHttpRequest * http); ++int clientCheckContentLength(request_t * r); + static DEFER httpAcceptDefer; + static log_type clientProcessRequest2(clientHttpRequest * http); + static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); + static int clientRequestBodyTooLarge(squid_off_t clen); + static void clientProcessBody(ConnStateData * conn); + static void clientEatRequestBody(clientHttpRequest *); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + static int + checkAccelOnly(clientHttpRequest * http) +@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res + http->request = requestLink(new_request); + } + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -931,11 +938,22 @@ httpRequestFree(void *data) + *H = http->next; + http->next = NULL; + dlinkDelete(&http->active, &ClientActiveRequests); ++#if HS_FEAT_ICAP ++ /*In the case that the upload of data breaks, we need this code here .... */ ++ if (NULL != http->icap_reqmod) { ++ if (cbdataValid(http->icap_reqmod)) ++ if (http->icap_reqmod->icap_fd > -1) { ++ comm_close(http->icap_reqmod->icap_fd); ++ } ++ cbdataUnlock(http->icap_reqmod); ++ http->icap_reqmod = NULL; ++ } ++#endif + cbdataFree(http); + } + + /* This is a handler normally called by comm_close() */ +-static void ++void + connStateFree(int fd, void *data) + { + ConnStateData *connState = data; +@@ -958,7 +976,6 @@ connStateFree(int fd, void *data) + } else + safe_free(connState->in.buf); + /* XXX account connState->in.buf */ +- pconnHistCount(0, connState->nrequests); + cbdataFree(connState); + #ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ +@@ -1103,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1122,7 +1139,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1148,7 +1165,7 @@ clientCachable(clientHttpRequest * http) + } + + /* Return true if we can query our neighbors for this object */ +-static int ++int + clientHierarchical(clientHttpRequest * http) + { + const char *url = http->uri; +@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -2449,6 +2466,11 @@ clientProcessRequest(clientHttpRequest * + debug(33, 4) ("clientProcessRequest: %s '%s'\n", + RequestMethodStr[r->method], + url); ++#if HS_FEAT_ICAP ++ if (clientIcapReqMod(http)) { ++ return; ++ } ++#endif + if (r->method == METHOD_CONNECT && !http->redirect.status) { + http->log_type = LOG_TCP_MISS; + sslStart(http, &http->out.size, &http->al.http.code); +@@ -2993,6 +3015,20 @@ clientReadRequest(int fd, void *data) + (long) conn->in.offset, (long) conn->in.size); + len = conn->in.size - conn->in.offset - 1; + } ++#if HS_FEAT_ICAP ++ /* ++ * This check exists because ICAP doesn't always work well ++ * with persistent (reused) connections. One version of the ++ * REQMOD code creates a fake ConnStateData, which doesn't have ++ * an in.buf. We want to make sure that the fake ConnStateData ++ * doesn't get used here. ++ */ ++ if (NULL == conn->in.buf) { ++ debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); ++ comm_close(fd); ++ return; ++ } ++#endif + statCounter.syscalls.sock.reads++; + size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); + if (size > 0) { +@@ -3096,7 +3132,8 @@ clientReadRequest(int fd, void *data) + /* add to the client request queue */ + for (H = &conn->chr; *H; H = &(*H)->next); + *H = http; +- conn->nrequests++; ++ F->pconn.uses++; ++ F->pconn.type = 0; + /* + * I wanted to lock 'http' here since its callback data for + * clientLifetimeTimeout(), but there's no logical place to +@@ -3266,7 +3303,7 @@ clientReadRequest(int fd, void *data) + } + + /* file_read like function, for reading body content */ +-static void ++void + clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) + { + ConnStateData *conn = request->body_reader_data; +@@ -3390,7 +3427,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data) + * Some data has been sent to the client, just close the FD + */ + comm_close(fd); +- } else if (conn->nrequests) { ++ } else if (fd_table[fd].pconn.uses) { + /* + * assume its a persistent connection; just close it + */ +@@ -3948,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re + } + } + } ++ ++#if HS_FEAT_ICAP ++static int ++clientIcapReqMod(clientHttpRequest * http) ++{ ++ ErrorState *err; ++ icap_service *service; ++ if (http->flags.did_icap_reqmod) ++ return 0; ++ if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) ++ return 0; ++ debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); ++ /* ++ * Note, we pass 'start' and 'log_addr' to ICAP so the access.log ++ * entry comes out right. The 'clientHttpRequest' created by ++ * the ICAP side is the one that gets logged. The first ++ * 'clientHttpRequest' does not get logged because its out.size ++ * is zero and log_type is unset. ++ */ ++ http->icap_reqmod = icapReqModStart(service, ++ http->uri, ++ http->request, ++ http->conn->fd, ++ http->start, ++ http->conn->log_addr, ++ (void *) http->conn); ++ if (NULL == http->icap_reqmod) { ++ return 0; ++ } else if (-1 == (int) http->icap_reqmod) { ++ /* produce error */ ++ http->icap_reqmod = NULL; ++ debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); ++ http->log_type = LOG_TCP_DENIED; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = ETIMEDOUT; ++ err->request = requestLink(http->request); ++ err->src_addr = http->conn->peer.sin_addr; ++ http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return 1; ++ } ++ cbdataLock(http->icap_reqmod); ++ http->flags.did_icap_reqmod = 1; ++ return 1; ++} ++#endif +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.18.6.6 +retrieving revision 1.18.6.2.12.9 +diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9 +--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6 ++++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9 +@@ -653,8 +653,8 @@ comm_close(int fd) + #endif + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + SSL_free(F->ssl); +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.29.2.18 +retrieving revision 1.29.2.8.2.17 +diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17 +--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18 ++++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17 +@@ -93,6 +93,7 @@ typedef enum { + ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ + ERR_TOO_BIG, + TCP_RESET, ++ ERR_ICAP_FAILURE, + ERR_INVALID_RESP, + ERR_MAX + } err_type; +@@ -438,6 +439,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -610,6 +614,12 @@ typedef enum { + MEM_TLV, + MEM_SWAP_LOG_DATA, + MEM_CLIENT_REQ_BUF, ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -709,9 +719,14 @@ typedef enum { + CBDATA_RemovalPolicyWalker, + CBDATA_RemovalPurgeWalker, + CBDATA_store_client, ++#ifdef HS_FEAT_ICAP ++ CBDATA_IcapStateData, ++ CBDATA_icap_service, ++#endif + CBDATA_FIRST_CUSTOM_TYPE = 1000 + } cbdata_type; + ++ + /* + * Return codes from checkVary(request) + */ +@@ -742,4 +757,68 @@ enum { + + #endif + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.13.6.15 +retrieving revision 1.13.6.3.2.15 +diff -p -u -b -r1.13.6.15 -r1.13.6.3.2.15 +--- src/forward.c 2 Sep 2005 02:13:43 -0000 1.13.6.15 ++++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15 +@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status + else + hierarchyNote(&fwdState->request->hier, fs->code, request->host); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; + if (fs->peer) + peerConnectSucceded(fs->peer); + fwdDispatch(fwdState); +@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data) + void + fwdFail(FwdState * fwdState, ErrorState * errorState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", + err_type_str[errorState->type], + httpStatusString(errorState->http_status), +@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data) + void + fwdUnregister(int fd, FwdState * fwdState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); + assert(fd == fwdState->server_fd); + assert(fd > -1); +@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat + void + fwdComplete(FwdState * fwdState) + { +- StoreEntry *e = fwdState->entry; ++ StoreEntry *e; ++ if (NULL == fwdState) ++ return; ++ e = fwdState->entry; + assert(e->store_status == STORE_PENDING); + debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), + e->mem_obj->reply->sline.status); +Index: src/globals.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/globals.h,v +retrieving revision 1.14.6.7 +retrieving revision 1.14.6.3.2.5 +diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5 +--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7 ++++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5 +@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */ + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.17.6.32 +retrieving revision 1.17.6.3.6.39 +diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.39 +--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32 ++++ src/http.c 23 Nov 2005 20:33:07 -0000 1.17.6.3.6.39 +@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry; + + static PF httpReadReply; + static void httpSendRequest(HttpStateData *); +-static PF httpStateFree; ++PF httpStateFree; + static PF httpTimeout; + static void httpCacheNegatively(StoreEntry *); + static void httpMakePrivate(StoreEntry *); +@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *) + static int httpCachableReply(HttpStateData *); + static void httpMaybeRemovePublic(StoreEntry *, http_status); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data) + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; ++#if HS_FEAT_ICAP ++ cbdataUnlock(httpState->icap_writer); ++#endif + cbdataFree(httpState); + } + +@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht + } + + /* rewrite this later using new interfaces @?@ */ +-static void ++void + httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) + { + StoreEntry *entry = httpState->entry; +@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; ++ squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ +- if (httpState->reply_hdr_state < 2) ++ if (httpState->reply_hdr_state < 2) { ++ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", ++ httpState->reply_hdr_state); + return 0; ++ } + clen = httpReplyBodySize(httpState->request->method, reply); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ content_bytes_read = httpState->icap_writer->fake_content_length; ++ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); ++ } else ++#endif ++ content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ +- if (mem->inmem_hi > clen + reply->hdr_sz) ++ if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ +- if (mem->inmem_hi < clen + reply->hdr_sz) ++ if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data) + delay_id delay_id; + #endif + ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ /*The folowing entry can not be marked as aborted. ++ * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; +@@ -579,6 +605,33 @@ httpReadReply(int fd, void *data) + else + delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); + #endif ++ ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ IcapStateData *icap = httpState->icap_writer; ++ /* ++ * Ok we have a received a response from the web server, so try to ++ * connect the icap server if it's the first attemps. If we try ++ * to connect to the icap server, defer this request (do not read ++ * the buffer), and defer until icapConnectOver() is not called. ++ */ ++ if (icap->flags.connect_requested == 0) { ++ debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); ++ if (!icapConnect(icap, icapConnectOver)) { ++ debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); ++ icap->flags.connect_requested = 1; ++ /* Wait for more data or EOF condition */ ++ commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ } ++#endif ++ + errno = 0; + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, read_sz); +@@ -595,7 +648,13 @@ httpReadReply(int fd, void *data) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +- if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ (void) 0; ++ else ++#endif ++ ++ if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { + /* Skip whitespace */ + while (len > 0 && xisspace(*buf)) + xmemmove(buf, buf + 1, len--); +@@ -625,6 +684,12 @@ httpReadReply(int fd, void *data) + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { ++ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); ++ icapSendRespMod(httpState->icap_writer, buf, len, 1); ++ } ++#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we +@@ -677,7 +742,28 @@ httpReadReply(int fd, void *data) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ if (cbdataValid(httpState->icap_writer)) { ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } ++ } else ++#endif + storeAppend(entry, buf, len); ++ ++ ++ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + /* + * the above storeAppend() call could ABORT this entry, +@@ -724,10 +810,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); +@@ -766,6 +863,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -776,6 +877,34 @@ httpReadReply(int fd, void *data) + } + } + ++#ifdef HS_FEAT_ICAP ++static int ++httpReadReplyWaitForIcap(int fd, void *data) ++{ ++ HttpStateData *httpState = data; ++ if (NULL == httpState->icap_writer) ++ return 0; ++ /* ++ * Do not defer when we are not connected to the icap server. ++ * Defer when the icap server connection is not established but pending ++ * Defer when the icap server is busy (writing on the socket) ++ */ ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", ++ fd, httpState->icap_writer->flags.connect_requested); ++ if (!httpState->icap_writer->flags.connect_requested) ++ return 0; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", ++ fd, httpState->icap_writer->flags.connect_pending); ++ if (httpState->icap_writer->flags.connect_pending) ++ return 1; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", ++ fd, httpState->icap_writer->flags.write_pending); ++ if (httpState->icap_writer->flags.write_pending) ++ return 1; ++ return 0; ++} ++#endif ++ + /* This will be called when request write is complete. Schedule read of + * reply. */ + static void +@@ -803,6 +932,63 @@ httpSendComplete(int fd, char *bufnotuse + comm_close(fd); + return; + } else { ++ /* Schedule read reply. */ ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart( ++ ICAP_SERVICE_RESPMOD_PRECACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (-1 == (int) httpState->icap_writer) { ++ /* TODO: send error here and exit */ ++ ErrorState *err; ++ httpState->icap_writer = 0; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(httpState->orig_request); ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ return; ++ } else if (httpState->icap_writer) { ++ request_flags fake_flags = httpState->orig_request->flags; ++ method_t fake_method = entry->mem_obj->method; ++ const char *fake_msg = "this is a fake entry for " ++ " response sent to an ICAP RESPMOD server"; ++ cbdataLock(httpState->icap_writer); ++ /* ++ * this httpState will give the data it reads to ++ * the icap server, rather than put it into ++ * a StoreEntry ++ */ ++ storeUnlockObject(httpState->entry); ++ storeUnregisterAbort(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -811,8 +997,18 @@ httpSendComplete(int fd, char *bufnotuse + * the timeout for POST/PUT requests that have very large + * request bodies. + */ ++ ++ /* removed in stable5: ++ * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ */ + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +- commSetDefer(fd, fwdCheckDeferRead, entry); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); ++ commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); ++ } else ++#endif ++ commSetDefer(httpState->fd, fwdCheckDeferRead, entry); + } + httpState->flags.request_sent = 1; + } +@@ -1010,8 +1206,11 @@ httpBuildRequestHeader(request_t * reque + if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { + const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); + httpHdrCcSetMaxAge(cc, getMaxAge(url)); ++#ifndef HS_FEAT_ICAP ++ /* Don;t bother - if the url you want to cache is redirected? */ + if (strLen(request->urlpath)) + assert(strstr(url, strBuf(request->urlpath))); ++#endif + } + /* Set no-cache if determined needed but not found */ + if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) +@@ -1119,6 +1318,7 @@ httpStart(FwdState * fwd) + int fd = fwd->server_fd; + HttpStateData *httpState; + request_t *proxy_req; ++ /* ErrorState *err; */ + request_t *orig_req = fwd->request; + debug(11, 3) ("httpStart: \"%s %s\"\n", + RequestMethodStr[orig_req->method], +@@ -1156,12 +1356,22 @@ httpStart(FwdState * fwd) + httpState->request = requestLink(orig_req); + httpState->orig_request = requestLink(orig_req); + } ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (httpState->icap_writer) { ++ return; ++ } ++ } ++#endif + /* + * register the handler to free HTTP state data when the FD closes + */ + comm_add_close_handler(fd, httpStateFree, httpState); + statCounter.server.all.requests++; + statCounter.server.http.requests++; ++ + httpSendRequest(httpState); + /* + * We used to set the read timeout here, but not any more. +Index: src/icap_common.c +=================================================================== +RCS file: src/icap_common.c +diff -N src/icap_common.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_common.c 22 Nov 2005 22:41:48 -0000 1.1.2.39 +@@ -0,0 +1,785 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} +Index: src/icap_opt.c +=================================================================== +RCS file: src/icap_opt.c +diff -N src/icap_opt.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_opt.c 22 Nov 2005 22:41:48 -0000 1.1.2.17 +@@ -0,0 +1,519 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: src/icap_reqmod.c +=================================================================== +RCS file: src/icap_reqmod.c +diff -N src/icap_reqmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_reqmod.c 6 Dec 2005 21:53:44 -0000 1.1.2.58 +@@ -0,0 +1,976 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++#define ICAP_PROXY_KEEP_ALIVE 0 ++ ++/* ++ * These once-static functions are required to be global for ICAP ++ */ ++ ++PF clientReadRequest; ++PF connStateFree; ++int clientReadDefer(int fd, void *data); ++int clientCheckContentLength(request_t * r); ++void clientProcessRequest(clientHttpRequest *); ++int clientCachable(clientHttpRequest *); ++int clientHierarchical(clientHttpRequest *); ++void clientReadBody(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++ ++static PF icapReqModReadHttpHdrs; ++static PF icapReqModReadHttpBody; ++static CWCB icapReqModSendBodyChunk; ++static CBCB icapReqModBodyHandler; ++static BODY_HANDLER icapReqModBodyReader; ++static STRCB icapReqModMemBufAppend; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++static const char *crlf = "\r\n"; ++ ++/* ++ * icapExpectedHttpReqHdrSize ++ * ++ * calculate the size of the HTTP headers that we expect ++ * to read from the ICAP server. ++ */ ++static int ++icapExpectedHttpReqHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) ++ return (icap->enc.req_body - icap->enc.req_hdr); ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ fatal("icapExpectedHttpReqHdrSize: unexpected case"); ++ return 0; ++} ++ ++/* ++ * icapReqModCreateClientState ++ * ++ * Creates fake client_side data structures so we can use ++ * that module to read/parse the HTTP request that we read ++ * from the ICAP server. ++ */ ++static clientHttpRequest * ++icapReqModCreateClientState(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http; ++ if (!cbdataValid(icap->reqmod.client_cookie)) { ++ debug(81, 3) ("Whups, client cookie invalid\n"); ++ icap->reqmod.client_fd = -1; ++ return NULL; ++ } ++ http = cbdataAlloc(clientHttpRequest); ++ /* ++ * use our own urlCanonicalClean here, because urlCanonicalClean ++ * may strip everything after a question-mark. As http->uri ++ * is used when doing a request to a parent proxy, we need the full ++ * url here. ++ */ ++ http->uri = xstrdup(urlCanonical(icap->request)); ++ http->log_uri = xstrndup(http->uri, MAX_URL); ++ http->range_iter.boundary = StringNull; ++ http->request = requestLink(request ? request : icap->request); ++ http->flags.did_icap_reqmod = 1; ++ http->start = icap->reqmod.start; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ ++ http->conn->chr = http; ++ ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ err->request = requestLink(request); ++ request->flags.proxy_keepalive = 0; ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ if (!clientCheckContentLength(request)) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED); ++ err->request = requestLink(request); ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ /* Do we expect a request-body? */ ++ if (request->content_length > 0) { ++ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); ++ if (request->body_reader_data) ++ cbdataUnlock(request->body_reader_data); ++ request->body_reader = icapReqModBodyReader; ++ request->body_reader_data = icap; /* XXX cbdataLock? */ ++ cbdataLock(icap); /*Yes sure ..... */ ++ memBufDefInit(&icap->reqmod.http_entity.buf); ++ } ++ if (clientCachable(http)) ++ request->flags.cachable = 1; ++ if (clientHierarchical(http)) ++ request->flags.hierarchical = 1; ++ clientProcessRequest(http); ++} ++ ++/* ++ * icapReqModParseHttpError ++ * ++ * Handle an error when parsing the new HTTP request we read ++ * from the ICAP server. ++ */ ++static void ++icapReqModParseHttpError(IcapStateData * icap, const char *reason) ++{ ++ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); ++} ++ ++/* ++ * icapEntryError ++ * ++ * A wrapper for errorCon() and errorAppendEntry(). ++ */ ++static void ++icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) ++{ ++ ErrorState *err; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, null_request_flags); ++ err = errorCon(et, hs); ++ err->xerrno = xerrno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(http->entry, err); ++} ++ ++/* ++ * icapReqModParseHttpRequest ++ * ++ * Parse the HTTP request that we read from the ICAP server. ++ * Creates and fills in the request_t structure. ++ */ ++static void ++icapReqModParseHttpRequest(IcapStateData * icap) ++{ ++ char *mstr; ++ char *uri; ++ char *inbuf; ++ char *t; ++ char *token; ++ char *headers; ++ method_t method; ++ request_t *request; ++ http_version_t http_ver; ++ int reqlen = icap->reqmod.hdr_buf.size; ++ int hdrlen; ++ ++ /* ++ * Lazy, make a copy of the buf so I can chop it up with strtok() ++ */ ++ inbuf = xcalloc(reqlen + 1, 1); ++ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); ++ ++ if ((mstr = strtok(inbuf, "\t ")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); ++ icapReqModParseHttpError(icap, "error:invalid-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ method = urlParseMethod(mstr); ++ if (method == METHOD_NONE) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", ++ mstr); ++ icapReqModParseHttpError(icap, "error:unsupported-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ /* look for URL+HTTP/x.x */ ++ if ((uri = strtok(NULL, "\n")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); ++ icapReqModParseHttpError(icap, "error:missing-url"); ++ xfree(inbuf); ++ return; ++ } ++ while (xisspace(*uri)) ++ uri++; ++ t = uri + strlen(uri); ++ assert(*t == '\0'); ++ token = NULL; ++ while (t > uri) { ++ t--; ++ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { ++ token = t + 1; ++ break; ++ } ++ } ++ while (t > uri && xisspace(*t)) ++ *(t--) = '\0'; ++ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); ++ if (token == NULL) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); ++ icapReqModParseHttpError(icap, "error:missing-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); ++ icapReqModParseHttpError(icap, "error:invalid-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", ++ http_ver.major, http_ver.minor); ++ ++ headers = strtok(NULL, null_string); ++ hdrlen = inbuf + reqlen - headers; ++ ++ if ((request = urlParse(method, uri)) == NULL) { ++ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ /* compile headers */ ++ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { ++ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", ++ uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, ++ 3) ++ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); ++ request->http_ver = http_ver; ++ request->client_addr = icap->request->client_addr; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request != NULL) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service *service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->reqmod.http_entity.bytes_read >= icap->request->content_length) ++ icap->flags.reqmod_http_entity_eof = 1; ++ ++ if (!icap->flags.reqmod_http_entity_eof) ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ /* ++ * Notify the other side if it is waiting for data from us ++ */ ++ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.callback); ++ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.callback && icap->reqmod.http_entity.buf.size) { ++ icapReqModPassHttpBody(icap, ++ icap->reqmod.http_entity.callback_buf, ++ icap->reqmod.http_entity.callback_bufsize, ++ icap->reqmod.http_entity.callback, ++ icap->reqmod.http_entity.callback_data); ++ icap->reqmod.http_entity.callback = NULL; ++ cbdataUnlock(icap->reqmod.http_entity.callback_data); ++ ++ } ++} ++ ++/* ++ * icapReqModPassHttpBody ++ * ++ * Called from http.c after request headers have been sent. ++ * This function feeds the http.c module chunks of the request ++ * body that were stored in the http_entity.buf MemBuf. ++ */ ++static void ++icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ debug(81, 3) ("icapReqModPassHttpBody: called\n"); ++ if (!buf) { ++ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", ++ icap->icap_fd, buf, (int) size, cbdata); ++ comm_close(icap->icap_fd); ++ return; ++ } ++ if (!cbdataValid(cbdata)) { ++ debug(81, ++ 1) ++ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", ++ icap->icap_fd); ++ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ ++ /*icapReqModKeepAliveOrClose(icap); */ ++ return; ++ } ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.buf.size) { ++ int copy_sz = icap->reqmod.http_entity.buf.size; ++ if (copy_sz > size) ++ copy_sz = size; ++ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); ++ /* XXX don't let Alex see this ugliness */ ++ xmemmove(icap->reqmod.http_entity.buf.buf, ++ icap->reqmod.http_entity.buf.buf + copy_sz, ++ icap->reqmod.http_entity.buf.size - copy_sz); ++ icap->reqmod.http_entity.buf.size -= copy_sz; ++ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", ++ copy_sz); ++ callback(buf, copy_sz, cbdata); ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ return; ++ } ++ if (icap->flags.reqmod_http_entity_eof) { ++ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); ++ callback(buf, 0, cbdata); ++ icapReqModKeepAliveOrClose(icap); ++ return; ++ } ++ /* ++ * We have no data for the other side at this point. Save all ++ * these values and use them when we do have data. ++ */ ++ assert(NULL == icap->reqmod.http_entity.callback); ++ icap->reqmod.http_entity.callback = callback; ++ icap->reqmod.http_entity.callback_data = cbdata; ++ icap->reqmod.http_entity.callback_buf = buf; ++ icap->reqmod.http_entity.callback_bufsize = size; ++ cbdataLock(icap->reqmod.http_entity.callback_data); ++} ++ ++/* ++ * Body reader handler for use with request->body_reader function ++ * Simple a wrapper for icapReqModPassHttpBody function ++ */ ++ ++static void ++icapReqModBodyReader(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ IcapStateData *icap = request->body_reader_data; ++ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); ++} ++ ++/* ++ * icapReqModMemBufAppend ++ * ++ * stupid wrapper to eliminate compiler warnings ++ */ ++static void ++icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) ++{ ++ memBufAppend(data, buf, size); ++} +Index: src/icap_respmod.c +=================================================================== +RCS file: src/icap_respmod.c +diff -N src/icap_respmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_respmod.c 23 Nov 2005 20:34:34 -0000 1.1.2.60 +@@ -0,0 +1,1039 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ ++ memBufPrintf(mb, crlf); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++ if (service->flags.allow_204) ++ memBufPrintf(mb, "Allow: 204\r\n"); ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ } ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ /* No data sent yet. Start with headers */ ++ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { ++ buf += icap->sc; ++ len -= icap->sc; ++ } ++ /* ++ * Then we do not have http responce headers. All data (previous and those in buf) ++ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... ++ */ ++ if (icap->sc < 0) { ++ memBufAppend(&icap->respmod.buffer, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->sc = icap->respmod.req_hdr_copy.size; ++ icap->respmod.req_hdr_copy.size = 0; ++ buf = NULL; ++ len = 0; ++ } ++ } ++ if (0 == icap->sc) { ++ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ ++ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); ++ memBufClean(&mb); ++ return; ++ } ++#if ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ ++ icap->flags.preview_done = 1; ++ ++ if (!icap->flags.preview_done) { ++ /* preview not yet sent */ ++ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size ++ && len > 0) { ++ /* Try to collect at least preview_size+1 bytes */ ++ /* By collecting one more byte than needed for preview we know best */ ++ /* whether we have to send the ieof chunk extension */ ++ size = icap->respmod.buffer.size + len; ++ if (size > preview_size + 1) ++ size = preview_size + 1; ++ size -= icap->respmod.buffer.size; ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", ++ icap->icap_fd, size); ++ memBufAppend(&icap->respmod.buffer, buf, size); ++ buf = ((char *) buf) + size; ++ len -= size; ++ } ++ if (icap->respmod.buffer.size > preview_size || theEnd) { ++ /* we got enough bytes for preview or this is the last call */ ++ /* add preview preview now */ ++ if (icap->respmod.buffer.size > 0) { ++ size = icap->respmod.buffer.size; ++ if (size > preview_size) ++ size = preview_size; ++ memBufPrintf(&mb, "%x\r\n", size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += size; ++ } ++ if (icap->respmod.buffer.size <= preview_size) { ++ /* content length is less than preview size+1 */ ++ if (icap->respmod.res_body_sz) ++ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ } else { ++ char ch; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ /* end of preview, wait for continue or 204 signal */ ++ /* copy the extra byte and all other data to the icap buffer */ ++ /* so that it can be handled next time */ ++ ch = icap->respmod.buffer.buf[preview_size]; ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ memBufAppend(&icap->respmod.buffer, &ch, 1); ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", ++ icap->icap_fd, len + 1); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ } ++ icap->flags.preview_done = 1; ++ icap->flags.wait_for_preview_reply = 1; ++ } ++ } else if (icap->flags.wait_for_preview_reply) { ++ /* received new data while waiting for preview response */ ++ /* add data to internal buffer and send later */ ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", ++ icap->icap_fd, len); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ /* do not send any data now while waiting for preview response */ ++ /* but prepare for read more data on the HTTP connection */ ++ memBufClean(&mb); ++ return; ++ } else ++#endif ++ { ++ /* after preview completed and ICAP preview response received */ ++ /* there may still be some data in the buffer */ ++ if (icap->respmod.buffer.size > 0) { ++ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, ++ icap->respmod.buffer.size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += icap->respmod.buffer.size; ++ memBufReset(&icap->respmod.buffer); ++ } ++ if (len > 0) { ++ memBufPrintf(&mb, "%x\r\n", len); ++ memBufAppend(&mb, buf, len); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += len; ++ } ++ if (icap->flags.send_zero_chunk) { ++ /* send zero end chunk */ ++ icap->flags.send_zero_chunk = 0; ++ icap->flags.http_server_eof = 1; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ } ++ /* wait for data coming from ICAP server as soon as we sent something */ ++ /* but of course only until we got the response header */ ++ if (!icap->flags.got_reply) ++ icap->flags.wait_for_reply = 1; ++ } ++ commSetTimeout(icap->icap_fd, -1, NULL, NULL); ++ ++ if (!mb.size) { ++ memBufClean(&mb); ++ return; ++ } ++ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ icap->flags.write_pending = 1; ++ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); ++} ++ ++static void ++icapRespModReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ int status = 0; ++ int isIcap = 0; ++ int directResponse = 0; ++ ErrorState *err; ++ const char *start; ++ const char *end; ++ ++ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ }; ++ /* OK here we have responce. Lets stop filling the ++ * icap->respmod.resp_copy buffer .... ++ */ ++ icap->flags.copy_response = 0; ++ ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++#if ICAP_PREVIEW ++ if (icap->flags.wait_for_preview_reply) { ++ if (100 == status) { ++ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ /* if http_server_eof ++ * call again icapSendRespMod to handle data that ++ * was received while waiting for this ICAP response ++ * else let http to call icapSendRespMod when new data arrived ++ */ ++ if (icap->flags.http_server_eof) ++ icapSendRespMod(icap, NULL, 0, 0); ++ /* ++ * reset the header to send the rest of the preview ++ */ ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufReset(&icap->icap_hdr); ++ ++ /*We do n't need it any more ....... */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ ++ return; ++ } ++ if (204 == status) { ++ debug(81, ++ 5) ("icapRespModReadReply: 204 No modification received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ } ++ } ++#endif /*ICAP_PREVIEW */ ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ if (204 == status) { ++ debug(81, 3) ("got 204 status from ICAP server\n"); ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ ++ /* ++ * "directResponse" is the normal case here. If we don't have ++ * a response header or body, it is an error. ++ */ ++ if (!directResponse) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ /* Next, gobble any data before the HTTP response starts */ ++ if (icap->enc.res_hdr > -1) ++ icap->bytes_to_gobble = icap->enc.res_hdr; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++} ++ ++ ++/* ++ * Gobble up (read) some bytes until we get to the start of the body ++ */ ++static void ++icapRespModGobble(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); ++ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, ++ icap->bytes_to_gobble); ++ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); ++ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); ++ if (len < 0) { ++ /* XXX error */ ++ abort(); ++ } ++ icap->bytes_to_gobble -= len; ++ if (icap->bytes_to_gobble) ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++ else ++ icapReadReply(fd, icap); ++} ++ ++ ++static void ++icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ErrorState *err; ++ ++ icap->flags.write_pending = 0; ++ debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ if (cbdataValid(icap)) ++ err->request = requestLink(icap->request); ++ storeEntryReset(icap->respmod.entry); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); ++ comm_close(fd); ++ return; ++ } ++ if (icap->flags.send_zero_chunk) { ++ debug(81, ++ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); ++ icap->flags.send_zero_chunk = 0; ++ icapSendRespMod(icap, NULL, 0, 1); ++ return; ++ } ++ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { ++ /* Schedule reading the ICAP response */ ++ debug(81, ++ 3) ++ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", ++ fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++#if 1 ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++#else ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ /* ++ * Set the read timeout only after all data has been sent ++ * or we are waiting for a preview response ++ * If the ICAP server does not return any data till all data ++ * has been sent, we are likely to hit the timeout for large ++ * HTTP bodies ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ } ++#endif ++ } ++} ++ ++void ++icapConnectOver(int fd, int status, void *data) ++{ ++ ErrorState *err; ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ if (status < 0) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); ++ icapOptSetUnreachable(icap->current_service); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++} ++ ++ ++ ++IcapStateData * ++icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, ++ http_state_flags http_flags) ++{ ++ IcapStateData *icap = NULL; ++ CNCB *theCallback = NULL; ++ icap_service *service = NULL; ++ ++ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); ++ assert(type >= 0 && type < ICAP_SERVICE_MAX); ++ ++ service = icapService(type, request); ++ if (!service) { ++ debug(81, 3) ("icapRespModStart: no service found\n"); ++ return NULL; /* no service found */ ++ } ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ++ ("icapRespModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ++ ("icapRespModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ switch (type) { ++ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change ++ * this switch, because callbacks isn't keep */ ++ case ICAP_SERVICE_RESPMOD_PRECACHE: ++ theCallback = icapConnectOver; ++ break; ++ default: ++ fatalf("icapRespModStart: unsupported service type '%s'\n", ++ icap_service_type_str[type]); ++ break; ++ } ++ ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->request = requestLink(request); ++ icap->respmod.entry = entry; ++ if (entry) ++ storeLockObject(entry); ++ icap->http_flags = http_flags; ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ ++ /* ++ * Don't create socket to the icap server now, but only for the first ++ * packet receive from the http server. This will resolve all timeout ++ * between the web server and icap server. ++ */ ++ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); ++ icap->flags.connect_requested = 0; ++ ++ /* ++ * make a copy the HTTP response that we send to the ICAP server in ++ * case it turns out to be a 204 ++ */ ++#ifdef SUPPORT_ICAP_204 ++ icap->flags.copy_response = 1; ++#elif ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) ++ icap->flags.copy_response = 0; ++ else ++ icap->flags.copy_response = 1; ++#else ++ icap->flags.copy_response = 0; ++#endif ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapRespModStart: returning %p\n", icap); ++ return icap; ++} ++ ++static int ++icapHttpReplyHdrState(IcapStateData * icap) ++{ ++ assert(icap); ++ if (NULL == icap->httpState) ++ return 0; ++ return icap->httpState->reply_hdr_state; ++} ++ ++static void ++icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) ++{ ++ if (NULL == icap->httpState) { ++ icap->httpState = cbdataAlloc(HttpStateData); ++ icap->httpState->request = requestLink(icap->request); ++ icap->httpState->orig_request = requestLink(icap->request); ++ icap->httpState->entry = icap->respmod.entry; ++ storeLockObject(icap->httpState->entry); /* lock it */ ++ } ++ httpProcessReplyHeader(icap->httpState, buf, size); ++ if (2 == icap->httpState->reply_hdr_state) ++ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); ++} ++ ++/* ++ * icapRespModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapRespModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink((request_t *) request); ++ err->xerrno = errno; ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ } else { ++ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", ++ fd); ++ comm_close(fd); ++ } ++ return; ++ } ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++} ++ ++static int ++icapReadReply2(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ debug(81, 3) ("icapReadReply2\n"); ++ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); ++ err->xerrno = errno; ++ err->request = requestLink((request_t *) request); ++ errorAppendEntry(entry, err); ++ icap->flags.http_server_eof = 1; ++ return -1; ++ } ++ if (icap->chunk_buf.size == 0) { ++ /* Retrieval done. */ ++ if (icapHttpReplyHdrState(icap) < 2) ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ icap->flags.http_server_eof = 1; ++ icapReadReply3(icap); ++ return 0; ++ } ++ if (icapHttpReplyHdrState(icap) == 0) { ++ int expect = icapExpectedHttpReplyHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed < 0 || needed >= 0); ++ if (0 > expect) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ } else if (0 == expect) { ++ /* ++ * this icap reply doesn't give us new HTTP headers ++ * so we must copy them from our copy ++ */ ++ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, ++ __LINE__); ++ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ ++ storeAppend(entry, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ } ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ ++ } else if (needed) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ if (icap->chunk_buf.size >= needed) { ++ storeAppend(entry, icap->chunk_buf.buf, needed); ++ so_far += needed; ++ xmemmove(icap->chunk_buf.buf, ++ icap->chunk_buf.buf + needed, ++ icap->chunk_buf.size - needed); ++ icap->chunk_buf.size -= needed; ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; ++ } else { ++ /* ++ * We don't have the full HTTP reply headers yet, so keep ++ * the partial reply buffered in 'chunk_buf' and wait ++ * for more. ++ */ ++ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); ++ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } ++ } ++ icap->http_header_bytes_read_so_far = so_far; ++ } ++ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, ++ (int) icap->chunk_buf.size); ++ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, ++ icap->flags.no_content); ++ if (icap->flags.no_content) { ++ /* data from http.c is not chunked */ ++ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", ++ icap->chunk_buf.size); ++ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); ++ icap->chunk_buf.size = 0; ++ } ++ } else if (2 == icapHttpReplyHdrState(icap)) { ++ if (icap->chunk_buf.size) ++ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); ++ } ++ icapReadReply3(icap); ++ return 0; ++} ++ ++static void ++icapReadReply3(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ int fd = icap->icap_fd; ++ debug(81, 3) ("icapReadReply3\n"); ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ icapRespModKeepAliveOrClose(icap); ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.28.6.25 +retrieving revision 1.28.6.8.2.11 +diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11 +--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25 ++++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11 +@@ -350,6 +350,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + authenticateShutdown(); + externalAclShutdown(); +@@ -378,6 +381,9 @@ mainReconfigure(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -507,6 +513,9 @@ mainInitialize(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); +Index: src/mem.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/mem.c,v +retrieving revision 1.13 +retrieving revision 1.13.28.2 +diff -p -u -b -r1.13 -r1.13.28.2 +--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13 ++++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2 +@@ -243,6 +243,13 @@ memInit(void) + memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.140.1 +diff -p -u -b -r1.2 -r1.2.140.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1 +@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str"; + $pat{'icp_opcode'} = "icp_opcode_str"; + $pat{'swap_log_op'} = "swap_log_op_str"; + $pat{'lookup_t'} = "lookup_t_str"; ++$pat{'icap_service_t'} = "icap_service_type_str"; + + $state = 0; # start state + while (<>) { +Index: src/pconn.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/pconn.c,v +retrieving revision 1.6.38.2 +retrieving revision 1.6.60.2 +diff -p -u -b -r1.6.38.2 -r1.6.60.2 +--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2 ++++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2 +@@ -46,6 +46,9 @@ struct _pconn { + #define PCONN_HIST_SZ (1<<16) + int client_pconn_hist[PCONN_HIST_SZ]; + int server_pconn_hist[PCONN_HIST_SZ]; ++#ifdef HS_FEAT_ICAP ++int icap_server_pconn_hist[PCONN_HIST_SZ]; ++#endif + + static PF pconnRead; + static PF pconnTimeout; +@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); + } ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(e, ++ "\n" ++ "ICAP-server persistent connection counts:\n" ++ "\n" ++ "\treq/\n" ++ "\tconn count\n" ++ "\t---- ---------\n"); ++ for (i = 0; i < PCONN_HIST_SZ; i++) { ++ if (icap_server_pconn_hist[i] == 0) ++ continue; ++ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); ++ } ++#endif + } + + /* ========== PUBLIC FUNCTIONS ============================================ */ +@@ -173,6 +190,9 @@ pconnInit(void) + for (i = 0; i < PCONN_HIST_SZ; i++) { + client_pconn_hist[i] = 0; + server_pconn_hist[i] = 0; ++#ifdef HS_FEAT_ICAP ++ icap_server_pconn_hist[i] = 0; ++#endif + } + pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); + pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); +@@ -248,11 +268,15 @@ pconnHistCount(int what, int i) + { + if (i >= PCONN_HIST_SZ) + i = PCONN_HIST_SZ - 1; +- /* what == 0 for client, 1 for server */ ++ /* what == 0 for client, 1 for server, 2 for ICAP server */ + if (what == 0) + client_pconn_hist[i]++; + else if (what == 1) + server_pconn_hist[i]++; ++#ifdef HS_FEAT_ICAP ++ else if (what == 2) ++ icap_server_pconn_hist[i]++; ++#endif + else + assert(0); + } +Index: src/protos.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/protos.h,v +retrieving revision 1.41.6.33 +retrieving revision 1.41.6.13.2.37 +diff -p -u -b -r1.41.6.33 -r1.41.6.13.2.37 +--- src/protos.h 16 Sep 2005 02:13:25 -0000 1.41.6.33 ++++ src/protos.h 6 Dec 2005 21:53:44 -0000 1.41.6.13.2.37 +@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *); + /* http.c */ + extern int httpCachable(method_t); + extern void httpStart(FwdState *); ++extern void httpParseReplyHeaders(const char *, http_reply *); ++extern void httpProcessReplyHeader(HttpStateData *, const char *, int); + extern int httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, +@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c + extern FREE *memBufFreeFunc(MemBuf * mb); + /* puts report on MemBuf _module_ usage into mb */ + extern void memBufReport(MemBuf * mb); ++extern int memBufRead(int fd, MemBuf * mb); + + extern char *mime_get_header(const char *mime, const char *header); + extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); +@@ -1341,4 +1344,49 @@ extern void externalAclShutdown(void); + extern int externalAclRequiresAuth(void *acl_data); + extern char *strtokFile(void); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.13.6.8 +retrieving revision 1.13.6.6.2.11 +diff -p -u -b -r1.13.6.8 -r1.13.6.6.2.11 +--- src/squid.h 26 Mar 2005 03:15:58 -0000 1.13.6.8 ++++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11 +@@ -38,6 +38,14 @@ + #include "config.h" + + /* ++ * experimental defines for ICAP ++ */ ++#ifdef HS_FEAT_ICAP ++#define ICAP_PREVIEW 1 ++#define SUPPORT_ICAP_204 0 ++#endif ++ ++/* + * On some systems, FD_SETSIZE is set to something lower than the + * actual number of files which can be opened. IRIX is one case, + * NetBSD is another. So here we increase FD_SETSIZE to our +Index: src/stat.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/stat.c,v +retrieving revision 1.13.6.14 +retrieving revision 1.13.6.7.2.7 +diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7 +--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14 ++++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7 +@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min + storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", + XAVG(server.other.kbytes_out.kb)); + ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", ++ XAVG(icap.all.requests)); ++ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", ++ XAVG(icap.all.errors)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", ++ XAVG(icap.all.kbytes_in.kb)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", ++ XAVG(icap.all.kbytes_out.kb)); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", + XAVG(icp.pkts_sent)); + storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", +@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry) + storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", + (int) f->server.other.kbytes_out.kb); + ++#if HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %d\n", ++ (int) f->icap.all.requests); ++ storeAppendPrintf(sentry, "icap.all.errors = %d\n", ++ (int) f->icap.all.errors); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", ++ (int) f->icap.all.kbytes_in.kb); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", ++ (int) f->icap.all.kbytes_out.kb); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", + f->icp.pkts_sent); + storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", +@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s) + storeAppendPrintf(s, "\tme: %s:%d\n", + inet_ntoa(conn->me.sin_addr), + ntohs(conn->me.sin_port)); +- storeAppendPrintf(s, "\tnrequests: %d\n", +- conn->nrequests); + storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", + conn->defer.n, (long int) conn->defer.until); + } +Index: src/store.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/store.c,v +retrieving revision 1.16.6.9 +retrieving revision 1.16.6.2.2.8 +diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8 +--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9 ++++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8 +@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.48.2.43 +retrieving revision 1.48.2.9.2.48 +diff -p -u -b -r1.48.2.43 -r1.48.2.9.2.48 +--- src/structs.h 4 Sep 2005 02:13:28 -0000 1.48.2.43 ++++ src/structs.h 30 Nov 2005 21:52:15 -0000 1.48.2.9.2.48 +@@ -384,6 +384,22 @@ struct _RemovalPolicySettings { + wordlist *args; + }; + ++#if HS_FEAT_ICAP ++struct _IcapConfig { ++ int onoff; ++ int preview_enable; ++ icap_service *service_head; ++ icap_class *class_head; ++ icap_access *access_head; ++ int preview_size; ++ int check_interval; ++ int send_client_ip; ++ int send_auth_user; ++ char *auth_scheme; ++}; ++ ++#endif /* HS_FEAT_ICAP */ ++ + struct _SquidConfig { + struct { + squid_off_t maxSize; +@@ -714,6 +730,9 @@ struct _SquidConfig { + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + external_acl *externalAclHelperList; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -787,7 +806,10 @@ struct _fde { + } flags; + squid_off_t bytes_read; + squid_off_t bytes_written; +- int uses; /* ie # req's over persistent conn */ ++ struct { ++ int uses; ++ int type; ++ } pconn; + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; +@@ -982,6 +1004,130 @@ struct _http_state_flags { + unsigned int request_sent:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -993,10 +1139,14 @@ struct _HttpStateData { + int fd; + http_state_flags flags; + FwdState *fwd; ++#ifdef HS_FEAT_ICAP ++ struct _IcapStateData *icap_writer; ++#endif + char *body_buf; + int body_buf_sz; + }; + ++ + struct _icpUdpData { + struct sockaddr_in address; + void *msg; +@@ -1092,6 +1242,7 @@ struct _clientHttpRequest { + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; ++ unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { +@@ -1100,6 +1251,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1127,7 +1281,6 @@ struct _ConnStateData { + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; +- int nrequests; + struct { + int n; + time_t until; +@@ -1678,6 +1831,9 @@ struct _request_t { + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + }; +@@ -1784,7 +1940,11 @@ struct _StatCounters { + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; +- } server; ++ } ++#if HS_FEAT_ICAP ++ icap, ++#endif ++ server; + struct { + int pkts_sent; + int queries_sent; +Index: src/typedefs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/typedefs.h,v +retrieving revision 1.25.6.8 +retrieving revision 1.25.6.1.6.13 +diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13 +--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8 ++++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13 +@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade + typedef struct _HttpBody HttpBody; + typedef struct _HttpReply HttpReply; + typedef struct _HttpStateData HttpStateData; ++#ifdef HS_FEAT_ICAP ++typedef struct _IcapStateData IcapStateData; ++typedef struct _IcapConfig IcapConfig; ++typedef struct _icap_service icap_service; ++typedef struct _icap_service_list icap_service_list; ++typedef struct _icap_class icap_class; ++typedef struct _icap_access icap_access; ++typedef struct _IcapOptData IcapOptData; ++#endif + typedef struct _icpUdpData icpUdpData; + typedef struct _clientHttpRequest clientHttpRequest; + typedef struct _ConnStateData ConnStateData; +Index: src/url.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/url.c,v +retrieving revision 1.7.6.6 +retrieving revision 1.7.6.5.2.2 +diff -p -u -b -r1.7.6.6 -r1.7.6.5.2.2 +--- src/url.c 12 Nov 2005 03:13:48 -0000 1.7.6.6 ++++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -221,6 +224,10 @@ urlParseProtocol(const char *s) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; ++#ifdef HS_FEAT_ICAP ++ if (strcasecmp(s, "icap") == 0) ++ return PROTO_ICAP; ++#endif + return PROTO_NONE; + } + +@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p) + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; ++#ifdef HS_FEAT_ICAP ++ case PROTO_ICAP: ++ return 1344; ++#endif + default: + return 0; + } diff --git a/www/squid31/Makefile b/www/squid31/Makefile index 31916d4cb34e..1f75bf5d90f8 100644 --- a/www/squid31/Makefile +++ b/www/squid31/Makefile @@ -66,10 +66,14 @@ # Override the maximum number of filedescriptors. Useful if you # build as another user who is not privileged to use the amount # of filedescriptors the resulting binary is expected to support. +# --enable-ntlm-fail-open +# Enable NTLM fail open, where a helper that fails one of the +# Authentication steps can allow squid to still authenticate the user # PORTNAME= squid PORTVERSION= 2.5.12 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= \ ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ @@ -82,6 +86,7 @@ DISTNAME= squid-2.5.STABLE12 DIST_SUBDIR= squid2.5 PATCH_SITES= http://www.squid-cache.org/Versions/v2/2.5/bugs/ +PATCHFILES= squid-2.5.STABLE12-SMB_BadFetch.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -120,6 +125,7 @@ OPTIONS= SQUID_LDAP_AUTH "Install LDAP authentication helpers" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ + SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ SQUID_COSS "Enable the COSS storage scheme" off \ SQUID_LARGEFILE "Support log and cache files >2GB" off \ @@ -293,6 +299,12 @@ EXTRA_PATCHES+= ${PATCHDIR}/follow_xff-2.5.patch \ ${PATCHDIR}/follow_xff-configure.patch CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif +.if defined(WITH_SQUID_ICAP) +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.5-core.patch \ + ${PATCHDIR}/icap-2.5-bootstrap.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE +.endif .if defined(WITH_SQUID_LARGEFILE) CONFIGURE_ARGS+= --with-large-files --enable-large-cache-files .endif diff --git a/www/squid31/distinfo b/www/squid31/distinfo index 5d1b5428ba58..3e55ac8d1717 100644 --- a/www/squid31/distinfo +++ b/www/squid31/distinfo @@ -1,2 +1,6 @@ MD5 (squid2.5/squid-2.5.STABLE12.tar.bz2) = 7354255015b3772a1e024dfac173e48c +SHA256 (squid2.5/squid-2.5.STABLE12.tar.bz2) = ba0ccd956323f0dad46c19aa8d40c537846fedfc3778b5730e5610f16c0d9af1 SIZE (squid2.5/squid-2.5.STABLE12.tar.bz2) = 1075111 +MD5 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 8e83b776c0d015bd4137cc1ca08f6d38 +SHA256 (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 9ca8427c2eb9e5cbdb5a49fb5cb94fc00853ad965f87666f8fc35236e98bc0ae +SIZE (squid2.5/squid-2.5.STABLE12-SMB_BadFetch.patch) = 826 diff --git a/www/squid31/files/icap-2.5-bootstrap.patch b/www/squid31/files/icap-2.5-bootstrap.patch new file mode 100644 index 000000000000..247ca0c94cbc --- /dev/null +++ b/www/squid31/files/icap-2.5-bootstrap.patch @@ -0,0 +1,422 @@ +Patch 2 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.5-core.patch for further information. + +Patch last updated: 2005-12-17 + +--- configure.orig Sat Oct 22 11:56:01 2005 ++++ configure Sat Dec 17 17:45:21 2005 +@@ -70,6 +70,8 @@ + ac_help="$ac_help + --enable-delay-pools Enable delay pools to limit bandwidth usage" + ac_help="$ac_help ++ --enable-icap-support Enable iCAP client capability" ++ac_help="$ac_help + --enable-useragent-log Enable logging of User-Agent header" + ac_help="$ac_help + --enable-referer-log Enable logging of Referer header" +@@ -2170,6 +2172,38 @@ + + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ cat >> confdefs.h <<\EOF ++#define HS_FEAT_ICAP 1 ++EOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ fi ++ ++fi ++ ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -7428,14 +7462,14 @@ + fi + ;; + esac +- echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 +-echo "configure:7433: checking for main in -lpthread" >&5 ++ echo $ac_n "checking for main in -pthread""... $ac_c" 1>&6 ++echo "configure:7433: checking for main in -pthread" >&5 + ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 + else + ac_save_LIBS="$LIBS" +-LIBS="-lpthread $LIBS" ++LIBS="-pthread $LIBS" + cat > conftest.$ac_ext <<EOF + #line 7441 "configure" + #include "confdefs.h" +@@ -7465,7 +7499,7 @@ + #define $ac_tr_lib 1 + EOF + +- LIBS="-lpthread $LIBS" ++ LIBS="-pthread $LIBS" + + else + echo "$ac_t""no" 1>&6 +@@ -7769,6 +7803,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strtoll \ + sysconf \ + syslog \ +@@ -7898,6 +7934,50 @@ + fi + fi + ++ ++if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++fi ++ ++ ++ ++ + echo $ac_n "checking if va_copy is implemented""... $ac_c" 1>&6 + echo "configure:7903: checking if va_copy is implemented" >&5 + if eval "test \"`echo '$''{'ac_cv_func_va_copy'+set}'`\" = set"; then +@@ -9072,6 +9152,8 @@ + s%@ENABLE_PINGER_FALSE@%$ENABLE_PINGER_FALSE%g + s%@USE_DELAY_POOLS_TRUE@%$USE_DELAY_POOLS_TRUE%g + s%@USE_DELAY_POOLS_FALSE@%$USE_DELAY_POOLS_FALSE%g ++s%@USE_ICAP_TRUE@%$USE_ICAP_TRUE%g ++s%@USE_ICAP_FALSE@%$USE_ICAP_FALSE%g + s%@USE_SNMP_TRUE@%$USE_SNMP_TRUE%g + s%@USE_SNMP_FALSE@%$USE_SNMP_FALSE%g + s%@SNMPLIB@%$SNMPLIB%g +@@ -9118,6 +9200,10 @@ + s%@LIB_LBER@%$LIB_LBER%g + s%@NEED_OWN_SNPRINTF_TRUE@%$NEED_OWN_SNPRINTF_TRUE%g + s%@NEED_OWN_SNPRINTF_FALSE@%$NEED_OWN_SNPRINTF_FALSE%g ++s%@NEED_OWN_STRNSTR_TRUE@%$NEED_OWN_STRNSTR_TRUE%g ++s%@NEED_OWN_STRNSTR_FALSE@%$NEED_OWN_STRNSTR_FALSE%g ++s%@NEED_OWN_STRCASESTR_TRUE@%$NEED_OWN_STRCASESTR_TRUE%g ++s%@NEED_OWN_STRCASESTR_FALSE@%$NEED_OWN_STRCASESTR_FALSE%g + s%@REGEXLIB@%$REGEXLIB%g + s%@LIBREGEX@%$LIBREGEX%g + s%@LIBOBJS@%$LIBOBJS%g +--- include/autoconf.h.in.orig Tue Sep 13 02:12:34 2005 ++++ include/autoconf.h.in Sat Dec 17 17:45:21 2005 +@@ -124,6 +124,11 @@ + */ + #undef DELAY_POOLS + ++/* ++ * ICAP - Internet Content Adaptation Protocol ++ */ ++#undef HS_FEAT_ICAP ++ + /* + * If you want to log User-Agent request header values, define this. + * By default, they are written to useragent.log in the Squid log +@@ -574,6 +579,12 @@ + + /* Define if you have the statfs function. */ + #undef HAVE_STATFS ++ ++/* Define if you have the strcasestr function. */ ++#undef HAVE_STRCASESTR ++ ++/* Define if you have the strnstr function. */ ++#undef HAVE_STRNSTR + + /* Define if you have the strerror function. */ + #undef HAVE_STRERROR +--- lib/Makefile.in.orig Wed Sep 28 22:57:20 2005 ++++ lib/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -123,6 +123,13 @@ + + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = ++ ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++ ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++ + @NEED_OWN_MD5_TRUE@MD5SOURCE = md5.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = + +@@ -158,6 +165,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +@@ -196,13 +205,18 @@ + @NEED_OWN_MD5_FALSE@am__objects_1 = + @NEED_OWN_SNPRINTF_FALSE@am__objects_2 = + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) ++@NEED_OWN_STRNSTR_FALSE@am__objects_3 = ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_FALSE@am__objects_4 = + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) stub_memaccount.$(OBJEXT) \ ++ util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) cru + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -224,15 +238,16 @@ + @AMDEP_TRUE@ $(DEPDIR)/dlmalloc.Po $(DEPDIR)/drand48.Po \ + @AMDEP_TRUE@ $(DEPDIR)/getfullhostname.Po $(DEPDIR)/hash.Po \ + @AMDEP_TRUE@ $(DEPDIR)/heap.Po $(DEPDIR)/html_quote.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/initgroups.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/iso3307.Po $(DEPDIR)/md5.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/ntlmauth.Po $(DEPDIR)/radix.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1035.Po $(DEPDIR)/rfc1123.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/rfc1738.Po $(DEPDIR)/rfc2617.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/safe_inet_addr.Po $(DEPDIR)/snprintf.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/splay.Po $(DEPDIR)/strerror.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/stub_memaccount.Po $(DEPDIR)/tempnam.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uudecode.Po ++@AMDEP_TRUE@ $(DEPDIR)/inet_ntoa.Po $(DEPDIR)/iso3307.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/md5.Po $(DEPDIR)/ntlmauth.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/radix.Po $(DEPDIR)/rfc1035.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc1123.Po $(DEPDIR)/rfc1738.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/rfc2617.Po $(DEPDIR)/safe_inet_addr.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/snprintf.Po $(DEPDIR)/splay.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strcasestr.Po $(DEPDIR)/strerror.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/strnstr.Po $(DEPDIR)/stub_memaccount.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/tempnam.Po $(DEPDIR)/util.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/uudecode.Po + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -241,8 +256,8 @@ + DIST_SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) \ + $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) \ + $(libregex_a_SOURCES) +-DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c \ +- initgroups.c strerror.c tempnam.c ++DIST_COMMON = Makefile.am Makefile.in drand48.c inet_ntoa.c strerror.c \ ++ tempnam.c + SOURCES = $(libdlmalloc_a_SOURCES) $(libmiscutil_a_SOURCES) $(EXTRA_libmiscutil_a_SOURCES) $(libntlmauth_a_SOURCES) $(libregex_a_SOURCES) + + all: all-am +@@ -295,7 +310,6 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/heap.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/html_quote.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/inet_ntoa.Po@am__quote@ +-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/initgroups.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iso3307.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/md5.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ntlmauth.Po@am__quote@ +@@ -307,7 +321,9 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/safe_inet_addr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splay.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerror.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tempnam.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Wed Sep 28 22:57:21 2005 ++++ src/Makefile.in Sat Dec 17 17:45:21 2005 +@@ -125,6 +125,9 @@ + install_sh = @install_sh@ + makesnmplib = @makesnmplib@ + ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c ++@USE_ICAP_FALSE@ICAPSOURCE = ++ + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver +@@ -249,6 +252,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -468,54 +472,58 @@ + pinger_LDADD = $(LDADD) + pinger_DEPENDENCIES = + pinger_LDFLAGS = +-@USE_DELAY_POOLS_TRUE@am__objects_3 = delay_pools.$(OBJEXT) +-@USE_DELAY_POOLS_FALSE@am__objects_3 = +-@USE_DNSSERVER_FALSE@am__objects_4 = dns_internal.$(OBJEXT) +-@USE_DNSSERVER_TRUE@am__objects_4 = dns.$(OBJEXT) +-@ENABLE_HTCP_TRUE@am__objects_5 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_FALSE@am__objects_6 = +-@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) +-@USE_SNMP_FALSE@am__objects_7 = +-@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) +-@ENABLE_SSL_FALSE@am__objects_8 = +-@ENABLE_UNLINKD_FALSE@am__objects_9 = +-@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_10 = ++@USE_DELAY_POOLS_FALSE@am__objects_5 = ++@USE_DELAY_POOLS_TRUE@am__objects_5 = delay_pools.$(OBJEXT) ++@USE_DNSSERVER_FALSE@am__objects_6 = dns_internal.$(OBJEXT) ++@USE_DNSSERVER_TRUE@am__objects_6 = dns.$(OBJEXT) ++@ENABLE_HTCP_TRUE@am__objects_7 = htcp.$(OBJEXT) ++@USE_ICAP_TRUE@am__objects_8 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@USE_ICAP_FALSE@am__objects_8 = ++@MAKE_LEAKFINDER_TRUE@am__objects_9 = leakfinder.$(OBJEXT) ++@MAKE_LEAKFINDER_FALSE@am__objects_9 = ++@USE_SNMP_TRUE@am__objects_10 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT) ++@USE_SNMP_FALSE@am__objects_10 = ++@ENABLE_SSL_FALSE@am__objects_11 = ++@ENABLE_SSL_TRUE@am__objects_11 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_12 = unlinkd.$(OBJEXT) ++@ENABLE_UNLINKD_FALSE@am__objects_12 = ++@ENABLE_WIN32SPECIFIC_FALSE@am__objects_13 = ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_13 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \ + cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \ + client_db.$(OBJEXT) client_side.$(OBJEXT) comm.$(OBJEXT) \ +- comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_3) \ +- disk.$(OBJEXT) $(am__objects_4) errorpage.$(OBJEXT) \ ++ comm_select.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \ ++ disk.$(OBJEXT) $(am__objects_6) errorpage.$(OBJEXT) \ + ETag.$(OBJEXT) event.$(OBJEXT) external_acl.$(OBJEXT) \ + fd.$(OBJEXT) filemap.$(OBJEXT) forward.$(OBJEXT) \ + fqdncache.$(OBJEXT) ftp.$(OBJEXT) gopher.$(OBJEXT) \ +- helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ ++ helper.$(OBJEXT) $(am__objects_7) http.$(OBJEXT) \ + HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_6) \ +- logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_8) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_9) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \ + MemPool.$(OBJEXT) MemBuf.$(OBJEXT) mime.$(OBJEXT) \ + multicast.$(OBJEXT) neighbors.$(OBJEXT) net_db.$(OBJEXT) \ + Packer.$(OBJEXT) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \ + peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ +- ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_10) \ ++ ssl.$(OBJEXT) $(am__objects_11) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_12) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- whois.$(OBJEXT) $(am__objects_10) ++ whois.$(OBJEXT) $(am__objects_13) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -563,7 +571,9 @@ + @AMDEP_TRUE@ $(DEPDIR)/fqdncache.Po $(DEPDIR)/ftp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/globals.Po $(DEPDIR)/gopher.Po \ + @AMDEP_TRUE@ $(DEPDIR)/helper.Po $(DEPDIR)/htcp.Po \ +-@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icmp.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/http.Po $(DEPDIR)/icap_common.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_opt.Po $(DEPDIR)/icap_reqmod.Po \ ++@AMDEP_TRUE@ $(DEPDIR)/icap_respmod.Po $(DEPDIR)/icmp.Po \ + @AMDEP_TRUE@ $(DEPDIR)/icp_v2.Po $(DEPDIR)/icp_v3.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ident.Po $(DEPDIR)/internal.Po \ + @AMDEP_TRUE@ $(DEPDIR)/ipc.Po $(DEPDIR)/ipcache.Po \ +@@ -777,6 +787,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/icp_v3.Po@am__quote@ diff --git a/www/squid31/files/icap-2.5-core.patch b/www/squid31/files/icap-2.5-core.patch new file mode 100644 index 000000000000..22d209c18fb4 --- /dev/null +++ b/www/squid31/files/icap-2.5-core.patch @@ -0,0 +1,7059 @@ +Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck <tmseck@netcologne.de>. + +This patch only contains the parts of the original patchset that +actually implement the ICAP client functionality. The updates to +the build infrastructure are omitted to avoid the need to run an +autotools bootstrap. Instead, we simulate said bootstrapping with +a second patch, icap-2.5-bootstrap.patch. + +The patchset was pulled from the project's CVS repository +at cvs.devel.squid-cache.org using + +cvs diff -u -b -N -kk -rs2_5 -ricap-2_5 + +See also +<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5> +for the "official" auto-generated patchset. + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2005-12-17 + +Index: errors/Bulgarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Bulgarian/ERR_ICAP_FAILURE +diff -N errors/Bulgarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Catalan/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Catalan/ERR_ICAP_FAILURE +diff -N errors/Catalan/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Czech/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Czech/ERR_ICAP_FAILURE +diff -N errors/Czech/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Danish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Danish/ERR_ICAP_FAILURE +diff -N errors/Danish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Dutch/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Dutch/ERR_ICAP_FAILURE +diff -N errors/Dutch/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/English/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/English/ERR_ICAP_FAILURE +diff -N errors/English/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Estonian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Estonian/ERR_ICAP_FAILURE +diff -N errors/Estonian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Finnish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Finnish/ERR_ICAP_FAILURE +diff -N errors/Finnish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/French/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/French/ERR_ICAP_FAILURE +diff -N errors/French/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/German/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/German/ERR_ICAP_FAILURE +diff -N errors/German/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2 +@@ -0,0 +1,33 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>FEHLER</H1> ++<H2>Der angeforderte URL konnte nicht geholt werden</H2> ++<HR noshade size="1px"> ++<P> ++Während des Versuches, den URL<BR> ++<A HREF="%U">%U</A> ++ ++<BR> ++zu laden, trat der folgende Fehler auf: ++<UL> ++<LI> ++<STRONG> ++ICAP-Protokollfehler ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: ++<UL> ++<LI>Nicht erreichbarer ICAP-Server ++<LI>Ungültige Antwort vom ICAP-Server ++ ++</UL> ++</P> ++ ++<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Greek/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Greek/ERR_ICAP_FAILURE +diff -N errors/Greek/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hebrew/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hebrew/ERR_ICAP_FAILURE +diff -N errors/Hebrew/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Hungarian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Hungarian/ERR_ICAP_FAILURE +diff -N errors/Hungarian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Italian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Italian/ERR_ICAP_FAILURE +diff -N errors/Italian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Japanese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Japanese/ERR_ICAP_FAILURE +diff -N errors/Japanese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Korean/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Korean/ERR_ICAP_FAILURE +diff -N errors/Korean/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Lithuanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Lithuanian/ERR_ICAP_FAILURE +diff -N errors/Lithuanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Polish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Polish/ERR_ICAP_FAILURE +diff -N errors/Polish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Portuguese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Portuguese/ERR_ICAP_FAILURE +diff -N errors/Portuguese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Romanian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Romanian/ERR_ICAP_FAILURE +diff -N errors/Romanian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-1251/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-1251/ERR_ICAP_FAILURE +diff -N errors/Russian-1251/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Russian-koi8-r/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE +diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Serbian/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Serbian/ERR_ICAP_FAILURE +diff -N errors/Serbian/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Simplify_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE +diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Slovak/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Slovak/ERR_ICAP_FAILURE +diff -N errors/Slovak/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Spanish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Spanish/ERR_ICAP_FAILURE +diff -N errors/Spanish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Swedish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Swedish/ERR_ICAP_FAILURE +diff -N errors/Swedish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Traditional_Chinese/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE +diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: errors/Turkish/ERR_ICAP_FAILURE +=================================================================== +RCS file: errors/Turkish/ERR_ICAP_FAILURE +diff -N errors/Turkish/ERR_ICAP_FAILURE +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1 +@@ -0,0 +1,31 @@ ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ++<HTML><HEAD> ++<TITLE>ERROR: The requested URL could not be retrieved</TITLE> ++<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> ++</HEAD><BODY> ++<H1>ERROR</H1> ++<H2>The requested URL could not be retrieved</H2> ++<HR noshade size="1px"> ++<P> ++While attempting to retrieve the URL: ++<A HREF="%U">%U</A> ++<P> ++the following error was encountered: ++<UL> ++<LI> ++<STRONG> ++ICAP protocol error. ++</STRONG> ++</UL> ++ ++<P> ++<P> ++Some aspect of the ICAP communication failed. Possible problems: ++<UL> ++<LI>ICAP server is not reachable. ++<LI>Illegal response from ICAP server. ++</UL> ++</P> ++ ++<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.10 +retrieving revision 1.10.30.2 +diff -p -u -b -r1.10 -r1.10.30.2 +--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10 ++++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2 +@@ -132,4 +132,12 @@ double drand48(void); + */ + int statMemoryAccounted(void); + ++#ifndef HAVE_STRNSTR ++extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); ++#endif ++ ++#ifndef HAVE_STRCASESTR ++extern char *strcasestr(const char *haystack, const char *needle); ++#endif ++ + #endif /* SQUID_UTIL_H */ +Index: lib/Makefile.am +=================================================================== +RCS file: /cvsroot/squid/squid/lib/Makefile.am,v +retrieving revision 1.4 +retrieving revision 1.4.26.2 +diff -p -u -b -r1.4 -r1.4.26.2 +--- lib/Makefile.am 21 Nov 2001 23:48:57 -0000 1.4 ++++ lib/Makefile.am 6 Apr 2004 13:04:38 -0000 1.4.26.2 +@@ -8,6 +8,19 @@ SNPRINTFSOURCE=snprintf.c + else + SNPRINTFSOURCE= + endif ++ ++if NEED_OWN_STRNSTR ++STRNSTRSOURCE=strnstr.c ++else ++STRNSTRSOURCE= ++endif ++ ++if NEED_OWN_STRCASESTR ++STRCASESTRSOURCE=strcasestr.c ++else ++STRCASESTRSOURCE= ++endif ++ + if NEED_OWN_MD5 + MD5SOURCE=md5.c + else +@@ -43,6 +56,8 @@ libmiscutil_a_SOURCES = \ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + stub_memaccount.c \ + util.c \ + uudecode.c +Index: lib/strcasestr.c +=================================================================== +RCS file: lib/strcasestr.c +diff -N lib/strcasestr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1 +@@ -0,0 +1,126 @@ ++/* Return the offset of one string within another. ++ Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* ++ * My personal strstr() implementation that beats most other algorithms. ++ * Until someone tells me otherwise, I assume that this is the ++ * fastest implementation of strstr() in C. ++ * I deliberately chose not to comment it. You should have at least ++ * as much fun trying to understand it, as I had to write it :-). ++ * ++ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ ++ ++/* ++ * modified to work outside of glibc (rhorstmann, 06/04/2004) ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRCASESTR ++#include <ctype.h> ++ ++typedef unsigned chartype; ++ ++char * ++strcasestr (phaystack, pneedle) ++ const char *phaystack; ++ const char *pneedle; ++{ ++ register const unsigned char *haystack, *needle; ++ register chartype b, c; ++ ++ haystack = (const unsigned char *) phaystack; ++ needle = (const unsigned char *) pneedle; ++ ++ b = tolower (*needle); ++ if (b != '\0') ++ { ++ haystack--; /* possible ANSI violation */ ++ do ++ { ++ c = *++haystack; ++ if (c == '\0') ++ goto ret0; ++ } ++ while (tolower (c) != (int) b); ++ ++ c = tolower (*++needle); ++ if (c == '\0') ++ goto foundneedle; ++ ++needle; ++ goto jin; ++ ++ for (;;) ++ { ++ register chartype a; ++ register const unsigned char *rhaystack, *rneedle; ++ ++ do ++ { ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ if (tolower (a) == (int) b) ++ break; ++ a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++shloop: ++ ; ++ } ++ while (tolower (a) != (int) b); ++ ++jin: a = *++haystack; ++ if (a == '\0') ++ goto ret0; ++ ++ if (tolower (a) != (int) c) ++ goto shloop; ++ ++ rhaystack = haystack-- + 1; ++ rneedle = needle; ++ a = tolower (*rneedle); ++ ++ if (tolower (*rhaystack) == (int) a) ++ do ++ { ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ if (tolower (*rhaystack) != (int) a) ++ break; ++ if (a == '\0') ++ goto foundneedle; ++ ++rhaystack; ++ a = tolower (*++needle); ++ } ++ while (tolower (*rhaystack) == (int) a); ++ ++ needle = rneedle; /* took the register-poor approach */ ++ ++ if (a == '\0') ++ break; ++ } ++ } ++foundneedle: ++ return (char*) haystack; ++ret0: ++ return 0; ++} ++#endif +Index: lib/strnstr.c +=================================================================== +RCS file: lib/strnstr.c +diff -N lib/strnstr.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include <string.h> ++#include <util.h> ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.5.30.3 +retrieving revision 1.5.44.8 +diff -p -u -b -r1.5.30.3 -r1.5.44.8 +--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3 ++++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8 +@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb) + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); + } ++ ++int ++memBufRead(int fd, MemBuf * mb) ++{ ++ int len; ++ if (mb->capacity == mb->size) ++ memBufGrow(mb, SQUID_TCP_SO_RCVBUF); ++ len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); ++ if (len) ++ mb->size += len; ++ return len; ++} +Index: src/cache_cf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cache_cf.c,v +retrieving revision 1.38.6.29 +retrieving revision 1.38.6.11.2.22 +diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22 +--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29 ++++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22 +@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli + return bodylist.head == NULL; + } + ++#ifdef HS_FEAT_ICAP ++ ++/*************************************************** ++ * prototypes ++ */ ++static int icap_service_process(icap_service * s); ++static void icap_service_init(icap_service * s); ++static void icap_service_destroy(icap_service * s); ++icap_service *icap_service_lookup(char *name); ++static int icap_class_process(icap_class * c); ++static void icap_class_destroy(icap_class * c); ++static void icap_access_destroy(icap_access * a); ++static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access <servicename> {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.14.6.1 +retrieving revision 1.14.32.2 +diff -p -u -b -r1.14.6.1 -r1.14.32.2 +--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1 ++++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2 +@@ -144,6 +144,10 @@ cbdataInit(void) + CREATE_CBDATA(statefulhelper); + CREATE_CBDATA(helper_stateful_server); + CREATE_CBDATA(HttpStateData); ++#ifdef HS_FEAT_ICAP ++ CREATE_CBDATA(IcapStateData); ++ CREATE_CBDATA(icap_service); ++#endif + CREATE_CBDATA_FREE(peer, peerDestroy); + CREATE_CBDATA(ps_state); + CREATE_CBDATA(RemovalPolicy); +Index: src/cf.data.pre +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf.data.pre,v +retrieving revision 1.49.2.84 +retrieving revision 1.49.2.33.2.32 +diff -p -u -b -r1.49.2.84 -r1.49.2.33.2.32 +--- src/cf.data.pre 21 Oct 2005 02:13:47 -0000 1.49.2.84 ++++ src/cf.data.pre 24 Oct 2005 17:07:42 -0000 1.49.2.33.2.32 +@@ -2397,7 +2397,6 @@ DOC_START + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -2716,6 +2715,177 @@ DOC_START + DOC_END + + COMMENT_START ++ ICAP OPTIONS ++ ----------------------------------------------------------------------------- ++COMMENT_END ++ ++NAME: icap_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.onoff ++DEFAULT: off ++DOC_START ++ If you want to enable the ICAP client module, set this to on. ++DOC_END ++ ++NAME: icap_preview_enable ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.preview_enable ++DEFAULT: off ++DOC_START ++ Set this to 'on' if you want to enable the ICAP preview ++ feature in Squid. ++DOC_END ++ ++NAME: icap_preview_size ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.preview_size ++DEFAULT: -1 ++DOC_START ++ The default size of preview data to be sent to the ICAP server. ++ -1 means no preview. This value might be overwritten on a per server ++ basis by OPTIONS requests. ++DOC_END ++ ++NAME: icap_check_interval ++TYPE: int ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.check_interval ++DEFAULT: 300 ++DOC_START ++ If an ICAP server does not respond, it gets marked as unreachable. Squid ++ will try again to reach it after this time. ++DOC_END ++ ++NAME: icap_send_client_ip ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_client_ip ++DEFAULT: off ++DOC_START ++ This adds the header "X-Client-IP" to ICAP requests. Can also be ++ set from the server's response to OPTIONS. ++DOC_END ++ ++NAME: icap_send_auth_user ++TYPE: onoff ++IFDEF: HS_FEAT_ICAP ++COMMENT: on|off ++LOC: Config.icapcfg.send_auth_user ++DEFAULT: off ++DOC_START ++ This adds the header "X-Authenticated-User" to ICAP requests ++ if proxy access is authentified. Can also be set from the server's ++ response to OPTIONS. ++DOC_END ++ ++NAME: icap_auth_scheme ++TYPE: string ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg.auth_scheme ++DEFAULT: Local://%u ++DOC_START ++ Authentification scheme to pass to ICAP requests if ++ icap_send_auth_user is enabled. The first occurence of "%u" ++ is replaced by the authentified user name. If no "%u" is found, ++ the username is added at the end of the scheme. ++ ++ See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, ++ section 3.4 for details on this. ++ ++ Examples: ++ ++ icap_auth_scheme Local://%u ++ icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com ++ icap_auth_scheme WinNT://nt-domain/%u ++ icap_auth_scheme Radius://radius-server/%u ++DOC_END ++ ++NAME: icap_service ++TYPE: icap_service_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines a single ICAP service ++ ++ icap_service servicename vectoring_point bypass service_url [options ...] ++ ++ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache ++ This specifies at which point of request processing the ICAP ++ service should be plugged in. ++ bypass = 1|0 ++ If set to 1 and the ICAP server cannot be reached, the request will go ++ through without being processed by an ICAP server ++ service_url = icap://servername:port/service ++ ++ Options: ++ ++ no-keep-alive To always close the connection to icap server ++ after the transaction completes ++ ++ ++ Note: reqmod_precache and respmod_postcache is not yet implemented ++ ++ Load-balancing and high availability: ++ You can obtain load-balancing and high availability by defining a ++ named service with different definitions. Then, the client ++ loops through the different entities of the service providing ++ load-balancing. If an entity is marked as unreachable, the client goes ++ one step further to the next entity: you have the high-availability. ++ See the service_1 definition below ++ ++Example: ++icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod ++icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive ++icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod ++DOC_END ++ ++NAME: icap_class ++TYPE: icap_class_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Defines an ICAP service chain. If there are multiple services per ++ vectoring point, they are processed in the specified order. ++ ++ icap_class classname servicename... ++ ++Example: ++icap_class class_1 service_1 service_2 ++icap class class_2 service_1 service_3 ++DOC_END ++ ++NAME: icap_access ++TYPE: icap_access_type ++IFDEF: HS_FEAT_ICAP ++LOC: Config.icapcfg ++DEFAULT: none ++DOC_START ++ Redirects a request through an ICAP service class, depending ++ on given acls ++ ++ icap_access classname allow|deny [!]aclname... ++ ++ The icap_access statements are processed in the order they appear in ++ this configuration file. If an access list matches, the processing stops. ++ For an "allow" rule, the specified class is used for the request. A "deny" ++ rule simply stops processing without using the class. You can also use the ++ special classname "None". ++ ++ For backward compatibility, it is also possible to use services ++ directly here. ++Example: ++icap_access class_1 allow all ++DOC_END ++ ++COMMENT_START + MISCELLANEOUS + ----------------------------------------------------------------------------- + COMMENT_END +Index: src/cf_gen_defines +=================================================================== +RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v +retrieving revision 1.5 +retrieving revision 1.5.48.3 +diff -p -u -b -r1.5 -r1.5.48.3 +--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5 ++++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3 +@@ -18,12 +18,13 @@ BEGIN { + define["USE_UNLINKD"]="--enable-unlinkd" + define["USE_USERAGENT_LOG"]="--enable-useragent-log" + define["USE_WCCP"]="--enable-wccp" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +- DEFINE=define[$2] ++ DEFINE = define[$2] + else +- DEFINE="-D" $2 ++ DEFINE = "-D" $2 + print "{\"" $2 "\", \"" DEFINE "\", " + print "#if " $2 + print "1" +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.47.2.71 +retrieving revision 1.47.2.28.2.40 +diff -p -u -b -r1.47.2.71 -r1.47.2.28.2.40 +--- src/client_side.c 19 Oct 2005 02:13:20 -0000 1.47.2.71 ++++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40 +@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n"; + static CWCB clientWriteComplete; + static CWCB clientWriteBodyComplete; + static PF clientReadRequest; +-static PF connStateFree; ++PF connStateFree; + static PF requestTimeout; + static PF clientLifetimeTimeout; + static int clientCheckTransferDone(clientHttpRequest *); +@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien + static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); + static void clientPackTermBound(String boundary, MemBuf * mb); + static void clientInterpretRequestHeaders(clientHttpRequest *); +-static void clientProcessRequest(clientHttpRequest *); ++void clientProcessRequest(clientHttpRequest *); + static void clientProcessExpired(void *data); + static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); +-static int clientCachable(clientHttpRequest * http); +-static int clientHierarchical(clientHttpRequest * http); +-static int clientCheckContentLength(request_t * r); ++int clientCachable(clientHttpRequest * http); ++int clientHierarchical(clientHttpRequest * http); ++int clientCheckContentLength(request_t * r); + static DEFER httpAcceptDefer; + static log_type clientProcessRequest2(clientHttpRequest * http); + static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); + static int clientRequestBodyTooLarge(squid_off_t clen); + static void clientProcessBody(ConnStateData * conn); + static void clientEatRequestBody(clientHttpRequest *); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + static int + checkAccelOnly(clientHttpRequest * http) +@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res + http->request = requestLink(new_request); + } + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -931,11 +938,22 @@ httpRequestFree(void *data) + *H = http->next; + http->next = NULL; + dlinkDelete(&http->active, &ClientActiveRequests); ++#if HS_FEAT_ICAP ++ /*In the case that the upload of data breaks, we need this code here .... */ ++ if (NULL != http->icap_reqmod) { ++ if (cbdataValid(http->icap_reqmod)) ++ if (http->icap_reqmod->icap_fd > -1) { ++ comm_close(http->icap_reqmod->icap_fd); ++ } ++ cbdataUnlock(http->icap_reqmod); ++ http->icap_reqmod = NULL; ++ } ++#endif + cbdataFree(http); + } + + /* This is a handler normally called by comm_close() */ +-static void ++void + connStateFree(int fd, void *data) + { + ConnStateData *connState = data; +@@ -958,7 +976,6 @@ connStateFree(int fd, void *data) + } else + safe_free(connState->in.buf); + /* XXX account connState->in.buf */ +- pconnHistCount(0, connState->nrequests); + cbdataFree(connState); + #ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ +@@ -1103,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1122,7 +1139,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1148,7 +1165,7 @@ clientCachable(clientHttpRequest * http) + } + + /* Return true if we can query our neighbors for this object */ +-static int ++int + clientHierarchical(clientHttpRequest * http) + { + const char *url = http->uri; +@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -2449,6 +2466,11 @@ clientProcessRequest(clientHttpRequest * + debug(33, 4) ("clientProcessRequest: %s '%s'\n", + RequestMethodStr[r->method], + url); ++#if HS_FEAT_ICAP ++ if (clientIcapReqMod(http)) { ++ return; ++ } ++#endif + if (r->method == METHOD_CONNECT && !http->redirect.status) { + http->log_type = LOG_TCP_MISS; + sslStart(http, &http->out.size, &http->al.http.code); +@@ -2993,6 +3015,20 @@ clientReadRequest(int fd, void *data) + (long) conn->in.offset, (long) conn->in.size); + len = conn->in.size - conn->in.offset - 1; + } ++#if HS_FEAT_ICAP ++ /* ++ * This check exists because ICAP doesn't always work well ++ * with persistent (reused) connections. One version of the ++ * REQMOD code creates a fake ConnStateData, which doesn't have ++ * an in.buf. We want to make sure that the fake ConnStateData ++ * doesn't get used here. ++ */ ++ if (NULL == conn->in.buf) { ++ debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); ++ comm_close(fd); ++ return; ++ } ++#endif + statCounter.syscalls.sock.reads++; + size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); + if (size > 0) { +@@ -3096,7 +3132,8 @@ clientReadRequest(int fd, void *data) + /* add to the client request queue */ + for (H = &conn->chr; *H; H = &(*H)->next); + *H = http; +- conn->nrequests++; ++ F->pconn.uses++; ++ F->pconn.type = 0; + /* + * I wanted to lock 'http' here since its callback data for + * clientLifetimeTimeout(), but there's no logical place to +@@ -3266,7 +3303,7 @@ clientReadRequest(int fd, void *data) + } + + /* file_read like function, for reading body content */ +-static void ++void + clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) + { + ConnStateData *conn = request->body_reader_data; +@@ -3390,7 +3427,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data) + * Some data has been sent to the client, just close the FD + */ + comm_close(fd); +- } else if (conn->nrequests) { ++ } else if (fd_table[fd].pconn.uses) { + /* + * assume its a persistent connection; just close it + */ +@@ -3948,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re + } + } + } ++ ++#if HS_FEAT_ICAP ++static int ++clientIcapReqMod(clientHttpRequest * http) ++{ ++ ErrorState *err; ++ icap_service *service; ++ if (http->flags.did_icap_reqmod) ++ return 0; ++ if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) ++ return 0; ++ debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); ++ /* ++ * Note, we pass 'start' and 'log_addr' to ICAP so the access.log ++ * entry comes out right. The 'clientHttpRequest' created by ++ * the ICAP side is the one that gets logged. The first ++ * 'clientHttpRequest' does not get logged because its out.size ++ * is zero and log_type is unset. ++ */ ++ http->icap_reqmod = icapReqModStart(service, ++ http->uri, ++ http->request, ++ http->conn->fd, ++ http->start, ++ http->conn->log_addr, ++ (void *) http->conn); ++ if (NULL == http->icap_reqmod) { ++ return 0; ++ } else if (-1 == (int) http->icap_reqmod) { ++ /* produce error */ ++ http->icap_reqmod = NULL; ++ debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); ++ http->log_type = LOG_TCP_DENIED; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = ETIMEDOUT; ++ err->request = requestLink(http->request); ++ err->src_addr = http->conn->peer.sin_addr; ++ http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return 1; ++ } ++ cbdataLock(http->icap_reqmod); ++ http->flags.did_icap_reqmod = 1; ++ return 1; ++} ++#endif +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.18.6.6 +retrieving revision 1.18.6.2.12.9 +diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9 +--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6 ++++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9 +@@ -653,8 +653,8 @@ comm_close(int fd) + #endif + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + SSL_free(F->ssl); +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.29.2.18 +retrieving revision 1.29.2.8.2.17 +diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17 +--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18 ++++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17 +@@ -93,6 +93,7 @@ typedef enum { + ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ + ERR_TOO_BIG, + TCP_RESET, ++ ERR_ICAP_FAILURE, + ERR_INVALID_RESP, + ERR_MAX + } err_type; +@@ -438,6 +439,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -610,6 +614,12 @@ typedef enum { + MEM_TLV, + MEM_SWAP_LOG_DATA, + MEM_CLIENT_REQ_BUF, ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -709,9 +719,14 @@ typedef enum { + CBDATA_RemovalPolicyWalker, + CBDATA_RemovalPurgeWalker, + CBDATA_store_client, ++#ifdef HS_FEAT_ICAP ++ CBDATA_IcapStateData, ++ CBDATA_icap_service, ++#endif + CBDATA_FIRST_CUSTOM_TYPE = 1000 + } cbdata_type; + ++ + /* + * Return codes from checkVary(request) + */ +@@ -742,4 +757,68 @@ enum { + + #endif + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.13.6.15 +retrieving revision 1.13.6.3.2.15 +diff -p -u -b -r1.13.6.15 -r1.13.6.3.2.15 +--- src/forward.c 2 Sep 2005 02:13:43 -0000 1.13.6.15 ++++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15 +@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status + else + hierarchyNote(&fwdState->request->hier, fs->code, request->host); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; + if (fs->peer) + peerConnectSucceded(fs->peer); + fwdDispatch(fwdState); +@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data) + void + fwdFail(FwdState * fwdState, ErrorState * errorState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", + err_type_str[errorState->type], + httpStatusString(errorState->http_status), +@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data) + void + fwdUnregister(int fd, FwdState * fwdState) + { ++ if (NULL == fwdState) ++ return; + debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); + assert(fd == fwdState->server_fd); + assert(fd > -1); +@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat + void + fwdComplete(FwdState * fwdState) + { +- StoreEntry *e = fwdState->entry; ++ StoreEntry *e; ++ if (NULL == fwdState) ++ return; ++ e = fwdState->entry; + assert(e->store_status == STORE_PENDING); + debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), + e->mem_obj->reply->sline.status); +Index: src/globals.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/globals.h,v +retrieving revision 1.14.6.7 +retrieving revision 1.14.6.3.2.5 +diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5 +--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7 ++++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5 +@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */ + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.17.6.32 +retrieving revision 1.17.6.3.6.39 +diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.39 +--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32 ++++ src/http.c 23 Nov 2005 20:33:07 -0000 1.17.6.3.6.39 +@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry; + + static PF httpReadReply; + static void httpSendRequest(HttpStateData *); +-static PF httpStateFree; ++PF httpStateFree; + static PF httpTimeout; + static void httpCacheNegatively(StoreEntry *); + static void httpMakePrivate(StoreEntry *); +@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *) + static int httpCachableReply(HttpStateData *); + static void httpMaybeRemovePublic(StoreEntry *, http_status); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data) + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; ++#if HS_FEAT_ICAP ++ cbdataUnlock(httpState->icap_writer); ++#endif + cbdataFree(httpState); + } + +@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht + } + + /* rewrite this later using new interfaces @?@ */ +-static void ++void + httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) + { + StoreEntry *entry = httpState->entry; +@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; ++ squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ +- if (httpState->reply_hdr_state < 2) ++ if (httpState->reply_hdr_state < 2) { ++ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", ++ httpState->reply_hdr_state); + return 0; ++ } + clen = httpReplyBodySize(httpState->request->method, reply); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ content_bytes_read = httpState->icap_writer->fake_content_length; ++ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); ++ } else ++#endif ++ content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ +- if (mem->inmem_hi > clen + reply->hdr_sz) ++ if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ +- if (mem->inmem_hi < clen + reply->hdr_sz) ++ if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data) + delay_id delay_id; + #endif + ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ /*The folowing entry can not be marked as aborted. ++ * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; +@@ -579,6 +605,33 @@ httpReadReply(int fd, void *data) + else + delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); + #endif ++ ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ IcapStateData *icap = httpState->icap_writer; ++ /* ++ * Ok we have a received a response from the web server, so try to ++ * connect the icap server if it's the first attemps. If we try ++ * to connect to the icap server, defer this request (do not read ++ * the buffer), and defer until icapConnectOver() is not called. ++ */ ++ if (icap->flags.connect_requested == 0) { ++ debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); ++ if (!icapConnect(icap, icapConnectOver)) { ++ debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); ++ icap->flags.connect_requested = 1; ++ /* Wait for more data or EOF condition */ ++ commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ return; ++ } ++ } ++#endif ++ + errno = 0; + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, read_sz); +@@ -595,7 +648,13 @@ httpReadReply(int fd, void *data) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +- if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ (void) 0; ++ else ++#endif ++ ++ if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { + /* Skip whitespace */ + while (len > 0 && xisspace(*buf)) + xmemmove(buf, buf + 1, len--); +@@ -625,6 +684,12 @@ httpReadReply(int fd, void *data) + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { ++ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); ++ icapSendRespMod(httpState->icap_writer, buf, len, 1); ++ } ++#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we +@@ -677,7 +742,28 @@ httpReadReply(int fd, void *data) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ if (cbdataValid(httpState->icap_writer)) { ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } ++ } else ++#endif + storeAppend(entry, buf, len); ++ ++ ++ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); ++#if HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ if (!httpState->icap_writer->respmod.entry) { ++ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); ++ comm_close(fd); ++ return; ++ } ++ } else ++#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + /* + * the above storeAppend() call could ABORT this entry, +@@ -724,10 +810,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); +@@ -766,6 +863,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[httpState->orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -776,6 +877,34 @@ httpReadReply(int fd, void *data) + } + } + ++#ifdef HS_FEAT_ICAP ++static int ++httpReadReplyWaitForIcap(int fd, void *data) ++{ ++ HttpStateData *httpState = data; ++ if (NULL == httpState->icap_writer) ++ return 0; ++ /* ++ * Do not defer when we are not connected to the icap server. ++ * Defer when the icap server connection is not established but pending ++ * Defer when the icap server is busy (writing on the socket) ++ */ ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", ++ fd, httpState->icap_writer->flags.connect_requested); ++ if (!httpState->icap_writer->flags.connect_requested) ++ return 0; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", ++ fd, httpState->icap_writer->flags.connect_pending); ++ if (httpState->icap_writer->flags.connect_pending) ++ return 1; ++ debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", ++ fd, httpState->icap_writer->flags.write_pending); ++ if (httpState->icap_writer->flags.write_pending) ++ return 1; ++ return 0; ++} ++#endif ++ + /* This will be called when request write is complete. Schedule read of + * reply. */ + static void +@@ -803,6 +932,63 @@ httpSendComplete(int fd, char *bufnotuse + comm_close(fd); + return; + } else { ++ /* Schedule read reply. */ ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart( ++ ICAP_SERVICE_RESPMOD_PRECACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (-1 == (int) httpState->icap_writer) { ++ /* TODO: send error here and exit */ ++ ErrorState *err; ++ httpState->icap_writer = 0; ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(httpState->orig_request); ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ return; ++ } else if (httpState->icap_writer) { ++ request_flags fake_flags = httpState->orig_request->flags; ++ method_t fake_method = entry->mem_obj->method; ++ const char *fake_msg = "this is a fake entry for " ++ " response sent to an ICAP RESPMOD server"; ++ cbdataLock(httpState->icap_writer); ++ /* ++ * this httpState will give the data it reads to ++ * the icap server, rather than put it into ++ * a StoreEntry ++ */ ++ storeUnlockObject(httpState->entry); ++ storeUnregisterAbort(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -811,8 +997,18 @@ httpSendComplete(int fd, char *bufnotuse + * the timeout for POST/PUT requests that have very large + * request bodies. + */ ++ ++ /* removed in stable5: ++ * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); ++ */ + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +- commSetDefer(fd, fwdCheckDeferRead, entry); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); ++ commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); ++ } else ++#endif ++ commSetDefer(httpState->fd, fwdCheckDeferRead, entry); + } + httpState->flags.request_sent = 1; + } +@@ -1010,8 +1206,11 @@ httpBuildRequestHeader(request_t * reque + if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { + const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); + httpHdrCcSetMaxAge(cc, getMaxAge(url)); ++#ifndef HS_FEAT_ICAP ++ /* Don;t bother - if the url you want to cache is redirected? */ + if (strLen(request->urlpath)) + assert(strstr(url, strBuf(request->urlpath))); ++#endif + } + /* Set no-cache if determined needed but not found */ + if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) +@@ -1119,6 +1318,7 @@ httpStart(FwdState * fwd) + int fd = fwd->server_fd; + HttpStateData *httpState; + request_t *proxy_req; ++ /* ErrorState *err; */ + request_t *orig_req = fwd->request; + debug(11, 3) ("httpStart: \"%s %s\"\n", + RequestMethodStr[orig_req->method], +@@ -1156,12 +1356,22 @@ httpStart(FwdState * fwd) + httpState->request = requestLink(orig_req); + httpState->orig_request = requestLink(orig_req); + } ++#ifdef HS_FEAT_ICAP ++ if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { ++ httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, ++ httpState->orig_request, httpState->entry, httpState->flags); ++ if (httpState->icap_writer) { ++ return; ++ } ++ } ++#endif + /* + * register the handler to free HTTP state data when the FD closes + */ + comm_add_close_handler(fd, httpStateFree, httpState); + statCounter.server.all.requests++; + statCounter.server.http.requests++; ++ + httpSendRequest(httpState); + /* + * We used to set the read timeout here, but not any more. +Index: src/icap_common.c +=================================================================== +RCS file: src/icap_common.c +diff -N src/icap_common.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_common.c 22 Nov 2005 22:41:48 -0000 1.1.2.39 +@@ -0,0 +1,785 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} +Index: src/icap_opt.c +=================================================================== +RCS file: src/icap_opt.c +diff -N src/icap_opt.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_opt.c 22 Nov 2005 22:41:48 -0000 1.1.2.17 +@@ -0,0 +1,519 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: src/icap_reqmod.c +=================================================================== +RCS file: src/icap_reqmod.c +diff -N src/icap_reqmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_reqmod.c 6 Dec 2005 21:53:44 -0000 1.1.2.58 +@@ -0,0 +1,976 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++#define ICAP_PROXY_KEEP_ALIVE 0 ++ ++/* ++ * These once-static functions are required to be global for ICAP ++ */ ++ ++PF clientReadRequest; ++PF connStateFree; ++int clientReadDefer(int fd, void *data); ++int clientCheckContentLength(request_t * r); ++void clientProcessRequest(clientHttpRequest *); ++int clientCachable(clientHttpRequest *); ++int clientHierarchical(clientHttpRequest *); ++void clientReadBody(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata); ++ ++static PF icapReqModReadHttpHdrs; ++static PF icapReqModReadHttpBody; ++static CWCB icapReqModSendBodyChunk; ++static CBCB icapReqModBodyHandler; ++static BODY_HANDLER icapReqModBodyReader; ++static STRCB icapReqModMemBufAppend; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++static const char *crlf = "\r\n"; ++ ++/* ++ * icapExpectedHttpReqHdrSize ++ * ++ * calculate the size of the HTTP headers that we expect ++ * to read from the ICAP server. ++ */ ++static int ++icapExpectedHttpReqHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) ++ return (icap->enc.req_body - icap->enc.req_hdr); ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ fatal("icapExpectedHttpReqHdrSize: unexpected case"); ++ return 0; ++} ++ ++/* ++ * icapReqModCreateClientState ++ * ++ * Creates fake client_side data structures so we can use ++ * that module to read/parse the HTTP request that we read ++ * from the ICAP server. ++ */ ++static clientHttpRequest * ++icapReqModCreateClientState(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http; ++ if (!cbdataValid(icap->reqmod.client_cookie)) { ++ debug(81, 3) ("Whups, client cookie invalid\n"); ++ icap->reqmod.client_fd = -1; ++ return NULL; ++ } ++ http = cbdataAlloc(clientHttpRequest); ++ /* ++ * use our own urlCanonicalClean here, because urlCanonicalClean ++ * may strip everything after a question-mark. As http->uri ++ * is used when doing a request to a parent proxy, we need the full ++ * url here. ++ */ ++ http->uri = xstrdup(urlCanonical(icap->request)); ++ http->log_uri = xstrndup(http->uri, MAX_URL); ++ http->range_iter.boundary = StringNull; ++ http->request = requestLink(request ? request : icap->request); ++ http->flags.did_icap_reqmod = 1; ++ http->start = icap->reqmod.start; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ ++ http->conn->chr = http; ++ ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ err->request = requestLink(request); ++ request->flags.proxy_keepalive = 0; ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ if (!clientCheckContentLength(request)) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED); ++ err->request = requestLink(request); ++ http->entry = ++ clientCreateStoreEntry(http, request->method, null_request_flags); ++ errorAppendEntry(http->entry, err); ++ return; ++ } ++ /* Do we expect a request-body? */ ++ if (request->content_length > 0) { ++ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); ++ if (request->body_reader_data) ++ cbdataUnlock(request->body_reader_data); ++ request->body_reader = icapReqModBodyReader; ++ request->body_reader_data = icap; /* XXX cbdataLock? */ ++ cbdataLock(icap); /*Yes sure ..... */ ++ memBufDefInit(&icap->reqmod.http_entity.buf); ++ } ++ if (clientCachable(http)) ++ request->flags.cachable = 1; ++ if (clientHierarchical(http)) ++ request->flags.hierarchical = 1; ++ clientProcessRequest(http); ++} ++ ++/* ++ * icapReqModParseHttpError ++ * ++ * Handle an error when parsing the new HTTP request we read ++ * from the ICAP server. ++ */ ++static void ++icapReqModParseHttpError(IcapStateData * icap, const char *reason) ++{ ++ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); ++} ++ ++/* ++ * icapEntryError ++ * ++ * A wrapper for errorCon() and errorAppendEntry(). ++ */ ++static void ++icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) ++{ ++ ErrorState *err; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, null_request_flags); ++ err = errorCon(et, hs); ++ err->xerrno = xerrno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(http->entry, err); ++} ++ ++/* ++ * icapReqModParseHttpRequest ++ * ++ * Parse the HTTP request that we read from the ICAP server. ++ * Creates and fills in the request_t structure. ++ */ ++static void ++icapReqModParseHttpRequest(IcapStateData * icap) ++{ ++ char *mstr; ++ char *uri; ++ char *inbuf; ++ char *t; ++ char *token; ++ char *headers; ++ method_t method; ++ request_t *request; ++ http_version_t http_ver; ++ int reqlen = icap->reqmod.hdr_buf.size; ++ int hdrlen; ++ ++ /* ++ * Lazy, make a copy of the buf so I can chop it up with strtok() ++ */ ++ inbuf = xcalloc(reqlen + 1, 1); ++ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); ++ ++ if ((mstr = strtok(inbuf, "\t ")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); ++ icapReqModParseHttpError(icap, "error:invalid-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ method = urlParseMethod(mstr); ++ if (method == METHOD_NONE) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", ++ mstr); ++ icapReqModParseHttpError(icap, "error:unsupported-request-method"); ++ xfree(inbuf); ++ return; ++ } ++ /* look for URL+HTTP/x.x */ ++ if ((uri = strtok(NULL, "\n")) == NULL) { ++ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); ++ icapReqModParseHttpError(icap, "error:missing-url"); ++ xfree(inbuf); ++ return; ++ } ++ while (xisspace(*uri)) ++ uri++; ++ t = uri + strlen(uri); ++ assert(*t == '\0'); ++ token = NULL; ++ while (t > uri) { ++ t--; ++ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { ++ token = t + 1; ++ break; ++ } ++ } ++ while (t > uri && xisspace(*t)) ++ *(t--) = '\0'; ++ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); ++ if (token == NULL) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); ++ icapReqModParseHttpError(icap, "error:missing-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { ++ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); ++ icapReqModParseHttpError(icap, "error:invalid-http-ident"); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", ++ http_ver.major, http_ver.minor); ++ ++ headers = strtok(NULL, null_string); ++ hdrlen = inbuf + reqlen - headers; ++ ++ if ((request = urlParse(method, uri)) == NULL) { ++ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ /* compile headers */ ++ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { ++ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", ++ uri, __FILE__, __LINE__); ++ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); ++ xfree(inbuf); ++ return; ++ } ++ debug(81, ++ 3) ++ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); ++ request->http_ver = http_ver; ++ request->client_addr = icap->request->client_addr; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request != NULL) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service *service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->reqmod.http_entity.bytes_read >= icap->request->content_length) ++ icap->flags.reqmod_http_entity_eof = 1; ++ ++ if (!icap->flags.reqmod_http_entity_eof) ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ /* ++ * Notify the other side if it is waiting for data from us ++ */ ++ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.callback); ++ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.callback && icap->reqmod.http_entity.buf.size) { ++ icapReqModPassHttpBody(icap, ++ icap->reqmod.http_entity.callback_buf, ++ icap->reqmod.http_entity.callback_bufsize, ++ icap->reqmod.http_entity.callback, ++ icap->reqmod.http_entity.callback_data); ++ icap->reqmod.http_entity.callback = NULL; ++ cbdataUnlock(icap->reqmod.http_entity.callback_data); ++ ++ } ++} ++ ++/* ++ * icapReqModPassHttpBody ++ * ++ * Called from http.c after request headers have been sent. ++ * This function feeds the http.c module chunks of the request ++ * body that were stored in the http_entity.buf MemBuf. ++ */ ++static void ++icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ debug(81, 3) ("icapReqModPassHttpBody: called\n"); ++ if (!buf) { ++ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", ++ icap->icap_fd, buf, (int) size, cbdata); ++ comm_close(icap->icap_fd); ++ return; ++ } ++ if (!cbdataValid(cbdata)) { ++ debug(81, ++ 1) ++ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", ++ icap->icap_fd); ++ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ ++ /*icapReqModKeepAliveOrClose(icap); */ ++ return; ++ } ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ if (icap->reqmod.http_entity.buf.size) { ++ int copy_sz = icap->reqmod.http_entity.buf.size; ++ if (copy_sz > size) ++ copy_sz = size; ++ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); ++ /* XXX don't let Alex see this ugliness */ ++ xmemmove(icap->reqmod.http_entity.buf.buf, ++ icap->reqmod.http_entity.buf.buf + copy_sz, ++ icap->reqmod.http_entity.buf.size - copy_sz); ++ icap->reqmod.http_entity.buf.size -= copy_sz; ++ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", ++ copy_sz); ++ callback(buf, copy_sz, cbdata); ++ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", ++ icap->reqmod.http_entity.buf.size); ++ return; ++ } ++ if (icap->flags.reqmod_http_entity_eof) { ++ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); ++ callback(buf, 0, cbdata); ++ icapReqModKeepAliveOrClose(icap); ++ return; ++ } ++ /* ++ * We have no data for the other side at this point. Save all ++ * these values and use them when we do have data. ++ */ ++ assert(NULL == icap->reqmod.http_entity.callback); ++ icap->reqmod.http_entity.callback = callback; ++ icap->reqmod.http_entity.callback_data = cbdata; ++ icap->reqmod.http_entity.callback_buf = buf; ++ icap->reqmod.http_entity.callback_bufsize = size; ++ cbdataLock(icap->reqmod.http_entity.callback_data); ++} ++ ++/* ++ * Body reader handler for use with request->body_reader function ++ * Simple a wrapper for icapReqModPassHttpBody function ++ */ ++ ++static void ++icapReqModBodyReader(request_t * request, char *buf, size_t size, ++ CBCB * callback, void *cbdata) ++{ ++ IcapStateData *icap = request->body_reader_data; ++ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); ++} ++ ++/* ++ * icapReqModMemBufAppend ++ * ++ * stupid wrapper to eliminate compiler warnings ++ */ ++static void ++icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) ++{ ++ memBufAppend(data, buf, size); ++} +Index: src/icap_respmod.c +=================================================================== +RCS file: src/icap_respmod.c +diff -N src/icap_respmod.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ src/icap_respmod.c 23 Nov 2005 20:34:34 -0000 1.1.2.60 +@@ -0,0 +1,1039 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ ++ memBufPrintf(mb, crlf); ++ if (Config.icapcfg.send_client_ip || service->flags.need_x_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if ((Config.icapcfg.send_auth_user ++ || service->flags.need_x_authenticated_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++ if (service->flags.allow_204) ++ memBufPrintf(mb, "Allow: 204\r\n"); ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ } ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ /* No data sent yet. Start with headers */ ++ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { ++ buf += icap->sc; ++ len -= icap->sc; ++ } ++ /* ++ * Then we do not have http responce headers. All data (previous and those in buf) ++ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... ++ */ ++ if (icap->sc < 0) { ++ memBufAppend(&icap->respmod.buffer, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->sc = icap->respmod.req_hdr_copy.size; ++ icap->respmod.req_hdr_copy.size = 0; ++ buf = NULL; ++ len = 0; ++ } ++ } ++ if (0 == icap->sc) { ++ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ ++ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); ++ memBufClean(&mb); ++ return; ++ } ++#if ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ ++ icap->flags.preview_done = 1; ++ ++ if (!icap->flags.preview_done) { ++ /* preview not yet sent */ ++ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size ++ && len > 0) { ++ /* Try to collect at least preview_size+1 bytes */ ++ /* By collecting one more byte than needed for preview we know best */ ++ /* whether we have to send the ieof chunk extension */ ++ size = icap->respmod.buffer.size + len; ++ if (size > preview_size + 1) ++ size = preview_size + 1; ++ size -= icap->respmod.buffer.size; ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", ++ icap->icap_fd, size); ++ memBufAppend(&icap->respmod.buffer, buf, size); ++ buf = ((char *) buf) + size; ++ len -= size; ++ } ++ if (icap->respmod.buffer.size > preview_size || theEnd) { ++ /* we got enough bytes for preview or this is the last call */ ++ /* add preview preview now */ ++ if (icap->respmod.buffer.size > 0) { ++ size = icap->respmod.buffer.size; ++ if (size > preview_size) ++ size = preview_size; ++ memBufPrintf(&mb, "%x\r\n", size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += size; ++ } ++ if (icap->respmod.buffer.size <= preview_size) { ++ /* content length is less than preview size+1 */ ++ if (icap->respmod.res_body_sz) ++ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ } else { ++ char ch; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ /* end of preview, wait for continue or 204 signal */ ++ /* copy the extra byte and all other data to the icap buffer */ ++ /* so that it can be handled next time */ ++ ch = icap->respmod.buffer.buf[preview_size]; ++ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ ++ memBufAppend(&icap->respmod.buffer, &ch, 1); ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", ++ icap->icap_fd, len + 1); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ } ++ icap->flags.preview_done = 1; ++ icap->flags.wait_for_preview_reply = 1; ++ } ++ } else if (icap->flags.wait_for_preview_reply) { ++ /* received new data while waiting for preview response */ ++ /* add data to internal buffer and send later */ ++ debug(81, ++ 3) ++ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", ++ icap->icap_fd, len); ++ if (len > 0) ++ memBufAppend(&icap->respmod.buffer, buf, len); ++ /* do not send any data now while waiting for preview response */ ++ /* but prepare for read more data on the HTTP connection */ ++ memBufClean(&mb); ++ return; ++ } else ++#endif ++ { ++ /* after preview completed and ICAP preview response received */ ++ /* there may still be some data in the buffer */ ++ if (icap->respmod.buffer.size > 0) { ++ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); ++ memBufAppend(&mb, icap->respmod.buffer.buf, ++ icap->respmod.buffer.size); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += icap->respmod.buffer.size; ++ memBufReset(&icap->respmod.buffer); ++ } ++ if (len > 0) { ++ memBufPrintf(&mb, "%x\r\n", len); ++ memBufAppend(&mb, buf, len); ++ memBufAppend(&mb, crlf, 2); ++ icap->sc += len; ++ } ++ if (icap->flags.send_zero_chunk) { ++ /* send zero end chunk */ ++ icap->flags.send_zero_chunk = 0; ++ icap->flags.http_server_eof = 1; ++ memBufAppend(&mb, "0\r\n\r\n", 5); ++ } ++ /* wait for data coming from ICAP server as soon as we sent something */ ++ /* but of course only until we got the response header */ ++ if (!icap->flags.got_reply) ++ icap->flags.wait_for_reply = 1; ++ } ++ commSetTimeout(icap->icap_fd, -1, NULL, NULL); ++ ++ if (!mb.size) { ++ memBufClean(&mb); ++ return; ++ } ++ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ icap->flags.write_pending = 1; ++ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); ++} ++ ++static void ++icapRespModReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ int status = 0; ++ int isIcap = 0; ++ int directResponse = 0; ++ ErrorState *err; ++ const char *start; ++ const char *end; ++ ++ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ }; ++ /* OK here we have responce. Lets stop filling the ++ * icap->respmod.resp_copy buffer .... ++ */ ++ icap->flags.copy_response = 0; ++ ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++#if ICAP_PREVIEW ++ if (icap->flags.wait_for_preview_reply) { ++ if (100 == status) { ++ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ /* if http_server_eof ++ * call again icapSendRespMod to handle data that ++ * was received while waiting for this ICAP response ++ * else let http to call icapSendRespMod when new data arrived ++ */ ++ if (icap->flags.http_server_eof) ++ icapSendRespMod(icap, NULL, 0, 0); ++ /* ++ * reset the header to send the rest of the preview ++ */ ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufReset(&icap->icap_hdr); ++ ++ /*We do n't need it any more ....... */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ ++ return; ++ } ++ if (204 == status) { ++ debug(81, ++ 5) ("icapRespModReadReply: 204 No modification received\n"); ++ icap->flags.wait_for_preview_reply = 0; ++ } ++ } ++#endif /*ICAP_PREVIEW */ ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ if (204 == status) { ++ debug(81, 3) ("got 204 status from ICAP server\n"); ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ ++ /* ++ * "directResponse" is the normal case here. If we don't have ++ * a response header or body, it is an error. ++ */ ++ if (!directResponse) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink(icap->request); ++ err->xerrno = errno; ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ /* Next, gobble any data before the HTTP response starts */ ++ if (icap->enc.res_hdr > -1) ++ icap->bytes_to_gobble = icap->enc.res_hdr; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++} ++ ++ ++/* ++ * Gobble up (read) some bytes until we get to the start of the body ++ */ ++static void ++icapRespModGobble(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); ++ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, ++ icap->bytes_to_gobble); ++ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); ++ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); ++ if (len < 0) { ++ /* XXX error */ ++ abort(); ++ } ++ icap->bytes_to_gobble -= len; ++ if (icap->bytes_to_gobble) ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); ++ else ++ icapReadReply(fd, icap); ++} ++ ++ ++static void ++icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ErrorState *err; ++ ++ icap->flags.write_pending = 0; ++ debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ if (cbdataValid(icap)) ++ err->request = requestLink(icap->request); ++ storeEntryReset(icap->respmod.entry); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ return; ++ } ++ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); ++ comm_close(fd); ++ return; ++ } ++ if (icap->flags.send_zero_chunk) { ++ debug(81, ++ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); ++ icap->flags.send_zero_chunk = 0; ++ icapSendRespMod(icap, NULL, 0, 1); ++ return; ++ } ++ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { ++ /* Schedule reading the ICAP response */ ++ debug(81, ++ 3) ++ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", ++ fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++#if 1 ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++#else ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ /* ++ * Set the read timeout only after all data has been sent ++ * or we are waiting for a preview response ++ * If the ICAP server does not return any data till all data ++ * has been sent, we are likely to hit the timeout for large ++ * HTTP bodies ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ } ++#endif ++ } ++} ++ ++void ++icapConnectOver(int fd, int status, void *data) ++{ ++ ErrorState *err; ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ if (status < 0) { ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->xerrno = errno; ++ err->request = requestLink(icap->request); ++ errorAppendEntry(icap->respmod.entry, err); ++ comm_close(fd); ++ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); ++ icapOptSetUnreachable(icap->current_service); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); ++} ++ ++ ++ ++IcapStateData * ++icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, ++ http_state_flags http_flags) ++{ ++ IcapStateData *icap = NULL; ++ CNCB *theCallback = NULL; ++ icap_service *service = NULL; ++ ++ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); ++ assert(type >= 0 && type < ICAP_SERVICE_MAX); ++ ++ service = icapService(type, request); ++ if (!service) { ++ debug(81, 3) ("icapRespModStart: no service found\n"); ++ return NULL; /* no service found */ ++ } ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ++ ("icapRespModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ++ ("icapRespModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ switch (type) { ++ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change ++ * this switch, because callbacks isn't keep */ ++ case ICAP_SERVICE_RESPMOD_PRECACHE: ++ theCallback = icapConnectOver; ++ break; ++ default: ++ fatalf("icapRespModStart: unsupported service type '%s'\n", ++ icap_service_type_str[type]); ++ break; ++ } ++ ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->request = requestLink(request); ++ icap->respmod.entry = entry; ++ if (entry) ++ storeLockObject(entry); ++ icap->http_flags = http_flags; ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ ++ /* ++ * Don't create socket to the icap server now, but only for the first ++ * packet receive from the http server. This will resolve all timeout ++ * between the web server and icap server. ++ */ ++ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); ++ icap->flags.connect_requested = 0; ++ ++ /* ++ * make a copy the HTTP response that we send to the ICAP server in ++ * case it turns out to be a 204 ++ */ ++#ifdef SUPPORT_ICAP_204 ++ icap->flags.copy_response = 1; ++#elif ICAP_PREVIEW ++ if (preview_size < 0 || !Config.icapcfg.preview_enable) ++ icap->flags.copy_response = 0; ++ else ++ icap->flags.copy_response = 1; ++#else ++ icap->flags.copy_response = 0; ++#endif ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapRespModStart: returning %p\n", icap); ++ return icap; ++} ++ ++static int ++icapHttpReplyHdrState(IcapStateData * icap) ++{ ++ assert(icap); ++ if (NULL == icap->httpState) ++ return 0; ++ return icap->httpState->reply_hdr_state; ++} ++ ++static void ++icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) ++{ ++ if (NULL == icap->httpState) { ++ icap->httpState = cbdataAlloc(HttpStateData); ++ icap->httpState->request = requestLink(icap->request); ++ icap->httpState->orig_request = requestLink(icap->request); ++ icap->httpState->entry = icap->respmod.entry; ++ storeLockObject(icap->httpState->entry); /* lock it */ ++ } ++ httpProcessReplyHeader(icap->httpState, buf, size); ++ if (2 == icap->httpState->reply_hdr_state) ++ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); ++} ++ ++/* ++ * icapRespModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapRespModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ err->request = requestLink((request_t *) request); ++ err->xerrno = errno; ++ errorAppendEntry(entry, err); ++ comm_close(fd); ++ } else { ++ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", ++ fd); ++ comm_close(fd); ++ } ++ return; ++ } ++ if (icapReadReply2(icap) < 0) ++ comm_close(fd); ++} ++ ++static int ++icapReadReply2(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ debug(81, 3) ("icapReadReply2\n"); ++ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); ++ err->xerrno = errno; ++ err->request = requestLink((request_t *) request); ++ errorAppendEntry(entry, err); ++ icap->flags.http_server_eof = 1; ++ return -1; ++ } ++ if (icap->chunk_buf.size == 0) { ++ /* Retrieval done. */ ++ if (icapHttpReplyHdrState(icap) < 2) ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ icap->flags.http_server_eof = 1; ++ icapReadReply3(icap); ++ return 0; ++ } ++ if (icapHttpReplyHdrState(icap) == 0) { ++ int expect = icapExpectedHttpReplyHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed < 0 || needed >= 0); ++ if (0 > expect) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ } else if (0 == expect) { ++ /* ++ * this icap reply doesn't give us new HTTP headers ++ * so we must copy them from our copy ++ */ ++ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, ++ __LINE__); ++ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ ++ storeAppend(entry, ++ icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ } ++ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, ++ icap->chunk_buf.size); ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ ++ } else if (needed) { ++ icapProcessHttpReplyHeader(icap, ++ icap->chunk_buf.buf, icap->chunk_buf.size); ++ if (icap->chunk_buf.size >= needed) { ++ storeAppend(entry, icap->chunk_buf.buf, needed); ++ so_far += needed; ++ xmemmove(icap->chunk_buf.buf, ++ icap->chunk_buf.buf + needed, ++ icap->chunk_buf.size - needed); ++ icap->chunk_buf.size -= needed; ++ assert(icapHttpReplyHdrState(icap) == 2); ++ icap->chunk_size = 0; ++ } else { ++ /* ++ * We don't have the full HTTP reply headers yet, so keep ++ * the partial reply buffered in 'chunk_buf' and wait ++ * for more. ++ */ ++ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); ++ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } ++ } ++ icap->http_header_bytes_read_so_far = so_far; ++ } ++ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, ++ (int) icap->chunk_buf.size); ++ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, ++ icap->flags.no_content); ++ if (icap->flags.no_content) { ++ /* data from http.c is not chunked */ ++ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", ++ icap->chunk_buf.size); ++ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); ++ icap->chunk_buf.size = 0; ++ } ++ } else if (2 == icapHttpReplyHdrState(icap)) { ++ if (icap->chunk_buf.size) ++ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); ++ } ++ icapReadReply3(icap); ++ return 0; ++} ++ ++static void ++icapReadReply3(IcapStateData * icap) ++{ ++ StoreEntry *entry = icap->respmod.entry; ++ int fd = icap->icap_fd; ++ debug(81, 3) ("icapReadReply3\n"); ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ icapRespModKeepAliveOrClose(icap); ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.28.6.25 +retrieving revision 1.28.6.8.2.11 +diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11 +--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25 ++++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11 +@@ -350,6 +350,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + authenticateShutdown(); + externalAclShutdown(); +@@ -378,6 +381,9 @@ mainReconfigure(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -507,6 +513,9 @@ mainInitialize(void) + idnsInit(); + #endif + redirectInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); +Index: src/mem.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/mem.c,v +retrieving revision 1.13 +retrieving revision 1.13.28.2 +diff -p -u -b -r1.13 -r1.13.28.2 +--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13 ++++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2 +@@ -243,6 +243,13 @@ memInit(void) + memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.140.1 +diff -p -u -b -r1.2 -r1.2.140.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1 +@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str"; + $pat{'icp_opcode'} = "icp_opcode_str"; + $pat{'swap_log_op'} = "swap_log_op_str"; + $pat{'lookup_t'} = "lookup_t_str"; ++$pat{'icap_service_t'} = "icap_service_type_str"; + + $state = 0; # start state + while (<>) { +Index: src/pconn.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/pconn.c,v +retrieving revision 1.6.38.2 +retrieving revision 1.6.60.2 +diff -p -u -b -r1.6.38.2 -r1.6.60.2 +--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2 ++++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2 +@@ -46,6 +46,9 @@ struct _pconn { + #define PCONN_HIST_SZ (1<<16) + int client_pconn_hist[PCONN_HIST_SZ]; + int server_pconn_hist[PCONN_HIST_SZ]; ++#ifdef HS_FEAT_ICAP ++int icap_server_pconn_hist[PCONN_HIST_SZ]; ++#endif + + static PF pconnRead; + static PF pconnTimeout; +@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); + } ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(e, ++ "\n" ++ "ICAP-server persistent connection counts:\n" ++ "\n" ++ "\treq/\n" ++ "\tconn count\n" ++ "\t---- ---------\n"); ++ for (i = 0; i < PCONN_HIST_SZ; i++) { ++ if (icap_server_pconn_hist[i] == 0) ++ continue; ++ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); ++ } ++#endif + } + + /* ========== PUBLIC FUNCTIONS ============================================ */ +@@ -173,6 +190,9 @@ pconnInit(void) + for (i = 0; i < PCONN_HIST_SZ; i++) { + client_pconn_hist[i] = 0; + server_pconn_hist[i] = 0; ++#ifdef HS_FEAT_ICAP ++ icap_server_pconn_hist[i] = 0; ++#endif + } + pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); + pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); +@@ -248,11 +268,15 @@ pconnHistCount(int what, int i) + { + if (i >= PCONN_HIST_SZ) + i = PCONN_HIST_SZ - 1; +- /* what == 0 for client, 1 for server */ ++ /* what == 0 for client, 1 for server, 2 for ICAP server */ + if (what == 0) + client_pconn_hist[i]++; + else if (what == 1) + server_pconn_hist[i]++; ++#ifdef HS_FEAT_ICAP ++ else if (what == 2) ++ icap_server_pconn_hist[i]++; ++#endif + else + assert(0); + } +Index: src/protos.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/protos.h,v +retrieving revision 1.41.6.33 +retrieving revision 1.41.6.13.2.37 +diff -p -u -b -r1.41.6.33 -r1.41.6.13.2.37 +--- src/protos.h 16 Sep 2005 02:13:25 -0000 1.41.6.33 ++++ src/protos.h 6 Dec 2005 21:53:44 -0000 1.41.6.13.2.37 +@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *); + /* http.c */ + extern int httpCachable(method_t); + extern void httpStart(FwdState *); ++extern void httpParseReplyHeaders(const char *, http_reply *); ++extern void httpProcessReplyHeader(HttpStateData *, const char *, int); + extern int httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, +@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c + extern FREE *memBufFreeFunc(MemBuf * mb); + /* puts report on MemBuf _module_ usage into mb */ + extern void memBufReport(MemBuf * mb); ++extern int memBufRead(int fd, MemBuf * mb); + + extern char *mime_get_header(const char *mime, const char *header); + extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); +@@ -1341,4 +1344,49 @@ extern void externalAclShutdown(void); + extern int externalAclRequiresAuth(void *acl_data); + extern char *strtokFile(void); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.13.6.8 +retrieving revision 1.13.6.6.2.11 +diff -p -u -b -r1.13.6.8 -r1.13.6.6.2.11 +--- src/squid.h 26 Mar 2005 03:15:58 -0000 1.13.6.8 ++++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11 +@@ -38,6 +38,14 @@ + #include "config.h" + + /* ++ * experimental defines for ICAP ++ */ ++#ifdef HS_FEAT_ICAP ++#define ICAP_PREVIEW 1 ++#define SUPPORT_ICAP_204 0 ++#endif ++ ++/* + * On some systems, FD_SETSIZE is set to something lower than the + * actual number of files which can be opened. IRIX is one case, + * NetBSD is another. So here we increase FD_SETSIZE to our +Index: src/stat.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/stat.c,v +retrieving revision 1.13.6.14 +retrieving revision 1.13.6.7.2.7 +diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7 +--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14 ++++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7 +@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min + storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", + XAVG(server.other.kbytes_out.kb)); + ++#ifdef HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", ++ XAVG(icap.all.requests)); ++ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", ++ XAVG(icap.all.errors)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", ++ XAVG(icap.all.kbytes_in.kb)); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", ++ XAVG(icap.all.kbytes_out.kb)); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", + XAVG(icp.pkts_sent)); + storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", +@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry) + storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", + (int) f->server.other.kbytes_out.kb); + ++#if HS_FEAT_ICAP ++ storeAppendPrintf(sentry, "icap.all.requests = %d\n", ++ (int) f->icap.all.requests); ++ storeAppendPrintf(sentry, "icap.all.errors = %d\n", ++ (int) f->icap.all.errors); ++ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", ++ (int) f->icap.all.kbytes_in.kb); ++ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", ++ (int) f->icap.all.kbytes_out.kb); ++#endif ++ + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", + f->icp.pkts_sent); + storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", +@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s) + storeAppendPrintf(s, "\tme: %s:%d\n", + inet_ntoa(conn->me.sin_addr), + ntohs(conn->me.sin_port)); +- storeAppendPrintf(s, "\tnrequests: %d\n", +- conn->nrequests); + storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", + conn->defer.n, (long int) conn->defer.until); + } +Index: src/store.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/store.c,v +retrieving revision 1.16.6.9 +retrieving revision 1.16.6.2.2.8 +diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8 +--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9 ++++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8 +@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.48.2.43 +retrieving revision 1.48.2.9.2.48 +diff -p -u -b -r1.48.2.43 -r1.48.2.9.2.48 +--- src/structs.h 4 Sep 2005 02:13:28 -0000 1.48.2.43 ++++ src/structs.h 30 Nov 2005 21:52:15 -0000 1.48.2.9.2.48 +@@ -384,6 +384,22 @@ struct _RemovalPolicySettings { + wordlist *args; + }; + ++#if HS_FEAT_ICAP ++struct _IcapConfig { ++ int onoff; ++ int preview_enable; ++ icap_service *service_head; ++ icap_class *class_head; ++ icap_access *access_head; ++ int preview_size; ++ int check_interval; ++ int send_client_ip; ++ int send_auth_user; ++ char *auth_scheme; ++}; ++ ++#endif /* HS_FEAT_ICAP */ ++ + struct _SquidConfig { + struct { + squid_off_t maxSize; +@@ -714,6 +730,9 @@ struct _SquidConfig { + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + external_acl *externalAclHelperList; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -787,7 +806,10 @@ struct _fde { + } flags; + squid_off_t bytes_read; + squid_off_t bytes_written; +- int uses; /* ie # req's over persistent conn */ ++ struct { ++ int uses; ++ int type; ++ } pconn; + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; +@@ -982,6 +1004,130 @@ struct _http_state_flags { + unsigned int request_sent:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -993,10 +1139,14 @@ struct _HttpStateData { + int fd; + http_state_flags flags; + FwdState *fwd; ++#ifdef HS_FEAT_ICAP ++ struct _IcapStateData *icap_writer; ++#endif + char *body_buf; + int body_buf_sz; + }; + ++ + struct _icpUdpData { + struct sockaddr_in address; + void *msg; +@@ -1092,6 +1242,7 @@ struct _clientHttpRequest { + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; ++ unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { +@@ -1100,6 +1251,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1127,7 +1281,6 @@ struct _ConnStateData { + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; +- int nrequests; + struct { + int n; + time_t until; +@@ -1678,6 +1831,9 @@ struct _request_t { + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + }; +@@ -1784,7 +1940,11 @@ struct _StatCounters { + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; +- } server; ++ } ++#if HS_FEAT_ICAP ++ icap, ++#endif ++ server; + struct { + int pkts_sent; + int queries_sent; +Index: src/typedefs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/typedefs.h,v +retrieving revision 1.25.6.8 +retrieving revision 1.25.6.1.6.13 +diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13 +--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8 ++++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13 +@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade + typedef struct _HttpBody HttpBody; + typedef struct _HttpReply HttpReply; + typedef struct _HttpStateData HttpStateData; ++#ifdef HS_FEAT_ICAP ++typedef struct _IcapStateData IcapStateData; ++typedef struct _IcapConfig IcapConfig; ++typedef struct _icap_service icap_service; ++typedef struct _icap_service_list icap_service_list; ++typedef struct _icap_class icap_class; ++typedef struct _icap_access icap_access; ++typedef struct _IcapOptData IcapOptData; ++#endif + typedef struct _icpUdpData icpUdpData; + typedef struct _clientHttpRequest clientHttpRequest; + typedef struct _ConnStateData ConnStateData; +Index: src/url.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/url.c,v +retrieving revision 1.7.6.6 +retrieving revision 1.7.6.5.2.2 +diff -p -u -b -r1.7.6.6 -r1.7.6.5.2.2 +--- src/url.c 12 Nov 2005 03:13:48 -0000 1.7.6.6 ++++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -221,6 +224,10 @@ urlParseProtocol(const char *s) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; ++#ifdef HS_FEAT_ICAP ++ if (strcasecmp(s, "icap") == 0) ++ return PROTO_ICAP; ++#endif + return PROTO_NONE; + } + +@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p) + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; ++#ifdef HS_FEAT_ICAP ++ case PROTO_ICAP: ++ return 1344; ++#endif + default: + return 0; + } |