---------------------
PatchSet 5338 
Date: 2002/10/11 15:40:40
Author: rbcollins
Branch: rbcollins_cxxtest
Tag: (none) 
Log:
more conversions

Members: 
	src/ICP.h:1.1.2.4->1.1.2.5 
	src/Makefile.am:1.29.2.23->1.29.2.24 
	src/acl.cc:1.1.2.2->1.1.2.3 
	src/cache_cf.cc:1.1.2.4->1.1.2.5 
	src/client_db.cc:1.1.2.1->1.1.2.2 
	src/client_side.cc:1.1.2.1->1.1.2.2 
	src/comm.c:1.27.2.1->1.27.2.2(DEAD) 
	src/comm.cc:1.1->1.1.2.1 
	src/comm_kqueue.c:1.3.6.1->1.3.6.2(DEAD) 
	src/comm_kqueue.cc:1.1->1.1.2.1 
	src/comm_poll.c:1.6.2.1->1.6.2.2(DEAD) 
	src/comm_poll.cc:1.1->1.1.2.1 
	src/comm_select.c:1.15.2.1->1.15.2.2(DEAD) 
	src/comm_select.cc:1.1->1.1.2.1 
	src/debug.c:1.10->1.10.6.1(DEAD) 
	src/debug.cc:1.1->1.1.2.1 
	src/disk.c:1.12->1.12.2.1(DEAD) 
	src/disk.cc:1.1->1.1.2.1 
	src/fqdncache.c:1.17.6.1->1.17.6.2(DEAD) 
	src/fqdncache.cc:1.1->1.1.2.1 
	src/ftp.c:1.28.6.2->1.28.6.3(DEAD) 
	src/ftp.cc:1.1->1.1.2.1 
	src/gopher.c:1.19.2.1->1.19.2.2(DEAD) 
	src/gopher.cc:1.1->1.1.2.1 
	src/helper.c:1.26.2.1->1.26.2.2(DEAD) 
	src/helper.cc:1.1->1.1.2.1 
	src/icp_v2.cc:1.1.2.17->1.1.2.18 
	src/internal.c:1.9.18.1->1.9.18.2(DEAD) 
	src/internal.cc:1.1->1.1.2.1 
	src/ipc.c:1.9->1.9.20.1(DEAD) 
	src/ipc.cc:1.1->1.1.2.1 
	src/net_db.cc:1.1.2.1->1.1.2.2 
	src/peer_digest.c:1.17.2.1->1.17.2.2(DEAD) 
	src/peer_digest.cc:1.1->1.1.2.1 
	src/peer_select.c:1.16.6.2->1.16.6.3(DEAD) 
	src/peer_select.cc:1.1->1.1.2.1 
	src/pinger.c:1.4.96.1->1.4.96.2(DEAD) 
	src/pinger.cc:1.1->1.1.2.1 
	src/referer.c:1.4->1.4.60.1(DEAD) 
	src/referer.cc:1.1->1.1.2.1 
	src/refresh.c:1.8.14.1->1.8.14.2(DEAD) 
	src/refresh.cc:1.1->1.1.2.1 
	src/snmp_agent.cc:1.1.2.1->1.1.2.2 
	src/snmp_core.c:1.10->1.10.28.1(DEAD) 
	src/snmp_core.cc:1.1->1.1.2.1 
	src/ssl_support.c:1.7->1.7.14.1(DEAD) 
	src/ssl_support.cc:1.1->1.1.2.1 
	src/store_key_md5.c:1.6->1.6.66.1(DEAD) 
	src/store_key_md5.cc:1.1->1.1.2.1 
	src/store_rebuild.c:1.9.44.2->1.9.44.3(DEAD) 
	src/store_rebuild.cc:1.1->1.1.2.1 
	src/store_swapin.c:1.7.2.1->1.7.2.2(DEAD) 
	src/store_swapin.cc:1.1->1.1.2.1 
	src/store_swapout.c:1.14.2.1->1.14.2.2(DEAD) 
	src/store_swapout.cc:1.1->1.1.2.1 
	src/structs.h:1.70.2.8->1.70.2.9 

Index: squid/src/ICP.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/ICP.h,v
retrieving revision 1.1.2.4
retrieving revision 1.1.2.5
diff -u -r1.1.2.4 -r1.1.2.5
--- squid/src/ICP.h	9 Oct 2002 05:55:49 -0000	1.1.2.4
+++ squid/src/ICP.h	11 Oct 2002 15:40:40 -0000	1.1.2.5
@@ -1,6 +1,6 @@
 
 /*
- * $Id: ICP.h,v 1.1.2.4 2002/10/09 05:55:49 rbcollins Exp $
+ * $Id: ICP.h,v 1.1.2.5 2002/10/11 15:40:40 rbcollins Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -56,6 +56,7 @@
     _icp_common_t (char *buf, unsigned int len);
     void handleReply(char *buf, struct sockaddr_in *from);
     static _icp_common_t *createMessage(icp_opcode opcode, int flags, const char *url, int reqnum, int pad);
+    icp_opcode getOpCode() const;
 #endif
 };
 
Index: squid/src/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v
retrieving revision 1.29.2.23
retrieving revision 1.29.2.24
diff -u -r1.29.2.23 -r1.29.2.24
--- squid/src/Makefile.am	9 Oct 2002 14:36:40 -0000	1.29.2.23
+++ squid/src/Makefile.am	11 Oct 2002 15:40:41 -0000	1.29.2.24
@@ -15,7 +15,7 @@
 endif
 
 if USE_SNMP
-SNMPSOURCE = snmp_core.c snmp_agent.cc
+SNMPSOURCE = snmp_core.cc snmp_agent.cc
 else
 SNMPSOURCE = 
 endif
@@ -57,7 +57,7 @@
 endif
 
 if ENABLE_SSL
-SSLSOURCE = ssl_support.c
+SSLSOURCE = ssl_support.cc
 else
 SSLSOURCE = 
 endif
@@ -109,10 +109,10 @@
 	dns_internal.cc \
 	htcp.cc \
 	leakfinder.c \
-	snmp_core.c \
+	snmp_core.cc \
 	snmp_agent.cc \
 	unlinkd.cc \
-	ssl_support.c \
+	ssl_support.cc \
 	ssl_support.h \
 	win32.cc
 
@@ -133,14 +133,14 @@
 	client_side_request.h \
 	clientStream.cc \
 	clientStream.h \
-	comm.c \
-	comm_select.c \
-	comm_poll.c \
-	comm_kqueue.c \
-	debug.c \
+	comm.cc \
+	comm_select.cc \
+	comm_poll.cc \
+	comm_kqueue.cc \
+	debug.cc \
 	defines.h \
 	$(DELAY_POOL_SOURCE) \
-	disk.c \
+	disk.cc \
 	$(DNSSOURCE) \
 	enums.h \
 	errorpage.cc \
@@ -150,11 +150,11 @@
 	fd.cc \
 	filemap.cc \
 	forward.c \
-	fqdncache.c \
-	ftp.c \
+	fqdncache.cc \
+	ftp.cc \
 	globals.h \
-	gopher.c \
-	helper.c \
+	gopher.cc \
+	helper.cc \
 	$(HTCPSOURCE) \
 	http.cc \
 	HttpStatusLine.cc \
@@ -171,8 +171,8 @@
 	icp_v2.cc \
 	icp_v3.cc \
 	ident.cc \
-	internal.c \
-	ipc.c \
+	internal.cc \
+	ipc.cc \
 	ipcache.cc \
 	IPInterception.cc \
 	IPInterception.h \
@@ -188,12 +188,12 @@
 	Packer.cc \
 	$(XPROF_STATS_SOURCE) \
 	pconn.cc \
-	peer_digest.c \
-	peer_select.c \
+	peer_digest.cc \
+	peer_select.cc \
 	protos.h \
 	redirect.cc \
-	referer.c \
-	refresh.c \
+	referer.cc \
+	refresh.cc \
 	send-announce.cc \
 	$(SNMPSOURCE) \
 	squid.h \
@@ -210,12 +210,12 @@
 	StoreClient.h \
 	store_digest.cc \
 	store_dir.cc \
-	store_key_md5.c \
+	store_key_md5.cc \
 	store_log.cc \
-	store_rebuild.c \
-	store_swapin.c \
+	store_rebuild.cc \
+	store_swapin.cc \
 	store_swapmeta.cc \
-	store_swapout.c \
+	store_swapout.cc \
 	structs.h \
 	tools.cc \
 	typedefs.h \
@@ -257,8 +257,8 @@
 	$(COMPILE) -DUNLINK_DAEMON -c $(srcdir)/unlinkd.cc -o $@
 
 pinger_SOURCES = \
-	pinger.c \
-	debug.c
+	pinger.cc \
+	debug.cc
 
 dnsserver_SOURCES = dnsserver.cc
 recv_announce_SOURCES = recv-announce.cc
Index: squid/src/acl.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/acl.cc,v
retrieving revision 1.1.2.2
retrieving revision 1.1.2.3
diff -u -r1.1.2.2 -r1.1.2.3
--- squid/src/acl.cc	9 Oct 2002 00:53:35 -0000	1.1.2.2
+++ squid/src/acl.cc	11 Oct 2002 15:40:42 -0000	1.1.2.3
@@ -1,5 +1,5 @@
 /*
- * $Id: acl.cc,v 1.1.2.2 2002/10/09 00:53:35 rbcollins Exp $
+ * $Id: acl.cc,v 1.1.2.3 2002/10/11 15:40:42 rbcollins Exp $
  *
  * DEBUG: section 28    Access Control
  * AUTHOR: Duane Wessels
@@ -610,7 +610,7 @@
 {
     char *t = NULL;
     while ((t = strtokFile()))
-	wordlistAdd(curlist, t);
+	wordlistAdd((wordlist **)curlist, t);
 }
 #endif
 
@@ -1630,7 +1630,7 @@
 	/* NOTREACHED */
 #if SQUID_SNMP
     case ACL_SNMP_COMMUNITY:
-	return aclMatchWordList(ae->data, checklist->snmp_community);
+	return aclMatchWordList((wordlist *)ae->data, checklist->snmp_community);
 	/* NOTREACHED */
 #endif
     case ACL_SRC_ASN:
@@ -2476,7 +2476,7 @@
 	return aclDumpDomainList(a->data);
 #if SQUID_SNMP
     case ACL_SNMP_COMMUNITY:
-	return wordlistDup(a->data);
+	return wordlistDup((wordlist *)a->data);
 #endif
 #if USE_IDENT
     case ACL_IDENT:
Index: squid/src/cache_cf.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/cache_cf.cc,v
retrieving revision 1.1.2.4
retrieving revision 1.1.2.5
diff -u -r1.1.2.4 -r1.1.2.5
--- squid/src/cache_cf.cc	6 Oct 2002 06:07:44 -0000	1.1.2.4
+++ squid/src/cache_cf.cc	11 Oct 2002 15:40:42 -0000	1.1.2.5
@@ -1,6 +1,6 @@
 
 /*
- * $Id: cache_cf.cc,v 1.1.2.4 2002/10/06 06:07:44 rbcollins Exp $
+ * $Id: cache_cf.cc,v 1.1.2.5 2002/10/11 15:40:42 rbcollins Exp $
  *
  * DEBUG: section 3     Configuration File Parsing
  * AUTHOR: Harvest Derived
@@ -2378,7 +2378,7 @@
     } else {
 	self_destruct();
     }
-    s = xcalloc(1, sizeof(*s));
+    s = (https_port_list *)xcalloc(1, sizeof(*s));
     s->s.sin_port = htons(port);
     if (NULL == host)
 	s->s.sin_addr = any_addr;
Index: squid/src/client_db.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/client_db.cc,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- squid/src/client_db.cc	9 Oct 2002 00:23:16 -0000	1.1.2.1
+++ squid/src/client_db.cc	11 Oct 2002 15:40:43 -0000	1.1.2.2
@@ -1,6 +1,6 @@
 
 /*
- * $Id: client_db.cc,v 1.1.2.1 2002/10/09 00:23:16 rbcollins Exp $
+ * $Id: client_db.cc,v 1.1.2.2 2002/10/11 15:40:43 rbcollins Exp $
  *
  * DEBUG: section 0     Client Database
  * AUTHOR: Duane Wessels
@@ -298,7 +298,7 @@
 	break;
     case MESH_CTBL_HTHITS:
 	aggr = 0;
-	for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; l++) {
+	for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
 	    if (logTypeIsATcpHit(l))
 		aggr += c->Http.result_hist[l];
 	}
Index: squid/src/client_side.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/client_side.cc,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- squid/src/client_side.cc	9 Oct 2002 14:14:26 -0000	1.1.2.1
+++ squid/src/client_side.cc	11 Oct 2002 15:40:43 -0000	1.1.2.2
@@ -1,6 +1,6 @@
 
 /*
- * $Id: client_side.cc,v 1.1.2.1 2002/10/09 14:14:26 rbcollins Exp $
+ * $Id: client_side.cc,v 1.1.2.2 2002/10/11 15:40:43 rbcollins Exp $
  *
  * DEBUG: section 33    Client-side Routines
  * AUTHOR: Duane Wessels
@@ -1747,7 +1747,7 @@
 static void
 clientNegotiateSSL(int fd, void *data)
 {
-    ConnStateData *conn = data;
+    ConnStateData *conn = (ConnStateData *)data;
     X509 *client_cert;
     int ret;
 
@@ -1795,7 +1795,7 @@
 httpsAccept(int sock, void *data)
 {
     int *N = &incoming_sockets_accepted;
-    https_port_data *https_port = data;
+    https_port_data *https_port = (https_port_data *)data;
     SSL_CTX *sslContext = https_port->sslContext;
     int fd = -1;
     ConnStateData *connState = NULL;
--- squid/src/comm.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,1050 +0,0 @@
-
-/*
- * $Id: comm.c,v 1.27.2.1 2002/10/09 05:09:23 rbcollins Exp $
- *
- * DEBUG: section 5     Socket Functions
- * AUTHOR: Harvest Derived
- *
- * 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"
-
-#if defined(_SQUID_CYGWIN_)
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-
-typedef struct {
-    char *host;
-    u_short port;
-    struct sockaddr_in S;
-    CNCB *callback;
-    void *data;
-    struct in_addr in_addr;
-    int locks;
-    int fd;
-    int tries;
-    int addrcount;
-    int connstart;
-} ConnectStateData;
-
-/* STATIC */
-static int commBind(int s, struct in_addr, u_short port);
-static void commSetReuseAddr(int);
-static void commSetNoLinger(int);
-static void CommWriteStateCallbackAndFree(int fd, comm_err_t code);
-#ifdef TCP_NODELAY
-static void commSetTcpNoDelay(int);
-#endif
-static void commSetTcpRcvbuf(int, int);
-static PF commConnectFree;
-static PF commConnectHandle;
-static PF commHandleWrite;
-static IPH commConnectDnsHandle;
-static void commConnectCallback(ConnectStateData * cs, comm_err_t status);
-static int commResetFD(ConnectStateData * cs);
-static int commRetryConnect(ConnectStateData * cs);
-CBDATA_TYPE(ConnectStateData);
-
-static MemPool *comm_write_pool = NULL;
-static MemPool *conn_close_pool = NULL;
-
-static void
-CommWriteStateCallbackAndFree(int fd, comm_err_t code)
-{
-    CommWriteStateData *CommWriteState = fd_table[fd].rwstate;
-    CWCB *callback = NULL;
-    void *cbdata;
-    fd_table[fd].rwstate = NULL;
-    if (CommWriteState == NULL)
-	return;
-    if (CommWriteState->free_func) {
-	FREE *free_func = CommWriteState->free_func;
-	void *free_buf = CommWriteState->buf;
-	CommWriteState->free_func = NULL;
-	CommWriteState->buf = NULL;
-	free_func(free_buf);
-    }
-    callback = CommWriteState->handler;
-    CommWriteState->handler = NULL;
-    if (callback && cbdataReferenceValidDone(CommWriteState->handler_data, &cbdata))
-	callback(fd, CommWriteState->buf, CommWriteState->offset, code, cbdata);
-    memPoolFree(comm_write_pool, CommWriteState);
-}
-
-/* Return the local port associated with fd. */
-u_short
-comm_local_port(int fd)
-{
-    struct sockaddr_in addr;
-    socklen_t addr_len = 0;
-    fde *F = &fd_table[fd];
-
-    /* If the fd is closed already, just return */
-    if (!F->flags.open) {
-	debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd);
-	return 0;
-    }
-    if (F->local_port)
-	return F->local_port;
-    addr_len = sizeof(addr);
-    if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
-	debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());
-	return 0;
-    }
-    F->local_port = ntohs(addr.sin_port);
-    debug(5, 6) ("comm_local_port: FD %d: port %d\n", fd, (int) F->local_port);
-    return F->local_port;
-}
-
-static comm_err_t
-commBind(int s, struct in_addr in_addr, u_short port)
-{
-    struct sockaddr_in S;
-
-    memset(&S, '\0', sizeof(S));
-    S.sin_family = AF_INET;
-    S.sin_port = htons(port);
-    S.sin_addr = in_addr;
-    statCounter.syscalls.sock.binds++;
-    if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)
-	return COMM_OK;
-    debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n",
-	s,
-	S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),
-	(int) port,
-	xstrerror());
-    return COMM_ERROR;
-}
-
-/* Create a socket. Default is blocking, stream (TCP) socket.  IO_TYPE
- * is OR of flags specified in comm.h. Defaults TOS */
-int
-comm_open(int sock_type,
-    int proto,
-    struct in_addr addr,
-    u_short port,
-    int flags,
-    const char *note)
-{
-    return comm_openex(sock_type, proto, addr, port, flags, 0, note);
-}
-
-
-/* Create a socket. Default is blocking, stream (TCP) socket.  IO_TYPE
- * is OR of flags specified in defines.h:COMM_* */
-int
-comm_openex(int sock_type,
-    int proto,
-    struct in_addr addr,
-    u_short port,
-    int flags,
-    unsigned char TOS,
-    const char *note)
-{
-    int new_socket;
-    int tos = 0;
-    fde *F = NULL;
-
-    PROF_start(comm_open);
-    /* Create socket for accepting new connections. */
-    statCounter.syscalls.sock.sockets++;
-    if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {
-	/* Increase the number of reserved fd's if calls to socket()
-	 * are failing because the open file table is full.  This
-	 * limits the number of simultaneous clients */
-	switch (errno) {
-	case ENFILE:
-	case EMFILE:
-	    debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror());
-	    fdAdjustReserved();
-	    break;
-	default:
-	    debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror());
-	}
-	PROF_stop(comm_open);
-	return -1;
-    }
-    /* set TOS if needed */
-    if (TOS) {
-#ifdef IP_TOS
-	tos = TOS;
-	if (setsockopt(new_socket, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
-	        debug(50, 1) ("comm_open: setsockopt(IP_TOS) on FD %d: %s\n",
-		new_socket, xstrerror());
-#else
-	debug(50, 0) ("comm_open: setsockopt(IP_TOS) not supported on this platform\n");
-#endif
-    }
-    /* update fdstat */
-    debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket);
-    fd_open(new_socket, FD_SOCKET, note);
-    F = &fd_table[new_socket];
-    F->local_addr = addr;
-    F->tos = tos;
-    if (!(flags & COMM_NOCLOEXEC))
-	commSetCloseOnExec(new_socket);
-    if ((flags & COMM_REUSEADDR))
-	commSetReuseAddr(new_socket);
-    if (port > (u_short) 0) {
-	commSetNoLinger(new_socket);
-	if (opt_reuseaddr)
-	    commSetReuseAddr(new_socket);
-    }
-    if (addr.s_addr != no_addr.s_addr) {
-	if (commBind(new_socket, addr, port) != COMM_OK) {
-	    comm_close(new_socket);
-	    return -1;
-	    PROF_stop(comm_open);
-	}
-    }
-    F->local_port = port;
-
-    if (flags & COMM_NONBLOCKING)
-	if (commSetNonBlocking(new_socket) == COMM_ERROR) {
-	    return -1;
-	    PROF_stop(comm_open);
-	}
-#ifdef TCP_NODELAY
-    if (sock_type == SOCK_STREAM)
-	commSetTcpNoDelay(new_socket);
-#endif
-    if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
-	commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
-    PROF_stop(comm_open);
-    return new_socket;
-}
-
-/*
- * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to      
- * impose an upper limit.  Solaris' listen(3n) page says it has   
- * no limit on this parameter, but sys/socket.h sets SOMAXCONN 
- * to 5.  HP-UX currently has a limit of 20.  SunOS is 5 and
- * OSF 3.0 is 8.
- */
-int
-comm_listen(int sock)
-{
-    int x;
-    if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {
-	debug(50, 0) ("comm_listen: listen(%d, %d): %s\n",
-	    Squid_MaxFD >> 2,
-	    sock, xstrerror());
-	return x;
-    }
-    return sock;
-}
-
-void
-commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
-{
-    ConnectStateData *cs;
-    debug(5, 3) ("commConnectStart: FD %d, %s:%d\n", fd, host, (int) port);
-    cs = cbdataAlloc(ConnectStateData);
-    cs->fd = fd;
-    cs->host = xstrdup(host);
-    cs->port = port;
-    cs->callback = callback;
-    cs->data = cbdataReference(data);
-    comm_add_close_handler(fd, commConnectFree, cs);
-    cs->locks++;
-    ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
-}
-
-static void
-commConnectDnsHandle(const ipcache_addrs * ia, void *data)
-{
-    ConnectStateData *cs = data;
-    assert(cs->locks == 1);
-    cs->locks--;
-    if (ia == NULL) {
-	debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host);
-	if (!dns_error_message) {
-	    dns_error_message = "Unknown DNS error";
-	    debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n");
-	}
-	assert(dns_error_message != NULL);
-	commConnectCallback(cs, COMM_ERR_DNS);
-	return;
-    }
-    assert(ia->cur < ia->count);
-    cs->in_addr = ia->in_addrs[ia->cur];
-    ipcacheCycleAddr(cs->host, NULL);
-    cs->addrcount = ia->count;
-    cs->connstart = squid_curtime;
-    commConnectHandle(cs->fd, cs);
-}
-
-static void
-commConnectCallback(ConnectStateData * cs, comm_err_t status)
-{
-    CNCB *callback = cs->callback;
-    void *cbdata = cs->data;
-    int fd = cs->fd;
-    comm_remove_close_handler(fd, commConnectFree, cs);
-    cs->callback = NULL;
-    cs->data = NULL;
-    commSetTimeout(fd, -1, NULL, NULL);
-    commConnectFree(fd, cs);
-    if (cbdataReferenceValid(cbdata))
-	callback(fd, status, cbdata);
-}
-
-static void
-commConnectFree(int fd, void *data)
-{
-    ConnectStateData *cs = data;
-    debug(5, 3) ("commConnectFree: FD %d\n", fd);
-    cbdataReferenceDone(cs->data);
-    safe_free(cs->host);
-    cbdataFree(cs);
-}
-
-/* Reset FD so that we can connect() again */
-static int
-commResetFD(ConnectStateData * cs)
-{
-    int fd2;
-    fde *F;
-    if (!cbdataReferenceValid(cs->data))
-	return 0;
-    statCounter.syscalls.sock.sockets++;
-    fd2 = socket(AF_INET, SOCK_STREAM, 0);
-    statCounter.syscalls.sock.sockets++;
-    if (fd2 < 0) {
-	debug(5, 0) ("commResetFD: socket: %s\n", xstrerror());
-	if (ENFILE == errno || EMFILE == errno)
-	    fdAdjustReserved();
-	return 0;
-    }
-    if (dup2(fd2, cs->fd) < 0) {
-	debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror());
-	if (ENFILE == errno || EMFILE == errno)
-	    fdAdjustReserved();
-	close(fd2);
-	return 0;
-    }
-    close(fd2);
-    F = &fd_table[cs->fd];
-    fd_table[cs->fd].flags.called_connect = 0;
-    /*
-     * yuck, this has assumptions about comm_open() arguments for
-     * the original socket
-     */
-    if (commBind(cs->fd, F->local_addr, F->local_port) != COMM_OK) {
-	debug(5, 0) ("commResetFD: bind: %s\n", xstrerror());
-	return 0;
-    }
-#ifdef IP_TOS
-    if (F->tos) {
-	int tos = F->tos;
-	if (setsockopt(cs->fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
-	        debug(50, 1) ("commResetFD: setsockopt(IP_TOS) on FD %d: %s\n", cs->fd, xstrerror());
-    }
-#endif
-    if (F->flags.close_on_exec)
-	commSetCloseOnExec(cs->fd);
-    if (F->flags.nonblocking)
-	commSetNonBlocking(cs->fd);
-#ifdef TCP_NODELAY
-    if (F->flags.nodelay)
-	commSetTcpNoDelay(cs->fd);
-#endif
-    if (Config.tcpRcvBufsz > 0)
-	commSetTcpRcvbuf(cs->fd, Config.tcpRcvBufsz);
-    return 1;
-}
-
-static int
-commRetryConnect(ConnectStateData * cs)
-{
-    assert(cs->addrcount > 0);
-    if (cs->addrcount == 1) {
-	if (cs->tries >= Config.retry.maxtries)
-	    return 0;
-	if (squid_curtime - cs->connstart > Config.Timeout.connect)
-	    return 0;
-    } else {
-	if (cs->tries > cs->addrcount)
-	    return 0;
-    }
-    return commResetFD(cs);
-}
-
-/* Connect SOCK to specified DEST_PORT at DEST_HOST. */
-static void
-commConnectHandle(int fd, void *data)
-{
-    ConnectStateData *cs = data;
-    if (cs->S.sin_addr.s_addr == 0) {
-	cs->S.sin_family = AF_INET;
-	cs->S.sin_addr = cs->in_addr;
-	cs->S.sin_port = htons(cs->port);
-	if (Config.onoff.log_fqdn)
-	    fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS);
-    }
-    switch (comm_connect_addr(fd, &cs->S)) {
-    case COMM_INPROGRESS:
-	debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd);
-	commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0);
-	break;
-    case COMM_OK:
-	ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr);
-	commConnectCallback(cs, COMM_OK);
-	break;
-    default:
-	cs->tries++;
-	ipcacheMarkBadAddr(cs->host, cs->S.sin_addr);
-	if (Config.onoff.test_reachability)
-	    netdbDeleteAddrNetwork(cs->S.sin_addr);
-	if (commRetryConnect(cs)) {
-	    cs->locks++;
-	    ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
-	} else {
-	    commConnectCallback(cs, COMM_ERR_CONNECT);
-	}
-	break;
-    }
-}
-
-int
-commSetTimeout(int fd, int timeout, PF * handler, void *data)
-{
-    fde *F;
-    debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);
-    assert(fd >= 0);
-    assert(fd < Squid_MaxFD);
-    F = &fd_table[fd];
-    assert(F->flags.open);
-    if (timeout < 0) {
-	cbdataReferenceDone(F->timeout_data);
-	F->timeout_handler = NULL;
-	F->timeout = 0;
-    } else {
-	assert(handler || F->timeout_handler);
-	if (handler) {
-	    cbdataReferenceDone(F->timeout_data);
-	    F->timeout_handler = handler;
-	    F->timeout_data = cbdataReference(data);
-	}
-	F->timeout = squid_curtime + (time_t) timeout;
-    }
-    return F->timeout;
-}
-
-int
-comm_connect_addr(int sock, const struct sockaddr_in *address)
-{
-    comm_err_t status = COMM_OK;
-    fde *F = &fd_table[sock];
-    int x;
-    int err = 0;
-    socklen_t errlen;
-    assert(ntohs(address->sin_port) != 0);
-    PROF_start(comm_connect_addr);
-    /* Establish connection. */
-    errno = 0;
-    if (!F->flags.called_connect) {
-	F->flags.called_connect = 1;
-	statCounter.syscalls.sock.connects++;
-	x = connect(sock, (struct sockaddr *) address, sizeof(*address));
-	if (x < 0)
-	    debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror());
-    } else {
-#if defined(_SQUID_NEWSOS6_)
-	/* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */
-	connect(sock, (struct sockaddr *) address, sizeof(*address));
-	if (errno == EINVAL) {
-	    errlen = sizeof(err);
-	    x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
-	    if (x >= 0)
-		errno = x;
-	}
-#else
-	errlen = sizeof(err);
-	x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
-	if (x == 0)
-	    errno = err;
-#if defined(_SQUID_SOLARIS_)
-	/*
-	 * Solaris 2.4's socket emulation doesn't allow you
-	 * to determine the error from a failed non-blocking
-	 * connect and just returns EPIPE.  Create a fake
-	 * error message for connect.   -- fenner@parc.xerox.com
-	 */
-	if (x < 0 && errno == EPIPE)
-	    errno = ENOTCONN;
-#endif
-#endif
-    }
-    PROF_stop(comm_connect_addr);
-    if (errno == 0 || errno == EISCONN)
-	status = COMM_OK;
-    else if (ignoreErrno(errno))
-	status = COMM_INPROGRESS;
-    else
-	return COMM_ERROR;
-    xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16);
-    F->remote_port = ntohs(address->sin_port);
-    if (status == COMM_OK) {
-	debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n",
-	    sock, F->ipaddr, F->remote_port);
-    } else if (status == COMM_INPROGRESS) {
-	debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock);
-    }
-    return status;
-}
-
-/* Wait for an incoming connection on FD.  FD should be a socket returned
- * from comm_listen. */
-int
-comm_accept(int fd, struct sockaddr_in *pn, struct sockaddr_in *me)
-{
-    int sock;
-    struct sockaddr_in P;
-    struct sockaddr_in M;
-    socklen_t Slen;
-    fde *F = NULL;
-    Slen = sizeof(P);
-    statCounter.syscalls.sock.accepts++;
-    PROF_start(comm_accept);
-    if ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) {
-	PROF_stop(comm_accept);
-	if (ignoreErrno(errno)) {
-	    debug(50, 5) ("comm_accept: FD %d: %s\n", fd, xstrerror());
-	    return COMM_NOMESSAGE;
-	} else if (ENFILE == errno || EMFILE == errno) {
-	    debug(50, 3) ("comm_accept: FD %d: %s\n", fd, xstrerror());
-	    return COMM_ERROR;
-	} else {
-	    debug(50, 1) ("comm_accept: FD %d: %s\n", fd, xstrerror());
-	    return COMM_ERROR;
-	}
-    }
-    if (pn)
-	*pn = P;
-    Slen = sizeof(M);
-    memset(&M, '\0', Slen);
-    getsockname(sock, (struct sockaddr *) &M, &Slen);
-    if (me)
-	*me = M;
-    commSetCloseOnExec(sock);
-    /* fdstat update */
-    fd_open(sock, FD_SOCKET, "HTTP Request");
-    F = &fd_table[sock];
-    xstrncpy(F->ipaddr, inet_ntoa(P.sin_addr), 16);
-    F->remote_port = htons(P.sin_port);
-    F->local_port = htons(M.sin_port);
-    commSetNonBlocking(sock);
-    PROF_stop(comm_accept);
-    return sock;
-}
-
-void
-commCallCloseHandlers(int fd)
-{
-    fde *F = &fd_table[fd];
-    close_handler *ch;
-    debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd);
-    while ((ch = F->closeHandler) != NULL) {
-	F->closeHandler = ch->next;
-	debug(5, 5) ("commCallCloseHandlers: ch->handler=%p\n", ch->handler);
-	if (cbdataReferenceValid(ch->data))
-	    ch->handler(fd, ch->data);
-	cbdataReferenceDone(ch->data);
-	memPoolFree(conn_close_pool, ch);	/* AAA */
-    }
-}
-
-#if LINGERING_CLOSE
-static void
-commLingerClose(int fd, void *unused)
-{
-    LOCAL_ARRAY(char, buf, 1024);
-    int n;
-    n = FD_READ_METHOD(fd, buf, 1024);
-    if (n < 0)
-	debug(5, 3) ("commLingerClose: FD %d read: %s\n", fd, xstrerror());
-    comm_close(fd);
-}
-
-static void
-commLingerTimeout(int fd, void *unused)
-{
-    debug(5, 3) ("commLingerTimeout: FD %d\n", fd);
-    comm_close(fd);
-}
-
-/*
- * Inspired by apache
- */
-void
-comm_lingering_close(int fd)
-{
-#if USE_SSL
-    if (fd_table[fd].ssl)
-	ssl_shutdown_method(fd);
-#endif
-    if (shutdown(fd, 1) < 0) {
-	comm_close(fd);
-	return;
-    }
-    fd_note(fd, "lingering close");
-    commSetTimeout(fd, 10, commLingerTimeout, NULL);
-    commSetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
-}
-#endif
-
-/*
- * enable linger with time of 0 so that when the socket is
- * closed, TCP generates a RESET
- */
-void
-comm_reset_close(int fd)
-{
-    struct linger L;
-    L.l_onoff = 1;
-    L.l_linger = 0;
-    if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
-	debug(50, 0) ("commResetTCPClose: FD %d: %s\n", fd, xstrerror());
-    comm_close(fd);
-}
-
-void
-comm_close(int fd)
-{
-    fde *F = NULL;
-
-    debug(5, 5) ("comm_close: FD %d\n", fd);
-    assert(fd >= 0);
-    assert(fd < Squid_MaxFD);
-    F = &fd_table[fd];
-
-    if (F->flags.closing)
-	return;
-    if (shutting_down && (!F->flags.open || F->type == FD_FILE))
-	return;
-    assert(F->flags.open);
-    assert(F->type != FD_FILE);
-    PROF_start(comm_close);
-    F->flags.closing = 1;
-#if USE_SSL
-    if (F->ssl)
-	ssl_shutdown_method(fd);
-#endif
-    commSetTimeout(fd, -1, NULL, NULL);
-    CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING);
-    commCallCloseHandlers(fd);
-    if (F->uses)		/* assume persistent connect count */
-	pconnHistCount(1, F->uses);
-#if USE_SSL
-    if (F->ssl) {
-	SSL_free(F->ssl);
-	F->ssl = NULL;
-    }
-#endif
-    fd_close(fd);		/* update fdstat */
-    close(fd);
-    statCounter.syscalls.sock.closes++;
-    PROF_stop(comm_close);
-}
-
-/* Send a udp datagram to specified TO_ADDR. */
-int
-comm_udp_sendto(int fd,
-    const struct sockaddr_in *to_addr,
-    int addr_len,
-    const void *buf,
-    int len)
-{
-    int x;
-    PROF_start(comm_udp_sendto);
-    statCounter.syscalls.sock.sendtos++;
-    x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len);
-    PROF_stop(comm_udp_sendto);
-    if (x < 0) {
-#ifdef _SQUID_LINUX_
-	if (ECONNREFUSED != errno)
-#endif
-	    debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n",
-		fd,
-		inet_ntoa(to_addr->sin_addr),
-		(int) htons(to_addr->sin_port),
-		xstrerror());
-	return COMM_ERROR;
-    }
-    return x;
-}
-
-void
-commSetDefer(int fd, DEFER * func, void *data)
-{
-    fde *F = &fd_table[fd];
-    F->defer_check = func;
-    F->defer_data = data;
-}
-
-void
-comm_add_close_handler(int fd, PF * handler, void *data)
-{
-    close_handler *new = memPoolAlloc(conn_close_pool);		/* AAA */
-    close_handler *c;
-    debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n",
-	fd, handler, data);
-    for (c = fd_table[fd].closeHandler; c; c = c->next)
-	assert(c->handler != handler || c->data != data);
-    new->handler = handler;
-    new->data = cbdataReference(data);
-    new->next = fd_table[fd].closeHandler;
-    fd_table[fd].closeHandler = new;
-}
-
-void
-comm_remove_close_handler(int fd, PF * handler, void *data)
-{
-    close_handler *p;
-    close_handler *last = NULL;
-    /* Find handler in list */
-    debug(5, 5) ("comm_remove_close_handler: FD %d, handler=%p, data=%p\n",
-	fd, handler, data);
-    for (p = fd_table[fd].closeHandler; p != NULL; last = p, p = p->next)
-	if (p->handler == handler && p->data == data)
-	    break;		/* This is our handler */
-    assert(p != NULL);
-    /* Remove list entry */
-    if (last)
-	last->next = p->next;
-    else
-	fd_table[fd].closeHandler = p->next;
-    cbdataReferenceDone(p->data);
-    memPoolFree(conn_close_pool, p);
-}
-
-static void
-commSetNoLinger(int fd)
-{
-    struct linger L;
-    L.l_onoff = 0;		/* off */
-    L.l_linger = 0;
-    if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
-	debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror());
-    fd_table[fd].flags.nolinger = 1;
-}
-
-static void
-commSetReuseAddr(int fd)
-{
-    int on = 1;
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
-	debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror());
-}
-
-static void
-commSetTcpRcvbuf(int fd, int size)
-{
-    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
-	debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n",
-	    fd, size, xstrerror());
-}
-
-int
-commSetNonBlocking(int fd)
-{
-    int flags;
-    int dummy = 0;
-#ifdef _SQUID_CYGWIN_
-    int nonblocking = TRUE;
-    if (fd_table[fd].type != FD_PIPE) {
-	if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
-	    debug(50, 0) ("commSetNonBlocking: FD %d: %s %u\n", fd, xstrerror(), fd_table[fd].type);
-	    return COMM_ERROR;
-	}
-    } else {
-#endif
-	if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
-	    debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
-	    return COMM_ERROR;
-	}
-	if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
-	    debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror());
-	    return COMM_ERROR;
-	}
-#ifdef _SQUID_CYGWIN_
-    }
-#endif
-    fd_table[fd].flags.nonblocking = 1;
-    return 0;
-}
-
-int
-commUnsetNonBlocking(int fd)
-{
-    int flags;
-    int dummy = 0;
-    if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
-	debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
-	return COMM_ERROR;
-    }
-    if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
-	debug(50, 0) ("commUnsetNonBlocking: FD %d: %s\n", fd, xstrerror());
-	return COMM_ERROR;
-    }
-    fd_table[fd].flags.nonblocking = 0;
-    return 0;
-}
-
-void
-commSetCloseOnExec(int fd)
-{
-#ifdef FD_CLOEXEC
-    int flags;
-    int dummy = 0;
-    if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
-	debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
-	return;
-    }
-    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
-	debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror());
-    fd_table[fd].flags.close_on_exec = 1;
-#endif
-}
-
-#ifdef TCP_NODELAY
-static void
-commSetTcpNoDelay(int fd)
-{
-    int on = 1;
-    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
-	debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror());
-    fd_table[fd].flags.nodelay = 1;
-}
-#endif
-
-
-void
-comm_init(void)
-{
-    fd_table = xcalloc(Squid_MaxFD, sizeof(fde));
-    /* XXX account fd_table */
-    /* Keep a few file descriptors free so that we don't run out of FD's
-     * after accepting a client but before it opens a socket or a file.
-     * Since Squid_MaxFD can be as high as several thousand, don't waste them */
-    RESERVED_FD = XMIN(100, Squid_MaxFD / 4);
-    CBDATA_INIT_TYPE(ConnectStateData);
-    comm_write_pool = memPoolCreate("CommWriteStateData", sizeof(CommWriteStateData));
-    conn_close_pool = memPoolCreate("close_handler", sizeof(close_handler));
-}
-
-/* Write to FD. */
-static void
-commHandleWrite(int fd, void *data)
-{
-    CommWriteStateData *state = data;
-    int len = 0;
-    int nleft;
-
-    PROF_start(commHandleWrite);
-    debug(5, 5) ("commHandleWrite: FD %d: off %ld, sz %ld.\n",
-	fd, (long int) state->offset, (long int) state->size);
-
-    nleft = state->size - state->offset;
-    len = FD_WRITE_METHOD(fd, state->buf + state->offset, nleft);
-    debug(5, 5) ("commHandleWrite: write() returns %d\n", len);
-    fd_bytes(fd, len, FD_WRITE);
-    statCounter.syscalls.sock.writes++;
-
-    if (len == 0) {
-	/* Note we even call write if nleft == 0 */
-	/* We're done */
-	if (nleft != 0)
-	    debug(5, 1) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft);
-	CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK);
-    } else if (len < 0) {
-	/* An error */
-	if (fd_table[fd].flags.socket_eof) {
-	    debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
-		fd, xstrerror());
-	    CommWriteStateCallbackAndFree(fd, COMM_ERROR);
-	} else if (ignoreErrno(errno)) {
-	    debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n",
-		fd, xstrerror());
-	    commSetSelect(fd,
-		COMM_SELECT_WRITE,
-		commHandleWrite,
-		state,
-		0);
-	} else {
-	    debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
-		fd, xstrerror());
-	    CommWriteStateCallbackAndFree(fd, COMM_ERROR);
-	}
-    } else {
-	/* A successful write, continue */
-	state->offset += len;
-	if (state->offset < state->size) {
-	    /* Not done, reinstall the write handler and write some more */
-	    commSetSelect(fd,
-		COMM_SELECT_WRITE,
-		commHandleWrite,
-		state,
-		0);
-	} else {
-	    CommWriteStateCallbackAndFree(fd, COMM_OK);
-	}
-    }
-    PROF_stop(commHandleWrite);
-}
-
-
-
-/*
- * Queue a write. handler/handler_data are called when the write
- * completes, on error, or on file descriptor close.
- *
- * free_func is used to free the passed buffer when the write has completed.
- */
-void
-comm_write(int fd, const char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func)
-{
-    CommWriteStateData *state = fd_table[fd].rwstate;
-    debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n",
-	fd, size, handler, handler_data);
-    if (NULL != state) {
-	debug(5, 1) ("comm_write: fd_table[%d].rwstate != NULL\n", fd);
-	memPoolFree(comm_write_pool, state);
-	fd_table[fd].rwstate = NULL;
-    }
-    fd_table[fd].rwstate = state = memPoolAlloc(comm_write_pool);
-    state->buf = (char *) buf;
-    state->size = size;
-    state->offset = 0;
-    state->handler = handler;
-    state->handler_data = cbdataReference(handler_data);
-    state->free_func = free_func;
-    commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, state, 0);
-}
-
-/* a wrapper around comm_write to allow for MemBuf to be comm_written in a snap */
-void
-comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data)
-{
-    comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
-}
-
-/*
- * hm, this might be too general-purpose for all the places we'd
- * like to use it.
- */
-int
-ignoreErrno(int ierrno)
-{
-    switch (ierrno) {
-    case EINPROGRESS:
-    case EWOULDBLOCK:
-#if EAGAIN != EWOULDBLOCK
-    case EAGAIN:
-#endif
-    case EALREADY:
-    case EINTR:
-#ifdef ERESTART
-    case ERESTART:
-#endif
-	return 1;
-    default:
-	return 0;
-    }
-    /* NOTREACHED */
-}
-
-void
-commCloseAllSockets(void)
-{
-    int fd;
-    fde *F = NULL;
-    for (fd = 0; fd <= Biggest_FD; fd++) {
-	F = &fd_table[fd];
-	if (!F->flags.open)
-	    continue;
-	if (F->type != FD_SOCKET)
-	    continue;
-	if (F->flags.ipc)	/* don't close inter-process sockets */
-	    continue;
-	if (F->timeout_handler) {
-	    PF *callback = F->timeout_handler;
-	    void *cbdata = NULL;
-	    F->timeout_handler = NULL;
-	    debug(5, 5) ("commCloseAllSockets: FD %d: Calling timeout handler\n",
-		fd);
-	    if (cbdataReferenceValidDone(F->timeout_data, &cbdata))
-		callback(fd, cbdata);
-	} else {
-	    debug(5, 5) ("commCloseAllSockets: FD %d: calling comm_close()\n", fd);
-	    comm_close(fd);
-	}
-    }
-}
-
-void
-checkTimeouts(void)
-{
-    int fd;
-    fde *F = NULL;
-    PF *callback;
-    for (fd = 0; fd <= Biggest_FD; fd++) {
-	F = &fd_table[fd];
-	if (!F->flags.open)
-	    continue;
-	if (F->timeout == 0)
-	    continue;
-	if (F->timeout > squid_curtime)
-	    continue;
-	debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd);
-	if (F->timeout_handler) {
-	    debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd);
-	    callback = F->timeout_handler;
-	    F->timeout_handler = NULL;
-	    callback(fd, F->timeout_data);
-	} else {
-	    debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd);
-	    comm_close(fd);
-	}
-    }
-}
-
-
-int
-commDeferRead(int fd)
-{
-    fde *F = &fd_table[fd];
-    if (F->defer_check == NULL)
-	return 0;
-    return F->defer_check(fd, F->defer_data);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/comm.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,1050 @@
+
+/*
+ * $Id: comm.cc,v 1.1.2.1 2002/10/11 15:40:44 rbcollins Exp $
+ *
+ * DEBUG: section 5     Socket Functions
+ * AUTHOR: Harvest Derived
+ *
+ * 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"
+
+#if defined(_SQUID_CYGWIN_)
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+typedef struct {
+    char *host;
+    u_short port;
+    struct sockaddr_in S;
+    CNCB *callback;
+    void *data;
+    struct in_addr in_addr;
+    int locks;
+    int fd;
+    int tries;
+    int addrcount;
+    int connstart;
+} ConnectStateData;
+
+/* STATIC */
+static comm_err_t commBind(int s, struct in_addr, u_short port);
+static void commSetReuseAddr(int);
+static void commSetNoLinger(int);
+static void CommWriteStateCallbackAndFree(int fd, comm_err_t code);
+#ifdef TCP_NODELAY
+static void commSetTcpNoDelay(int);
+#endif
+static void commSetTcpRcvbuf(int, int);
+static PF commConnectFree;
+static PF commConnectHandle;
+static PF commHandleWrite;
+static IPH commConnectDnsHandle;
+static void commConnectCallback(ConnectStateData * cs, comm_err_t status);
+static int commResetFD(ConnectStateData * cs);
+static int commRetryConnect(ConnectStateData * cs);
+CBDATA_TYPE(ConnectStateData);
+
+static MemPool *comm_write_pool = NULL;
+static MemPool *conn_close_pool = NULL;
+
+static void
+CommWriteStateCallbackAndFree(int fd, comm_err_t code)
+{
+    CommWriteStateData *CommWriteState = fd_table[fd].rwstate;
+    CWCB *callback = NULL;
+    void *cbdata;
+    fd_table[fd].rwstate = NULL;
+    if (CommWriteState == NULL)
+	return;
+    if (CommWriteState->free_func) {
+	FREE *free_func = CommWriteState->free_func;
+	void *free_buf = CommWriteState->buf;
+	CommWriteState->free_func = NULL;
+	CommWriteState->buf = NULL;
+	free_func(free_buf);
+    }
+    callback = CommWriteState->handler;
+    CommWriteState->handler = NULL;
+    if (callback && cbdataReferenceValidDone(CommWriteState->handler_data, &cbdata))
+	callback(fd, CommWriteState->buf, CommWriteState->offset, code, cbdata);
+    memPoolFree(comm_write_pool, CommWriteState);
+}
+
+/* Return the local port associated with fd. */
+u_short
+comm_local_port(int fd)
+{
+    struct sockaddr_in addr;
+    socklen_t addr_len = 0;
+    fde *F = &fd_table[fd];
+
+    /* If the fd is closed already, just return */
+    if (!F->flags.open) {
+	debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd);
+	return 0;
+    }
+    if (F->local_port)
+	return F->local_port;
+    addr_len = sizeof(addr);
+    if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
+	debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());
+	return 0;
+    }
+    F->local_port = ntohs(addr.sin_port);
+    debug(5, 6) ("comm_local_port: FD %d: port %d\n", fd, (int) F->local_port);
+    return F->local_port;
+}
+
+static comm_err_t
+commBind(int s, struct in_addr in_addr, u_short port)
+{
+    struct sockaddr_in S;
+
+    memset(&S, '\0', sizeof(S));
+    S.sin_family = AF_INET;
+    S.sin_port = htons(port);
+    S.sin_addr = in_addr;
+    statCounter.syscalls.sock.binds++;
+    if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)
+	return COMM_OK;
+    debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n",
+	s,
+	S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),
+	(int) port,
+	xstrerror());
+    return COMM_ERROR;
+}
+
+/* Create a socket. Default is blocking, stream (TCP) socket.  IO_TYPE
+ * is OR of flags specified in comm.h. Defaults TOS */
+int
+comm_open(int sock_type,
+    int proto,
+    struct in_addr addr,
+    u_short port,
+    int flags,
+    const char *note)
+{
+    return comm_openex(sock_type, proto, addr, port, flags, 0, note);
+}
+
+
+/* Create a socket. Default is blocking, stream (TCP) socket.  IO_TYPE
+ * is OR of flags specified in defines.h:COMM_* */
+int
+comm_openex(int sock_type,
+    int proto,
+    struct in_addr addr,
+    u_short port,
+    int flags,
+    unsigned char TOS,
+    const char *note)
+{
+    int new_socket;
+    int tos = 0;
+    fde *F = NULL;
+
+    PROF_start(comm_open);
+    /* Create socket for accepting new connections. */
+    statCounter.syscalls.sock.sockets++;
+    if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {
+	/* Increase the number of reserved fd's if calls to socket()
+	 * are failing because the open file table is full.  This
+	 * limits the number of simultaneous clients */
+	switch (errno) {
+	case ENFILE:
+	case EMFILE:
+	    debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror());
+	    fdAdjustReserved();
+	    break;
+	default:
+	    debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror());
+	}
+	PROF_stop(comm_open);
+	return -1;
+    }
+    /* set TOS if needed */
+    if (TOS) {
+#ifdef IP_TOS
+	tos = TOS;
+	if (setsockopt(new_socket, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
+	        debug(50, 1) ("comm_open: setsockopt(IP_TOS) on FD %d: %s\n",
+		new_socket, xstrerror());
+#else
+	debug(50, 0) ("comm_open: setsockopt(IP_TOS) not supported on this platform\n");
+#endif
+    }
+    /* update fdstat */
+    debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket);
+    fd_open(new_socket, FD_SOCKET, note);
+    F = &fd_table[new_socket];
+    F->local_addr = addr;
+    F->tos = tos;
+    if (!(flags & COMM_NOCLOEXEC))
+	commSetCloseOnExec(new_socket);
+    if ((flags & COMM_REUSEADDR))
+	commSetReuseAddr(new_socket);
+    if (port > (u_short) 0) {
+	commSetNoLinger(new_socket);
+	if (opt_reuseaddr)
+	    commSetReuseAddr(new_socket);
+    }
+    if (addr.s_addr != no_addr.s_addr) {
+	if (commBind(new_socket, addr, port) != COMM_OK) {
+	    comm_close(new_socket);
+	    return -1;
+	    PROF_stop(comm_open);
+	}
+    }
+    F->local_port = port;
+
+    if (flags & COMM_NONBLOCKING)
+	if (commSetNonBlocking(new_socket) == COMM_ERROR) {
+	    return -1;
+	    PROF_stop(comm_open);
+	}
+#ifdef TCP_NODELAY
+    if (sock_type == SOCK_STREAM)
+	commSetTcpNoDelay(new_socket);
+#endif
+    if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
+	commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
+    PROF_stop(comm_open);
+    return new_socket;
+}
+
+/*
+ * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to      
+ * impose an upper limit.  Solaris' listen(3n) page says it has   
+ * no limit on this parameter, but sys/socket.h sets SOMAXCONN 
+ * to 5.  HP-UX currently has a limit of 20.  SunOS is 5 and
+ * OSF 3.0 is 8.
+ */
+int
+comm_listen(int sock)
+{
+    int x;
+    if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {
+	debug(50, 0) ("comm_listen: listen(%d, %d): %s\n",
+	    Squid_MaxFD >> 2,
+	    sock, xstrerror());
+	return x;
+    }
+    return sock;
+}
+
+void
+commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
+{
+    ConnectStateData *cs;
+    debug(5, 3) ("commConnectStart: FD %d, %s:%d\n", fd, host, (int) port);
+    cs = cbdataAlloc(ConnectStateData);
+    cs->fd = fd;
+    cs->host = xstrdup(host);
+    cs->port = port;
+    cs->callback = callback;
+    cs->data = cbdataReference(data);
+    comm_add_close_handler(fd, commConnectFree, cs);
+    cs->locks++;
+    ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
+}
+
+static void
+commConnectDnsHandle(const ipcache_addrs * ia, void *data)
+{
+    ConnectStateData *cs = (ConnectStateData *)data;
+    assert(cs->locks == 1);
+    cs->locks--;
+    if (ia == NULL) {
+	debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host);
+	if (!dns_error_message) {
+	    dns_error_message = "Unknown DNS error";
+	    debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n");
+	}
+	assert(dns_error_message != NULL);
+	commConnectCallback(cs, COMM_ERR_DNS);
+	return;
+    }
+    assert(ia->cur < ia->count);
+    cs->in_addr = ia->in_addrs[ia->cur];
+    ipcacheCycleAddr(cs->host, NULL);
+    cs->addrcount = ia->count;
+    cs->connstart = squid_curtime;
+    commConnectHandle(cs->fd, cs);
+}
+
+static void
+commConnectCallback(ConnectStateData * cs, comm_err_t status)
+{
+    CNCB *callback = cs->callback;
+    void *cbdata = cs->data;
+    int fd = cs->fd;
+    comm_remove_close_handler(fd, commConnectFree, cs);
+    cs->callback = NULL;
+    cs->data = NULL;
+    commSetTimeout(fd, -1, NULL, NULL);
+    commConnectFree(fd, cs);
+    if (cbdataReferenceValid(cbdata))
+	callback(fd, status, cbdata);
+}
+
+static void
+commConnectFree(int fd, void *data)
+{
+    ConnectStateData *cs = (ConnectStateData *)data;
+    debug(5, 3) ("commConnectFree: FD %d\n", fd);
+    cbdataReferenceDone(cs->data);
+    safe_free(cs->host);
+    cbdataFree(cs);
+}
+
+/* Reset FD so that we can connect() again */
+static int
+commResetFD(ConnectStateData * cs)
+{
+    int fd2;
+    fde *F;
+    if (!cbdataReferenceValid(cs->data))
+	return 0;
+    statCounter.syscalls.sock.sockets++;
+    fd2 = socket(AF_INET, SOCK_STREAM, 0);
+    statCounter.syscalls.sock.sockets++;
+    if (fd2 < 0) {
+	debug(5, 0) ("commResetFD: socket: %s\n", xstrerror());
+	if (ENFILE == errno || EMFILE == errno)
+	    fdAdjustReserved();
+	return 0;
+    }
+    if (dup2(fd2, cs->fd) < 0) {
+	debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror());
+	if (ENFILE == errno || EMFILE == errno)
+	    fdAdjustReserved();
+	close(fd2);
+	return 0;
+    }
+    close(fd2);
+    F = &fd_table[cs->fd];
+    fd_table[cs->fd].flags.called_connect = 0;
+    /*
+     * yuck, this has assumptions about comm_open() arguments for
+     * the original socket
+     */
+    if (commBind(cs->fd, F->local_addr, F->local_port) != COMM_OK) {
+	debug(5, 0) ("commResetFD: bind: %s\n", xstrerror());
+	return 0;
+    }
+#ifdef IP_TOS
+    if (F->tos) {
+	int tos = F->tos;
+	if (setsockopt(cs->fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
+	        debug(50, 1) ("commResetFD: setsockopt(IP_TOS) on FD %d: %s\n", cs->fd, xstrerror());
+    }
+#endif
+    if (F->flags.close_on_exec)
+	commSetCloseOnExec(cs->fd);
+    if (F->flags.nonblocking)
+	commSetNonBlocking(cs->fd);
+#ifdef TCP_NODELAY
+    if (F->flags.nodelay)
+	commSetTcpNoDelay(cs->fd);
+#endif
+    if (Config.tcpRcvBufsz > 0)
+	commSetTcpRcvbuf(cs->fd, Config.tcpRcvBufsz);
+    return 1;
+}
+
+static int
+commRetryConnect(ConnectStateData * cs)
+{
+    assert(cs->addrcount > 0);
+    if (cs->addrcount == 1) {
+	if (cs->tries >= Config.retry.maxtries)
+	    return 0;
+	if (squid_curtime - cs->connstart > Config.Timeout.connect)
+	    return 0;
+    } else {
+	if (cs->tries > cs->addrcount)
+	    return 0;
+    }
+    return commResetFD(cs);
+}
+
+/* Connect SOCK to specified DEST_PORT at DEST_HOST. */
+static void
+commConnectHandle(int fd, void *data)
+{
+    ConnectStateData *cs = (ConnectStateData *)data;
+    if (cs->S.sin_addr.s_addr == 0) {
+	cs->S.sin_family = AF_INET;
+	cs->S.sin_addr = cs->in_addr;
+	cs->S.sin_port = htons(cs->port);
+	if (Config.onoff.log_fqdn)
+	    fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS);
+    }
+    switch (comm_connect_addr(fd, &cs->S)) {
+    case COMM_INPROGRESS:
+	debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd);
+	commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0);
+	break;
+    case COMM_OK:
+	ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr);
+	commConnectCallback(cs, COMM_OK);
+	break;
+    default:
+	cs->tries++;
+	ipcacheMarkBadAddr(cs->host, cs->S.sin_addr);
+	if (Config.onoff.test_reachability)
+	    netdbDeleteAddrNetwork(cs->S.sin_addr);
+	if (commRetryConnect(cs)) {
+	    cs->locks++;
+	    ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
+	} else {
+	    commConnectCallback(cs, COMM_ERR_CONNECT);
+	}
+	break;
+    }
+}
+
+int
+commSetTimeout(int fd, int timeout, PF * handler, void *data)
+{
+    fde *F;
+    debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);
+    assert(fd >= 0);
+    assert(fd < Squid_MaxFD);
+    F = &fd_table[fd];
+    assert(F->flags.open);
+    if (timeout < 0) {
+	cbdataReferenceDone(F->timeout_data);
+	F->timeout_handler = NULL;
+	F->timeout = 0;
+    } else {
+	assert(handler || F->timeout_handler);
+	if (handler) {
+	    cbdataReferenceDone(F->timeout_data);
+	    F->timeout_handler = handler;
+	    F->timeout_data = cbdataReference(data);
+	}
+	F->timeout = squid_curtime + (time_t) timeout;
+    }
+    return F->timeout;
+}
+
+int
+comm_connect_addr(int sock, const struct sockaddr_in *address)
+{
+    comm_err_t status = COMM_OK;
+    fde *F = &fd_table[sock];
+    int x;
+    int err = 0;
+    socklen_t errlen;
+    assert(ntohs(address->sin_port) != 0);
+    PROF_start(comm_connect_addr);
+    /* Establish connection. */
+    errno = 0;
+    if (!F->flags.called_connect) {
+	F->flags.called_connect = 1;
+	statCounter.syscalls.sock.connects++;
+	x = connect(sock, (struct sockaddr *) address, sizeof(*address));
+	if (x < 0)
+	    debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror());
+    } else {
+#if defined(_SQUID_NEWSOS6_)
+	/* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */
+	connect(sock, (struct sockaddr *) address, sizeof(*address));
+	if (errno == EINVAL) {
+	    errlen = sizeof(err);
+	    x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
+	    if (x >= 0)
+		errno = x;
+	}
+#else
+	errlen = sizeof(err);
+	x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
+	if (x == 0)
+	    errno = err;
+#if defined(_SQUID_SOLARIS_)
+	/*
+	 * Solaris 2.4's socket emulation doesn't allow you
+	 * to determine the error from a failed non-blocking
+	 * connect and just returns EPIPE.  Create a fake
+	 * error message for connect.   -- fenner@parc.xerox.com
+	 */
+	if (x < 0 && errno == EPIPE)
+	    errno = ENOTCONN;
+#endif
+#endif
+    }
+    PROF_stop(comm_connect_addr);
+    if (errno == 0 || errno == EISCONN)
+	status = COMM_OK;
+    else if (ignoreErrno(errno))
+	status = COMM_INPROGRESS;
+    else
+	return COMM_ERROR;
+    xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16);
+    F->remote_port = ntohs(address->sin_port);
+    if (status == COMM_OK) {
+	debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n",
+	    sock, F->ipaddr, F->remote_port);
+    } else if (status == COMM_INPROGRESS) {
+	debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock);
+    }
+    return status;
+}
+
+/* Wait for an incoming connection on FD.  FD should be a socket returned
+ * from comm_listen. */
+int
+comm_accept(int fd, struct sockaddr_in *pn, struct sockaddr_in *me)
+{
+    int sock;
+    struct sockaddr_in P;
+    struct sockaddr_in M;
+    socklen_t Slen;
+    fde *F = NULL;
+    Slen = sizeof(P);
+    statCounter.syscalls.sock.accepts++;
+    PROF_start(comm_accept);
+    if ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) {
+	PROF_stop(comm_accept);
+	if (ignoreErrno(errno)) {
+	    debug(50, 5) ("comm_accept: FD %d: %s\n", fd, xstrerror());
+	    return COMM_NOMESSAGE;
+	} else if (ENFILE == errno || EMFILE == errno) {
+	    debug(50, 3) ("comm_accept: FD %d: %s\n", fd, xstrerror());
+	    return COMM_ERROR;
+	} else {
+	    debug(50, 1) ("comm_accept: FD %d: %s\n", fd, xstrerror());
+	    return COMM_ERROR;
+	}
+    }
+    if (pn)
+	*pn = P;
+    Slen = sizeof(M);
+    memset(&M, '\0', Slen);
+    getsockname(sock, (struct sockaddr *) &M, &Slen);
+    if (me)
+	*me = M;
+    commSetCloseOnExec(sock);
+    /* fdstat update */
+    fd_open(sock, FD_SOCKET, "HTTP Request");
+    F = &fd_table[sock];
+    xstrncpy(F->ipaddr, inet_ntoa(P.sin_addr), 16);
+    F->remote_port = htons(P.sin_port);
+    F->local_port = htons(M.sin_port);
+    commSetNonBlocking(sock);
+    PROF_stop(comm_accept);
+    return sock;
+}
+
+void
+commCallCloseHandlers(int fd)
+{
+    fde *F = &fd_table[fd];
+    close_handler *ch;
+    debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd);
+    while ((ch = F->closeHandler) != NULL) {
+	F->closeHandler = ch->next;
+	debug(5, 5) ("commCallCloseHandlers: ch->handler=%p\n", ch->handler);
+	if (cbdataReferenceValid(ch->data))
+	    ch->handler(fd, ch->data);
+	cbdataReferenceDone(ch->data);
+	memPoolFree(conn_close_pool, ch);	/* AAA */
+    }
+}
+
+#if LINGERING_CLOSE
+static void
+commLingerClose(int fd, void *unused)
+{
+    LOCAL_ARRAY(char, buf, 1024);
+    int n;
+    n = FD_READ_METHOD(fd, buf, 1024);
+    if (n < 0)
+	debug(5, 3) ("commLingerClose: FD %d read: %s\n", fd, xstrerror());
+    comm_close(fd);
+}
+
+static void
+commLingerTimeout(int fd, void *unused)
+{
+    debug(5, 3) ("commLingerTimeout: FD %d\n", fd);
+    comm_close(fd);
+}
+
+/*
+ * Inspired by apache
+ */
+void
+comm_lingering_close(int fd)
+{
+#if USE_SSL
+    if (fd_table[fd].ssl)
+	ssl_shutdown_method(fd);
+#endif
+    if (shutdown(fd, 1) < 0) {
+	comm_close(fd);
+	return;
+    }
+    fd_note(fd, "lingering close");
+    commSetTimeout(fd, 10, commLingerTimeout, NULL);
+    commSetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
+}
+#endif
+
+/*
+ * enable linger with time of 0 so that when the socket is
+ * closed, TCP generates a RESET
+ */
+void
+comm_reset_close(int fd)
+{
+    struct linger L;
+    L.l_onoff = 1;
+    L.l_linger = 0;
+    if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
+	debug(50, 0) ("commResetTCPClose: FD %d: %s\n", fd, xstrerror());
+    comm_close(fd);
+}
+
+void
+comm_close(int fd)
+{
+    fde *F = NULL;
+
+    debug(5, 5) ("comm_close: FD %d\n", fd);
+    assert(fd >= 0);
+    assert(fd < Squid_MaxFD);
+    F = &fd_table[fd];
+
+    if (F->flags.closing)
+	return;
+    if (shutting_down && (!F->flags.open || F->type == FD_FILE))
+	return;
+    assert(F->flags.open);
+    assert(F->type != FD_FILE);
+    PROF_start(comm_close);
+    F->flags.closing = 1;
+#if USE_SSL
+    if (F->ssl)
+	ssl_shutdown_method(fd);
+#endif
+    commSetTimeout(fd, -1, NULL, NULL);
+    CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING);
+    commCallCloseHandlers(fd);
+    if (F->uses)		/* assume persistent connect count */
+	pconnHistCount(1, F->uses);
+#if USE_SSL
+    if (F->ssl) {
+	SSL_free(F->ssl);
+	F->ssl = NULL;
+    }
+#endif
+    fd_close(fd);		/* update fdstat */
+    close(fd);
+    statCounter.syscalls.sock.closes++;
+    PROF_stop(comm_close);
+}
+
+/* Send a udp datagram to specified TO_ADDR. */
+int
+comm_udp_sendto(int fd,
+    const struct sockaddr_in *to_addr,
+    int addr_len,
+    const void *buf,
+    int len)
+{
+    int x;
+    PROF_start(comm_udp_sendto);
+    statCounter.syscalls.sock.sendtos++;
+    x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len);
+    PROF_stop(comm_udp_sendto);
+    if (x < 0) {
+#ifdef _SQUID_LINUX_
+	if (ECONNREFUSED != errno)
+#endif
+	    debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n",
+		fd,
+		inet_ntoa(to_addr->sin_addr),
+		(int) htons(to_addr->sin_port),
+		xstrerror());
+	return COMM_ERROR;
+    }
+    return x;
+}
+
+void
+commSetDefer(int fd, DEFER * func, void *data)
+{
+    fde *F = &fd_table[fd];
+    F->defer_check = func;
+    F->defer_data = data;
+}
+
+void
+comm_add_close_handler(int fd, PF * handler, void *data)
+{
+    close_handler *newHandler = (close_handler *)memPoolAlloc(conn_close_pool);		/* AAA */
+    close_handler *c;
+    debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n",
+	fd, handler, data);
+    for (c = fd_table[fd].closeHandler; c; c = c->next)
+	assert(c->handler != handler || c->data != data);
+    newHandler->handler = handler;
+    newHandler->data = cbdataReference(data);
+    newHandler->next = fd_table[fd].closeHandler;
+    fd_table[fd].closeHandler = newHandler;
+}
+
+void
+comm_remove_close_handler(int fd, PF * handler, void *data)
+{
+    close_handler *p;
+    close_handler *last = NULL;
+    /* Find handler in list */
+    debug(5, 5) ("comm_remove_close_handler: FD %d, handler=%p, data=%p\n",
+	fd, handler, data);
+    for (p = fd_table[fd].closeHandler; p != NULL; last = p, p = p->next)
+	if (p->handler == handler && p->data == data)
+	    break;		/* This is our handler */
+    assert(p != NULL);
+    /* Remove list entry */
+    if (last)
+	last->next = p->next;
+    else
+	fd_table[fd].closeHandler = p->next;
+    cbdataReferenceDone(p->data);
+    memPoolFree(conn_close_pool, p);
+}
+
+static void
+commSetNoLinger(int fd)
+{
+    struct linger L;
+    L.l_onoff = 0;		/* off */
+    L.l_linger = 0;
+    if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
+	debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror());
+    fd_table[fd].flags.nolinger = 1;
+}
+
+static void
+commSetReuseAddr(int fd)
+{
+    int on = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
+	debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror());
+}
+
+static void
+commSetTcpRcvbuf(int fd, int size)
+{
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
+	debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n",
+	    fd, size, xstrerror());
+}
+
+int
+commSetNonBlocking(int fd)
+{
+    int flags;
+    int dummy = 0;
+#ifdef _SQUID_CYGWIN_
+    int nonblocking = TRUE;
+    if (fd_table[fd].type != FD_PIPE) {
+	if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
+	    debug(50, 0) ("commSetNonBlocking: FD %d: %s %u\n", fd, xstrerror(), fd_table[fd].type);
+	    return COMM_ERROR;
+	}
+    } else {
+#endif
+	if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
+	    debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
+	    return COMM_ERROR;
+	}
+	if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
+	    debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror());
+	    return COMM_ERROR;
+	}
+#ifdef _SQUID_CYGWIN_
+    }
+#endif
+    fd_table[fd].flags.nonblocking = 1;
+    return 0;
+}
+
+int
+commUnsetNonBlocking(int fd)
+{
+    int flags;
+    int dummy = 0;
+    if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
+	debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
+	return COMM_ERROR;
+    }
+    if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
+	debug(50, 0) ("commUnsetNonBlocking: FD %d: %s\n", fd, xstrerror());
+	return COMM_ERROR;
+    }
+    fd_table[fd].flags.nonblocking = 0;
+    return 0;
+}
+
+void
+commSetCloseOnExec(int fd)
+{
+#ifdef FD_CLOEXEC
+    int flags;
+    int dummy = 0;
+    if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
+	debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
+	return;
+    }
+    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
+	debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror());
+    fd_table[fd].flags.close_on_exec = 1;
+#endif
+}
+
+#ifdef TCP_NODELAY
+static void
+commSetTcpNoDelay(int fd)
+{
+    int on = 1;
+    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
+	debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror());
+    fd_table[fd].flags.nodelay = 1;
+}
+#endif
+
+
+void
+comm_init(void)
+{
+    fd_table = (fde *)xcalloc(Squid_MaxFD, sizeof(fde));
+    /* XXX account fd_table */
+    /* Keep a few file descriptors free so that we don't run out of FD's
+     * after accepting a client but before it opens a socket or a file.
+     * Since Squid_MaxFD can be as high as several thousand, don't waste them */
+    RESERVED_FD = XMIN(100, Squid_MaxFD / 4);
+    CBDATA_INIT_TYPE(ConnectStateData);
+    comm_write_pool = memPoolCreate("CommWriteStateData", sizeof(CommWriteStateData));
+    conn_close_pool = memPoolCreate("close_handler", sizeof(close_handler));
+}
+
+/* Write to FD. */
+static void
+commHandleWrite(int fd, void *data)
+{
+    CommWriteStateData *state = (CommWriteStateData *)data;
+    int len = 0;
+    int nleft;
+
+    PROF_start(commHandleWrite);
+    debug(5, 5) ("commHandleWrite: FD %d: off %ld, sz %ld.\n",
+	fd, (long int) state->offset, (long int) state->size);
+
+    nleft = state->size - state->offset;
+    len = FD_WRITE_METHOD(fd, state->buf + state->offset, nleft);
+    debug(5, 5) ("commHandleWrite: write() returns %d\n", len);
+    fd_bytes(fd, len, FD_WRITE);
+    statCounter.syscalls.sock.writes++;
+
+    if (len == 0) {
+	/* Note we even call write if nleft == 0 */
+	/* We're done */
+	if (nleft != 0)
+	    debug(5, 1) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft);
+	CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK);
+    } else if (len < 0) {
+	/* An error */
+	if (fd_table[fd].flags.socket_eof) {
+	    debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
+		fd, xstrerror());
+	    CommWriteStateCallbackAndFree(fd, COMM_ERROR);
+	} else if (ignoreErrno(errno)) {
+	    debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n",
+		fd, xstrerror());
+	    commSetSelect(fd,
+		COMM_SELECT_WRITE,
+		commHandleWrite,
+		state,
+		0);
+	} else {
+	    debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
+		fd, xstrerror());
+	    CommWriteStateCallbackAndFree(fd, COMM_ERROR);
+	}
+    } else {
+	/* A successful write, continue */
+	state->offset += len;
+	if (state->offset < (off_t)state->size) {
+	    /* Not done, reinstall the write handler and write some more */
+	    commSetSelect(fd,
+		COMM_SELECT_WRITE,
+		commHandleWrite,
+		state,
+		0);
+	} else {
+	    CommWriteStateCallbackAndFree(fd, COMM_OK);
+	}
+    }
+    PROF_stop(commHandleWrite);
+}
+
+
+
+/*
+ * Queue a write. handler/handler_data are called when the write
+ * completes, on error, or on file descriptor close.
+ *
+ * free_func is used to free the passed buffer when the write has completed.
+ */
+void
+comm_write(int fd, const char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func)
+{
+    CommWriteStateData *state = fd_table[fd].rwstate;
+    debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n",
+	fd, size, handler, handler_data);
+    if (NULL != state) {
+	debug(5, 1) ("comm_write: fd_table[%d].rwstate != NULL\n", fd);
+	memPoolFree(comm_write_pool, state);
+	fd_table[fd].rwstate = NULL;
+    }
+    fd_table[fd].rwstate = state = (CommWriteStateData *)memPoolAlloc(comm_write_pool);
+    state->buf = (char *) buf;
+    state->size = size;
+    state->offset = 0;
+    state->handler = handler;
+    state->handler_data = cbdataReference(handler_data);
+    state->free_func = free_func;
+    commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, state, 0);
+}
+
+/* a wrapper around comm_write to allow for MemBuf to be comm_written in a snap */
+void
+comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data)
+{
+    comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
+}
+
+/*
+ * hm, this might be too general-purpose for all the places we'd
+ * like to use it.
+ */
+int
+ignoreErrno(int ierrno)
+{
+    switch (ierrno) {
+    case EINPROGRESS:
+    case EWOULDBLOCK:
+#if EAGAIN != EWOULDBLOCK
+    case EAGAIN:
+#endif
+    case EALREADY:
+    case EINTR:
+#ifdef ERESTART
+    case ERESTART:
+#endif
+	return 1;
+    default:
+	return 0;
+    }
+    /* NOTREACHED */
+}
+
+void
+commCloseAllSockets(void)
+{
+    int fd;
+    fde *F = NULL;
+    for (fd = 0; fd <= Biggest_FD; fd++) {
+	F = &fd_table[fd];
+	if (!F->flags.open)
+	    continue;
+	if (F->type != FD_SOCKET)
+	    continue;
+	if (F->flags.ipc)	/* don't close inter-process sockets */
+	    continue;
+	if (F->timeout_handler) {
+	    PF *callback = F->timeout_handler;
+	    void *cbdata = NULL;
+	    F->timeout_handler = NULL;
+	    debug(5, 5) ("commCloseAllSockets: FD %d: Calling timeout handler\n",
+		fd);
+	    if (cbdataReferenceValidDone(F->timeout_data, &cbdata))
+		callback(fd, cbdata);
+	} else {
+	    debug(5, 5) ("commCloseAllSockets: FD %d: calling comm_close()\n", fd);
+	    comm_close(fd);
+	}
+    }
+}
+
+void
+checkTimeouts(void)
+{
+    int fd;
+    fde *F = NULL;
+    PF *callback;
+    for (fd = 0; fd <= Biggest_FD; fd++) {
+	F = &fd_table[fd];
+	if (!F->flags.open)
+	    continue;
+	if (F->timeout == 0)
+	    continue;
+	if (F->timeout > squid_curtime)
+	    continue;
+	debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd);
+	if (F->timeout_handler) {
+	    debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd);
+	    callback = F->timeout_handler;
+	    F->timeout_handler = NULL;
+	    callback(fd, F->timeout_data);
+	} else {
+	    debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd);
+	    comm_close(fd);
+	}
+    }
+}
+
+
+int
+commDeferRead(int fd)
+{
+    fde *F = &fd_table[fd];
+    if (F->defer_check == NULL)
+	return 0;
+    return F->defer_check(fd, F->defer_data);
+}
--- squid/src/comm_kqueue.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,286 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 5    Socket functions
- *
- * 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.
- *
- */
-
-/*
- * This code was originally written by Benno Rice and hacked on quite
- * a bit by Adrian. Adrian then took it to the hybrid-ircd project to use
- * in their new IO subsystem. After a year of modifications and some
- * rather interesting changes (event aggregation) its back in squid.
- * Thanks to the ircd-hybrid guys.
- */
-
-/*
- * XXX Currently not implemented / supported by this module XXX
- *
- * - delay pools
- * - deferred reads
- *
- * So, its not entirely useful in a production setup since if a read
- * is meant to be deferred it isn't (we're not even throwing the event
- * away here). Eventually the rest of the code will be rewritten
- * so deferred reads aren't required.
- *  -- adrian
- */
-
-#include "squid.h"
-#include "Store.h"
-
-#ifdef USE_KQUEUE
-
-#include <sys/event.h>
-
-
-#define KE_LENGTH	128
-
-/* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
-
-#ifndef EV_SET
-#define EV_SET(kevp, a, b, c, d, e, f) do {     \
-        (kevp)->ident = (a);                    \
-        (kevp)->filter = (b);                   \
-        (kevp)->flags = (c);                    \
-        (kevp)->fflags = (d);                   \
-        (kevp)->data = (e);                     \
-        (kevp)->udata = (f);                    \
-} while(0)
-#endif
-
-static void kq_update_events(int, short, PF *);
-static int kq;
-static struct timespec zero_timespec;
-
-static struct kevent *kqlst;	/* kevent buffer */
-static int kqmax;		/* max structs to buffer */
-static int kqoff;		/* offset into the buffer */
-
-
-/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
-/* Private functions */
-
-void
-kq_update_events(int fd, short filter, PF * handler)
-{
-    PF *cur_handler;
-    int kep_flags;
-
-#if 0
-    int retval;
-#endif
-
-    switch (filter) {
-    case EVFILT_READ:
-	cur_handler = fd_table[fd].read_handler;
-	break;
-    case EVFILT_WRITE:
-	cur_handler = fd_table[fd].write_handler;
-	break;
-    default:
-	/* XXX bad! -- adrian */
-	return;
-	break;
-    }
-
-    if ((cur_handler == NULL && handler != NULL)
-	|| (cur_handler != NULL && handler == NULL)) {
-	struct kevent *kep;
-
-	kep = kqlst + kqoff;
-
-	if (handler != NULL) {
-	    kep_flags = (EV_ADD | EV_ONESHOT);
-	} else {
-	    kep_flags = EV_DELETE;
-	}
-
-	EV_SET(kep, (uintptr_t) fd, filter, kep_flags, 0, 0, 0);
-
-	if (kqoff == kqmax) {
-	    int ret;
-
-	    ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
-	    /* jdc -- someone needs to do error checking... */
-	    if (ret == -1) {
-		perror("kq_update_events(): kevent()");
-		return;
-	    }
-	    kqoff = 0;
-	} else {
-	    kqoff++;
-	}
-#if 0
-	if (retval < 0) {
-	    /* Error! */
-	    if (ke.flags & EV_ERROR) {
-		errno = ke.data;
-	    }
-	}
-#endif
-    }
-}
-
-
-
-/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
-/* Public functions */
-
-
-/*
- * comm_select_init
- *
- * This is a needed exported function which will be called to initialise
- * the network loop code.
- */
-void
-comm_select_init(void)
-{
-    kq = kqueue();
-    if (kq < 0) {
-	fatal("comm_select_init: Couldn't open kqueue fd!\n");
-    }
-    kqmax = getdtablesize();
-    kqlst = xmalloc(sizeof(*kqlst) * kqmax);
-    zero_timespec.tv_sec = 0;
-    zero_timespec.tv_nsec = 0;
-}
-
-/*
- * comm_setselect
- *
- * This is a needed exported function which will be called to register
- * and deregister interest in a pending IO state for a given FD.
- */
-void
-commSetSelect(int fd, unsigned int type, PF * handler,
-    void *client_data, time_t timeout)
-{
-    fde *F = &fd_table[fd];
-    assert(fd >= 0);
-    assert(F->flags.open);
-
-    if (type & COMM_SELECT_READ) {
-	kq_update_events(fd, EVFILT_READ, handler);
-	F->read_handler = handler;
-	F->read_data = client_data;
-    }
-    if (type & COMM_SELECT_WRITE) {
-	kq_update_events(fd, EVFILT_WRITE, handler);
-	F->write_handler = handler;
-	F->write_data = client_data;
-    }
-    if (timeout)
-	F->timeout = squid_curtime + timeout;
-
-}
-
-/*
- * Check all connections for new connections and input data that is to be
- * processed. Also check for connections with data queued and whether we can
- * write it out.
- */
-
-/*
- * comm_select
- *
- * Called to do the new-style IO, courtesy of of squid (like most of this
- * new IO code). This routine handles the stuff we've hidden in
- * comm_setselect and fd_table[] and calls callbacks for IO ready
- * events.
- */
-
-comm_err_t
-comm_select(int msec)
-{
-    int num, i;
-    static struct kevent ke[KE_LENGTH];
-    struct timespec poll_time;
-
-    do {
-	/*
-	 * remember we are doing NANOseconds here, not micro/milli. God knows
-	 * why jlemon used a timespec, but hey, he wrote the interface, not I
-	 *   -- Adrian
-	 */
-	poll_time.tv_sec = msec / 1000;
-	poll_time.tv_nsec = (msec % 1000) * 1000000;
-	for (;;) {
-	    num = kevent(kq, kqlst, kqoff, ke, KE_LENGTH, &poll_time);
-	    statCounter.select_loops++;
-	    kqoff = 0;
-	    if (num >= 0)
-		break;
-	    if (ignoreErrno(errno))
-		break;
-	    getCurrentTime();
-	    return COMM_ERROR;
-	    /* NOTREACHED */
-	}
-
-	getCurrentTime();
-	if (num == 0)
-	    continue;
-
-	for (i = 0; i < num; i++) {
-	    int fd = (int) ke[i].ident;
-	    PF *hdl = NULL;
-	    fde *F = &fd_table[fd];
-
-	    if (ke[i].flags & EV_ERROR) {
-		errno = ke[i].data;
-		/* XXX error == bad! -- adrian */
-		continue;	/* XXX! */
-	    }
-	    switch (ke[i].filter) {
-	    case EVFILT_READ:
-		if ((hdl = F->read_handler) != NULL) {
-		    F->read_handler = NULL;
-		    hdl(fd, F->read_data);
-		}
-	    case EVFILT_WRITE:
-		if ((hdl = F->write_handler) != NULL) {
-		    F->write_handler = NULL;
-		    hdl(fd, F->write_data);
-		}
-	    default:
-		/* Bad! -- adrian */
-		break;
-	    }
-	}
-	return COMM_OK;
-    }
-    while (0);			/* XXX should rip this out! -- adrian */
-    /* XXX Get here, we broke! */
-    return 0;
-}
-
-#endif /* USE_KQUEUE */
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/comm_kqueue.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,286 @@
+
+/*
+ * $Id: comm_kqueue.cc,v 1.1.2.1 2002/10/11 15:40:44 rbcollins Exp $
+ *
+ * DEBUG: section 5    Socket functions
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This code was originally written by Benno Rice and hacked on quite
+ * a bit by Adrian. Adrian then took it to the hybrid-ircd project to use
+ * in their new IO subsystem. After a year of modifications and some
+ * rather interesting changes (event aggregation) its back in squid.
+ * Thanks to the ircd-hybrid guys.
+ */
+
+/*
+ * XXX Currently not implemented / supported by this module XXX
+ *
+ * - delay pools
+ * - deferred reads
+ *
+ * So, its not entirely useful in a production setup since if a read
+ * is meant to be deferred it isn't (we're not even throwing the event
+ * away here). Eventually the rest of the code will be rewritten
+ * so deferred reads aren't required.
+ *  -- adrian
+ */
+
+#include "squid.h"
+#include "Store.h"
+
+#ifdef USE_KQUEUE
+
+#include <sys/event.h>
+
+
+#define KE_LENGTH	128
+
+/* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
+
+#ifndef EV_SET
+#define EV_SET(kevp, a, b, c, d, e, f) do {     \
+        (kevp)->ident = (a);                    \
+        (kevp)->filter = (b);                   \
+        (kevp)->flags = (c);                    \
+        (kevp)->fflags = (d);                   \
+        (kevp)->data = (e);                     \
+        (kevp)->udata = (f);                    \
+} while(0)
+#endif
+
+static void kq_update_events(int, short, PF *);
+static int kq;
+static struct timespec zero_timespec;
+
+static struct kevent *kqlst;	/* kevent buffer */
+static int kqmax;		/* max structs to buffer */
+static int kqoff;		/* offset into the buffer */
+
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+/* Private functions */
+
+void
+kq_update_events(int fd, short filter, PF * handler)
+{
+    PF *cur_handler;
+    int kep_flags;
+
+#if 0
+    int retval;
+#endif
+
+    switch (filter) {
+    case EVFILT_READ:
+	cur_handler = fd_table[fd].read_handler;
+	break;
+    case EVFILT_WRITE:
+	cur_handler = fd_table[fd].write_handler;
+	break;
+    default:
+	/* XXX bad! -- adrian */
+	return;
+	break;
+    }
+
+    if ((cur_handler == NULL && handler != NULL)
+	|| (cur_handler != NULL && handler == NULL)) {
+	struct kevent *kep;
+
+	kep = kqlst + kqoff;
+
+	if (handler != NULL) {
+	    kep_flags = (EV_ADD | EV_ONESHOT);
+	} else {
+	    kep_flags = EV_DELETE;
+	}
+
+	EV_SET(kep, (uintptr_t) fd, filter, kep_flags, 0, 0, 0);
+
+	if (kqoff == kqmax) {
+	    int ret;
+
+	    ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
+	    /* jdc -- someone needs to do error checking... */
+	    if (ret == -1) {
+		perror("kq_update_events(): kevent()");
+		return;
+	    }
+	    kqoff = 0;
+	} else {
+	    kqoff++;
+	}
+#if 0
+	if (retval < 0) {
+	    /* Error! */
+	    if (ke.flags & EV_ERROR) {
+		errno = ke.data;
+	    }
+	}
+#endif
+    }
+}
+
+
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+/* Public functions */
+
+
+/*
+ * comm_select_init
+ *
+ * This is a needed exported function which will be called to initialise
+ * the network loop code.
+ */
+void
+comm_select_init(void)
+{
+    kq = kqueue();
+    if (kq < 0) {
+	fatal("comm_select_init: Couldn't open kqueue fd!\n");
+    }
+    kqmax = getdtablesize();
+    kqlst = xmalloc(sizeof(*kqlst) * kqmax);
+    zero_timespec.tv_sec = 0;
+    zero_timespec.tv_nsec = 0;
+}
+
+/*
+ * comm_setselect
+ *
+ * This is a needed exported function which will be called to register
+ * and deregister interest in a pending IO state for a given FD.
+ */
+void
+commSetSelect(int fd, unsigned int type, PF * handler,
+    void *client_data, time_t timeout)
+{
+    fde *F = &fd_table[fd];
+    assert(fd >= 0);
+    assert(F->flags.open);
+
+    if (type & COMM_SELECT_READ) {
+	kq_update_events(fd, EVFILT_READ, handler);
+	F->read_handler = handler;
+	F->read_data = client_data;
+    }
+    if (type & COMM_SELECT_WRITE) {
+	kq_update_events(fd, EVFILT_WRITE, handler);
+	F->write_handler = handler;
+	F->write_data = client_data;
+    }
+    if (timeout)
+	F->timeout = squid_curtime + timeout;
+
+}
+
+/*
+ * Check all connections for new connections and input data that is to be
+ * processed. Also check for connections with data queued and whether we can
+ * write it out.
+ */
+
+/*
+ * comm_select
+ *
+ * Called to do the new-style IO, courtesy of of squid (like most of this
+ * new IO code). This routine handles the stuff we've hidden in
+ * comm_setselect and fd_table[] and calls callbacks for IO ready
+ * events.
+ */
+
+comm_err_t
+comm_select(int msec)
+{
+    int num, i;
+    static struct kevent ke[KE_LENGTH];
+    struct timespec poll_time;
+
+    do {
+	/*
+	 * remember we are doing NANOseconds here, not micro/milli. God knows
+	 * why jlemon used a timespec, but hey, he wrote the interface, not I
+	 *   -- Adrian
+	 */
+	poll_time.tv_sec = msec / 1000;
+	poll_time.tv_nsec = (msec % 1000) * 1000000;
+	for (;;) {
+	    num = kevent(kq, kqlst, kqoff, ke, KE_LENGTH, &poll_time);
+	    statCounter.select_loops++;
+	    kqoff = 0;
+	    if (num >= 0)
+		break;
+	    if (ignoreErrno(errno))
+		break;
+	    getCurrentTime();
+	    return COMM_ERROR;
+	    /* NOTREACHED */
+	}
+
+	getCurrentTime();
+	if (num == 0)
+	    continue;
+
+	for (i = 0; i < num; i++) {
+	    int fd = (int) ke[i].ident;
+	    PF *hdl = NULL;
+	    fde *F = &fd_table[fd];
+
+	    if (ke[i].flags & EV_ERROR) {
+		errno = ke[i].data;
+		/* XXX error == bad! -- adrian */
+		continue;	/* XXX! */
+	    }
+	    switch (ke[i].filter) {
+	    case EVFILT_READ:
+		if ((hdl = F->read_handler) != NULL) {
+		    F->read_handler = NULL;
+		    hdl(fd, F->read_data);
+		}
+	    case EVFILT_WRITE:
+		if ((hdl = F->write_handler) != NULL) {
+		    F->write_handler = NULL;
+		    hdl(fd, F->write_data);
+		}
+	    default:
+		/* Bad! -- adrian */
+		break;
+	    }
+	}
+	return COMM_OK;
+    }
+    while (0);			/* XXX should rip this out! -- adrian */
+    /* XXX Get here, we broke! */
+    return 0;
+}
+
+#endif /* USE_KQUEUE */
--- squid/src/comm_poll.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,630 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 5     Socket Functions
- *
- * 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"
-#include "Store.h"
-
-#ifdef USE_POLL
-
-static int MAX_POLL_TIME = 1000;	/* see also comm_quick_poll_required() */
-
-#ifndef        howmany
-#define howmany(x, y)   (((x)+((y)-1))/(y))
-#endif
-#ifndef        NBBY
-#define        NBBY    8
-#endif
-#define FD_MASK_BYTES sizeof(fd_mask)
-#define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
-
-/* STATIC */
-static int fdIsHttp(int fd);
-static int fdIsIcp(int fd);
-static int fdIsDns(int fd);
-static OBJH commIncomingStats;
-static int comm_check_incoming_poll_handlers(int nfds, int *fds);
-static void comm_poll_dns_incoming(void);
-static void commUpdateReadBits(int fd, PF * handler);
-static void commUpdateWriteBits(int fd, PF * handler);
-
-static fd_set global_readfds;
-static fd_set global_writefds;
-static int nreadfds;
-static int nwritefds;
-
-/*
- * Automatic tuning for incoming requests:
- *
- * INCOMING sockets are the ICP and HTTP ports.  We need to check these
- * fairly regularly, but how often?  When the load increases, we
- * want to check the incoming sockets more often.  If we have a lot
- * of incoming ICP, then we need to check these sockets more than
- * if we just have HTTP.
- *
- * The variables 'incoming_icp_interval' and 'incoming_http_interval' 
- * determine how many normal I/O events to process before checking
- * incoming sockets again.  Note we store the incoming_interval
- * multipled by a factor of (2^INCOMING_FACTOR) to have some
- * pseudo-floating point precision.
- *
- * The variable 'icp_io_events' and 'http_io_events' counts how many normal
- * I/O events have been processed since the last check on the incoming
- * sockets.  When io_events > incoming_interval, its time to check incoming
- * sockets.
- *
- * Every time we check incoming sockets, we count how many new messages
- * or connections were processed.  This is used to adjust the
- * incoming_interval for the next iteration.  The new incoming_interval
- * is calculated as the current incoming_interval plus what we would
- * like to see as an average number of events minus the number of
- * events just processed.
- *
- *  incoming_interval = incoming_interval + target_average - number_of_events_processed
- *
- * There are separate incoming_interval counters for both HTTP and ICP events
- * 
- * You can see the current values of the incoming_interval's, as well as
- * a histogram of 'incoming_events' by asking the cache manager
- * for 'comm_incoming', e.g.:
- *
- *      % ./client mgr:comm_incoming
- *
- * Caveats:
- *
- *      - We have MAX_INCOMING_INTEGER as a magic upper limit on
- *        incoming_interval for both types of sockets.  At the
- *        largest value the cache will effectively be idling.
- *
- *      - The higher the INCOMING_FACTOR, the slower the algorithm will
- *        respond to load spikes/increases/decreases in demand. A value
- *        between 3 and 8 is recommended.
- */
-
-#define MAX_INCOMING_INTEGER 256
-#define INCOMING_FACTOR 5
-#define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR)
-static int icp_io_events = 0;
-static int dns_io_events = 0;
-static int http_io_events = 0;
-static int incoming_icp_interval = 16 << INCOMING_FACTOR;
-static int incoming_dns_interval = 16 << INCOMING_FACTOR;
-static int incoming_http_interval = 16 << INCOMING_FACTOR;
-#define commCheckICPIncoming (++icp_io_events > (incoming_icp_interval>> INCOMING_FACTOR))
-#define commCheckDNSIncoming (++dns_io_events > (incoming_dns_interval>> INCOMING_FACTOR))
-#define commCheckHTTPIncoming (++http_io_events > (incoming_http_interval>> INCOMING_FACTOR))
-
-
-void
-commSetSelect(int fd, unsigned int type, PF * handler, void *client_data,
-    time_t timeout)
-{
-    fde *F = &fd_table[fd];
-    assert(fd >= 0);
-    assert(F->flags.open);
-    debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type);
-    if (type & COMM_SELECT_READ) {
-	F->read_handler = handler;
-	F->read_data = client_data;
-	commUpdateReadBits(fd, handler);
-    }
-    if (type & COMM_SELECT_WRITE) {
-	F->write_handler = handler;
-	F->write_data = client_data;
-	commUpdateWriteBits(fd, handler);
-    }
-    if (timeout)
-	F->timeout = squid_curtime + timeout;
-}
-
-static int
-fdIsIcp(int fd)
-{
-    if (fd == theInIcpConnection)
-	return 1;
-    if (fd == theOutIcpConnection)
-	return 1;
-    return 0;
-}
-
-static int
-fdIsDns(int fd)
-{
-    if (fd == DnsSocket)
-	return 1;
-    return 0;
-}
-
-static int
-fdIsHttp(int fd)
-{
-    int j;
-    for (j = 0; j < NHttpSockets; j++) {
-	if (fd == HttpSockets[j])
-	    return 1;
-    }
-    return 0;
-}
-
-#if DELAY_POOLS
-static int slowfdcnt = 0;
-static int slowfdarr[SQUID_MAXFD];
-
-static void
-commAddSlowFd(int fd)
-{
-    assert(slowfdcnt < SQUID_MAXFD);
-    slowfdarr[slowfdcnt++] = fd;
-}
-
-static int
-commGetSlowFd(void)
-{
-    int whichfd, retfd;
-
-    if (!slowfdcnt)
-	return -1;
-    whichfd = squid_random() % slowfdcnt;
-    retfd = slowfdarr[whichfd];
-    slowfdarr[whichfd] = slowfdarr[--slowfdcnt];
-    return retfd;
-}
-#endif
-
-static int
-comm_check_incoming_poll_handlers(int nfds, int *fds)
-{
-    int i;
-    int fd;
-    PF *hdl = NULL;
-    int npfds;
-    struct pollfd pfds[3 + MAXHTTPPORTS];
-    PROF_start(comm_check_incoming);
-    incoming_sockets_accepted = 0;
-    for (i = npfds = 0; i < nfds; i++) {
-	int events;
-	fd = fds[i];
-	events = 0;
-	if (fd_table[fd].read_handler)
-	    events |= POLLRDNORM;
-	if (fd_table[fd].write_handler)
-	    events |= POLLWRNORM;
-	if (events) {
-	    pfds[npfds].fd = fd;
-	    pfds[npfds].events = events;
-	    pfds[npfds].revents = 0;
-	    npfds++;
-	}
-    }
-    if (!nfds) {
-	PROF_stop(comm_check_incoming);
-	return -1;
-    }
-    getCurrentTime();
-    statCounter.syscalls.polls++;
-    if (poll(pfds, npfds, 0) < 1) {
-	PROF_stop(comm_check_incoming);
-	return incoming_sockets_accepted;
-    }
-    for (i = 0; i < npfds; i++) {
-	int revents;
-	if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1))
-	    continue;
-	if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
-	    if ((hdl = fd_table[fd].read_handler)) {
-		fd_table[fd].read_handler = NULL;
-		hdl(fd, fd_table[fd].read_data);
-	    } else if (pfds[i].events & POLLRDNORM)
-		debug(5, 1) ("comm_poll_incoming: FD %d NULL read handler\n",
-		    fd);
-	}
-	if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
-	    if ((hdl = fd_table[fd].write_handler)) {
-		fd_table[fd].write_handler = NULL;
-		hdl(fd, fd_table[fd].write_data);
-	    } else if (pfds[i].events & POLLWRNORM)
-		debug(5, 1) ("comm_poll_incoming: FD %d NULL write_handler\n",
-		    fd);
-	}
-    }
-    PROF_stop(comm_check_incoming);
-    return incoming_sockets_accepted;
-}
-
-static void
-comm_poll_icp_incoming(void)
-{
-    int nfds = 0;
-    int fds[2];
-    int nevents;
-    icp_io_events = 0;
-    if (theInIcpConnection >= 0)
-	fds[nfds++] = theInIcpConnection;
-    if (theInIcpConnection != theOutIcpConnection)
-	if (theOutIcpConnection >= 0)
-	    fds[nfds++] = theOutIcpConnection;
-    if (nfds == 0)
-	return;
-    nevents = comm_check_incoming_poll_handlers(nfds, fds);
-    incoming_icp_interval += Config.comm_incoming.icp_average - nevents;
-    if (incoming_icp_interval < Config.comm_incoming.icp_min_poll)
-	incoming_icp_interval = Config.comm_incoming.icp_min_poll;
-    if (incoming_icp_interval > MAX_INCOMING_INTERVAL)
-	incoming_icp_interval = MAX_INCOMING_INTERVAL;
-    if (nevents > INCOMING_ICP_MAX)
-	nevents = INCOMING_ICP_MAX;
-    statHistCount(&statCounter.comm_icp_incoming, nevents);
-}
-
-static void
-comm_poll_http_incoming(void)
-{
-    int nfds = 0;
-    int fds[MAXHTTPPORTS];
-    int j;
-    int nevents;
-    http_io_events = 0;
-    for (j = 0; j < NHttpSockets; j++) {
-	if (HttpSockets[j] < 0)
-	    continue;
-	if (commDeferRead(HttpSockets[j]))
-	    continue;
-	fds[nfds++] = HttpSockets[j];
-    }
-    nevents = comm_check_incoming_poll_handlers(nfds, fds);
-    incoming_http_interval = incoming_http_interval
-	+ Config.comm_incoming.http_average - nevents;
-    if (incoming_http_interval < Config.comm_incoming.http_min_poll)
-	incoming_http_interval = Config.comm_incoming.http_min_poll;
-    if (incoming_http_interval > MAX_INCOMING_INTERVAL)
-	incoming_http_interval = MAX_INCOMING_INTERVAL;
-    if (nevents > INCOMING_HTTP_MAX)
-	nevents = INCOMING_HTTP_MAX;
-    statHistCount(&statCounter.comm_http_incoming, nevents);
-}
-
-/* poll all sockets; call handlers for those that are ready. */
-comm_err_t
-comm_select(int msec)
-{
-    struct pollfd pfds[SQUID_MAXFD];
-#if DELAY_POOLS
-    fd_set slowfds;
-#endif
-    PF *hdl = NULL;
-    int fd;
-    int i;
-    int maxfd;
-    unsigned long nfds;
-    unsigned long npending;
-    int num;
-    int callicp = 0, callhttp = 0;
-    int calldns = 0;
-    static time_t last_timeout = 0;
-    double timeout = current_dtime + (msec / 1000.0);
-    do {
-	double start;
-	getCurrentTime();
-	start = current_dtime;
-	/* Handle any fs callbacks that need doing */
-	storeDirCallback();
-#if DELAY_POOLS
-	FD_ZERO(&slowfds);
-#endif
-	if (commCheckICPIncoming)
-	    comm_poll_icp_incoming();
-	if (commCheckDNSIncoming)
-	    comm_poll_dns_incoming();
-	if (commCheckHTTPIncoming)
-	    comm_poll_http_incoming();
-	PROF_start(comm_poll_prep_pfds);
-	callicp = calldns = callhttp = 0;
-	nfds = 0;
-	npending = 0;
-	maxfd = Biggest_FD + 1;
-	for (i = 0; i < maxfd; i++) {
-	    int events;
-	    events = 0;
-	    /* Check each open socket for a handler. */
-	    if (fd_table[i].read_handler) {
-		switch (commDeferRead(i)) {
-		case 0:
-		    events |= POLLRDNORM;
-		    break;
-		case 1:
-		    break;
-#if DELAY_POOLS
-		case -1:
-		    events |= POLLRDNORM;
-		    FD_SET(i, &slowfds);
-		    break;
-#endif
-		default:
-		    fatalf("bad return value from commDeferRead(FD %d)\n", i);
-		}
-	    }
-	    if (fd_table[i].write_handler)
-		events |= POLLWRNORM;
-	    if (events) {
-		pfds[nfds].fd = i;
-		pfds[nfds].events = events;
-		pfds[nfds].revents = 0;
-		nfds++;
-		if ((events & POLLRDNORM) && fd_table[i].flags.read_pending)
-		    npending++;
-	    }
-	}
-	PROF_stop(comm_poll_prep_pfds);
-	if (nfds == 0) {
-	    assert(shutting_down);
-	    return COMM_SHUTDOWN;
-	}
-	if (npending)
-	    msec = 0;
-	if (msec > MAX_POLL_TIME)
-	    msec = MAX_POLL_TIME;
-	for (;;) {
-	    PROF_start(comm_poll_normal);
-	    statCounter.syscalls.polls++;
-	    num = poll(pfds, nfds, msec);
-	    statCounter.select_loops++;
-	    PROF_stop(comm_poll_normal);
-	    if (num >= 0 || npending >= 0)
-		break;
-	    if (ignoreErrno(errno))
-		continue;
-	    debug(5, 0) ("comm_poll: poll failure: %s\n", xstrerror());
-	    assert(errno != EINVAL);
-	    return COMM_ERROR;
-	    /* NOTREACHED */
-	}
-	debug(5, num ? 5 : 8) ("comm_poll: %d+%ld FDs ready\n", num, npending);
-	statHistCount(&statCounter.select_fds_hist, num);
-	/* Check timeout handlers ONCE each second. */
-	if (squid_curtime > last_timeout) {
-	    last_timeout = squid_curtime;
-	    checkTimeouts();
-	}
-	if (num == 0 && npending == 0)
-	    continue;
-	/* scan each socket but the accept socket. Poll this 
-	 * more frequently to minimize losses due to the 5 connect 
-	 * limit in SunOS */
-	PROF_start(comm_handle_ready_fd);
-	for (i = 0; i < nfds; i++) {
-	    fde *F;
-	    int revents = pfds[i].revents;
-	    fd = pfds[i].fd;
-	    if (fd == -1)
-		continue;
-	    if (fd_table[fd].flags.read_pending)
-		revents |= POLLIN;
-	    if (revents == 0)
-		continue;
-	    if (fdIsIcp(fd)) {
-		callicp = 1;
-		continue;
-	    }
-	    if (fdIsDns(fd)) {
-		calldns = 1;
-		continue;
-	    }
-	    if (fdIsHttp(fd)) {
-		callhttp = 1;
-		continue;
-	    }
-	    F = &fd_table[fd];
-	    if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
-		debug(5, 6) ("comm_poll: FD %d ready for reading\n", fd);
-		if (NULL == (hdl = F->read_handler))
-		    (void) 0;
-#if DELAY_POOLS
-		else if (FD_ISSET(fd, &slowfds))
-		    commAddSlowFd(fd);
-#endif
-		else {
-		    PROF_start(comm_read_handler);
-		    F->read_handler = NULL;
-		    hdl(fd, F->read_data);
-		    PROF_stop(comm_read_handler);
-		    statCounter.select_fds++;
-		    if (commCheckICPIncoming)
-			comm_poll_icp_incoming();
-		    if (commCheckDNSIncoming)
-			comm_poll_dns_incoming();
-		    if (commCheckHTTPIncoming)
-			comm_poll_http_incoming();
-		}
-	    }
-	    if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
-		debug(5, 5) ("comm_poll: FD %d ready for writing\n", fd);
-		if ((hdl = F->write_handler)) {
-		    PROF_start(comm_write_handler);
-		    F->write_handler = NULL;
-		    hdl(fd, F->write_data);
-		    PROF_stop(comm_write_handler);
-		    statCounter.select_fds++;
-		    if (commCheckICPIncoming)
-			comm_poll_icp_incoming();
-		    if (commCheckDNSIncoming)
-			comm_poll_dns_incoming();
-		    if (commCheckHTTPIncoming)
-			comm_poll_http_incoming();
-		}
-	    }
-	    if (revents & POLLNVAL) {
-		close_handler *ch;
-		debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd);
-		debug(5, 0) ("FD %d is a %s\n", fd, fdTypeStr[F->type]);
-		debug(5, 0) ("--> %s\n", F->desc);
-		debug(5, 0) ("tmout:%p read:%p write:%p\n",
-		    F->timeout_handler,
-		    F->read_handler,
-		    F->write_handler);
-		for (ch = F->closeHandler; ch; ch = ch->next)
-		    debug(5, 0) (" close handler: %p\n", ch->handler);
-		if (F->closeHandler) {
-		    commCallCloseHandlers(fd);
-		} else if (F->timeout_handler) {
-		    debug(5, 0) ("comm_poll: Calling Timeout Handler\n");
-		    F->timeout_handler(fd, F->timeout_data);
-		}
-		F->closeHandler = NULL;
-		F->timeout_handler = NULL;
-		F->read_handler = NULL;
-		F->write_handler = NULL;
-		if (F->flags.open)
-		    fd_close(fd);
-	    }
-	}
-	PROF_stop(comm_handle_ready_fd);
-	if (callicp)
-	    comm_poll_icp_incoming();
-	if (calldns)
-	    comm_poll_dns_incoming();
-	if (callhttp)
-	    comm_poll_http_incoming();
-#if DELAY_POOLS
-	while ((fd = commGetSlowFd()) != -1) {
-	    fde *F = &fd_table[fd];
-	    debug(5, 6) ("comm_select: slow FD %d selected for reading\n", fd);
-	    if ((hdl = F->read_handler)) {
-		F->read_handler = NULL;
-		hdl(fd, F->read_data);
-		statCounter.select_fds++;
-		if (commCheckICPIncoming)
-		    comm_poll_icp_incoming();
-		if (commCheckDNSIncoming)
-		    comm_poll_dns_incoming();
-		if (commCheckHTTPIncoming)
-		    comm_poll_http_incoming();
-	    }
-	}
-#endif
-	getCurrentTime();
-	statCounter.select_time += (current_dtime - start);
-	return COMM_OK;
-    }
-    while (timeout > current_dtime);
-    debug(5, 8) ("comm_poll: time out: %ld.\n", (long int) squid_curtime);
-    return COMM_TIMEOUT;
-}
-
-
-static void
-comm_poll_dns_incoming(void)
-{
-    int nfds = 0;
-    int fds[2];
-    int nevents;
-    dns_io_events = 0;
-    if (DnsSocket < 0)
-	return;
-    fds[nfds++] = DnsSocket;
-    nevents = comm_check_incoming_poll_handlers(nfds, fds);
-    if (nevents < 0)
-	return;
-    incoming_dns_interval += Config.comm_incoming.dns_average - nevents;
-    if (incoming_dns_interval < Config.comm_incoming.dns_min_poll)
-	incoming_dns_interval = Config.comm_incoming.dns_min_poll;
-    if (incoming_dns_interval > MAX_INCOMING_INTERVAL)
-	incoming_dns_interval = MAX_INCOMING_INTERVAL;
-    if (nevents > INCOMING_DNS_MAX)
-	nevents = INCOMING_DNS_MAX;
-    statHistCount(&statCounter.comm_dns_incoming, nevents);
-}
-
-void
-comm_select_init(void)
-{
-    cachemgrRegister("comm_incoming",
-	"comm_incoming() stats",
-	commIncomingStats, 0, 1);
-    FD_ZERO(&global_readfds);
-    FD_ZERO(&global_writefds);
-    nreadfds = nwritefds = 0;
-}
-
-
-static void
-commIncomingStats(StoreEntry * sentry)
-{
-    StatCounters *f = &statCounter;
-    storeAppendPrintf(sentry, "Current incoming_icp_interval: %d\n",
-	incoming_icp_interval >> INCOMING_FACTOR);
-    storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n",
-	incoming_dns_interval >> INCOMING_FACTOR);
-    storeAppendPrintf(sentry, "Current incoming_http_interval: %d\n",
-	incoming_http_interval >> INCOMING_FACTOR);
-    storeAppendPrintf(sentry, "\n");
-    storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n");
-    storeAppendPrintf(sentry, "ICP Messages handled per comm_poll_icp_incoming() call:\n");
-    statHistDump(&f->comm_icp_incoming, sentry, statHistIntDumper);
-    storeAppendPrintf(sentry, "DNS Messages handled per comm_poll_dns_incoming() call:\n");
-    statHistDump(&f->comm_dns_incoming, sentry, statHistIntDumper);
-    storeAppendPrintf(sentry, "HTTP Messages handled per comm_poll_http_incoming() call:\n");
-    statHistDump(&f->comm_http_incoming, sentry, statHistIntDumper);
-}
-
-void
-commUpdateReadBits(int fd, PF * handler)
-{
-    if (handler && !FD_ISSET(fd, &global_readfds)) {
-	FD_SET(fd, &global_readfds);
-	nreadfds++;
-    } else if (!handler && FD_ISSET(fd, &global_readfds)) {
-	FD_CLR(fd, &global_readfds);
-	nreadfds--;
-    }
-}
-
-void
-commUpdateWriteBits(int fd, PF * handler)
-{
-    if (handler && !FD_ISSET(fd, &global_writefds)) {
-	FD_SET(fd, &global_writefds);
-	nwritefds++;
-    } else if (!handler && FD_ISSET(fd, &global_writefds)) {
-	FD_CLR(fd, &global_writefds);
-	nwritefds--;
-    }
-}
-
-/* Called by async-io or diskd to speed up the polling */
-void
-comm_quick_poll_required(void)
-{
-    MAX_POLL_TIME = 10;
-}
-
-#endif /* USE_POLL */
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/comm_poll.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,629 @@
+
+/*
+ * $Id: comm_poll.cc,v 1.1.2.1 2002/10/11 15:40:47 rbcollins Exp $
+ *
+ * DEBUG: section 5     Socket Functions
+ *
+ * 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"
+#include "Store.h"
+
+#ifdef USE_POLL
+
+static int MAX_POLL_TIME = 1000;	/* see also comm_quick_poll_required() */
+
+#ifndef        howmany
+#define howmany(x, y)   (((x)+((y)-1))/(y))
+#endif
+#ifndef        NBBY
+#define        NBBY    8
+#endif
+#define FD_MASK_BYTES sizeof(fd_mask)
+#define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
+
+/* STATIC */
+static int fdIsHttp(int fd);
+static int fdIsIcp(int fd);
+static int fdIsDns(int fd);
+static OBJH commIncomingStats;
+static int comm_check_incoming_poll_handlers(int nfds, int *fds);
+static void comm_poll_dns_incoming(void);
+static void commUpdateReadBits(int fd, PF * handler);
+static void commUpdateWriteBits(int fd, PF * handler);
+
+static fd_set global_readfds;
+static fd_set global_writefds;
+static int nreadfds;
+static int nwritefds;
+
+/*
+ * Automatic tuning for incoming requests:
+ *
+ * INCOMING sockets are the ICP and HTTP ports.  We need to check these
+ * fairly regularly, but how often?  When the load increases, we
+ * want to check the incoming sockets more often.  If we have a lot
+ * of incoming ICP, then we need to check these sockets more than
+ * if we just have HTTP.
+ *
+ * The variables 'incoming_icp_interval' and 'incoming_http_interval' 
+ * determine how many normal I/O events to process before checking
+ * incoming sockets again.  Note we store the incoming_interval
+ * multipled by a factor of (2^INCOMING_FACTOR) to have some
+ * pseudo-floating point precision.
+ *
+ * The variable 'icp_io_events' and 'http_io_events' counts how many normal
+ * I/O events have been processed since the last check on the incoming
+ * sockets.  When io_events > incoming_interval, its time to check incoming
+ * sockets.
+ *
+ * Every time we check incoming sockets, we count how many new messages
+ * or connections were processed.  This is used to adjust the
+ * incoming_interval for the next iteration.  The new incoming_interval
+ * is calculated as the current incoming_interval plus what we would
+ * like to see as an average number of events minus the number of
+ * events just processed.
+ *
+ *  incoming_interval = incoming_interval + target_average - number_of_events_processed
+ *
+ * There are separate incoming_interval counters for both HTTP and ICP events
+ * 
+ * You can see the current values of the incoming_interval's, as well as
+ * a histogram of 'incoming_events' by asking the cache manager
+ * for 'comm_incoming', e.g.:
+ *
+ *      % ./client mgr:comm_incoming
+ *
+ * Caveats:
+ *
+ *      - We have MAX_INCOMING_INTEGER as a magic upper limit on
+ *        incoming_interval for both types of sockets.  At the
+ *        largest value the cache will effectively be idling.
+ *
+ *      - The higher the INCOMING_FACTOR, the slower the algorithm will
+ *        respond to load spikes/increases/decreases in demand. A value
+ *        between 3 and 8 is recommended.
+ */
+
+#define MAX_INCOMING_INTEGER 256
+#define INCOMING_FACTOR 5
+#define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR)
+static int icp_io_events = 0;
+static int dns_io_events = 0;
+static int http_io_events = 0;
+static int incoming_icp_interval = 16 << INCOMING_FACTOR;
+static int incoming_dns_interval = 16 << INCOMING_FACTOR;
+static int incoming_http_interval = 16 << INCOMING_FACTOR;
+#define commCheckICPIncoming (++icp_io_events > (incoming_icp_interval>> INCOMING_FACTOR))
+#define commCheckDNSIncoming (++dns_io_events > (incoming_dns_interval>> INCOMING_FACTOR))
+#define commCheckHTTPIncoming (++http_io_events > (incoming_http_interval>> INCOMING_FACTOR))
+
+
+void
+commSetSelect(int fd, unsigned int type, PF * handler, void *client_data,
+    time_t timeout)
+{
+    fde *F = &fd_table[fd];
+    assert(fd >= 0);
+    assert(F->flags.open);
+    debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type);
+    if (type & COMM_SELECT_READ) {
+	F->read_handler = handler;
+	F->read_data = client_data;
+	commUpdateReadBits(fd, handler);
+    }
+    if (type & COMM_SELECT_WRITE) {
+	F->write_handler = handler;
+	F->write_data = client_data;
+	commUpdateWriteBits(fd, handler);
+    }
+    if (timeout)
+	F->timeout = squid_curtime + timeout;
+}
+
+static int
+fdIsIcp(int fd)
+{
+    if (fd == theInIcpConnection)
+	return 1;
+    if (fd == theOutIcpConnection)
+	return 1;
+    return 0;
+}
+
+static int
+fdIsDns(int fd)
+{
+    if (fd == DnsSocket)
+	return 1;
+    return 0;
+}
+
+static int
+fdIsHttp(int fd)
+{
+    int j;
+    for (j = 0; j < NHttpSockets; j++) {
+	if (fd == HttpSockets[j])
+	    return 1;
+    }
+    return 0;
+}
+
+#if DELAY_POOLS
+static int slowfdcnt = 0;
+static int slowfdarr[SQUID_MAXFD];
+
+static void
+commAddSlowFd(int fd)
+{
+    assert(slowfdcnt < SQUID_MAXFD);
+    slowfdarr[slowfdcnt++] = fd;
+}
+
+static int
+commGetSlowFd(void)
+{
+    int whichfd, retfd;
+
+    if (!slowfdcnt)
+	return -1;
+    whichfd = squid_random() % slowfdcnt;
+    retfd = slowfdarr[whichfd];
+    slowfdarr[whichfd] = slowfdarr[--slowfdcnt];
+    return retfd;
+}
+#endif
+
+static int
+comm_check_incoming_poll_handlers(int nfds, int *fds)
+{
+    int i;
+    int fd;
+    PF *hdl = NULL;
+    int npfds;
+    struct pollfd pfds[3 + MAXHTTPPORTS];
+    PROF_start(comm_check_incoming);
+    incoming_sockets_accepted = 0;
+    for (i = npfds = 0; i < nfds; i++) {
+	int events;
+	fd = fds[i];
+	events = 0;
+	if (fd_table[fd].read_handler)
+	    events |= POLLRDNORM;
+	if (fd_table[fd].write_handler)
+	    events |= POLLWRNORM;
+	if (events) {
+	    pfds[npfds].fd = fd;
+	    pfds[npfds].events = events;
+	    pfds[npfds].revents = 0;
+	    npfds++;
+	}
+    }
+    if (!nfds) {
+	PROF_stop(comm_check_incoming);
+	return -1;
+    }
+    getCurrentTime();
+    statCounter.syscalls.polls++;
+    if (poll(pfds, npfds, 0) < 1) {
+	PROF_stop(comm_check_incoming);
+	return incoming_sockets_accepted;
+    }
+    for (i = 0; i < npfds; i++) {
+	int revents;
+	if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1))
+	    continue;
+	if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
+	    if ((hdl = fd_table[fd].read_handler)) {
+		fd_table[fd].read_handler = NULL;
+		hdl(fd, fd_table[fd].read_data);
+	    } else if (pfds[i].events & POLLRDNORM)
+		debug(5, 1) ("comm_poll_incoming: FD %d NULL read handler\n",
+		    fd);
+	}
+	if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
+	    if ((hdl = fd_table[fd].write_handler)) {
+		fd_table[fd].write_handler = NULL;
+		hdl(fd, fd_table[fd].write_data);
+	    } else if (pfds[i].events & POLLWRNORM)
+		debug(5, 1) ("comm_poll_incoming: FD %d NULL write_handler\n",
+		    fd);
+	}
+    }
+    PROF_stop(comm_check_incoming);
+    return incoming_sockets_accepted;
+}
+
+static void
+comm_poll_icp_incoming(void)
+{
+    int nfds = 0;
+    int fds[2];
+    int nevents;
+    icp_io_events = 0;
+    if (theInIcpConnection >= 0)
+	fds[nfds++] = theInIcpConnection;
+    if (theInIcpConnection != theOutIcpConnection)
+	if (theOutIcpConnection >= 0)
+	    fds[nfds++] = theOutIcpConnection;
+    if (nfds == 0)
+	return;
+    nevents = comm_check_incoming_poll_handlers(nfds, fds);
+    incoming_icp_interval += Config.comm_incoming.icp_average - nevents;
+    if (incoming_icp_interval < Config.comm_incoming.icp_min_poll)
+	incoming_icp_interval = Config.comm_incoming.icp_min_poll;
+    if (incoming_icp_interval > MAX_INCOMING_INTERVAL)
+	incoming_icp_interval = MAX_INCOMING_INTERVAL;
+    if (nevents > INCOMING_ICP_MAX)
+	nevents = INCOMING_ICP_MAX;
+    statHistCount(&statCounter.comm_icp_incoming, nevents);
+}
+
+static void
+comm_poll_http_incoming(void)
+{
+    int nfds = 0;
+    int fds[MAXHTTPPORTS];
+    int j;
+    int nevents;
+    http_io_events = 0;
+    for (j = 0; j < NHttpSockets; j++) {
+	if (HttpSockets[j] < 0)
+	    continue;
+	if (commDeferRead(HttpSockets[j]))
+	    continue;
+	fds[nfds++] = HttpSockets[j];
+    }
+    nevents = comm_check_incoming_poll_handlers(nfds, fds);
+    incoming_http_interval = incoming_http_interval
+	+ Config.comm_incoming.http_average - nevents;
+    if (incoming_http_interval < Config.comm_incoming.http_min_poll)
+	incoming_http_interval = Config.comm_incoming.http_min_poll;
+    if (incoming_http_interval > MAX_INCOMING_INTERVAL)
+	incoming_http_interval = MAX_INCOMING_INTERVAL;
+    if (nevents > INCOMING_HTTP_MAX)
+	nevents = INCOMING_HTTP_MAX;
+    statHistCount(&statCounter.comm_http_incoming, nevents);
+}
+
+/* poll all sockets; call handlers for those that are ready. */
+comm_err_t
+comm_select(int msec)
+{
+    struct pollfd pfds[SQUID_MAXFD];
+#if DELAY_POOLS
+    fd_set slowfds;
+#endif
+    PF *hdl = NULL;
+    int fd;
+    int maxfd;
+    unsigned long nfds;
+    unsigned long npending;
+    int num;
+    int callicp = 0, callhttp = 0;
+    int calldns = 0;
+    static time_t last_timeout = 0;
+    double timeout = current_dtime + (msec / 1000.0);
+    do {
+	double start;
+	getCurrentTime();
+	start = current_dtime;
+	/* Handle any fs callbacks that need doing */
+	storeDirCallback();
+#if DELAY_POOLS
+	FD_ZERO(&slowfds);
+#endif
+	if (commCheckICPIncoming)
+	    comm_poll_icp_incoming();
+	if (commCheckDNSIncoming)
+	    comm_poll_dns_incoming();
+	if (commCheckHTTPIncoming)
+	    comm_poll_http_incoming();
+	PROF_start(comm_poll_prep_pfds);
+	callicp = calldns = callhttp = 0;
+	nfds = 0;
+	npending = 0;
+	maxfd = Biggest_FD + 1;
+	for (int i = 0; i < maxfd; i++) {
+	    int events;
+	    events = 0;
+	    /* Check each open socket for a handler. */
+	    if (fd_table[i].read_handler) {
+		switch (commDeferRead(i)) {
+		case 0:
+		    events |= POLLRDNORM;
+		    break;
+		case 1:
+		    break;
+#if DELAY_POOLS
+		case -1:
+		    events |= POLLRDNORM;
+		    FD_SET(i, &slowfds);
+		    break;
+#endif
+		default:
+		    fatalf("bad return value from commDeferRead(FD %d)\n", i);
+		}
+	    }
+	    if (fd_table[i].write_handler)
+		events |= POLLWRNORM;
+	    if (events) {
+		pfds[nfds].fd = i;
+		pfds[nfds].events = events;
+		pfds[nfds].revents = 0;
+		nfds++;
+		if ((events & POLLRDNORM) && fd_table[i].flags.read_pending)
+		    npending++;
+	    }
+	}
+	PROF_stop(comm_poll_prep_pfds);
+	if (nfds == 0) {
+	    assert(shutting_down);
+	    return COMM_SHUTDOWN;
+	}
+	if (npending)
+	    msec = 0;
+	if (msec > MAX_POLL_TIME)
+	    msec = MAX_POLL_TIME;
+	for (;;) {
+	    PROF_start(comm_poll_normal);
+	    statCounter.syscalls.polls++;
+	    num = poll(pfds, nfds, msec);
+	    statCounter.select_loops++;
+	    PROF_stop(comm_poll_normal);
+	    if (num >= 0 || npending >= 0)
+		break;
+	    if (ignoreErrno(errno))
+		continue;
+	    debug(5, 0) ("comm_poll: poll failure: %s\n", xstrerror());
+	    assert(errno != EINVAL);
+	    return COMM_ERROR;
+	    /* NOTREACHED */
+	}
+	debug(5, num ? 5 : 8) ("comm_poll: %d+%ld FDs ready\n", num, npending);
+	statHistCount(&statCounter.select_fds_hist, num);
+	/* Check timeout handlers ONCE each second. */
+	if (squid_curtime > last_timeout) {
+	    last_timeout = squid_curtime;
+	    checkTimeouts();
+	}
+	if (num == 0 && npending == 0)
+	    continue;
+	/* scan each socket but the accept socket. Poll this 
+	 * more frequently to minimize losses due to the 5 connect 
+	 * limit in SunOS */
+	PROF_start(comm_handle_ready_fd);
+	for (size_t loopIndex = 0; loopIndex < nfds; loopIndex++) {
+	    fde *F;
+	    int revents = pfds[loopIndex].revents;
+	    fd = pfds[loopIndex].fd;
+	    if (fd == -1)
+		continue;
+	    if (fd_table[fd].flags.read_pending)
+		revents |= POLLIN;
+	    if (revents == 0)
+		continue;
+	    if (fdIsIcp(fd)) {
+		callicp = 1;
+		continue;
+	    }
+	    if (fdIsDns(fd)) {
+		calldns = 1;
+		continue;
+	    }
+	    if (fdIsHttp(fd)) {
+		callhttp = 1;
+		continue;
+	    }
+	    F = &fd_table[fd];
+	    if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
+		debug(5, 6) ("comm_poll: FD %d ready for reading\n", fd);
+		if (NULL == (hdl = F->read_handler))
+		    (void) 0;
+#if DELAY_POOLS
+		else if (FD_ISSET(fd, &slowfds))
+		    commAddSlowFd(fd);
+#endif
+		else {
+		    PROF_start(comm_read_handler);
+		    F->read_handler = NULL;
+		    hdl(fd, F->read_data);
+		    PROF_stop(comm_read_handler);
+		    statCounter.select_fds++;
+		    if (commCheckICPIncoming)
+			comm_poll_icp_incoming();
+		    if (commCheckDNSIncoming)
+			comm_poll_dns_incoming();
+		    if (commCheckHTTPIncoming)
+			comm_poll_http_incoming();
+		}
+	    }
+	    if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
+		debug(5, 5) ("comm_poll: FD %d ready for writing\n", fd);
+		if ((hdl = F->write_handler)) {
+		    PROF_start(comm_write_handler);
+		    F->write_handler = NULL;
+		    hdl(fd, F->write_data);
+		    PROF_stop(comm_write_handler);
+		    statCounter.select_fds++;
+		    if (commCheckICPIncoming)
+			comm_poll_icp_incoming();
+		    if (commCheckDNSIncoming)
+			comm_poll_dns_incoming();
+		    if (commCheckHTTPIncoming)
+			comm_poll_http_incoming();
+		}
+	    }
+	    if (revents & POLLNVAL) {
+		close_handler *ch;
+		debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd);
+		debug(5, 0) ("FD %d is a %s\n", fd, fdTypeStr[F->type]);
+		debug(5, 0) ("--> %s\n", F->desc);
+		debug(5, 0) ("tmout:%p read:%p write:%p\n",
+		    F->timeout_handler,
+		    F->read_handler,
+		    F->write_handler);
+		for (ch = F->closeHandler; ch; ch = ch->next)
+		    debug(5, 0) (" close handler: %p\n", ch->handler);
+		if (F->closeHandler) {
+		    commCallCloseHandlers(fd);
+		} else if (F->timeout_handler) {
+		    debug(5, 0) ("comm_poll: Calling Timeout Handler\n");
+		    F->timeout_handler(fd, F->timeout_data);
+		}
+		F->closeHandler = NULL;
+		F->timeout_handler = NULL;
+		F->read_handler = NULL;
+		F->write_handler = NULL;
+		if (F->flags.open)
+		    fd_close(fd);
+	    }
+	}
+	PROF_stop(comm_handle_ready_fd);
+	if (callicp)
+	    comm_poll_icp_incoming();
+	if (calldns)
+	    comm_poll_dns_incoming();
+	if (callhttp)
+	    comm_poll_http_incoming();
+#if DELAY_POOLS
+	while ((fd = commGetSlowFd()) != -1) {
+	    fde *F = &fd_table[fd];
+	    debug(5, 6) ("comm_select: slow FD %d selected for reading\n", fd);
+	    if ((hdl = F->read_handler)) {
+		F->read_handler = NULL;
+		hdl(fd, F->read_data);
+		statCounter.select_fds++;
+		if (commCheckICPIncoming)
+		    comm_poll_icp_incoming();
+		if (commCheckDNSIncoming)
+		    comm_poll_dns_incoming();
+		if (commCheckHTTPIncoming)
+		    comm_poll_http_incoming();
+	    }
+	}
+#endif
+	getCurrentTime();
+	statCounter.select_time += (current_dtime - start);
+	return COMM_OK;
+    }
+    while (timeout > current_dtime);
+    debug(5, 8) ("comm_poll: time out: %ld.\n", (long int) squid_curtime);
+    return COMM_TIMEOUT;
+}
+
+
+static void
+comm_poll_dns_incoming(void)
+{
+    int nfds = 0;
+    int fds[2];
+    int nevents;
+    dns_io_events = 0;
+    if (DnsSocket < 0)
+	return;
+    fds[nfds++] = DnsSocket;
+    nevents = comm_check_incoming_poll_handlers(nfds, fds);
+    if (nevents < 0)
+	return;
+    incoming_dns_interval += Config.comm_incoming.dns_average - nevents;
+    if (incoming_dns_interval < Config.comm_incoming.dns_min_poll)
+	incoming_dns_interval = Config.comm_incoming.dns_min_poll;
+    if (incoming_dns_interval > MAX_INCOMING_INTERVAL)
+	incoming_dns_interval = MAX_INCOMING_INTERVAL;
+    if (nevents > INCOMING_DNS_MAX)
+	nevents = INCOMING_DNS_MAX;
+    statHistCount(&statCounter.comm_dns_incoming, nevents);
+}
+
+void
+comm_select_init(void)
+{
+    cachemgrRegister("comm_incoming",
+	"comm_incoming() stats",
+	commIncomingStats, 0, 1);
+    FD_ZERO(&global_readfds);
+    FD_ZERO(&global_writefds);
+    nreadfds = nwritefds = 0;
+}
+
+
+static void
+commIncomingStats(StoreEntry * sentry)
+{
+    StatCounters *f = &statCounter;
+    storeAppendPrintf(sentry, "Current incoming_icp_interval: %d\n",
+	incoming_icp_interval >> INCOMING_FACTOR);
+    storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n",
+	incoming_dns_interval >> INCOMING_FACTOR);
+    storeAppendPrintf(sentry, "Current incoming_http_interval: %d\n",
+	incoming_http_interval >> INCOMING_FACTOR);
+    storeAppendPrintf(sentry, "\n");
+    storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n");
+    storeAppendPrintf(sentry, "ICP Messages handled per comm_poll_icp_incoming() call:\n");
+    statHistDump(&f->comm_icp_incoming, sentry, statHistIntDumper);
+    storeAppendPrintf(sentry, "DNS Messages handled per comm_poll_dns_incoming() call:\n");
+    statHistDump(&f->comm_dns_incoming, sentry, statHistIntDumper);
+    storeAppendPrintf(sentry, "HTTP Messages handled per comm_poll_http_incoming() call:\n");
+    statHistDump(&f->comm_http_incoming, sentry, statHistIntDumper);
+}
+
+void
+commUpdateReadBits(int fd, PF * handler)
+{
+    if (handler && !FD_ISSET(fd, &global_readfds)) {
+	FD_SET(fd, &global_readfds);
+	nreadfds++;
+    } else if (!handler && FD_ISSET(fd, &global_readfds)) {
+	FD_CLR(fd, &global_readfds);
+	nreadfds--;
+    }
+}
+
+void
+commUpdateWriteBits(int fd, PF * handler)
+{
+    if (handler && !FD_ISSET(fd, &global_writefds)) {
+	FD_SET(fd, &global_writefds);
+	nwritefds++;
+    } else if (!handler && FD_ISSET(fd, &global_writefds)) {
+	FD_CLR(fd, &global_writefds);
+	nwritefds--;
+    }
+}
+
+/* Called by async-io or diskd to speed up the polling */
+void
+comm_quick_poll_required(void)
+{
+    MAX_POLL_TIME = 10;
+}
+
+#endif /* USE_POLL */
--- squid/src/comm_select.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,736 +0,0 @@
-
-/*
- * $Id: comm_select.c,v 1.15.2.1 2002/10/04 07:07:24 rbcollins Exp $
- *
- * DEBUG: section 5     Socket Functions
- *
- * 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"
-#include "Store.h"
-
-#ifdef USE_SELECT
-
-static int MAX_POLL_TIME = 1000;	/* see also comm_quick_poll_required() */
-
-#ifndef        howmany
-#define howmany(x, y)   (((x)+((y)-1))/(y))
-#endif
-#ifndef        NBBY
-#define        NBBY    8
-#endif
-#define FD_MASK_BYTES sizeof(fd_mask)
-#define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
-
-/* STATIC */
-static int examine_select(fd_set *, fd_set *);
-static int fdIsHttp(int fd);
-static int fdIsIcp(int fd);
-static int fdIsDns(int fd);
-static OBJH commIncomingStats;
-static int comm_check_incoming_select_handlers(int nfds, int *fds);
-static void comm_select_dns_incoming(void);
-static void commUpdateReadBits(int fd, PF * handler);
-static void commUpdateWriteBits(int fd, PF * handler);
-
-
-static struct timeval zero_tv;
-static fd_set global_readfds;
-static fd_set global_writefds;
-static int nreadfds;
-static int nwritefds;
-
-/*
- * Automatic tuning for incoming requests:
- *
- * INCOMING sockets are the ICP and HTTP ports.  We need to check these
- * fairly regularly, but how often?  When the load increases, we
- * want to check the incoming sockets more often.  If we have a lot
- * of incoming ICP, then we need to check these sockets more than
- * if we just have HTTP.
- *
- * The variables 'incoming_icp_interval' and 'incoming_http_interval' 
- * determine how many normal I/O events to process before checking
- * incoming sockets again.  Note we store the incoming_interval
- * multipled by a factor of (2^INCOMING_FACTOR) to have some
- * pseudo-floating point precision.
- *
- * The variable 'icp_io_events' and 'http_io_events' counts how many normal
- * I/O events have been processed since the last check on the incoming
- * sockets.  When io_events > incoming_interval, its time to check incoming
- * sockets.
- *
- * Every time we check incoming sockets, we count how many new messages
- * or connections were processed.  This is used to adjust the
- * incoming_interval for the next iteration.  The new incoming_interval
- * is calculated as the current incoming_interval plus what we would
- * like to see as an average number of events minus the number of
- * events just processed.
- *
- *  incoming_interval = incoming_interval + target_average - number_of_events_processed
- *
- * There are separate incoming_interval counters for both HTTP and ICP events
- * 
- * You can see the current values of the incoming_interval's, as well as
- * a histogram of 'incoming_events' by asking the cache manager
- * for 'comm_incoming', e.g.:
- *
- *      % ./client mgr:comm_incoming
- *
- * Caveats:
- *
- *      - We have MAX_INCOMING_INTEGER as a magic upper limit on
- *        incoming_interval for both types of sockets.  At the
- *        largest value the cache will effectively be idling.
- *
- *      - The higher the INCOMING_FACTOR, the slower the algorithm will
- *        respond to load spikes/increases/decreases in demand. A value
- *        between 3 and 8 is recommended.
- */
-
-#define MAX_INCOMING_INTEGER 256
-#define INCOMING_FACTOR 5
-#define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR)
-static int icp_io_events = 0;
-static int dns_io_events = 0;
-static int http_io_events = 0;
-static int incoming_icp_interval = 16 << INCOMING_FACTOR;
-static int incoming_dns_interval = 16 << INCOMING_FACTOR;
-static int incoming_http_interval = 16 << INCOMING_FACTOR;
-#define commCheckICPIncoming (++icp_io_events > (incoming_icp_interval>> INCOMING_FACTOR))
-#define commCheckDNSIncoming (++dns_io_events > (incoming_dns_interval>> INCOMING_FACTOR))
-#define commCheckHTTPIncoming (++http_io_events > (incoming_http_interval>> INCOMING_FACTOR))
-
-void
-commSetSelect(int fd, unsigned int type, PF * handler, void *client_data,
-    time_t timeout)
-{
-    fde *F = &fd_table[fd];
-    assert(fd >= 0);
-    assert(F->flags.open);
-    debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type);
-    if (type & COMM_SELECT_READ) {
-	F->read_handler = handler;
-	F->read_data = client_data;
-	commUpdateReadBits(fd, handler);
-    }
-    if (type & COMM_SELECT_WRITE) {
-	F->write_handler = handler;
-	F->write_data = client_data;
-	commUpdateWriteBits(fd, handler);
-    }
-    if (timeout)
-	F->timeout = squid_curtime + timeout;
-}
-
-
-static int
-fdIsIcp(int fd)
-{
-    if (fd == theInIcpConnection)
-	return 1;
-    if (fd == theOutIcpConnection)
-	return 1;
-    return 0;
-}
-
-static int
-fdIsDns(int fd)
-{
-    if (fd == DnsSocket)
-	return 1;
-    return 0;
-}
-
-static int
-fdIsHttp(int fd)
-{
-    int j;
-    for (j = 0; j < NHttpSockets; j++) {
-	if (fd == HttpSockets[j])
-	    return 1;
-    }
-    return 0;
-}
-
-#if DELAY_POOLS
-static int slowfdcnt = 0;
-static int slowfdarr[SQUID_MAXFD];
-
-static void
-commAddSlowFd(int fd)
-{
-    assert(slowfdcnt < SQUID_MAXFD);
-    slowfdarr[slowfdcnt++] = fd;
-}
-
-static int
-commGetSlowFd(void)
-{
-    int whichfd, retfd;
-
-    if (!slowfdcnt)
-	return -1;
-    whichfd = squid_random() % slowfdcnt;
-    retfd = slowfdarr[whichfd];
-    slowfdarr[whichfd] = slowfdarr[--slowfdcnt];
-    return retfd;
-}
-#endif
-
-static int
-comm_check_incoming_select_handlers(int nfds, int *fds)
-{
-    int i;
-    int fd;
-    int maxfd = 0;
-    PF *hdl = NULL;
-    fd_set read_mask;
-    fd_set write_mask;
-    FD_ZERO(&read_mask);
-    FD_ZERO(&write_mask);
-    incoming_sockets_accepted = 0;
-    for (i = 0; i < nfds; i++) {
-	fd = fds[i];
-	if (fd_table[fd].read_handler) {
-	    FD_SET(fd, &read_mask);
-	    if (fd > maxfd)
-		maxfd = fd;
-	}
-	if (fd_table[fd].write_handler) {
-	    FD_SET(fd, &write_mask);
-	    if (fd > maxfd)
-		maxfd = fd;
-	}
-    }
-    if (maxfd++ == 0)
-	return -1;
-    getCurrentTime();
-    statCounter.syscalls.selects++;
-    if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) < 1)
-	return incoming_sockets_accepted;
-    for (i = 0; i < nfds; i++) {
-	fd = fds[i];
-	if (FD_ISSET(fd, &read_mask)) {
-	    if ((hdl = fd_table[fd].read_handler) != NULL) {
-		fd_table[fd].read_handler = NULL;
-		commUpdateReadBits(fd, NULL);
-		hdl(fd, fd_table[fd].read_data);
-	    } else {
-		debug(5, 1) ("comm_select_incoming: FD %d NULL read handler\n",
-		    fd);
-	    }
-	}
-	if (FD_ISSET(fd, &write_mask)) {
-	    if ((hdl = fd_table[fd].write_handler) != NULL) {
-		fd_table[fd].write_handler = NULL;
-		commUpdateWriteBits(fd, NULL);
-		hdl(fd, fd_table[fd].write_data);
-	    } else {
-		debug(5, 1) ("comm_select_incoming: FD %d NULL write handler\n",
-		    fd);
-	    }
-	}
-    }
-    return incoming_sockets_accepted;
-}
-
-static void
-comm_select_icp_incoming(void)
-{
-    int nfds = 0;
-    int fds[2];
-    int nevents;
-    icp_io_events = 0;
-    if (theInIcpConnection >= 0)
-	fds[nfds++] = theInIcpConnection;
-    if (theInIcpConnection != theOutIcpConnection)
-	if (theOutIcpConnection >= 0)
-	    fds[nfds++] = theOutIcpConnection;
-    if (nfds == 0)
-	return;
-    nevents = comm_check_incoming_select_handlers(nfds, fds);
-    incoming_icp_interval += Config.comm_incoming.icp_average - nevents;
-    if (incoming_icp_interval < 0)
-	incoming_icp_interval = 0;
-    if (incoming_icp_interval > MAX_INCOMING_INTERVAL)
-	incoming_icp_interval = MAX_INCOMING_INTERVAL;
-    if (nevents > INCOMING_ICP_MAX)
-	nevents = INCOMING_ICP_MAX;
-    statHistCount(&statCounter.comm_icp_incoming, nevents);
-}
-
-static void
-comm_select_http_incoming(void)
-{
-    int nfds = 0;
-    int fds[MAXHTTPPORTS];
-    int j;
-    int nevents;
-    http_io_events = 0;
-    for (j = 0; j < NHttpSockets; j++) {
-	if (HttpSockets[j] < 0)
-	    continue;
-	if (commDeferRead(HttpSockets[j]))
-	    continue;
-	fds[nfds++] = HttpSockets[j];
-    }
-    nevents = comm_check_incoming_select_handlers(nfds, fds);
-    incoming_http_interval += Config.comm_incoming.http_average - nevents;
-    if (incoming_http_interval < 0)
-	incoming_http_interval = 0;
-    if (incoming_http_interval > MAX_INCOMING_INTERVAL)
-	incoming_http_interval = MAX_INCOMING_INTERVAL;
-    if (nevents > INCOMING_HTTP_MAX)
-	nevents = INCOMING_HTTP_MAX;
-    statHistCount(&statCounter.comm_http_incoming, nevents);
-}
-
-#define DEBUG_FDBITS 0
-/* Select on all sockets; call handlers for those that are ready. */
-comm_err_t
-comm_select(int msec)
-{
-    fd_set readfds;
-    fd_set pendingfds;
-    fd_set writefds;
-#if DELAY_POOLS
-    fd_set slowfds;
-#endif
-    PF *hdl = NULL;
-    int fd;
-    int maxfd;
-    int num;
-    int pending;
-    int callicp = 0, callhttp = 0;
-    int calldns = 0;
-    int maxindex;
-    int k;
-    int j;
-#if DEBUG_FDBITS
-    int i;
-#endif
-    fd_mask *fdsp;
-    fd_mask *pfdsp;
-    fd_mask tmask;
-    static time_t last_timeout = 0;
-    struct timeval poll_time;
-    double timeout = current_dtime + (msec / 1000.0);
-    fde *F;
-    do {
-	getCurrentTime();
-#if DELAY_POOLS
-	FD_ZERO(&slowfds);
-#endif
-	/* Handle any fs callbacks that need doing */
-	storeDirCallback();
-	if (commCheckICPIncoming)
-	    comm_select_icp_incoming();
-	if (commCheckDNSIncoming)
-	    comm_select_dns_incoming();
-	if (commCheckHTTPIncoming)
-	    comm_select_http_incoming();
-	callicp = calldns = callhttp = 0;
-	maxfd = Biggest_FD + 1;
-	xmemcpy(&readfds, &global_readfds,
-	    howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES);
-	xmemcpy(&writefds, &global_writefds,
-	    howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES);
-	/* remove stalled FDs, and deal with pending descriptors */
-	pending = 0;
-	FD_ZERO(&pendingfds);
-	maxindex = howmany(maxfd, FD_MASK_BITS);
-	fdsp = (fd_mask *) & readfds;
-	for (j = 0; j < maxindex; j++) {
-	    if ((tmask = fdsp[j]) == 0)
-		continue;	/* no bits here */
-	    for (k = 0; k < FD_MASK_BITS; k++) {
-		if (!EBIT_TEST(tmask, k))
-		    continue;
-		/* Found a set bit */
-		fd = (j * FD_MASK_BITS) + k;
-		switch (commDeferRead(fd)) {
-		case 0:
-		    break;
-		case 1:
-		    FD_CLR(fd, &readfds);
-		    break;
-#if DELAY_POOLS
-		case -1:
-		    FD_SET(fd, &slowfds);
-		    break;
-#endif
-		default:
-		    fatalf("bad return value from commDeferRead(FD %d)\n", fd);
-		}
-		if (FD_ISSET(fd, &readfds) && fd_table[fd].flags.read_pending) {
-		    FD_SET(fd, &pendingfds);
-		    pending++;
-		}
-	    }
-	}
-#if DEBUG_FDBITS
-	for (i = 0; i < maxfd; i++) {
-	    /* Check each open socket for a handler. */
-#if DELAY_POOLS
-	    if (fd_table[i].read_handler && commDeferRead(i) != 1) {
-#else
-	    if (fd_table[i].read_handler && !commDeferRead(i)) {
-#endif
-		assert(FD_ISSET(i, &readfds));
-	    }
-	    if (fd_table[i].write_handler) {
-		assert(FD_ISSET(i, &writefds));
-	    }
-	}
-#endif
-	if (nreadfds + nwritefds == 0) {
-	    assert(shutting_down);
-	    return COMM_SHUTDOWN;
-	}
-	if (msec > MAX_POLL_TIME)
-	    msec = MAX_POLL_TIME;
-#ifdef _SQUID_OS2_
-	if (msec < 0)
-	    msec = MAX_POLL_TIME;
-#endif
-	if (pending)
-	    msec = 0;
-	for (;;) {
-	    poll_time.tv_sec = msec / 1000;
-	    poll_time.tv_usec = (msec % 1000) * 1000;
-	    statCounter.syscalls.selects++;
-	    num = select(maxfd, &readfds, &writefds, NULL, &poll_time);
-	    statCounter.select_loops++;
-	    if (num >= 0 || pending > 0)
-		break;
-	    if (ignoreErrno(errno))
-		break;
-	    debug(50, 0) ("comm_select: select failure: %s\n",
-		xstrerror());
-	    examine_select(&readfds, &writefds);
-	    return COMM_ERROR;
-	    /* NOTREACHED */
-	}
-	if (num < 0 && !pending)
-	    continue;
-	debug(5, num ? 5 : 8) ("comm_select: %d+%d FDs ready at %d\n",
-	    num, pending, (int) squid_curtime);
-	statHistCount(&statCounter.select_fds_hist, num);
-	/* Check lifetime and timeout handlers ONCE each second.
-	 * Replaces brain-dead check every time through the loop! */
-	if (squid_curtime > last_timeout) {
-	    last_timeout = squid_curtime;
-	    checkTimeouts();
-	}
-	if (num == 0 && pending == 0)
-	    continue;
-	/* Scan return fd masks for ready descriptors */
-	fdsp = (fd_mask *) & readfds;
-	pfdsp = (fd_mask *) & pendingfds;
-	maxindex = howmany(maxfd, FD_MASK_BITS);
-	for (j = 0; j < maxindex; j++) {
-	    if ((tmask = (fdsp[j] | pfdsp[j])) == 0)
-		continue;	/* no bits here */
-	    for (k = 0; k < FD_MASK_BITS; k++) {
-		if (tmask == 0)
-		    break;	/* no more bits left */
-		if (!EBIT_TEST(tmask, k))
-		    continue;
-		/* Found a set bit */
-		fd = (j * FD_MASK_BITS) + k;
-		EBIT_CLR(tmask, k);	/* this will be done */
-#if DEBUG_FDBITS
-		debug(5, 9) ("FD %d bit set for reading\n", fd);
-		assert(FD_ISSET(fd, &readfds));
-#endif
-		if (fdIsIcp(fd)) {
-		    callicp = 1;
-		    continue;
-		}
-		if (fdIsDns(fd)) {
-		    calldns = 1;
-		    continue;
-		}
-		if (fdIsHttp(fd)) {
-		    callhttp = 1;
-		    continue;
-		}
-		F = &fd_table[fd];
-		debug(5, 6) ("comm_select: FD %d ready for reading\n", fd);
-		if (NULL == (hdl = F->read_handler))
-		    (void) 0;
-#if DELAY_POOLS
-		else if (FD_ISSET(fd, &slowfds))
-		    commAddSlowFd(fd);
-#endif
-		else {
-		    F->read_handler = NULL;
-		    commUpdateReadBits(fd, NULL);
-		    hdl(fd, F->read_data);
-		    statCounter.select_fds++;
-		    if (commCheckICPIncoming)
-			comm_select_icp_incoming();
-		    if (commCheckDNSIncoming)
-			comm_select_dns_incoming();
-		    if (commCheckHTTPIncoming)
-			comm_select_http_incoming();
-		}
-	    }
-	}
-	fdsp = (fd_mask *) & writefds;
-	for (j = 0; j < maxindex; j++) {
-	    if ((tmask = fdsp[j]) == 0)
-		continue;	/* no bits here */
-	    for (k = 0; k < FD_MASK_BITS; k++) {
-		if (tmask == 0)
-		    break;	/* no more bits left */
-		if (!EBIT_TEST(tmask, k))
-		    continue;
-		/* Found a set bit */
-		fd = (j * FD_MASK_BITS) + k;
-		EBIT_CLR(tmask, k);	/* this will be done */
-#if DEBUG_FDBITS
-		debug(5, 9) ("FD %d bit set for writing\n", fd);
-		assert(FD_ISSET(fd, &writefds));
-#endif
-		if (fdIsIcp(fd)) {
-		    callicp = 1;
-		    continue;
-		}
-		if (fdIsDns(fd)) {
-		    calldns = 1;
-		    continue;
-		}
-		if (fdIsHttp(fd)) {
-		    callhttp = 1;
-		    continue;
-		}
-		F = &fd_table[fd];
-		debug(5, 5) ("comm_select: FD %d ready for writing\n", fd);
-		if ((hdl = F->write_handler)) {
-		    F->write_handler = NULL;
-		    commUpdateWriteBits(fd, NULL);
-		    hdl(fd, F->write_data);
-		    statCounter.select_fds++;
-		    if (commCheckICPIncoming)
-			comm_select_icp_incoming();
-		    if (commCheckDNSIncoming)
-			comm_select_dns_incoming();
-		    if (commCheckHTTPIncoming)
-			comm_select_http_incoming();
-		}
-	    }
-	}
-	if (callicp)
-	    comm_select_icp_incoming();
-	if (calldns)
-	    comm_select_dns_incoming();
-	if (callhttp)
-	    comm_select_http_incoming();
-#if DELAY_POOLS
-	while ((fd = commGetSlowFd()) != -1) {
-	    F = &fd_table[fd];
-	    debug(5, 6) ("comm_select: slow FD %d selected for reading\n", fd);
-	    if ((hdl = F->read_handler)) {
-		F->read_handler = NULL;
-		commUpdateReadBits(fd, NULL);
-		hdl(fd, F->read_data);
-		statCounter.select_fds++;
-		if (commCheckICPIncoming)
-		    comm_select_icp_incoming();
-		if (commCheckDNSIncoming)
-		    comm_select_dns_incoming();
-		if (commCheckHTTPIncoming)
-		    comm_select_http_incoming();
-	    }
-	}
-#endif
-	return COMM_OK;
-    }
-    while (timeout > current_dtime);
-    debug(5, 8) ("comm_select: time out: %d\n", (int) squid_curtime);
-    return COMM_TIMEOUT;
-}
-
-static void
-comm_select_dns_incoming(void)
-{
-    int nfds = 0;
-    int fds[2];
-    int nevents;
-    dns_io_events = 0;
-    if (DnsSocket < 0)
-	return;
-    fds[nfds++] = DnsSocket;
-    nevents = comm_check_incoming_select_handlers(nfds, fds);
-    if (nevents < 0)
-	return;
-    incoming_dns_interval += Config.comm_incoming.dns_average - nevents;
-    if (incoming_dns_interval < Config.comm_incoming.dns_min_poll)
-	incoming_dns_interval = Config.comm_incoming.dns_min_poll;
-    if (incoming_dns_interval > MAX_INCOMING_INTERVAL)
-	incoming_dns_interval = MAX_INCOMING_INTERVAL;
-    if (nevents > INCOMING_DNS_MAX)
-	nevents = INCOMING_DNS_MAX;
-    statHistCount(&statCounter.comm_dns_incoming, nevents);
-}
-
-void
-comm_select_init(void)
-{
-    zero_tv.tv_sec = 0;
-    zero_tv.tv_usec = 0;
-    cachemgrRegister("comm_incoming",
-	"comm_incoming() stats",
-	commIncomingStats, 0, 1);
-    FD_ZERO(&global_readfds);
-    FD_ZERO(&global_writefds);
-    nreadfds = nwritefds = 0;
-}
-
-/*
- * examine_select - debug routine.
- *
- * I spend the day chasing this core dump that occurs when both the client
- * and the server side of a cache fetch simultaneoulsy abort the
- * connection.  While I haven't really studied the code to figure out how
- * it happens, the snippet below may prevent the cache from exitting:
- * 
- * Call this from where the select loop fails.
- */
-static int
-examine_select(fd_set * readfds, fd_set * writefds)
-{
-    int fd = 0;
-    fd_set read_x;
-    fd_set write_x;
-    struct timeval tv;
-    close_handler *ch = NULL;
-    fde *F = NULL;
-    struct stat sb;
-    debug(5, 0) ("examine_select: Examining open file descriptors...\n");
-    for (fd = 0; fd < Squid_MaxFD; fd++) {
-	FD_ZERO(&read_x);
-	FD_ZERO(&write_x);
-	tv.tv_sec = tv.tv_usec = 0;
-	if (FD_ISSET(fd, readfds))
-	    FD_SET(fd, &read_x);
-	else if (FD_ISSET(fd, writefds))
-	    FD_SET(fd, &write_x);
-	else
-	    continue;
-	statCounter.syscalls.selects++;
-	errno = 0;
-	if (!fstat(fd, &sb)) {
-	    debug(5, 5) ("FD %d is valid.\n", fd);
-	    continue;
-	}
-	F = &fd_table[fd];
-	debug(5, 0) ("FD %d: %s\n", fd, xstrerror());
-	debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd);
-	debug(5, 0) ("FD %d is a %s called '%s'\n",
-	    fd,
-	    fdTypeStr[F->type],
-	    F->desc);
-	debug(5, 0) ("tmout:%p read:%p write:%p\n",
-	    F->timeout_handler,
-	    F->read_handler,
-	    F->write_handler);
-	for (ch = F->closeHandler; ch; ch = ch->next)
-	    debug(5, 0) (" close handler: %p\n", ch->handler);
-	if (F->closeHandler) {
-	    commCallCloseHandlers(fd);
-	} else if (F->timeout_handler) {
-	    debug(5, 0) ("examine_select: Calling Timeout Handler\n");
-	    F->timeout_handler(fd, F->timeout_data);
-	}
-	F->closeHandler = NULL;
-	F->timeout_handler = NULL;
-	F->read_handler = NULL;
-	F->write_handler = NULL;
-	FD_CLR(fd, readfds);
-	FD_CLR(fd, writefds);
-    }
-    return 0;
-}
-
-
-static void
-commIncomingStats(StoreEntry * sentry)
-{
-    StatCounters *f = &statCounter;
-    storeAppendPrintf(sentry, "Current incoming_icp_interval: %d\n",
-	incoming_icp_interval >> INCOMING_FACTOR);
-    storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n",
-	incoming_dns_interval >> INCOMING_FACTOR);
-    storeAppendPrintf(sentry, "Current incoming_http_interval: %d\n",
-	incoming_http_interval >> INCOMING_FACTOR);
-    storeAppendPrintf(sentry, "\n");
-    storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n");
-    storeAppendPrintf(sentry, "ICP Messages handled per comm_select_icp_incoming() call:\n");
-    statHistDump(&f->comm_icp_incoming, sentry, statHistIntDumper);
-    storeAppendPrintf(sentry, "DNS Messages handled per comm_select_dns_incoming() call:\n");
-    statHistDump(&f->comm_dns_incoming, sentry, statHistIntDumper);
-    storeAppendPrintf(sentry, "HTTP Messages handled per comm_select_http_incoming() call:\n");
-    statHistDump(&f->comm_http_incoming, sentry, statHistIntDumper);
-}
-
-void
-commUpdateReadBits(int fd, PF * handler)
-{
-    if (handler && !FD_ISSET(fd, &global_readfds)) {
-	FD_SET(fd, &global_readfds);
-	nreadfds++;
-    } else if (!handler && FD_ISSET(fd, &global_readfds)) {
-	FD_CLR(fd, &global_readfds);
-	nreadfds--;
-    }
-}
-
-void
-commUpdateWriteBits(int fd, PF * handler)
-{
-    if (handler && !FD_ISSET(fd, &global_writefds)) {
-	FD_SET(fd, &global_writefds);
-	nwritefds++;
-    } else if (!handler && FD_ISSET(fd, &global_writefds)) {
-	FD_CLR(fd, &global_writefds);
-	nwritefds--;
-    }
-}
-
-/* Called by async-io or diskd to speed up the polling */
-void
-comm_quick_poll_required(void)
-{
-    MAX_POLL_TIME = 10;
-}
-
-#endif /* USE_SELECT */
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/comm_select.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,736 @@
+
+/*
+ * $Id: comm_select.cc,v 1.1.2.1 2002/10/11 15:40:47 rbcollins Exp $
+ *
+ * DEBUG: section 5     Socket Functions
+ *
+ * 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"
+#include "Store.h"
+
+#ifdef USE_SELECT
+
+static int MAX_POLL_TIME = 1000;	/* see also comm_quick_poll_required() */
+
+#ifndef        howmany
+#define howmany(x, y)   (((x)+((y)-1))/(y))
+#endif
+#ifndef        NBBY
+#define        NBBY    8
+#endif
+#define FD_MASK_BYTES sizeof(fd_mask)
+#define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
+
+/* STATIC */
+static int examine_select(fd_set *, fd_set *);
+static int fdIsHttp(int fd);
+static int fdIsIcp(int fd);
+static int fdIsDns(int fd);
+static OBJH commIncomingStats;
+static int comm_check_incoming_select_handlers(int nfds, int *fds);
+static void comm_select_dns_incoming(void);
+static void commUpdateReadBits(int fd, PF * handler);
+static void commUpdateWriteBits(int fd, PF * handler);
+
+
+static struct timeval zero_tv;
+static fd_set global_readfds;
+static fd_set global_writefds;
+static int nreadfds;
+static int nwritefds;
+
+/*
+ * Automatic tuning for incoming requests:
+ *
+ * INCOMING sockets are the ICP and HTTP ports.  We need to check these
+ * fairly regularly, but how often?  When the load increases, we
+ * want to check the incoming sockets more often.  If we have a lot
+ * of incoming ICP, then we need to check these sockets more than
+ * if we just have HTTP.
+ *
+ * The variables 'incoming_icp_interval' and 'incoming_http_interval' 
+ * determine how many normal I/O events to process before checking
+ * incoming sockets again.  Note we store the incoming_interval
+ * multipled by a factor of (2^INCOMING_FACTOR) to have some
+ * pseudo-floating point precision.
+ *
+ * The variable 'icp_io_events' and 'http_io_events' counts how many normal
+ * I/O events have been processed since the last check on the incoming
+ * sockets.  When io_events > incoming_interval, its time to check incoming
+ * sockets.
+ *
+ * Every time we check incoming sockets, we count how many new messages
+ * or connections were processed.  This is used to adjust the
+ * incoming_interval for the next iteration.  The new incoming_interval
+ * is calculated as the current incoming_interval plus what we would
+ * like to see as an average number of events minus the number of
+ * events just processed.
+ *
+ *  incoming_interval = incoming_interval + target_average - number_of_events_processed
+ *
+ * There are separate incoming_interval counters for both HTTP and ICP events
+ * 
+ * You can see the current values of the incoming_interval's, as well as
+ * a histogram of 'incoming_events' by asking the cache manager
+ * for 'comm_incoming', e.g.:
+ *
+ *      % ./client mgr:comm_incoming
+ *
+ * Caveats:
+ *
+ *      - We have MAX_INCOMING_INTEGER as a magic upper limit on
+ *        incoming_interval for both types of sockets.  At the
+ *        largest value the cache will effectively be idling.
+ *
+ *      - The higher the INCOMING_FACTOR, the slower the algorithm will
+ *        respond to load spikes/increases/decreases in demand. A value
+ *        between 3 and 8 is recommended.
+ */
+
+#define MAX_INCOMING_INTEGER 256
+#define INCOMING_FACTOR 5
+#define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR)
+static int icp_io_events = 0;
+static int dns_io_events = 0;
+static int http_io_events = 0;
+static int incoming_icp_interval = 16 << INCOMING_FACTOR;
+static int incoming_dns_interval = 16 << INCOMING_FACTOR;
+static int incoming_http_interval = 16 << INCOMING_FACTOR;
+#define commCheckICPIncoming (++icp_io_events > (incoming_icp_interval>> INCOMING_FACTOR))
+#define commCheckDNSIncoming (++dns_io_events > (incoming_dns_interval>> INCOMING_FACTOR))
+#define commCheckHTTPIncoming (++http_io_events > (incoming_http_interval>> INCOMING_FACTOR))
+
+void
+commSetSelect(int fd, unsigned int type, PF * handler, void *client_data,
+    time_t timeout)
+{
+    fde *F = &fd_table[fd];
+    assert(fd >= 0);
+    assert(F->flags.open);
+    debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type);
+    if (type & COMM_SELECT_READ) {
+	F->read_handler = handler;
+	F->read_data = client_data;
+	commUpdateReadBits(fd, handler);
+    }
+    if (type & COMM_SELECT_WRITE) {
+	F->write_handler = handler;
+	F->write_data = client_data;
+	commUpdateWriteBits(fd, handler);
+    }
+    if (timeout)
+	F->timeout = squid_curtime + timeout;
+}
+
+
+static int
+fdIsIcp(int fd)
+{
+    if (fd == theInIcpConnection)
+	return 1;
+    if (fd == theOutIcpConnection)
+	return 1;
+    return 0;
+}
+
+static int
+fdIsDns(int fd)
+{
+    if (fd == DnsSocket)
+	return 1;
+    return 0;
+}
+
+static int
+fdIsHttp(int fd)
+{
+    int j;
+    for (j = 0; j < NHttpSockets; j++) {
+	if (fd == HttpSockets[j])
+	    return 1;
+    }
+    return 0;
+}
+
+#if DELAY_POOLS
+static int slowfdcnt = 0;
+static int slowfdarr[SQUID_MAXFD];
+
+static void
+commAddSlowFd(int fd)
+{
+    assert(slowfdcnt < SQUID_MAXFD);
+    slowfdarr[slowfdcnt++] = fd;
+}
+
+static int
+commGetSlowFd(void)
+{
+    int whichfd, retfd;
+
+    if (!slowfdcnt)
+	return -1;
+    whichfd = squid_random() % slowfdcnt;
+    retfd = slowfdarr[whichfd];
+    slowfdarr[whichfd] = slowfdarr[--slowfdcnt];
+    return retfd;
+}
+#endif
+
+static int
+comm_check_incoming_select_handlers(int nfds, int *fds)
+{
+    int i;
+    int fd;
+    int maxfd = 0;
+    PF *hdl = NULL;
+    fd_set read_mask;
+    fd_set write_mask;
+    FD_ZERO(&read_mask);
+    FD_ZERO(&write_mask);
+    incoming_sockets_accepted = 0;
+    for (i = 0; i < nfds; i++) {
+	fd = fds[i];
+	if (fd_table[fd].read_handler) {
+	    FD_SET(fd, &read_mask);
+	    if (fd > maxfd)
+		maxfd = fd;
+	}
+	if (fd_table[fd].write_handler) {
+	    FD_SET(fd, &write_mask);
+	    if (fd > maxfd)
+		maxfd = fd;
+	}
+    }
+    if (maxfd++ == 0)
+	return -1;
+    getCurrentTime();
+    statCounter.syscalls.selects++;
+    if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) < 1)
+	return incoming_sockets_accepted;
+    for (i = 0; i < nfds; i++) {
+	fd = fds[i];
+	if (FD_ISSET(fd, &read_mask)) {
+	    if ((hdl = fd_table[fd].read_handler) != NULL) {
+		fd_table[fd].read_handler = NULL;
+		commUpdateReadBits(fd, NULL);
+		hdl(fd, fd_table[fd].read_data);
+	    } else {
+		debug(5, 1) ("comm_select_incoming: FD %d NULL read handler\n",
+		    fd);
+	    }
+	}
+	if (FD_ISSET(fd, &write_mask)) {
+	    if ((hdl = fd_table[fd].write_handler) != NULL) {
+		fd_table[fd].write_handler = NULL;
+		commUpdateWriteBits(fd, NULL);
+		hdl(fd, fd_table[fd].write_data);
+	    } else {
+		debug(5, 1) ("comm_select_incoming: FD %d NULL write handler\n",
+		    fd);
+	    }
+	}
+    }
+    return incoming_sockets_accepted;
+}
+
+static void
+comm_select_icp_incoming(void)
+{
+    int nfds = 0;
+    int fds[2];
+    int nevents;
+    icp_io_events = 0;
+    if (theInIcpConnection >= 0)
+	fds[nfds++] = theInIcpConnection;
+    if (theInIcpConnection != theOutIcpConnection)
+	if (theOutIcpConnection >= 0)
+	    fds[nfds++] = theOutIcpConnection;
+    if (nfds == 0)
+	return;
+    nevents = comm_check_incoming_select_handlers(nfds, fds);
+    incoming_icp_interval += Config.comm_incoming.icp_average - nevents;
+    if (incoming_icp_interval < 0)
+	incoming_icp_interval = 0;
+    if (incoming_icp_interval > MAX_INCOMING_INTERVAL)
+	incoming_icp_interval = MAX_INCOMING_INTERVAL;
+    if (nevents > INCOMING_ICP_MAX)
+	nevents = INCOMING_ICP_MAX;
+    statHistCount(&statCounter.comm_icp_incoming, nevents);
+}
+
+static void
+comm_select_http_incoming(void)
+{
+    int nfds = 0;
+    int fds[MAXHTTPPORTS];
+    int j;
+    int nevents;
+    http_io_events = 0;
+    for (j = 0; j < NHttpSockets; j++) {
+	if (HttpSockets[j] < 0)
+	    continue;
+	if (commDeferRead(HttpSockets[j]))
+	    continue;
+	fds[nfds++] = HttpSockets[j];
+    }
+    nevents = comm_check_incoming_select_handlers(nfds, fds);
+    incoming_http_interval += Config.comm_incoming.http_average - nevents;
+    if (incoming_http_interval < 0)
+	incoming_http_interval = 0;
+    if (incoming_http_interval > MAX_INCOMING_INTERVAL)
+	incoming_http_interval = MAX_INCOMING_INTERVAL;
+    if (nevents > INCOMING_HTTP_MAX)
+	nevents = INCOMING_HTTP_MAX;
+    statHistCount(&statCounter.comm_http_incoming, nevents);
+}
+
+#define DEBUG_FDBITS 0
+/* Select on all sockets; call handlers for those that are ready. */
+comm_err_t
+comm_select(int msec)
+{
+    fd_set readfds;
+    fd_set pendingfds;
+    fd_set writefds;
+#if DELAY_POOLS
+    fd_set slowfds;
+#endif
+    PF *hdl = NULL;
+    int fd;
+    int maxfd;
+    int num;
+    int pending;
+    int callicp = 0, callhttp = 0;
+    int calldns = 0;
+    int maxindex;
+    int k;
+    int j;
+#if DEBUG_FDBITS
+    int i;
+#endif
+    fd_mask *fdsp;
+    fd_mask *pfdsp;
+    fd_mask tmask;
+    static time_t last_timeout = 0;
+    struct timeval poll_time;
+    double timeout = current_dtime + (msec / 1000.0);
+    fde *F;
+    do {
+	getCurrentTime();
+#if DELAY_POOLS
+	FD_ZERO(&slowfds);
+#endif
+	/* Handle any fs callbacks that need doing */
+	storeDirCallback();
+	if (commCheckICPIncoming)
+	    comm_select_icp_incoming();
+	if (commCheckDNSIncoming)
+	    comm_select_dns_incoming();
+	if (commCheckHTTPIncoming)
+	    comm_select_http_incoming();
+	callicp = calldns = callhttp = 0;
+	maxfd = Biggest_FD + 1;
+	xmemcpy(&readfds, &global_readfds,
+	    howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES);
+	xmemcpy(&writefds, &global_writefds,
+	    howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES);
+	/* remove stalled FDs, and deal with pending descriptors */
+	pending = 0;
+	FD_ZERO(&pendingfds);
+	maxindex = howmany(maxfd, FD_MASK_BITS);
+	fdsp = (fd_mask *) & readfds;
+	for (j = 0; j < maxindex; j++) {
+	    if ((tmask = fdsp[j]) == 0)
+		continue;	/* no bits here */
+	    for (k = 0; k < FD_MASK_BITS; k++) {
+		if (!EBIT_TEST(tmask, k))
+		    continue;
+		/* Found a set bit */
+		fd = (j * FD_MASK_BITS) + k;
+		switch (commDeferRead(fd)) {
+		case 0:
+		    break;
+		case 1:
+		    FD_CLR(fd, &readfds);
+		    break;
+#if DELAY_POOLS
+		case -1:
+		    FD_SET(fd, &slowfds);
+		    break;
+#endif
+		default:
+		    fatalf("bad return value from commDeferRead(FD %d)\n", fd);
+		}
+		if (FD_ISSET(fd, &readfds) && fd_table[fd].flags.read_pending) {
+		    FD_SET(fd, &pendingfds);
+		    pending++;
+		}
+	    }
+	}
+#if DEBUG_FDBITS
+	for (i = 0; i < maxfd; i++) {
+	    /* Check each open socket for a handler. */
+#if DELAY_POOLS
+	    if (fd_table[i].read_handler && commDeferRead(i) != 1) {
+#else
+	    if (fd_table[i].read_handler && !commDeferRead(i)) {
+#endif
+		assert(FD_ISSET(i, &readfds));
+	    }
+	    if (fd_table[i].write_handler) {
+		assert(FD_ISSET(i, &writefds));
+	    }
+	}
+#endif
+	if (nreadfds + nwritefds == 0) {
+	    assert(shutting_down);
+	    return COMM_SHUTDOWN;
+	}
+	if (msec > MAX_POLL_TIME)
+	    msec = MAX_POLL_TIME;
+#ifdef _SQUID_OS2_
+	if (msec < 0)
+	    msec = MAX_POLL_TIME;
+#endif
+	if (pending)
+	    msec = 0;
+	for (;;) {
+	    poll_time.tv_sec = msec / 1000;
+	    poll_time.tv_usec = (msec % 1000) * 1000;
+	    statCounter.syscalls.selects++;
+	    num = select(maxfd, &readfds, &writefds, NULL, &poll_time);
+	    statCounter.select_loops++;
+	    if (num >= 0 || pending > 0)
+		break;
+	    if (ignoreErrno(errno))
+		break;
+	    debug(50, 0) ("comm_select: select failure: %s\n",
+		xstrerror());
+	    examine_select(&readfds, &writefds);
+	    return COMM_ERROR;
+	    /* NOTREACHED */
+	}
+	if (num < 0 && !pending)
+	    continue;
+	debug(5, num ? 5 : 8) ("comm_select: %d+%d FDs ready at %d\n",
+	    num, pending, (int) squid_curtime);
+	statHistCount(&statCounter.select_fds_hist, num);
+	/* Check lifetime and timeout handlers ONCE each second.
+	 * Replaces brain-dead check every time through the loop! */
+	if (squid_curtime > last_timeout) {
+	    last_timeout = squid_curtime;
+	    checkTimeouts();
+	}
+	if (num == 0 && pending == 0)
+	    continue;
+	/* Scan return fd masks for ready descriptors */
+	fdsp = (fd_mask *) & readfds;
+	pfdsp = (fd_mask *) & pendingfds;
+	maxindex = howmany(maxfd, FD_MASK_BITS);
+	for (j = 0; j < maxindex; j++) {
+	    if ((tmask = (fdsp[j] | pfdsp[j])) == 0)
+		continue;	/* no bits here */
+	    for (k = 0; k < FD_MASK_BITS; k++) {
+		if (tmask == 0)
+		    break;	/* no more bits left */
+		if (!EBIT_TEST(tmask, k))
+		    continue;
+		/* Found a set bit */
+		fd = (j * FD_MASK_BITS) + k;
+		EBIT_CLR(tmask, k);	/* this will be done */
+#if DEBUG_FDBITS
+		debug(5, 9) ("FD %d bit set for reading\n", fd);
+		assert(FD_ISSET(fd, &readfds));
+#endif
+		if (fdIsIcp(fd)) {
+		    callicp = 1;
+		    continue;
+		}
+		if (fdIsDns(fd)) {
+		    calldns = 1;
+		    continue;
+		}
+		if (fdIsHttp(fd)) {
+		    callhttp = 1;
+		    continue;
+		}
+		F = &fd_table[fd];
+		debug(5, 6) ("comm_select: FD %d ready for reading\n", fd);
+		if (NULL == (hdl = F->read_handler))
+		    (void) 0;
+#if DELAY_POOLS
+		else if (FD_ISSET(fd, &slowfds))
+		    commAddSlowFd(fd);
+#endif
+		else {
+		    F->read_handler = NULL;
+		    commUpdateReadBits(fd, NULL);
+		    hdl(fd, F->read_data);
+		    statCounter.select_fds++;
+		    if (commCheckICPIncoming)
+			comm_select_icp_incoming();
+		    if (commCheckDNSIncoming)
+			comm_select_dns_incoming();
+		    if (commCheckHTTPIncoming)
+			comm_select_http_incoming();
+		}
+	    }
+	}
+	fdsp = (fd_mask *) & writefds;
+	for (j = 0; j < maxindex; j++) {
+	    if ((tmask = fdsp[j]) == 0)
+		continue;	/* no bits here */
+	    for (k = 0; k < FD_MASK_BITS; k++) {
+		if (tmask == 0)
+		    break;	/* no more bits left */
+		if (!EBIT_TEST(tmask, k))
+		    continue;
+		/* Found a set bit */
+		fd = (j * FD_MASK_BITS) + k;
+		EBIT_CLR(tmask, k);	/* this will be done */
+#if DEBUG_FDBITS
+		debug(5, 9) ("FD %d bit set for writing\n", fd);
+		assert(FD_ISSET(fd, &writefds));
+#endif
+		if (fdIsIcp(fd)) {
+		    callicp = 1;
+		    continue;
+		}
+		if (fdIsDns(fd)) {
+		    calldns = 1;
+		    continue;
+		}
+		if (fdIsHttp(fd)) {
+		    callhttp = 1;
+		    continue;
+		}
+		F = &fd_table[fd];
+		debug(5, 5) ("comm_select: FD %d ready for writing\n", fd);
+		if ((hdl = F->write_handler)) {
+		    F->write_handler = NULL;
+		    commUpdateWriteBits(fd, NULL);
+		    hdl(fd, F->write_data);
+		    statCounter.select_fds++;
+		    if (commCheckICPIncoming)
+			comm_select_icp_incoming();
+		    if (commCheckDNSIncoming)
+			comm_select_dns_incoming();
+		    if (commCheckHTTPIncoming)
+			comm_select_http_incoming();
+		}
+	    }
+	}
+	if (callicp)
+	    comm_select_icp_incoming();
+	if (calldns)
+	    comm_select_dns_incoming();
+	if (callhttp)
+	    comm_select_http_incoming();
+#if DELAY_POOLS
+	while ((fd = commGetSlowFd()) != -1) {
+	    F = &fd_table[fd];
+	    debug(5, 6) ("comm_select: slow FD %d selected for reading\n", fd);
+	    if ((hdl = F->read_handler)) {
+		F->read_handler = NULL;
+		commUpdateReadBits(fd, NULL);
+		hdl(fd, F->read_data);
+		statCounter.select_fds++;
+		if (commCheckICPIncoming)
+		    comm_select_icp_incoming();
+		if (commCheckDNSIncoming)
+		    comm_select_dns_incoming();
+		if (commCheckHTTPIncoming)
+		    comm_select_http_incoming();
+	    }
+	}
+#endif
+	return COMM_OK;
+    }
+    while (timeout > current_dtime);
+    debug(5, 8) ("comm_select: time out: %d\n", (int) squid_curtime);
+    return COMM_TIMEOUT;
+}
+
+static void
+comm_select_dns_incoming(void)
+{
+    int nfds = 0;
+    int fds[2];
+    int nevents;
+    dns_io_events = 0;
+    if (DnsSocket < 0)
+	return;
+    fds[nfds++] = DnsSocket;
+    nevents = comm_check_incoming_select_handlers(nfds, fds);
+    if (nevents < 0)
+	return;
+    incoming_dns_interval += Config.comm_incoming.dns_average - nevents;
+    if (incoming_dns_interval < Config.comm_incoming.dns_min_poll)
+	incoming_dns_interval = Config.comm_incoming.dns_min_poll;
+    if (incoming_dns_interval > MAX_INCOMING_INTERVAL)
+	incoming_dns_interval = MAX_INCOMING_INTERVAL;
+    if (nevents > INCOMING_DNS_MAX)
+	nevents = INCOMING_DNS_MAX;
+    statHistCount(&statCounter.comm_dns_incoming, nevents);
+}
+
+void
+comm_select_init(void)
+{
+    zero_tv.tv_sec = 0;
+    zero_tv.tv_usec = 0;
+    cachemgrRegister("comm_incoming",
+	"comm_incoming() stats",
+	commIncomingStats, 0, 1);
+    FD_ZERO(&global_readfds);
+    FD_ZERO(&global_writefds);
+    nreadfds = nwritefds = 0;
+}
+
+/*
+ * examine_select - debug routine.
+ *
+ * I spend the day chasing this core dump that occurs when both the client
+ * and the server side of a cache fetch simultaneoulsy abort the
+ * connection.  While I haven't really studied the code to figure out how
+ * it happens, the snippet below may prevent the cache from exitting:
+ * 
+ * Call this from where the select loop fails.
+ */
+static int
+examine_select(fd_set * readfds, fd_set * writefds)
+{
+    int fd = 0;
+    fd_set read_x;
+    fd_set write_x;
+    struct timeval tv;
+    close_handler *ch = NULL;
+    fde *F = NULL;
+    struct stat sb;
+    debug(5, 0) ("examine_select: Examining open file descriptors...\n");
+    for (fd = 0; fd < Squid_MaxFD; fd++) {
+	FD_ZERO(&read_x);
+	FD_ZERO(&write_x);
+	tv.tv_sec = tv.tv_usec = 0;
+	if (FD_ISSET(fd, readfds))
+	    FD_SET(fd, &read_x);
+	else if (FD_ISSET(fd, writefds))
+	    FD_SET(fd, &write_x);
+	else
+	    continue;
+	statCounter.syscalls.selects++;
+	errno = 0;
+	if (!fstat(fd, &sb)) {
+	    debug(5, 5) ("FD %d is valid.\n", fd);
+	    continue;
+	}
+	F = &fd_table[fd];
+	debug(5, 0) ("FD %d: %s\n", fd, xstrerror());
+	debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd);
+	debug(5, 0) ("FD %d is a %s called '%s'\n",
+	    fd,
+	    fdTypeStr[F->type],
+	    F->desc);
+	debug(5, 0) ("tmout:%p read:%p write:%p\n",
+	    F->timeout_handler,
+	    F->read_handler,
+	    F->write_handler);
+	for (ch = F->closeHandler; ch; ch = ch->next)
+	    debug(5, 0) (" close handler: %p\n", ch->handler);
+	if (F->closeHandler) {
+	    commCallCloseHandlers(fd);
+	} else if (F->timeout_handler) {
+	    debug(5, 0) ("examine_select: Calling Timeout Handler\n");
+	    F->timeout_handler(fd, F->timeout_data);
+	}
+	F->closeHandler = NULL;
+	F->timeout_handler = NULL;
+	F->read_handler = NULL;
+	F->write_handler = NULL;
+	FD_CLR(fd, readfds);
+	FD_CLR(fd, writefds);
+    }
+    return 0;
+}
+
+
+static void
+commIncomingStats(StoreEntry * sentry)
+{
+    StatCounters *f = &statCounter;
+    storeAppendPrintf(sentry, "Current incoming_icp_interval: %d\n",
+	incoming_icp_interval >> INCOMING_FACTOR);
+    storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n",
+	incoming_dns_interval >> INCOMING_FACTOR);
+    storeAppendPrintf(sentry, "Current incoming_http_interval: %d\n",
+	incoming_http_interval >> INCOMING_FACTOR);
+    storeAppendPrintf(sentry, "\n");
+    storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n");
+    storeAppendPrintf(sentry, "ICP Messages handled per comm_select_icp_incoming() call:\n");
+    statHistDump(&f->comm_icp_incoming, sentry, statHistIntDumper);
+    storeAppendPrintf(sentry, "DNS Messages handled per comm_select_dns_incoming() call:\n");
+    statHistDump(&f->comm_dns_incoming, sentry, statHistIntDumper);
+    storeAppendPrintf(sentry, "HTTP Messages handled per comm_select_http_incoming() call:\n");
+    statHistDump(&f->comm_http_incoming, sentry, statHistIntDumper);
+}
+
+void
+commUpdateReadBits(int fd, PF * handler)
+{
+    if (handler && !FD_ISSET(fd, &global_readfds)) {
+	FD_SET(fd, &global_readfds);
+	nreadfds++;
+    } else if (!handler && FD_ISSET(fd, &global_readfds)) {
+	FD_CLR(fd, &global_readfds);
+	nreadfds--;
+    }
+}
+
+void
+commUpdateWriteBits(int fd, PF * handler)
+{
+    if (handler && !FD_ISSET(fd, &global_writefds)) {
+	FD_SET(fd, &global_writefds);
+	nwritefds++;
+    } else if (!handler && FD_ISSET(fd, &global_writefds)) {
+	FD_CLR(fd, &global_writefds);
+	nwritefds--;
+    }
+}
+
+/* Called by async-io or diskd to speed up the polling */
+void
+comm_quick_poll_required(void)
+{
+    MAX_POLL_TIME = 10;
+}
+
+#endif /* USE_SELECT */
--- squid/src/debug.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,436 +0,0 @@
-
-/*
- * $Id: debug.c,v 1.10 2002/09/01 16:30:42 squidadm Exp $
- *
- * DEBUG: section 0     Debug Routines
- * AUTHOR: Harvest Derived
- *
- * 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 char *debug_log_file = NULL;
-static int Ctx_Lock = 0;
-static const char *debugLogTime(time_t);
-static void ctx_print(void);
-#if HAVE_SYSLOG
-static void _db_print_syslog(const char *format, va_list args);
-#endif
-static void _db_print_stderr(const char *format, va_list args);
-static void _db_print_file(const char *format, va_list args);
-
-void
-#if STDC_HEADERS
-_db_print(const char *format,...)
-{
-#else
-_db_print(va_alist)
-     va_dcl
-{
-    const char *format = NULL;
-#endif
-    LOCAL_ARRAY(char, f, BUFSIZ);
-    va_list args1;
-#if STDC_HEADERS
-    va_list args2;
-    va_list args3;
-#else
-#define args2 args1
-#define args3 args1
-#endif
-    /* give a chance to context-based debugging to print current context */
-    if (!Ctx_Lock)
-	ctx_print();
-#if STDC_HEADERS
-    va_start(args1, format);
-    va_start(args2, format);
-    va_start(args3, format);
-#else
-    format = va_arg(args1, const char *);
-#endif
-    snprintf(f, BUFSIZ, "%s| %s",
-	debugLogTime(squid_curtime),
-	format);
-    _db_print_file(f, args1);
-    _db_print_stderr(f, args2);
-#if HAVE_SYSLOG
-    _db_print_syslog(format, args3);
-#endif
-    va_end(args1);
-#if STDC_HEADERS
-    va_end(args2);
-    va_end(args3);
-#endif
-}
-
-static void
-_db_print_file(const char *format, va_list args)
-{
-    if (debug_log == NULL)
-	return;
-    /* give a chance to context-based debugging to print current context */
-    if (!Ctx_Lock)
-	ctx_print();
-    vfprintf(debug_log, format, args);
-    if (!Config.onoff.buffered_logs)
-	fflush(debug_log);
-}
-
-static void
-_db_print_stderr(const char *format, va_list args)
-{
-    if (opt_debug_stderr < _db_level)
-	return;
-    if (debug_log == stderr)
-	return;
-    vfprintf(stderr, format, args);
-}
-
-#if HAVE_SYSLOG
-static void
-_db_print_syslog(const char *format, va_list args)
-{
-    LOCAL_ARRAY(char, tmpbuf, BUFSIZ);
-    /* level 0,1 go to syslog */
-    if (_db_level > 1)
-	return;
-    if (0 == opt_syslog_enable)
-	return;
-    tmpbuf[0] = '\0';
-    vsnprintf(tmpbuf, BUFSIZ, format, args);
-    tmpbuf[BUFSIZ - 1] = '\0';
-    syslog(_db_level == 0 ? LOG_WARNING : LOG_NOTICE, "%s", tmpbuf);
-}
-#endif /* HAVE_SYSLOG */
-
-static void
-debugArg(const char *arg)
-{
-    int s = 0;
-    int l = 0;
-    int i;
-    if (!strncasecmp(arg, "ALL", 3)) {
-	s = -1;
-	arg += 4;
-    } else {
-	s = atoi(arg);
-	while (*arg && *arg++ != ',');
-    }
-    l = atoi(arg);
-    assert(s >= -1);
-    assert(s < MAX_DEBUG_SECTIONS);
-    if (l < 0)
-	l = 0;
-    if (l > 10)
-	l = 10;
-    if (s >= 0) {
-	debugLevels[s] = l;
-	return;
-    }
-    for (i = 0; i < MAX_DEBUG_SECTIONS; i++)
-	debugLevels[i] = l;
-}
-
-static void
-debugOpenLog(const char *logfile)
-{
-    if (logfile == NULL) {
-	debug_log = stderr;
-	return;
-    }
-    if (debug_log_file)
-	xfree(debug_log_file);
-    debug_log_file = xstrdup(logfile);	/* keep a static copy */
-    if (debug_log && debug_log != stderr)
-	fclose(debug_log);
-    debug_log = fopen(logfile, "a+");
-    if (!debug_log) {
-	fprintf(stderr, "WARNING: Cannot write log file: %s\n", logfile);
-	perror(logfile);
-	fprintf(stderr, "         messages will be sent to 'stderr'.\n");
-	fflush(stderr);
-	debug_log = stderr;
-    }
-#if defined(_SQUID_CYGWIN_)||defined(_SQUID_MSWIN_)
-    setmode(fileno(debug_log), O_TEXT);
-#endif
-}
-
-void
-_db_init(const char *logfile, const char *options)
-{
-    int i;
-    char *p = NULL;
-    char *s = NULL;
-
-    for (i = 0; i < MAX_DEBUG_SECTIONS; i++)
-	debugLevels[i] = -1;
-
-    if (options) {
-	p = xstrdup(options);
-	for (s = strtok(p, w_space); s; s = strtok(NULL, w_space))
-	    debugArg(s);
-	xfree(p);
-    }
-    debugOpenLog(logfile);
-
-#if HAVE_SYSLOG && defined(LOG_LOCAL4)
-    if (opt_syslog_enable)
-	openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
-#endif /* HAVE_SYSLOG */
-
-}
-
-void
-_db_rotate_log(void)
-{
-    int i;
-    LOCAL_ARRAY(char, from, MAXPATHLEN);
-    LOCAL_ARRAY(char, to, MAXPATHLEN);
-#ifdef S_ISREG
-    struct stat sb;
-#endif
-
-    if (debug_log_file == NULL)
-	return;
-#ifdef S_ISREG
-    if (stat(debug_log_file, &sb) == 0)
-	if (S_ISREG(sb.st_mode) == 0)
-	    return;
-#endif
-
-    /*
-     * NOTE: we cannot use xrename here without having it in a
-     * separate file -- tools.c has too many dependencies to be
-     * used everywhere debug.c is used.
-     */
-    /* Rotate numbers 0 through N up one */
-    for (i = Config.Log.rotateNumber; i > 1;) {
-	i--;
-	snprintf(from, MAXPATHLEN, "%s.%d", debug_log_file, i - 1);
-	snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, i);
-	rename(from, to);
-    }
-/*
- * You can't rename open files on Microsoft "operating systems"
- * so we close before renaming.
- */
-#ifdef _SQUID_MSWIN_
-    if (debug_log != stderr)
-	fclose(debug_log);
-#endif
-    /* Rotate the current log to .0 */
-    if (Config.Log.rotateNumber > 0) {
-	snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, 0);
-	rename(debug_log_file, to);
-    }
-    /* Close and reopen the log.  It may have been renamed "manually"
-     * before HUP'ing us. */
-    if (debug_log != stderr)
-	debugOpenLog(Config.Log.log);
-}
-
-static const char *
-debugLogTime(time_t t)
-{
-    struct tm *tm;
-    static char buf[128];
-    static time_t last_t = 0;
-    if (t != last_t) {
-	tm = localtime(&t);
-	strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
-	last_t = t;
-    }
-    return buf;
-}
-
-void
-xassert(const char *msg, const char *file, int line)
-{
-    debug(0, 0) ("assertion failed: %s:%d: \"%s\"\n", file, line, msg);
-    if (!shutting_down)
-	abort();
-}
-
-/*
- * Context-based Debugging
- *
- * Rationale
- * ---------
- * 
- * When you have a long nested processing sequence, it is often impossible
- * for low level routines to know in what larger context they operate. If a
- * routine coredumps, one can restore the context using debugger trace.
- * However, in many case you do not want to coredump, but just want to report
- * a potential problem. A report maybe useless out of problem context.
- * 
- * To solve this potential problem, use the following approach:
- * 
- * int
- * top_level_foo(const char *url)
- * {
- *      // define current context
- *      // note: we stack but do not dup ctx descriptions!
- *      Ctx ctx = ctx_enter(url);
- *      ...
- *      // go down; middle_level_bar will eventually call bottom_level_boo
- *      middle_level_bar(method, protocol);
- *      ...
- *      // exit, clean after yourself
- *      ctx_exit(ctx);
- * }
- * 
- * void
- * bottom_level_boo(int status, void *data)
- * {
- *      // detect exceptional condition, and simply report it, the context
- *      // information will be available somewhere close in the log file
- *      if (status == STRANGE_STATUS)
- *      debug(13, 6) ("DOS attack detected, data: %p\n", data);
- *      ...
- * }
- * 
- * Current implementation is extremely simple but still very handy. It has a
- * negligible overhead (descriptions are not duplicated).
- * 
- * When the _first_ debug message for a given context is printed, it is
- * prepended with the current context description. Context is printed with
- * the same debugging level as the original message.
- * 
- * Note that we do not print context every type you do ctx_enter(). This
- * approach would produce too many useless messages.  For the same reason, a
- * context description is printed at most _once_ even if you have 10
- * debugging messages within one context.
- * 
- * Contexts can be nested, of course. You must use ctx_enter() to enter a
- * context (push it onto stack).  It is probably safe to exit several nested
- * contexts at _once_ by calling ctx_exit() at the top level (this will pop
- * all context till current one). However, as in any stack, you cannot start
- * in the middle.
- * 
- * Analysis: 
- * i)   locate debugging message,
- * ii)  locate current context by going _upstream_ in your log file,
- * iii) hack away.
- *
- *
- * To-Do: 
- * -----
- *
- *       decide if we want to dup() descriptions (adds overhead) but allows to
- *       add printf()-style interface
- *
- * implementation:
- * ---------------
- *
- * descriptions for contexts over CTX_MAX_LEVEL limit are ignored, you probably
- * have a bug if your nesting goes that deep.
- */
-
-#define CTX_MAX_LEVEL 255
-
-/*
- * produce a warning when nesting reaches this level and then double
- * the level
- */
-static int Ctx_Warn_Level = 32;
-/* all descriptions has been printed up to this level */
-static int Ctx_Reported_Level = -1;
-/* descriptions are still valid or active up to this level */
-static int Ctx_Valid_Level = -1;
-/* current level, the number of nested ctx_enter() calls */
-static int Ctx_Current_Level = -1;
-/* saved descriptions (stack) */
-static const char *Ctx_Descrs[CTX_MAX_LEVEL + 1];
-/* "safe" get secription */
-static const char *ctx_get_descr(Ctx ctx);
-
-
-Ctx
-ctx_enter(const char *descr)
-{
-    Ctx_Current_Level++;
-
-    if (Ctx_Current_Level <= CTX_MAX_LEVEL)
-	Ctx_Descrs[Ctx_Current_Level] = descr;
-
-    if (Ctx_Current_Level == Ctx_Warn_Level) {
-	debug(0, 0) ("# ctx: suspiciously deep (%d) nesting:\n", Ctx_Warn_Level);
-	Ctx_Warn_Level *= 2;
-    }
-    return Ctx_Current_Level;
-}
-
-void
-ctx_exit(Ctx ctx)
-{
-    assert(ctx >= 0);
-    Ctx_Current_Level = (ctx >= 0) ? ctx - 1 : -1;
-    if (Ctx_Valid_Level > Ctx_Current_Level)
-	Ctx_Valid_Level = Ctx_Current_Level;
-}
-
-/*
- * the idea id to print each context description at most once but provide enough
- * info for deducing the current execution stack
- */
-static void
-ctx_print(void)
-{
-    /* lock so _db_print will not call us recursively */
-    Ctx_Lock++;
-    /* ok, user saw [0,Ctx_Reported_Level] descriptions */
-    /* first inform about entries popped since user saw them */
-    if (Ctx_Valid_Level < Ctx_Reported_Level) {
-	if (Ctx_Reported_Level != Ctx_Valid_Level + 1)
-	    _db_print("ctx: exit levels from %2d down to %2d\n",
-		Ctx_Reported_Level, Ctx_Valid_Level + 1);
-	else
-	    _db_print("ctx: exit level %2d\n", Ctx_Reported_Level);
-	Ctx_Reported_Level = Ctx_Valid_Level;
-    }
-    /* report new contexts that were pushed since last report */
-    while (Ctx_Reported_Level < Ctx_Current_Level) {
-	Ctx_Reported_Level++;
-	Ctx_Valid_Level++;
-	_db_print("ctx: enter level %2d: '%s'\n", Ctx_Reported_Level,
-	    ctx_get_descr(Ctx_Reported_Level));
-    }
-    /* unlock */
-    Ctx_Lock--;
-}
-
-/* checks for nulls and overflows */
-static const char *
-ctx_get_descr(Ctx ctx)
-{
-    if (ctx < 0 || ctx > CTX_MAX_LEVEL)
-	return "<lost>";
-    return Ctx_Descrs[ctx] ? Ctx_Descrs[ctx] : "<null>";
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/debug.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,436 @@
+
+/*
+ * $Id: debug.cc,v 1.1.2.1 2002/10/11 15:40:48 rbcollins Exp $
+ *
+ * DEBUG: section 0     Debug Routines
+ * AUTHOR: Harvest Derived
+ *
+ * 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 char *debug_log_file = NULL;
+static int Ctx_Lock = 0;
+static const char *debugLogTime(time_t);
+static void ctx_print(void);
+#if HAVE_SYSLOG
+static void _db_print_syslog(const char *format, va_list args);
+#endif
+static void _db_print_stderr(const char *format, va_list args);
+static void _db_print_file(const char *format, va_list args);
+
+void
+#if STDC_HEADERS
+_db_print(const char *format,...)
+{
+#else
+_db_print(va_alist)
+     va_dcl
+{
+    const char *format = NULL;
+#endif
+    LOCAL_ARRAY(char, f, BUFSIZ);
+    va_list args1;
+#if STDC_HEADERS
+    va_list args2;
+    va_list args3;
+#else
+#define args2 args1
+#define args3 args1
+#endif
+    /* give a chance to context-based debugging to print current context */
+    if (!Ctx_Lock)
+	ctx_print();
+#if STDC_HEADERS
+    va_start(args1, format);
+    va_start(args2, format);
+    va_start(args3, format);
+#else
+    format = va_arg(args1, const char *);
+#endif
+    snprintf(f, BUFSIZ, "%s| %s",
+	debugLogTime(squid_curtime),
+	format);
+    _db_print_file(f, args1);
+    _db_print_stderr(f, args2);
+#if HAVE_SYSLOG
+    _db_print_syslog(format, args3);
+#endif
+    va_end(args1);
+#if STDC_HEADERS
+    va_end(args2);
+    va_end(args3);
+#endif
+}
+
+static void
+_db_print_file(const char *format, va_list args)
+{
+    if (debug_log == NULL)
+	return;
+    /* give a chance to context-based debugging to print current context */
+    if (!Ctx_Lock)
+	ctx_print();
+    vfprintf(debug_log, format, args);
+    if (!Config.onoff.buffered_logs)
+	fflush(debug_log);
+}
+
+static void
+_db_print_stderr(const char *format, va_list args)
+{
+    if (opt_debug_stderr < _db_level)
+	return;
+    if (debug_log == stderr)
+	return;
+    vfprintf(stderr, format, args);
+}
+
+#if HAVE_SYSLOG
+static void
+_db_print_syslog(const char *format, va_list args)
+{
+    LOCAL_ARRAY(char, tmpbuf, BUFSIZ);
+    /* level 0,1 go to syslog */
+    if (_db_level > 1)
+	return;
+    if (0 == opt_syslog_enable)
+	return;
+    tmpbuf[0] = '\0';
+    vsnprintf(tmpbuf, BUFSIZ, format, args);
+    tmpbuf[BUFSIZ - 1] = '\0';
+    syslog(_db_level == 0 ? LOG_WARNING : LOG_NOTICE, "%s", tmpbuf);
+}
+#endif /* HAVE_SYSLOG */
+
+static void
+debugArg(const char *arg)
+{
+    int s = 0;
+    int l = 0;
+    int i;
+    if (!strncasecmp(arg, "ALL", 3)) {
+	s = -1;
+	arg += 4;
+    } else {
+	s = atoi(arg);
+	while (*arg && *arg++ != ',');
+    }
+    l = atoi(arg);
+    assert(s >= -1);
+    assert(s < MAX_DEBUG_SECTIONS);
+    if (l < 0)
+	l = 0;
+    if (l > 10)
+	l = 10;
+    if (s >= 0) {
+	debugLevels[s] = l;
+	return;
+    }
+    for (i = 0; i < MAX_DEBUG_SECTIONS; i++)
+	debugLevels[i] = l;
+}
+
+static void
+debugOpenLog(const char *logfile)
+{
+    if (logfile == NULL) {
+	debug_log = stderr;
+	return;
+    }
+    if (debug_log_file)
+	xfree(debug_log_file);
+    debug_log_file = xstrdup(logfile);	/* keep a static copy */
+    if (debug_log && debug_log != stderr)
+	fclose(debug_log);
+    debug_log = fopen(logfile, "a+");
+    if (!debug_log) {
+	fprintf(stderr, "WARNING: Cannot write log file: %s\n", logfile);
+	perror(logfile);
+	fprintf(stderr, "         messages will be sent to 'stderr'.\n");
+	fflush(stderr);
+	debug_log = stderr;
+    }
+#if defined(_SQUID_CYGWIN_)||defined(_SQUID_MSWIN_)
+    setmode(fileno(debug_log), O_TEXT);
+#endif
+}
+
+void
+_db_init(const char *logfile, const char *options)
+{
+    int i;
+    char *p = NULL;
+    char *s = NULL;
+
+    for (i = 0; i < MAX_DEBUG_SECTIONS; i++)
+	debugLevels[i] = -1;
+
+    if (options) {
+	p = xstrdup(options);
+	for (s = strtok(p, w_space); s; s = strtok(NULL, w_space))
+	    debugArg(s);
+	xfree(p);
+    }
+    debugOpenLog(logfile);
+
+#if HAVE_SYSLOG && defined(LOG_LOCAL4)
+    if (opt_syslog_enable)
+	openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
+#endif /* HAVE_SYSLOG */
+
+}
+
+void
+_db_rotate_log(void)
+{
+    int i;
+    LOCAL_ARRAY(char, from, MAXPATHLEN);
+    LOCAL_ARRAY(char, to, MAXPATHLEN);
+#ifdef S_ISREG
+    struct stat sb;
+#endif
+
+    if (debug_log_file == NULL)
+	return;
+#ifdef S_ISREG
+    if (stat(debug_log_file, &sb) == 0)
+	if (S_ISREG(sb.st_mode) == 0)
+	    return;
+#endif
+
+    /*
+     * NOTE: we cannot use xrename here without having it in a
+     * separate file -- tools.c has too many dependencies to be
+     * used everywhere debug.c is used.
+     */
+    /* Rotate numbers 0 through N up one */
+    for (i = Config.Log.rotateNumber; i > 1;) {
+	i--;
+	snprintf(from, MAXPATHLEN, "%s.%d", debug_log_file, i - 1);
+	snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, i);
+	rename(from, to);
+    }
+/*
+ * You can't rename open files on Microsoft "operating systems"
+ * so we close before renaming.
+ */
+#ifdef _SQUID_MSWIN_
+    if (debug_log != stderr)
+	fclose(debug_log);
+#endif
+    /* Rotate the current log to .0 */
+    if (Config.Log.rotateNumber > 0) {
+	snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, 0);
+	rename(debug_log_file, to);
+    }
+    /* Close and reopen the log.  It may have been renamed "manually"
+     * before HUP'ing us. */
+    if (debug_log != stderr)
+	debugOpenLog(Config.Log.log);
+}
+
+static const char *
+debugLogTime(time_t t)
+{
+    struct tm *tm;
+    static char buf[128];
+    static time_t last_t = 0;
+    if (t != last_t) {
+	tm = localtime(&t);
+	strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
+	last_t = t;
+    }
+    return buf;
+}
+
+void
+xassert(const char *msg, const char *file, int line)
+{
+    debug(0, 0) ("assertion failed: %s:%d: \"%s\"\n", file, line, msg);
+    if (!shutting_down)
+	abort();
+}
+
+/*
+ * Context-based Debugging
+ *
+ * Rationale
+ * ---------
+ * 
+ * When you have a long nested processing sequence, it is often impossible
+ * for low level routines to know in what larger context they operate. If a
+ * routine coredumps, one can restore the context using debugger trace.
+ * However, in many case you do not want to coredump, but just want to report
+ * a potential problem. A report maybe useless out of problem context.
+ * 
+ * To solve this potential problem, use the following approach:
+ * 
+ * int
+ * top_level_foo(const char *url)
+ * {
+ *      // define current context
+ *      // note: we stack but do not dup ctx descriptions!
+ *      Ctx ctx = ctx_enter(url);
+ *      ...
+ *      // go down; middle_level_bar will eventually call bottom_level_boo
+ *      middle_level_bar(method, protocol);
+ *      ...
+ *      // exit, clean after yourself
+ *      ctx_exit(ctx);
+ * }
+ * 
+ * void
+ * bottom_level_boo(int status, void *data)
+ * {
+ *      // detect exceptional condition, and simply report it, the context
+ *      // information will be available somewhere close in the log file
+ *      if (status == STRANGE_STATUS)
+ *      debug(13, 6) ("DOS attack detected, data: %p\n", data);
+ *      ...
+ * }
+ * 
+ * Current implementation is extremely simple but still very handy. It has a
+ * negligible overhead (descriptions are not duplicated).
+ * 
+ * When the _first_ debug message for a given context is printed, it is
+ * prepended with the current context description. Context is printed with
+ * the same debugging level as the original message.
+ * 
+ * Note that we do not print context every type you do ctx_enter(). This
+ * approach would produce too many useless messages.  For the same reason, a
+ * context description is printed at most _once_ even if you have 10
+ * debugging messages within one context.
+ * 
+ * Contexts can be nested, of course. You must use ctx_enter() to enter a
+ * context (push it onto stack).  It is probably safe to exit several nested
+ * contexts at _once_ by calling ctx_exit() at the top level (this will pop
+ * all context till current one). However, as in any stack, you cannot start
+ * in the middle.
+ * 
+ * Analysis: 
+ * i)   locate debugging message,
+ * ii)  locate current context by going _upstream_ in your log file,
+ * iii) hack away.
+ *
+ *
+ * To-Do: 
+ * -----
+ *
+ *       decide if we want to dup() descriptions (adds overhead) but allows to
+ *       add printf()-style interface
+ *
+ * implementation:
+ * ---------------
+ *
+ * descriptions for contexts over CTX_MAX_LEVEL limit are ignored, you probably
+ * have a bug if your nesting goes that deep.
+ */
+
+#define CTX_MAX_LEVEL 255
+
+/*
+ * produce a warning when nesting reaches this level and then double
+ * the level
+ */
+static int Ctx_Warn_Level = 32;
+/* all descriptions has been printed up to this level */
+static int Ctx_Reported_Level = -1;
+/* descriptions are still valid or active up to this level */
+static int Ctx_Valid_Level = -1;
+/* current level, the number of nested ctx_enter() calls */
+static int Ctx_Current_Level = -1;
+/* saved descriptions (stack) */
+static const char *Ctx_Descrs[CTX_MAX_LEVEL + 1];
+/* "safe" get secription */
+static const char *ctx_get_descr(Ctx ctx);
+
+
+Ctx
+ctx_enter(const char *descr)
+{
+    Ctx_Current_Level++;
+
+    if (Ctx_Current_Level <= CTX_MAX_LEVEL)
+	Ctx_Descrs[Ctx_Current_Level] = descr;
+
+    if (Ctx_Current_Level == Ctx_Warn_Level) {
+	debug(0, 0) ("# ctx: suspiciously deep (%d) nesting:\n", Ctx_Warn_Level);
+	Ctx_Warn_Level *= 2;
+    }
+    return Ctx_Current_Level;
+}
+
+void
+ctx_exit(Ctx ctx)
+{
+    assert(ctx >= 0);
+    Ctx_Current_Level = (ctx >= 0) ? ctx - 1 : -1;
+    if (Ctx_Valid_Level > Ctx_Current_Level)
+	Ctx_Valid_Level = Ctx_Current_Level;
+}
+
+/*
+ * the idea id to print each context description at most once but provide enough
+ * info for deducing the current execution stack
+ */
+static void
+ctx_print(void)
+{
+    /* lock so _db_print will not call us recursively */
+    Ctx_Lock++;
+    /* ok, user saw [0,Ctx_Reported_Level] descriptions */
+    /* first inform about entries popped since user saw them */
+    if (Ctx_Valid_Level < Ctx_Reported_Level) {
+	if (Ctx_Reported_Level != Ctx_Valid_Level + 1)
+	    _db_print("ctx: exit levels from %2d down to %2d\n",
+		Ctx_Reported_Level, Ctx_Valid_Level + 1);
+	else
+	    _db_print("ctx: exit level %2d\n", Ctx_Reported_Level);
+	Ctx_Reported_Level = Ctx_Valid_Level;
+    }
+    /* report new contexts that were pushed since last report */
+    while (Ctx_Reported_Level < Ctx_Current_Level) {
+	Ctx_Reported_Level++;
+	Ctx_Valid_Level++;
+	_db_print("ctx: enter level %2d: '%s'\n", Ctx_Reported_Level,
+	    ctx_get_descr(Ctx_Reported_Level));
+    }
+    /* unlock */
+    Ctx_Lock--;
+}
+
+/* checks for nulls and overflows */
+static const char *
+ctx_get_descr(Ctx ctx)
+{
+    if (ctx < 0 || ctx > CTX_MAX_LEVEL)
+	return "<lost>";
+    return Ctx_Descrs[ctx] ? Ctx_Descrs[ctx] : "<null>";
+}
--- squid/src/disk.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,418 +0,0 @@
-
-/*
- * $Id: disk.c,v 1.12 2002/10/02 11:10:47 squidadm Exp $
- *
- * DEBUG: section 6     Disk I/O Routines
- * AUTHOR: Harvest Derived
- *
- * 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 PF diskHandleRead;
-static PF diskHandleWrite;
-
-#if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_)
-static int
-diskWriteIsComplete(int fd)
-{
-    return fd_table[fd].disk.write_q ? 0 : 1;
-}
-#endif
-
-void
-disk_init(void)
-{
-    (void) 0;
-}
-
-/*
- * opens a disk file specified by 'path'.  This function always
- * blocks!  There is no callback.
- */
-int
-file_open(const char *path, int mode)
-{
-    int fd;
-    PROF_start(file_open);
-    if (FILE_MODE(mode) == O_WRONLY)
-	mode |= O_APPEND;
-    mode |= SQUID_NONBLOCK;
-    errno = 0;
-    fd = open(path, mode, 0644);
-    statCounter.syscalls.disk.opens++;
-    if (fd < 0) {
-	debug(50, 3) ("file_open: error opening file %s: %s\n", path,
-	    xstrerror());
-	fd = DISK_ERROR;
-    } else {
-	debug(6, 5) ("file_open: FD %d\n", fd);
-	commSetCloseOnExec(fd);
-	fd_open(fd, FD_FILE, path);
-    }
-    PROF_stop(file_open);
-    return fd;
-}
-
-
-/* close a disk file. */
-void
-file_close(int fd)
-{
-    fde *F = &fd_table[fd];
-    PF *read_callback;
-    PROF_start(file_close);
-    assert(fd >= 0);
-    assert(F->flags.open);
-    if ((read_callback = F->read_handler)) {
-	F->read_handler = NULL;
-	read_callback(-1, F->read_data);
-    }
-    if (F->flags.write_daemon) {
-#if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_)
-	/*
-	 * on some operating systems, you can not delete or rename
-	 * open files, so we won't allow delayed close.
-	 */
-	while (!diskWriteIsComplete(fd))
-	    diskHandleWrite(fd, NULL);
-#else
-	F->flags.close_request = 1;
-	debug(6, 2) ("file_close: FD %d, delaying close\n", fd);
-	PROF_stop(file_close);
-	return;
-#endif
-    }
-    /*
-     * Assert there is no write callback.  Otherwise we might be
-     * leaking write state data by closing the descriptor
-     */
-    assert(F->write_handler == NULL);
-    F->flags.closing = 1;
-#if CALL_FSYNC_BEFORE_CLOSE
-    fsync(fd);
-#endif
-    close(fd);
-    debug(6, F->flags.close_request ? 2 : 5)
-	("file_close: FD %d, really closing\n", fd);
-    fd_close(fd);
-    statCounter.syscalls.disk.closes++;
-    PROF_stop(file_close);
-}
-
-/*
- * This function has the purpose of combining multiple writes.  This is
- * to facilitate the ASYNC_IO option since it can only guarantee 1
- * write to a file per trip around the comm.c select() loop. That's bad
- * because more than 1 write can be made to the access.log file per
- * trip, and so this code is purely designed to help batch multiple
- * sequential writes to the access.log file.  Squid will never issue
- * multiple writes for any other file type during 1 trip around the
- * select() loop.       --SLF
- */
-static void
-diskCombineWrites(struct _fde_disk *fdd)
-{
-    int len = 0;
-    dwrite_q *q = NULL;
-    dwrite_q *wq = NULL;
-    /*
-     * We need to combine multiple write requests on an FD's write
-     * queue But only if we don't need to seek() in between them, ugh!
-     * XXX This currently ignores any seeks (file_offset)
-     */
-    if (fdd->write_q != NULL && fdd->write_q->next != NULL) {
-	len = 0;
-	for (q = fdd->write_q; q != NULL; q = q->next)
-	    len += q->len - q->buf_offset;
-	wq = memAllocate(MEM_DWRITE_Q);
-	wq->buf = xmalloc(len);
-	wq->len = 0;
-	wq->buf_offset = 0;
-	wq->next = NULL;
-	wq->free_func = xfree;
-	do {
-	    q = fdd->write_q;
-	    len = q->len - q->buf_offset;
-	    xmemcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
-	    wq->len += len;
-	    fdd->write_q = q->next;
-	    if (q->free_func)
-		(q->free_func) (q->buf);
-	    if (q) {
-		memFree(q, MEM_DWRITE_Q);
-		q = NULL;
-	    }
-	} while (fdd->write_q != NULL);
-	fdd->write_q_tail = wq;
-	fdd->write_q = wq;
-    }
-}
-
-/* write handler */
-static void
-diskHandleWrite(int fd, void *notused)
-{
-    int len = 0;
-    fde *F = &fd_table[fd];
-    struct _fde_disk *fdd = &F->disk;
-    dwrite_q *q = fdd->write_q;
-    int status = DISK_OK;
-    int do_close;
-    if (NULL == q)
-	return;
-    PROF_start(diskHandleWrite);
-    debug(6, 3) ("diskHandleWrite: FD %d\n", fd);
-    F->flags.write_daemon = 0;
-    assert(fdd->write_q != NULL);
-    assert(fdd->write_q->len > fdd->write_q->buf_offset);
-    debug(6, 3) ("diskHandleWrite: FD %d writing %d bytes\n",
-	fd, (int) (fdd->write_q->len - fdd->write_q->buf_offset));
-    errno = 0;
-    if (fdd->write_q->file_offset != -1)
-	lseek(fd, fdd->write_q->file_offset, SEEK_SET);
-    len = FD_WRITE_METHOD(fd,
-	fdd->write_q->buf + fdd->write_q->buf_offset,
-	fdd->write_q->len - fdd->write_q->buf_offset);
-    debug(6, 3) ("diskHandleWrite: FD %d len = %d\n", fd, len);
-    statCounter.syscalls.disk.writes++;
-    fd_bytes(fd, len, FD_WRITE);
-    if (len < 0) {
-	if (!ignoreErrno(errno)) {
-	    status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
-	    debug(50, 1) ("diskHandleWrite: FD %d: disk write error: %s\n",
-		fd, xstrerror());
-	    /*
-	     * If there is no write callback, then this file is
-	     * most likely something important like a log file, or
-	     * an interprocess pipe.  Its not a swapfile.  We feel
-	     * that a write failure on a log file is rather important,
-	     * and Squid doesn't otherwise deal with this condition.
-	     * So to get the administrators attention, we exit with
-	     * a fatal message.
-	     */
-	    if (fdd->wrt_handle == NULL)
-		fatal("Write failure -- check your disk space and cache.log");
-	    /*
-	     * If there is a write failure, then we notify the
-	     * upper layer via the callback, at the end of this
-	     * function.  Meanwhile, flush all pending buffers
-	     * here.  Let the upper layer decide how to handle the
-	     * failure.  This will prevent experiencing multiple,
-	     * repeated write failures for the same FD because of
-	     * the queued data.
-	     */
-	    do {
-		fdd->write_q = q->next;
-		if (q->free_func)
-		    (q->free_func) (q->buf);
-		if (q) {
-		    memFree(q, MEM_DWRITE_Q);
-		    q = NULL;
-		}
-	    } while ((q = fdd->write_q));
-	}
-	len = 0;
-    }
-    if (q != NULL) {
-	/* q might become NULL from write failure above */
-	q->buf_offset += len;
-	if (q->buf_offset > q->len)
-	    debug(50, 1) ("diskHandleWriteComplete: q->buf_offset > q->len (%p,%d, %d, %d FD %d)\n",
-		q, (int) q->buf_offset, q->len, len, fd);
-	assert(q->buf_offset <= q->len);
-	if (q->buf_offset == q->len) {
-	    /* complete write */
-	    fdd->write_q = q->next;
-	    if (q->free_func)
-		(q->free_func) (q->buf);
-	    if (q) {
-		memFree(q, MEM_DWRITE_Q);
-		q = NULL;
-	    }
-	}
-    }
-    if (fdd->write_q == NULL) {
-	/* no more data */
-	fdd->write_q_tail = NULL;
-    } else {
-	/* another block is queued */
-	diskCombineWrites(fdd);
-	commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
-	F->flags.write_daemon = 1;
-    }
-    do_close = F->flags.close_request;
-    if (fdd->wrt_handle) {
-	DWCB *callback = fdd->wrt_handle;
-	void *cbdata;
-	fdd->wrt_handle = NULL;
-	if (cbdataReferenceValidDone(fdd->wrt_handle_data, &cbdata)) {
-	    callback(fd, status, len, cbdata);
-	    /*
-	     * NOTE, this callback can close the FD, so we must
-	     * not touch 'F', 'fdd', etc. after this.
-	     */
-	    PROF_stop(diskHandleWrite);
-	    return;
-	    /* XXX But what about close_request??? */
-	}
-    }
-    if (do_close)
-	file_close(fd);
-    PROF_stop(diskHandleWrite);
-}
-
-
-/* write block to a file */
-/* write back queue. Only one writer at a time. */
-/* call a handle when writing is complete. */
-void
-file_write(int fd,
-    off_t file_offset,
-    void *ptr_to_buf,
-    int len,
-    DWCB * handle,
-    void *handle_data,
-    FREE * free_func)
-{
-    dwrite_q *wq = NULL;
-    fde *F = &fd_table[fd];
-    PROF_start(file_write);
-    assert(fd >= 0);
-    assert(F->flags.open);
-    /* if we got here. Caller is eligible to write. */
-    wq = memAllocate(MEM_DWRITE_Q);
-    wq->file_offset = file_offset;
-    wq->buf = ptr_to_buf;
-    wq->len = len;
-    wq->buf_offset = 0;
-    wq->next = NULL;
-    wq->free_func = free_func;
-    if (!F->disk.wrt_handle_data) {
-	F->disk.wrt_handle = handle;
-	F->disk.wrt_handle_data = cbdataReference(handle_data);
-    } else {
-	/* Detect if there is multiple concurrent users of this fd.. we only support one callback */
-	assert(F->disk.wrt_handle_data == handle_data && F->disk.wrt_handle == handle);
-    }
-    /* add to queue */
-    if (F->disk.write_q == NULL) {
-	/* empty queue */
-	F->disk.write_q = F->disk.write_q_tail = wq;
-    } else {
-	F->disk.write_q_tail->next = wq;
-	F->disk.write_q_tail = wq;
-    }
-    if (!F->flags.write_daemon) {
-	diskHandleWrite(fd, NULL);
-    }
-    PROF_stop(file_write);
-}
-
-/*
- * a wrapper around file_write to allow for MemBuf to be file_written
- * in a snap
- */
-void
-file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
-{
-    file_write(fd, off, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
-}
-
-/* Read from FD */
-static void
-diskHandleRead(int fd, void *data)
-{
-    dread_ctrl *ctrl_dat = data;
-    fde *F = &fd_table[fd];
-    int len;
-    int rc = DISK_OK;
-    /*
-     * FD < 0 indicates premature close; we just have to free
-     * the state data.
-     */
-    if (fd < 0) {
-	memFree(ctrl_dat, MEM_DREAD_CTRL);
-	return;
-    }
-    PROF_start(diskHandleRead);
-    if (F->disk.offset != ctrl_dat->offset) {
-	debug(6, 3) ("diskHandleRead: FD %d seeking to offset %d\n",
-	    fd, (int) ctrl_dat->offset);
-	lseek(fd, ctrl_dat->offset, SEEK_SET);	/* XXX ignore return? */
-	statCounter.syscalls.disk.seeks++;
-	F->disk.offset = ctrl_dat->offset;
-    }
-    errno = 0;
-    len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
-    if (len > 0)
-	F->disk.offset += len;
-    statCounter.syscalls.disk.reads++;
-    fd_bytes(fd, len, FD_READ);
-    if (len < 0) {
-	if (ignoreErrno(errno)) {
-	    commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
-	    PROF_stop(diskHandleRead);
-	    return;
-	}
-	debug(50, 1) ("diskHandleRead: FD %d: %s\n", fd, xstrerror());
-	len = 0;
-	rc = DISK_ERROR;
-    } else if (len == 0) {
-	rc = DISK_EOF;
-    }
-    if (cbdataReferenceValid(ctrl_dat->client_data))
-	ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
-    cbdataReferenceDone(ctrl_dat->client_data);
-    memFree(ctrl_dat, MEM_DREAD_CTRL);
-    PROF_stop(diskHandleRead);
-}
-
-
-/* start read operation */
-/* buffer must be allocated from the caller. 
- * It must have at least req_len space in there. 
- * call handler when a reading is complete. */
-void
-file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
-{
-    dread_ctrl *ctrl_dat;
-    PROF_start(file_read);
-    assert(fd >= 0);
-    ctrl_dat = memAllocate(MEM_DREAD_CTRL);
-    ctrl_dat->fd = fd;
-    ctrl_dat->offset = offset;
-    ctrl_dat->req_len = req_len;
-    ctrl_dat->buf = buf;
-    ctrl_dat->end_of_file = 0;
-    ctrl_dat->handler = handler;
-    ctrl_dat->client_data = cbdataReference(client_data);
-    diskHandleRead(fd, ctrl_dat);
-    PROF_stop(file_read);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/disk.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,418 @@
+
+/*
+ * $Id: disk.cc,v 1.1.2.1 2002/10/11 15:40:48 rbcollins Exp $
+ *
+ * DEBUG: section 6     Disk I/O Routines
+ * AUTHOR: Harvest Derived
+ *
+ * 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 PF diskHandleRead;
+static PF diskHandleWrite;
+
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_)
+static int
+diskWriteIsComplete(int fd)
+{
+    return fd_table[fd].disk.write_q ? 0 : 1;
+}
+#endif
+
+void
+disk_init(void)
+{
+    (void) 0;
+}
+
+/*
+ * opens a disk file specified by 'path'.  This function always
+ * blocks!  There is no callback.
+ */
+int
+file_open(const char *path, int mode)
+{
+    int fd;
+    PROF_start(file_open);
+    if (FILE_MODE(mode) == O_WRONLY)
+	mode |= O_APPEND;
+    mode |= SQUID_NONBLOCK;
+    errno = 0;
+    fd = open(path, mode, 0644);
+    statCounter.syscalls.disk.opens++;
+    if (fd < 0) {
+	debug(50, 3) ("file_open: error opening file %s: %s\n", path,
+	    xstrerror());
+	fd = DISK_ERROR;
+    } else {
+	debug(6, 5) ("file_open: FD %d\n", fd);
+	commSetCloseOnExec(fd);
+	fd_open(fd, FD_FILE, path);
+    }
+    PROF_stop(file_open);
+    return fd;
+}
+
+
+/* close a disk file. */
+void
+file_close(int fd)
+{
+    fde *F = &fd_table[fd];
+    PF *read_callback;
+    PROF_start(file_close);
+    assert(fd >= 0);
+    assert(F->flags.open);
+    if ((read_callback = F->read_handler)) {
+	F->read_handler = NULL;
+	read_callback(-1, F->read_data);
+    }
+    if (F->flags.write_daemon) {
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_)
+	/*
+	 * on some operating systems, you can not delete or rename
+	 * open files, so we won't allow delayed close.
+	 */
+	while (!diskWriteIsComplete(fd))
+	    diskHandleWrite(fd, NULL);
+#else
+	F->flags.close_request = 1;
+	debug(6, 2) ("file_close: FD %d, delaying close\n", fd);
+	PROF_stop(file_close);
+	return;
+#endif
+    }
+    /*
+     * Assert there is no write callback.  Otherwise we might be
+     * leaking write state data by closing the descriptor
+     */
+    assert(F->write_handler == NULL);
+    F->flags.closing = 1;
+#if CALL_FSYNC_BEFORE_CLOSE
+    fsync(fd);
+#endif
+    close(fd);
+    debug(6, F->flags.close_request ? 2 : 5)
+	("file_close: FD %d, really closing\n", fd);
+    fd_close(fd);
+    statCounter.syscalls.disk.closes++;
+    PROF_stop(file_close);
+}
+
+/*
+ * This function has the purpose of combining multiple writes.  This is
+ * to facilitate the ASYNC_IO option since it can only guarantee 1
+ * write to a file per trip around the comm.c select() loop. That's bad
+ * because more than 1 write can be made to the access.log file per
+ * trip, and so this code is purely designed to help batch multiple
+ * sequential writes to the access.log file.  Squid will never issue
+ * multiple writes for any other file type during 1 trip around the
+ * select() loop.       --SLF
+ */
+static void
+diskCombineWrites(struct _fde_disk *fdd)
+{
+    int len = 0;
+    dwrite_q *q = NULL;
+    dwrite_q *wq = NULL;
+    /*
+     * We need to combine multiple write requests on an FD's write
+     * queue But only if we don't need to seek() in between them, ugh!
+     * XXX This currently ignores any seeks (file_offset)
+     */
+    if (fdd->write_q != NULL && fdd->write_q->next != NULL) {
+	len = 0;
+	for (q = fdd->write_q; q != NULL; q = q->next)
+	    len += q->len - q->buf_offset;
+	wq = (dwrite_q *)memAllocate(MEM_DWRITE_Q);
+	wq->buf = (char *)xmalloc(len);
+	wq->len = 0;
+	wq->buf_offset = 0;
+	wq->next = NULL;
+	wq->free_func = xfree;
+	do {
+	    q = fdd->write_q;
+	    len = q->len - q->buf_offset;
+	    xmemcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
+	    wq->len += len;
+	    fdd->write_q = q->next;
+	    if (q->free_func)
+		(q->free_func) (q->buf);
+	    if (q) {
+		memFree(q, MEM_DWRITE_Q);
+		q = NULL;
+	    }
+	} while (fdd->write_q != NULL);
+	fdd->write_q_tail = wq;
+	fdd->write_q = wq;
+    }
+}
+
+/* write handler */
+static void
+diskHandleWrite(int fd, void *notused)
+{
+    int len = 0;
+    fde *F = &fd_table[fd];
+    struct _fde_disk *fdd = &F->disk;
+    dwrite_q *q = fdd->write_q;
+    int status = DISK_OK;
+    int do_close;
+    if (NULL == q)
+	return;
+    PROF_start(diskHandleWrite);
+    debug(6, 3) ("diskHandleWrite: FD %d\n", fd);
+    F->flags.write_daemon = 0;
+    assert(fdd->write_q != NULL);
+    assert(fdd->write_q->len > fdd->write_q->buf_offset);
+    debug(6, 3) ("diskHandleWrite: FD %d writing %d bytes\n",
+	fd, (int) (fdd->write_q->len - fdd->write_q->buf_offset));
+    errno = 0;
+    if (fdd->write_q->file_offset != -1)
+	lseek(fd, fdd->write_q->file_offset, SEEK_SET);
+    len = FD_WRITE_METHOD(fd,
+	fdd->write_q->buf + fdd->write_q->buf_offset,
+	fdd->write_q->len - fdd->write_q->buf_offset);
+    debug(6, 3) ("diskHandleWrite: FD %d len = %d\n", fd, len);
+    statCounter.syscalls.disk.writes++;
+    fd_bytes(fd, len, FD_WRITE);
+    if (len < 0) {
+	if (!ignoreErrno(errno)) {
+	    status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
+	    debug(50, 1) ("diskHandleWrite: FD %d: disk write error: %s\n",
+		fd, xstrerror());
+	    /*
+	     * If there is no write callback, then this file is
+	     * most likely something important like a log file, or
+	     * an interprocess pipe.  Its not a swapfile.  We feel
+	     * that a write failure on a log file is rather important,
+	     * and Squid doesn't otherwise deal with this condition.
+	     * So to get the administrators attention, we exit with
+	     * a fatal message.
+	     */
+	    if (fdd->wrt_handle == NULL)
+		fatal("Write failure -- check your disk space and cache.log");
+	    /*
+	     * If there is a write failure, then we notify the
+	     * upper layer via the callback, at the end of this
+	     * function.  Meanwhile, flush all pending buffers
+	     * here.  Let the upper layer decide how to handle the
+	     * failure.  This will prevent experiencing multiple,
+	     * repeated write failures for the same FD because of
+	     * the queued data.
+	     */
+	    do {
+		fdd->write_q = q->next;
+		if (q->free_func)
+		    (q->free_func) (q->buf);
+		if (q) {
+		    memFree(q, MEM_DWRITE_Q);
+		    q = NULL;
+		}
+	    } while ((q = fdd->write_q));
+	}
+	len = 0;
+    }
+    if (q != NULL) {
+	/* q might become NULL from write failure above */
+	q->buf_offset += len;
+	if (q->buf_offset > q->len)
+	    debug(50, 1) ("diskHandleWriteComplete: q->buf_offset > q->len (%p,%d, %d, %d FD %d)\n",
+		q, (int) q->buf_offset, q->len, len, fd);
+	assert(q->buf_offset <= q->len);
+	if (q->buf_offset == q->len) {
+	    /* complete write */
+	    fdd->write_q = q->next;
+	    if (q->free_func)
+		(q->free_func) (q->buf);
+	    if (q) {
+		memFree(q, MEM_DWRITE_Q);
+		q = NULL;
+	    }
+	}
+    }
+    if (fdd->write_q == NULL) {
+	/* no more data */
+	fdd->write_q_tail = NULL;
+    } else {
+	/* another block is queued */
+	diskCombineWrites(fdd);
+	commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
+	F->flags.write_daemon = 1;
+    }
+    do_close = F->flags.close_request;
+    if (fdd->wrt_handle) {
+	DWCB *callback = fdd->wrt_handle;
+	void *cbdata;
+	fdd->wrt_handle = NULL;
+	if (cbdataReferenceValidDone(fdd->wrt_handle_data, &cbdata)) {
+	    callback(fd, status, len, cbdata);
+	    /*
+	     * NOTE, this callback can close the FD, so we must
+	     * not touch 'F', 'fdd', etc. after this.
+	     */
+	    PROF_stop(diskHandleWrite);
+	    return;
+	    /* XXX But what about close_request??? */
+	}
+    }
+    if (do_close)
+	file_close(fd);
+    PROF_stop(diskHandleWrite);
+}
+
+
+/* write block to a file */
+/* write back queue. Only one writer at a time. */
+/* call a handle when writing is complete. */
+void
+file_write(int fd,
+    off_t file_offset,
+    void *ptr_to_buf,
+    int len,
+    DWCB * handle,
+    void *handle_data,
+    FREE * free_func)
+{
+    dwrite_q *wq = NULL;
+    fde *F = &fd_table[fd];
+    PROF_start(file_write);
+    assert(fd >= 0);
+    assert(F->flags.open);
+    /* if we got here. Caller is eligible to write. */
+    wq = (dwrite_q *)memAllocate(MEM_DWRITE_Q);
+    wq->file_offset = file_offset;
+    wq->buf = (char *)ptr_to_buf;
+    wq->len = len;
+    wq->buf_offset = 0;
+    wq->next = NULL;
+    wq->free_func = free_func;
+    if (!F->disk.wrt_handle_data) {
+	F->disk.wrt_handle = handle;
+	F->disk.wrt_handle_data = cbdataReference(handle_data);
+    } else {
+	/* Detect if there is multiple concurrent users of this fd.. we only support one callback */
+	assert(F->disk.wrt_handle_data == handle_data && F->disk.wrt_handle == handle);
+    }
+    /* add to queue */
+    if (F->disk.write_q == NULL) {
+	/* empty queue */
+	F->disk.write_q = F->disk.write_q_tail = wq;
+    } else {
+	F->disk.write_q_tail->next = wq;
+	F->disk.write_q_tail = wq;
+    }
+    if (!F->flags.write_daemon) {
+	diskHandleWrite(fd, NULL);
+    }
+    PROF_stop(file_write);
+}
+
+/*
+ * a wrapper around file_write to allow for MemBuf to be file_written
+ * in a snap
+ */
+void
+file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
+{
+    file_write(fd, off, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
+}
+
+/* Read from FD */
+static void
+diskHandleRead(int fd, void *data)
+{
+    dread_ctrl *ctrl_dat = (dread_ctrl *)data;
+    fde *F = &fd_table[fd];
+    int len;
+    int rc = DISK_OK;
+    /*
+     * FD < 0 indicates premature close; we just have to free
+     * the state data.
+     */
+    if (fd < 0) {
+	memFree(ctrl_dat, MEM_DREAD_CTRL);
+	return;
+    }
+    PROF_start(diskHandleRead);
+    if (F->disk.offset != ctrl_dat->offset) {
+	debug(6, 3) ("diskHandleRead: FD %d seeking to offset %d\n",
+	    fd, (int) ctrl_dat->offset);
+	lseek(fd, ctrl_dat->offset, SEEK_SET);	/* XXX ignore return? */
+	statCounter.syscalls.disk.seeks++;
+	F->disk.offset = ctrl_dat->offset;
+    }
+    errno = 0;
+    len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
+    if (len > 0)
+	F->disk.offset += len;
+    statCounter.syscalls.disk.reads++;
+    fd_bytes(fd, len, FD_READ);
+    if (len < 0) {
+	if (ignoreErrno(errno)) {
+	    commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
+	    PROF_stop(diskHandleRead);
+	    return;
+	}
+	debug(50, 1) ("diskHandleRead: FD %d: %s\n", fd, xstrerror());
+	len = 0;
+	rc = DISK_ERROR;
+    } else if (len == 0) {
+	rc = DISK_EOF;
+    }
+    if (cbdataReferenceValid(ctrl_dat->client_data))
+	ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
+    cbdataReferenceDone(ctrl_dat->client_data);
+    memFree(ctrl_dat, MEM_DREAD_CTRL);
+    PROF_stop(diskHandleRead);
+}
+
+
+/* start read operation */
+/* buffer must be allocated from the caller. 
+ * It must have at least req_len space in there. 
+ * call handler when a reading is complete. */
+void
+file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
+{
+    dread_ctrl *ctrl_dat;
+    PROF_start(file_read);
+    assert(fd >= 0);
+    ctrl_dat = (dread_ctrl *)memAllocate(MEM_DREAD_CTRL);
+    ctrl_dat->fd = fd;
+    ctrl_dat->offset = offset;
+    ctrl_dat->req_len = req_len;
+    ctrl_dat->buf = buf;
+    ctrl_dat->end_of_file = 0;
+    ctrl_dat->handler = handler;
+    ctrl_dat->client_data = cbdataReference(client_data);
+    diskHandleRead(fd, ctrl_dat);
+    PROF_stop(file_read);
+}
--- squid/src/fqdncache.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,650 +0,0 @@
-
-/*
- * $Id: fqdncache.c,v 1.17.6.1 2002/10/04 07:07:25 rbcollins Exp $
- *
- * DEBUG: section 35    FQDN Cache
- * AUTHOR: Harvest Derived
- *
- * 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"
-#include "Store.h"
-
-#define FQDN_LOW_WATER       90
-#define FQDN_HIGH_WATER      95
-
-typedef struct _fqdncache_entry fqdncache_entry;
-
-struct _fqdncache_entry {
-    hash_link hash;		/* must be first */
-    time_t lastref;
-    time_t expires;
-    unsigned char name_count;
-    char *names[FQDN_MAX_NAMES + 1];
-    FQDNH *handler;
-    void *handlerData;
-    char *error_message;
-    struct timeval request_time;
-    dlink_node lru;
-    unsigned short locks;
-    struct {
-	unsigned int negcached:1;
-	unsigned int fromhosts:1;
-    } flags;
-};
-
-static struct {
-    int requests;
-    int replies;
-    int hits;
-    int misses;
-    int negative_hits;
-    int errors;
-    int ghba_calls;		/* # calls to blocking gethostbyaddr() */
-} FqdncacheStats;
-
-static dlink_list lru_list;
-
-#if USE_DNSSERVERS
-static HLPCB fqdncacheHandleReply;
-static fqdncache_entry *fqdncacheParse(const char *buf);
-#else
-static IDNSCB fqdncacheHandleReply;
-static fqdncache_entry *fqdncacheParse(rfc1035_rr *, int);
-#endif
-static void fqdncacheRelease(fqdncache_entry *);
-static fqdncache_entry *fqdncacheCreateEntry(const char *name);
-static void fqdncacheCallback(fqdncache_entry *);
-static fqdncache_entry *fqdncache_get(const char *);
-static FQDNH dummy_handler;
-static int fqdncacheExpiredEntry(const fqdncache_entry *);
-static void fqdncacheLockEntry(fqdncache_entry * f);
-static void fqdncacheUnlockEntry(fqdncache_entry * f);
-static FREE fqdncacheFreeEntry;
-static void fqdncacheAddEntry(fqdncache_entry * f);
-
-static hash_table *fqdn_table = NULL;
-
-static long fqdncache_low = 180;
-static long fqdncache_high = 200;
-
-/* removes the given fqdncache entry */
-static void
-fqdncacheRelease(fqdncache_entry * f)
-{
-    int k;
-    hash_remove_link(fqdn_table, (hash_link *) f);
-    for (k = 0; k < (int) f->name_count; k++)
-	safe_free(f->names[k]);
-    debug(35, 5) ("fqdncacheRelease: Released FQDN record for '%s'.\n",
-	hashKeyStr(&f->hash));
-    dlinkDelete(&f->lru, &lru_list);
-    safe_free(f->hash.key);
-    safe_free(f->error_message);
-    memFree(f, MEM_FQDNCACHE_ENTRY);
-}
-
-/* return match for given name */
-static fqdncache_entry *
-fqdncache_get(const char *name)
-{
-    hash_link *e;
-    static fqdncache_entry *f;
-    f = NULL;
-    if (fqdn_table) {
-	if ((e = hash_lookup(fqdn_table, name)) != NULL)
-	    f = (fqdncache_entry *) e;
-    }
-    return f;
-}
-
-static int
-fqdncacheExpiredEntry(const fqdncache_entry * f)
-{
-    /* all static entries are locked, so this takes care of them too */
-    if (f->locks != 0)
-	return 0;
-    if (f->expires > squid_curtime)
-	return 0;
-    return 1;
-}
-
-void
-fqdncache_purgelru(void *notused)
-{
-    dlink_node *m;
-    dlink_node *prev = NULL;
-    fqdncache_entry *f;
-    int removed = 0;
-    eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
-    for (m = lru_list.tail; m; m = prev) {
-	if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low)
-	    break;
-	prev = m->prev;
-	f = m->data;
-	if (f->locks != 0)
-	    continue;
-	fqdncacheRelease(f);
-	removed++;
-    }
-    debug(35, 9) ("fqdncache_purgelru: removed %d entries\n", removed);
-}
-
-static void
-purge_entries_fromhosts(void)
-{
-    dlink_node *m = lru_list.head;
-    fqdncache_entry *i = NULL;
-    fqdncache_entry *t;
-    while (m) {
-	if (i != NULL) {	/* need to delay deletion */
-	    fqdncacheRelease(i);	/* we just override locks */
-	    i = NULL;
-	}
-	t = m->data;
-	if (t->flags.fromhosts)
-	    i = t;
-	m = m->next;
-    }
-    if (i != NULL)
-	fqdncacheRelease(i);
-}
-
-/* create blank fqdncache_entry */
-static fqdncache_entry *
-fqdncacheCreateEntry(const char *name)
-{
-    static fqdncache_entry *f;
-    f = memAllocate(MEM_FQDNCACHE_ENTRY);
-    f->hash.key = xstrdup(name);
-    f->expires = squid_curtime + Config.negativeDnsTtl;
-    return f;
-}
-
-static void
-fqdncacheAddEntry(fqdncache_entry * f)
-{
-    hash_link *e = hash_lookup(fqdn_table, f->hash.key);
-    if (NULL != e) {
-	/* avoid colission */
-	fqdncache_entry *q = (fqdncache_entry *) e;
-	fqdncacheRelease(q);
-    }
-    hash_join(fqdn_table, &f->hash);
-    dlinkAdd(f, &f->lru, &lru_list);
-    f->lastref = squid_curtime;
-}
-
-/* walks down the pending list, calling handlers */
-static void
-fqdncacheCallback(fqdncache_entry * f)
-{
-    FQDNH *callback;
-    void *cbdata;
-    f->lastref = squid_curtime;
-    if (!f->handler)
-	return;
-    fqdncacheLockEntry(f);
-    callback = f->handler;
-    f->handler = NULL;
-    if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
-	dns_error_message = f->error_message;
-	callback(f->flags.negcached ? NULL : f->names[0], cbdata);
-    }
-    fqdncacheUnlockEntry(f);
-}
-
-static fqdncache_entry *
-#if USE_DNSSERVERS
-fqdncacheParse(const char *inbuf)
-{
-    LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
-    char *token;
-    static fqdncache_entry f;
-    int ttl;
-    memset(&f, '\0', sizeof(f));
-    f.expires = squid_curtime;
-    f.flags.negcached = 1;
-    if (inbuf == NULL) {
-	debug(35, 1) ("fqdncacheParse: Got <NULL> reply\n");
-	return &f;
-    }
-    xstrncpy(buf, inbuf, DNS_INBUF_SZ);
-    debug(35, 5) ("fqdncacheParse: parsing: {%s}\n", buf);
-    token = strtok(buf, w_space);
-    if (NULL == token) {
-	debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting '$name'\n");
-	return &f;
-    }
-    if (0 == strcmp(token, "$fail")) {
-	f.expires = squid_curtime + Config.negativeDnsTtl;
-	token = strtok(NULL, "\n");
-	assert(NULL != token);
-	f.error_message = xstrdup(token);
-	return &f;
-    }
-    if (0 != strcmp(token, "$name")) {
-	debug(35, 1) ("fqdncacheParse: Got '%s', expecting '$name'\n", token);
-	return &f;
-    }
-    token = strtok(NULL, w_space);
-    if (NULL == token) {
-	debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting TTL\n");
-	return &f;
-    }
-    f.flags.negcached = 0;
-    ttl = atoi(token);
-    if (ttl > 0)
-	f.expires = squid_curtime + ttl;
-    else
-	f.expires = squid_curtime + Config.positiveDnsTtl;
-    token = strtok(NULL, w_space);
-    if (NULL != token) {
-	f.names[0] = xstrdup(token);
-	f.name_count = 1;
-    }
-    return &f;
-}
-#else
-fqdncacheParse(rfc1035_rr * answers, int nr)
-{
-    static fqdncache_entry f;
-    int k;
-    int na = 0;
-    memset(&f, '\0', sizeof(f));
-    f.expires = squid_curtime;
-    f.flags.negcached = 1;
-    if (nr < 0) {
-	debug(35, 3) ("fqdncacheParse: Lookup failed (error %d)\n",
-	    rfc1035_errno);
-	assert(rfc1035_error_message);
-	f.error_message = xstrdup(rfc1035_error_message);
-	return &f;
-    }
-    if (nr == 0) {
-	debug(35, 3) ("fqdncacheParse: No DNS records\n");
-	f.error_message = xstrdup("No DNS records");
-	return &f;
-    }
-    debug(35, 3) ("fqdncacheParse: %d answers\n", nr);
-    assert(answers);
-    for (k = 0; k < nr; k++) {
-	if (answers[k].type != RFC1035_TYPE_PTR)
-	    continue;
-	if (answers[k]._class != RFC1035_CLASS_IN)
-	    continue;
-	na++;
-	f.flags.negcached = 0;
-	f.names[0] = xstrdup(answers[k].rdata);
-	f.name_count = 1;
-	f.expires = squid_curtime + answers[k].ttl;
-	return &f;
-    }
-    debug(35, 1) ("fqdncacheParse: No PTR record\n");
-    f.error_message = xstrdup("No PTR record");
-    return &f;
-}
-#endif
-
-static void
-#if USE_DNSSERVERS
-fqdncacheHandleReply(void *data, char *reply)
-#else
-fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na)
-#endif
-{
-    int n;
-    generic_cbdata *c = data;
-    fqdncache_entry *f = c->data;
-    fqdncache_entry *x = NULL;
-    cbdataFree(c);
-    c = NULL;
-    n = ++FqdncacheStats.replies;
-    statHistCount(&statCounter.dns.svc_time,
-	tvSubMsec(f->request_time, current_time));
-#if USE_DNSSERVERS
-    x = fqdncacheParse(reply);
-#else
-    x = fqdncacheParse(answers, na);
-#endif
-    assert(x);
-    f->name_count = x->name_count;
-    for (n = 0; n < (int) f->name_count; n++)
-	f->names[n] = x->names[n];
-    f->error_message = x->error_message;
-    f->expires = x->expires;
-    f->flags = x->flags;
-    fqdncacheAddEntry(f);
-    fqdncacheCallback(f);
-}
-
-void
-fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData)
-{
-    fqdncache_entry *f = NULL;
-    char *name = inet_ntoa(addr);
-    generic_cbdata *c;
-    assert(handler);
-    debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name);
-    FqdncacheStats.requests++;
-    if (name == NULL || name[0] == '\0') {
-	debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n");
-	handler(NULL, handlerData);
-	return;
-    }
-    f = fqdncache_get(name);
-    if (NULL == f) {
-	/* miss */
-	(void) 0;
-    } else if (fqdncacheExpiredEntry(f)) {
-	/* hit, but expired -- bummer */
-	fqdncacheRelease(f);
-	f = NULL;
-    } else {
-	/* hit */
-	debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name);
-	if (f->flags.negcached)
-	    FqdncacheStats.negative_hits++;
-	else
-	    FqdncacheStats.hits++;
-	f->handler = handler;
-	f->handlerData = cbdataReference(handlerData);
-	fqdncacheCallback(f);
-	return;
-    }
-
-    debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name);
-    FqdncacheStats.misses++;
-    f = fqdncacheCreateEntry(name);
-    f->handler = handler;
-    f->handlerData = cbdataReference(handlerData);
-    f->request_time = current_time;
-    c = cbdataAlloc(generic_cbdata);
-    c->data = f;
-#if USE_DNSSERVERS
-    dnsSubmit(hashKeyStr(&f->hash), fqdncacheHandleReply, c);
-#else
-    idnsPTRLookup(addr, fqdncacheHandleReply, c);
-#endif
-}
-
-/* initialize the fqdncache */
-void
-fqdncache_init(void)
-{
-    int n;
-    if (fqdn_table)
-	return;
-    debug(35, 3) ("Initializing FQDN Cache...\n");
-    memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
-    memset(&lru_list, '\0', sizeof(lru_list));
-    fqdncache_high = (long) (((float) Config.fqdncache.size *
-	    (float) FQDN_HIGH_WATER) / (float) 100);
-    fqdncache_low = (long) (((float) Config.fqdncache.size *
-	    (float) FQDN_LOW_WATER) / (float) 100);
-    n = hashPrime(fqdncache_high / 4);
-    fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4);
-    cachemgrRegister("fqdncache",
-	"FQDN Cache Stats and Contents",
-	fqdnStats, 0, 1);
-    memDataInit(MEM_FQDNCACHE_ENTRY, "fqdncache_entry",
-	sizeof(fqdncache_entry), 0);
-}
-
-const char *
-fqdncache_gethostbyaddr(struct in_addr addr, int flags)
-{
-    char *name = inet_ntoa(addr);
-    fqdncache_entry *f = NULL;
-    struct in_addr ip;
-    assert(name);
-    FqdncacheStats.requests++;
-    f = fqdncache_get(name);
-    if (NULL == f) {
-	(void) 0;
-    } else if (fqdncacheExpiredEntry(f)) {
-	fqdncacheRelease(f);
-	f = NULL;
-    } else if (f->flags.negcached) {
-	FqdncacheStats.negative_hits++;
-	dns_error_message = f->error_message;
-	return NULL;
-    } else {
-	FqdncacheStats.hits++;
-	f->lastref = squid_curtime;
-	return f->names[0];
-    }
-    /* check if it's already a FQDN address in text form. */
-    if (!safe_inet_addr(name, &ip))
-	return name;
-    FqdncacheStats.misses++;
-    if (flags & FQDN_LOOKUP_IF_MISS)
-	fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL);
-    return NULL;
-}
-
-
-/* process objects list */
-void
-fqdnStats(StoreEntry * sentry)
-{
-    fqdncache_entry *f = NULL;
-    int k;
-    int ttl;
-    if (fqdn_table == NULL)
-	return;
-    storeAppendPrintf(sentry, "FQDN Cache Statistics:\n");
-    storeAppendPrintf(sentry, "FQDNcache Entries: %d\n",
-	memInUse(MEM_FQDNCACHE_ENTRY));
-    storeAppendPrintf(sentry, "FQDNcache Requests: %d\n",
-	FqdncacheStats.requests);
-    storeAppendPrintf(sentry, "FQDNcache Hits: %d\n",
-	FqdncacheStats.hits);
-    storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n",
-	FqdncacheStats.negative_hits);
-    storeAppendPrintf(sentry, "FQDNcache Misses: %d\n",
-	FqdncacheStats.misses);
-    storeAppendPrintf(sentry, "Blocking calls to gethostbyaddr(): %d\n",
-	FqdncacheStats.ghba_calls);
-    storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n");
-    storeAppendPrintf(sentry, "%-15.15s %3s %3s %3s %s\n",
-	"Address", "Flg", "TTL", "Cnt", "Hostnames");
-    hash_first(fqdn_table);
-    while ((f = (fqdncache_entry *) hash_next(fqdn_table))) {
-	ttl = (f->flags.fromhosts ? -1 : (f->expires - squid_curtime));
-	storeAppendPrintf(sentry, "%-15.15s  %c%c %3.3d % 3d",
-	    hashKeyStr(&f->hash),
-	    f->flags.negcached ? 'N' : ' ',
-	    f->flags.fromhosts ? 'H' : ' ',
-	    ttl,
-	    (int) f->name_count);
-	for (k = 0; k < (int) f->name_count; k++)
-	    storeAppendPrintf(sentry, " %s", f->names[k]);
-	storeAppendPrintf(sentry, "\n");
-    }
-}
-
-static void
-dummy_handler(const char *bufnotused, void *datanotused)
-{
-    return;
-}
-
-const char *
-fqdnFromAddr(struct in_addr addr)
-{
-    const char *n;
-    static char buf[32];
-    if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
-	return n;
-    xstrncpy(buf, inet_ntoa(addr), 32);
-    return buf;
-}
-
-static void
-fqdncacheLockEntry(fqdncache_entry * f)
-{
-    if (f->locks++ == 0) {
-	dlinkDelete(&f->lru, &lru_list);
-	dlinkAdd(f, &f->lru, &lru_list);
-    }
-}
-
-static void
-fqdncacheUnlockEntry(fqdncache_entry * f)
-{
-    assert(f->locks > 0);
-    f->locks--;
-    if (fqdncacheExpiredEntry(f))
-	fqdncacheRelease(f);
-}
-
-static void
-fqdncacheFreeEntry(void *data)
-{
-    fqdncache_entry *f = data;
-    int k;
-    for (k = 0; k < (int) f->name_count; k++)
-	safe_free(f->names[k]);
-    safe_free(f->hash.key);
-    safe_free(f->error_message);
-    memFree(f, MEM_FQDNCACHE_ENTRY);
-}
-
-void
-fqdncacheFreeMemory(void)
-{
-    hashFreeItems(fqdn_table, fqdncacheFreeEntry);
-    hashFreeMemory(fqdn_table);
-    fqdn_table = NULL;
-}
-
-/* Recalculate FQDN cache size upon reconfigure */
-void
-fqdncache_restart(void)
-{
-    fqdncache_high = (long) (((float) Config.fqdncache.size *
-	    (float) FQDN_HIGH_WATER) / (float) 100);
-    fqdncache_low = (long) (((float) Config.fqdncache.size *
-	    (float) FQDN_LOW_WATER) / (float) 100);
-    purge_entries_fromhosts();
-}
-
-/*
- *  adds a "static" entry from /etc/hosts.  the worldist is to be
- *  managed by the caller, including pointed-to strings
- */
-void
-fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames)
-{
-    fqdncache_entry *fce;
-    int j = 0;
-    if ((fce = fqdncache_get(addr))) {
-	if (1 == fce->flags.fromhosts) {
-	    fqdncacheUnlockEntry(fce);
-	} else if (fce->locks > 0) {
-	    debug(35, 1) ("fqdncacheAddEntryFromHosts: can't add static entry for locked address '%s'\n", addr);
-	    return;
-	} else {
-	    fqdncacheRelease(fce);
-	}
-    }
-    fce = fqdncacheCreateEntry(addr);
-    while (hostnames) {
-	fce->names[j] = xstrdup(hostnames->key);
-	j++;
-	hostnames = hostnames->next;
-	if (j >= FQDN_MAX_NAMES)
-	    break;
-    }
-    fce->name_count = j;
-    fce->names[j] = NULL;	/* it's safe */
-    fce->flags.fromhosts = 1;
-    fqdncacheAddEntry(fce);
-    fqdncacheLockEntry(fce);
-}
-
-
-#ifdef SQUID_SNMP
-/*
- * The function to return the fqdn statistics via SNMP
- */
-
-variable_list *
-snmp_netFqdnFn(variable_list * Var, snint * ErrP)
-{
-    variable_list *Answer = NULL;
-    debug(49, 5) ("snmp_netFqdnFn: Processing request:\n");
-    snmpDebugOid(5, Var->name, Var->name_length);
-    *ErrP = SNMP_ERR_NOERROR;
-    switch (Var->name[LEN_SQ_NET + 1]) {
-    case FQDN_ENT:
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    memInUse(MEM_FQDNCACHE_ENTRY),
-	    SMI_GAUGE32);
-	break;
-    case FQDN_REQ:
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    FqdncacheStats.requests,
-	    SMI_COUNTER32);
-	break;
-    case FQDN_HITS:
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    FqdncacheStats.hits,
-	    SMI_COUNTER32);
-	break;
-    case FQDN_PENDHIT:
-	/* this is now worthless */
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    0,
-	    SMI_GAUGE32);
-	break;
-    case FQDN_NEGHIT:
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    FqdncacheStats.negative_hits,
-	    SMI_COUNTER32);
-	break;
-    case FQDN_MISS:
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    FqdncacheStats.misses,
-	    SMI_COUNTER32);
-	break;
-    case FQDN_GHBN:
-	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    FqdncacheStats.ghba_calls,
-	    SMI_COUNTER32);
-	break;
-    default:
-	*ErrP = SNMP_ERR_NOSUCHNAME;
-	break;
-    }
-    return Answer;
-}
-
-#endif /*SQUID_SNMP */
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fqdncache.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,650 @@
+
+/*
+ * $Id: fqdncache.cc,v 1.1.2.1 2002/10/11 15:40:48 rbcollins Exp $
+ *
+ * DEBUG: section 35    FQDN Cache
+ * AUTHOR: Harvest Derived
+ *
+ * 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"
+#include "Store.h"
+
+#define FQDN_LOW_WATER       90
+#define FQDN_HIGH_WATER      95
+
+typedef struct _fqdncache_entry fqdncache_entry;
+
+struct _fqdncache_entry {
+    hash_link hash;		/* must be first */
+    time_t lastref;
+    time_t expires;
+    unsigned char name_count;
+    char *names[FQDN_MAX_NAMES + 1];
+    FQDNH *handler;
+    void *handlerData;
+    char *error_message;
+    struct timeval request_time;
+    dlink_node lru;
+    unsigned short locks;
+    struct {
+	unsigned int negcached:1;
+	unsigned int fromhosts:1;
+    } flags;
+};
+
+static struct {
+    int requests;
+    int replies;
+    int hits;
+    int misses;
+    int negative_hits;
+    int errors;
+    int ghba_calls;		/* # calls to blocking gethostbyaddr() */
+} FqdncacheStats;
+
+static dlink_list lru_list;
+
+#if USE_DNSSERVERS
+static HLPCB fqdncacheHandleReply;
+static fqdncache_entry *fqdncacheParse(const char *buf);
+#else
+static IDNSCB fqdncacheHandleReply;
+static fqdncache_entry *fqdncacheParse(rfc1035_rr *, int);
+#endif
+static void fqdncacheRelease(fqdncache_entry *);
+static fqdncache_entry *fqdncacheCreateEntry(const char *name);
+static void fqdncacheCallback(fqdncache_entry *);
+static fqdncache_entry *fqdncache_get(const char *);
+static FQDNH dummy_handler;
+static int fqdncacheExpiredEntry(const fqdncache_entry *);
+static void fqdncacheLockEntry(fqdncache_entry * f);
+static void fqdncacheUnlockEntry(fqdncache_entry * f);
+static FREE fqdncacheFreeEntry;
+static void fqdncacheAddEntry(fqdncache_entry * f);
+
+static hash_table *fqdn_table = NULL;
+
+static long fqdncache_low = 180;
+static long fqdncache_high = 200;
+
+/* removes the given fqdncache entry */
+static void
+fqdncacheRelease(fqdncache_entry * f)
+{
+    int k;
+    hash_remove_link(fqdn_table, (hash_link *) f);
+    for (k = 0; k < (int) f->name_count; k++)
+	safe_free(f->names[k]);
+    debug(35, 5) ("fqdncacheRelease: Released FQDN record for '%s'.\n",
+	hashKeyStr(&f->hash));
+    dlinkDelete(&f->lru, &lru_list);
+    safe_free(f->hash.key);
+    safe_free(f->error_message);
+    memFree(f, MEM_FQDNCACHE_ENTRY);
+}
+
+/* return match for given name */
+static fqdncache_entry *
+fqdncache_get(const char *name)
+{
+    hash_link *e;
+    static fqdncache_entry *f;
+    f = NULL;
+    if (fqdn_table) {
+	if ((e = (hash_link *)hash_lookup(fqdn_table, name)) != NULL)
+	    f = (fqdncache_entry *) e;
+    }
+    return f;
+}
+
+static int
+fqdncacheExpiredEntry(const fqdncache_entry * f)
+{
+    /* all static entries are locked, so this takes care of them too */
+    if (f->locks != 0)
+	return 0;
+    if (f->expires > squid_curtime)
+	return 0;
+    return 1;
+}
+
+void
+fqdncache_purgelru(void *notused)
+{
+    dlink_node *m;
+    dlink_node *prev = NULL;
+    fqdncache_entry *f;
+    int removed = 0;
+    eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
+    for (m = lru_list.tail; m; m = prev) {
+	if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low)
+	    break;
+	prev = m->prev;
+	f = (fqdncache_entry *)m->data;
+	if (f->locks != 0)
+	    continue;
+	fqdncacheRelease(f);
+	removed++;
+    }
+    debug(35, 9) ("fqdncache_purgelru: removed %d entries\n", removed);
+}
+
+static void
+purge_entries_fromhosts(void)
+{
+    dlink_node *m = lru_list.head;
+    fqdncache_entry *i = NULL;
+    fqdncache_entry *t;
+    while (m) {
+	if (i != NULL) {	/* need to delay deletion */
+	    fqdncacheRelease(i);	/* we just override locks */
+	    i = NULL;
+	}
+	t = (fqdncache_entry *)m->data;
+	if (t->flags.fromhosts)
+	    i = t;
+	m = m->next;
+    }
+    if (i != NULL)
+	fqdncacheRelease(i);
+}
+
+/* create blank fqdncache_entry */
+static fqdncache_entry *
+fqdncacheCreateEntry(const char *name)
+{
+    static fqdncache_entry *f;
+    f = (fqdncache_entry *)memAllocate(MEM_FQDNCACHE_ENTRY);
+    f->hash.key = xstrdup(name);
+    f->expires = squid_curtime + Config.negativeDnsTtl;
+    return f;
+}
+
+static void
+fqdncacheAddEntry(fqdncache_entry * f)
+{
+    hash_link *e = (hash_link *)hash_lookup(fqdn_table, f->hash.key);
+    if (NULL != e) {
+	/* avoid colission */
+	fqdncache_entry *q = (fqdncache_entry *) e;
+	fqdncacheRelease(q);
+    }
+    hash_join(fqdn_table, &f->hash);
+    dlinkAdd(f, &f->lru, &lru_list);
+    f->lastref = squid_curtime;
+}
+
+/* walks down the pending list, calling handlers */
+static void
+fqdncacheCallback(fqdncache_entry * f)
+{
+    FQDNH *callback;
+    void *cbdata;
+    f->lastref = squid_curtime;
+    if (!f->handler)
+	return;
+    fqdncacheLockEntry(f);
+    callback = f->handler;
+    f->handler = NULL;
+    if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
+	dns_error_message = f->error_message;
+	callback(f->flags.negcached ? NULL : f->names[0], cbdata);
+    }
+    fqdncacheUnlockEntry(f);
+}
+
+static fqdncache_entry *
+#if USE_DNSSERVERS
+fqdncacheParse(const char *inbuf)
+{
+    LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
+    char *token;
+    static fqdncache_entry f;
+    int ttl;
+    memset(&f, '\0', sizeof(f));
+    f.expires = squid_curtime;
+    f.flags.negcached = 1;
+    if (inbuf == NULL) {
+	debug(35, 1) ("fqdncacheParse: Got <NULL> reply\n");
+	return &f;
+    }
+    xstrncpy(buf, inbuf, DNS_INBUF_SZ);
+    debug(35, 5) ("fqdncacheParse: parsing: {%s}\n", buf);
+    token = strtok(buf, w_space);
+    if (NULL == token) {
+	debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting '$name'\n");
+	return &f;
+    }
+    if (0 == strcmp(token, "$fail")) {
+	f.expires = squid_curtime + Config.negativeDnsTtl;
+	token = strtok(NULL, "\n");
+	assert(NULL != token);
+	f.error_message = xstrdup(token);
+	return &f;
+    }
+    if (0 != strcmp(token, "$name")) {
+	debug(35, 1) ("fqdncacheParse: Got '%s', expecting '$name'\n", token);
+	return &f;
+    }
+    token = strtok(NULL, w_space);
+    if (NULL == token) {
+	debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting TTL\n");
+	return &f;
+    }
+    f.flags.negcached = 0;
+    ttl = atoi(token);
+    if (ttl > 0)
+	f.expires = squid_curtime + ttl;
+    else
+	f.expires = squid_curtime + Config.positiveDnsTtl;
+    token = strtok(NULL, w_space);
+    if (NULL != token) {
+	f.names[0] = xstrdup(token);
+	f.name_count = 1;
+    }
+    return &f;
+}
+#else
+fqdncacheParse(rfc1035_rr * answers, int nr)
+{
+    static fqdncache_entry f;
+    int k;
+    int na = 0;
+    memset(&f, '\0', sizeof(f));
+    f.expires = squid_curtime;
+    f.flags.negcached = 1;
+    if (nr < 0) {
+	debug(35, 3) ("fqdncacheParse: Lookup failed (error %d)\n",
+	    rfc1035_errno);
+	assert(rfc1035_error_message);
+	f.error_message = xstrdup(rfc1035_error_message);
+	return &f;
+    }
+    if (nr == 0) {
+	debug(35, 3) ("fqdncacheParse: No DNS records\n");
+	f.error_message = xstrdup("No DNS records");
+	return &f;
+    }
+    debug(35, 3) ("fqdncacheParse: %d answers\n", nr);
+    assert(answers);
+    for (k = 0; k < nr; k++) {
+	if (answers[k].type != RFC1035_TYPE_PTR)
+	    continue;
+	if (answers[k]._class != RFC1035_CLASS_IN)
+	    continue;
+	na++;
+	f.flags.negcached = 0;
+	f.names[0] = xstrdup(answers[k].rdata);
+	f.name_count = 1;
+	f.expires = squid_curtime + answers[k].ttl;
+	return &f;
+    }
+    debug(35, 1) ("fqdncacheParse: No PTR record\n");
+    f.error_message = xstrdup("No PTR record");
+    return &f;
+}
+#endif
+
+static void
+#if USE_DNSSERVERS
+fqdncacheHandleReply(void *data, char *reply)
+#else
+fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na)
+#endif
+{
+    int n;
+    generic_cbdata *c = (generic_cbdata *)data;
+    fqdncache_entry *f = (fqdncache_entry *)c->data;
+    fqdncache_entry *x = NULL;
+    cbdataFree(c);
+    c = NULL;
+    n = ++FqdncacheStats.replies;
+    statHistCount(&statCounter.dns.svc_time,
+	tvSubMsec(f->request_time, current_time));
+#if USE_DNSSERVERS
+    x = fqdncacheParse(reply);
+#else
+    x = fqdncacheParse(answers, na);
+#endif
+    assert(x);
+    f->name_count = x->name_count;
+    for (n = 0; n < (int) f->name_count; n++)
+	f->names[n] = x->names[n];
+    f->error_message = x->error_message;
+    f->expires = x->expires;
+    f->flags = x->flags;
+    fqdncacheAddEntry(f);
+    fqdncacheCallback(f);
+}
+
+void
+fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData)
+{
+    fqdncache_entry *f = NULL;
+    char *name = inet_ntoa(addr);
+    generic_cbdata *c;
+    assert(handler);
+    debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name);
+    FqdncacheStats.requests++;
+    if (name == NULL || name[0] == '\0') {
+	debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n");
+	handler(NULL, handlerData);
+	return;
+    }
+    f = fqdncache_get(name);
+    if (NULL == f) {
+	/* miss */
+	(void) 0;
+    } else if (fqdncacheExpiredEntry(f)) {
+	/* hit, but expired -- bummer */
+	fqdncacheRelease(f);
+	f = NULL;
+    } else {
+	/* hit */
+	debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name);
+	if (f->flags.negcached)
+	    FqdncacheStats.negative_hits++;
+	else
+	    FqdncacheStats.hits++;
+	f->handler = handler;
+	f->handlerData = cbdataReference(handlerData);
+	fqdncacheCallback(f);
+	return;
+    }
+
+    debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name);
+    FqdncacheStats.misses++;
+    f = fqdncacheCreateEntry(name);
+    f->handler = handler;
+    f->handlerData = cbdataReference(handlerData);
+    f->request_time = current_time;
+    c = cbdataAlloc(generic_cbdata);
+    c->data = f;
+#if USE_DNSSERVERS
+    dnsSubmit(hashKeyStr(&f->hash), fqdncacheHandleReply, c);
+#else
+    idnsPTRLookup(addr, fqdncacheHandleReply, c);
+#endif
+}
+
+/* initialize the fqdncache */
+void
+fqdncache_init(void)
+{
+    int n;
+    if (fqdn_table)
+	return;
+    debug(35, 3) ("Initializing FQDN Cache...\n");
+    memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
+    memset(&lru_list, '\0', sizeof(lru_list));
+    fqdncache_high = (long) (((float) Config.fqdncache.size *
+	    (float) FQDN_HIGH_WATER) / (float) 100);
+    fqdncache_low = (long) (((float) Config.fqdncache.size *
+	    (float) FQDN_LOW_WATER) / (float) 100);
+    n = hashPrime(fqdncache_high / 4);
+    fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4);
+    cachemgrRegister("fqdncache",
+	"FQDN Cache Stats and Contents",
+	fqdnStats, 0, 1);
+    memDataInit(MEM_FQDNCACHE_ENTRY, "fqdncache_entry",
+	sizeof(fqdncache_entry), 0);
+}
+
+const char *
+fqdncache_gethostbyaddr(struct in_addr addr, int flags)
+{
+    char *name = inet_ntoa(addr);
+    fqdncache_entry *f = NULL;
+    struct in_addr ip;
+    assert(name);
+    FqdncacheStats.requests++;
+    f = fqdncache_get(name);
+    if (NULL == f) {
+	(void) 0;
+    } else if (fqdncacheExpiredEntry(f)) {
+	fqdncacheRelease(f);
+	f = NULL;
+    } else if (f->flags.negcached) {
+	FqdncacheStats.negative_hits++;
+	dns_error_message = f->error_message;
+	return NULL;
+    } else {
+	FqdncacheStats.hits++;
+	f->lastref = squid_curtime;
+	return f->names[0];
+    }
+    /* check if it's already a FQDN address in text form. */
+    if (!safe_inet_addr(name, &ip))
+	return name;
+    FqdncacheStats.misses++;
+    if (flags & FQDN_LOOKUP_IF_MISS)
+	fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL);
+    return NULL;
+}
+
+
+/* process objects list */
+void
+fqdnStats(StoreEntry * sentry)
+{
+    fqdncache_entry *f = NULL;
+    int k;
+    int ttl;
+    if (fqdn_table == NULL)
+	return;
+    storeAppendPrintf(sentry, "FQDN Cache Statistics:\n");
+    storeAppendPrintf(sentry, "FQDNcache Entries: %d\n",
+	memInUse(MEM_FQDNCACHE_ENTRY));
+    storeAppendPrintf(sentry, "FQDNcache Requests: %d\n",
+	FqdncacheStats.requests);
+    storeAppendPrintf(sentry, "FQDNcache Hits: %d\n",
+	FqdncacheStats.hits);
+    storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n",
+	FqdncacheStats.negative_hits);
+    storeAppendPrintf(sentry, "FQDNcache Misses: %d\n",
+	FqdncacheStats.misses);
+    storeAppendPrintf(sentry, "Blocking calls to gethostbyaddr(): %d\n",
+	FqdncacheStats.ghba_calls);
+    storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n");
+    storeAppendPrintf(sentry, "%-15.15s %3s %3s %3s %s\n",
+	"Address", "Flg", "TTL", "Cnt", "Hostnames");
+    hash_first(fqdn_table);
+    while ((f = (fqdncache_entry *) hash_next(fqdn_table))) {
+	ttl = (f->flags.fromhosts ? -1 : (f->expires - squid_curtime));
+	storeAppendPrintf(sentry, "%-15.15s  %c%c %3.3d % 3d",
+	    hashKeyStr(&f->hash),
+	    f->flags.negcached ? 'N' : ' ',
+	    f->flags.fromhosts ? 'H' : ' ',
+	    ttl,
+	    (int) f->name_count);
+	for (k = 0; k < (int) f->name_count; k++)
+	    storeAppendPrintf(sentry, " %s", f->names[k]);
+	storeAppendPrintf(sentry, "\n");
+    }
+}
+
+static void
+dummy_handler(const char *bufnotused, void *datanotused)
+{
+    return;
+}
+
+const char *
+fqdnFromAddr(struct in_addr addr)
+{
+    const char *n;
+    static char buf[32];
+    if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
+	return n;
+    xstrncpy(buf, inet_ntoa(addr), 32);
+    return buf;
+}
+
+static void
+fqdncacheLockEntry(fqdncache_entry * f)
+{
+    if (f->locks++ == 0) {
+	dlinkDelete(&f->lru, &lru_list);
+	dlinkAdd(f, &f->lru, &lru_list);
+    }
+}
+
+static void
+fqdncacheUnlockEntry(fqdncache_entry * f)
+{
+    assert(f->locks > 0);
+    f->locks--;
+    if (fqdncacheExpiredEntry(f))
+	fqdncacheRelease(f);
+}
+
+static void
+fqdncacheFreeEntry(void *data)
+{
+    fqdncache_entry *f = (fqdncache_entry *)data;
+    int k;
+    for (k = 0; k < (int) f->name_count; k++)
+	safe_free(f->names[k]);
+    safe_free(f->hash.key);
+    safe_free(f->error_message);
+    memFree(f, MEM_FQDNCACHE_ENTRY);
+}
+
+void
+fqdncacheFreeMemory(void)
+{
+    hashFreeItems(fqdn_table, fqdncacheFreeEntry);
+    hashFreeMemory(fqdn_table);
+    fqdn_table = NULL;
+}
+
+/* Recalculate FQDN cache size upon reconfigure */
+void
+fqdncache_restart(void)
+{
+    fqdncache_high = (long) (((float) Config.fqdncache.size *
+	    (float) FQDN_HIGH_WATER) / (float) 100);
+    fqdncache_low = (long) (((float) Config.fqdncache.size *
+	    (float) FQDN_LOW_WATER) / (float) 100);
+    purge_entries_fromhosts();
+}
+
+/*
+ *  adds a "static" entry from /etc/hosts.  the worldist is to be
+ *  managed by the caller, including pointed-to strings
+ */
+void
+fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames)
+{
+    fqdncache_entry *fce;
+    int j = 0;
+    if ((fce = fqdncache_get(addr))) {
+	if (1 == fce->flags.fromhosts) {
+	    fqdncacheUnlockEntry(fce);
+	} else if (fce->locks > 0) {
+	    debug(35, 1) ("fqdncacheAddEntryFromHosts: can't add static entry for locked address '%s'\n", addr);
+	    return;
+	} else {
+	    fqdncacheRelease(fce);
+	}
+    }
+    fce = fqdncacheCreateEntry(addr);
+    while (hostnames) {
+	fce->names[j] = xstrdup(hostnames->key);
+	j++;
+	hostnames = hostnames->next;
+	if (j >= FQDN_MAX_NAMES)
+	    break;
+    }
+    fce->name_count = j;
+    fce->names[j] = NULL;	/* it's safe */
+    fce->flags.fromhosts = 1;
+    fqdncacheAddEntry(fce);
+    fqdncacheLockEntry(fce);
+}
+
+
+#ifdef SQUID_SNMP
+/*
+ * The function to return the fqdn statistics via SNMP
+ */
+
+variable_list *
+snmp_netFqdnFn(variable_list * Var, snint * ErrP)
+{
+    variable_list *Answer = NULL;
+    debug(49, 5) ("snmp_netFqdnFn: Processing request:\n");
+    snmpDebugOid(5, Var->name, Var->name_length);
+    *ErrP = SNMP_ERR_NOERROR;
+    switch (Var->name[LEN_SQ_NET + 1]) {
+    case FQDN_ENT:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    memInUse(MEM_FQDNCACHE_ENTRY),
+	    SMI_GAUGE32);
+	break;
+    case FQDN_REQ:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    FqdncacheStats.requests,
+	    SMI_COUNTER32);
+	break;
+    case FQDN_HITS:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    FqdncacheStats.hits,
+	    SMI_COUNTER32);
+	break;
+    case FQDN_PENDHIT:
+	/* this is now worthless */
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    0,
+	    SMI_GAUGE32);
+	break;
+    case FQDN_NEGHIT:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    FqdncacheStats.negative_hits,
+	    SMI_COUNTER32);
+	break;
+    case FQDN_MISS:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    FqdncacheStats.misses,
+	    SMI_COUNTER32);
+	break;
+    case FQDN_GHBN:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    FqdncacheStats.ghba_calls,
+	    SMI_COUNTER32);
+	break;
+    default:
+	*ErrP = SNMP_ERR_NOSUCHNAME;
+	break;
+    }
+    return Answer;
+}
+
+#endif /*SQUID_SNMP */
--- squid/src/ftp.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,2615 +0,0 @@
-
-/*
- * $Id: ftp.c,v 1.28.6.2 2002/10/09 14:14:26 rbcollins Exp $
- *
- * DEBUG: section 9     File Transfer Protocol (FTP)
- * AUTHOR: Harvest Derived
- *
- * 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"
-#include "Store.h"
-
-
-static const char *const crlf = "\r\n";
-static char cbuf[1024];
-
-typedef enum {
-    BEGIN,
-    SENT_USER,
-    SENT_PASS,
-    SENT_TYPE,
-    SENT_MDTM,
-    SENT_SIZE,
-    SENT_PORT,
-    SENT_PASV,
-    SENT_CWD,
-    SENT_LIST,
-    SENT_NLST,
-    SENT_REST,
-    SENT_RETR,
-    SENT_STOR,
-    SENT_QUIT,
-    READING_DATA,
-    WRITING_DATA,
-    SENT_MKDIR
-} ftp_state_t;
-
-struct _ftp_flags {
-    unsigned int isdir:1;
-    unsigned int pasv_supported:1;
-    unsigned int skip_whitespace:1;
-    unsigned int rest_supported:1;
-    unsigned int pasv_only:1;
-    unsigned int authenticated:1;
-    unsigned int http_header_sent:1;
-    unsigned int tried_nlst:1;
-    unsigned int need_base_href:1;
-    unsigned int root_dir:1;
-    unsigned int no_dotdot:1;
-    unsigned int html_header_sent:1;
-    unsigned int binary:1;
-    unsigned int try_slash_hack:1;
-    unsigned int put:1;
-    unsigned int put_mkdir:1;
-    unsigned int listformat_unknown:1;
-    unsigned int datachannel_hack:1;
-};
-
-typedef struct _Ftpdata {
-    StoreEntry *entry;
-    request_t *request;
-    char user[MAX_URL];
-    char password[MAX_URL];
-    int password_url;
-    char *reply_hdr;
-    int reply_hdr_state;
-    String title_url;
-    String base_href;
-    int conn_att;
-    int login_att;
-    ftp_state_t state;
-    time_t mdtm;
-    int size;
-    wordlist *pathcomps;
-    char *filepath;
-    int restart_offset;
-    int restarted_offset;
-    int rest_att;
-    char *proxy_host;
-    size_t list_width;
-    wordlist *cwd_message;
-    char *old_request;
-    char *old_reply;
-    char *old_filepath;
-    char typecode;
-    struct {
-	int fd;
-	char *buf;
-	size_t size;
-	off_t offset;
-	wordlist *message;
-	char *last_command;
-	char *last_reply;
-	int replycode;
-    } ctrl;
-    struct {
-	int fd;
-	char *buf;
-	size_t size;
-	off_t offset;
-	char *host;
-	u_short port;
-    } data;
-    struct _ftp_flags flags;
-    FwdState *fwd;
-} FtpStateData;
-
-typedef struct {
-    char type;
-    int size;
-    char *date;
-    char *name;
-    char *showname;
-    char *link;
-} ftpListParts;
-
-typedef void (FTPSM) (FtpStateData *);
-
-#define FTP_LOGIN_ESCAPED 1
-#define FTP_LOGIN_NOT_ESCAPED 0
-
-/* Local functions */
-static CNCB ftpPasvCallback;
-static PF ftpDataRead;
-static PF ftpDataWrite;
-static CWCB ftpDataWriteCallback;
-static PF ftpStateFree;
-static PF ftpTimeout;
-static PF ftpReadControlReply;
-static CWCB ftpWriteCommandCallback;
-static void ftpLoginParser(const char *, FtpStateData *, int escaped);
-static wordlist *ftpParseControlReply(char *, size_t, int *, int *);
-static int ftpRestartable(FtpStateData * ftpState);
-static void ftpAppendSuccessHeader(FtpStateData * ftpState);
-static void ftpAuthRequired(HttpReply * reply, request_t * request, const char *realm);
-static void ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState);
-static void ftpUnhack(FtpStateData * ftpState);
-static void ftpScheduleReadControlReply(FtpStateData *, int);
-static void ftpHandleControlReply(FtpStateData *);
-static char *ftpHtmlifyListEntry(const char *line, FtpStateData * ftpState);
-static void ftpFailed(FtpStateData *, err_type);
-static void ftpFailedErrorMessage(FtpStateData *, err_type);
-
-/*
- * State machine functions
- * send == state transition
- * read == wait for response, and select next state transition
- * other == Transition logic
- */
-static FTPSM ftpReadWelcome;
-static FTPSM ftpSendUser;
-static FTPSM ftpReadUser;
-static FTPSM ftpSendPass;
-static FTPSM ftpReadPass;
-static FTPSM ftpSendType;
-static FTPSM ftpReadType;
-static FTPSM ftpSendMdtm;
-static FTPSM ftpReadMdtm;
-static FTPSM ftpSendSize;
-static FTPSM ftpReadSize;
-static FTPSM ftpSendPort;
-static FTPSM ftpReadPort;
-static FTPSM ftpSendPasv;
-static FTPSM ftpReadPasv;
-static FTPSM ftpTraverseDirectory;
-static FTPSM ftpListDir;
-static FTPSM ftpGetFile;
-static FTPSM ftpSendCwd;
-static FTPSM ftpReadCwd;
-static FTPSM ftpRestOrList;
-static FTPSM ftpSendList;
-static FTPSM ftpSendNlst;
-static FTPSM ftpReadList;
-static FTPSM ftpSendRest;
-static FTPSM ftpReadRest;
-static FTPSM ftpSendRetr;
-static FTPSM ftpReadRetr;
-static FTPSM ftpReadTransferDone;
-static FTPSM ftpSendStor;
-static FTPSM ftpReadStor;
-static FTPSM ftpWriteTransferDone;
-static FTPSM ftpSendReply;
-static FTPSM ftpSendMkdir;
-static FTPSM ftpReadMkdir;
-static FTPSM ftpFail;
-static FTPSM ftpSendQuit;
-static FTPSM ftpReadQuit;
-/************************************************
-** State Machine Description (excluding hacks) **
-*************************************************
-From			To
----------------------------------------
-Welcome			User
-User			Pass
-Pass			Type
-Type			TraverseDirectory / GetFile
-TraverseDirectory	Cwd / GetFile / ListDir
-Cwd			TraverseDirectory / Mkdir
-GetFile			Mdtm
-Mdtm			Size
-Size			Pasv
-ListDir			Pasv
-Pasv			FileOrList
-FileOrList		Rest / Retr / Nlst / List / Mkdir (PUT /xxx;type=d)
-Rest			Retr
-Retr / Nlst / List	DataRead* (on datachannel)
-DataRead*		ReadTransferDone
-ReadTransferDone	DataTransferDone
-Stor			DataWrite* (on datachannel)
-DataWrite*		RequestPutBody** (from client)
-RequestPutBody**	DataWrite* / WriteTransferDone
-WriteTransferDone	DataTransferDone
-DataTransferDone	Quit
-Quit			-
-************************************************/
-
-FTPSM *FTP_SM_FUNCS[] =
-{
-    ftpReadWelcome,		/* BEGIN */
-    ftpReadUser,		/* SENT_USER */
-    ftpReadPass,		/* SENT_PASS */
-    ftpReadType,		/* SENT_TYPE */
-    ftpReadMdtm,		/* SENT_MDTM */
-    ftpReadSize,		/* SENT_SIZE */
-    ftpReadPort,		/* SENT_PORT */
-    ftpReadPasv,		/* SENT_PASV */
-    ftpReadCwd,			/* SENT_CWD */
-    ftpReadList,		/* SENT_LIST */
-    ftpReadList,		/* SENT_NLST */
-    ftpReadRest,		/* SENT_REST */
-    ftpReadRetr,		/* SENT_RETR */
-    ftpReadStor,		/* SENT_STOR */
-    ftpReadQuit,		/* SENT_QUIT */
-    ftpReadTransferDone,	/* READING_DATA (RETR,LIST,NLST) */
-    ftpWriteTransferDone,	/* WRITING_DATA (STOR) */
-    ftpReadMkdir		/* SENT_MKDIR */
-};
-
-static void
-ftpStateFree(int fdnotused, void *data)
-{
-    FtpStateData *ftpState = data;
-    if (ftpState == NULL)
-	return;
-    debug(9, 3) ("ftpStateFree: %s\n", storeUrl(ftpState->entry));
-    storeUnregisterAbort(ftpState->entry);
-    storeUnlockObject(ftpState->entry);
-    if (ftpState->reply_hdr) {
-	memFree(ftpState->reply_hdr, MEM_8K_BUF);
-	ftpState->reply_hdr = NULL;
-    }
-    requestUnlink(ftpState->request);
-    if (ftpState->ctrl.buf) {
-	memFreeBuf(ftpState->ctrl.size, ftpState->ctrl.buf);
-	ftpState->ctrl.buf = NULL;
-    }
-    if (ftpState->data.buf) {
-	memFreeBuf(ftpState->data.size, ftpState->data.buf);
-	ftpState->data.buf = NULL;
-    }
-    if (ftpState->pathcomps)
-	wordlistDestroy(&ftpState->pathcomps);
-    if (ftpState->ctrl.message)
-	wordlistDestroy(&ftpState->ctrl.message);
-    if (ftpState->cwd_message)
-	wordlistDestroy(&ftpState->cwd_message);
-    safe_free(ftpState->ctrl.last_reply);
-    safe_free(ftpState->ctrl.last_command);
-    safe_free(ftpState->old_request);
-    safe_free(ftpState->old_reply);
-    safe_free(ftpState->old_filepath);
-    stringClean(&ftpState->title_url);
-    stringClean(&ftpState->base_href);
-    safe_free(ftpState->filepath);
-    safe_free(ftpState->data.host);
-    if (ftpState->data.fd > -1) {
-	comm_close(ftpState->data.fd);
-	ftpState->data.fd = -1;
-    }
-    cbdataFree(ftpState);
-}
-
-static void
-ftpLoginParser(const char *login, FtpStateData * ftpState, int escaped)
-{
-    char *s = NULL;
-    xstrncpy(ftpState->user, login, MAX_URL);
-    if ((s = strchr(ftpState->user, ':'))) {
-	*s = 0;
-	xstrncpy(ftpState->password, s + 1, MAX_URL);
-	if (escaped)
-	    rfc1738_unescape(ftpState->password);
-	ftpState->password_url = 1;
-    } else {
-	xstrncpy(ftpState->password, null_string, MAX_URL);
-    }
-    if (escaped)
-	rfc1738_unescape(ftpState->user);
-    if (ftpState->user[0] || ftpState->password[0])
-	return;
-    xstrncpy(ftpState->user, "anonymous", MAX_URL);
-    xstrncpy(ftpState->password, Config.Ftp.anon_user, MAX_URL);
-}
-
-static void
-ftpTimeout(int fd, void *data)
-{
-    FtpStateData *ftpState = data;
-    StoreEntry *entry = ftpState->entry;
-    debug(9, 4) ("ftpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
-    if (SENT_PASV == ftpState->state && fd == ftpState->data.fd) {
-	/* stupid ftp.netscape.com */
-	ftpState->fwd->flags.dont_retry = 0;
-	ftpState->fwd->flags.ftp_pasv_failed = 1;
-	debug(9, 1) ("ftpTimeout: timeout in SENT_PASV state\n");
-    }
-    ftpFailed(ftpState, ERR_READ_TIMEOUT);
-    /* ftpFailed closes ctrl.fd and frees ftpState */
-}
-
-static void
-ftpListingStart(FtpStateData * ftpState)
-{
-    StoreEntry *e = ftpState->entry;
-    wordlist *w;
-    char *dirup;
-    int i, j, k;
-    char *title;
-    storeBuffer(e);
-    storeAppendPrintf(e, "<!-- HTML listing generated by Squid %s -->\n",
-	version_string);
-    storeAppendPrintf(e, "<!-- %s -->\n", mkrfc1123(squid_curtime));
-    storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    storeAppendPrintf(e, "<HTML><HEAD><TITLE>\n");
-    storeAppendPrintf(e, "FTP Directory: %s\n",
-	html_quote(strBuf(ftpState->title_url)));
-    storeAppendPrintf(e, "</TITLE>\n");
-    storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
-    if (ftpState->flags.need_base_href)
-	storeAppendPrintf(e, "<BASE HREF=\"%s\">\n",
-	    html_quote(strBuf(ftpState->base_href)));
-    storeAppendPrintf(e, "</HEAD><BODY>\n");
-    if (ftpState->cwd_message) {
-	storeAppendPrintf(e, "<PRE>\n");
-	for (w = ftpState->cwd_message; w; w = w->next)
-	    storeAppendPrintf(e, "%s\n", html_quote(w->key));
-	storeAppendPrintf(e, "</PRE>\n");
-	storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
-	wordlistDestroy(&ftpState->cwd_message);
-    }
-    storeAppendPrintf(e, "<H2>\n");
-    storeAppendPrintf(e, "FTP Directory: ");
-    /* "ftp://" == 6 characters */
-    assert(strLen(ftpState->title_url) >= 6);
-    title = html_quote(strBuf(ftpState->title_url));
-    for (i = 6, j = 0; title[i]; j = i) {
-	storeAppendPrintf(e, "<A HREF=\"");
-	i += strcspn(&title[i], "/");
-	if (title[i] == '/')
-	    i++;
-	for (k = 0; k < i; k++)
-	    storeAppendPrintf(e, "%c", title[k]);
-	storeAppendPrintf(e, "\">");
-	for (k = j; k < i - 1; k++)
-	    storeAppendPrintf(e, "%c", title[k]);
-	if (strBuf(ftpState->title_url)[k] != '/')
-	    storeAppendPrintf(e, "%c", title[k++]);
-	storeAppendPrintf(e, "</A>");
-	if (k < i)
-	    storeAppendPrintf(e, "%c", title[k++]);
-	if (i == j) {
-	    /* Error guard, or "assert" */
-	    storeAppendPrintf(e, "ERROR: Failed to parse URL: %s\n",
-		html_quote(strBuf(ftpState->title_url)));
-	    debug(9, 0) ("Failed to parse URL: %s\n", strBuf(ftpState->title_url));
-	    break;
-	}
-    }
-    storeAppendPrintf(e, "</H2>\n");
-    storeAppendPrintf(e, "<PRE>\n");
-    dirup = ftpHtmlifyListEntry("<internal-dirup>", ftpState);
-    storeAppend(e, dirup, strlen(dirup));
-    storeBufferFlush(e);
-    ftpState->flags.html_header_sent = 1;
-}
-
-static void
-ftpListingFinish(FtpStateData * ftpState)
-{
-    StoreEntry *e = ftpState->entry;
-    storeBuffer(e);
-    storeAppendPrintf(e, "</PRE>\n");
-    if (ftpState->flags.listformat_unknown && !ftpState->flags.tried_nlst) {
-	storeAppendPrintf(e, "<A HREF=\"./;type=d\">[As plain directory]</A>\n");
-    } else if (ftpState->typecode == 'D') {
-	storeAppendPrintf(e, "<A HREF=\"./\">[As extended directory]</A>\n");
-    }
-    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
-    storeAppendPrintf(e, "<ADDRESS>\n");
-    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
-	mkrfc1123(squid_curtime),
-	getMyHostname(),
-	full_appname_string);
-    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
-    storeBufferFlush(e);
-}
-
-static const char *Month[] =
-{
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static int
-is_month(const char *buf)
-{
-    int i;
-    for (i = 0; i < 12; i++)
-	if (!strcasecmp(buf, Month[i]))
-	    return 1;
-    return 0;
-}
-
-
-static void
-ftpListPartsFree(ftpListParts ** parts)
-{
-    safe_free((*parts)->date);
-    safe_free((*parts)->name);
-    safe_free((*parts)->showname);
-    safe_free((*parts)->link);
-    safe_free(*parts);
-}
-
-#define MAX_TOKENS 64
-
-static ftpListParts *
-ftpListParseParts(const char *buf, struct _ftp_flags flags)
-{
-    ftpListParts *p = NULL;
-    char *t = NULL;
-    const char *ct = NULL;
-    char *tokens[MAX_TOKENS];
-    int i;
-    int n_tokens;
-    static char tbuf[128];
-    char *xbuf = NULL;
-    static int scan_ftp_initialized = 0;
-    static regex_t scan_ftp_integer;
-    static regex_t scan_ftp_time;
-    static regex_t scan_ftp_dostime;
-    static regex_t scan_ftp_dosdate;
-
-    if (!scan_ftp_initialized) {
-	scan_ftp_initialized = 1;
-	regcomp(&scan_ftp_integer, "^[0123456789]+$", REG_EXTENDED | REG_NOSUB);
-	regcomp(&scan_ftp_time, "^[0123456789:]+$", REG_EXTENDED | REG_NOSUB);
-	regcomp(&scan_ftp_dosdate, "^[0123456789]+-[0123456789]+-[0123456789]+$", REG_EXTENDED | REG_NOSUB);
-	regcomp(&scan_ftp_dostime, "^[0123456789]+:[0123456789]+[AP]M$", REG_EXTENDED | REG_NOSUB | REG_ICASE);
-    }
-    if (buf == NULL)
-	return NULL;
-    if (*buf == '\0')
-	return NULL;
-    p = xcalloc(1, sizeof(ftpListParts));
-    n_tokens = 0;
-    memset(tokens, 0, sizeof(tokens));
-    xbuf = xstrdup(buf);
-    if (flags.tried_nlst) {
-	/* Machine readable format, one name per line */
-	p->name = xbuf;
-	p->type = '\0';
-	return p;
-    }
-    for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space))
-	tokens[n_tokens++] = xstrdup(t);
-    xfree(xbuf);
-    /* locate the Month field */
-    for (i = 3; i < n_tokens - 2; i++) {
-	char *size = tokens[i - 1];
-	char *month = tokens[i];
-	char *day = tokens[i + 1];
-	char *year = tokens[i + 2];
-	if (!is_month(month))
-	    continue;
-	if (regexec(&scan_ftp_integer, size, 0, NULL, 0) != 0)
-	    continue;
-	if (regexec(&scan_ftp_integer, day, 0, NULL, 0) != 0)
-	    continue;
-	if (regexec(&scan_ftp_time, day, 0, NULL, 0) != 0)	/* Yr | hh:mm */
-	    continue;
-	snprintf(tbuf, 128, "%s %2s %5s",
-	    month, day, year);
-	if (!strstr(buf, tbuf))
-	    snprintf(tbuf, 128, "%s %2s %-5s",
-		month, day, year);
-	if ((t = strstr(buf, tbuf))) {
-	    p->type = *tokens[0];
-	    p->size = atoi(size);
-	    p->date = xstrdup(tbuf);
-	    if (flags.skip_whitespace) {
-		t += strlen(tbuf);
-		while (strchr(w_space, *t))
-		    t++;
-	    } else {
-		/* XXX assumes a single space between date and filename
-		 * suggested by:  Nathan.Bailey@cc.monash.edu.au and
-		 * Mike Battersby <mike@starbug.bofh.asn.au> */
-		t += strlen(tbuf) + 1;
-	    }
-	    p->name = xstrdup(t);
-	    if ((t = strstr(p->name, " -> "))) {
-		*t = '\0';
-		p->link = xstrdup(t + 4);
-	    }
-	    goto found;
-	}
-	break;
-    }
-    /* try it as a DOS listing, 04-05-70 09:33PM ... */
-    if (n_tokens > 3 &&
-	regexec(&scan_ftp_dosdate, tokens[0], 0, NULL, 0) == 0 &&
-	regexec(&scan_ftp_dostime, tokens[1], 0, NULL, 0) == 0) {
-	if (!strcasecmp(tokens[2], "<dir>")) {
-	    p->type = 'd';
-	} else {
-	    p->type = '-';
-	    p->size = atoi(tokens[2]);
-	}
-	snprintf(tbuf, 128, "%s %s", tokens[0], tokens[1]);
-	p->date = xstrdup(tbuf);
-	if (p->type == 'd') {
-	    /* Directory.. name begins with first printable after <dir> */
-	    ct = strstr(buf, tokens[2]);
-	    ct += strlen(tokens[2]);
-	    while (xisspace(*ct))
-		ct++;
-	    if (!*ct)
-		ct = NULL;
-	} else {
-	    /* A file. Name begins after size, with a space in between */
-	    snprintf(tbuf, 128, " %s %s", tokens[2], tokens[3]);
-	    ct = strstr(buf, tbuf);
-	    if (ct) {
-		ct += strlen(tokens[2]) + 2;
-	    }
-	}
-	p->name = xstrdup(ct ? ct : tokens[3]);
-	goto found;
-    }
-    /* Try EPLF format; carson@lehman.com */
-    if (buf[0] == '+') {
-	ct = buf + 1;
-	p->type = 0;
-	while (ct && *ct) {
-	    time_t t;
-	    int l = strcspn(ct + 1, ",");
-	    char *tmp;
-	    if (l < 1)
-		goto blank;
-	    switch (*ct) {
-	    case '\t':
-		p->name = xstrndup(ct + 1, l + 1);
-		break;
-	    case 's':
-		p->size = atoi(ct + 1);
-		break;
-	    case 'm':
-		t = (time_t) strtol(ct + 1, &tmp, 0);
-		if (*tmp || (tmp == ct + 1))
-		    break;	/* not a valid integer */
-		p->date = xstrdup(ctime(&t));
-		*(strstr(p->date, "\n")) = '\0';
-		break;
-	    case '/':
-		p->type = 'd';
-		break;
-	    case 'r':
-		p->type = '-';
-		break;
-	    case 'i':
-		break;
-	    default:
-		break;
-	    }
-	  blank:
-	    ct = strstr(ct, ",");
-	    if (ct) {
-		ct++;
-	    }
-	}
-	if (p->type == 0) {
-	    p->type = '-';
-	}
-	if (p->name)
-	    goto found;
-	else
-	    safe_free(p->date);
-    }
-  found:
-    for (i = 0; i < n_tokens; i++)
-	xfree(tokens[i]);
-    if (!p->name)
-	ftpListPartsFree(&p);	/* cleanup */
-    return p;
-}
-
-static const char *
-dots_fill(size_t len)
-{
-    static char buf[256];
-    int i = 0;
-    if (len > Config.Ftp.list_width) {
-	memset(buf, ' ', 256);
-	buf[0] = '\n';
-	buf[Config.Ftp.list_width + 4] = '\0';
-	return buf;
-    }
-    for (i = (int) len; i < Config.Ftp.list_width; i++)
-	buf[i - len] = (i % 2) ? '.' : ' ';
-    buf[i - len] = '\0';
-    return buf;
-}
-
-static char *
-ftpHtmlifyListEntry(const char *line, FtpStateData * ftpState)
-{
-    LOCAL_ARRAY(char, icon, 2048);
-    LOCAL_ARRAY(char, href, 2048 + 40);
-    LOCAL_ARRAY(char, text, 2048);
-    LOCAL_ARRAY(char, size, 2048);
-    LOCAL_ARRAY(char, chdir, 2048 + 40);
-    LOCAL_ARRAY(char, view, 2048 + 40);
-    LOCAL_ARRAY(char, download, 2048 + 40);
-    LOCAL_ARRAY(char, link, 2048 + 40);
-    LOCAL_ARRAY(char, html, 8192);
-    size_t width = Config.Ftp.list_width;
-    ftpListParts *parts;
-    *icon = *href = *text = *size = *chdir = *view = *download = *link = *html = '\0';
-    if ((int) strlen(line) > 1024) {
-	snprintf(html, 8192, "%s\n", line);
-	return html;
-    }
-    /* Handle builtin <dirup> */
-    if (strcmp(line, "<internal-dirup>") == 0) {
-	/* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> {link} */
-	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
-	    mimeGetIconURL("internal-dirup"),
-	    "[DIRUP]");
-	if (!ftpState->flags.no_dotdot && !ftpState->flags.root_dir) {
-	    /* Normal directory */
-	    strcpy(href, "../");
-	    strcpy(text, "Parent Directory");
-	} else if (!ftpState->flags.no_dotdot && ftpState->flags.root_dir) {
-	    /* "Top level" directory */
-	    strcpy(href, "%2e%2e/");
-	    strcpy(text, "Parent Directory");
-	    snprintf(link, 2048, "(<A HREF=\"%s\">%s</A>)",
-		"%2f/",
-		"Root Directory");
-	} else if (ftpState->flags.no_dotdot && !ftpState->flags.root_dir) {
-	    /* Normal directory where last component is / or ..  */
-	    strcpy(href, "%2e%2e/");
-	    strcpy(text, "Parent Directory");
-	    snprintf(link, 2048, "(<A HREF=\"%s\">%s</A>)",
-		"../",
-		"Back");
-	} else {		/* NO_DOTDOT && ROOT_DIR */
-	    /* "UNIX Root" directory */
-	    strcpy(href, "../");
-	    strcpy(text, "Home Directory");
-	}
-	snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A> %s\n",
-	    href, icon, href, text, link);
-	return html;
-    }
-    if ((parts = ftpListParseParts(line, ftpState->flags)) == NULL) {
-	const char *p;
-	snprintf(html, 8192, "%s\n", line);
-	for (p = line; *p && xisspace(*p); p++);
-	if (*p && !xisspace(*p))
-	    ftpState->flags.listformat_unknown = 1;
-	return html;
-    }
-    if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
-	*html = '\0';
-	ftpListPartsFree(&parts);
-	return html;
-    }
-    parts->size += 1023;
-    parts->size >>= 10;
-    parts->showname = xstrdup(parts->name);
-    if (!Config.Ftp.list_wrap) {
-	if (strlen(parts->showname) > width - 1) {
-	    *(parts->showname + width - 1) = '>';
-	    *(parts->showname + width - 0) = '\0';
-	}
-    }
-    /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n  */
-    xstrncpy(href, rfc1738_escape_part(parts->name), 2048);
-    xstrncpy(text, parts->showname, 2048);
-    switch (parts->type) {
-    case 'd':
-	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
-	    mimeGetIconURL("internal-dir"),
-	    "[DIR]");
-	strcat(href, "/");	/* margin is allocated above */
-	break;
-    case 'l':
-	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
-	    mimeGetIconURL("internal-link"),
-	    "[LINK]");
-	/* sometimes there is an 'l' flag, but no "->" link */
-	if (parts->link) {
-	    char *link2 = xstrdup(html_quote(rfc1738_escape(parts->link)));
-	    snprintf(link, 2048, " -> <A HREF=\"%s\">%s</A>",
-		link2,
-		html_quote(parts->link));
-	    safe_free(link2);
-	}
-	break;
-    case '\0':
-	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
-	    mimeGetIconURL(parts->name),
-	    "[UNKNOWN]");
-	snprintf(chdir, 2048, " <A HREF=\"%s/;type=d\"><IMG border=\"0\" SRC=\"%s\" "
-	    "ALT=\"[DIR]\"></A>",
-	    rfc1738_escape_part(parts->name),
-	    mimeGetIconURL("internal-dir"));
-	break;
-    case '-':
-    default:
-	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
-	    mimeGetIconURL(parts->name),
-	    "[FILE]");
-	snprintf(size, 2048, " %6dk", parts->size);
-	break;
-    }
-    if (parts->type != 'd') {
-	if (mimeGetViewOption(parts->name)) {
-	    snprintf(view, 2048, " <A HREF=\"%s;type=a\"><IMG border=\"0\" SRC=\"%s\" "
-		"ALT=\"[VIEW]\"></A>",
-		href, mimeGetIconURL("internal-view"));
-	}
-	if (mimeGetDownloadOption(parts->name)) {
-	    snprintf(download, 2048, " <A HREF=\"%s;type=i\"><IMG border=\"0\" SRC=\"%s\" "
-		"ALT=\"[DOWNLOAD]\"></A>",
-		href, mimeGetIconURL("internal-download"));
-	}
-    }
-    /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> . . . {date}{size}{chdir}{view}{download}{link}\n  */
-    if (parts->type != '\0') {
-	snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A>%s "
-	    "%s%8s%s%s%s%s\n",
-	    href, icon, href, html_quote(text), dots_fill(strlen(text)),
-	    parts->date, size, chdir, view, download, link);
-    } else {
-	/* Plain listing. {icon} {text} ... {chdir}{view}{download} */
-	snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A>%s "
-	    "%s%s%s%s\n",
-	    href, icon, href, html_quote(text), dots_fill(strlen(text)),
-	    chdir, view, download, link);
-    }
-    ftpListPartsFree(&parts);
-    return html;
-}
-
-static void
-ftpParseListing(FtpStateData * ftpState)
-{
-    char *buf = ftpState->data.buf;
-    char *sbuf;			/* NULL-terminated copy of buf */
-    char *end;
-    char *line;
-    char *s;
-    char *t;
-    size_t linelen;
-    size_t usable;
-    StoreEntry *e = ftpState->entry;
-    int len = ftpState->data.offset;
-    /*
-     * We need a NULL-terminated buffer for scanning, ick
-     */
-    sbuf = xmalloc(len + 1);
-    xstrncpy(sbuf, buf, len + 1);
-    end = sbuf + len - 1;
-    while (*end != '\r' && *end != '\n' && end > sbuf)
-	end--;
-    usable = end - sbuf;
-    debug(9, 3) ("ftpParseListing: usable = %d\n", (int) usable);
-    if (usable == 0) {
-	debug(9, 3) ("ftpParseListing: didn't find end for %s\n", storeUrl(e));
-	xfree(sbuf);
-	return;
-    }
-    debug(9, 3) ("ftpParseListing: %d bytes to play with\n", len);
-    line = memAllocate(MEM_4K_BUF);
-    end++;
-    storeBuffer(e);
-    s = sbuf;
-    s += strspn(s, crlf);
-    for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
-	debug(9, 3) ("ftpParseListing: s = {%s}\n", s);
-	linelen = strcspn(s, crlf) + 1;
-	if (linelen < 2)
-	    break;
-	if (linelen > 4096)
-	    linelen = 4096;
-	xstrncpy(line, s, linelen);
-	debug(9, 7) ("ftpParseListing: {%s}\n", line);
-	if (!strncmp(line, "total", 5))
-	    continue;
-	t = ftpHtmlifyListEntry(line, ftpState);
-	assert(t != NULL);
-	storeAppend(e, t, strlen(t));
-    }
-    storeBufferFlush(e);
-    assert(usable <= len);
-    if (usable < len) {
-	/* must copy partial line to beginning of buf */
-	linelen = len - usable;
-	if (linelen > 4096)
-	    linelen = 4096;
-	xstrncpy(line, end, linelen);
-	xstrncpy(ftpState->data.buf, line, ftpState->data.size);
-	ftpState->data.offset = strlen(ftpState->data.buf);
-    }
-    memFree(line, MEM_4K_BUF);
-    xfree(sbuf);
-}
-
-static void
-ftpDataComplete(FtpStateData * ftpState)
-{
-    debug(9, 3) ("ftpDataComplete\n");
-    /* Connection closed; transfer done. */
-    if (ftpState->data.fd > -1) {
-	/*
-	 * close data socket so it does not occupy resources while
-	 * we wait
-	 */
-	comm_close(ftpState->data.fd);
-	ftpState->data.fd = -1;
-    }
-    /* expect the "transfer complete" message on the control socket */
-    ftpScheduleReadControlReply(ftpState, 1);
-}
-
-static void
-ftpDataRead(int fd, void *data)
-{
-    FtpStateData *ftpState = data;
-    int len;
-    int j;
-    int bin;
-    StoreEntry *entry = ftpState->entry;
-    size_t read_sz;
-#if DELAY_POOLS
-    MemObject *mem = entry->mem_obj;
-    delay_id delay_id = delayMostBytesAllowed(mem);
-#endif
-    assert(fd == ftpState->data.fd);
-    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
-	comm_close(ftpState->ctrl.fd);
-	return;
-    }
-    errno = 0;
-    read_sz = ftpState->data.size - ftpState->data.offset;
-#if DELAY_POOLS
-    read_sz = delayBytesWanted(delay_id, 1, read_sz);
-#endif
-    memset(ftpState->data.buf + ftpState->data.offset, '\0', read_sz);
-    statCounter.syscalls.sock.reads++;
-    len = FD_READ_METHOD(fd, ftpState->data.buf + ftpState->data.offset, read_sz);
-    if (len > 0) {
-	fd_bytes(fd, len, FD_READ);
-#if DELAY_POOLS
-	delayBytesIn(delay_id, len);
-#endif
-	kb_incr(&statCounter.server.all.kbytes_in, len);
-	kb_incr(&statCounter.server.ftp.kbytes_in, len);
-	ftpState->data.offset += len;
-    }
-    debug(9, 5) ("ftpDataRead: FD %d, Read %d bytes\n", fd, len);
-    if (len > 0) {
-	IOStats.Ftp.reads++;
-	for (j = len - 1, bin = 0; j; bin++)
-	    j >>= 1;
-	IOStats.Ftp.read_hist[bin]++;
-    }
-    if (ftpState->flags.isdir && !ftpState->flags.html_header_sent && len >= 0) {
-	ftpListingStart(ftpState);
-    }
-    if (len < 0) {
-	debug(50, ignoreErrno(errno) ? 3 : 1) ("ftpDataRead: read error: %s\n", xstrerror());
-	if (ignoreErrno(errno)) {
-	    commSetSelect(fd,
-		COMM_SELECT_READ,
-		ftpDataRead,
-		data,
-		Config.Timeout.read);
-	} else {
-	    ftpFailed(ftpState, ERR_READ_ERROR);
-	    /* ftpFailed closes ctrl.fd and frees ftpState */
-	    return;
-	}
-    } else if (len == 0) {
-	ftpDataComplete(ftpState);
-    } else {
-	if (ftpState->flags.isdir) {
-	    ftpParseListing(ftpState);
-	} else {
-	    storeAppend(entry, ftpState->data.buf, len);
-	    ftpState->data.offset = 0;
-	}
-	commSetSelect(fd,
-	    COMM_SELECT_READ,
-	    ftpDataRead,
-	    data,
-	    Config.Timeout.read);
-    }
-}
-
-/*
- * ftpCheckAuth
- *
- * Return 1 if we have everything needed to complete this request.
- * Return 0 if something is missing.
- */
-static int
-ftpCheckAuth(FtpStateData * ftpState, const HttpHeader * req_hdr)
-{
-    char *orig_user;
-    const char *auth;
-    ftpLoginParser(ftpState->request->login, ftpState, FTP_LOGIN_ESCAPED);
-    if (!ftpState->user[0])
-	return 1;		/* no name */
-    if (ftpState->password_url || ftpState->password[0])
-	return 1;		/* passwd provided in URL */
-    /* URL has name, but no passwd */
-    if (!(auth = httpHeaderGetAuth(req_hdr, HDR_AUTHORIZATION, "Basic")))
-	return 0;		/* need auth header */
-    ftpState->flags.authenticated = 1;
-    orig_user = xstrdup(ftpState->user);
-    ftpLoginParser(auth, ftpState, FTP_LOGIN_NOT_ESCAPED);
-    if (strcmp(orig_user, ftpState->user) == 0) {
-	xfree(orig_user);
-	return 1;		/* same username */
-    }
-    xstrncpy(ftpState->user, orig_user, sizeof(ftpState->user));
-    xfree(orig_user);
-    return 0;			/* different username */
-}
-
-static void
-ftpCheckUrlpath(FtpStateData * ftpState)
-{
-    request_t *request = ftpState->request;
-    int l;
-    const char *t;
-    if ((t = strRChr(request->urlpath, ';')) != NULL) {
-	if (strncasecmp(t + 1, "type=", 5) == 0) {
-	    ftpState->typecode = (char) toupper((int) *(t + 6));
-	    strCutPtr(request->urlpath, t);
-	}
-    }
-    l = strLen(request->urlpath);
-    ftpState->flags.need_base_href = 1;
-    /* check for null path */
-    if (!l) {
-	ftpState->flags.isdir = 1;
-	ftpState->flags.root_dir = 1;
-    } else if (!strCmp(request->urlpath, "/%2f/")) {
-	/* UNIX root directory */
-	ftpState->flags.need_base_href = 0;
-	ftpState->flags.isdir = 1;
-	ftpState->flags.root_dir = 1;
-    } else if ((l >= 1) && (*(strBuf(request->urlpath) + l - 1) == '/')) {
-	/* Directory URL, ending in / */
-	ftpState->flags.isdir = 1;
-	ftpState->flags.need_base_href = 0;
-	if (l == 1)
-	    ftpState->flags.root_dir = 1;
-    }
-}
-
-static void
-ftpBuildTitleUrl(FtpStateData * ftpState)
-{
-    request_t *request = ftpState->request;
-
-    stringReset(&ftpState->title_url, "ftp://");
-    if (strcmp(ftpState->user, "anonymous")) {
-	strCat(ftpState->title_url, ftpState->user);
-	strCat(ftpState->title_url, "@");
-    }
-    strCat(ftpState->title_url, request->host);
-    if (request->port != urlDefaultPort(PROTO_FTP)) {
-	strCat(ftpState->title_url, ":");
-	strCat(ftpState->title_url, xitoa(request->port));
-    }
-    strCat(ftpState->title_url, strBuf(request->urlpath));
-
-    stringReset(&ftpState->base_href, "ftp://");
-    if (strcmp(ftpState->user, "anonymous") != 0) {
-	strCat(ftpState->base_href, rfc1738_escape_part(ftpState->user));
-	if (ftpState->password_url) {
-	    strCat(ftpState->base_href, ":");
-	    strCat(ftpState->base_href, rfc1738_escape_part(ftpState->password));
-	}
-	strCat(ftpState->base_href, "@");
-    }
-    strCat(ftpState->base_href, request->host);
-    if (request->port != urlDefaultPort(PROTO_FTP)) {
-	strCat(ftpState->base_href, ":");
-	strCat(ftpState->base_href, xitoa(request->port));
-    }
-    strCat(ftpState->base_href, strBuf(request->urlpath));
-    strCat(ftpState->base_href, "/");
-}
-
-CBDATA_TYPE(FtpStateData);
-void
-ftpStart(FwdState * fwd)
-{
-    request_t *request = fwd->request;
-    StoreEntry *entry = fwd->entry;
-    int fd = fwd->server_fd;
-    LOCAL_ARRAY(char, realm, 8192);
-    const char *url = storeUrl(entry);
-    FtpStateData *ftpState;
-    HttpReply *reply;
-
-    CBDATA_INIT_TYPE(FtpStateData);
-    ftpState = cbdataAlloc(FtpStateData);
-    debug(9, 3) ("ftpStart: '%s'\n", url);
-    statCounter.server.all.requests++;
-    statCounter.server.ftp.requests++;
-    storeLockObject(entry);
-    ftpState->entry = entry;
-    ftpState->request = requestLink(request);
-    ftpState->ctrl.fd = fd;
-    ftpState->data.fd = -1;
-    ftpState->size = -1;
-    ftpState->mdtm = -1;
-    if (!Config.Ftp.passive)
-	ftpState->flags.rest_supported = 0;
-    else if (fwd->flags.ftp_pasv_failed)
-	ftpState->flags.pasv_supported = 0;
-    else
-	ftpState->flags.pasv_supported = 1;
-    ftpState->flags.rest_supported = 1;
-    ftpState->fwd = fwd;
-    comm_add_close_handler(fd, ftpStateFree, ftpState);
-    if (ftpState->request->method == METHOD_PUT)
-	ftpState->flags.put = 1;
-    if (!ftpCheckAuth(ftpState, &request->header)) {
-	/* This request is not fully authenticated */
-	if (request->port == 21) {
-	    snprintf(realm, 8192, "ftp %s", ftpState->user);
-	} else {
-	    snprintf(realm, 8192, "ftp %s port %d",
-		ftpState->user, request->port);
-	}
-	/* create reply */
-	reply = entry->mem_obj->reply;
-	assert(reply != NULL);
-	/* create appropriate reply */
-	ftpAuthRequired(reply, request, realm);
-	httpReplySwapOut(reply, entry);
-	fwdComplete(ftpState->fwd);
-	comm_close(fd);
-	return;
-    }
-    ftpCheckUrlpath(ftpState);
-    ftpBuildTitleUrl(ftpState);
-    debug(9, 5) ("ftpStart: host=%s, path=%s, user=%s, passwd=%s\n",
-	ftpState->request->host, strBuf(ftpState->request->urlpath),
-	ftpState->user, ftpState->password);
-    ftpState->state = BEGIN;
-    ftpState->ctrl.last_command = xstrdup("Connect to server");
-    ftpState->ctrl.buf = memAllocBuf(4096, &ftpState->ctrl.size);
-    ftpState->ctrl.offset = 0;
-    ftpState->data.buf = memAllocBuf(SQUID_TCP_SO_RCVBUF, &ftpState->data.size);
-    ftpScheduleReadControlReply(ftpState, 0);
-}
-
-/* ====================================================================== */
-
-static void
-ftpWriteCommand(const char *buf, FtpStateData * ftpState)
-{
-    debug(9, 5) ("ftpWriteCommand: %s\n", buf);
-    safe_free(ftpState->ctrl.last_command);
-    safe_free(ftpState->ctrl.last_reply);
-    ftpState->ctrl.last_command = xstrdup(buf);
-    comm_write(ftpState->ctrl.fd,
-	xstrdup(buf),
-	strlen(buf),
-	ftpWriteCommandCallback,
-	ftpState,
-	xfree);
-    ftpScheduleReadControlReply(ftpState, 0);
-}
-
-static void
-ftpWriteCommandCallback(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
-{
-    FtpStateData *ftpState = data;
-    debug(9, 7) ("ftpWriteCommandCallback: wrote %d bytes\n", (int) size);
-    if (size > 0) {
-	fd_bytes(fd, size, FD_WRITE);
-	kb_incr(&statCounter.server.all.kbytes_out, size);
-	kb_incr(&statCounter.server.ftp.kbytes_out, size);
-    }
-    if (errflag == COMM_ERR_CLOSING)
-	return;
-    if (errflag) {
-	debug(9, 1) ("ftpWriteCommandCallback: FD %d: %s\n", fd, xstrerror());
-	ftpFailed(ftpState, ERR_WRITE_ERROR);
-	/* ftpFailed closes ctrl.fd and frees ftpState */
-	return;
-    }
-}
-
-static wordlist *
-ftpParseControlReply(char *buf, size_t len, int *codep, int *used)
-{
-    char *s;
-    char *sbuf;
-    char *end;
-    int usable;
-    int complete = 0;
-    wordlist *head = NULL;
-    wordlist *list;
-    wordlist **tail = &head;
-    off_t offset;
-    size_t linelen;
-    int code = -1;
-    debug(9, 5) ("ftpParseControlReply\n");
-    /*
-     * We need a NULL-terminated buffer for scanning, ick
-     */
-    sbuf = xmalloc(len + 1);
-    xstrncpy(sbuf, buf, len + 1);
-    end = sbuf + len - 1;
-    while (*end != '\r' && *end != '\n' && end > sbuf)
-	end--;
-    usable = end - sbuf;
-    debug(9, 3) ("ftpParseControlReply: usable = %d\n", usable);
-    if (usable == 0) {
-	debug(9, 3) ("ftpParseControlReply: didn't find end of line\n");
-	safe_free(sbuf);
-	return NULL;
-    }
-    debug(9, 3) ("ftpParseControlReply: %d bytes to play with\n", (int) len);
-    end++;
-    s = sbuf;
-    s += strspn(s, crlf);
-    for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
-	if (complete)
-	    break;
-	debug(9, 3) ("ftpParseControlReply: s = {%s}\n", s);
-	linelen = strcspn(s, crlf) + 1;
-	if (linelen < 2)
-	    break;
-	if (linelen > 3)
-	    complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
-	if (complete)
-	    code = atoi(s);
-	offset = 0;
-	if (linelen > 3)
-	    if (*s >= '0' && *s <= '9' && (*(s + 3) == '-' || *(s + 3) == ' '))
-		offset = 4;
-	list = memAllocate(MEM_WORDLIST);
-	list->key = xmalloc(linelen - offset);
-	xstrncpy(list->key, s + offset, linelen - offset);
-	debug(9, 7) ("%d %s\n", code, list->key);
-	*tail = list;
-	tail = &list->next;
-    }
-    *used = (int) (s - sbuf);
-    safe_free(sbuf);
-    if (!complete)
-	wordlistDestroy(&head);
-    if (codep)
-	*codep = code;
-    return head;
-}
-
-static void
-ftpScheduleReadControlReply(FtpStateData * ftpState, int buffered_ok)
-{
-    debug(9, 3) ("ftpScheduleReadControlReply: FD %d\n", ftpState->ctrl.fd);
-    if (buffered_ok && ftpState->ctrl.offset > 0) {
-	/* We've already read some reply data */
-	ftpHandleControlReply(ftpState);
-    } else {
-	commSetSelect(ftpState->ctrl.fd,
-	    COMM_SELECT_READ,
-	    ftpReadControlReply,
-	    ftpState,
-	    Config.Timeout.read);
-	/*
-	 * Cancel the timeout on the Data socket (if any) and
-	 * establish one on the control socket.
-	 */
-	if (ftpState->data.fd > -1)
-	    commSetTimeout(ftpState->data.fd, -1, NULL, NULL);
-	commSetTimeout(ftpState->ctrl.fd, Config.Timeout.read, ftpTimeout,
-	    ftpState);
-    }
-}
-
-static void
-ftpReadControlReply(int fd, void *data)
-{
-    FtpStateData *ftpState = data;
-    StoreEntry *entry = ftpState->entry;
-    int len;
-    debug(9, 5) ("ftpReadControlReply\n");
-    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
-	comm_close(ftpState->ctrl.fd);
-	return;
-    }
-    assert(ftpState->ctrl.offset < ftpState->ctrl.size);
-    statCounter.syscalls.sock.reads++;
-    len = FD_READ_METHOD(fd,
-	ftpState->ctrl.buf + ftpState->ctrl.offset,
-	ftpState->ctrl.size - ftpState->ctrl.offset);
-    if (len > 0) {
-	fd_bytes(fd, len, FD_READ);
-	kb_incr(&statCounter.server.all.kbytes_in, len);
-	kb_incr(&statCounter.server.ftp.kbytes_in, len);
-    }
-    debug(9, 5) ("ftpReadControlReply: FD %d, Read %d bytes\n", fd, len);
-    if (len < 0) {
-	debug(50, ignoreErrno(errno) ? 3 : 1) ("ftpReadControlReply: read error: %s\n", xstrerror());
-	if (ignoreErrno(errno)) {
-	    ftpScheduleReadControlReply(ftpState, 0);
-	} else {
-	    ftpFailed(ftpState, ERR_READ_ERROR);
-	    /* ftpFailed closes ctrl.fd and frees ftpState */
-	    return;
-	}
-	return;
-    }
-    if (len == 0) {
-	if (entry->store_status == STORE_PENDING) {
-	    ftpFailed(ftpState, ERR_FTP_FAILURE);
-	    /* ftpFailed closes ctrl.fd and frees ftpState */
-	    return;
-	}
-	comm_close(ftpState->ctrl.fd);
-	return;
-    }
-    len += ftpState->ctrl.offset;
-    ftpState->ctrl.offset = len;
-    assert(len <= ftpState->ctrl.size);
-    ftpHandleControlReply(ftpState);
-}
-
-static void
-ftpHandleControlReply(FtpStateData * ftpState)
-{
-    wordlist **W;
-    int bytes_used = 0;
-    wordlistDestroy(&ftpState->ctrl.message);
-    ftpState->ctrl.message = ftpParseControlReply(ftpState->ctrl.buf,
-	ftpState->ctrl.offset, &ftpState->ctrl.replycode, &bytes_used);
-    if (ftpState->ctrl.message == NULL) {
-	/* didn't get complete reply yet */
-	if (ftpState->ctrl.offset == ftpState->ctrl.size) {
-	    ftpState->ctrl.buf = memReallocBuf(ftpState->ctrl.buf, ftpState->ctrl.size << 1, &ftpState->ctrl.size);
-	}
-	ftpScheduleReadControlReply(ftpState, 0);
-	return;
-    } else if (ftpState->ctrl.offset == bytes_used) {
-	/* used it all up */
-	ftpState->ctrl.offset = 0;
-    } else {
-	/* Got some data past the complete reply */
-	assert(bytes_used < ftpState->ctrl.offset);
-	ftpState->ctrl.offset -= bytes_used;
-	xmemmove(ftpState->ctrl.buf, ftpState->ctrl.buf + bytes_used,
-	    ftpState->ctrl.offset);
-    }
-    /* Move the last line of the reply message to ctrl.last_reply */
-    for (W = &ftpState->ctrl.message; (*W)->next; W = &(*W)->next);
-    safe_free(ftpState->ctrl.last_reply);
-    ftpState->ctrl.last_reply = xstrdup((*W)->key);
-    wordlistDestroy(W);
-    /* Copy the rest of the message to cwd_message to be printed in
-     * error messages
-     */
-    wordlistAddWl(&ftpState->cwd_message, ftpState->ctrl.message);
-    debug(9, 8) ("ftpHandleControlReply: state=%d, code=%d\n", ftpState->state,
-	ftpState->ctrl.replycode);
-    FTP_SM_FUNCS[ftpState->state] (ftpState);
-}
-
-/* ====================================================================== */
-
-static void
-ftpReadWelcome(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("ftpReadWelcome\n");
-    if (ftpState->flags.pasv_only)
-	ftpState->login_att++;
-    /* Dont retry if the FTP server accepted the connection */
-    ftpState->fwd->flags.dont_retry = 1;
-    if (code == 220) {
-	if (ftpState->ctrl.message) {
-	    if (strstr(ftpState->ctrl.message->key, "NetWare"))
-		ftpState->flags.skip_whitespace = 1;
-	}
-	ftpSendUser(ftpState);
-    } else if (code == 120) {
-	if (NULL != ftpState->ctrl.message)
-	    debug(9, 3) ("FTP server is busy: %s\n",
-		ftpState->ctrl.message->key);
-	return;
-    } else {
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpSendUser(FtpStateData * ftpState)
-{
-    if (ftpState->proxy_host != NULL)
-	snprintf(cbuf, 1024, "USER %s@%s\r\n",
-	    ftpState->user,
-	    ftpState->request->host);
-    else
-	snprintf(cbuf, 1024, "USER %s\r\n", ftpState->user);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_USER;
-}
-
-static void
-ftpReadUser(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("ftpReadUser\n");
-    if (code == 230) {
-	ftpReadPass(ftpState);
-    } else if (code == 331) {
-	ftpSendPass(ftpState);
-    } else {
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpSendPass(FtpStateData * ftpState)
-{
-    snprintf(cbuf, 1024, "PASS %s\r\n", ftpState->password);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_PASS;
-}
-
-static void
-ftpReadPass(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("ftpReadPass\n");
-    if (code == 230) {
-	ftpSendType(ftpState);
-    } else {
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpSendType(FtpStateData * ftpState)
-{
-    const char *t;
-    const char *filename;
-    char mode;
-    /*
-     * Ref section 3.2.2 of RFC 1738
-     */
-    mode = ftpState->typecode;
-    switch (mode) {
-    case 'D':
-	mode = 'A';
-	break;
-    case 'A':
-    case 'I':
-	break;
-    default:
-	if (ftpState->flags.isdir) {
-	    mode = 'A';
-	} else {
-	    t = strRChr(ftpState->request->urlpath, '/');
-	    filename = t ? t + 1 : strBuf(ftpState->request->urlpath);
-	    mode = mimeGetTransferMode(filename);
-	}
-	break;
-    }
-    if (mode == 'I')
-	ftpState->flags.binary = 1;
-    else
-	ftpState->flags.binary = 0;
-    snprintf(cbuf, 1024, "TYPE %c\r\n", mode);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_TYPE;
-}
-
-static void
-ftpReadType(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    char *path;
-    char *d, *p;
-    debug(9, 3) ("This is ftpReadType\n");
-    if (code == 200) {
-	p = path = xstrdup(strBuf(ftpState->request->urlpath));
-	if (*p == '/')
-	    p++;
-	while (*p) {
-	    d = p;
-	    p += strcspn(p, "/");
-	    if (*p)
-		*p++ = '\0';
-	    rfc1738_unescape(d);
-	    wordlistAdd(&ftpState->pathcomps, d);
-	}
-	xfree(path);
-	if (ftpState->pathcomps)
-	    ftpTraverseDirectory(ftpState);
-	else
-	    ftpListDir(ftpState);
-    } else {
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpTraverseDirectory(FtpStateData * ftpState)
-{
-    wordlist *w;
-    debug(9, 4) ("ftpTraverseDirectory %s\n",
-	ftpState->filepath ? ftpState->filepath : "<NULL>");
-
-    safe_free(ftpState->filepath);
-    /* Done? */
-    if (ftpState->pathcomps == NULL) {
-	debug(9, 3) ("the final component was a directory\n");
-	ftpListDir(ftpState);
-	return;
-    }
-    /* Go to next path component */
-    w = ftpState->pathcomps;
-    ftpState->filepath = w->key;
-    ftpState->pathcomps = w->next;
-    memFree(w, MEM_WORDLIST);
-    /* Check if we are to CWD or RETR */
-    if (ftpState->pathcomps != NULL || ftpState->flags.isdir) {
-	ftpSendCwd(ftpState);
-    } else {
-	debug(9, 3) ("final component is probably a file\n");
-	ftpGetFile(ftpState);
-	return;
-    }
-}
-
-static void
-ftpSendCwd(FtpStateData * ftpState)
-{
-    char *path = ftpState->filepath;
-    debug(9, 3) ("ftpSendCwd\n");
-    if (!strcmp(path, "..") || !strcmp(path, "/")) {
-	ftpState->flags.no_dotdot = 1;
-    } else {
-	ftpState->flags.no_dotdot = 0;
-    }
-    if (*path)
-	snprintf(cbuf, 1024, "CWD %s\r\n", path);
-    else
-	snprintf(cbuf, 1024, "CWD\r\n");
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_CWD;
-}
-
-static void
-ftpReadCwd(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadCwd\n");
-    if (code >= 200 && code < 300) {
-	/* CWD OK */
-	ftpUnhack(ftpState);
-	/* Reset cwd_message to only include the last message */
-	if (ftpState->cwd_message)
-	    wordlistDestroy(&ftpState->cwd_message);
-	ftpState->cwd_message = ftpState->ctrl.message;
-	ftpState->ctrl.message = NULL;
-	/* Continue to traverse the path */
-	ftpTraverseDirectory(ftpState);
-    } else {
-	/* CWD FAILED */
-	if (!ftpState->flags.put)
-	    ftpFail(ftpState);
-	else
-	    ftpSendMkdir(ftpState);
-    }
-}
-
-static void
-ftpSendMkdir(FtpStateData * ftpState)
-{
-    char *path = ftpState->filepath;
-    debug(9, 3) ("ftpSendMkdir: with path=%s\n", path);
-    snprintf(cbuf, 1024, "MKD %s\r\n", path);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_MKDIR;
-}
-
-static void
-ftpReadMkdir(FtpStateData * ftpState)
-{
-    char *path = ftpState->filepath;
-    int code = ftpState->ctrl.replycode;
-
-    debug(9, 3) ("ftpReadMkdir: path %s, code %d\n", path, code);
-    if (code == 257) {		/* success */
-	ftpSendCwd(ftpState);
-    } else if (code == 550) {	/* dir exists */
-	if (ftpState->flags.put_mkdir) {
-	    ftpState->flags.put_mkdir = 1;
-	    ftpSendCwd(ftpState);
-	} else
-	    ftpSendReply(ftpState);
-    } else
-	ftpSendReply(ftpState);
-}
-
-static void
-ftpGetFile(FtpStateData * ftpState)
-{
-    assert(*ftpState->filepath != '\0');
-    ftpState->flags.isdir = 0;
-    ftpSendMdtm(ftpState);
-}
-
-static void
-ftpListDir(FtpStateData * ftpState)
-{
-    if (!ftpState->flags.isdir) {
-	debug(9, 3) ("Directory path did not end in /\n");
-	strCat(ftpState->title_url, "/");
-	ftpState->flags.isdir = 1;
-	ftpState->flags.need_base_href = 1;
-    }
-    ftpSendPasv(ftpState);
-}
-
-static void
-ftpSendMdtm(FtpStateData * ftpState)
-{
-    assert(*ftpState->filepath != '\0');
-    snprintf(cbuf, 1024, "MDTM %s\r\n", ftpState->filepath);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_MDTM;
-}
-
-static void
-ftpReadMdtm(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadMdtm\n");
-    if (code == 213) {
-	ftpState->mdtm = parse_iso3307_time(ftpState->ctrl.last_reply);
-	ftpUnhack(ftpState);
-    } else if (code < 0) {
-	ftpFail(ftpState);
-    }
-    ftpSendSize(ftpState);
-}
-
-static void
-ftpSendSize(FtpStateData * ftpState)
-{
-    /* Only send SIZE for binary transfers. The returned size
-     * is useless on ASCII transfers */
-    if (ftpState->flags.binary) {
-	assert(ftpState->filepath != NULL);
-	assert(*ftpState->filepath != '\0');
-	snprintf(cbuf, 1024, "SIZE %s\r\n", ftpState->filepath);
-	ftpWriteCommand(cbuf, ftpState);
-	ftpState->state = SENT_SIZE;
-    } else
-	/* Skip to next state no non-binary transfers */
-	ftpSendPasv(ftpState);
-}
-
-static void
-ftpReadSize(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadSize\n");
-    if (code == 213) {
-	ftpUnhack(ftpState);
-	ftpState->size = atoi(ftpState->ctrl.last_reply);
-	if (ftpState->size == 0) {
-	    debug(9, 2) ("ftpReadSize: SIZE reported %s on %s\n",
-		ftpState->ctrl.last_reply,
-		strBuf(ftpState->title_url));
-	    ftpState->size = -1;
-	}
-    } else if (code < 0) {
-	ftpFail(ftpState);
-    }
-    ftpSendPasv(ftpState);
-}
-
-static void
-ftpSendPasv(FtpStateData * ftpState)
-{
-    int fd;
-    struct sockaddr_in addr;
-    socklen_t addr_len;
-    if (ftpState->request->method == METHOD_HEAD) {
-	/* Terminate here for HEAD requests */
-	ftpAppendSuccessHeader(ftpState);
-	storeTimestampsSet(ftpState->entry);
-	/*
-	 * On rare occasions I'm seeing the entry get aborted after
-	 * ftpReadControlReply() and before here, probably when
-	 * trying to write to the client.
-	 */
-	if (!EBIT_TEST(ftpState->entry->flags, ENTRY_ABORTED))
-	    fwdComplete(ftpState->fwd);
-	ftpSendQuit(ftpState);
-	return;
-    }
-    if (ftpState->data.fd >= 0) {
-	if (!ftpState->flags.datachannel_hack) {
-	    /* We are already connected, reuse this connection. */
-	    ftpRestOrList(ftpState);
-	    return;
-	} else {
-	    /* Close old connection */
-	    comm_close(ftpState->data.fd);
-	    ftpState->data.fd = -1;
-	}
-    }
-    if (!ftpState->flags.pasv_supported) {
-	ftpSendPort(ftpState);
-	return;
-    }
-    addr_len = sizeof(addr);
-    if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
-	debug(9, 0) ("ftpSendPasv: getsockname(%d,..): %s\n",
-	    ftpState->ctrl.fd, xstrerror());
-	ftpFail(ftpState);
-	return;
-    }
-    /* Open data channel with the same local address as control channel */
-    fd = comm_open(SOCK_STREAM,
-	0,
-	addr.sin_addr,
-	0,
-	COMM_NONBLOCKING,
-	storeUrl(ftpState->entry));
-    debug(9, 3) ("ftpSendPasv: Unconnected data socket created on FD %d\n", fd);
-    if (fd < 0) {
-	ftpFail(ftpState);
-	return;
-    }
-    /*
-     * No comm_add_close_handler() here.  If we have both ctrl and
-     * data FD's call ftpStateFree() upon close, then we have
-     * to delete the close handler which did NOT get called
-     * to prevent ftpStateFree() getting called twice.
-     * Instead we'll always call comm_close() on the ctrl FD.
-     */
-    ftpState->data.fd = fd;
-    snprintf(cbuf, 1024, "PASV\r\n");
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_PASV;
-    /*
-     * ugly hack for ftp servers like ftp.netscape.com that sometimes
-     * dont acknowledge PORT commands.
-     */
-    commSetTimeout(ftpState->data.fd, 15, ftpTimeout, ftpState);
-}
-
-static void
-ftpReadPasv(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    int h1, h2, h3, h4;
-    int p1, p2;
-    int n;
-    u_short port;
-    int fd = ftpState->data.fd;
-    char *buf;
-    LOCAL_ARRAY(char, ipaddr, 1024);
-    debug(9, 3) ("This is ftpReadPasv\n");
-    if (code != 227) {
-	debug(9, 3) ("PASV not supported by remote end\n");
-	ftpSendPort(ftpState);
-	return;
-    }
-    /*  227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).  */
-    /*  ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
-    debug(9, 5) ("scanning: %s\n", ftpState->ctrl.last_reply);
-    buf = strstr(ftpState->ctrl.last_reply, "(");
-    if (!buf) {
-	debug(9, 1) ("Unsafe PASV reply from %s: '%s'\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
-	ftpSendPort(ftpState);
-	return;
-    }
-    buf++;			/* skip ( */
-    n = sscanf(buf, "%d,%d,%d,%d,%d,%d", &h1, &h2, &h3, &h4, &p1, &p2);
-    if (n != 6 || p1 < 0 || p2 < 0 || p1 > 255 || p2 > 255) {
-	debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
-	ftpSendPort(ftpState);
-	return;
-    }
-    snprintf(ipaddr, 1024, "%d.%d.%d.%d", h1, h2, h3, h4);
-    if (!safe_inet_addr(ipaddr, NULL)) {
-	debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
-	ftpSendPort(ftpState);
-	return;
-    }
-    port = ((p1 << 8) + p2);
-    if (0 == port) {
-	debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
-	ftpSendPort(ftpState);
-	return;
-    }
-    if (Config.Ftp.sanitycheck) {
-	if (strcmp(fd_table[ftpState->ctrl.fd].ipaddr, ipaddr) != 0) {
-	    debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
-	    ftpSendPort(ftpState);
-	    return;
-	}
-	if (port < 1024) {
-	    debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
-	    ftpSendPort(ftpState);
-	    return;
-	}
-    }
-    debug(9, 5) ("ftpReadPasv: connecting to %s, port %d\n", ipaddr, port);
-    ftpState->data.port = port;
-    ftpState->data.host = xstrdup(ipaddr);
-    safe_free(ftpState->ctrl.last_command);
-    safe_free(ftpState->ctrl.last_reply);
-    ftpState->ctrl.last_command = xstrdup("Connect to server data port");
-    commConnectStart(fd, ipaddr, port, ftpPasvCallback, ftpState);
-}
-
-static void
-ftpPasvCallback(int fd, int status, void *data)
-{
-    FtpStateData *ftpState = data;
-    debug(9, 3) ("ftpPasvCallback\n");
-    if (status != COMM_OK) {
-	debug(9, 2) ("ftpPasvCallback: failed to connect. Retrying without PASV.\n");
-	ftpState->fwd->flags.dont_retry = 0;	/* this is a retryable error */
-	ftpState->fwd->flags.ftp_pasv_failed = 1;
-	ftpFailed(ftpState, ERR_NONE);
-	/* ftpFailed closes ctrl.fd and frees ftpState */
-	return;
-    }
-    ftpRestOrList(ftpState);
-}
-
-static int
-ftpOpenListenSocket(FtpStateData * ftpState, int fallback)
-{
-    int fd;
-    struct sockaddr_in addr;
-    socklen_t addr_len;
-    int on = 1;
-    u_short port = 0;
-    /*
-     * Tear down any old data connection if any. We are about to
-     * establish a new one.
-     */
-    if (ftpState->data.fd > 0) {
-	comm_close(ftpState->data.fd);
-	ftpState->data.fd = -1;
-    }
-    /*
-     * Set up a listen socket on the same local address as the
-     * control connection.
-     */
-    addr_len = sizeof(addr);
-    if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
-	debug(9, 0) ("ftpOpenListenSocket: getsockname(%d,..): %s\n",
-	    ftpState->ctrl.fd, xstrerror());
-	return -1;
-    }
-    /*
-     * REUSEADDR is needed in fallback mode, since the same port is
-     * used for both control and data.
-     */
-    if (fallback) {
-	setsockopt(ftpState->ctrl.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
-	port = ntohs(addr.sin_port);
-    }
-    fd = comm_open(SOCK_STREAM,
-	0,
-	addr.sin_addr,
-	port,
-	COMM_NONBLOCKING | (fallback ? COMM_REUSEADDR : 0),
-	storeUrl(ftpState->entry));
-    debug(9, 3) ("ftpOpenListenSocket: Unconnected data socket created on FD %d\n", fd);
-    if (fd < 0) {
-	debug(9, 0) ("ftpOpenListenSocket: comm_open failed\n");
-	return -1;
-    }
-    if (comm_listen(fd) < 0) {
-	comm_close(fd);
-	return -1;
-    }
-    ftpState->data.fd = fd;
-    ftpState->data.port = comm_local_port(fd);
-    ftpState->data.host = NULL;
-    return fd;
-}
-
-static void
-ftpSendPort(FtpStateData * ftpState)
-{
-    int fd;
-    struct sockaddr_in addr;
-    socklen_t addr_len;
-    unsigned char *addrptr;
-    unsigned char *portptr;
-    debug(9, 3) ("This is ftpSendPort\n");
-    ftpState->flags.pasv_supported = 0;
-    fd = ftpOpenListenSocket(ftpState, 0);
-    addr_len = sizeof(addr);
-    if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
-	debug(9, 0) ("ftpSendPort: getsockname(%d,..): %s\n", fd, xstrerror());
-	/* XXX Need to set error message */
-	ftpFail(ftpState);
-	return;
-    }
-    addrptr = (unsigned char *) &addr.sin_addr.s_addr;
-    portptr = (unsigned char *) &addr.sin_port;
-    snprintf(cbuf, 1024, "PORT %d,%d,%d,%d,%d,%d\r\n",
-	addrptr[0], addrptr[1], addrptr[2], addrptr[3],
-	portptr[0], portptr[1]);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_PORT;
-}
-
-static void
-ftpReadPort(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadPort\n");
-    if (code != 200) {
-	/* Fall back on using the same port as the control connection */
-	debug(9, 3) ("PORT not supported by remote end\n");
-	ftpOpenListenSocket(ftpState, 1);
-    }
-    ftpRestOrList(ftpState);
-}
-
-/* "read" handler to accept data connection */
-static void
-ftpAcceptDataConnection(int fd, void *data)
-{
-    FtpStateData *ftpState = data;
-    struct sockaddr_in my_peer, me;
-    debug(9, 3) ("ftpAcceptDataConnection\n");
-
-    if (EBIT_TEST(ftpState->entry->flags, ENTRY_ABORTED)) {
-	comm_close(ftpState->ctrl.fd);
-	return;
-    }
-    fd = comm_accept(fd, &my_peer, &me);
-    if (Config.Ftp.sanitycheck) {
-	char *ipaddr = inet_ntoa(my_peer.sin_addr);
-	if (strcmp(fd_table[ftpState->ctrl.fd].ipaddr, ipaddr) != 0) {
-	    debug(9, 1) ("FTP data connection from unexpected server (%s:%d), expecting %s\n", ipaddr, (int) ntohs(my_peer.sin_port), fd_table[ftpState->ctrl.fd].ipaddr);
-	    comm_close(fd);
-	    commSetSelect(ftpState->data.fd,
-		COMM_SELECT_READ,
-		ftpAcceptDataConnection,
-		ftpState,
-		0);
-	    return;
-	}
-    }
-    if (fd < 0) {
-	debug(9, 1) ("ftpHandleDataAccept: comm_accept(%d): %s", fd, xstrerror());
-	/* XXX Need to set error message */
-	ftpFail(ftpState);
-	return;
-    }
-    /* Replace the Listen socket with the accepted data socket */
-    comm_close(ftpState->data.fd);
-    debug(9, 3) ("ftpAcceptDataConnection: Connected data socket on FD %d\n", fd);
-    ftpState->data.fd = fd;
-    ftpState->data.port = ntohs(my_peer.sin_port);
-    ftpState->data.host = xstrdup(inet_ntoa(my_peer.sin_addr));
-    commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
-    commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
-	ftpState);
-    /* XXX We should have a flag to track connect state...
-     *    host NULL -> not connected, port == local port
-     *    host set  -> connected, port == remote port
-     */
-    /* Restart state (SENT_NLST/LIST/RETR) */
-    FTP_SM_FUNCS[ftpState->state] (ftpState);
-}
-
-static void
-ftpRestOrList(FtpStateData * ftpState)
-{
-    debug(9, 3) ("This is ftpRestOrList\n");
-    if (ftpState->typecode == 'D') {
-	ftpState->flags.isdir = 1;
-	ftpState->flags.need_base_href = 1;
-	if (ftpState->flags.put) {
-	    ftpSendMkdir(ftpState);	/* PUT name;type=d */
-	} else {
-	    ftpSendNlst(ftpState);	/* GET name;type=d  sec 3.2.2 of RFC 1738 */
-	}
-    } else if (ftpState->flags.put) {
-	debug(9, 3) ("ftpRestOrList: Sending STOR request...\n");
-	ftpSendStor(ftpState);
-    } else if (ftpState->flags.isdir)
-	ftpSendList(ftpState);
-    else if (ftpRestartable(ftpState))
-	ftpSendRest(ftpState);
-    else
-	ftpSendRetr(ftpState);
-}
-
-static void
-ftpSendStor(FtpStateData * ftpState)
-{
-    if (ftpState->filepath != NULL) {
-	/* Plain file upload */
-	snprintf(cbuf, 1024, "STOR %s\r\n", ftpState->filepath);
-	ftpWriteCommand(cbuf, ftpState);
-	ftpState->state = SENT_STOR;
-    } else if (httpHeaderGetInt(&ftpState->request->header, HDR_CONTENT_LENGTH) > 0) {
-	/* File upload without a filename. use STOU to generate one */
-	snprintf(cbuf, 1024, "STOU\r\n");
-	ftpWriteCommand(cbuf, ftpState);
-	ftpState->state = SENT_STOR;
-    } else {
-	/* No file to transfer. Only create directories if needed */
-	ftpSendReply(ftpState);
-    }
-}
-
-static void
-ftpReadStor(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadStor\n");
-    if (code == 125 || (code == 150 && ftpState->data.host)) {
-	/* Begin data transfer */
-	debug(9, 3) ("ftpReadStor: starting data transfer\n");
-	commSetSelect(ftpState->data.fd,
-	    COMM_SELECT_WRITE,
-	    ftpDataWrite,
-	    ftpState,
-	    Config.Timeout.read);
-	/*
-	 * Cancel the timeout on the Control socket and
-	 * establish one on the data socket.
-	 */
-	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
-	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
-	    ftpState);
-	ftpState->state = WRITING_DATA;
-	debug(9, 3) ("ftpReadStor: writing data channel\n");
-    } else if (code == 150) {
-	/* Accept data channel */
-	debug(9, 3) ("ftpReadStor: accepting data channel\n");
-	commSetSelect(ftpState->data.fd,
-	    COMM_SELECT_READ,
-	    ftpAcceptDataConnection,
-	    ftpState,
-	    0);
-    } else {
-	debug(9, 3) ("ftpReadStor: Unexpected reply code %03d\n", code);
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpSendRest(FtpStateData * ftpState)
-{
-    snprintf(cbuf, 1024, "REST %d\r\n", ftpState->restart_offset);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_REST;
-}
-
-static int
-ftpRestartable(FtpStateData * ftpState)
-{
-    if (ftpState->restart_offset > 0)
-	return 1;
-    if (!ftpState->request->range)
-	return 0;
-    if (!ftpState->flags.binary)
-	return 0;
-    if (ftpState->size <= 0)
-	return 0;
-
-    ftpState->restart_offset = httpHdrRangeLowestOffset(ftpState->request->range, (size_t) ftpState->size);
-    if (ftpState->restart_offset <= 0)
-	return 0;
-    return 1;
-}
-
-static void
-ftpReadRest(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadRest\n");
-    assert(ftpState->restart_offset > 0);
-    if (code == 350) {
-	ftpState->restarted_offset = ftpState->restart_offset;
-	ftpSendRetr(ftpState);
-    } else if (code > 0) {
-	debug(9, 3) ("ftpReadRest: REST not supported\n");
-	ftpState->flags.rest_supported = 0;
-	ftpSendRetr(ftpState);
-    } else {
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpSendList(FtpStateData * ftpState)
-{
-    if (ftpState->filepath) {
-	ftpState->flags.need_base_href = 1;
-	snprintf(cbuf, 1024, "LIST %s\r\n", ftpState->filepath);
-    } else {
-	snprintf(cbuf, 1024, "LIST\r\n");
-    }
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_LIST;
-}
-
-static void
-ftpSendNlst(FtpStateData * ftpState)
-{
-    ftpState->flags.tried_nlst = 1;
-    if (ftpState->filepath) {
-	ftpState->flags.need_base_href = 1;
-	snprintf(cbuf, 1024, "NLST %s\r\n", ftpState->filepath);
-    } else {
-	snprintf(cbuf, 1024, "NLST\r\n");
-    }
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_NLST;
-}
-
-static void
-ftpReadList(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadList\n");
-    if (code == 125 || (code == 150 && ftpState->data.host)) {
-	/* Begin data transfer */
-	ftpAppendSuccessHeader(ftpState);
-	commSetSelect(ftpState->data.fd,
-	    COMM_SELECT_READ,
-	    ftpDataRead,
-	    ftpState,
-	    Config.Timeout.read);
-	commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry);
-	ftpState->state = READING_DATA;
-	/*
-	 * Cancel the timeout on the Control socket and establish one
-	 * on the data socket
-	 */
-	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
-	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState);
-	return;
-    } else if (code == 150) {
-	/* Accept data channel */
-	commSetSelect(ftpState->data.fd,
-	    COMM_SELECT_READ,
-	    ftpAcceptDataConnection,
-	    ftpState,
-	    0);
-	/*
-	 * Cancel the timeout on the Control socket and establish one
-	 * on the data socket
-	 */
-	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
-	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState);
-	return;
-    } else if (!ftpState->flags.tried_nlst && code > 300) {
-	ftpSendNlst(ftpState);
-    } else {
-	ftpFail(ftpState);
-	return;
-    }
-}
-
-static void
-ftpSendRetr(FtpStateData * ftpState)
-{
-    assert(ftpState->filepath != NULL);
-    snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath);
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_RETR;
-}
-
-static void
-ftpReadRetr(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadRetr\n");
-    if (code == 125 || (code == 150 && ftpState->data.host)) {
-	/* Begin data transfer */
-	debug(9, 3) ("ftpReadRetr: reading data channel\n");
-	ftpAppendSuccessHeader(ftpState);
-	commSetSelect(ftpState->data.fd,
-	    COMM_SELECT_READ,
-	    ftpDataRead,
-	    ftpState,
-	    Config.Timeout.read);
-	commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry);
-	ftpState->state = READING_DATA;
-	/*
-	 * Cancel the timeout on the Control socket and establish one
-	 * on the data socket
-	 */
-	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
-	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
-	    ftpState);
-    } else if (code == 150) {
-	/* Accept data channel */
-	commSetSelect(ftpState->data.fd,
-	    COMM_SELECT_READ,
-	    ftpAcceptDataConnection,
-	    ftpState,
-	    0);
-	/*
-	 * Cancel the timeout on the Control socket and establish one
-	 * on the data socket
-	 */
-	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
-	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
-	    ftpState);
-    } else if (code >= 300) {
-	if (!ftpState->flags.try_slash_hack) {
-	    /* Try this as a directory missing trailing slash... */
-	    ftpHackShortcut(ftpState, ftpSendCwd);
-	} else {
-	    ftpFail(ftpState);
-	}
-    } else {
-	ftpFail(ftpState);
-    }
-}
-
-static void
-ftpReadTransferDone(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpReadTransferDone\n");
-    if (code == 226) {
-	/* Connection closed; retrieval done. */
-	if (ftpState->flags.html_header_sent)
-	    ftpListingFinish(ftpState);
-	fwdUnregister(ftpState->ctrl.fd, ftpState->fwd);
-	fwdComplete(ftpState->fwd);
-	ftpSendQuit(ftpState);
-    } else {			/* != 226 */
-	debug(9, 1) ("ftpReadTransferDone: Got code %d after reading data\n",
-	    code);
-	ftpFailed(ftpState, ERR_FTP_FAILURE);
-	/* ftpFailed closes ctrl.fd and frees ftpState */
-	return;
-    }
-}
-
-/* This will be called when there is data available to put */
-static void
-ftpRequestBody(char *buf, ssize_t size, void *data)
-{
-    FtpStateData *ftpState = (FtpStateData *) data;
-    debug(9, 3) ("ftpRequestBody: buf=%p size=%d ftpState=%p\n", buf, (int) size, data);
-    ftpState->data.offset = size;
-    if (size > 0) {
-	/* DataWrite */
-	comm_write(ftpState->data.fd, buf, size, ftpDataWriteCallback, data, NULL);
-    } else if (size < 0) {
-	/* Error */
-	debug(9, 1) ("ftpRequestBody: request aborted");
-	ftpFailed(ftpState, ERR_READ_ERROR);
-    } else if (size == 0) {
-	/* End of transfer */
-	ftpDataComplete(ftpState);
-    }
-}
-
-/* This will be called when the put write is completed */
-static void
-ftpDataWriteCallback(int fd, char *buf, size_t size, int err, void *data)
-{
-    FtpStateData *ftpState = (FtpStateData *) data;
-    if (!err) {
-	/* Shedule the rest of the request */
-	clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState);
-    } else {
-	debug(9, 1) ("ftpDataWriteCallback: write error: %s\n", xstrerror());
-	ftpFailed(ftpState, ERR_WRITE_ERROR);
-    }
-}
-
-static void
-ftpDataWrite(int ftp, void *data)
-{
-    FtpStateData *ftpState = (FtpStateData *) data;
-    debug(9, 3) ("ftpDataWrite\n");
-    /* This starts the body transfer */
-    clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState);
-}
-
-static void
-ftpWriteTransferDone(FtpStateData * ftpState)
-{
-    int code = ftpState->ctrl.replycode;
-    debug(9, 3) ("This is ftpWriteTransferDone\n");
-    if (code != 226) {
-	debug(9, 1) ("ftpReadTransferDone: Got code %d after sending data\n",
-	    code);
-	ftpFailed(ftpState, ERR_FTP_PUT_ERROR);
-	return;
-    }
-    storeTimestampsSet(ftpState->entry);	/* XXX Is this needed? */
-    ftpSendReply(ftpState);
-}
-
-static void
-ftpSendQuit(FtpStateData * ftpState)
-{
-    assert(ftpState->ctrl.fd > -1);
-    snprintf(cbuf, 1024, "QUIT\r\n");
-    ftpWriteCommand(cbuf, ftpState);
-    ftpState->state = SENT_QUIT;
-}
-
-static void
-ftpReadQuit(FtpStateData * ftpState)
-{
-    comm_close(ftpState->ctrl.fd);
-}
-
-static void
-ftpTrySlashHack(FtpStateData * ftpState)
-{
-    char *path;
-    ftpState->flags.try_slash_hack = 1;
-    /* Free old paths */
-    if (ftpState->pathcomps)
-	wordlistDestroy(&ftpState->pathcomps);
-    safe_free(ftpState->filepath);
-    /* Build the new path (urlpath begins with /) */
-    path = xstrdup(strBuf(ftpState->request->urlpath));
-    rfc1738_unescape(path);
-    ftpState->filepath = path;
-    /* And off we go */
-    ftpGetFile(ftpState);
-}
-
-static void
-ftpTryDatachannelHack(FtpStateData * ftpState)
-{
-    ftpState->flags.datachannel_hack = 1;
-    /* we have to undo some of the slash hack... */
-    if (ftpState->old_filepath != NULL) {
-	ftpState->flags.try_slash_hack = 0;
-	safe_free(ftpState->filepath);
-	ftpState->filepath = ftpState->old_filepath;
-	ftpState->old_filepath = NULL;
-    }
-    ftpState->flags.tried_nlst = 0;
-    /* And off we go */
-    if (ftpState->flags.isdir) {
-	ftpListDir(ftpState);
-    } else {
-	ftpGetFile(ftpState);
-    }
-    return;
-}
-
-/* Forget hack status. Next error is shown to the user */
-static void
-ftpUnhack(FtpStateData * ftpState)
-{
-    if (ftpState->old_request != NULL) {
-	safe_free(ftpState->old_request);
-	safe_free(ftpState->old_reply);
-    }
-}
-
-static void
-ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState)
-{
-    /* Clear some unwanted state */
-    ftpState->restarted_offset = 0;
-    ftpState->restart_offset = 0;
-    /* Save old error message & some state info */
-    if (ftpState->old_request == NULL) {
-	ftpState->old_request = ftpState->ctrl.last_command;
-	ftpState->ctrl.last_command = NULL;
-	ftpState->old_reply = ftpState->ctrl.last_reply;
-	ftpState->ctrl.last_reply = NULL;
-	if (ftpState->pathcomps == NULL && ftpState->filepath != NULL)
-	    ftpState->old_filepath = xstrdup(ftpState->filepath);
-    }
-    /* Jump to the "hack" state */
-    nextState(ftpState);
-}
-
-static void
-ftpFail(FtpStateData * ftpState)
-{
-    debug(9, 3) ("ftpFail\n");
-    /* Try the / hack to support "Netscape" FTP URL's for retreiving files */
-    if (!ftpState->flags.isdir &&	/* Not a directory */
-	!ftpState->flags.try_slash_hack &&	/* Not in slash hack */
-	ftpState->mdtm <= 0 && ftpState->size < 0 &&	/* Not known as a file */
-	strNCaseCmp(ftpState->request->urlpath, "/%2f", 4) != 0) {	/* No slash encoded */
-	switch (ftpState->state) {
-	case SENT_CWD:
-	case SENT_RETR:
-	    /* Try the / hack */
-	    ftpHackShortcut(ftpState, ftpTrySlashHack);
-	    return;
-	default:
-	    break;
-	}
-    }
-    /* Try to reopen datachannel */
-    if (!ftpState->flags.datachannel_hack &&
-	ftpState->pathcomps == NULL) {
-	switch (ftpState->state) {
-	case SENT_RETR:
-	case SENT_LIST:
-	case SENT_NLST:
-	    /* Try to reopen datachannel */
-	    ftpHackShortcut(ftpState, ftpTryDatachannelHack);
-	    return;
-	default:
-	    break;
-	}
-    }
-    ftpFailed(ftpState, ERR_NONE);
-    /* ftpFailed closes ctrl.fd and frees ftpState */
-}
-
-static void
-ftpFailed(FtpStateData * ftpState, err_type error)
-{
-    StoreEntry *entry = ftpState->entry;
-    if (entry->mem_obj->inmem_hi == 0)
-	ftpFailedErrorMessage(ftpState, error);
-    if (ftpState->data.fd > -1) {
-	comm_close(ftpState->data.fd);
-	ftpState->data.fd = -1;
-    }
-    comm_close(ftpState->ctrl.fd);
-}
-
-static void
-ftpFailedErrorMessage(FtpStateData * ftpState, err_type error)
-{
-    ErrorState *err;
-    const char *command, *reply;
-    /* Translate FTP errors into HTTP errors */
-    err = NULL;
-    switch (error) {
-    case ERR_NONE:
-	switch (ftpState->state) {
-	case SENT_USER:
-	case SENT_PASS:
-	    if (ftpState->ctrl.replycode > 500)
-		err = errorCon(ERR_FTP_FORBIDDEN, HTTP_FORBIDDEN);
-	    else if (ftpState->ctrl.replycode == 421)
-		err = errorCon(ERR_FTP_UNAVAILABLE, HTTP_SERVICE_UNAVAILABLE);
-	    break;
-	case SENT_CWD:
-	case SENT_RETR:
-	    if (ftpState->ctrl.replycode == 550)
-		err = errorCon(ERR_FTP_NOT_FOUND, HTTP_NOT_FOUND);
-	    break;
-	default:
-	    break;
-	}
-	break;
-    case ERR_READ_TIMEOUT:
-	err = errorCon(error, HTTP_GATEWAY_TIMEOUT);
-	break;
-    default:
-	err = errorCon(error, HTTP_BAD_GATEWAY);
-	break;
-    }
-    if (err == NULL)
-	err = errorCon(ERR_FTP_FAILURE, HTTP_BAD_GATEWAY);
-    err->xerrno = errno;
-    err->request = requestLink(ftpState->request);
-    err->ftp.server_msg = ftpState->ctrl.message;
-    ftpState->ctrl.message = NULL;
-    if (ftpState->old_request)
-	command = ftpState->old_request;
-    else
-	command = ftpState->ctrl.last_command;
-    if (command && strncmp(command, "PASS", 4) == 0)
-	command = "PASS <yourpassword>";
-    if (ftpState->old_reply)
-	reply = ftpState->old_reply;
-    else
-	reply = ftpState->ctrl.last_reply;
-    if (command)
-	err->ftp.request = xstrdup(command);
-    if (reply)
-	err->ftp.reply = xstrdup(reply);
-    fwdFail(ftpState->fwd, err);
-}
-
-static void
-ftpSendReply(FtpStateData * ftpState)
-{
-    ErrorState *err;
-    int code = ftpState->ctrl.replycode;
-    http_status http_code;
-    err_type err_code = ERR_NONE;
-    debug(9, 5) ("ftpSendReply: %s, code %d\n",
-	storeUrl(ftpState->entry), code);
-    if (cbdataReferenceValid(ftpState))
-	debug(9, 5) ("ftpSendReply: ftpState (%p) is valid!\n", ftpState);
-    if (code == 226) {
-	err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
-	http_code = (ftpState->mdtm > 0) ? HTTP_ACCEPTED : HTTP_CREATED;
-    } else if (code == 227) {
-	err_code = ERR_FTP_PUT_CREATED;
-	http_code = HTTP_CREATED;
-    } else {
-	err_code = ERR_FTP_PUT_ERROR;
-	http_code = HTTP_INTERNAL_SERVER_ERROR;
-    }
-    err = errorCon(err_code, http_code);
-    err->request = requestLink(ftpState->request);
-    if (ftpState->old_request)
-	err->ftp.request = xstrdup(ftpState->old_request);
-    else
-	err->ftp.request = xstrdup(ftpState->ctrl.last_command);
-    if (ftpState->old_reply)
-	err->ftp.reply = xstrdup(ftpState->old_reply);
-    else if (ftpState->ctrl.last_reply)
-	err->ftp.reply = xstrdup(ftpState->ctrl.last_reply);
-    else
-	err->ftp.reply = xstrdup("");
-    errorAppendEntry(ftpState->entry, err);
-    storeBufferFlush(ftpState->entry);
-    ftpSendQuit(ftpState);
-}
-
-static void
-ftpAppendSuccessHeader(FtpStateData * ftpState)
-{
-    const char *mime_type = NULL;
-    const char *mime_enc = NULL;
-    String urlpath = ftpState->request->urlpath;
-    const char *filename = NULL;
-    const char *t = NULL;
-    StoreEntry *e = ftpState->entry;
-    http_reply *reply = e->mem_obj->reply;
-    http_version_t version;
-
-    if (ftpState->flags.http_header_sent)
-	return;
-    ftpState->flags.http_header_sent = 1;
-    assert(e->mem_obj->inmem_hi == 0);
-    EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
-    filename = (t = strRChr(urlpath, '/')) ? t + 1 : strBuf(urlpath);
-    if (ftpState->flags.isdir) {
-	mime_type = "text/html";
-    } else {
-	switch (ftpState->typecode) {
-	case 'I':
-	    mime_type = "application/octet-stream";
-	    mime_enc = mimeGetContentEncoding(filename);
-	    break;
-	case 'A':
-	    mime_type = "text/plain";
-	    break;
-	default:
-	    mime_type = mimeGetContentType(filename);
-	    mime_enc = mimeGetContentEncoding(filename);
-	    break;
-	}
-    }
-    storeBuffer(e);
-    httpReplyReset(reply);
-    /* set standard stuff */
-    if (ftpState->restarted_offset) {
-	/* Partial reply */
-	HttpHdrRangeSpec range_spec;
-	range_spec.offset = ftpState->restarted_offset;
-	range_spec.length = ftpState->size - ftpState->restarted_offset;
-	httpBuildVersion(&version, 1, 0);
-	httpReplySetHeaders(reply, version, HTTP_PARTIAL_CONTENT, "Gatewaying",
-	    mime_type, ftpState->size - ftpState->restarted_offset, ftpState->mdtm, -2);
-	httpHeaderAddContRange(&reply->header, range_spec, ftpState->size);
-    } else {
-	/* Full reply */
-	httpBuildVersion(&version, 1, 0);
-	httpReplySetHeaders(reply, version, HTTP_OK, "Gatewaying",
-	    mime_type, ftpState->size, ftpState->mdtm, -2);
-    }
-    /* additional info */
-    if (mime_enc)
-	httpHeaderPutStr(&reply->header, HDR_CONTENT_ENCODING, mime_enc);
-    httpReplySwapOut(reply, e);
-    storeBufferFlush(e);
-    reply->hdr_sz = e->mem_obj->inmem_hi;
-    storeTimestampsSet(e);
-    if (ftpState->flags.authenticated) {
-	/*
-	 * Authenticated requests can't be cached.
-	 */
-	storeRelease(e);
-    } else if (EBIT_TEST(e->flags, ENTRY_CACHABLE) && !ftpState->restarted_offset) {
-	storeSetPublicKey(e);
-    } else {
-	storeRelease(e);
-    }
-}
-
-static void
-ftpAuthRequired(HttpReply * old_reply, request_t * request, const char *realm)
-{
-    ErrorState *err = errorCon(ERR_CACHE_ACCESS_DENIED, HTTP_UNAUTHORIZED);
-    HttpReply *rep;
-    err->request = requestLink(request);
-    rep = errorBuildReply(err);
-    errorStateFree(err);
-    /* add Authenticate header */
-    httpHeaderPutAuth(&rep->header, "Basic", realm);
-    /* move new reply to the old one */
-    httpReplyAbsorb(old_reply, rep);
-}
-
-char *
-ftpUrlWith2f(const request_t * request)
-{
-    LOCAL_ARRAY(char, buf, MAX_URL);
-    LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
-    LOCAL_ARRAY(char, portbuf, 32);
-    char *t;
-    portbuf[0] = '\0';
-    if (request->protocol != PROTO_FTP)
-	return NULL;
-    if (request->port != urlDefaultPort(request->protocol))
-	snprintf(portbuf, 32, ":%d", request->port);
-    loginbuf[0] = '\0';
-    if ((int) strlen(request->login) > 0) {
-	xstrncpy(loginbuf, request->login, sizeof(loginbuf) - 2);
-	if ((t = strchr(loginbuf, ':')))
-	    *t = '\0';
-	strcat(loginbuf, "@");
-    }
-    snprintf(buf, MAX_URL, "%s://%s%s%s%s%s",
-	ProtocolStr[request->protocol],
-	loginbuf,
-	request->host,
-	portbuf,
-	"/%2f",
-	strBuf(request->urlpath));
-    if ((t = strchr(buf, '?')))
-	*t = '\0';
-    return buf;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/ftp.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,2615 @@
+
+/*
+ * $Id: ftp.cc,v 1.1.2.1 2002/10/11 15:40:49 rbcollins Exp $
+ *
+ * DEBUG: section 9     File Transfer Protocol (FTP)
+ * AUTHOR: Harvest Derived
+ *
+ * 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"
+#include "Store.h"
+
+
+static const char *const crlf = "\r\n";
+static char cbuf[1024];
+
+typedef enum {
+    BEGIN,
+    SENT_USER,
+    SENT_PASS,
+    SENT_TYPE,
+    SENT_MDTM,
+    SENT_SIZE,
+    SENT_PORT,
+    SENT_PASV,
+    SENT_CWD,
+    SENT_LIST,
+    SENT_NLST,
+    SENT_REST,
+    SENT_RETR,
+    SENT_STOR,
+    SENT_QUIT,
+    READING_DATA,
+    WRITING_DATA,
+    SENT_MKDIR
+} ftp_state_t;
+
+struct _ftp_flags {
+    unsigned int isdir:1;
+    unsigned int pasv_supported:1;
+    unsigned int skip_whitespace:1;
+    unsigned int rest_supported:1;
+    unsigned int pasv_only:1;
+    unsigned int authenticated:1;
+    unsigned int http_header_sent:1;
+    unsigned int tried_nlst:1;
+    unsigned int need_base_href:1;
+    unsigned int root_dir:1;
+    unsigned int no_dotdot:1;
+    unsigned int html_header_sent:1;
+    unsigned int binary:1;
+    unsigned int try_slash_hack:1;
+    unsigned int put:1;
+    unsigned int put_mkdir:1;
+    unsigned int listformat_unknown:1;
+    unsigned int datachannel_hack:1;
+};
+
+typedef struct _Ftpdata {
+    StoreEntry *entry;
+    request_t *request;
+    char user[MAX_URL];
+    char password[MAX_URL];
+    int password_url;
+    char *reply_hdr;
+    int reply_hdr_state;
+    String title_url;
+    String base_href;
+    int conn_att;
+    int login_att;
+    ftp_state_t state;
+    time_t mdtm;
+    int size;
+    wordlist *pathcomps;
+    char *filepath;
+    int restart_offset;
+    int restarted_offset;
+    int rest_att;
+    char *proxy_host;
+    size_t list_width;
+    wordlist *cwd_message;
+    char *old_request;
+    char *old_reply;
+    char *old_filepath;
+    char typecode;
+    struct {
+	int fd;
+	char *buf;
+	size_t size;
+	off_t offset;
+	wordlist *message;
+	char *last_command;
+	char *last_reply;
+	int replycode;
+    } ctrl;
+    struct {
+	int fd;
+	char *buf;
+	size_t size;
+	off_t offset;
+	char *host;
+	u_short port;
+    } data;
+    struct _ftp_flags flags;
+    FwdState *fwd;
+} FtpStateData;
+
+typedef struct {
+    char type;
+    int size;
+    char *date;
+    char *name;
+    char *showname;
+    char *link;
+} ftpListParts;
+
+typedef void (FTPSM) (FtpStateData *);
+
+#define FTP_LOGIN_ESCAPED 1
+#define FTP_LOGIN_NOT_ESCAPED 0
+
+/* Local functions */
+static CNCB ftpPasvCallback;
+static PF ftpDataRead;
+static PF ftpDataWrite;
+static CWCB ftpDataWriteCallback;
+static PF ftpStateFree;
+static PF ftpTimeout;
+static PF ftpReadControlReply;
+static CWCB ftpWriteCommandCallback;
+static void ftpLoginParser(const char *, FtpStateData *, int escaped);
+static wordlist *ftpParseControlReply(char *, size_t, int *, int *);
+static int ftpRestartable(FtpStateData * ftpState);
+static void ftpAppendSuccessHeader(FtpStateData * ftpState);
+static void ftpAuthRequired(HttpReply * reply, request_t * request, const char *realm);
+static void ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState);
+static void ftpUnhack(FtpStateData * ftpState);
+static void ftpScheduleReadControlReply(FtpStateData *, int);
+static void ftpHandleControlReply(FtpStateData *);
+static char *ftpHtmlifyListEntry(const char *line, FtpStateData * ftpState);
+static void ftpFailed(FtpStateData *, err_type);
+static void ftpFailedErrorMessage(FtpStateData *, err_type);
+
+/*
+ * State machine functions
+ * send == state transition
+ * read == wait for response, and select next state transition
+ * other == Transition logic
+ */
+static FTPSM ftpReadWelcome;
+static FTPSM ftpSendUser;
+static FTPSM ftpReadUser;
+static FTPSM ftpSendPass;
+static FTPSM ftpReadPass;
+static FTPSM ftpSendType;
+static FTPSM ftpReadType;
+static FTPSM ftpSendMdtm;
+static FTPSM ftpReadMdtm;
+static FTPSM ftpSendSize;
+static FTPSM ftpReadSize;
+static FTPSM ftpSendPort;
+static FTPSM ftpReadPort;
+static FTPSM ftpSendPasv;
+static FTPSM ftpReadPasv;
+static FTPSM ftpTraverseDirectory;
+static FTPSM ftpListDir;
+static FTPSM ftpGetFile;
+static FTPSM ftpSendCwd;
+static FTPSM ftpReadCwd;
+static FTPSM ftpRestOrList;
+static FTPSM ftpSendList;
+static FTPSM ftpSendNlst;
+static FTPSM ftpReadList;
+static FTPSM ftpSendRest;
+static FTPSM ftpReadRest;
+static FTPSM ftpSendRetr;
+static FTPSM ftpReadRetr;
+static FTPSM ftpReadTransferDone;
+static FTPSM ftpSendStor;
+static FTPSM ftpReadStor;
+static FTPSM ftpWriteTransferDone;
+static FTPSM ftpSendReply;
+static FTPSM ftpSendMkdir;
+static FTPSM ftpReadMkdir;
+static FTPSM ftpFail;
+static FTPSM ftpSendQuit;
+static FTPSM ftpReadQuit;
+/************************************************
+** State Machine Description (excluding hacks) **
+*************************************************
+From			To
+---------------------------------------
+Welcome			User
+User			Pass
+Pass			Type
+Type			TraverseDirectory / GetFile
+TraverseDirectory	Cwd / GetFile / ListDir
+Cwd			TraverseDirectory / Mkdir
+GetFile			Mdtm
+Mdtm			Size
+Size			Pasv
+ListDir			Pasv
+Pasv			FileOrList
+FileOrList		Rest / Retr / Nlst / List / Mkdir (PUT /xxx;type=d)
+Rest			Retr
+Retr / Nlst / List	DataRead* (on datachannel)
+DataRead*		ReadTransferDone
+ReadTransferDone	DataTransferDone
+Stor			DataWrite* (on datachannel)
+DataWrite*		RequestPutBody** (from client)
+RequestPutBody**	DataWrite* / WriteTransferDone
+WriteTransferDone	DataTransferDone
+DataTransferDone	Quit
+Quit			-
+************************************************/
+
+FTPSM *FTP_SM_FUNCS[] =
+{
+    ftpReadWelcome,		/* BEGIN */
+    ftpReadUser,		/* SENT_USER */
+    ftpReadPass,		/* SENT_PASS */
+    ftpReadType,		/* SENT_TYPE */
+    ftpReadMdtm,		/* SENT_MDTM */
+    ftpReadSize,		/* SENT_SIZE */
+    ftpReadPort,		/* SENT_PORT */
+    ftpReadPasv,		/* SENT_PASV */
+    ftpReadCwd,			/* SENT_CWD */
+    ftpReadList,		/* SENT_LIST */
+    ftpReadList,		/* SENT_NLST */
+    ftpReadRest,		/* SENT_REST */
+    ftpReadRetr,		/* SENT_RETR */
+    ftpReadStor,		/* SENT_STOR */
+    ftpReadQuit,		/* SENT_QUIT */
+    ftpReadTransferDone,	/* READING_DATA (RETR,LIST,NLST) */
+    ftpWriteTransferDone,	/* WRITING_DATA (STOR) */
+    ftpReadMkdir		/* SENT_MKDIR */
+};
+
+static void
+ftpStateFree(int fdnotused, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    if (ftpState == NULL)
+	return;
+    debug(9, 3) ("ftpStateFree: %s\n", storeUrl(ftpState->entry));
+    storeUnregisterAbort(ftpState->entry);
+    storeUnlockObject(ftpState->entry);
+    if (ftpState->reply_hdr) {
+	memFree(ftpState->reply_hdr, MEM_8K_BUF);
+	ftpState->reply_hdr = NULL;
+    }
+    requestUnlink(ftpState->request);
+    if (ftpState->ctrl.buf) {
+	memFreeBuf(ftpState->ctrl.size, ftpState->ctrl.buf);
+	ftpState->ctrl.buf = NULL;
+    }
+    if (ftpState->data.buf) {
+	memFreeBuf(ftpState->data.size, ftpState->data.buf);
+	ftpState->data.buf = NULL;
+    }
+    if (ftpState->pathcomps)
+	wordlistDestroy(&ftpState->pathcomps);
+    if (ftpState->ctrl.message)
+	wordlistDestroy(&ftpState->ctrl.message);
+    if (ftpState->cwd_message)
+	wordlistDestroy(&ftpState->cwd_message);
+    safe_free(ftpState->ctrl.last_reply);
+    safe_free(ftpState->ctrl.last_command);
+    safe_free(ftpState->old_request);
+    safe_free(ftpState->old_reply);
+    safe_free(ftpState->old_filepath);
+    stringClean(&ftpState->title_url);
+    stringClean(&ftpState->base_href);
+    safe_free(ftpState->filepath);
+    safe_free(ftpState->data.host);
+    if (ftpState->data.fd > -1) {
+	comm_close(ftpState->data.fd);
+	ftpState->data.fd = -1;
+    }
+    cbdataFree(ftpState);
+}
+
+static void
+ftpLoginParser(const char *login, FtpStateData * ftpState, int escaped)
+{
+    char *s = NULL;
+    xstrncpy(ftpState->user, login, MAX_URL);
+    if ((s = strchr(ftpState->user, ':'))) {
+	*s = 0;
+	xstrncpy(ftpState->password, s + 1, MAX_URL);
+	if (escaped)
+	    rfc1738_unescape(ftpState->password);
+	ftpState->password_url = 1;
+    } else {
+	xstrncpy(ftpState->password, null_string, MAX_URL);
+    }
+    if (escaped)
+	rfc1738_unescape(ftpState->user);
+    if (ftpState->user[0] || ftpState->password[0])
+	return;
+    xstrncpy(ftpState->user, "anonymous", MAX_URL);
+    xstrncpy(ftpState->password, Config.Ftp.anon_user, MAX_URL);
+}
+
+static void
+ftpTimeout(int fd, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    StoreEntry *entry = ftpState->entry;
+    debug(9, 4) ("ftpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
+    if (SENT_PASV == ftpState->state && fd == ftpState->data.fd) {
+	/* stupid ftp.netscape.com */
+	ftpState->fwd->flags.dont_retry = 0;
+	ftpState->fwd->flags.ftp_pasv_failed = 1;
+	debug(9, 1) ("ftpTimeout: timeout in SENT_PASV state\n");
+    }
+    ftpFailed(ftpState, ERR_READ_TIMEOUT);
+    /* ftpFailed closes ctrl.fd and frees ftpState */
+}
+
+static void
+ftpListingStart(FtpStateData * ftpState)
+{
+    StoreEntry *e = ftpState->entry;
+    wordlist *w;
+    char *dirup;
+    int i, j, k;
+    char *title;
+    storeBuffer(e);
+    storeAppendPrintf(e, "<!-- HTML listing generated by Squid %s -->\n",
+	version_string);
+    storeAppendPrintf(e, "<!-- %s -->\n", mkrfc1123(squid_curtime));
+    storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
+    storeAppendPrintf(e, "<HTML><HEAD><TITLE>\n");
+    storeAppendPrintf(e, "FTP Directory: %s\n",
+	html_quote(strBuf(ftpState->title_url)));
+    storeAppendPrintf(e, "</TITLE>\n");
+    storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
+    if (ftpState->flags.need_base_href)
+	storeAppendPrintf(e, "<BASE HREF=\"%s\">\n",
+	    html_quote(strBuf(ftpState->base_href)));
+    storeAppendPrintf(e, "</HEAD><BODY>\n");
+    if (ftpState->cwd_message) {
+	storeAppendPrintf(e, "<PRE>\n");
+	for (w = ftpState->cwd_message; w; w = w->next)
+	    storeAppendPrintf(e, "%s\n", html_quote(w->key));
+	storeAppendPrintf(e, "</PRE>\n");
+	storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
+	wordlistDestroy(&ftpState->cwd_message);
+    }
+    storeAppendPrintf(e, "<H2>\n");
+    storeAppendPrintf(e, "FTP Directory: ");
+    /* "ftp://" == 6 characters */
+    assert(strLen(ftpState->title_url) >= 6);
+    title = html_quote(strBuf(ftpState->title_url));
+    for (i = 6, j = 0; title[i]; j = i) {
+	storeAppendPrintf(e, "<A HREF=\"");
+	i += strcspn(&title[i], "/");
+	if (title[i] == '/')
+	    i++;
+	for (k = 0; k < i; k++)
+	    storeAppendPrintf(e, "%c", title[k]);
+	storeAppendPrintf(e, "\">");
+	for (k = j; k < i - 1; k++)
+	    storeAppendPrintf(e, "%c", title[k]);
+	if (strBuf(ftpState->title_url)[k] != '/')
+	    storeAppendPrintf(e, "%c", title[k++]);
+	storeAppendPrintf(e, "</A>");
+	if (k < i)
+	    storeAppendPrintf(e, "%c", title[k++]);
+	if (i == j) {
+	    /* Error guard, or "assert" */
+	    storeAppendPrintf(e, "ERROR: Failed to parse URL: %s\n",
+		html_quote(strBuf(ftpState->title_url)));
+	    debug(9, 0) ("Failed to parse URL: %s\n", strBuf(ftpState->title_url));
+	    break;
+	}
+    }
+    storeAppendPrintf(e, "</H2>\n");
+    storeAppendPrintf(e, "<PRE>\n");
+    dirup = ftpHtmlifyListEntry("<internal-dirup>", ftpState);
+    storeAppend(e, dirup, strlen(dirup));
+    storeBufferFlush(e);
+    ftpState->flags.html_header_sent = 1;
+}
+
+static void
+ftpListingFinish(FtpStateData * ftpState)
+{
+    StoreEntry *e = ftpState->entry;
+    storeBuffer(e);
+    storeAppendPrintf(e, "</PRE>\n");
+    if (ftpState->flags.listformat_unknown && !ftpState->flags.tried_nlst) {
+	storeAppendPrintf(e, "<A HREF=\"./;type=d\">[As plain directory]</A>\n");
+    } else if (ftpState->typecode == 'D') {
+	storeAppendPrintf(e, "<A HREF=\"./\">[As extended directory]</A>\n");
+    }
+    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
+    storeAppendPrintf(e, "<ADDRESS>\n");
+    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
+	mkrfc1123(squid_curtime),
+	getMyHostname(),
+	full_appname_string);
+    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
+    storeBufferFlush(e);
+}
+
+static const char *Month[] =
+{
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static int
+is_month(const char *buf)
+{
+    int i;
+    for (i = 0; i < 12; i++)
+	if (!strcasecmp(buf, Month[i]))
+	    return 1;
+    return 0;
+}
+
+
+static void
+ftpListPartsFree(ftpListParts ** parts)
+{
+    safe_free((*parts)->date);
+    safe_free((*parts)->name);
+    safe_free((*parts)->showname);
+    safe_free((*parts)->link);
+    safe_free(*parts);
+}
+
+#define MAX_TOKENS 64
+
+static ftpListParts *
+ftpListParseParts(const char *buf, struct _ftp_flags flags)
+{
+    ftpListParts *p = NULL;
+    char *t = NULL;
+    const char *ct = NULL;
+    char *tokens[MAX_TOKENS];
+    int i;
+    int n_tokens;
+    static char tbuf[128];
+    char *xbuf = NULL;
+    static int scan_ftp_initialized = 0;
+    static regex_t scan_ftp_integer;
+    static regex_t scan_ftp_time;
+    static regex_t scan_ftp_dostime;
+    static regex_t scan_ftp_dosdate;
+
+    if (!scan_ftp_initialized) {
+	scan_ftp_initialized = 1;
+	regcomp(&scan_ftp_integer, "^[0123456789]+$", REG_EXTENDED | REG_NOSUB);
+	regcomp(&scan_ftp_time, "^[0123456789:]+$", REG_EXTENDED | REG_NOSUB);
+	regcomp(&scan_ftp_dosdate, "^[0123456789]+-[0123456789]+-[0123456789]+$", REG_EXTENDED | REG_NOSUB);
+	regcomp(&scan_ftp_dostime, "^[0123456789]+:[0123456789]+[AP]M$", REG_EXTENDED | REG_NOSUB | REG_ICASE);
+    }
+    if (buf == NULL)
+	return NULL;
+    if (*buf == '\0')
+	return NULL;
+    p = (ftpListParts *)xcalloc(1, sizeof(ftpListParts));
+    n_tokens = 0;
+    memset(tokens, 0, sizeof(tokens));
+    xbuf = xstrdup(buf);
+    if (flags.tried_nlst) {
+	/* Machine readable format, one name per line */
+	p->name = xbuf;
+	p->type = '\0';
+	return p;
+    }
+    for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space))
+	tokens[n_tokens++] = xstrdup(t);
+    xfree(xbuf);
+    /* locate the Month field */
+    for (i = 3; i < n_tokens - 2; i++) {
+	char *size = tokens[i - 1];
+	char *month = tokens[i];
+	char *day = tokens[i + 1];
+	char *year = tokens[i + 2];
+	if (!is_month(month))
+	    continue;
+	if (regexec(&scan_ftp_integer, size, 0, NULL, 0) != 0)
+	    continue;
+	if (regexec(&scan_ftp_integer, day, 0, NULL, 0) != 0)
+	    continue;
+	if (regexec(&scan_ftp_time, day, 0, NULL, 0) != 0)	/* Yr | hh:mm */
+	    continue;
+	snprintf(tbuf, 128, "%s %2s %5s",
+	    month, day, year);
+	if (!strstr(buf, tbuf))
+	    snprintf(tbuf, 128, "%s %2s %-5s",
+		month, day, year);
+	if ((t = strstr(buf, tbuf))) {
+	    p->type = *tokens[0];
+	    p->size = atoi(size);
+	    p->date = xstrdup(tbuf);
+	    if (flags.skip_whitespace) {
+		t += strlen(tbuf);
+		while (strchr(w_space, *t))
+		    t++;
+	    } else {
+		/* XXX assumes a single space between date and filename
+		 * suggested by:  Nathan.Bailey@cc.monash.edu.au and
+		 * Mike Battersby <mike@starbug.bofh.asn.au> */
+		t += strlen(tbuf) + 1;
+	    }
+	    p->name = xstrdup(t);
+	    if ((t = strstr(p->name, " -> "))) {
+		*t = '\0';
+		p->link = xstrdup(t + 4);
+	    }
+	    goto found;
+	}
+	break;
+    }
+    /* try it as a DOS listing, 04-05-70 09:33PM ... */
+    if (n_tokens > 3 &&
+	regexec(&scan_ftp_dosdate, tokens[0], 0, NULL, 0) == 0 &&
+	regexec(&scan_ftp_dostime, tokens[1], 0, NULL, 0) == 0) {
+	if (!strcasecmp(tokens[2], "<dir>")) {
+	    p->type = 'd';
+	} else {
+	    p->type = '-';
+	    p->size = atoi(tokens[2]);
+	}
+	snprintf(tbuf, 128, "%s %s", tokens[0], tokens[1]);
+	p->date = xstrdup(tbuf);
+	if (p->type == 'd') {
+	    /* Directory.. name begins with first printable after <dir> */
+	    ct = strstr(buf, tokens[2]);
+	    ct += strlen(tokens[2]);
+	    while (xisspace(*ct))
+		ct++;
+	    if (!*ct)
+		ct = NULL;
+	} else {
+	    /* A file. Name begins after size, with a space in between */
+	    snprintf(tbuf, 128, " %s %s", tokens[2], tokens[3]);
+	    ct = strstr(buf, tbuf);
+	    if (ct) {
+		ct += strlen(tokens[2]) + 2;
+	    }
+	}
+	p->name = xstrdup(ct ? ct : tokens[3]);
+	goto found;
+    }
+    /* Try EPLF format; carson@lehman.com */
+    if (buf[0] == '+') {
+	ct = buf + 1;
+	p->type = 0;
+	while (ct && *ct) {
+	    time_t t;
+	    int l = strcspn(ct + 1, ",");
+	    char *tmp;
+	    if (l < 1)
+		goto blank;
+	    switch (*ct) {
+	    case '\t':
+		p->name = xstrndup(ct + 1, l + 1);
+		break;
+	    case 's':
+		p->size = atoi(ct + 1);
+		break;
+	    case 'm':
+		t = (time_t) strtol(ct + 1, &tmp, 0);
+		if (*tmp || (tmp == ct + 1))
+		    break;	/* not a valid integer */
+		p->date = xstrdup(ctime(&t));
+		*(strstr(p->date, "\n")) = '\0';
+		break;
+	    case '/':
+		p->type = 'd';
+		break;
+	    case 'r':
+		p->type = '-';
+		break;
+	    case 'i':
+		break;
+	    default:
+		break;
+	    }
+	  blank:
+	    ct = strstr(ct, ",");
+	    if (ct) {
+		ct++;
+	    }
+	}
+	if (p->type == 0) {
+	    p->type = '-';
+	}
+	if (p->name)
+	    goto found;
+	else
+	    safe_free(p->date);
+    }
+  found:
+    for (i = 0; i < n_tokens; i++)
+	xfree(tokens[i]);
+    if (!p->name)
+	ftpListPartsFree(&p);	/* cleanup */
+    return p;
+}
+
+static const char *
+dots_fill(size_t len)
+{
+    static char buf[256];
+    size_t i = 0;
+    if (len > Config.Ftp.list_width) {
+	memset(buf, ' ', 256);
+	buf[0] = '\n';
+	buf[Config.Ftp.list_width + 4] = '\0';
+	return buf;
+    }
+    for (i = len; i < Config.Ftp.list_width; i++)
+	buf[i - len] = (i % 2) ? '.' : ' ';
+    buf[i - len] = '\0';
+    return buf;
+}
+
+static char *
+ftpHtmlifyListEntry(const char *line, FtpStateData * ftpState)
+{
+    LOCAL_ARRAY(char, icon, 2048);
+    LOCAL_ARRAY(char, href, 2048 + 40);
+    LOCAL_ARRAY(char, text, 2048);
+    LOCAL_ARRAY(char, size, 2048);
+    LOCAL_ARRAY(char, chdir, 2048 + 40);
+    LOCAL_ARRAY(char, view, 2048 + 40);
+    LOCAL_ARRAY(char, download, 2048 + 40);
+    LOCAL_ARRAY(char, link, 2048 + 40);
+    LOCAL_ARRAY(char, html, 8192);
+    size_t width = Config.Ftp.list_width;
+    ftpListParts *parts;
+    *icon = *href = *text = *size = *chdir = *view = *download = *link = *html = '\0';
+    if ((int) strlen(line) > 1024) {
+	snprintf(html, 8192, "%s\n", line);
+	return html;
+    }
+    /* Handle builtin <dirup> */
+    if (strcmp(line, "<internal-dirup>") == 0) {
+	/* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> {link} */
+	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+	    mimeGetIconURL("internal-dirup"),
+	    "[DIRUP]");
+	if (!ftpState->flags.no_dotdot && !ftpState->flags.root_dir) {
+	    /* Normal directory */
+	    strcpy(href, "../");
+	    strcpy(text, "Parent Directory");
+	} else if (!ftpState->flags.no_dotdot && ftpState->flags.root_dir) {
+	    /* "Top level" directory */
+	    strcpy(href, "%2e%2e/");
+	    strcpy(text, "Parent Directory");
+	    snprintf(link, 2048, "(<A HREF=\"%s\">%s</A>)",
+		"%2f/",
+		"Root Directory");
+	} else if (ftpState->flags.no_dotdot && !ftpState->flags.root_dir) {
+	    /* Normal directory where last component is / or ..  */
+	    strcpy(href, "%2e%2e/");
+	    strcpy(text, "Parent Directory");
+	    snprintf(link, 2048, "(<A HREF=\"%s\">%s</A>)",
+		"../",
+		"Back");
+	} else {		/* NO_DOTDOT && ROOT_DIR */
+	    /* "UNIX Root" directory */
+	    strcpy(href, "../");
+	    strcpy(text, "Home Directory");
+	}
+	snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A> %s\n",
+	    href, icon, href, text, link);
+	return html;
+    }
+    if ((parts = ftpListParseParts(line, ftpState->flags)) == NULL) {
+	const char *p;
+	snprintf(html, 8192, "%s\n", line);
+	for (p = line; *p && xisspace(*p); p++);
+	if (*p && !xisspace(*p))
+	    ftpState->flags.listformat_unknown = 1;
+	return html;
+    }
+    if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
+	*html = '\0';
+	ftpListPartsFree(&parts);
+	return html;
+    }
+    parts->size += 1023;
+    parts->size >>= 10;
+    parts->showname = xstrdup(parts->name);
+    if (!Config.Ftp.list_wrap) {
+	if (strlen(parts->showname) > width - 1) {
+	    *(parts->showname + width - 1) = '>';
+	    *(parts->showname + width - 0) = '\0';
+	}
+    }
+    /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n  */
+    xstrncpy(href, rfc1738_escape_part(parts->name), 2048);
+    xstrncpy(text, parts->showname, 2048);
+    switch (parts->type) {
+    case 'd':
+	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+	    mimeGetIconURL("internal-dir"),
+	    "[DIR]");
+	strcat(href, "/");	/* margin is allocated above */
+	break;
+    case 'l':
+	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+	    mimeGetIconURL("internal-link"),
+	    "[LINK]");
+	/* sometimes there is an 'l' flag, but no "->" link */
+	if (parts->link) {
+	    char *link2 = xstrdup(html_quote(rfc1738_escape(parts->link)));
+	    snprintf(link, 2048, " -> <A HREF=\"%s\">%s</A>",
+		link2,
+		html_quote(parts->link));
+	    safe_free(link2);
+	}
+	break;
+    case '\0':
+	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+	    mimeGetIconURL(parts->name),
+	    "[UNKNOWN]");
+	snprintf(chdir, 2048, " <A HREF=\"%s/;type=d\"><IMG border=\"0\" SRC=\"%s\" "
+	    "ALT=\"[DIR]\"></A>",
+	    rfc1738_escape_part(parts->name),
+	    mimeGetIconURL("internal-dir"));
+	break;
+    case '-':
+    default:
+	snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+	    mimeGetIconURL(parts->name),
+	    "[FILE]");
+	snprintf(size, 2048, " %6dk", parts->size);
+	break;
+    }
+    if (parts->type != 'd') {
+	if (mimeGetViewOption(parts->name)) {
+	    snprintf(view, 2048, " <A HREF=\"%s;type=a\"><IMG border=\"0\" SRC=\"%s\" "
+		"ALT=\"[VIEW]\"></A>",
+		href, mimeGetIconURL("internal-view"));
+	}
+	if (mimeGetDownloadOption(parts->name)) {
+	    snprintf(download, 2048, " <A HREF=\"%s;type=i\"><IMG border=\"0\" SRC=\"%s\" "
+		"ALT=\"[DOWNLOAD]\"></A>",
+		href, mimeGetIconURL("internal-download"));
+	}
+    }
+    /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> . . . {date}{size}{chdir}{view}{download}{link}\n  */
+    if (parts->type != '\0') {
+	snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A>%s "
+	    "%s%8s%s%s%s%s\n",
+	    href, icon, href, html_quote(text), dots_fill(strlen(text)),
+	    parts->date, size, chdir, view, download, link);
+    } else {
+	/* Plain listing. {icon} {text} ... {chdir}{view}{download} */
+	snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A>%s "
+	    "%s%s%s%s\n",
+	    href, icon, href, html_quote(text), dots_fill(strlen(text)),
+	    chdir, view, download, link);
+    }
+    ftpListPartsFree(&parts);
+    return html;
+}
+
+static void
+ftpParseListing(FtpStateData * ftpState)
+{
+    char *buf = ftpState->data.buf;
+    char *sbuf;			/* NULL-terminated copy of buf */
+    char *end;
+    char *line;
+    char *s;
+    char *t;
+    size_t linelen;
+    size_t usable;
+    StoreEntry *e = ftpState->entry;
+    size_t len = ftpState->data.offset;
+    /*
+     * We need a NULL-terminated buffer for scanning, ick
+     */
+    sbuf = (char *)xmalloc(len + 1);
+    xstrncpy(sbuf, buf, len + 1);
+    end = sbuf + len - 1;
+    while (*end != '\r' && *end != '\n' && end > sbuf)
+	end--;
+    usable = end - sbuf;
+    debug(9, 3) ("ftpParseListing: usable = %d\n", (int) usable);
+    if (usable == 0) {
+	debug(9, 3) ("ftpParseListing: didn't find end for %s\n", storeUrl(e));
+	xfree(sbuf);
+	return;
+    }
+    debug(9, 3) ("ftpParseListing: %d bytes to play with\n", len);
+    line = (char *)memAllocate(MEM_4K_BUF);
+    end++;
+    storeBuffer(e);
+    s = sbuf;
+    s += strspn(s, crlf);
+    for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
+	debug(9, 3) ("ftpParseListing: s = {%s}\n", s);
+	linelen = strcspn(s, crlf) + 1;
+	if (linelen < 2)
+	    break;
+	if (linelen > 4096)
+	    linelen = 4096;
+	xstrncpy(line, s, linelen);
+	debug(9, 7) ("ftpParseListing: {%s}\n", line);
+	if (!strncmp(line, "total", 5))
+	    continue;
+	t = ftpHtmlifyListEntry(line, ftpState);
+	assert(t != NULL);
+	storeAppend(e, t, strlen(t));
+    }
+    storeBufferFlush(e);
+    assert(usable <= len);
+    if (usable < len) {
+	/* must copy partial line to beginning of buf */
+	linelen = len - usable;
+	if (linelen > 4096)
+	    linelen = 4096;
+	xstrncpy(line, end, linelen);
+	xstrncpy(ftpState->data.buf, line, ftpState->data.size);
+	ftpState->data.offset = strlen(ftpState->data.buf);
+    }
+    memFree(line, MEM_4K_BUF);
+    xfree(sbuf);
+}
+
+static void
+ftpDataComplete(FtpStateData * ftpState)
+{
+    debug(9, 3) ("ftpDataComplete\n");
+    /* Connection closed; transfer done. */
+    if (ftpState->data.fd > -1) {
+	/*
+	 * close data socket so it does not occupy resources while
+	 * we wait
+	 */
+	comm_close(ftpState->data.fd);
+	ftpState->data.fd = -1;
+    }
+    /* expect the "transfer complete" message on the control socket */
+    ftpScheduleReadControlReply(ftpState, 1);
+}
+
+static void
+ftpDataRead(int fd, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    int len;
+    int j;
+    int bin;
+    StoreEntry *entry = ftpState->entry;
+    size_t read_sz;
+#if DELAY_POOLS
+    MemObject *mem = entry->mem_obj;
+    delay_id delay_id = delayMostBytesAllowed(mem);
+#endif
+    assert(fd == ftpState->data.fd);
+    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+	comm_close(ftpState->ctrl.fd);
+	return;
+    }
+    errno = 0;
+    read_sz = ftpState->data.size - ftpState->data.offset;
+#if DELAY_POOLS
+    read_sz = delayBytesWanted(delay_id, 1, read_sz);
+#endif
+    memset(ftpState->data.buf + ftpState->data.offset, '\0', read_sz);
+    statCounter.syscalls.sock.reads++;
+    len = FD_READ_METHOD(fd, ftpState->data.buf + ftpState->data.offset, read_sz);
+    if (len > 0) {
+	fd_bytes(fd, len, FD_READ);
+#if DELAY_POOLS
+	delayBytesIn(delay_id, len);
+#endif
+	kb_incr(&statCounter.server.all.kbytes_in, len);
+	kb_incr(&statCounter.server.ftp.kbytes_in, len);
+	ftpState->data.offset += len;
+    }
+    debug(9, 5) ("ftpDataRead: FD %d, Read %d bytes\n", fd, len);
+    if (len > 0) {
+	IOStats.Ftp.reads++;
+	for (j = len - 1, bin = 0; j; bin++)
+	    j >>= 1;
+	IOStats.Ftp.read_hist[bin]++;
+    }
+    if (ftpState->flags.isdir && !ftpState->flags.html_header_sent && len >= 0) {
+	ftpListingStart(ftpState);
+    }
+    if (len < 0) {
+	debug(50, ignoreErrno(errno) ? 3 : 1) ("ftpDataRead: read error: %s\n", xstrerror());
+	if (ignoreErrno(errno)) {
+	    commSetSelect(fd,
+		COMM_SELECT_READ,
+		ftpDataRead,
+		ftpState,
+		Config.Timeout.read);
+	} else {
+	    ftpFailed(ftpState, ERR_READ_ERROR);
+	    /* ftpFailed closes ctrl.fd and frees ftpState */
+	    return;
+	}
+    } else if (len == 0) {
+	ftpDataComplete(ftpState);
+    } else {
+	if (ftpState->flags.isdir) {
+	    ftpParseListing(ftpState);
+	} else {
+	    storeAppend(entry, ftpState->data.buf, len);
+	    ftpState->data.offset = 0;
+	}
+	commSetSelect(fd,
+	    COMM_SELECT_READ,
+	    ftpDataRead,
+	    ftpState,
+	    Config.Timeout.read);
+    }
+}
+
+/*
+ * ftpCheckAuth
+ *
+ * Return 1 if we have everything needed to complete this request.
+ * Return 0 if something is missing.
+ */
+static int
+ftpCheckAuth(FtpStateData * ftpState, const HttpHeader * req_hdr)
+{
+    char *orig_user;
+    const char *auth;
+    ftpLoginParser(ftpState->request->login, ftpState, FTP_LOGIN_ESCAPED);
+    if (!ftpState->user[0])
+	return 1;		/* no name */
+    if (ftpState->password_url || ftpState->password[0])
+	return 1;		/* passwd provided in URL */
+    /* URL has name, but no passwd */
+    if (!(auth = httpHeaderGetAuth(req_hdr, HDR_AUTHORIZATION, "Basic")))
+	return 0;		/* need auth header */
+    ftpState->flags.authenticated = 1;
+    orig_user = xstrdup(ftpState->user);
+    ftpLoginParser(auth, ftpState, FTP_LOGIN_NOT_ESCAPED);
+    if (strcmp(orig_user, ftpState->user) == 0) {
+	xfree(orig_user);
+	return 1;		/* same username */
+    }
+    xstrncpy(ftpState->user, orig_user, sizeof(ftpState->user));
+    xfree(orig_user);
+    return 0;			/* different username */
+}
+
+static void
+ftpCheckUrlpath(FtpStateData * ftpState)
+{
+    request_t *request = ftpState->request;
+    int l;
+    const char *t;
+    if ((t = strRChr(request->urlpath, ';')) != NULL) {
+	if (strncasecmp(t + 1, "type=", 5) == 0) {
+	    ftpState->typecode = (char) toupper((int) *(t + 6));
+	    strCutPtr(request->urlpath, t);
+	}
+    }
+    l = strLen(request->urlpath);
+    ftpState->flags.need_base_href = 1;
+    /* check for null path */
+    if (!l) {
+	ftpState->flags.isdir = 1;
+	ftpState->flags.root_dir = 1;
+    } else if (!strCmp(request->urlpath, "/%2f/")) {
+	/* UNIX root directory */
+	ftpState->flags.need_base_href = 0;
+	ftpState->flags.isdir = 1;
+	ftpState->flags.root_dir = 1;
+    } else if ((l >= 1) && (*(strBuf(request->urlpath) + l - 1) == '/')) {
+	/* Directory URL, ending in / */
+	ftpState->flags.isdir = 1;
+	ftpState->flags.need_base_href = 0;
+	if (l == 1)
+	    ftpState->flags.root_dir = 1;
+    }
+}
+
+static void
+ftpBuildTitleUrl(FtpStateData * ftpState)
+{
+    request_t *request = ftpState->request;
+
+    stringReset(&ftpState->title_url, "ftp://");
+    if (strcmp(ftpState->user, "anonymous")) {
+	strCat(ftpState->title_url, ftpState->user);
+	strCat(ftpState->title_url, "@");
+    }
+    strCat(ftpState->title_url, request->host);
+    if (request->port != urlDefaultPort(PROTO_FTP)) {
+	strCat(ftpState->title_url, ":");
+	strCat(ftpState->title_url, xitoa(request->port));
+    }
+    strCat(ftpState->title_url, strBuf(request->urlpath));
+
+    stringReset(&ftpState->base_href, "ftp://");
+    if (strcmp(ftpState->user, "anonymous") != 0) {
+	strCat(ftpState->base_href, rfc1738_escape_part(ftpState->user));
+	if (ftpState->password_url) {
+	    strCat(ftpState->base_href, ":");
+	    strCat(ftpState->base_href, rfc1738_escape_part(ftpState->password));
+	}
+	strCat(ftpState->base_href, "@");
+    }
+    strCat(ftpState->base_href, request->host);
+    if (request->port != urlDefaultPort(PROTO_FTP)) {
+	strCat(ftpState->base_href, ":");
+	strCat(ftpState->base_href, xitoa(request->port));
+    }
+    strCat(ftpState->base_href, strBuf(request->urlpath));
+    strCat(ftpState->base_href, "/");
+}
+
+CBDATA_TYPE(FtpStateData);
+void
+ftpStart(FwdState * fwd)
+{
+    request_t *request = fwd->request;
+    StoreEntry *entry = fwd->entry;
+    int fd = fwd->server_fd;
+    LOCAL_ARRAY(char, realm, 8192);
+    const char *url = storeUrl(entry);
+    FtpStateData *ftpState;
+    HttpReply *reply;
+
+    CBDATA_INIT_TYPE(FtpStateData);
+    ftpState = cbdataAlloc(FtpStateData);
+    debug(9, 3) ("ftpStart: '%s'\n", url);
+    statCounter.server.all.requests++;
+    statCounter.server.ftp.requests++;
+    storeLockObject(entry);
+    ftpState->entry = entry;
+    ftpState->request = requestLink(request);
+    ftpState->ctrl.fd = fd;
+    ftpState->data.fd = -1;
+    ftpState->size = -1;
+    ftpState->mdtm = -1;
+    if (!Config.Ftp.passive)
+	ftpState->flags.rest_supported = 0;
+    else if (fwd->flags.ftp_pasv_failed)
+	ftpState->flags.pasv_supported = 0;
+    else
+	ftpState->flags.pasv_supported = 1;
+    ftpState->flags.rest_supported = 1;
+    ftpState->fwd = fwd;
+    comm_add_close_handler(fd, ftpStateFree, ftpState);
+    if (ftpState->request->method == METHOD_PUT)
+	ftpState->flags.put = 1;
+    if (!ftpCheckAuth(ftpState, &request->header)) {
+	/* This request is not fully authenticated */
+	if (request->port == 21) {
+	    snprintf(realm, 8192, "ftp %s", ftpState->user);
+	} else {
+	    snprintf(realm, 8192, "ftp %s port %d",
+		ftpState->user, request->port);
+	}
+	/* create reply */
+	reply = entry->mem_obj->reply;
+	assert(reply != NULL);
+	/* create appropriate reply */
+	ftpAuthRequired(reply, request, realm);
+	httpReplySwapOut(reply, entry);
+	fwdComplete(ftpState->fwd);
+	comm_close(fd);
+	return;
+    }
+    ftpCheckUrlpath(ftpState);
+    ftpBuildTitleUrl(ftpState);
+    debug(9, 5) ("ftpStart: host=%s, path=%s, user=%s, passwd=%s\n",
+	ftpState->request->host, strBuf(ftpState->request->urlpath),
+	ftpState->user, ftpState->password);
+    ftpState->state = BEGIN;
+    ftpState->ctrl.last_command = xstrdup("Connect to server");
+    ftpState->ctrl.buf = (char *)memAllocBuf(4096, &ftpState->ctrl.size);
+    ftpState->ctrl.offset = 0;
+    ftpState->data.buf = (char *)memAllocBuf(SQUID_TCP_SO_RCVBUF, &ftpState->data.size);
+    ftpScheduleReadControlReply(ftpState, 0);
+}
+
+/* ====================================================================== */
+
+static void
+ftpWriteCommand(const char *buf, FtpStateData * ftpState)
+{
+    debug(9, 5) ("ftpWriteCommand: %s\n", buf);
+    safe_free(ftpState->ctrl.last_command);
+    safe_free(ftpState->ctrl.last_reply);
+    ftpState->ctrl.last_command = xstrdup(buf);
+    comm_write(ftpState->ctrl.fd,
+	xstrdup(buf),
+	strlen(buf),
+	ftpWriteCommandCallback,
+	ftpState,
+	xfree);
+    ftpScheduleReadControlReply(ftpState, 0);
+}
+
+static void
+ftpWriteCommandCallback(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    debug(9, 7) ("ftpWriteCommandCallback: wrote %d bytes\n", (int) size);
+    if (size > 0) {
+	fd_bytes(fd, size, FD_WRITE);
+	kb_incr(&statCounter.server.all.kbytes_out, size);
+	kb_incr(&statCounter.server.ftp.kbytes_out, size);
+    }
+    if (errflag == COMM_ERR_CLOSING)
+	return;
+    if (errflag) {
+	debug(9, 1) ("ftpWriteCommandCallback: FD %d: %s\n", fd, xstrerror());
+	ftpFailed(ftpState, ERR_WRITE_ERROR);
+	/* ftpFailed closes ctrl.fd and frees ftpState */
+	return;
+    }
+}
+
+static wordlist *
+ftpParseControlReply(char *buf, size_t len, int *codep, int *used)
+{
+    char *s;
+    char *sbuf;
+    char *end;
+    int usable;
+    int complete = 0;
+    wordlist *head = NULL;
+    wordlist *list;
+    wordlist **tail = &head;
+    off_t offset;
+    size_t linelen;
+    int code = -1;
+    debug(9, 5) ("ftpParseControlReply\n");
+    /*
+     * We need a NULL-terminated buffer for scanning, ick
+     */
+    sbuf = (char *)xmalloc(len + 1);
+    xstrncpy(sbuf, buf, len + 1);
+    end = sbuf + len - 1;
+    while (*end != '\r' && *end != '\n' && end > sbuf)
+	end--;
+    usable = end - sbuf;
+    debug(9, 3) ("ftpParseControlReply: usable = %d\n", usable);
+    if (usable == 0) {
+	debug(9, 3) ("ftpParseControlReply: didn't find end of line\n");
+	safe_free(sbuf);
+	return NULL;
+    }
+    debug(9, 3) ("ftpParseControlReply: %d bytes to play with\n", (int) len);
+    end++;
+    s = sbuf;
+    s += strspn(s, crlf);
+    for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
+	if (complete)
+	    break;
+	debug(9, 3) ("ftpParseControlReply: s = {%s}\n", s);
+	linelen = strcspn(s, crlf) + 1;
+	if (linelen < 2)
+	    break;
+	if (linelen > 3)
+	    complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
+	if (complete)
+	    code = atoi(s);
+	offset = 0;
+	if (linelen > 3)
+	    if (*s >= '0' && *s <= '9' && (*(s + 3) == '-' || *(s + 3) == ' '))
+		offset = 4;
+	list = (wordlist *)memAllocate(MEM_WORDLIST);
+	list->key = (char *)xmalloc(linelen - offset);
+	xstrncpy(list->key, s + offset, linelen - offset);
+	debug(9, 7) ("%d %s\n", code, list->key);
+	*tail = list;
+	tail = &list->next;
+    }
+    *used = (int) (s - sbuf);
+    safe_free(sbuf);
+    if (!complete)
+	wordlistDestroy(&head);
+    if (codep)
+	*codep = code;
+    return head;
+}
+
+static void
+ftpScheduleReadControlReply(FtpStateData * ftpState, int buffered_ok)
+{
+    debug(9, 3) ("ftpScheduleReadControlReply: FD %d\n", ftpState->ctrl.fd);
+    if (buffered_ok && ftpState->ctrl.offset > 0) {
+	/* We've already read some reply data */
+	ftpHandleControlReply(ftpState);
+    } else {
+	commSetSelect(ftpState->ctrl.fd,
+	    COMM_SELECT_READ,
+	    ftpReadControlReply,
+	    ftpState,
+	    Config.Timeout.read);
+	/*
+	 * Cancel the timeout on the Data socket (if any) and
+	 * establish one on the control socket.
+	 */
+	if (ftpState->data.fd > -1)
+	    commSetTimeout(ftpState->data.fd, -1, NULL, NULL);
+	commSetTimeout(ftpState->ctrl.fd, Config.Timeout.read, ftpTimeout,
+	    ftpState);
+    }
+}
+
+static void
+ftpReadControlReply(int fd, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    StoreEntry *entry = ftpState->entry;
+    size_t len;
+    debug(9, 5) ("ftpReadControlReply\n");
+    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+	comm_close(ftpState->ctrl.fd);
+	return;
+    }
+    assert(ftpState->ctrl.offset < (off_t)ftpState->ctrl.size);
+    statCounter.syscalls.sock.reads++;
+    len = FD_READ_METHOD(fd,
+	ftpState->ctrl.buf + ftpState->ctrl.offset,
+	ftpState->ctrl.size - ftpState->ctrl.offset);
+    if (len > 0) {
+	fd_bytes(fd, len, FD_READ);
+	kb_incr(&statCounter.server.all.kbytes_in, len);
+	kb_incr(&statCounter.server.ftp.kbytes_in, len);
+    }
+    debug(9, 5) ("ftpReadControlReply: FD %d, Read %d bytes\n", fd, len);
+    if (len < 0) {
+	debug(50, ignoreErrno(errno) ? 3 : 1) ("ftpReadControlReply: read error: %s\n", xstrerror());
+	if (ignoreErrno(errno)) {
+	    ftpScheduleReadControlReply(ftpState, 0);
+	} else {
+	    ftpFailed(ftpState, ERR_READ_ERROR);
+	    /* ftpFailed closes ctrl.fd and frees ftpState */
+	    return;
+	}
+	return;
+    }
+    if (len == 0) {
+	if (entry->store_status == STORE_PENDING) {
+	    ftpFailed(ftpState, ERR_FTP_FAILURE);
+	    /* ftpFailed closes ctrl.fd and frees ftpState */
+	    return;
+	}
+	comm_close(ftpState->ctrl.fd);
+	return;
+    }
+    len += ftpState->ctrl.offset;
+    ftpState->ctrl.offset = len;
+    assert(len <= ftpState->ctrl.size);
+    ftpHandleControlReply(ftpState);
+}
+
+static void
+ftpHandleControlReply(FtpStateData * ftpState)
+{
+    wordlist **W;
+    int bytes_used = 0;
+    wordlistDestroy(&ftpState->ctrl.message);
+    ftpState->ctrl.message = ftpParseControlReply(ftpState->ctrl.buf,
+	ftpState->ctrl.offset, &ftpState->ctrl.replycode, &bytes_used);
+    if (ftpState->ctrl.message == NULL) {
+	/* didn't get complete reply yet */
+	if (ftpState->ctrl.offset == (off_t)ftpState->ctrl.size) {
+	    ftpState->ctrl.buf = (char *)memReallocBuf(ftpState->ctrl.buf, ftpState->ctrl.size << 1, &ftpState->ctrl.size);
+	}
+	ftpScheduleReadControlReply(ftpState, 0);
+	return;
+    } else if (ftpState->ctrl.offset == bytes_used) {
+	/* used it all up */
+	ftpState->ctrl.offset = 0;
+    } else {
+	/* Got some data past the complete reply */
+	assert(bytes_used < ftpState->ctrl.offset);
+	ftpState->ctrl.offset -= bytes_used;
+	xmemmove(ftpState->ctrl.buf, ftpState->ctrl.buf + bytes_used,
+	    ftpState->ctrl.offset);
+    }
+    /* Move the last line of the reply message to ctrl.last_reply */
+    for (W = &ftpState->ctrl.message; (*W)->next; W = &(*W)->next);
+    safe_free(ftpState->ctrl.last_reply);
+    ftpState->ctrl.last_reply = xstrdup((*W)->key);
+    wordlistDestroy(W);
+    /* Copy the rest of the message to cwd_message to be printed in
+     * error messages
+     */
+    wordlistAddWl(&ftpState->cwd_message, ftpState->ctrl.message);
+    debug(9, 8) ("ftpHandleControlReply: state=%d, code=%d\n", ftpState->state,
+	ftpState->ctrl.replycode);
+    FTP_SM_FUNCS[ftpState->state] (ftpState);
+}
+
+/* ====================================================================== */
+
+static void
+ftpReadWelcome(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("ftpReadWelcome\n");
+    if (ftpState->flags.pasv_only)
+	ftpState->login_att++;
+    /* Dont retry if the FTP server accepted the connection */
+    ftpState->fwd->flags.dont_retry = 1;
+    if (code == 220) {
+	if (ftpState->ctrl.message) {
+	    if (strstr(ftpState->ctrl.message->key, "NetWare"))
+		ftpState->flags.skip_whitespace = 1;
+	}
+	ftpSendUser(ftpState);
+    } else if (code == 120) {
+	if (NULL != ftpState->ctrl.message)
+	    debug(9, 3) ("FTP server is busy: %s\n",
+		ftpState->ctrl.message->key);
+	return;
+    } else {
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpSendUser(FtpStateData * ftpState)
+{
+    if (ftpState->proxy_host != NULL)
+	snprintf(cbuf, 1024, "USER %s@%s\r\n",
+	    ftpState->user,
+	    ftpState->request->host);
+    else
+	snprintf(cbuf, 1024, "USER %s\r\n", ftpState->user);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_USER;
+}
+
+static void
+ftpReadUser(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("ftpReadUser\n");
+    if (code == 230) {
+	ftpReadPass(ftpState);
+    } else if (code == 331) {
+	ftpSendPass(ftpState);
+    } else {
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpSendPass(FtpStateData * ftpState)
+{
+    snprintf(cbuf, 1024, "PASS %s\r\n", ftpState->password);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_PASS;
+}
+
+static void
+ftpReadPass(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("ftpReadPass\n");
+    if (code == 230) {
+	ftpSendType(ftpState);
+    } else {
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpSendType(FtpStateData * ftpState)
+{
+    const char *t;
+    const char *filename;
+    char mode;
+    /*
+     * Ref section 3.2.2 of RFC 1738
+     */
+    mode = ftpState->typecode;
+    switch (mode) {
+    case 'D':
+	mode = 'A';
+	break;
+    case 'A':
+    case 'I':
+	break;
+    default:
+	if (ftpState->flags.isdir) {
+	    mode = 'A';
+	} else {
+	    t = strRChr(ftpState->request->urlpath, '/');
+	    filename = t ? t + 1 : strBuf(ftpState->request->urlpath);
+	    mode = mimeGetTransferMode(filename);
+	}
+	break;
+    }
+    if (mode == 'I')
+	ftpState->flags.binary = 1;
+    else
+	ftpState->flags.binary = 0;
+    snprintf(cbuf, 1024, "TYPE %c\r\n", mode);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_TYPE;
+}
+
+static void
+ftpReadType(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    char *path;
+    char *d, *p;
+    debug(9, 3) ("This is ftpReadType\n");
+    if (code == 200) {
+	p = path = xstrdup(strBuf(ftpState->request->urlpath));
+	if (*p == '/')
+	    p++;
+	while (*p) {
+	    d = p;
+	    p += strcspn(p, "/");
+	    if (*p)
+		*p++ = '\0';
+	    rfc1738_unescape(d);
+	    wordlistAdd(&ftpState->pathcomps, d);
+	}
+	xfree(path);
+	if (ftpState->pathcomps)
+	    ftpTraverseDirectory(ftpState);
+	else
+	    ftpListDir(ftpState);
+    } else {
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpTraverseDirectory(FtpStateData * ftpState)
+{
+    wordlist *w;
+    debug(9, 4) ("ftpTraverseDirectory %s\n",
+	ftpState->filepath ? ftpState->filepath : "<NULL>");
+
+    safe_free(ftpState->filepath);
+    /* Done? */
+    if (ftpState->pathcomps == NULL) {
+	debug(9, 3) ("the final component was a directory\n");
+	ftpListDir(ftpState);
+	return;
+    }
+    /* Go to next path component */
+    w = ftpState->pathcomps;
+    ftpState->filepath = w->key;
+    ftpState->pathcomps = w->next;
+    memFree(w, MEM_WORDLIST);
+    /* Check if we are to CWD or RETR */
+    if (ftpState->pathcomps != NULL || ftpState->flags.isdir) {
+	ftpSendCwd(ftpState);
+    } else {
+	debug(9, 3) ("final component is probably a file\n");
+	ftpGetFile(ftpState);
+	return;
+    }
+}
+
+static void
+ftpSendCwd(FtpStateData * ftpState)
+{
+    char *path = ftpState->filepath;
+    debug(9, 3) ("ftpSendCwd\n");
+    if (!strcmp(path, "..") || !strcmp(path, "/")) {
+	ftpState->flags.no_dotdot = 1;
+    } else {
+	ftpState->flags.no_dotdot = 0;
+    }
+    if (*path)
+	snprintf(cbuf, 1024, "CWD %s\r\n", path);
+    else
+	snprintf(cbuf, 1024, "CWD\r\n");
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_CWD;
+}
+
+static void
+ftpReadCwd(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadCwd\n");
+    if (code >= 200 && code < 300) {
+	/* CWD OK */
+	ftpUnhack(ftpState);
+	/* Reset cwd_message to only include the last message */
+	if (ftpState->cwd_message)
+	    wordlistDestroy(&ftpState->cwd_message);
+	ftpState->cwd_message = ftpState->ctrl.message;
+	ftpState->ctrl.message = NULL;
+	/* Continue to traverse the path */
+	ftpTraverseDirectory(ftpState);
+    } else {
+	/* CWD FAILED */
+	if (!ftpState->flags.put)
+	    ftpFail(ftpState);
+	else
+	    ftpSendMkdir(ftpState);
+    }
+}
+
+static void
+ftpSendMkdir(FtpStateData * ftpState)
+{
+    char *path = ftpState->filepath;
+    debug(9, 3) ("ftpSendMkdir: with path=%s\n", path);
+    snprintf(cbuf, 1024, "MKD %s\r\n", path);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_MKDIR;
+}
+
+static void
+ftpReadMkdir(FtpStateData * ftpState)
+{
+    char *path = ftpState->filepath;
+    int code = ftpState->ctrl.replycode;
+
+    debug(9, 3) ("ftpReadMkdir: path %s, code %d\n", path, code);
+    if (code == 257) {		/* success */
+	ftpSendCwd(ftpState);
+    } else if (code == 550) {	/* dir exists */
+	if (ftpState->flags.put_mkdir) {
+	    ftpState->flags.put_mkdir = 1;
+	    ftpSendCwd(ftpState);
+	} else
+	    ftpSendReply(ftpState);
+    } else
+	ftpSendReply(ftpState);
+}
+
+static void
+ftpGetFile(FtpStateData * ftpState)
+{
+    assert(*ftpState->filepath != '\0');
+    ftpState->flags.isdir = 0;
+    ftpSendMdtm(ftpState);
+}
+
+static void
+ftpListDir(FtpStateData * ftpState)
+{
+    if (!ftpState->flags.isdir) {
+	debug(9, 3) ("Directory path did not end in /\n");
+	strCat(ftpState->title_url, "/");
+	ftpState->flags.isdir = 1;
+	ftpState->flags.need_base_href = 1;
+    }
+    ftpSendPasv(ftpState);
+}
+
+static void
+ftpSendMdtm(FtpStateData * ftpState)
+{
+    assert(*ftpState->filepath != '\0');
+    snprintf(cbuf, 1024, "MDTM %s\r\n", ftpState->filepath);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_MDTM;
+}
+
+static void
+ftpReadMdtm(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadMdtm\n");
+    if (code == 213) {
+	ftpState->mdtm = parse_iso3307_time(ftpState->ctrl.last_reply);
+	ftpUnhack(ftpState);
+    } else if (code < 0) {
+	ftpFail(ftpState);
+    }
+    ftpSendSize(ftpState);
+}
+
+static void
+ftpSendSize(FtpStateData * ftpState)
+{
+    /* Only send SIZE for binary transfers. The returned size
+     * is useless on ASCII transfers */
+    if (ftpState->flags.binary) {
+	assert(ftpState->filepath != NULL);
+	assert(*ftpState->filepath != '\0');
+	snprintf(cbuf, 1024, "SIZE %s\r\n", ftpState->filepath);
+	ftpWriteCommand(cbuf, ftpState);
+	ftpState->state = SENT_SIZE;
+    } else
+	/* Skip to next state no non-binary transfers */
+	ftpSendPasv(ftpState);
+}
+
+static void
+ftpReadSize(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadSize\n");
+    if (code == 213) {
+	ftpUnhack(ftpState);
+	ftpState->size = atoi(ftpState->ctrl.last_reply);
+	if (ftpState->size == 0) {
+	    debug(9, 2) ("ftpReadSize: SIZE reported %s on %s\n",
+		ftpState->ctrl.last_reply,
+		strBuf(ftpState->title_url));
+	    ftpState->size = -1;
+	}
+    } else if (code < 0) {
+	ftpFail(ftpState);
+    }
+    ftpSendPasv(ftpState);
+}
+
+static void
+ftpSendPasv(FtpStateData * ftpState)
+{
+    int fd;
+    struct sockaddr_in addr;
+    socklen_t addr_len;
+    if (ftpState->request->method == METHOD_HEAD) {
+	/* Terminate here for HEAD requests */
+	ftpAppendSuccessHeader(ftpState);
+	storeTimestampsSet(ftpState->entry);
+	/*
+	 * On rare occasions I'm seeing the entry get aborted after
+	 * ftpReadControlReply() and before here, probably when
+	 * trying to write to the client.
+	 */
+	if (!EBIT_TEST(ftpState->entry->flags, ENTRY_ABORTED))
+	    fwdComplete(ftpState->fwd);
+	ftpSendQuit(ftpState);
+	return;
+    }
+    if (ftpState->data.fd >= 0) {
+	if (!ftpState->flags.datachannel_hack) {
+	    /* We are already connected, reuse this connection. */
+	    ftpRestOrList(ftpState);
+	    return;
+	} else {
+	    /* Close old connection */
+	    comm_close(ftpState->data.fd);
+	    ftpState->data.fd = -1;
+	}
+    }
+    if (!ftpState->flags.pasv_supported) {
+	ftpSendPort(ftpState);
+	return;
+    }
+    addr_len = sizeof(addr);
+    if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
+	debug(9, 0) ("ftpSendPasv: getsockname(%d,..): %s\n",
+	    ftpState->ctrl.fd, xstrerror());
+	ftpFail(ftpState);
+	return;
+    }
+    /* Open data channel with the same local address as control channel */
+    fd = comm_open(SOCK_STREAM,
+	0,
+	addr.sin_addr,
+	0,
+	COMM_NONBLOCKING,
+	storeUrl(ftpState->entry));
+    debug(9, 3) ("ftpSendPasv: Unconnected data socket created on FD %d\n", fd);
+    if (fd < 0) {
+	ftpFail(ftpState);
+	return;
+    }
+    /*
+     * No comm_add_close_handler() here.  If we have both ctrl and
+     * data FD's call ftpStateFree() upon close, then we have
+     * to delete the close handler which did NOT get called
+     * to prevent ftpStateFree() getting called twice.
+     * Instead we'll always call comm_close() on the ctrl FD.
+     */
+    ftpState->data.fd = fd;
+    snprintf(cbuf, 1024, "PASV\r\n");
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_PASV;
+    /*
+     * ugly hack for ftp servers like ftp.netscape.com that sometimes
+     * dont acknowledge PORT commands.
+     */
+    commSetTimeout(ftpState->data.fd, 15, ftpTimeout, ftpState);
+}
+
+static void
+ftpReadPasv(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    int h1, h2, h3, h4;
+    int p1, p2;
+    int n;
+    u_short port;
+    int fd = ftpState->data.fd;
+    char *buf;
+    LOCAL_ARRAY(char, ipaddr, 1024);
+    debug(9, 3) ("This is ftpReadPasv\n");
+    if (code != 227) {
+	debug(9, 3) ("PASV not supported by remote end\n");
+	ftpSendPort(ftpState);
+	return;
+    }
+    /*  227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).  */
+    /*  ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
+    debug(9, 5) ("scanning: %s\n", ftpState->ctrl.last_reply);
+    buf = strstr(ftpState->ctrl.last_reply, "(");
+    if (!buf) {
+	debug(9, 1) ("Unsafe PASV reply from %s: '%s'\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
+	ftpSendPort(ftpState);
+	return;
+    }
+    buf++;			/* skip ( */
+    n = sscanf(buf, "%d,%d,%d,%d,%d,%d", &h1, &h2, &h3, &h4, &p1, &p2);
+    if (n != 6 || p1 < 0 || p2 < 0 || p1 > 255 || p2 > 255) {
+	debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
+	ftpSendPort(ftpState);
+	return;
+    }
+    snprintf(ipaddr, 1024, "%d.%d.%d.%d", h1, h2, h3, h4);
+    if (!safe_inet_addr(ipaddr, NULL)) {
+	debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
+	ftpSendPort(ftpState);
+	return;
+    }
+    port = ((p1 << 8) + p2);
+    if (0 == port) {
+	debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
+	ftpSendPort(ftpState);
+	return;
+    }
+    if (Config.Ftp.sanitycheck) {
+	if (strcmp(fd_table[ftpState->ctrl.fd].ipaddr, ipaddr) != 0) {
+	    debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
+	    ftpSendPort(ftpState);
+	    return;
+	}
+	if (port < 1024) {
+	    debug(9, 1) ("Unsafe PASV reply from %s: %s\n", fd_table[ftpState->ctrl.fd].ipaddr, ftpState->ctrl.last_reply);
+	    ftpSendPort(ftpState);
+	    return;
+	}
+    }
+    debug(9, 5) ("ftpReadPasv: connecting to %s, port %d\n", ipaddr, port);
+    ftpState->data.port = port;
+    ftpState->data.host = xstrdup(ipaddr);
+    safe_free(ftpState->ctrl.last_command);
+    safe_free(ftpState->ctrl.last_reply);
+    ftpState->ctrl.last_command = xstrdup("Connect to server data port");
+    commConnectStart(fd, ipaddr, port, ftpPasvCallback, ftpState);
+}
+
+static void
+ftpPasvCallback(int fd, comm_err_t status, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    debug(9, 3) ("ftpPasvCallback\n");
+    if (status != COMM_OK) {
+	debug(9, 2) ("ftpPasvCallback: failed to connect. Retrying without PASV.\n");
+	ftpState->fwd->flags.dont_retry = 0;	/* this is a retryable error */
+	ftpState->fwd->flags.ftp_pasv_failed = 1;
+	ftpFailed(ftpState, ERR_NONE);
+	/* ftpFailed closes ctrl.fd and frees ftpState */
+	return;
+    }
+    ftpRestOrList(ftpState);
+}
+
+static int
+ftpOpenListenSocket(FtpStateData * ftpState, int fallback)
+{
+    int fd;
+    struct sockaddr_in addr;
+    socklen_t addr_len;
+    int on = 1;
+    u_short port = 0;
+    /*
+     * Tear down any old data connection if any. We are about to
+     * establish a new one.
+     */
+    if (ftpState->data.fd > 0) {
+	comm_close(ftpState->data.fd);
+	ftpState->data.fd = -1;
+    }
+    /*
+     * Set up a listen socket on the same local address as the
+     * control connection.
+     */
+    addr_len = sizeof(addr);
+    if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
+	debug(9, 0) ("ftpOpenListenSocket: getsockname(%d,..): %s\n",
+	    ftpState->ctrl.fd, xstrerror());
+	return -1;
+    }
+    /*
+     * REUSEADDR is needed in fallback mode, since the same port is
+     * used for both control and data.
+     */
+    if (fallback) {
+	setsockopt(ftpState->ctrl.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
+	port = ntohs(addr.sin_port);
+    }
+    fd = comm_open(SOCK_STREAM,
+	0,
+	addr.sin_addr,
+	port,
+	COMM_NONBLOCKING | (fallback ? COMM_REUSEADDR : 0),
+	storeUrl(ftpState->entry));
+    debug(9, 3) ("ftpOpenListenSocket: Unconnected data socket created on FD %d\n", fd);
+    if (fd < 0) {
+	debug(9, 0) ("ftpOpenListenSocket: comm_open failed\n");
+	return -1;
+    }
+    if (comm_listen(fd) < 0) {
+	comm_close(fd);
+	return -1;
+    }
+    ftpState->data.fd = fd;
+    ftpState->data.port = comm_local_port(fd);
+    ftpState->data.host = NULL;
+    return fd;
+}
+
+static void
+ftpSendPort(FtpStateData * ftpState)
+{
+    int fd;
+    struct sockaddr_in addr;
+    socklen_t addr_len;
+    unsigned char *addrptr;
+    unsigned char *portptr;
+    debug(9, 3) ("This is ftpSendPort\n");
+    ftpState->flags.pasv_supported = 0;
+    fd = ftpOpenListenSocket(ftpState, 0);
+    addr_len = sizeof(addr);
+    if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
+	debug(9, 0) ("ftpSendPort: getsockname(%d,..): %s\n", fd, xstrerror());
+	/* XXX Need to set error message */
+	ftpFail(ftpState);
+	return;
+    }
+    addrptr = (unsigned char *) &addr.sin_addr.s_addr;
+    portptr = (unsigned char *) &addr.sin_port;
+    snprintf(cbuf, 1024, "PORT %d,%d,%d,%d,%d,%d\r\n",
+	addrptr[0], addrptr[1], addrptr[2], addrptr[3],
+	portptr[0], portptr[1]);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_PORT;
+}
+
+static void
+ftpReadPort(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadPort\n");
+    if (code != 200) {
+	/* Fall back on using the same port as the control connection */
+	debug(9, 3) ("PORT not supported by remote end\n");
+	ftpOpenListenSocket(ftpState, 1);
+    }
+    ftpRestOrList(ftpState);
+}
+
+/* "read" handler to accept data connection */
+static void
+ftpAcceptDataConnection(int fd, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *)data;
+    struct sockaddr_in my_peer, me;
+    debug(9, 3) ("ftpAcceptDataConnection\n");
+
+    if (EBIT_TEST(ftpState->entry->flags, ENTRY_ABORTED)) {
+	comm_close(ftpState->ctrl.fd);
+	return;
+    }
+    fd = comm_accept(fd, &my_peer, &me);
+    if (Config.Ftp.sanitycheck) {
+	char *ipaddr = inet_ntoa(my_peer.sin_addr);
+	if (strcmp(fd_table[ftpState->ctrl.fd].ipaddr, ipaddr) != 0) {
+	    debug(9, 1) ("FTP data connection from unexpected server (%s:%d), expecting %s\n", ipaddr, (int) ntohs(my_peer.sin_port), fd_table[ftpState->ctrl.fd].ipaddr);
+	    comm_close(fd);
+	    commSetSelect(ftpState->data.fd,
+		COMM_SELECT_READ,
+		ftpAcceptDataConnection,
+		ftpState,
+		0);
+	    return;
+	}
+    }
+    if (fd < 0) {
+	debug(9, 1) ("ftpHandleDataAccept: comm_accept(%d): %s", fd, xstrerror());
+	/* XXX Need to set error message */
+	ftpFail(ftpState);
+	return;
+    }
+    /* Replace the Listen socket with the accepted data socket */
+    comm_close(ftpState->data.fd);
+    debug(9, 3) ("ftpAcceptDataConnection: Connected data socket on FD %d\n", fd);
+    ftpState->data.fd = fd;
+    ftpState->data.port = ntohs(my_peer.sin_port);
+    ftpState->data.host = xstrdup(inet_ntoa(my_peer.sin_addr));
+    commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
+    commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
+	ftpState);
+    /* XXX We should have a flag to track connect state...
+     *    host NULL -> not connected, port == local port
+     *    host set  -> connected, port == remote port
+     */
+    /* Restart state (SENT_NLST/LIST/RETR) */
+    FTP_SM_FUNCS[ftpState->state] (ftpState);
+}
+
+static void
+ftpRestOrList(FtpStateData * ftpState)
+{
+    debug(9, 3) ("This is ftpRestOrList\n");
+    if (ftpState->typecode == 'D') {
+	ftpState->flags.isdir = 1;
+	ftpState->flags.need_base_href = 1;
+	if (ftpState->flags.put) {
+	    ftpSendMkdir(ftpState);	/* PUT name;type=d */
+	} else {
+	    ftpSendNlst(ftpState);	/* GET name;type=d  sec 3.2.2 of RFC 1738 */
+	}
+    } else if (ftpState->flags.put) {
+	debug(9, 3) ("ftpRestOrList: Sending STOR request...\n");
+	ftpSendStor(ftpState);
+    } else if (ftpState->flags.isdir)
+	ftpSendList(ftpState);
+    else if (ftpRestartable(ftpState))
+	ftpSendRest(ftpState);
+    else
+	ftpSendRetr(ftpState);
+}
+
+static void
+ftpSendStor(FtpStateData * ftpState)
+{
+    if (ftpState->filepath != NULL) {
+	/* Plain file upload */
+	snprintf(cbuf, 1024, "STOR %s\r\n", ftpState->filepath);
+	ftpWriteCommand(cbuf, ftpState);
+	ftpState->state = SENT_STOR;
+    } else if (httpHeaderGetInt(&ftpState->request->header, HDR_CONTENT_LENGTH) > 0) {
+	/* File upload without a filename. use STOU to generate one */
+	snprintf(cbuf, 1024, "STOU\r\n");
+	ftpWriteCommand(cbuf, ftpState);
+	ftpState->state = SENT_STOR;
+    } else {
+	/* No file to transfer. Only create directories if needed */
+	ftpSendReply(ftpState);
+    }
+}
+
+static void
+ftpReadStor(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadStor\n");
+    if (code == 125 || (code == 150 && ftpState->data.host)) {
+	/* Begin data transfer */
+	debug(9, 3) ("ftpReadStor: starting data transfer\n");
+	commSetSelect(ftpState->data.fd,
+	    COMM_SELECT_WRITE,
+	    ftpDataWrite,
+	    ftpState,
+	    Config.Timeout.read);
+	/*
+	 * Cancel the timeout on the Control socket and
+	 * establish one on the data socket.
+	 */
+	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
+	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
+	    ftpState);
+	ftpState->state = WRITING_DATA;
+	debug(9, 3) ("ftpReadStor: writing data channel\n");
+    } else if (code == 150) {
+	/* Accept data channel */
+	debug(9, 3) ("ftpReadStor: accepting data channel\n");
+	commSetSelect(ftpState->data.fd,
+	    COMM_SELECT_READ,
+	    ftpAcceptDataConnection,
+	    ftpState,
+	    0);
+    } else {
+	debug(9, 3) ("ftpReadStor: Unexpected reply code %03d\n", code);
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpSendRest(FtpStateData * ftpState)
+{
+    snprintf(cbuf, 1024, "REST %d\r\n", ftpState->restart_offset);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_REST;
+}
+
+static int
+ftpRestartable(FtpStateData * ftpState)
+{
+    if (ftpState->restart_offset > 0)
+	return 1;
+    if (!ftpState->request->range)
+	return 0;
+    if (!ftpState->flags.binary)
+	return 0;
+    if (ftpState->size <= 0)
+	return 0;
+
+    ftpState->restart_offset = httpHdrRangeLowestOffset(ftpState->request->range, (size_t) ftpState->size);
+    if (ftpState->restart_offset <= 0)
+	return 0;
+    return 1;
+}
+
+static void
+ftpReadRest(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadRest\n");
+    assert(ftpState->restart_offset > 0);
+    if (code == 350) {
+	ftpState->restarted_offset = ftpState->restart_offset;
+	ftpSendRetr(ftpState);
+    } else if (code > 0) {
+	debug(9, 3) ("ftpReadRest: REST not supported\n");
+	ftpState->flags.rest_supported = 0;
+	ftpSendRetr(ftpState);
+    } else {
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpSendList(FtpStateData * ftpState)
+{
+    if (ftpState->filepath) {
+	ftpState->flags.need_base_href = 1;
+	snprintf(cbuf, 1024, "LIST %s\r\n", ftpState->filepath);
+    } else {
+	snprintf(cbuf, 1024, "LIST\r\n");
+    }
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_LIST;
+}
+
+static void
+ftpSendNlst(FtpStateData * ftpState)
+{
+    ftpState->flags.tried_nlst = 1;
+    if (ftpState->filepath) {
+	ftpState->flags.need_base_href = 1;
+	snprintf(cbuf, 1024, "NLST %s\r\n", ftpState->filepath);
+    } else {
+	snprintf(cbuf, 1024, "NLST\r\n");
+    }
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_NLST;
+}
+
+static void
+ftpReadList(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadList\n");
+    if (code == 125 || (code == 150 && ftpState->data.host)) {
+	/* Begin data transfer */
+	ftpAppendSuccessHeader(ftpState);
+	commSetSelect(ftpState->data.fd,
+	    COMM_SELECT_READ,
+	    ftpDataRead,
+	    ftpState,
+	    Config.Timeout.read);
+	commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry);
+	ftpState->state = READING_DATA;
+	/*
+	 * Cancel the timeout on the Control socket and establish one
+	 * on the data socket
+	 */
+	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
+	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState);
+	return;
+    } else if (code == 150) {
+	/* Accept data channel */
+	commSetSelect(ftpState->data.fd,
+	    COMM_SELECT_READ,
+	    ftpAcceptDataConnection,
+	    ftpState,
+	    0);
+	/*
+	 * Cancel the timeout on the Control socket and establish one
+	 * on the data socket
+	 */
+	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
+	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState);
+	return;
+    } else if (!ftpState->flags.tried_nlst && code > 300) {
+	ftpSendNlst(ftpState);
+    } else {
+	ftpFail(ftpState);
+	return;
+    }
+}
+
+static void
+ftpSendRetr(FtpStateData * ftpState)
+{
+    assert(ftpState->filepath != NULL);
+    snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath);
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_RETR;
+}
+
+static void
+ftpReadRetr(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadRetr\n");
+    if (code == 125 || (code == 150 && ftpState->data.host)) {
+	/* Begin data transfer */
+	debug(9, 3) ("ftpReadRetr: reading data channel\n");
+	ftpAppendSuccessHeader(ftpState);
+	commSetSelect(ftpState->data.fd,
+	    COMM_SELECT_READ,
+	    ftpDataRead,
+	    ftpState,
+	    Config.Timeout.read);
+	commSetDefer(ftpState->data.fd, fwdCheckDeferRead, ftpState->entry);
+	ftpState->state = READING_DATA;
+	/*
+	 * Cancel the timeout on the Control socket and establish one
+	 * on the data socket
+	 */
+	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
+	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
+	    ftpState);
+    } else if (code == 150) {
+	/* Accept data channel */
+	commSetSelect(ftpState->data.fd,
+	    COMM_SELECT_READ,
+	    ftpAcceptDataConnection,
+	    ftpState,
+	    0);
+	/*
+	 * Cancel the timeout on the Control socket and establish one
+	 * on the data socket
+	 */
+	commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
+	commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
+	    ftpState);
+    } else if (code >= 300) {
+	if (!ftpState->flags.try_slash_hack) {
+	    /* Try this as a directory missing trailing slash... */
+	    ftpHackShortcut(ftpState, ftpSendCwd);
+	} else {
+	    ftpFail(ftpState);
+	}
+    } else {
+	ftpFail(ftpState);
+    }
+}
+
+static void
+ftpReadTransferDone(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpReadTransferDone\n");
+    if (code == 226) {
+	/* Connection closed; retrieval done. */
+	if (ftpState->flags.html_header_sent)
+	    ftpListingFinish(ftpState);
+	fwdUnregister(ftpState->ctrl.fd, ftpState->fwd);
+	fwdComplete(ftpState->fwd);
+	ftpSendQuit(ftpState);
+    } else {			/* != 226 */
+	debug(9, 1) ("ftpReadTransferDone: Got code %d after reading data\n",
+	    code);
+	ftpFailed(ftpState, ERR_FTP_FAILURE);
+	/* ftpFailed closes ctrl.fd and frees ftpState */
+	return;
+    }
+}
+
+/* This will be called when there is data available to put */
+static void
+ftpRequestBody(char *buf, ssize_t size, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *) data;
+    debug(9, 3) ("ftpRequestBody: buf=%p size=%d ftpState=%p\n", buf, (int) size, data);
+    ftpState->data.offset = size;
+    if (size > 0) {
+	/* DataWrite */
+	comm_write(ftpState->data.fd, buf, size, ftpDataWriteCallback, ftpState, NULL);
+    } else if (size < 0) {
+	/* Error */
+	debug(9, 1) ("ftpRequestBody: request aborted");
+	ftpFailed(ftpState, ERR_READ_ERROR);
+    } else if (size == 0) {
+	/* End of transfer */
+	ftpDataComplete(ftpState);
+    }
+}
+
+/* This will be called when the put write is completed */
+static void
+ftpDataWriteCallback(int fd, char *buf, size_t size, comm_err_t err, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *) data;
+    if (!err) {
+	/* Shedule the rest of the request */
+	clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState);
+    } else {
+	debug(9, 1) ("ftpDataWriteCallback: write error: %s\n", xstrerror());
+	ftpFailed(ftpState, ERR_WRITE_ERROR);
+    }
+}
+
+static void
+ftpDataWrite(int ftp, void *data)
+{
+    FtpStateData *ftpState = (FtpStateData *) data;
+    debug(9, 3) ("ftpDataWrite\n");
+    /* This starts the body transfer */
+    clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState);
+}
+
+static void
+ftpWriteTransferDone(FtpStateData * ftpState)
+{
+    int code = ftpState->ctrl.replycode;
+    debug(9, 3) ("This is ftpWriteTransferDone\n");
+    if (code != 226) {
+	debug(9, 1) ("ftpReadTransferDone: Got code %d after sending data\n",
+	    code);
+	ftpFailed(ftpState, ERR_FTP_PUT_ERROR);
+	return;
+    }
+    storeTimestampsSet(ftpState->entry);	/* XXX Is this needed? */
+    ftpSendReply(ftpState);
+}
+
+static void
+ftpSendQuit(FtpStateData * ftpState)
+{
+    assert(ftpState->ctrl.fd > -1);
+    snprintf(cbuf, 1024, "QUIT\r\n");
+    ftpWriteCommand(cbuf, ftpState);
+    ftpState->state = SENT_QUIT;
+}
+
+static void
+ftpReadQuit(FtpStateData * ftpState)
+{
+    comm_close(ftpState->ctrl.fd);
+}
+
+static void
+ftpTrySlashHack(FtpStateData * ftpState)
+{
+    char *path;
+    ftpState->flags.try_slash_hack = 1;
+    /* Free old paths */
+    if (ftpState->pathcomps)
+	wordlistDestroy(&ftpState->pathcomps);
+    safe_free(ftpState->filepath);
+    /* Build the new path (urlpath begins with /) */
+    path = xstrdup(strBuf(ftpState->request->urlpath));
+    rfc1738_unescape(path);
+    ftpState->filepath = path;
+    /* And off we go */
+    ftpGetFile(ftpState);
+}
+
+static void
+ftpTryDatachannelHack(FtpStateData * ftpState)
+{
+    ftpState->flags.datachannel_hack = 1;
+    /* we have to undo some of the slash hack... */
+    if (ftpState->old_filepath != NULL) {
+	ftpState->flags.try_slash_hack = 0;
+	safe_free(ftpState->filepath);
+	ftpState->filepath = ftpState->old_filepath;
+	ftpState->old_filepath = NULL;
+    }
+    ftpState->flags.tried_nlst = 0;
+    /* And off we go */
+    if (ftpState->flags.isdir) {
+	ftpListDir(ftpState);
+    } else {
+	ftpGetFile(ftpState);
+    }
+    return;
+}
+
+/* Forget hack status. Next error is shown to the user */
+static void
+ftpUnhack(FtpStateData * ftpState)
+{
+    if (ftpState->old_request != NULL) {
+	safe_free(ftpState->old_request);
+	safe_free(ftpState->old_reply);
+    }
+}
+
+static void
+ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState)
+{
+    /* Clear some unwanted state */
+    ftpState->restarted_offset = 0;
+    ftpState->restart_offset = 0;
+    /* Save old error message & some state info */
+    if (ftpState->old_request == NULL) {
+	ftpState->old_request = ftpState->ctrl.last_command;
+	ftpState->ctrl.last_command = NULL;
+	ftpState->old_reply = ftpState->ctrl.last_reply;
+	ftpState->ctrl.last_reply = NULL;
+	if (ftpState->pathcomps == NULL && ftpState->filepath != NULL)
+	    ftpState->old_filepath = xstrdup(ftpState->filepath);
+    }
+    /* Jump to the "hack" state */
+    nextState(ftpState);
+}
+
+static void
+ftpFail(FtpStateData * ftpState)
+{
+    debug(9, 3) ("ftpFail\n");
+    /* Try the / hack to support "Netscape" FTP URL's for retreiving files */
+    if (!ftpState->flags.isdir &&	/* Not a directory */
+	!ftpState->flags.try_slash_hack &&	/* Not in slash hack */
+	ftpState->mdtm <= 0 && ftpState->size < 0 &&	/* Not known as a file */
+	strNCaseCmp(ftpState->request->urlpath, "/%2f", 4) != 0) {	/* No slash encoded */
+	switch (ftpState->state) {
+	case SENT_CWD:
+	case SENT_RETR:
+	    /* Try the / hack */
+	    ftpHackShortcut(ftpState, ftpTrySlashHack);
+	    return;
+	default:
+	    break;
+	}
+    }
+    /* Try to reopen datachannel */
+    if (!ftpState->flags.datachannel_hack &&
+	ftpState->pathcomps == NULL) {
+	switch (ftpState->state) {
+	case SENT_RETR:
+	case SENT_LIST:
+	case SENT_NLST:
+	    /* Try to reopen datachannel */
+	    ftpHackShortcut(ftpState, ftpTryDatachannelHack);
+	    return;
+	default:
+	    break;
+	}
+    }
+    ftpFailed(ftpState, ERR_NONE);
+    /* ftpFailed closes ctrl.fd and frees ftpState */
+}
+
+static void
+ftpFailed(FtpStateData * ftpState, err_type error)
+{
+    StoreEntry *entry = ftpState->entry;
+    if (entry->mem_obj->inmem_hi == 0)
+	ftpFailedErrorMessage(ftpState, error);
+    if (ftpState->data.fd > -1) {
+	comm_close(ftpState->data.fd);
+	ftpState->data.fd = -1;
+    }
+    comm_close(ftpState->ctrl.fd);
+}
+
+static void
+ftpFailedErrorMessage(FtpStateData * ftpState, err_type error)
+{
+    ErrorState *err;
+    const char *command, *reply;
+    /* Translate FTP errors into HTTP errors */
+    err = NULL;
+    switch (error) {
+    case ERR_NONE:
+	switch (ftpState->state) {
+	case SENT_USER:
+	case SENT_PASS:
+	    if (ftpState->ctrl.replycode > 500)
+		err = errorCon(ERR_FTP_FORBIDDEN, HTTP_FORBIDDEN);
+	    else if (ftpState->ctrl.replycode == 421)
+		err = errorCon(ERR_FTP_UNAVAILABLE, HTTP_SERVICE_UNAVAILABLE);
+	    break;
+	case SENT_CWD:
+	case SENT_RETR:
+	    if (ftpState->ctrl.replycode == 550)
+		err = errorCon(ERR_FTP_NOT_FOUND, HTTP_NOT_FOUND);
+	    break;
+	default:
+	    break;
+	}
+	break;
+    case ERR_READ_TIMEOUT:
+	err = errorCon(error, HTTP_GATEWAY_TIMEOUT);
+	break;
+    default:
+	err = errorCon(error, HTTP_BAD_GATEWAY);
+	break;
+    }
+    if (err == NULL)
+	err = errorCon(ERR_FTP_FAILURE, HTTP_BAD_GATEWAY);
+    err->xerrno = errno;
+    err->request = requestLink(ftpState->request);
+    err->ftp.server_msg = ftpState->ctrl.message;
+    ftpState->ctrl.message = NULL;
+    if (ftpState->old_request)
+	command = ftpState->old_request;
+    else
+	command = ftpState->ctrl.last_command;
+    if (command && strncmp(command, "PASS", 4) == 0)
+	command = "PASS <yourpassword>";
+    if (ftpState->old_reply)
+	reply = ftpState->old_reply;
+    else
+	reply = ftpState->ctrl.last_reply;
+    if (command)
+	err->ftp.request = xstrdup(command);
+    if (reply)
+	err->ftp.reply = xstrdup(reply);
+    fwdFail(ftpState->fwd, err);
+}
+
+static void
+ftpSendReply(FtpStateData * ftpState)
+{
+    ErrorState *err;
+    int code = ftpState->ctrl.replycode;
+    http_status http_code;
+    err_type err_code = ERR_NONE;
+    debug(9, 5) ("ftpSendReply: %s, code %d\n",
+	storeUrl(ftpState->entry), code);
+    if (cbdataReferenceValid(ftpState))
+	debug(9, 5) ("ftpSendReply: ftpState (%p) is valid!\n", ftpState);
+    if (code == 226) {
+	err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
+	http_code = (ftpState->mdtm > 0) ? HTTP_ACCEPTED : HTTP_CREATED;
+    } else if (code == 227) {
+	err_code = ERR_FTP_PUT_CREATED;
+	http_code = HTTP_CREATED;
+    } else {
+	err_code = ERR_FTP_PUT_ERROR;
+	http_code = HTTP_INTERNAL_SERVER_ERROR;
+    }
+    err = errorCon(err_code, http_code);
+    err->request = requestLink(ftpState->request);
+    if (ftpState->old_request)
+	err->ftp.request = xstrdup(ftpState->old_request);
+    else
+	err->ftp.request = xstrdup(ftpState->ctrl.last_command);
+    if (ftpState->old_reply)
+	err->ftp.reply = xstrdup(ftpState->old_reply);
+    else if (ftpState->ctrl.last_reply)
+	err->ftp.reply = xstrdup(ftpState->ctrl.last_reply);
+    else
+	err->ftp.reply = xstrdup("");
+    errorAppendEntry(ftpState->entry, err);
+    storeBufferFlush(ftpState->entry);
+    ftpSendQuit(ftpState);
+}
+
+static void
+ftpAppendSuccessHeader(FtpStateData * ftpState)
+{
+    const char *mime_type = NULL;
+    const char *mime_enc = NULL;
+    String urlpath = ftpState->request->urlpath;
+    const char *filename = NULL;
+    const char *t = NULL;
+    StoreEntry *e = ftpState->entry;
+    http_reply *reply = e->mem_obj->reply;
+    http_version_t version;
+
+    if (ftpState->flags.http_header_sent)
+	return;
+    ftpState->flags.http_header_sent = 1;
+    assert(e->mem_obj->inmem_hi == 0);
+    EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
+    filename = (t = strRChr(urlpath, '/')) ? t + 1 : strBuf(urlpath);
+    if (ftpState->flags.isdir) {
+	mime_type = "text/html";
+    } else {
+	switch (ftpState->typecode) {
+	case 'I':
+	    mime_type = "application/octet-stream";
+	    mime_enc = mimeGetContentEncoding(filename);
+	    break;
+	case 'A':
+	    mime_type = "text/plain";
+	    break;
+	default:
+	    mime_type = mimeGetContentType(filename);
+	    mime_enc = mimeGetContentEncoding(filename);
+	    break;
+	}
+    }
+    storeBuffer(e);
+    httpReplyReset(reply);
+    /* set standard stuff */
+    if (ftpState->restarted_offset) {
+	/* Partial reply */
+	HttpHdrRangeSpec range_spec;
+	range_spec.offset = ftpState->restarted_offset;
+	range_spec.length = ftpState->size - ftpState->restarted_offset;
+	httpBuildVersion(&version, 1, 0);
+	httpReplySetHeaders(reply, version, HTTP_PARTIAL_CONTENT, "Gatewaying",
+	    mime_type, ftpState->size - ftpState->restarted_offset, ftpState->mdtm, -2);
+	httpHeaderAddContRange(&reply->header, range_spec, ftpState->size);
+    } else {
+	/* Full reply */
+	httpBuildVersion(&version, 1, 0);
+	httpReplySetHeaders(reply, version, HTTP_OK, "Gatewaying",
+	    mime_type, ftpState->size, ftpState->mdtm, -2);
+    }
+    /* additional info */
+    if (mime_enc)
+	httpHeaderPutStr(&reply->header, HDR_CONTENT_ENCODING, mime_enc);
+    httpReplySwapOut(reply, e);
+    storeBufferFlush(e);
+    reply->hdr_sz = e->mem_obj->inmem_hi;
+    storeTimestampsSet(e);
+    if (ftpState->flags.authenticated) {
+	/*
+	 * Authenticated requests can't be cached.
+	 */
+	storeRelease(e);
+    } else if (EBIT_TEST(e->flags, ENTRY_CACHABLE) && !ftpState->restarted_offset) {
+	storeSetPublicKey(e);
+    } else {
+	storeRelease(e);
+    }
+}
+
+static void
+ftpAuthRequired(HttpReply * old_reply, request_t * request, const char *realm)
+{
+    ErrorState *err = errorCon(ERR_CACHE_ACCESS_DENIED, HTTP_UNAUTHORIZED);
+    HttpReply *rep;
+    err->request = requestLink(request);
+    rep = errorBuildReply(err);
+    errorStateFree(err);
+    /* add Authenticate header */
+    httpHeaderPutAuth(&rep->header, "Basic", realm);
+    /* move new reply to the old one */
+    httpReplyAbsorb(old_reply, rep);
+}
+
+char *
+ftpUrlWith2f(const request_t * request)
+{
+    LOCAL_ARRAY(char, buf, MAX_URL);
+    LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
+    LOCAL_ARRAY(char, portbuf, 32);
+    char *t;
+    portbuf[0] = '\0';
+    if (request->protocol != PROTO_FTP)
+	return NULL;
+    if (request->port != urlDefaultPort(request->protocol))
+	snprintf(portbuf, 32, ":%d", request->port);
+    loginbuf[0] = '\0';
+    if ((int) strlen(request->login) > 0) {
+	xstrncpy(loginbuf, request->login, sizeof(loginbuf) - 2);
+	if ((t = strchr(loginbuf, ':')))
+	    *t = '\0';
+	strcat(loginbuf, "@");
+    }
+    snprintf(buf, MAX_URL, "%s://%s%s%s%s%s",
+	ProtocolStr[request->protocol],
+	loginbuf,
+	request->host,
+	portbuf,
+	"/%2f",
+	strBuf(request->urlpath));
+    if ((t = strchr(buf, '?')))
+	*t = '\0';
+    return buf;
+}
--- squid/src/gopher.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,852 +0,0 @@
-
-/*
- * $Id: gopher.c,v 1.19.2.1 2002/10/04 07:07:25 rbcollins Exp $
- *
- * DEBUG: section 10    Gopher
- * AUTHOR: Harvest Derived
- *
- * 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"
-#include "Store.h"
-
-/* gopher type code from rfc. Anawat. */
-#define GOPHER_FILE         '0'
-#define GOPHER_DIRECTORY    '1'
-#define GOPHER_CSO          '2'
-#define GOPHER_ERROR        '3'
-#define GOPHER_MACBINHEX    '4'
-#define GOPHER_DOSBIN       '5'
-#define GOPHER_UUENCODED    '6'
-#define GOPHER_INDEX        '7'
-#define GOPHER_TELNET       '8'
-#define GOPHER_BIN          '9'
-#define GOPHER_REDUNT       '+'
-#define GOPHER_3270         'T'
-#define GOPHER_GIF          'g'
-#define GOPHER_IMAGE        'I'
-
-#define GOPHER_HTML         'h'	/* HTML */
-#define GOPHER_INFO         'i'
-#define GOPHER_WWW          'w'	/* W3 address */
-#define GOPHER_SOUND        's'
-
-#define GOPHER_PLUS_IMAGE   ':'
-#define GOPHER_PLUS_MOVIE   ';'
-#define GOPHER_PLUS_SOUND   '<'
-
-#define GOPHER_PORT         70
-
-#define TAB                 '\t'
-#define TEMP_BUF_SIZE       4096
-#define MAX_CSO_RESULT      1024
-
-typedef struct gopher_ds {
-    StoreEntry *entry;
-    enum {
-	NORMAL,
-	HTML_DIR,
-	HTML_INDEX_RESULT,
-	HTML_CSO_RESULT,
-	HTML_INDEX_PAGE,
-	HTML_CSO_PAGE
-    } conversion;
-    int HTML_header_added;
-    char type_id;
-    char request[MAX_URL];
-    int data_in;
-    int cso_recno;
-    int len;
-    char *buf;			/* pts to a 4k page */
-    int fd;
-    request_t *req;
-    FwdState *fwdState;
-} GopherStateData;
-
-static PF gopherStateFree;
-static void gopher_mime_content(MemBuf * mb, const char *name, const char *def);
-static void gopherMimeCreate(GopherStateData *);
-static void gopher_request_parse(const request_t * req,
-    char *type_id,
-    char *request);
-static void gopherEndHTML(GopherStateData *);
-static void gopherToHTML(GopherStateData *, char *inbuf, int len);
-static PF gopherTimeout;
-static PF gopherReadReply;
-static CWCB gopherSendComplete;
-static PF gopherSendRequest;
-
-static char def_gopher_bin[] = "www/unknown";
-static char def_gopher_text[] = "text/plain";
-
-static void
-gopherStateFree(int fdnotused, void *data)
-{
-    GopherStateData *gopherState = data;
-    if (gopherState == NULL)
-	return;
-    if (gopherState->entry) {
-	storeUnlockObject(gopherState->entry);
-    }
-    if (gopherState->req) {
-	requestUnlink(gopherState->req);
-    }
-    memFree(gopherState->buf, MEM_4K_BUF);
-    gopherState->buf = NULL;
-    cbdataFree(gopherState);
-}
-
-
-/* figure out content type from file extension */
-static void
-gopher_mime_content(MemBuf * mb, const char *name, const char *def_ctype)
-{
-    char *ctype = mimeGetContentType(name);
-    char *cenc = mimeGetContentEncoding(name);
-    if (cenc)
-	memBufPrintf(mb, "Content-Encoding: %s\r\n", cenc);
-    memBufPrintf(mb, "Content-Type: %s\r\n",
-	ctype ? ctype : def_ctype);
-}
-
-
-
-/* create MIME Header for Gopher Data */
-static void
-gopherMimeCreate(GopherStateData * gopherState)
-{
-    MemBuf mb;
-
-    memBufDefInit(&mb);
-
-    memBufPrintf(&mb,
-	"HTTP/1.0 200 OK Gatewaying\r\n"
-	"Server: Squid/%s\r\n"
-	"Date: %s\r\n",
-	version_string, mkrfc1123(squid_curtime));
-
-    switch (gopherState->type_id) {
-
-    case GOPHER_DIRECTORY:
-    case GOPHER_INDEX:
-    case GOPHER_HTML:
-    case GOPHER_WWW:
-    case GOPHER_CSO:
-	memBufPrintf(&mb, "Content-Type: text/html\r\n");
-	break;
-    case GOPHER_GIF:
-    case GOPHER_IMAGE:
-    case GOPHER_PLUS_IMAGE:
-	memBufPrintf(&mb, "Content-Type: image/gif\r\n");
-	break;
-    case GOPHER_SOUND:
-    case GOPHER_PLUS_SOUND:
-	memBufPrintf(&mb, "Content-Type: audio/basic\r\n");
-	break;
-    case GOPHER_PLUS_MOVIE:
-	memBufPrintf(&mb, "Content-Type: video/mpeg\r\n");
-	break;
-    case GOPHER_MACBINHEX:
-    case GOPHER_DOSBIN:
-    case GOPHER_UUENCODED:
-    case GOPHER_BIN:
-	/* Rightnow We have no idea what it is. */
-	gopher_mime_content(&mb, gopherState->request, def_gopher_bin);
-	break;
-    case GOPHER_FILE:
-    default:
-	gopher_mime_content(&mb, gopherState->request, def_gopher_text);
-	break;
-    }
-    memBufPrintf(&mb, "\r\n");
-    EBIT_CLR(gopherState->entry->flags, ENTRY_FWD_HDR_WAIT);
-    storeAppend(gopherState->entry, mb.buf, mb.size);
-    memBufClean(&mb);
-}
-
-/* Parse a gopher request into components.  By Anawat. */
-static void
-gopher_request_parse(const request_t * req, char *type_id, char *request)
-{
-    const char *path = strBuf(req->urlpath);
-
-    if (request)
-	request[0] = '\0';
-
-    if (path && (*path == '/'))
-	path++;
-
-    if (!path || !*path) {
-	*type_id = GOPHER_DIRECTORY;
-	return;
-    }
-    *type_id = path[0];
-
-    if (request) {
-	xstrncpy(request, path + 1, MAX_URL);
-	/* convert %xx to char */
-	url_convert_hex(request, 0);
-    }
-}
-
-int
-gopherCachable(const request_t * req)
-{
-    int cachable = 1;
-    char type_id;
-    /* parse to see type */
-    gopher_request_parse(req,
-	&type_id,
-	NULL);
-    switch (type_id) {
-    case GOPHER_INDEX:
-    case GOPHER_CSO:
-    case GOPHER_TELNET:
-    case GOPHER_3270:
-	cachable = 0;
-	break;
-    default:
-	cachable = 1;
-    }
-    return cachable;
-}
-
-static void
-gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring)
-{
-    storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    storeAppendPrintf(e, "<HTML><HEAD><TITLE>");
-    storeAppendPrintf(e, title, substring);
-    storeAppendPrintf(e, "</TITLE>");
-    storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
-    storeAppendPrintf(e, "</HEAD>\n<BODY><H1>");
-    storeAppendPrintf(e, title, substring);
-    storeAppendPrintf(e, "</H1>\n");
-}
-
-static void
-gopherHTMLFooter(StoreEntry * e)
-{
-    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
-    storeAppendPrintf(e, "<ADDRESS>\n");
-    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
-	mkrfc1123(squid_curtime),
-	getMyHostname(),
-	full_appname_string);
-    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
-}
-
-static void
-gopherEndHTML(GopherStateData * gopherState)
-{
-    StoreEntry *e = gopherState->entry;
-    if (!gopherState->data_in) {
-	gopherHTMLHeader(e, "Server Return Nothing", NULL);
-	storeAppendPrintf(e, "<P>The Gopher query resulted in a blank response</P>");
-    } else {
-	storeAppendPrintf(e, "</PRE>\n");
-    }
-    gopherHTMLFooter(e);
-}
-
-
-/* Convert Gopher to HTML */
-/* Borrow part of code from libwww2 came with Mosaic distribution */
-static void
-gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
-{
-    char *pos = inbuf;
-    char *lpos = NULL;
-    char *tline = NULL;
-    LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
-    LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE);
-    String outbuf = StringNull;
-    char *name = NULL;
-    char *selector = NULL;
-    char *host = NULL;
-    char *port = NULL;
-    char *escaped_selector = NULL;
-    const char *icon_url = NULL;
-    char gtype;
-    StoreEntry *entry = NULL;
-
-    memset(tmpbuf, '\0', TEMP_BUF_SIZE);
-    memset(line, '\0', TEMP_BUF_SIZE);
-
-    entry = gopherState->entry;
-
-    if (gopherState->conversion == HTML_INDEX_PAGE) {
-	char *html_url = html_quote(storeUrl(entry));
-	gopherHTMLHeader(entry, "Gopher Index %s", html_url);
-	storeAppendPrintf(entry,
-	    "<p>This is a searchable Gopher index. Use the search\n"
-	    "function of your browser to enter search terms.\n"
-	    "<ISINDEX>\n");
-	gopherHTMLFooter(entry);
-	/* now let start sending stuff to client */
-	storeBufferFlush(entry);
-	gopherState->data_in = 1;
-
-	return;
-    }
-    if (gopherState->conversion == HTML_CSO_PAGE) {
-	char *html_url = html_quote(storeUrl(entry));
-	gopherHTMLHeader(entry, "CSO Search of %s", html_url);
-	storeAppendPrintf(entry,
-	    "<P>A CSO database usually contains a phonebook or\n"
-	    "directory.  Use the search function of your browser to enter\n"
-	    "search terms.</P><ISINDEX>\n");
-	gopherHTMLFooter(entry);
-	/* now let start sending stuff to client */
-	storeBufferFlush(entry);
-	gopherState->data_in = 1;
-
-	return;
-    }
-    inbuf[len] = '\0';
-
-    if (!gopherState->HTML_header_added) {
-	if (gopherState->conversion == HTML_CSO_RESULT)
-	    gopherHTMLHeader(entry, "CSO Search Result", NULL);
-	else
-	    gopherHTMLHeader(entry, "Gopher Menu", NULL);
-	strCat(outbuf, "<PRE>");
-	gopherState->HTML_header_added = 1;
-    }
-    while ((pos != NULL) && (pos < inbuf + len)) {
-
-	if (gopherState->len != 0) {
-	    /* there is something left from last tx. */
-	    xstrncpy(line, gopherState->buf, gopherState->len + 1);
-	    lpos = (char *) memccpy(line + gopherState->len, inbuf, '\n', len);
-	    if (lpos)
-		*lpos = '\0';
-	    else {
-		/* there is no complete line in inbuf */
-		/* copy it to temp buffer */
-		if (gopherState->len + len > TEMP_BUF_SIZE) {
-		    debug(10, 1) ("GopherHTML: Buffer overflow. Lost some data on URL: %s\n",
-			storeUrl(entry));
-		    len = TEMP_BUF_SIZE - gopherState->len;
-		}
-		xmemcpy(gopherState->buf + gopherState->len, inbuf, len);
-		gopherState->len += len;
-		return;
-	    }
-
-	    /* skip one line */
-	    pos = (char *) memchr(pos, '\n', len);
-	    if (pos)
-		pos++;
-
-	    /* we're done with the remain from last tx. */
-	    gopherState->len = 0;
-	    *(gopherState->buf) = '\0';
-	} else {
-
-	    lpos = (char *) memccpy(line, pos, '\n', len - (pos - inbuf));
-	    if (lpos)
-		*lpos = '\0';
-	    else {
-		/* there is no complete line in inbuf */
-		/* copy it to temp buffer */
-		if ((len - (pos - inbuf)) > TEMP_BUF_SIZE) {
-		    debug(10, 1) ("GopherHTML: Buffer overflow. Lost some data on URL: %s\n",
-			storeUrl(entry));
-		    len = TEMP_BUF_SIZE;
-		}
-		if (len > (pos - inbuf)) {
-		    xmemcpy(gopherState->buf, pos, len - (pos - inbuf));
-		    gopherState->len = len - (pos - inbuf);
-		}
-		break;
-	    }
-
-	    /* skip one line */
-	    pos = (char *) memchr(pos, '\n', len);
-	    if (pos)
-		pos++;
-
-	}
-
-	/* at this point. We should have one line in buffer to process */
-
-	if (*line == '.') {
-	    /* skip it */
-	    memset(line, '\0', TEMP_BUF_SIZE);
-	    continue;
-	}
-	switch (gopherState->conversion) {
-
-	case HTML_INDEX_RESULT:
-	case HTML_DIR:{
-		tline = line;
-		gtype = *tline++;
-		name = tline;
-		selector = strchr(tline, TAB);
-		if (selector) {
-		    *selector++ = '\0';
-		    host = strchr(selector, TAB);
-		    if (host) {
-			*host++ = '\0';
-			port = strchr(host, TAB);
-			if (port) {
-			    char *junk;
-			    port[0] = ':';
-			    junk = strchr(host, TAB);
-			    if (junk)
-				*junk++ = 0;	/* Chop port */
-			    else {
-				junk = strchr(host, '\r');
-				if (junk)
-				    *junk++ = 0;	/* Chop port */
-				else {
-				    junk = strchr(host, '\n');
-				    if (junk)
-					*junk++ = 0;	/* Chop port */
-				}
-			    }
-			    if ((port[1] == '0') && (!port[2]))
-				port[0] = 0;	/* 0 means none */
-			}
-			/* escape a selector here */
-			escaped_selector = xstrdup(rfc1738_escape_part(selector));
-
-			switch (gtype) {
-			case GOPHER_DIRECTORY:
-			    icon_url = mimeGetIconURL("internal-menu");
-			    break;
-			case GOPHER_HTML:
-			case GOPHER_FILE:
-			    icon_url = mimeGetIconURL("internal-text");
-			    break;
-			case GOPHER_INDEX:
-			case GOPHER_CSO:
-			    icon_url = mimeGetIconURL("internal-index");
-			    break;
-			case GOPHER_IMAGE:
-			case GOPHER_GIF:
-			case GOPHER_PLUS_IMAGE:
-			    icon_url = mimeGetIconURL("internal-image");
-			    break;
-			case GOPHER_SOUND:
-			case GOPHER_PLUS_SOUND:
-			    icon_url = mimeGetIconURL("internal-sound");
-			    break;
-			case GOPHER_PLUS_MOVIE:
-			    icon_url = mimeGetIconURL("internal-movie");
-			    break;
-			case GOPHER_TELNET:
-			case GOPHER_3270:
-			    icon_url = mimeGetIconURL("internal-telnet");
-			    break;
-			case GOPHER_BIN:
-			case GOPHER_MACBINHEX:
-			case GOPHER_DOSBIN:
-			case GOPHER_UUENCODED:
-			    icon_url = mimeGetIconURL("internal-binary");
-			    break;
-			case GOPHER_INFO:
-			    icon_url = NULL;
-			    break;
-			default:
-			    icon_url = mimeGetIconURL("internal-unknown");
-			    break;
-			}
-
-			memset(tmpbuf, '\0', TEMP_BUF_SIZE);
-			if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
-			    if (strlen(escaped_selector) != 0)
-				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
-				    icon_url, escaped_selector, rfc1738_escape_part(host),
-				    *port ? ":" : "", port, html_quote(name));
-			    else
-				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
-				    icon_url, rfc1738_escape_part(host), *port ? ":" : "",
-				    port, html_quote(name));
-
-			} else if (gtype == GOPHER_INFO) {
-			    snprintf(tmpbuf, TEMP_BUF_SIZE, "\t%s\n", html_quote(name));
-			} else {
-			    if (strncmp(selector, "GET /", 5) == 0) {
-				/* WWW link */
-				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
-				    icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
-			    } else {
-				/* Standard link */
-				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
-				    icon_url, host, gtype, escaped_selector, html_quote(name));
-			    }
-			}
-			safe_free(escaped_selector);
-			strCat(outbuf, tmpbuf);
-			gopherState->data_in = 1;
-		    } else {
-			memset(line, '\0', TEMP_BUF_SIZE);
-			continue;
-		    }
-		} else {
-		    memset(line, '\0', TEMP_BUF_SIZE);
-		    continue;
-		}
-		break;
-	    }			/* HTML_DIR, HTML_INDEX_RESULT */
-
-
-	case HTML_CSO_RESULT:{
-		if (line[0] == '-') {
-		    int code, recno;
-		    char *s_code, *s_recno, *result;
-
-		    s_code = strtok(line + 1, ":\n");
-		    s_recno = strtok(NULL, ":\n");
-		    result = strtok(NULL, "\n");
-
-		    if (!result)
-			break;
-
-		    code = atoi(s_code);
-		    recno = atoi(s_recno);
-
-		    if (code != 200)
-			break;
-
-		    if (gopherState->cso_recno != recno) {
-			snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
-			gopherState->cso_recno = recno;
-		    } else {
-			snprintf(tmpbuf, TEMP_BUF_SIZE, "%s\n", html_quote(result));
-		    }
-		    strCat(outbuf, tmpbuf);
-		    gopherState->data_in = 1;
-		    break;
-		} else {
-		    int code;
-		    char *s_code, *result;
-
-		    s_code = strtok(line, ":");
-		    result = strtok(NULL, "\n");
-
-		    if (!result)
-			break;
-
-		    code = atoi(s_code);
-		    switch (code) {
-
-		    case 200:{
-			    /* OK */
-			    /* Do nothing here */
-			    break;
-			}
-
-		    case 102:	/* Number of matches */
-		    case 501:	/* No Match */
-		    case 502:	/* Too Many Matches */
-			{
-			    /* Print the message the server returns */
-			    snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
-			    strCat(outbuf, tmpbuf);
-			    gopherState->data_in = 1;
-			    break;
-			}
-
-
-		    }
-		}
-
-	    }			/* HTML_CSO_RESULT */
-	default:
-	    break;		/* do nothing */
-
-	}			/* switch */
-
-    }				/* while loop */
-
-    if (strLen(outbuf) > 0) {
-	storeAppend(entry, strBuf(outbuf), strLen(outbuf));
-	/* now let start sending stuff to client */
-	storeBufferFlush(entry);
-    }
-    stringClean(&outbuf);
-    return;
-}
-
-static void
-gopherTimeout(int fd, void *data)
-{
-    GopherStateData *gopherState = data;
-    StoreEntry *entry = gopherState->entry;
-    debug(10, 4) ("gopherTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
-    if (entry->store_status == STORE_PENDING) {
-	if (entry->mem_obj->inmem_hi == 0) {
-	    fwdFail(gopherState->fwdState,
-		errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT));
-	}
-    }
-    comm_close(fd);
-}
-
-/* This will be called when data is ready to be read from fd.  Read until
- * error or connection closed. */
-static void
-gopherReadReply(int fd, void *data)
-{
-    GopherStateData *gopherState = data;
-    StoreEntry *entry = gopherState->entry;
-    char *buf = NULL;
-    int len;
-    int clen;
-    int bin;
-    size_t read_sz;
-#if DELAY_POOLS
-    delay_id delay_id = delayMostBytesAllowed(entry->mem_obj);
-#endif
-    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
-	comm_close(fd);
-	return;
-    }
-    errno = 0;
-    buf = memAllocate(MEM_4K_BUF);
-    read_sz = 4096 - 1;		/* leave room for termination */
-#if DELAY_POOLS
-    read_sz = delayBytesWanted(delay_id, 1, read_sz);
-#endif
-    /* leave one space for \0 in gopherToHTML */
-    statCounter.syscalls.sock.reads++;
-    len = FD_READ_METHOD(fd, buf, read_sz);
-    if (len > 0) {
-	fd_bytes(fd, len, FD_READ);
-#if DELAY_POOLS
-	delayBytesIn(delay_id, len);
-#endif
-	kb_incr(&statCounter.server.all.kbytes_in, len);
-	kb_incr(&statCounter.server.other.kbytes_in, len);
-    }
-    debug(10, 5) ("gopherReadReply: FD %d read len=%d\n", fd, len);
-    if (len > 0) {
-	commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
-	IOStats.Gopher.reads++;
-	for (clen = len - 1, bin = 0; clen; bin++)
-	    clen >>= 1;
-	IOStats.Gopher.read_hist[bin]++;
-    }
-    if (len < 0) {
-	debug(50, 1) ("gopherReadReply: error reading: %s\n", xstrerror());
-	if (ignoreErrno(errno)) {
-	    commSetSelect(fd, COMM_SELECT_READ, gopherReadReply, data, 0);
-	} else if (entry->mem_obj->inmem_hi == 0) {
-	    ErrorState *err;
-	    err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
-	    err->xerrno = errno;
-	    err->url = xstrdup(storeUrl(entry));
-	    errorAppendEntry(entry, err);
-	    comm_close(fd);
-	} else {
-	    comm_close(fd);
-	}
-    } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
-	ErrorState *err;
-	err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
-	err->xerrno = errno;
-	err->url = xstrdup(gopherState->request);
-	errorAppendEntry(entry, err);
-	comm_close(fd);
-    } else if (len == 0) {
-	/* Connection closed; retrieval done. */
-	/* flush the rest of data in temp buf if there is one. */
-	if (gopherState->conversion != NORMAL)
-	    gopherEndHTML(data);
-	storeTimestampsSet(entry);
-	storeBufferFlush(entry);
-	fwdComplete(gopherState->fwdState);
-	comm_close(fd);
-    } else {
-	if (gopherState->conversion != NORMAL) {
-	    gopherToHTML(data, buf, len);
-	} else {
-	    storeAppend(entry, buf, len);
-	}
-	commSetSelect(fd,
-	    COMM_SELECT_READ,
-	    gopherReadReply,
-	    data, 0);
-    }
-    memFree(buf, MEM_4K_BUF);
-    return;
-}
-
-/* This will be called when request write is complete. Schedule read of
- * reply. */
-static void
-gopherSendComplete(int fd, char *buf, size_t size, comm_err_t errflag, void *data)
-{
-    GopherStateData *gopherState = (GopherStateData *) data;
-    StoreEntry *entry = gopherState->entry;
-    debug(10, 5) ("gopherSendComplete: FD %d size: %d errflag: %d\n",
-	fd, (int) size, errflag);
-    if (size > 0) {
-	fd_bytes(fd, size, FD_WRITE);
-	kb_incr(&statCounter.server.all.kbytes_out, size);
-	kb_incr(&statCounter.server.other.kbytes_out, size);
-    }
-    if (errflag) {
-	ErrorState *err;
-	err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
-	err->xerrno = errno;
-	err->host = xstrdup(gopherState->req->host);
-	err->port = gopherState->req->port;
-	err->url = xstrdup(storeUrl(entry));
-	errorAppendEntry(entry, err);
-	comm_close(fd);
-	if (buf)
-	    memFree(buf, MEM_4K_BUF);	/* Allocated by gopherSendRequest. */
-	return;
-    }
-    /* 
-     * OK. We successfully reach remote site.  Start MIME typing
-     * stuff.  Do it anyway even though request is not HTML type.
-     */
-    gopherMimeCreate(gopherState);
-    switch (gopherState->type_id) {
-    case GOPHER_DIRECTORY:
-	/* we got to convert it first */
-	storeBuffer(entry);
-	gopherState->conversion = HTML_DIR;
-	gopherState->HTML_header_added = 0;
-	break;
-    case GOPHER_INDEX:
-	/* we got to convert it first */
-	storeBuffer(entry);
-	gopherState->conversion = HTML_INDEX_RESULT;
-	gopherState->HTML_header_added = 0;
-	break;
-    case GOPHER_CSO:
-	/* we got to convert it first */
-	storeBuffer(entry);
-	gopherState->conversion = HTML_CSO_RESULT;
-	gopherState->cso_recno = 0;
-	gopherState->HTML_header_added = 0;
-	break;
-    default:
-	gopherState->conversion = NORMAL;
-    }
-    /* Schedule read reply. */
-    commSetSelect(fd, COMM_SELECT_READ, gopherReadReply, gopherState, 0);
-    commSetDefer(fd, fwdCheckDeferRead, entry);
-    if (buf)
-	memFree(buf, MEM_4K_BUF);	/* Allocated by gopherSendRequest. */
-}
-
-/* This will be called when connect completes. Write request. */
-static void
-gopherSendRequest(int fd, void *data)
-{
-    GopherStateData *gopherState = data;
-    char *buf = memAllocate(MEM_4K_BUF);
-    if (gopherState->type_id == GOPHER_CSO) {
-	const char *t = strchr(gopherState->request, '?');
-	if (t != NULL)
-	    t++;		/* skip the ? */
-	else
-	    t = "";
-	snprintf(buf, 4096, "query %s\r\nquit\r\n", t);
-    } else if (gopherState->type_id == GOPHER_INDEX) {
-	char *t = strchr(gopherState->request, '?');
-	if (t != NULL)
-	    *t = '\t';
-	snprintf(buf, 4096, "%s\r\n", gopherState->request);
-    } else {
-	snprintf(buf, 4096, "%s\r\n", gopherState->request);
-    }
-    debug(10, 5) ("gopherSendRequest: FD %d\n", fd);
-    comm_write(fd,
-	buf,
-	strlen(buf),
-	gopherSendComplete,
-	data,
-	memFree4K);
-    if (EBIT_TEST(gopherState->entry->flags, ENTRY_CACHABLE))
-	storeSetPublicKey(gopherState->entry);	/* Make it public */
-}
-
-CBDATA_TYPE(GopherStateData);
-
-void
-gopherStart(FwdState * fwdState)
-{
-    int fd = fwdState->server_fd;
-    StoreEntry *entry = fwdState->entry;
-    GopherStateData *gopherState;
-    CBDATA_INIT_TYPE(GopherStateData);
-    gopherState = cbdataAlloc(GopherStateData);
-    gopherState->buf = memAllocate(MEM_4K_BUF);
-    storeLockObject(entry);
-    gopherState->entry = entry;
-    gopherState->fwdState = fwdState;
-    debug(10, 3) ("gopherStart: %s\n", storeUrl(entry));
-    statCounter.server.all.requests++;
-    statCounter.server.other.requests++;
-    /* Parse url. */
-    gopher_request_parse(fwdState->request,
-	&gopherState->type_id, gopherState->request);
-#if OLD_PARSE_ERROR_CODE
-    if (...) {
-	ErrorState *err;
-	err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
-	err->url = xstrdup(storeUrl(entry));
-	errorAppendEntry(entry, err);
-	gopherStateFree(-1, gopherState);
-	return;
-    }
-#endif
-    comm_add_close_handler(fd, gopherStateFree, gopherState);
-    if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
-	&& (strchr(gopherState->request, '?') == NULL)) {
-	/* Index URL without query word */
-	/* We have to generate search page back to client. No need for connection */
-	gopherMimeCreate(gopherState);
-	if (gopherState->type_id == GOPHER_INDEX) {
-	    gopherState->conversion = HTML_INDEX_PAGE;
-	} else {
-	    if (gopherState->type_id == GOPHER_CSO) {
-		gopherState->conversion = HTML_CSO_PAGE;
-	    } else {
-		gopherState->conversion = HTML_INDEX_PAGE;
-	    }
-	}
-	gopherToHTML(gopherState, (char *) NULL, 0);
-	fwdComplete(fwdState);
-	comm_close(fd);
-	return;
-    }
-    gopherState->fd = fd;
-    gopherState->fwdState = fwdState;
-    commSetSelect(fd, COMM_SELECT_WRITE, gopherSendRequest, gopherState, 0);
-    commSetTimeout(fd, Config.Timeout.read, gopherTimeout, gopherState);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/gopher.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,852 @@
+
+/*
+ * $Id: gopher.cc,v 1.1.2.1 2002/10/11 15:40:50 rbcollins Exp $
+ *
+ * DEBUG: section 10    Gopher
+ * AUTHOR: Harvest Derived
+ *
+ * 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"
+#include "Store.h"
+
+/* gopher type code from rfc. Anawat. */
+#define GOPHER_FILE         '0'
+#define GOPHER_DIRECTORY    '1'
+#define GOPHER_CSO          '2'
+#define GOPHER_ERROR        '3'
+#define GOPHER_MACBINHEX    '4'
+#define GOPHER_DOSBIN       '5'
+#define GOPHER_UUENCODED    '6'
+#define GOPHER_INDEX        '7'
+#define GOPHER_TELNET       '8'
+#define GOPHER_BIN          '9'
+#define GOPHER_REDUNT       '+'
+#define GOPHER_3270         'T'
+#define GOPHER_GIF          'g'
+#define GOPHER_IMAGE        'I'
+
+#define GOPHER_HTML         'h'	/* HTML */
+#define GOPHER_INFO         'i'
+#define GOPHER_WWW          'w'	/* W3 address */
+#define GOPHER_SOUND        's'
+
+#define GOPHER_PLUS_IMAGE   ':'
+#define GOPHER_PLUS_MOVIE   ';'
+#define GOPHER_PLUS_SOUND   '<'
+
+#define GOPHER_PORT         70
+
+#define TAB                 '\t'
+#define TEMP_BUF_SIZE       4096
+#define MAX_CSO_RESULT      1024
+
+typedef struct gopher_ds {
+    StoreEntry *entry;
+    enum {
+	NORMAL,
+	HTML_DIR,
+	HTML_INDEX_RESULT,
+	HTML_CSO_RESULT,
+	HTML_INDEX_PAGE,
+	HTML_CSO_PAGE
+    } conversion;
+    int HTML_header_added;
+    char type_id;
+    char request[MAX_URL];
+    int data_in;
+    int cso_recno;
+    int len;
+    char *buf;			/* pts to a 4k page */
+    int fd;
+    request_t *req;
+    FwdState *fwdState;
+} GopherStateData;
+
+static PF gopherStateFree;
+static void gopher_mime_content(MemBuf * mb, const char *name, const char *def);
+static void gopherMimeCreate(GopherStateData *);
+static void gopher_request_parse(const request_t * req,
+    char *type_id,
+    char *request);
+static void gopherEndHTML(GopherStateData *);
+static void gopherToHTML(GopherStateData *, char *inbuf, int len);
+static PF gopherTimeout;
+static PF gopherReadReply;
+static CWCB gopherSendComplete;
+static PF gopherSendRequest;
+
+static char def_gopher_bin[] = "www/unknown";
+static char def_gopher_text[] = "text/plain";
+
+static void
+gopherStateFree(int fdnotused, void *data)
+{
+    GopherStateData *gopherState = (GopherStateData *)data;
+    if (gopherState == NULL)
+	return;
+    if (gopherState->entry) {
+	storeUnlockObject(gopherState->entry);
+    }
+    if (gopherState->req) {
+	requestUnlink(gopherState->req);
+    }
+    memFree(gopherState->buf, MEM_4K_BUF);
+    gopherState->buf = NULL;
+    cbdataFree(gopherState);
+}
+
+
+/* figure out content type from file extension */
+static void
+gopher_mime_content(MemBuf * mb, const char *name, const char *def_ctype)
+{
+    char *ctype = mimeGetContentType(name);
+    char *cenc = mimeGetContentEncoding(name);
+    if (cenc)
+	memBufPrintf(mb, "Content-Encoding: %s\r\n", cenc);
+    memBufPrintf(mb, "Content-Type: %s\r\n",
+	ctype ? ctype : def_ctype);
+}
+
+
+
+/* create MIME Header for Gopher Data */
+static void
+gopherMimeCreate(GopherStateData * gopherState)
+{
+    MemBuf mb;
+
+    memBufDefInit(&mb);
+
+    memBufPrintf(&mb,
+	"HTTP/1.0 200 OK Gatewaying\r\n"
+	"Server: Squid/%s\r\n"
+	"Date: %s\r\n",
+	version_string, mkrfc1123(squid_curtime));
+
+    switch (gopherState->type_id) {
+
+    case GOPHER_DIRECTORY:
+    case GOPHER_INDEX:
+    case GOPHER_HTML:
+    case GOPHER_WWW:
+    case GOPHER_CSO:
+	memBufPrintf(&mb, "Content-Type: text/html\r\n");
+	break;
+    case GOPHER_GIF:
+    case GOPHER_IMAGE:
+    case GOPHER_PLUS_IMAGE:
+	memBufPrintf(&mb, "Content-Type: image/gif\r\n");
+	break;
+    case GOPHER_SOUND:
+    case GOPHER_PLUS_SOUND:
+	memBufPrintf(&mb, "Content-Type: audio/basic\r\n");
+	break;
+    case GOPHER_PLUS_MOVIE:
+	memBufPrintf(&mb, "Content-Type: video/mpeg\r\n");
+	break;
+    case GOPHER_MACBINHEX:
+    case GOPHER_DOSBIN:
+    case GOPHER_UUENCODED:
+    case GOPHER_BIN:
+	/* Rightnow We have no idea what it is. */
+	gopher_mime_content(&mb, gopherState->request, def_gopher_bin);
+	break;
+    case GOPHER_FILE:
+    default:
+	gopher_mime_content(&mb, gopherState->request, def_gopher_text);
+	break;
+    }
+    memBufPrintf(&mb, "\r\n");
+    EBIT_CLR(gopherState->entry->flags, ENTRY_FWD_HDR_WAIT);
+    storeAppend(gopherState->entry, mb.buf, mb.size);
+    memBufClean(&mb);
+}
+
+/* Parse a gopher request into components.  By Anawat. */
+static void
+gopher_request_parse(const request_t * req, char *type_id, char *request)
+{
+    const char *path = strBuf(req->urlpath);
+
+    if (request)
+	request[0] = '\0';
+
+    if (path && (*path == '/'))
+	path++;
+
+    if (!path || !*path) {
+	*type_id = GOPHER_DIRECTORY;
+	return;
+    }
+    *type_id = path[0];
+
+    if (request) {
+	xstrncpy(request, path + 1, MAX_URL);
+	/* convert %xx to char */
+	url_convert_hex(request, 0);
+    }
+}
+
+int
+gopherCachable(const request_t * req)
+{
+    int cachable = 1;
+    char type_id;
+    /* parse to see type */
+    gopher_request_parse(req,
+	&type_id,
+	NULL);
+    switch (type_id) {
+    case GOPHER_INDEX:
+    case GOPHER_CSO:
+    case GOPHER_TELNET:
+    case GOPHER_3270:
+	cachable = 0;
+	break;
+    default:
+	cachable = 1;
+    }
+    return cachable;
+}
+
+static void
+gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring)
+{
+    storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
+    storeAppendPrintf(e, "<HTML><HEAD><TITLE>");
+    storeAppendPrintf(e, title, substring);
+    storeAppendPrintf(e, "</TITLE>");
+    storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
+    storeAppendPrintf(e, "</HEAD>\n<BODY><H1>");
+    storeAppendPrintf(e, title, substring);
+    storeAppendPrintf(e, "</H1>\n");
+}
+
+static void
+gopherHTMLFooter(StoreEntry * e)
+{
+    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
+    storeAppendPrintf(e, "<ADDRESS>\n");
+    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
+	mkrfc1123(squid_curtime),
+	getMyHostname(),
+	full_appname_string);
+    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
+}
+
+static void
+gopherEndHTML(GopherStateData * gopherState)
+{
+    StoreEntry *e = gopherState->entry;
+    if (!gopherState->data_in) {
+	gopherHTMLHeader(e, "Server Return Nothing", NULL);
+	storeAppendPrintf(e, "<P>The Gopher query resulted in a blank response</P>");
+    } else {
+	storeAppendPrintf(e, "</PRE>\n");
+    }
+    gopherHTMLFooter(e);
+}
+
+
+/* Convert Gopher to HTML */
+/* Borrow part of code from libwww2 came with Mosaic distribution */
+static void
+gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
+{
+    char *pos = inbuf;
+    char *lpos = NULL;
+    char *tline = NULL;
+    LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
+    LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE);
+    String outbuf = StringNull;
+    char *name = NULL;
+    char *selector = NULL;
+    char *host = NULL;
+    char *port = NULL;
+    char *escaped_selector = NULL;
+    const char *icon_url = NULL;
+    char gtype;
+    StoreEntry *entry = NULL;
+
+    memset(tmpbuf, '\0', TEMP_BUF_SIZE);
+    memset(line, '\0', TEMP_BUF_SIZE);
+
+    entry = gopherState->entry;
+
+    if (gopherState->conversion == gopher_ds::HTML_INDEX_PAGE) {
+	char *html_url = html_quote(storeUrl(entry));
+	gopherHTMLHeader(entry, "Gopher Index %s", html_url);
+	storeAppendPrintf(entry,
+	    "<p>This is a searchable Gopher index. Use the search\n"
+	    "function of your browser to enter search terms.\n"
+	    "<ISINDEX>\n");
+	gopherHTMLFooter(entry);
+	/* now let start sending stuff to client */
+	storeBufferFlush(entry);
+	gopherState->data_in = 1;
+
+	return;
+    }
+    if (gopherState->conversion == gopher_ds::HTML_CSO_PAGE) {
+	char *html_url = html_quote(storeUrl(entry));
+	gopherHTMLHeader(entry, "CSO Search of %s", html_url);
+	storeAppendPrintf(entry,
+	    "<P>A CSO database usually contains a phonebook or\n"
+	    "directory.  Use the search function of your browser to enter\n"
+	    "search terms.</P><ISINDEX>\n");
+	gopherHTMLFooter(entry);
+	/* now let start sending stuff to client */
+	storeBufferFlush(entry);
+	gopherState->data_in = 1;
+
+	return;
+    }
+    inbuf[len] = '\0';
+
+    if (!gopherState->HTML_header_added) {
+	if (gopherState->conversion == gopher_ds::HTML_CSO_RESULT)
+	    gopherHTMLHeader(entry, "CSO Search Result", NULL);
+	else
+	    gopherHTMLHeader(entry, "Gopher Menu", NULL);
+	strCat(outbuf, "<PRE>");
+	gopherState->HTML_header_added = 1;
+    }
+    while ((pos != NULL) && (pos < inbuf + len)) {
+
+	if (gopherState->len != 0) {
+	    /* there is something left from last tx. */
+	    xstrncpy(line, gopherState->buf, gopherState->len + 1);
+	    lpos = (char *) memccpy(line + gopherState->len, inbuf, '\n', len);
+	    if (lpos)
+		*lpos = '\0';
+	    else {
+		/* there is no complete line in inbuf */
+		/* copy it to temp buffer */
+		if (gopherState->len + len > TEMP_BUF_SIZE) {
+		    debug(10, 1) ("GopherHTML: Buffer overflow. Lost some data on URL: %s\n",
+			storeUrl(entry));
+		    len = TEMP_BUF_SIZE - gopherState->len;
+		}
+		xmemcpy(gopherState->buf + gopherState->len, inbuf, len);
+		gopherState->len += len;
+		return;
+	    }
+
+	    /* skip one line */
+	    pos = (char *) memchr(pos, '\n', len);
+	    if (pos)
+		pos++;
+
+	    /* we're done with the remain from last tx. */
+	    gopherState->len = 0;
+	    *(gopherState->buf) = '\0';
+	} else {
+
+	    lpos = (char *) memccpy(line, pos, '\n', len - (pos - inbuf));
+	    if (lpos)
+		*lpos = '\0';
+	    else {
+		/* there is no complete line in inbuf */
+		/* copy it to temp buffer */
+		if ((len - (pos - inbuf)) > TEMP_BUF_SIZE) {
+		    debug(10, 1) ("GopherHTML: Buffer overflow. Lost some data on URL: %s\n",
+			storeUrl(entry));
+		    len = TEMP_BUF_SIZE;
+		}
+		if (len > (pos - inbuf)) {
+		    xmemcpy(gopherState->buf, pos, len - (pos - inbuf));
+		    gopherState->len = len - (pos - inbuf);
+		}
+		break;
+	    }
+
+	    /* skip one line */
+	    pos = (char *) memchr(pos, '\n', len);
+	    if (pos)
+		pos++;
+
+	}
+
+	/* at this point. We should have one line in buffer to process */
+
+	if (*line == '.') {
+	    /* skip it */
+	    memset(line, '\0', TEMP_BUF_SIZE);
+	    continue;
+	}
+	switch (gopherState->conversion) {
+
+	case gopher_ds::HTML_INDEX_RESULT:
+	case gopher_ds::HTML_DIR:{
+		tline = line;
+		gtype = *tline++;
+		name = tline;
+		selector = strchr(tline, TAB);
+		if (selector) {
+		    *selector++ = '\0';
+		    host = strchr(selector, TAB);
+		    if (host) {
+			*host++ = '\0';
+			port = strchr(host, TAB);
+			if (port) {
+			    char *junk;
+			    port[0] = ':';
+			    junk = strchr(host, TAB);
+			    if (junk)
+				*junk++ = 0;	/* Chop port */
+			    else {
+				junk = strchr(host, '\r');
+				if (junk)
+				    *junk++ = 0;	/* Chop port */
+				else {
+				    junk = strchr(host, '\n');
+				    if (junk)
+					*junk++ = 0;	/* Chop port */
+				}
+			    }
+			    if ((port[1] == '0') && (!port[2]))
+				port[0] = 0;	/* 0 means none */
+			}
+			/* escape a selector here */
+			escaped_selector = xstrdup(rfc1738_escape_part(selector));
+
+			switch (gtype) {
+			case GOPHER_DIRECTORY:
+			    icon_url = mimeGetIconURL("internal-menu");
+			    break;
+			case GOPHER_HTML:
+			case GOPHER_FILE:
+			    icon_url = mimeGetIconURL("internal-text");
+			    break;
+			case GOPHER_INDEX:
+			case GOPHER_CSO:
+			    icon_url = mimeGetIconURL("internal-index");
+			    break;
+			case GOPHER_IMAGE:
+			case GOPHER_GIF:
+			case GOPHER_PLUS_IMAGE:
+			    icon_url = mimeGetIconURL("internal-image");
+			    break;
+			case GOPHER_SOUND:
+			case GOPHER_PLUS_SOUND:
+			    icon_url = mimeGetIconURL("internal-sound");
+			    break;
+			case GOPHER_PLUS_MOVIE:
+			    icon_url = mimeGetIconURL("internal-movie");
+			    break;
+			case GOPHER_TELNET:
+			case GOPHER_3270:
+			    icon_url = mimeGetIconURL("internal-telnet");
+			    break;
+			case GOPHER_BIN:
+			case GOPHER_MACBINHEX:
+			case GOPHER_DOSBIN:
+			case GOPHER_UUENCODED:
+			    icon_url = mimeGetIconURL("internal-binary");
+			    break;
+			case GOPHER_INFO:
+			    icon_url = NULL;
+			    break;
+			default:
+			    icon_url = mimeGetIconURL("internal-unknown");
+			    break;
+			}
+
+			memset(tmpbuf, '\0', TEMP_BUF_SIZE);
+			if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
+			    if (strlen(escaped_selector) != 0)
+				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
+				    icon_url, escaped_selector, rfc1738_escape_part(host),
+				    *port ? ":" : "", port, html_quote(name));
+			    else
+				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
+				    icon_url, rfc1738_escape_part(host), *port ? ":" : "",
+				    port, html_quote(name));
+
+			} else if (gtype == GOPHER_INFO) {
+			    snprintf(tmpbuf, TEMP_BUF_SIZE, "\t%s\n", html_quote(name));
+			} else {
+			    if (strncmp(selector, "GET /", 5) == 0) {
+				/* WWW link */
+				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
+				    icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
+			    } else {
+				/* Standard link */
+				snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
+				    icon_url, host, gtype, escaped_selector, html_quote(name));
+			    }
+			}
+			safe_free(escaped_selector);
+			strCat(outbuf, tmpbuf);
+			gopherState->data_in = 1;
+		    } else {
+			memset(line, '\0', TEMP_BUF_SIZE);
+			continue;
+		    }
+		} else {
+		    memset(line, '\0', TEMP_BUF_SIZE);
+		    continue;
+		}
+		break;
+	    }			/* HTML_DIR, HTML_INDEX_RESULT */
+
+
+	case gopher_ds::HTML_CSO_RESULT:{
+		if (line[0] == '-') {
+		    int code, recno;
+		    char *s_code, *s_recno, *result;
+
+		    s_code = strtok(line + 1, ":\n");
+		    s_recno = strtok(NULL, ":\n");
+		    result = strtok(NULL, "\n");
+
+		    if (!result)
+			break;
+
+		    code = atoi(s_code);
+		    recno = atoi(s_recno);
+
+		    if (code != 200)
+			break;
+
+		    if (gopherState->cso_recno != recno) {
+			snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
+			gopherState->cso_recno = recno;
+		    } else {
+			snprintf(tmpbuf, TEMP_BUF_SIZE, "%s\n", html_quote(result));
+		    }
+		    strCat(outbuf, tmpbuf);
+		    gopherState->data_in = 1;
+		    break;
+		} else {
+		    int code;
+		    char *s_code, *result;
+
+		    s_code = strtok(line, ":");
+		    result = strtok(NULL, "\n");
+
+		    if (!result)
+			break;
+
+		    code = atoi(s_code);
+		    switch (code) {
+
+		    case 200:{
+			    /* OK */
+			    /* Do nothing here */
+			    break;
+			}
+
+		    case 102:	/* Number of matches */
+		    case 501:	/* No Match */
+		    case 502:	/* Too Many Matches */
+			{
+			    /* Print the message the server returns */
+			    snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
+			    strCat(outbuf, tmpbuf);
+			    gopherState->data_in = 1;
+			    break;
+			}
+
+
+		    }
+		}
+
+	    }			/* HTML_CSO_RESULT */
+	default:
+	    break;		/* do nothing */
+
+	}			/* switch */
+
+    }				/* while loop */
+
+    if (strLen(outbuf) > 0) {
+	storeAppend(entry, strBuf(outbuf), strLen(outbuf));
+	/* now let start sending stuff to client */
+	storeBufferFlush(entry);
+    }
+    stringClean(&outbuf);
+    return;
+}
+
+static void
+gopherTimeout(int fd, void *data)
+{
+    GopherStateData *gopherState = (GopherStateData *)data;
+    StoreEntry *entry = gopherState->entry;
+    debug(10, 4) ("gopherTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
+    if (entry->store_status == STORE_PENDING) {
+	if (entry->mem_obj->inmem_hi == 0) {
+	    fwdFail(gopherState->fwdState,
+		errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT));
+	}
+    }
+    comm_close(fd);
+}
+
+/* This will be called when data is ready to be read from fd.  Read until
+ * error or connection closed. */
+static void
+gopherReadReply(int fd, void *data)
+{
+    GopherStateData *gopherState = (GopherStateData *)data;
+    StoreEntry *entry = gopherState->entry;
+    char *buf = NULL;
+    int len;
+    int clen;
+    int bin;
+    size_t read_sz;
+#if DELAY_POOLS
+    delay_id delay_id = delayMostBytesAllowed(entry->mem_obj);
+#endif
+    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+	comm_close(fd);
+	return;
+    }
+    errno = 0;
+    buf = (char *)memAllocate(MEM_4K_BUF);
+    read_sz = 4096 - 1;		/* leave room for termination */
+#if DELAY_POOLS
+    read_sz = delayBytesWanted(delay_id, 1, read_sz);
+#endif
+    /* leave one space for \0 in gopherToHTML */
+    statCounter.syscalls.sock.reads++;
+    len = FD_READ_METHOD(fd, buf, read_sz);
+    if (len > 0) {
+	fd_bytes(fd, len, FD_READ);
+#if DELAY_POOLS
+	delayBytesIn(delay_id, len);
+#endif
+	kb_incr(&statCounter.server.all.kbytes_in, len);
+	kb_incr(&statCounter.server.other.kbytes_in, len);
+    }
+    debug(10, 5) ("gopherReadReply: FD %d read len=%d\n", fd, len);
+    if (len > 0) {
+	commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
+	IOStats.Gopher.reads++;
+	for (clen = len - 1, bin = 0; clen; bin++)
+	    clen >>= 1;
+	IOStats.Gopher.read_hist[bin]++;
+    }
+    if (len < 0) {
+	debug(50, 1) ("gopherReadReply: error reading: %s\n", xstrerror());
+	if (ignoreErrno(errno)) {
+	    commSetSelect(fd, COMM_SELECT_READ, gopherReadReply, gopherState, 0);
+	} else if (entry->mem_obj->inmem_hi == 0) {
+	    ErrorState *err;
+	    err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
+	    err->xerrno = errno;
+	    err->url = xstrdup(storeUrl(entry));
+	    errorAppendEntry(entry, err);
+	    comm_close(fd);
+	} else {
+	    comm_close(fd);
+	}
+    } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
+	ErrorState *err;
+	err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
+	err->xerrno = errno;
+	err->url = xstrdup(gopherState->request);
+	errorAppendEntry(entry, err);
+	comm_close(fd);
+    } else if (len == 0) {
+	/* Connection closed; retrieval done. */
+	/* flush the rest of data in temp buf if there is one. */
+	if (gopherState->conversion != gopher_ds::NORMAL)
+	    gopherEndHTML(gopherState);
+	storeTimestampsSet(entry);
+	storeBufferFlush(entry);
+	fwdComplete(gopherState->fwdState);
+	comm_close(fd);
+    } else {
+	if (gopherState->conversion != gopher_ds::NORMAL) {
+	    gopherToHTML(gopherState, buf, len);
+	} else {
+	    storeAppend(entry, buf, len);
+	}
+	commSetSelect(fd,
+	    COMM_SELECT_READ,
+	    gopherReadReply,
+	    gopherState, 0);
+    }
+    memFree(buf, MEM_4K_BUF);
+    return;
+}
+
+/* This will be called when request write is complete. Schedule read of
+ * reply. */
+static void
+gopherSendComplete(int fd, char *buf, size_t size, comm_err_t errflag, void *data)
+{
+    GopherStateData *gopherState = (GopherStateData *) data;
+    StoreEntry *entry = gopherState->entry;
+    debug(10, 5) ("gopherSendComplete: FD %d size: %d errflag: %d\n",
+	fd, (int) size, errflag);
+    if (size > 0) {
+	fd_bytes(fd, size, FD_WRITE);
+	kb_incr(&statCounter.server.all.kbytes_out, size);
+	kb_incr(&statCounter.server.other.kbytes_out, size);
+    }
+    if (errflag) {
+	ErrorState *err;
+	err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
+	err->xerrno = errno;
+	err->host = xstrdup(gopherState->req->host);
+	err->port = gopherState->req->port;
+	err->url = xstrdup(storeUrl(entry));
+	errorAppendEntry(entry, err);
+	comm_close(fd);
+	if (buf)
+	    memFree(buf, MEM_4K_BUF);	/* Allocated by gopherSendRequest. */
+	return;
+    }
+    /* 
+     * OK. We successfully reach remote site.  Start MIME typing
+     * stuff.  Do it anyway even though request is not HTML type.
+     */
+    gopherMimeCreate(gopherState);
+    switch (gopherState->type_id) {
+    case GOPHER_DIRECTORY:
+	/* we got to convert it first */
+	storeBuffer(entry);
+	gopherState->conversion = gopher_ds::HTML_DIR;
+	gopherState->HTML_header_added = 0;
+	break;
+    case GOPHER_INDEX:
+	/* we got to convert it first */
+	storeBuffer(entry);
+	gopherState->conversion = gopher_ds::HTML_INDEX_RESULT;
+	gopherState->HTML_header_added = 0;
+	break;
+    case GOPHER_CSO:
+	/* we got to convert it first */
+	storeBuffer(entry);
+	gopherState->conversion = gopher_ds::HTML_CSO_RESULT;
+	gopherState->cso_recno = 0;
+	gopherState->HTML_header_added = 0;
+	break;
+    default:
+	gopherState->conversion = gopher_ds::NORMAL;
+    }
+    /* Schedule read reply. */
+    commSetSelect(fd, COMM_SELECT_READ, gopherReadReply, gopherState, 0);
+    commSetDefer(fd, fwdCheckDeferRead, entry);
+    if (buf)
+	memFree(buf, MEM_4K_BUF);	/* Allocated by gopherSendRequest. */
+}
+
+/* This will be called when connect completes. Write request. */
+static void
+gopherSendRequest(int fd, void *data)
+{
+    GopherStateData *gopherState = (GopherStateData *)data;
+    char *buf = (char *)memAllocate(MEM_4K_BUF);
+    if (gopherState->type_id == GOPHER_CSO) {
+	const char *t = strchr(gopherState->request, '?');
+	if (t != NULL)
+	    t++;		/* skip the ? */
+	else
+	    t = "";
+	snprintf(buf, 4096, "query %s\r\nquit\r\n", t);
+    } else if (gopherState->type_id == GOPHER_INDEX) {
+	char *t = strchr(gopherState->request, '?');
+	if (t != NULL)
+	    *t = '\t';
+	snprintf(buf, 4096, "%s\r\n", gopherState->request);
+    } else {
+	snprintf(buf, 4096, "%s\r\n", gopherState->request);
+    }
+    debug(10, 5) ("gopherSendRequest: FD %d\n", fd);
+    comm_write(fd,
+	buf,
+	strlen(buf),
+	gopherSendComplete,
+	gopherState,
+	memFree4K);
+    if (EBIT_TEST(gopherState->entry->flags, ENTRY_CACHABLE))
+	storeSetPublicKey(gopherState->entry);	/* Make it public */
+}
+
+CBDATA_TYPE(GopherStateData);
+
+void
+gopherStart(FwdState * fwdState)
+{
+    int fd = fwdState->server_fd;
+    StoreEntry *entry = fwdState->entry;
+    GopherStateData *gopherState;
+    CBDATA_INIT_TYPE(GopherStateData);
+    gopherState = cbdataAlloc(GopherStateData);
+    gopherState->buf = (char *)memAllocate(MEM_4K_BUF);
+    storeLockObject(entry);
+    gopherState->entry = entry;
+    gopherState->fwdState = fwdState;
+    debug(10, 3) ("gopherStart: %s\n", storeUrl(entry));
+    statCounter.server.all.requests++;
+    statCounter.server.other.requests++;
+    /* Parse url. */
+    gopher_request_parse(fwdState->request,
+	&gopherState->type_id, gopherState->request);
+#if OLD_PARSE_ERROR_CODE
+    if (...) {
+	ErrorState *err;
+	err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
+	err->url = xstrdup(storeUrl(entry));
+	errorAppendEntry(entry, err);
+	gopherStateFree(-1, gopherState);
+	return;
+    }
+#endif
+    comm_add_close_handler(fd, gopherStateFree, gopherState);
+    if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
+	&& (strchr(gopherState->request, '?') == NULL)) {
+	/* Index URL without query word */
+	/* We have to generate search page back to client. No need for connection */
+	gopherMimeCreate(gopherState);
+	if (gopherState->type_id == GOPHER_INDEX) {
+	    gopherState->conversion = gopher_ds::HTML_INDEX_PAGE;
+	} else {
+	    if (gopherState->type_id == GOPHER_CSO) {
+		gopherState->conversion = gopher_ds::HTML_CSO_PAGE;
+	    } else {
+		gopherState->conversion = gopher_ds::HTML_INDEX_PAGE;
+	    }
+	}
+	gopherToHTML(gopherState, (char *) NULL, 0);
+	fwdComplete(fwdState);
+	comm_close(fd);
+	return;
+    }
+    gopherState->fd = fd;
+    gopherState->fwdState = fwdState;
+    commSetSelect(fd, COMM_SELECT_WRITE, gopherSendRequest, gopherState, 0);
+    commSetTimeout(fd, Config.Timeout.read, gopherTimeout, gopherState);
+}
--- squid/src/helper.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,1132 +0,0 @@
-
-/*
- * $Id: helper.c,v 1.26.2.1 2002/10/04 07:07:25 rbcollins Exp $
- *
- * DEBUG: section 84    Helper process maintenance
- * AUTHOR: Harvest Derived?
- *
- * 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"
-#include "Store.h"
-
-#define HELPER_MAX_ARGS 64
-
-static PF helperHandleRead;
-static PF helperStatefulHandleRead;
-static PF helperServerFree;
-static PF helperStatefulServerFree;
-static void Enqueue(helper * hlp, helper_request *);
-static helper_request *Dequeue(helper * hlp);
-static helper_stateful_request *StatefulDequeue(statefulhelper * hlp);
-static helper_server *GetFirstAvailable(helper * hlp);
-static helper_stateful_server *StatefulGetFirstAvailable(statefulhelper * hlp);
-static void helperDispatch(helper_server * srv, helper_request * r);
-static void helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r);
-static void helperKickQueue(helper * hlp);
-static void helperStatefulKickQueue(statefulhelper * hlp);
-static void helperRequestFree(helper_request * r);
-static void helperStatefulRequestFree(helper_stateful_request * r);
-static void StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r);
-static helper_stateful_request *StatefulServerDequeue(helper_stateful_server * srv);
-static void StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r);
-static void helperStatefulServerKickQueue(helper_stateful_server * srv);
-
-void
-helperOpenServers(helper * hlp)
-{
-    char *s;
-    char *progname;
-    char *shortname;
-    char *procname;
-    const char *args[HELPER_MAX_ARGS];
-    char fd_note_buf[FD_DESC_SZ];
-    helper_server *srv;
-    int nargs = 0;
-    int k;
-    int x;
-    int rfd;
-    int wfd;
-    wordlist *w;
-    if (hlp->cmdline == NULL)
-	return;
-    progname = hlp->cmdline->key;
-    if ((s = strrchr(progname, '/')))
-	shortname = xstrdup(s + 1);
-    else
-	shortname = xstrdup(progname);
-    debug(84, 1) ("helperOpenServers: Starting %d '%s' processes\n",
-	hlp->n_to_start, shortname);
-    procname = xmalloc(strlen(shortname) + 3);
-    snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
-    args[nargs++] = procname;
-    for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
-	args[nargs++] = w->key;
-    args[nargs++] = NULL;
-    assert(nargs <= HELPER_MAX_ARGS);
-    for (k = 0; k < hlp->n_to_start; k++) {
-	getCurrentTime();
-	rfd = wfd = -1;
-	x = ipcCreate(hlp->ipc_type,
-	    progname,
-	    args,
-	    shortname,
-	    &rfd,
-	    &wfd);
-	if (x < 0) {
-	    debug(84, 1) ("WARNING: Cannot run '%s' process.\n", progname);
-	    continue;
-	}
-	hlp->n_running++;
-	srv = cbdataAlloc(helper_server);
-	srv->pid = x;
-	srv->flags.alive = 1;
-	srv->index = k;
-	srv->rfd = rfd;
-	srv->wfd = wfd;
-	srv->buf = memAllocate(MEM_8K_BUF);
-	srv->buf_sz = 8192;
-	srv->offset = 0;
-	srv->parent = cbdataReference(hlp);
-	dlinkAddTail(srv, &srv->link, &hlp->servers);
-	if (rfd == wfd) {
-	    snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
-	    fd_note(rfd, fd_note_buf);
-	} else {
-	    snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
-	    fd_note(rfd, fd_note_buf);
-	    snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
-	    fd_note(wfd, fd_note_buf);
-	}
-	commSetNonBlocking(rfd);
-	if (wfd != rfd)
-	    commSetNonBlocking(wfd);
-	comm_add_close_handler(rfd, helperServerFree, srv);
-    }
-    safe_free(shortname);
-    safe_free(procname);
-    helperKickQueue(hlp);
-}
-
-void
-helperStatefulOpenServers(statefulhelper * hlp)
-{
-    char *s;
-    char *progname;
-    char *shortname;
-    char *procname;
-    const char *args[HELPER_MAX_ARGS];
-    char fd_note_buf[FD_DESC_SZ];
-    helper_stateful_server *srv;
-    int nargs = 0;
-    int k;
-    int x;
-    int rfd;
-    int wfd;
-    wordlist *w;
-    if (hlp->cmdline == NULL)
-	return;
-    progname = hlp->cmdline->key;
-    if ((s = strrchr(progname, '/')))
-	shortname = xstrdup(s + 1);
-    else
-	shortname = xstrdup(progname);
-    debug(84, 1) ("helperStatefulOpenServers: Starting %d '%s' processes\n",
-	hlp->n_to_start, shortname);
-    procname = xmalloc(strlen(shortname) + 3);
-    snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
-    args[nargs++] = procname;
-    for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
-	args[nargs++] = w->key;
-    args[nargs++] = NULL;
-    assert(nargs <= HELPER_MAX_ARGS);
-    for (k = 0; k < hlp->n_to_start; k++) {
-	getCurrentTime();
-	rfd = wfd = -1;
-	x = ipcCreate(hlp->ipc_type,
-	    progname,
-	    args,
-	    shortname,
-	    &rfd,
-	    &wfd);
-	if (x < 0) {
-	    debug(84, 1) ("WARNING: Cannot run '%s' process.\n", progname);
-	    continue;
-	}
-	hlp->n_running++;
-	srv = cbdataAlloc(helper_stateful_server);
-	srv->pid = x;
-	srv->flags.alive = 1;
-	srv->flags.reserved = S_HELPER_FREE;
-	srv->deferred_requests = 0;
-	srv->stats.deferbyfunc = 0;
-	srv->stats.deferbycb = 0;
-	srv->stats.submits = 0;
-	srv->stats.releases = 0;
-	srv->index = k;
-	srv->rfd = rfd;
-	srv->wfd = wfd;
-	srv->buf = memAllocate(MEM_8K_BUF);
-	srv->buf_sz = 8192;
-	srv->offset = 0;
-	srv->parent = cbdataReference(hlp);
-	if (hlp->datapool != NULL)
-	    srv->data = memPoolAlloc(hlp->datapool);
-	dlinkAddTail(srv, &srv->link, &hlp->servers);
-	if (rfd == wfd) {
-	    snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
-	    fd_note(rfd, fd_note_buf);
-	} else {
-	    snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
-	    fd_note(rfd, fd_note_buf);
-	    snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
-	    fd_note(wfd, fd_note_buf);
-	}
-	commSetNonBlocking(rfd);
-	if (wfd != rfd)
-	    commSetNonBlocking(wfd);
-	comm_add_close_handler(rfd, helperStatefulServerFree, srv);
-    }
-    safe_free(shortname);
-    safe_free(procname);
-    helperStatefulKickQueue(hlp);
-}
-
-
-void
-helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data)
-{
-    helper_request *r = memAllocate(MEM_HELPER_REQUEST);
-    helper_server *srv;
-    if (hlp == NULL) {
-	debug(84, 3) ("helperSubmit: hlp == NULL\n");
-	callback(data, NULL);
-	return;
-    }
-    r->callback = callback;
-    r->data = cbdataReference(data);
-    r->buf = xstrdup(buf);
-    if ((srv = GetFirstAvailable(hlp)))
-	helperDispatch(srv, r);
-    else
-	Enqueue(hlp, r);
-    debug(84, 9) ("helperSubmit: %s\n", buf);
-}
-
-/* lastserver = "server last used as part of a deferred or reserved
- * request sequence"
- */
-void
-helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver)
-{
-    helper_stateful_request *r = memAllocate(MEM_HELPER_STATEFUL_REQUEST);
-    helper_stateful_server *srv;
-    if (hlp == NULL) {
-	debug(84, 3) ("helperStatefulSubmit: hlp == NULL\n");
-	callback(data, 0, NULL);
-	return;
-    }
-    r->callback = callback;
-    r->data = cbdataReference(data);
-    if (buf != NULL) {
-	r->buf = xstrdup(buf);
-	r->placeholder = 0;
-    } else {
-	r->buf = NULL;
-	r->placeholder = 1;
-    }
-    if ((buf != NULL) && lastserver) {
-	debug(84, 5) ("StatefulSubmit with lastserver %p\n", lastserver);
-	/* the queue doesn't count for this assert because queued requests
-	 * have already gone through here and been tested.
-	 * It's legal to have deferred_requests == 0 and queue entries 
-	 * and status of S_HELPEER_DEFERRED.
-	 * BUT:  It's not legal to submit a new request w/lastserver in
-	 * that state.
-	 */
-	assert(!(lastserver->deferred_requests == 0 &&
-		lastserver->flags.reserved == S_HELPER_DEFERRED));
-	if (lastserver->flags.reserved != S_HELPER_RESERVED) {
-	    lastserver->stats.submits++;
-	    lastserver->deferred_requests--;
-	}
-	if (!(lastserver->request)) {
-	    debug(84, 5) ("StatefulSubmit dispatching\n");
-	    helperStatefulDispatch(lastserver, r);
-	} else {
-	    debug(84, 5) ("StatefulSubmit queuing\n");
-	    StatefulServerEnqueue(lastserver, r);
-	}
-    } else {
-	if ((srv = StatefulGetFirstAvailable(hlp))) {
-	    helperStatefulDispatch(srv, r);
-	} else
-	    StatefulEnqueue(hlp, r);
-    }
-    debug(84, 9) ("helperStatefulSubmit: placeholder: '%d', buf '%s'.\n", r->placeholder, buf);
-}
-
-helper_stateful_server *
-helperStatefulDefer(statefulhelper * hlp)
-/* find and add a deferred request to a server */
-{
-    dlink_node *n;
-    helper_stateful_server *srv = NULL, *rv = NULL;
-    if (hlp == NULL) {
-	debug(84, 3) ("helperStatefulReserve: hlp == NULL\n");
-	return NULL;
-    }
-    debug(84, 5) ("helperStatefulDefer: Running servers %d.\n", hlp->n_running);
-    if (hlp->n_running == 0) {
-	debug(84, 1) ("helperStatefulDefer: No running servers!. \n");
-	return NULL;
-    }
-    srv = StatefulGetFirstAvailable(hlp);
-    /* all currently busy:loop through servers and find server with the shortest queue */
-    rv = srv;
-    if (rv == NULL)
-	for (n = hlp->servers.head; n != NULL; n = n->next) {
-	    srv = n->data;
-	    if (srv->flags.reserved == S_HELPER_RESERVED)
-		continue;
-	    if (!srv->flags.alive)
-		continue;
-	    if ((hlp->IsAvailable != NULL) && (srv->data != NULL) &&
-		!(hlp->IsAvailable(srv->data)))
-		continue;
-	    if ((rv != NULL) && (rv->deferred_requests < srv->deferred_requests))
-		continue;
-	    rv = srv;
-	}
-    if (rv == NULL) {
-	debug(84, 1) ("helperStatefulDefer: None available.\n");
-	return NULL;
-    }
-    /* consistency check:
-     * when the deferred count is 0,
-     *   submits + releases == deferbyfunc + deferbycb
-     * Or in english, when there are no deferred requests, the amount
-     * we have submitted to the queue or cancelled must equal the amount
-     * we have said we wanted to be able to submit or cancel
-     */
-    if (rv->deferred_requests == 0)
-	assert(rv->stats.submits + rv->stats.releases ==
-	    rv->stats.deferbyfunc + rv->stats.deferbycb);
-
-    rv->flags.reserved = S_HELPER_DEFERRED;
-    rv->deferred_requests++;
-    rv->stats.deferbyfunc++;
-    return rv;
-}
-
-void
-helperStatefulReset(helper_stateful_server * srv)
-/* puts this helper back in the queue. the calling app is required to 
- * manage the state in the helper.
- */
-{
-    statefulhelper *hlp = srv->parent;
-    helper_stateful_request *r;
-    r = srv->request;
-    if (r != NULL) {
-	/* reset attempt DURING an outstaning request */
-	debug(84, 1) ("helperStatefulReset: RESET During request %s \n",
-	    hlp->id_name);
-	srv->flags.busy = 0;
-	srv->offset = 0;
-	helperStatefulRequestFree(r);
-	srv->request = NULL;
-    }
-    srv->flags.busy = 0;
-    if (srv->queue.head) {
-	srv->flags.reserved = S_HELPER_DEFERRED;
-	helperStatefulServerKickQueue(srv);
-    } else {
-	srv->flags.reserved = S_HELPER_FREE;
-	if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
-	    srv->parent->OnEmptyQueue(srv->data);
-	helperStatefulKickQueue(hlp);
-    }
-}
-
-void
-helperStatefulReleaseServer(helper_stateful_server * srv)
-/*decrease the number of 'waiting' clients that set the helper to be DEFERRED */
-{
-    srv->stats.releases++;
-    if (srv->flags.reserved == S_HELPER_DEFERRED) {
-	assert(srv->deferred_requests);
-	srv->deferred_requests--;
-    }
-    if (!(srv->deferred_requests) && (srv->flags.reserved == S_HELPER_DEFERRED) && !(srv->queue.head)) {
-	srv->flags.reserved = S_HELPER_FREE;
-	if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
-	    srv->parent->OnEmptyQueue(srv->data);
-    }
-}
-
-void *
-helperStatefulServerGetData(helper_stateful_server * srv)
-/* return a pointer to the stateful routines data area */
-{
-    return srv->data;
-}
-
-void
-helperStats(StoreEntry * sentry, helper * hlp)
-{
-    helper_server *srv;
-    dlink_node *link;
-    double tt;
-    storeAppendPrintf(sentry, "number running: %d of %d\n",
-	hlp->n_running, hlp->n_to_start);
-    storeAppendPrintf(sentry, "requests sent: %d\n",
-	hlp->stats.requests);
-    storeAppendPrintf(sentry, "replies received: %d\n",
-	hlp->stats.replies);
-    storeAppendPrintf(sentry, "queue length: %d\n",
-	hlp->stats.queue_size);
-    storeAppendPrintf(sentry, "avg service time: %.2f msec\n",
-	(double) hlp->stats.avg_svc_time / 1000.0);
-    storeAppendPrintf(sentry, "\n");
-    storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\n",
-	"#",
-	"FD",
-	"PID",
-	"# Requests",
-	"Flags",
-	"Time",
-	"Offset",
-	"Request");
-    for (link = hlp->servers.head; link; link = link->next) {
-	srv = link->data;
-	tt = 0.001 * tvSubMsec(srv->dispatch_time,
-	    srv->flags.busy ? current_time : srv->answer_time);
-	storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%c%c%c%c\t%7.3f\t%7d\t%s\n",
-	    srv->index + 1,
-	    srv->rfd,
-	    srv->pid,
-	    srv->stats.uses,
-	    srv->flags.alive ? 'A' : ' ',
-	    srv->flags.busy ? 'B' : ' ',
-	    srv->flags.closing ? 'C' : ' ',
-	    srv->flags.shutdown ? 'S' : ' ',
-	    tt < 0.0 ? 0.0 : tt,
-	    (int) srv->offset,
-	    srv->request ? log_quote(srv->request->buf) : "(none)");
-    }
-    storeAppendPrintf(sentry, "\nFlags key:\n\n");
-    storeAppendPrintf(sentry, "   A = ALIVE\n");
-    storeAppendPrintf(sentry, "   B = BUSY\n");
-    storeAppendPrintf(sentry, "   C = CLOSING\n");
-    storeAppendPrintf(sentry, "   S = SHUTDOWN\n");
-}
-
-void
-helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp)
-{
-    helper_stateful_server *srv;
-    dlink_node *link;
-    double tt;
-    storeAppendPrintf(sentry, "number running: %d of %d\n",
-	hlp->n_running, hlp->n_to_start);
-    storeAppendPrintf(sentry, "requests sent: %d\n",
-	hlp->stats.requests);
-    storeAppendPrintf(sentry, "replies received: %d\n",
-	hlp->stats.replies);
-    storeAppendPrintf(sentry, "queue length: %d\n",
-	hlp->stats.queue_size);
-    storeAppendPrintf(sentry, "avg service time: %d msec\n",
-	hlp->stats.avg_svc_time);
-    storeAppendPrintf(sentry, "\n");
-    storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\t%7s\n",
-	"#",
-	"FD",
-	"PID",
-	"# Requests",
-	"# Deferred Requests",
-	"Flags",
-	"Time",
-	"Offset",
-	"Request");
-    for (link = hlp->servers.head; link; link = link->next) {
-	srv = link->data;
-	tt = 0.001 * tvSubMsec(srv->dispatch_time, current_time);
-	storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%11d\t%c%c%c%c%c%c\t%7.3f\t%7d\t%s\n",
-	    srv->index + 1,
-	    srv->rfd,
-	    srv->pid,
-	    srv->stats.uses,
-	    (int) srv->deferred_requests,
-	    srv->flags.alive ? 'A' : ' ',
-	    srv->flags.busy ? 'B' : ' ',
-	    srv->flags.closing ? 'C' : ' ',
-	    srv->flags.reserved != S_HELPER_FREE ? 'R' : ' ',
-	    srv->flags.shutdown ? 'S' : ' ',
-	    srv->request ? (srv->request->placeholder ? 'P' : ' ') : ' ',
-	    tt < 0.0 ? 0.0 : tt,
-	    (int) srv->offset,
-	    srv->request ? log_quote(srv->request->buf) : "(none)");
-    }
-    storeAppendPrintf(sentry, "\nFlags key:\n\n");
-    storeAppendPrintf(sentry, "   A = ALIVE\n");
-    storeAppendPrintf(sentry, "   B = BUSY\n");
-    storeAppendPrintf(sentry, "   C = CLOSING\n");
-    storeAppendPrintf(sentry, "   R = RESERVED or DEFERRED\n");
-    storeAppendPrintf(sentry, "   S = SHUTDOWN\n");
-    storeAppendPrintf(sentry, "   P = PLACEHOLDER\n");
-}
-
-void
-helperShutdown(helper * hlp)
-{
-    dlink_node *link = hlp->servers.head;
-    while (link) {
-	helper_server *srv;
-	srv = link->data;
-	link = link->next;
-	if (!srv->flags.alive) {
-	    debug(34, 3) ("helperShutdown: %s #%d is NOT ALIVE.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	srv->flags.shutdown = 1;	/* request it to shut itself down */
-	if (srv->flags.busy) {
-	    debug(34, 3) ("helperShutdown: %s #%d is BUSY.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	if (srv->flags.closing) {
-	    debug(34, 3) ("helperShutdown: %s #%d is CLOSING.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	srv->flags.closing = 1;
-	/* the rest of the details is dealt with in the helperServerFree
-	 * close handler
-	 */
-	comm_close(srv->rfd);
-    }
-}
-
-void
-helperStatefulShutdown(statefulhelper * hlp)
-{
-    dlink_node *link = hlp->servers.head;
-    helper_stateful_server *srv;
-    while (link) {
-	srv = link->data;
-	link = link->next;
-	if (!srv->flags.alive) {
-	    debug(34, 3) ("helperStatefulShutdown: %s #%d is NOT ALIVE.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	srv->flags.shutdown = 1;	/* request it to shut itself down */
-	if (srv->flags.busy) {
-	    debug(34, 3) ("helperStatefulShutdown: %s #%d is BUSY.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	if (srv->flags.closing) {
-	    debug(34, 3) ("helperStatefulShutdown: %s #%d is CLOSING.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	if (srv->flags.reserved != S_HELPER_FREE) {
-	    debug(34, 3) ("helperStatefulShutdown: %s #%d is RESERVED.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	if (srv->deferred_requests) {
-	    debug(34, 3) ("helperStatefulShutdown: %s #%d has DEFERRED requests.\n",
-		hlp->id_name, srv->index + 1);
-	    continue;
-	}
-	srv->flags.closing = 1;
-	/* the rest of the details is dealt with in the helperStatefulServerFree
-	 * close handler
-	 */
-	comm_close(srv->rfd);
-    }
-}
-
-
-helper *
-helperCreate(const char *name)
-{
-    helper *hlp;
-    hlp = cbdataAlloc(helper);
-    hlp->id_name = name;
-    return hlp;
-}
-
-statefulhelper *
-helperStatefulCreate(const char *name)
-{
-    statefulhelper *hlp;
-    hlp = cbdataAlloc(statefulhelper);
-    hlp->id_name = name;
-    return hlp;
-}
-
-
-void
-helperFree(helper * hlp)
-{
-    if (!hlp)
-	return;
-    /* note, don't free hlp->name, it probably points to static memory */
-    if (hlp->queue.head)
-	debug(84, 0) ("WARNING: freeing %s helper with %d requests queued\n",
-	    hlp->id_name, hlp->stats.queue_size);
-    cbdataFree(hlp);
-}
-
-void
-helperStatefulFree(statefulhelper * hlp)
-{
-    if (!hlp)
-	return;
-    /* note, don't free hlp->name, it probably points to static memory */
-    if (hlp->queue.head)
-	debug(84, 0) ("WARNING: freeing %s helper with %d requests queued\n",
-	    hlp->id_name, hlp->stats.queue_size);
-    cbdataFree(hlp);
-}
-
-
-/* ====================================================================== */
-/* LOCAL FUNCTIONS */
-/* ====================================================================== */
-
-static void
-helperServerFree(int fd, void *data)
-{
-    helper_server *srv = data;
-    helper *hlp = srv->parent;
-    helper_request *r;
-    assert(srv->rfd == fd);
-    if (srv->buf) {
-	memFree(srv->buf, MEM_8K_BUF);
-	srv->buf = NULL;
-    }
-    if ((r = srv->request)) {
-	void *cbdata;
-	if (cbdataReferenceValidDone(r->data, &cbdata))
-	    r->callback(cbdata, srv->buf);
-	helperRequestFree(r);
-	srv->request = NULL;
-    }
-    if (srv->wfd != srv->rfd && srv->wfd != -1)
-	comm_close(srv->wfd);
-    dlinkDelete(&srv->link, &hlp->servers);
-    hlp->n_running--;
-    assert(hlp->n_running >= 0);
-    if (!srv->flags.shutdown) {
-	debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
-	    hlp->id_name, srv->index + 1, fd);
-	if (hlp->n_running < hlp->n_to_start / 2)
-	    fatalf("Too few %s processes are running", hlp->id_name);
-    }
-    cbdataReferenceDone(srv->parent);
-    cbdataFree(srv);
-}
-
-static void
-helperStatefulServerFree(int fd, void *data)
-{
-    helper_stateful_server *srv = data;
-    statefulhelper *hlp = srv->parent;
-    helper_stateful_request *r;
-    assert(srv->rfd == fd);
-    if (srv->buf) {
-	memFree(srv->buf, MEM_8K_BUF);
-	srv->buf = NULL;
-    }
-    if ((r = srv->request)) {
-	void *cbdata;
-	if (cbdataReferenceValidDone(r->data, &cbdata))
-	    r->callback(cbdata, srv, srv->buf);
-	helperStatefulRequestFree(r);
-	srv->request = NULL;
-    }
-    /* TODO: walk the local queue of requests and carry them all out */
-    if (srv->wfd != srv->rfd && srv->wfd != -1)
-	comm_close(srv->wfd);
-    dlinkDelete(&srv->link, &hlp->servers);
-    hlp->n_running--;
-    assert(hlp->n_running >= 0);
-    if (!srv->flags.shutdown) {
-	debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
-	    hlp->id_name, srv->index + 1, fd);
-	if (hlp->n_running < hlp->n_to_start / 2)
-	    fatalf("Too few %s processes are running", hlp->id_name);
-    }
-    if (srv->data != NULL)
-	memPoolFree(hlp->datapool, srv->data);
-    cbdataReferenceDone(srv->parent);
-    cbdataFree(srv);
-}
-
-
-static void
-helperHandleRead(int fd, void *data)
-{
-    int len;
-    char *t = NULL;
-    helper_server *srv = data;
-    helper_request *r;
-    helper *hlp = srv->parent;
-    assert(fd == srv->rfd);
-    assert(cbdataReferenceValid(data));
-    statCounter.syscalls.sock.reads++;
-    len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
-    fd_bytes(fd, len, FD_READ);
-    debug(84, 5) ("helperHandleRead: %d bytes from %s #%d.\n",
-	len, hlp->id_name, srv->index + 1);
-    if (len <= 0) {
-	if (len < 0)
-	    debug(50, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror());
-	comm_close(fd);
-	return;
-    }
-    srv->offset += len;
-    srv->buf[srv->offset] = '\0';
-    r = srv->request;
-    if (r == NULL) {
-	/* someone spoke without being spoken to */
-	debug(84, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes\n",
-	    hlp->id_name, srv->index + 1, len);
-	srv->offset = 0;
-    } else if ((t = strchr(srv->buf, '\n'))) {
-	/* end of reply found */
-	HLPCB *callback;
-	void *cbdata;
-	debug(84, 3) ("helperHandleRead: end of reply found\n");
-	*t = '\0';
-	callback = r->callback;
-	r->callback = NULL;
-	if (cbdataReferenceValidDone(r->data, &cbdata))
-	    callback(cbdata, srv->buf);
-	srv->flags.busy = 0;
-	srv->offset = 0;
-	helperRequestFree(r);
-	srv->request = NULL;
-	hlp->stats.replies++;
-	srv->answer_time = current_time;
-	hlp->stats.avg_svc_time =
-	    intAverage(hlp->stats.avg_svc_time,
-	    tvSubUsec(srv->dispatch_time, current_time),
-	    hlp->stats.replies, REDIRECT_AV_FACTOR);
-	if (srv->flags.shutdown) {
-	    int wfd = srv->wfd;
-	    srv->wfd = -1;
-	    comm_close(wfd);
-	} else
-	    helperKickQueue(hlp);
-    } else {
-	commSetSelect(srv->rfd, COMM_SELECT_READ, helperHandleRead, srv, 0);
-    }
-}
-
-static void
-helperStatefulHandleRead(int fd, void *data)
-{
-    int len;
-    char *t = NULL;
-    helper_stateful_server *srv = data;
-    helper_stateful_request *r;
-    statefulhelper *hlp = srv->parent;
-    assert(fd == srv->rfd);
-    assert(cbdataReferenceValid(data));
-    statCounter.syscalls.sock.reads++;
-    len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
-    fd_bytes(fd, len, FD_READ);
-    debug(84, 5) ("helperStatefulHandleRead: %d bytes from %s #%d.\n",
-	len, hlp->id_name, srv->index + 1);
-    if (len <= 0) {
-	if (len < 0)
-	    debug(50, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror());
-	comm_close(fd);
-	return;
-    }
-    srv->offset += len;
-    srv->buf[srv->offset] = '\0';
-    r = srv->request;
-    if (r == NULL) {
-	/* someone spoke without being spoken to */
-	debug(84, 1) ("helperStatefulHandleRead: unexpected read from %s #%d, %d bytes\n",
-	    hlp->id_name, srv->index + 1, len);
-	srv->offset = 0;
-    } else if ((t = strchr(srv->buf, '\n'))) {
-	/* end of reply found */
-	debug(84, 3) ("helperStatefulHandleRead: end of reply found\n");
-	*t = '\0';
-	if (cbdataReferenceValid(r->data)) {
-	    switch ((r->callback(r->data, srv, srv->buf))) {	/*if non-zero reserve helper */
-	    case S_HELPER_UNKNOWN:
-		fatal("helperStatefulHandleRead: either a non-state aware callback was give to the stateful helper routines, or an uninitialised callback response was recieved.\n");
-		break;
-	    case S_HELPER_RELEASE:	/* helper finished with */
-		if (!srv->deferred_requests && !srv->queue.head) {
-		    srv->flags.reserved = S_HELPER_FREE;
-		    if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
-			srv->parent->OnEmptyQueue(srv->data);
-		    debug(84, 5) ("StatefulHandleRead: releasing %s #%d\n", hlp->id_name, srv->index + 1);
-		} else {
-		    srv->flags.reserved = S_HELPER_DEFERRED;
-		    debug(84, 5) ("StatefulHandleRead: outstanding deferred requests on %s #%d. reserving for deferred requests.\n", hlp->id_name, srv->index + 1);
-		}
-		break;
-	    case S_HELPER_RESERVE:	/* 'pin' this helper for the caller */
-		if (!srv->queue.head) {
-		    assert(srv->deferred_requests == 0);
-		    srv->flags.reserved = S_HELPER_RESERVED;
-		    debug(84, 5) ("StatefulHandleRead: reserving %s #%d\n", hlp->id_name, srv->index + 1);
-		} else {
-		    fatal("StatefulHandleRead: Callback routine attempted to reserve a stateful helper with deferred requests. This can lead to deadlock.\n");
-		}
-		break;
-	    case S_HELPER_DEFER:
-		/* the helper is still needed, but can
-		 * be used for other requests in the meantime.
-		 */
-		srv->flags.reserved = S_HELPER_DEFERRED;
-		srv->deferred_requests++;
-		srv->stats.deferbycb++;
-		debug(84, 5) ("StatefulHandleRead: reserving %s #%d for deferred requests.\n", hlp->id_name, srv->index + 1);
-		break;
-	    default:
-		fatal("helperStatefulHandleRead: unknown stateful helper callback result.\n");
-	    }
-
-	} else {
-	    debug(84, 1) ("StatefulHandleRead: no callback data registered\n");
-	}
-	srv->flags.busy = 0;
-	srv->offset = 0;
-	helperStatefulRequestFree(r);
-	srv->request = NULL;
-	hlp->stats.replies++;
-	hlp->stats.avg_svc_time =
-	    intAverage(hlp->stats.avg_svc_time,
-	    tvSubMsec(srv->dispatch_time, current_time),
-	    hlp->stats.replies, REDIRECT_AV_FACTOR);
-	if (srv->flags.shutdown
-	    && srv->flags.reserved == S_HELPER_FREE
-	    && !srv->deferred_requests) {
-	    int wfd = srv->wfd;
-	    srv->wfd = -1;
-	    comm_close(wfd);
-	} else {
-	    if (srv->queue.head)
-		helperStatefulServerKickQueue(srv);
-	    else
-		helperStatefulKickQueue(hlp);
-	}
-    } else {
-	commSetSelect(srv->rfd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0);
-    }
-}
-
-static void
-Enqueue(helper * hlp, helper_request * r)
-{
-    dlink_node *link = memAllocate(MEM_DLINK_NODE);
-    dlinkAddTail(r, link, &hlp->queue);
-    hlp->stats.queue_size++;
-    if (hlp->stats.queue_size < hlp->n_running)
-	return;
-    if (squid_curtime - hlp->last_queue_warn < 600)
-	return;
-    if (shutting_down || reconfiguring)
-	return;
-    hlp->last_queue_warn = squid_curtime;
-    debug(84, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
-    debug(84, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
-    if (hlp->stats.queue_size > hlp->n_running * 2)
-	fatalf("Too many queued %s requests", hlp->id_name);
-    debug(84, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
-}
-
-static void
-StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r)
-{
-    dlink_node *link = memAllocate(MEM_DLINK_NODE);
-    dlinkAddTail(r, link, &hlp->queue);
-    hlp->stats.queue_size++;
-    if (hlp->stats.queue_size < hlp->n_running)
-	return;
-    if (hlp->stats.queue_size > hlp->n_running * 2)
-	fatalf("Too many queued %s requests", hlp->id_name);
-    if (squid_curtime - hlp->last_queue_warn < 600)
-	return;
-    if (shutting_down || reconfiguring)
-	return;
-    hlp->last_queue_warn = squid_curtime;
-    debug(84, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
-    debug(84, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
-    debug(84, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
-}
-
-static void
-StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r)
-{
-    dlink_node *link = memAllocate(MEM_DLINK_NODE);
-    dlinkAddTail(r, link, &srv->queue);
-/* TODO: warning if the queue on this server is more than X
- * We don't check the queue size at the moment, because
- * requests hitting here are deferrable 
- */
-/*    hlp->stats.queue_size++;
- * if (hlp->stats.queue_size < hlp->n_running)
- * return;
- * if (squid_curtime - hlp->last_queue_warn < 600)
- * return;
- * if (shutting_down || reconfiguring)
- * return;
- * hlp->last_queue_warn = squid_curtime;
- * debug(84, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
- * debug(84, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
- * if (hlp->stats.queue_size > hlp->n_running * 2)
- * fatalf("Too many queued %s requests", hlp->id_name);
- * debug(84, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);  */
-}
-
-
-static helper_request *
-Dequeue(helper * hlp)
-{
-    dlink_node *link;
-    helper_request *r = NULL;
-    if ((link = hlp->queue.head)) {
-	r = link->data;
-	dlinkDelete(link, &hlp->queue);
-	memFree(link, MEM_DLINK_NODE);
-	hlp->stats.queue_size--;
-    }
-    return r;
-}
-
-static helper_stateful_request *
-StatefulServerDequeue(helper_stateful_server * srv)
-{
-    dlink_node *link;
-    helper_stateful_request *r = NULL;
-    if ((link = srv->queue.head)) {
-	r = link->data;
-	dlinkDelete(link, &srv->queue);
-	memFree(link, MEM_DLINK_NODE);
-    }
-    return r;
-}
-
-static helper_stateful_request *
-StatefulDequeue(statefulhelper * hlp)
-{
-    dlink_node *link;
-    helper_stateful_request *r = NULL;
-    if ((link = hlp->queue.head)) {
-	r = link->data;
-	dlinkDelete(link, &hlp->queue);
-	memFree(link, MEM_DLINK_NODE);
-	hlp->stats.queue_size--;
-    }
-    return r;
-}
-
-static helper_server *
-GetFirstAvailable(helper * hlp)
-{
-    dlink_node *n;
-    helper_server *srv = NULL;
-    if (hlp->n_running == 0)
-	return NULL;
-    for (n = hlp->servers.head; n != NULL; n = n->next) {
-	srv = n->data;
-	if (srv->flags.busy)
-	    continue;
-	if (!srv->flags.alive)
-	    continue;
-	return srv;
-    }
-    return NULL;
-}
-
-static helper_stateful_server *
-StatefulGetFirstAvailable(statefulhelper * hlp)
-{
-    dlink_node *n;
-    helper_stateful_server *srv = NULL;
-    debug(84, 5) ("StatefulGetFirstAvailable: Running servers %d.\n", hlp->n_running);
-    if (hlp->n_running == 0)
-	return NULL;
-    for (n = hlp->servers.head; n != NULL; n = n->next) {
-	srv = n->data;
-	if (srv->flags.busy)
-	    continue;
-	if (srv->flags.reserved == S_HELPER_RESERVED)
-	    continue;
-	if (!srv->flags.alive)
-	    continue;
-	if ((hlp->IsAvailable != NULL) && (srv->data != NULL) && !(hlp->IsAvailable(srv->data)))
-	    continue;
-	return srv;
-    }
-    debug(84, 5) ("StatefulGetFirstAvailable: None available.\n");
-    return NULL;
-}
-
-
-static void
-helperDispatch(helper_server * srv, helper_request * r)
-{
-    helper *hlp = srv->parent;
-    if (!cbdataReferenceValid(r->data)) {
-	debug(84, 1) ("helperDispatch: invalid callback data\n");
-	helperRequestFree(r);
-	return;
-    }
-    assert(!srv->flags.busy);
-    srv->flags.busy = 1;
-    srv->request = r;
-    srv->dispatch_time = current_time;
-    comm_write(srv->wfd,
-	r->buf,
-	strlen(r->buf),
-	NULL,			/* Handler */
-	NULL,			/* Handler-data */
-	NULL);			/* free */
-    commSetSelect(srv->rfd,
-	COMM_SELECT_READ,
-	helperHandleRead,
-	srv, 0);
-    debug(84, 5) ("helperDispatch: Request sent to %s #%d, %d bytes\n",
-	hlp->id_name, srv->index + 1, (int) strlen(r->buf));
-    srv->stats.uses++;
-    hlp->stats.requests++;
-}
-
-static void
-helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r)
-{
-    statefulhelper *hlp = srv->parent;
-    if (!cbdataReferenceValid(r->data)) {
-	debug(84, 1) ("helperStatefulDispatch: invalid callback data\n");
-	helperStatefulRequestFree(r);
-	return;
-    }
-    debug(84, 9) ("helperStatefulDispatch busying helper %s #%d\n", hlp->id_name, srv->index + 1);
-    if (r->placeholder == 1) {
-	/* a callback is needed before this request can _use_ a helper. */
-	/* we don't care about releasing/deferring this helper. The request NEVER
-	 * gets to the helper. So we throw away the return code */
-	r->callback(r->data, srv, NULL);
-	/* throw away the placeholder */
-	helperStatefulRequestFree(r);
-	/* and push the queue. Note that the callback may have submitted a new 
-	 * request to the helper which is why we test for the request*/
-	if (srv->request == NULL) {
-	    if (srv->flags.shutdown
-		&& srv->flags.reserved == S_HELPER_FREE
-		&& !srv->deferred_requests) {
-		int wfd = srv->wfd;
-		srv->wfd = -1;
-		comm_close(wfd);
-	    } else {
-		if (srv->queue.head)
-		    helperStatefulServerKickQueue(srv);
-		else
-		    helperStatefulKickQueue(hlp);
-	    }
-	}
-	return;
-    }
-    srv->flags.busy = 1;
-    srv->request = r;
-    srv->dispatch_time = current_time;
-    comm_write(srv->wfd,
-	r->buf,
-	strlen(r->buf),
-	NULL,			/* Handler */
-	NULL,			/* Handler-data */
-	NULL);			/* free */
-    commSetSelect(srv->rfd,
-	COMM_SELECT_READ,
-	helperStatefulHandleRead,
-	srv, 0);
-    debug(84, 5) ("helperStatefulDispatch: Request sent to %s #%d, %d bytes\n",
-	hlp->id_name, srv->index + 1, (int) strlen(r->buf));
-    srv->stats.uses++;
-    hlp->stats.requests++;
-}
-
-
-static void
-helperKickQueue(helper * hlp)
-{
-    helper_request *r;
-    helper_server *srv;
-    while ((srv = GetFirstAvailable(hlp)) && (r = Dequeue(hlp)))
-	helperDispatch(srv, r);
-}
-
-static void
-helperStatefulKickQueue(statefulhelper * hlp)
-{
-    helper_stateful_request *r;
-    helper_stateful_server *srv;
-    while ((srv = StatefulGetFirstAvailable(hlp)) && (r = StatefulDequeue(hlp)))
-	helperStatefulDispatch(srv, r);
-}
-
-static void
-helperStatefulServerKickQueue(helper_stateful_server * srv)
-{
-    helper_stateful_request *r;
-    if ((r = StatefulServerDequeue(srv)))
-	helperStatefulDispatch(srv, r);
-}
-
-static void
-helperRequestFree(helper_request * r)
-{
-    cbdataReferenceDone(r->data);
-    xfree(r->buf);
-    memFree(r, MEM_HELPER_REQUEST);
-}
-
-static void
-helperStatefulRequestFree(helper_stateful_request * r)
-{
-    cbdataReferenceDone(r->data);
-    xfree(r->buf);
-    memFree(r, MEM_HELPER_STATEFUL_REQUEST);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/helper.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,1132 @@
+
+/*
+ * $Id: helper.cc,v 1.1.2.1 2002/10/11 15:40:51 rbcollins Exp $
+ *
+ * DEBUG: section 84    Helper process maintenance
+ * AUTHOR: Harvest Derived?
+ *
+ * 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"
+#include "Store.h"
+
+#define HELPER_MAX_ARGS 64
+
+static PF helperHandleRead;
+static PF helperStatefulHandleRead;
+static PF helperServerFree;
+static PF helperStatefulServerFree;
+static void Enqueue(helper * hlp, helper_request *);
+static helper_request *Dequeue(helper * hlp);
+static helper_stateful_request *StatefulDequeue(statefulhelper * hlp);
+static helper_server *GetFirstAvailable(helper * hlp);
+static helper_stateful_server *StatefulGetFirstAvailable(statefulhelper * hlp);
+static void helperDispatch(helper_server * srv, helper_request * r);
+static void helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r);
+static void helperKickQueue(helper * hlp);
+static void helperStatefulKickQueue(statefulhelper * hlp);
+static void helperRequestFree(helper_request * r);
+static void helperStatefulRequestFree(helper_stateful_request * r);
+static void StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r);
+static helper_stateful_request *StatefulServerDequeue(helper_stateful_server * srv);
+static void StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r);
+static void helperStatefulServerKickQueue(helper_stateful_server * srv);
+
+void
+helperOpenServers(helper * hlp)
+{
+    char *s;
+    char *progname;
+    char *shortname;
+    char *procname;
+    const char *args[HELPER_MAX_ARGS];
+    char fd_note_buf[FD_DESC_SZ];
+    helper_server *srv;
+    int nargs = 0;
+    int k;
+    int x;
+    int rfd;
+    int wfd;
+    wordlist *w;
+    if (hlp->cmdline == NULL)
+	return;
+    progname = hlp->cmdline->key;
+    if ((s = strrchr(progname, '/')))
+	shortname = xstrdup(s + 1);
+    else
+	shortname = xstrdup(progname);
+    debug(84, 1) ("helperOpenServers: Starting %d '%s' processes\n",
+	hlp->n_to_start, shortname);
+    procname = (char *)xmalloc(strlen(shortname) + 3);
+    snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
+    args[nargs++] = procname;
+    for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
+	args[nargs++] = w->key;
+    args[nargs++] = NULL;
+    assert(nargs <= HELPER_MAX_ARGS);
+    for (k = 0; k < hlp->n_to_start; k++) {
+	getCurrentTime();
+	rfd = wfd = -1;
+	x = ipcCreate(hlp->ipc_type,
+	    progname,
+	    args,
+	    shortname,
+	    &rfd,
+	    &wfd);
+	if (x < 0) {
+	    debug(84, 1) ("WARNING: Cannot run '%s' process.\n", progname);
+	    continue;
+	}
+	hlp->n_running++;
+	srv = cbdataAlloc(helper_server);
+	srv->pid = x;
+	srv->flags.alive = 1;
+	srv->index = k;
+	srv->rfd = rfd;
+	srv->wfd = wfd;
+	srv->buf = (char *)memAllocate(MEM_8K_BUF);
+	srv->buf_sz = 8192;
+	srv->offset = 0;
+	srv->parent = cbdataReference(hlp);
+	dlinkAddTail(srv, &srv->link, &hlp->servers);
+	if (rfd == wfd) {
+	    snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
+	    fd_note(rfd, fd_note_buf);
+	} else {
+	    snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
+	    fd_note(rfd, fd_note_buf);
+	    snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
+	    fd_note(wfd, fd_note_buf);
+	}
+	commSetNonBlocking(rfd);
+	if (wfd != rfd)
+	    commSetNonBlocking(wfd);
+	comm_add_close_handler(rfd, helperServerFree, srv);
+    }
+    safe_free(shortname);
+    safe_free(procname);
+    helperKickQueue(hlp);
+}
+
+void
+helperStatefulOpenServers(statefulhelper * hlp)
+{
+    char *s;
+    char *progname;
+    char *shortname;
+    char *procname;
+    const char *args[HELPER_MAX_ARGS];
+    char fd_note_buf[FD_DESC_SZ];
+    helper_stateful_server *srv;
+    int nargs = 0;
+    int k;
+    int x;
+    int rfd;
+    int wfd;
+    wordlist *w;
+    if (hlp->cmdline == NULL)
+	return;
+    progname = hlp->cmdline->key;
+    if ((s = strrchr(progname, '/')))
+	shortname = xstrdup(s + 1);
+    else
+	shortname = xstrdup(progname);
+    debug(84, 1) ("helperStatefulOpenServers: Starting %d '%s' processes\n",
+	hlp->n_to_start, shortname);
+    procname = (char *)xmalloc(strlen(shortname) + 3);
+    snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
+    args[nargs++] = procname;
+    for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
+	args[nargs++] = w->key;
+    args[nargs++] = NULL;
+    assert(nargs <= HELPER_MAX_ARGS);
+    for (k = 0; k < hlp->n_to_start; k++) {
+	getCurrentTime();
+	rfd = wfd = -1;
+	x = ipcCreate(hlp->ipc_type,
+	    progname,
+	    args,
+	    shortname,
+	    &rfd,
+	    &wfd);
+	if (x < 0) {
+	    debug(84, 1) ("WARNING: Cannot run '%s' process.\n", progname);
+	    continue;
+	}
+	hlp->n_running++;
+	srv = cbdataAlloc(helper_stateful_server);
+	srv->pid = x;
+	srv->flags.alive = 1;
+	srv->flags.reserved = S_HELPER_FREE;
+	srv->deferred_requests = 0;
+	srv->stats.deferbyfunc = 0;
+	srv->stats.deferbycb = 0;
+	srv->stats.submits = 0;
+	srv->stats.releases = 0;
+	srv->index = k;
+	srv->rfd = rfd;
+	srv->wfd = wfd;
+	srv->buf = (char *)memAllocate(MEM_8K_BUF);
+	srv->buf_sz = 8192;
+	srv->offset = 0;
+	srv->parent = cbdataReference(hlp);
+	if (hlp->datapool != NULL)
+	    srv->data = memPoolAlloc(hlp->datapool);
+	dlinkAddTail(srv, &srv->link, &hlp->servers);
+	if (rfd == wfd) {
+	    snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
+	    fd_note(rfd, fd_note_buf);
+	} else {
+	    snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
+	    fd_note(rfd, fd_note_buf);
+	    snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
+	    fd_note(wfd, fd_note_buf);
+	}
+	commSetNonBlocking(rfd);
+	if (wfd != rfd)
+	    commSetNonBlocking(wfd);
+	comm_add_close_handler(rfd, helperStatefulServerFree, srv);
+    }
+    safe_free(shortname);
+    safe_free(procname);
+    helperStatefulKickQueue(hlp);
+}
+
+
+void
+helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data)
+{
+    helper_request *r = (helper_request *)memAllocate(MEM_HELPER_REQUEST);
+    helper_server *srv;
+    if (hlp == NULL) {
+	debug(84, 3) ("helperSubmit: hlp == NULL\n");
+	callback(data, NULL);
+	return;
+    }
+    r->callback = callback;
+    r->data = cbdataReference(data);
+    r->buf = xstrdup(buf);
+    if ((srv = GetFirstAvailable(hlp)))
+	helperDispatch(srv, r);
+    else
+	Enqueue(hlp, r);
+    debug(84, 9) ("helperSubmit: %s\n", buf);
+}
+
+/* lastserver = "server last used as part of a deferred or reserved
+ * request sequence"
+ */
+void
+helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver)
+{
+    helper_stateful_request *r = (helper_stateful_request *)memAllocate(MEM_HELPER_STATEFUL_REQUEST);
+    helper_stateful_server *srv;
+    if (hlp == NULL) {
+	debug(84, 3) ("helperStatefulSubmit: hlp == NULL\n");
+	callback(data, 0, NULL);
+	return;
+    }
+    r->callback = callback;
+    r->data = cbdataReference(data);
+    if (buf != NULL) {
+	r->buf = xstrdup(buf);
+	r->placeholder = 0;
+    } else {
+	r->buf = NULL;
+	r->placeholder = 1;
+    }
+    if ((buf != NULL) && lastserver) {
+	debug(84, 5) ("StatefulSubmit with lastserver %p\n", lastserver);
+	/* the queue doesn't count for this assert because queued requests
+	 * have already gone through here and been tested.
+	 * It's legal to have deferred_requests == 0 and queue entries 
+	 * and status of S_HELPEER_DEFERRED.
+	 * BUT:  It's not legal to submit a new request w/lastserver in
+	 * that state.
+	 */
+	assert(!(lastserver->deferred_requests == 0 &&
+		lastserver->flags.reserved == S_HELPER_DEFERRED));
+	if (lastserver->flags.reserved != S_HELPER_RESERVED) {
+	    lastserver->stats.submits++;
+	    lastserver->deferred_requests--;
+	}
+	if (!(lastserver->request)) {
+	    debug(84, 5) ("StatefulSubmit dispatching\n");
+	    helperStatefulDispatch(lastserver, r);
+	} else {
+	    debug(84, 5) ("StatefulSubmit queuing\n");
+	    StatefulServerEnqueue(lastserver, r);
+	}
+    } else {
+	if ((srv = StatefulGetFirstAvailable(hlp))) {
+	    helperStatefulDispatch(srv, r);
+	} else
+	    StatefulEnqueue(hlp, r);
+    }
+    debug(84, 9) ("helperStatefulSubmit: placeholder: '%d', buf '%s'.\n", r->placeholder, buf);
+}
+
+helper_stateful_server *
+helperStatefulDefer(statefulhelper * hlp)
+/* find and add a deferred request to a server */
+{
+    dlink_node *n;
+    helper_stateful_server *srv = NULL, *rv = NULL;
+    if (hlp == NULL) {
+	debug(84, 3) ("helperStatefulReserve: hlp == NULL\n");
+	return NULL;
+    }
+    debug(84, 5) ("helperStatefulDefer: Running servers %d.\n", hlp->n_running);
+    if (hlp->n_running == 0) {
+	debug(84, 1) ("helperStatefulDefer: No running servers!. \n");
+	return NULL;
+    }
+    srv = StatefulGetFirstAvailable(hlp);
+    /* all currently busy:loop through servers and find server with the shortest queue */
+    rv = srv;
+    if (rv == NULL)
+	for (n = hlp->servers.head; n != NULL; n = n->next) {
+	    srv = (helper_stateful_server *)n->data;
+	    if (srv->flags.reserved == S_HELPER_RESERVED)
+		continue;
+	    if (!srv->flags.alive)
+		continue;
+	    if ((hlp->IsAvailable != NULL) && (srv->data != NULL) &&
+		!(hlp->IsAvailable(srv->data)))
+		continue;
+	    if ((rv != NULL) && (rv->deferred_requests < srv->deferred_requests))
+		continue;
+	    rv = srv;
+	}
+    if (rv == NULL) {
+	debug(84, 1) ("helperStatefulDefer: None available.\n");
+	return NULL;
+    }
+    /* consistency check:
+     * when the deferred count is 0,
+     *   submits + releases == deferbyfunc + deferbycb
+     * Or in english, when there are no deferred requests, the amount
+     * we have submitted to the queue or cancelled must equal the amount
+     * we have said we wanted to be able to submit or cancel
+     */
+    if (rv->deferred_requests == 0)
+	assert(rv->stats.submits + rv->stats.releases ==
+	    rv->stats.deferbyfunc + rv->stats.deferbycb);
+
+    rv->flags.reserved = S_HELPER_DEFERRED;
+    rv->deferred_requests++;
+    rv->stats.deferbyfunc++;
+    return rv;
+}
+
+void
+helperStatefulReset(helper_stateful_server * srv)
+/* puts this helper back in the queue. the calling app is required to 
+ * manage the state in the helper.
+ */
+{
+    statefulhelper *hlp = srv->parent;
+    helper_stateful_request *r;
+    r = srv->request;
+    if (r != NULL) {
+	/* reset attempt DURING an outstaning request */
+	debug(84, 1) ("helperStatefulReset: RESET During request %s \n",
+	    hlp->id_name);
+	srv->flags.busy = 0;
+	srv->offset = 0;
+	helperStatefulRequestFree(r);
+	srv->request = NULL;
+    }
+    srv->flags.busy = 0;
+    if (srv->queue.head) {
+	srv->flags.reserved = S_HELPER_DEFERRED;
+	helperStatefulServerKickQueue(srv);
+    } else {
+	srv->flags.reserved = S_HELPER_FREE;
+	if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
+	    srv->parent->OnEmptyQueue(srv->data);
+	helperStatefulKickQueue(hlp);
+    }
+}
+
+void
+helperStatefulReleaseServer(helper_stateful_server * srv)
+/*decrease the number of 'waiting' clients that set the helper to be DEFERRED */
+{
+    srv->stats.releases++;
+    if (srv->flags.reserved == S_HELPER_DEFERRED) {
+	assert(srv->deferred_requests);
+	srv->deferred_requests--;
+    }
+    if (!(srv->deferred_requests) && (srv->flags.reserved == S_HELPER_DEFERRED) && !(srv->queue.head)) {
+	srv->flags.reserved = S_HELPER_FREE;
+	if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
+	    srv->parent->OnEmptyQueue(srv->data);
+    }
+}
+
+void *
+helperStatefulServerGetData(helper_stateful_server * srv)
+/* return a pointer to the stateful routines data area */
+{
+    return srv->data;
+}
+
+void
+helperStats(StoreEntry * sentry, helper * hlp)
+{
+    helper_server *srv;
+    dlink_node *link;
+    double tt;
+    storeAppendPrintf(sentry, "number running: %d of %d\n",
+	hlp->n_running, hlp->n_to_start);
+    storeAppendPrintf(sentry, "requests sent: %d\n",
+	hlp->stats.requests);
+    storeAppendPrintf(sentry, "replies received: %d\n",
+	hlp->stats.replies);
+    storeAppendPrintf(sentry, "queue length: %d\n",
+	hlp->stats.queue_size);
+    storeAppendPrintf(sentry, "avg service time: %.2f msec\n",
+	(double) hlp->stats.avg_svc_time / 1000.0);
+    storeAppendPrintf(sentry, "\n");
+    storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\n",
+	"#",
+	"FD",
+	"PID",
+	"# Requests",
+	"Flags",
+	"Time",
+	"Offset",
+	"Request");
+    for (link = hlp->servers.head; link; link = link->next) {
+	srv = (helper_server*)link->data;
+	tt = 0.001 * tvSubMsec(srv->dispatch_time,
+	    srv->flags.busy ? current_time : srv->answer_time);
+	storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%c%c%c%c\t%7.3f\t%7d\t%s\n",
+	    srv->index + 1,
+	    srv->rfd,
+	    srv->pid,
+	    srv->stats.uses,
+	    srv->flags.alive ? 'A' : ' ',
+	    srv->flags.busy ? 'B' : ' ',
+	    srv->flags.closing ? 'C' : ' ',
+	    srv->flags.shutdown ? 'S' : ' ',
+	    tt < 0.0 ? 0.0 : tt,
+	    (int) srv->offset,
+	    srv->request ? log_quote(srv->request->buf) : "(none)");
+    }
+    storeAppendPrintf(sentry, "\nFlags key:\n\n");
+    storeAppendPrintf(sentry, "   A = ALIVE\n");
+    storeAppendPrintf(sentry, "   B = BUSY\n");
+    storeAppendPrintf(sentry, "   C = CLOSING\n");
+    storeAppendPrintf(sentry, "   S = SHUTDOWN\n");
+}
+
+void
+helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp)
+{
+    helper_stateful_server *srv;
+    dlink_node *link;
+    double tt;
+    storeAppendPrintf(sentry, "number running: %d of %d\n",
+	hlp->n_running, hlp->n_to_start);
+    storeAppendPrintf(sentry, "requests sent: %d\n",
+	hlp->stats.requests);
+    storeAppendPrintf(sentry, "replies received: %d\n",
+	hlp->stats.replies);
+    storeAppendPrintf(sentry, "queue length: %d\n",
+	hlp->stats.queue_size);
+    storeAppendPrintf(sentry, "avg service time: %d msec\n",
+	hlp->stats.avg_svc_time);
+    storeAppendPrintf(sentry, "\n");
+    storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\t%7s\n",
+	"#",
+	"FD",
+	"PID",
+	"# Requests",
+	"# Deferred Requests",
+	"Flags",
+	"Time",
+	"Offset",
+	"Request");
+    for (link = hlp->servers.head; link; link = link->next) {
+	srv = (helper_stateful_server *)link->data;
+	tt = 0.001 * tvSubMsec(srv->dispatch_time, current_time);
+	storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%11d\t%c%c%c%c%c%c\t%7.3f\t%7d\t%s\n",
+	    srv->index + 1,
+	    srv->rfd,
+	    srv->pid,
+	    srv->stats.uses,
+	    (int) srv->deferred_requests,
+	    srv->flags.alive ? 'A' : ' ',
+	    srv->flags.busy ? 'B' : ' ',
+	    srv->flags.closing ? 'C' : ' ',
+	    srv->flags.reserved != S_HELPER_FREE ? 'R' : ' ',
+	    srv->flags.shutdown ? 'S' : ' ',
+	    srv->request ? (srv->request->placeholder ? 'P' : ' ') : ' ',
+	    tt < 0.0 ? 0.0 : tt,
+	    (int) srv->offset,
+	    srv->request ? log_quote(srv->request->buf) : "(none)");
+    }
+    storeAppendPrintf(sentry, "\nFlags key:\n\n");
+    storeAppendPrintf(sentry, "   A = ALIVE\n");
+    storeAppendPrintf(sentry, "   B = BUSY\n");
+    storeAppendPrintf(sentry, "   C = CLOSING\n");
+    storeAppendPrintf(sentry, "   R = RESERVED or DEFERRED\n");
+    storeAppendPrintf(sentry, "   S = SHUTDOWN\n");
+    storeAppendPrintf(sentry, "   P = PLACEHOLDER\n");
+}
+
+void
+helperShutdown(helper * hlp)
+{
+    dlink_node *link = hlp->servers.head;
+    while (link) {
+	helper_server *srv;
+	srv = (helper_server *)link->data;
+	link = link->next;
+	if (!srv->flags.alive) {
+	    debug(34, 3) ("helperShutdown: %s #%d is NOT ALIVE.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	srv->flags.shutdown = 1;	/* request it to shut itself down */
+	if (srv->flags.busy) {
+	    debug(34, 3) ("helperShutdown: %s #%d is BUSY.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	if (srv->flags.closing) {
+	    debug(34, 3) ("helperShutdown: %s #%d is CLOSING.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	srv->flags.closing = 1;
+	/* the rest of the details is dealt with in the helperServerFree
+	 * close handler
+	 */
+	comm_close(srv->rfd);
+    }
+}
+
+void
+helperStatefulShutdown(statefulhelper * hlp)
+{
+    dlink_node *link = hlp->servers.head;
+    helper_stateful_server *srv;
+    while (link) {
+	srv = (helper_stateful_server *)link->data;
+	link = link->next;
+	if (!srv->flags.alive) {
+	    debug(34, 3) ("helperStatefulShutdown: %s #%d is NOT ALIVE.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	srv->flags.shutdown = 1;	/* request it to shut itself down */
+	if (srv->flags.busy) {
+	    debug(34, 3) ("helperStatefulShutdown: %s #%d is BUSY.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	if (srv->flags.closing) {
+	    debug(34, 3) ("helperStatefulShutdown: %s #%d is CLOSING.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	if (srv->flags.reserved != S_HELPER_FREE) {
+	    debug(34, 3) ("helperStatefulShutdown: %s #%d is RESERVED.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	if (srv->deferred_requests) {
+	    debug(34, 3) ("helperStatefulShutdown: %s #%d has DEFERRED requests.\n",
+		hlp->id_name, srv->index + 1);
+	    continue;
+	}
+	srv->flags.closing = 1;
+	/* the rest of the details is dealt with in the helperStatefulServerFree
+	 * close handler
+	 */
+	comm_close(srv->rfd);
+    }
+}
+
+
+helper *
+helperCreate(const char *name)
+{
+    helper *hlp;
+    hlp = cbdataAlloc(helper);
+    hlp->id_name = name;
+    return hlp;
+}
+
+statefulhelper *
+helperStatefulCreate(const char *name)
+{
+    statefulhelper *hlp;
+    hlp = cbdataAlloc(statefulhelper);
+    hlp->id_name = name;
+    return hlp;
+}
+
+
+void
+helperFree(helper * hlp)
+{
+    if (!hlp)
+	return;
+    /* note, don't free hlp->name, it probably points to static memory */
+    if (hlp->queue.head)
+	debug(84, 0) ("WARNING: freeing %s helper with %d requests queued\n",
+	    hlp->id_name, hlp->stats.queue_size);
+    cbdataFree(hlp);
+}
+
+void
+helperStatefulFree(statefulhelper * hlp)
+{
+    if (!hlp)
+	return;
+    /* note, don't free hlp->name, it probably points to static memory */
+    if (hlp->queue.head)
+	debug(84, 0) ("WARNING: freeing %s helper with %d requests queued\n",
+	    hlp->id_name, hlp->stats.queue_size);
+    cbdataFree(hlp);
+}
+
+
+/* ====================================================================== */
+/* LOCAL FUNCTIONS */
+/* ====================================================================== */
+
+static void
+helperServerFree(int fd, void *data)
+{
+    helper_server *srv = (helper_server *)data;
+    helper *hlp = srv->parent;
+    helper_request *r;
+    assert(srv->rfd == fd);
+    if (srv->buf) {
+	memFree(srv->buf, MEM_8K_BUF);
+	srv->buf = NULL;
+    }
+    if ((r = srv->request)) {
+	void *cbdata;
+	if (cbdataReferenceValidDone(r->data, &cbdata))
+	    r->callback(cbdata, srv->buf);
+	helperRequestFree(r);
+	srv->request = NULL;
+    }
+    if (srv->wfd != srv->rfd && srv->wfd != -1)
+	comm_close(srv->wfd);
+    dlinkDelete(&srv->link, &hlp->servers);
+    hlp->n_running--;
+    assert(hlp->n_running >= 0);
+    if (!srv->flags.shutdown) {
+	debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
+	    hlp->id_name, srv->index + 1, fd);
+	if (hlp->n_running < hlp->n_to_start / 2)
+	    fatalf("Too few %s processes are running", hlp->id_name);
+    }
+    cbdataReferenceDone(srv->parent);
+    cbdataFree(srv);
+}
+
+static void
+helperStatefulServerFree(int fd, void *data)
+{
+    helper_stateful_server *srv = (helper_stateful_server *)data;
+    statefulhelper *hlp = srv->parent;
+    helper_stateful_request *r;
+    assert(srv->rfd == fd);
+    if (srv->buf) {
+	memFree(srv->buf, MEM_8K_BUF);
+	srv->buf = NULL;
+    }
+    if ((r = srv->request)) {
+	void *cbdata;
+	if (cbdataReferenceValidDone(r->data, &cbdata))
+	    r->callback(cbdata, srv, srv->buf);
+	helperStatefulRequestFree(r);
+	srv->request = NULL;
+    }
+    /* TODO: walk the local queue of requests and carry them all out */
+    if (srv->wfd != srv->rfd && srv->wfd != -1)
+	comm_close(srv->wfd);
+    dlinkDelete(&srv->link, &hlp->servers);
+    hlp->n_running--;
+    assert(hlp->n_running >= 0);
+    if (!srv->flags.shutdown) {
+	debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
+	    hlp->id_name, srv->index + 1, fd);
+	if (hlp->n_running < hlp->n_to_start / 2)
+	    fatalf("Too few %s processes are running", hlp->id_name);
+    }
+    if (srv->data != NULL)
+	memPoolFree(hlp->datapool, srv->data);
+    cbdataReferenceDone(srv->parent);
+    cbdataFree(srv);
+}
+
+
+static void
+helperHandleRead(int fd, void *data)
+{
+    int len;
+    char *t = NULL;
+    helper_server *srv = (helper_server *)data;
+    helper_request *r;
+    helper *hlp = srv->parent;
+    assert(fd == srv->rfd);
+    assert(cbdataReferenceValid(data));
+    statCounter.syscalls.sock.reads++;
+    len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
+    fd_bytes(fd, len, FD_READ);
+    debug(84, 5) ("helperHandleRead: %d bytes from %s #%d.\n",
+	len, hlp->id_name, srv->index + 1);
+    if (len <= 0) {
+	if (len < 0)
+	    debug(50, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror());
+	comm_close(fd);
+	return;
+    }
+    srv->offset += len;
+    srv->buf[srv->offset] = '\0';
+    r = srv->request;
+    if (r == NULL) {
+	/* someone spoke without being spoken to */
+	debug(84, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes\n",
+	    hlp->id_name, srv->index + 1, len);
+	srv->offset = 0;
+    } else if ((t = strchr(srv->buf, '\n'))) {
+	/* end of reply found */
+	HLPCB *callback;
+	void *cbdata;
+	debug(84, 3) ("helperHandleRead: end of reply found\n");
+	*t = '\0';
+	callback = r->callback;
+	r->callback = NULL;
+	if (cbdataReferenceValidDone(r->data, &cbdata))
+	    callback(cbdata, srv->buf);
+	srv->flags.busy = 0;
+	srv->offset = 0;
+	helperRequestFree(r);
+	srv->request = NULL;
+	hlp->stats.replies++;
+	srv->answer_time = current_time;
+	hlp->stats.avg_svc_time =
+	    intAverage(hlp->stats.avg_svc_time,
+	    tvSubUsec(srv->dispatch_time, current_time),
+	    hlp->stats.replies, REDIRECT_AV_FACTOR);
+	if (srv->flags.shutdown) {
+	    int wfd = srv->wfd;
+	    srv->wfd = -1;
+	    comm_close(wfd);
+	} else
+	    helperKickQueue(hlp);
+    } else {
+	commSetSelect(srv->rfd, COMM_SELECT_READ, helperHandleRead, srv, 0);
+    }
+}
+
+static void
+helperStatefulHandleRead(int fd, void *data)
+{
+    int len;
+    char *t = NULL;
+    helper_stateful_server *srv = (helper_stateful_server *)data;
+    helper_stateful_request *r;
+    statefulhelper *hlp = srv->parent;
+    assert(fd == srv->rfd);
+    assert(cbdataReferenceValid(data));
+    statCounter.syscalls.sock.reads++;
+    len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
+    fd_bytes(fd, len, FD_READ);
+    debug(84, 5) ("helperStatefulHandleRead: %d bytes from %s #%d.\n",
+	len, hlp->id_name, srv->index + 1);
+    if (len <= 0) {
+	if (len < 0)
+	    debug(50, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror());
+	comm_close(fd);
+	return;
+    }
+    srv->offset += len;
+    srv->buf[srv->offset] = '\0';
+    r = srv->request;
+    if (r == NULL) {
+	/* someone spoke without being spoken to */
+	debug(84, 1) ("helperStatefulHandleRead: unexpected read from %s #%d, %d bytes\n",
+	    hlp->id_name, srv->index + 1, len);
+	srv->offset = 0;
+    } else if ((t = strchr(srv->buf, '\n'))) {
+	/* end of reply found */
+	debug(84, 3) ("helperStatefulHandleRead: end of reply found\n");
+	*t = '\0';
+	if (cbdataReferenceValid(r->data)) {
+	    switch ((r->callback(r->data, srv, srv->buf))) {	/*if non-zero reserve helper */
+	    case S_HELPER_UNKNOWN:
+		fatal("helperStatefulHandleRead: either a non-state aware callback was give to the stateful helper routines, or an uninitialised callback response was recieved.\n");
+		break;
+	    case S_HELPER_RELEASE:	/* helper finished with */
+		if (!srv->deferred_requests && !srv->queue.head) {
+		    srv->flags.reserved = S_HELPER_FREE;
+		    if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
+			srv->parent->OnEmptyQueue(srv->data);
+		    debug(84, 5) ("StatefulHandleRead: releasing %s #%d\n", hlp->id_name, srv->index + 1);
+		} else {
+		    srv->flags.reserved = S_HELPER_DEFERRED;
+		    debug(84, 5) ("StatefulHandleRead: outstanding deferred requests on %s #%d. reserving for deferred requests.\n", hlp->id_name, srv->index + 1);
+		}
+		break;
+	    case S_HELPER_RESERVE:	/* 'pin' this helper for the caller */
+		if (!srv->queue.head) {
+		    assert(srv->deferred_requests == 0);
+		    srv->flags.reserved = S_HELPER_RESERVED;
+		    debug(84, 5) ("StatefulHandleRead: reserving %s #%d\n", hlp->id_name, srv->index + 1);
+		} else {
+		    fatal("StatefulHandleRead: Callback routine attempted to reserve a stateful helper with deferred requests. This can lead to deadlock.\n");
+		}
+		break;
+	    case S_HELPER_DEFER:
+		/* the helper is still needed, but can
+		 * be used for other requests in the meantime.
+		 */
+		srv->flags.reserved = S_HELPER_DEFERRED;
+		srv->deferred_requests++;
+		srv->stats.deferbycb++;
+		debug(84, 5) ("StatefulHandleRead: reserving %s #%d for deferred requests.\n", hlp->id_name, srv->index + 1);
+		break;
+	    default:
+		fatal("helperStatefulHandleRead: unknown stateful helper callback result.\n");
+	    }
+
+	} else {
+	    debug(84, 1) ("StatefulHandleRead: no callback data registered\n");
+	}
+	srv->flags.busy = 0;
+	srv->offset = 0;
+	helperStatefulRequestFree(r);
+	srv->request = NULL;
+	hlp->stats.replies++;
+	hlp->stats.avg_svc_time =
+	    intAverage(hlp->stats.avg_svc_time,
+	    tvSubMsec(srv->dispatch_time, current_time),
+	    hlp->stats.replies, REDIRECT_AV_FACTOR);
+	if (srv->flags.shutdown
+	    && srv->flags.reserved == S_HELPER_FREE
+	    && !srv->deferred_requests) {
+	    int wfd = srv->wfd;
+	    srv->wfd = -1;
+	    comm_close(wfd);
+	} else {
+	    if (srv->queue.head)
+		helperStatefulServerKickQueue(srv);
+	    else
+		helperStatefulKickQueue(hlp);
+	}
+    } else {
+	commSetSelect(srv->rfd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0);
+    }
+}
+
+static void
+Enqueue(helper * hlp, helper_request * r)
+{
+    dlink_node *link = (dlink_node *)memAllocate(MEM_DLINK_NODE);
+    dlinkAddTail(r, link, &hlp->queue);
+    hlp->stats.queue_size++;
+    if (hlp->stats.queue_size < hlp->n_running)
+	return;
+    if (squid_curtime - hlp->last_queue_warn < 600)
+	return;
+    if (shutting_down || reconfiguring)
+	return;
+    hlp->last_queue_warn = squid_curtime;
+    debug(84, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
+    debug(84, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
+    if (hlp->stats.queue_size > hlp->n_running * 2)
+	fatalf("Too many queued %s requests", hlp->id_name);
+    debug(84, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
+}
+
+static void
+StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r)
+{
+    dlink_node *link = (dlink_node *)memAllocate(MEM_DLINK_NODE);
+    dlinkAddTail(r, link, &hlp->queue);
+    hlp->stats.queue_size++;
+    if (hlp->stats.queue_size < hlp->n_running)
+	return;
+    if (hlp->stats.queue_size > hlp->n_running * 2)
+	fatalf("Too many queued %s requests", hlp->id_name);
+    if (squid_curtime - hlp->last_queue_warn < 600)
+	return;
+    if (shutting_down || reconfiguring)
+	return;
+    hlp->last_queue_warn = squid_curtime;
+    debug(84, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
+    debug(84, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
+    debug(84, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
+}
+
+static void
+StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r)
+{
+    dlink_node *link = (dlink_node *)memAllocate(MEM_DLINK_NODE);
+    dlinkAddTail(r, link, &srv->queue);
+/* TODO: warning if the queue on this server is more than X
+ * We don't check the queue size at the moment, because
+ * requests hitting here are deferrable 
+ */
+/*    hlp->stats.queue_size++;
+ * if (hlp->stats.queue_size < hlp->n_running)
+ * return;
+ * if (squid_curtime - hlp->last_queue_warn < 600)
+ * return;
+ * if (shutting_down || reconfiguring)
+ * return;
+ * hlp->last_queue_warn = squid_curtime;
+ * debug(84, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
+ * debug(84, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
+ * if (hlp->stats.queue_size > hlp->n_running * 2)
+ * fatalf("Too many queued %s requests", hlp->id_name);
+ * debug(84, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);  */
+}
+
+
+static helper_request *
+Dequeue(helper * hlp)
+{
+    dlink_node *link;
+    helper_request *r = NULL;
+    if ((link = hlp->queue.head)) {
+	r = (helper_request *)link->data;
+	dlinkDelete(link, &hlp->queue);
+	memFree(link, MEM_DLINK_NODE);
+	hlp->stats.queue_size--;
+    }
+    return r;
+}
+
+static helper_stateful_request *
+StatefulServerDequeue(helper_stateful_server * srv)
+{
+    dlink_node *link;
+    helper_stateful_request *r = NULL;
+    if ((link = srv->queue.head)) {
+	r = (helper_stateful_request *)link->data;
+	dlinkDelete(link, &srv->queue);
+	memFree(link, MEM_DLINK_NODE);
+    }
+    return r;
+}
+
+static helper_stateful_request *
+StatefulDequeue(statefulhelper * hlp)
+{
+    dlink_node *link;
+    helper_stateful_request *r = NULL;
+    if ((link = hlp->queue.head)) {
+	r = (helper_stateful_request *)link->data;
+	dlinkDelete(link, &hlp->queue);
+	memFree(link, MEM_DLINK_NODE);
+	hlp->stats.queue_size--;
+    }
+    return r;
+}
+
+static helper_server *
+GetFirstAvailable(helper * hlp)
+{
+    dlink_node *n;
+    helper_server *srv = NULL;
+    if (hlp->n_running == 0)
+	return NULL;
+    for (n = hlp->servers.head; n != NULL; n = n->next) {
+	srv = (helper_server *)n->data;
+	if (srv->flags.busy)
+	    continue;
+	if (!srv->flags.alive)
+	    continue;
+	return srv;
+    }
+    return NULL;
+}
+
+static helper_stateful_server *
+StatefulGetFirstAvailable(statefulhelper * hlp)
+{
+    dlink_node *n;
+    helper_stateful_server *srv = NULL;
+    debug(84, 5) ("StatefulGetFirstAvailable: Running servers %d.\n", hlp->n_running);
+    if (hlp->n_running == 0)
+	return NULL;
+    for (n = hlp->servers.head; n != NULL; n = n->next) {
+	srv = (helper_stateful_server *)n->data;
+	if (srv->flags.busy)
+	    continue;
+	if (srv->flags.reserved == S_HELPER_RESERVED)
+	    continue;
+	if (!srv->flags.alive)
+	    continue;
+	if ((hlp->IsAvailable != NULL) && (srv->data != NULL) && !(hlp->IsAvailable(srv->data)))
+	    continue;
+	return srv;
+    }
+    debug(84, 5) ("StatefulGetFirstAvailable: None available.\n");
+    return NULL;
+}
+
+
+static void
+helperDispatch(helper_server * srv, helper_request * r)
+{
+    helper *hlp = srv->parent;
+    if (!cbdataReferenceValid(r->data)) {
+	debug(84, 1) ("helperDispatch: invalid callback data\n");
+	helperRequestFree(r);
+	return;
+    }
+    assert(!srv->flags.busy);
+    srv->flags.busy = 1;
+    srv->request = r;
+    srv->dispatch_time = current_time;
+    comm_write(srv->wfd,
+	r->buf,
+	strlen(r->buf),
+	NULL,			/* Handler */
+	NULL,			/* Handler-data */
+	NULL);			/* free */
+    commSetSelect(srv->rfd,
+	COMM_SELECT_READ,
+	helperHandleRead,
+	srv, 0);
+    debug(84, 5) ("helperDispatch: Request sent to %s #%d, %d bytes\n",
+	hlp->id_name, srv->index + 1, (int) strlen(r->buf));
+    srv->stats.uses++;
+    hlp->stats.requests++;
+}
+
+static void
+helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r)
+{
+    statefulhelper *hlp = srv->parent;
+    if (!cbdataReferenceValid(r->data)) {
+	debug(84, 1) ("helperStatefulDispatch: invalid callback data\n");
+	helperStatefulRequestFree(r);
+	return;
+    }
+    debug(84, 9) ("helperStatefulDispatch busying helper %s #%d\n", hlp->id_name, srv->index + 1);
+    if (r->placeholder == 1) {
+	/* a callback is needed before this request can _use_ a helper. */
+	/* we don't care about releasing/deferring this helper. The request NEVER
+	 * gets to the helper. So we throw away the return code */
+	r->callback(r->data, srv, NULL);
+	/* throw away the placeholder */
+	helperStatefulRequestFree(r);
+	/* and push the queue. Note that the callback may have submitted a new 
+	 * request to the helper which is why we test for the request*/
+	if (srv->request == NULL) {
+	    if (srv->flags.shutdown
+		&& srv->flags.reserved == S_HELPER_FREE
+		&& !srv->deferred_requests) {
+		int wfd = srv->wfd;
+		srv->wfd = -1;
+		comm_close(wfd);
+	    } else {
+		if (srv->queue.head)
+		    helperStatefulServerKickQueue(srv);
+		else
+		    helperStatefulKickQueue(hlp);
+	    }
+	}
+	return;
+    }
+    srv->flags.busy = 1;
+    srv->request = r;
+    srv->dispatch_time = current_time;
+    comm_write(srv->wfd,
+	r->buf,
+	strlen(r->buf),
+	NULL,			/* Handler */
+	NULL,			/* Handler-data */
+	NULL);			/* free */
+    commSetSelect(srv->rfd,
+	COMM_SELECT_READ,
+	helperStatefulHandleRead,
+	srv, 0);
+    debug(84, 5) ("helperStatefulDispatch: Request sent to %s #%d, %d bytes\n",
+	hlp->id_name, srv->index + 1, (int) strlen(r->buf));
+    srv->stats.uses++;
+    hlp->stats.requests++;
+}
+
+
+static void
+helperKickQueue(helper * hlp)
+{
+    helper_request *r;
+    helper_server *srv;
+    while ((srv = GetFirstAvailable(hlp)) && (r = Dequeue(hlp)))
+	helperDispatch(srv, r);
+}
+
+static void
+helperStatefulKickQueue(statefulhelper * hlp)
+{
+    helper_stateful_request *r;
+    helper_stateful_server *srv;
+    while ((srv = StatefulGetFirstAvailable(hlp)) && (r = StatefulDequeue(hlp)))
+	helperStatefulDispatch(srv, r);
+}
+
+static void
+helperStatefulServerKickQueue(helper_stateful_server * srv)
+{
+    helper_stateful_request *r;
+    if ((r = StatefulServerDequeue(srv)))
+	helperStatefulDispatch(srv, r);
+}
+
+static void
+helperRequestFree(helper_request * r)
+{
+    cbdataReferenceDone(r->data);
+    xfree(r->buf);
+    memFree(r, MEM_HELPER_REQUEST);
+}
+
+static void
+helperStatefulRequestFree(helper_stateful_request * r)
+{
+    cbdataReferenceDone(r->data);
+    xfree(r->buf);
+    memFree(r, MEM_HELPER_STATEFUL_REQUEST);
+}
Index: squid/src/icp_v2.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/icp_v2.cc,v
retrieving revision 1.1.2.17
retrieving revision 1.1.2.18
diff -u -r1.1.2.17 -r1.1.2.18
--- squid/src/icp_v2.cc	9 Oct 2002 05:55:49 -0000	1.1.2.17
+++ squid/src/icp_v2.cc	11 Oct 2002 15:40:51 -0000	1.1.2.18
@@ -1,6 +1,6 @@
 
 /*
- * $Id: icp_v2.cc,v 1.1.2.17 2002/10/09 05:55:49 rbcollins Exp $
+ * $Id: icp_v2.cc,v 1.1.2.18 2002/10/11 15:40:51 rbcollins Exp $
  *
  * DEBUG: section 12    Internet Cache Protocol
  * AUTHOR: Duane Wessels
@@ -69,6 +69,14 @@
     pad = ntohl(pad);
 }
 
+icp_opcode
+_icp_common_t::getOpCode() const
+{
+    if (opcode > (char)ICP_END)
+	return ICP_INVALID;
+    return (icp_opcode)opcode;
+}
+
 /* ICPState */
 
 ICPState:: ICPState(icp_common_t & aHeader):header(aHeader)
--- squid/src/internal.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,153 +0,0 @@
-
-/*
- * $Id: internal.c,v 1.9.18.1 2002/10/04 07:07:26 rbcollins Exp $
- *
- * DEBUG: section 76    Internal Squid Object handling
- * AUTHOR: Duane, Alex, Henrik
- *
- * 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"
-#include "Store.h"
-
-/* called when we "miss" on an internal object;
- * generate known dynamic objects, 
- * return HTTP_NOT_FOUND for others
- */
-void
-internalStart(request_t * request, StoreEntry * entry)
-{
-    ErrorState *err;
-    const char *upath = strBuf(request->urlpath);
-    http_version_t version;
-    debug(76, 3) ("internalStart: %s requesting '%s'\n",
-	inet_ntoa(request->client_addr), upath);
-    if (0 == strcmp(upath, "/squid-internal-dynamic/netdb")) {
-	netdbBinaryExchange(entry);
-    } else if (0 == strcmp(upath, "/squid-internal-periodic/store_digest")) {
-#if USE_CACHE_DIGESTS
-	const char *msgbuf = "This cache is currently building its digest.\n";
-#else
-	const char *msgbuf = "This cache does not suport Cache Digests.\n";
-#endif
-	httpBuildVersion(&version, 1, 0);
-	httpReplySetHeaders(entry->mem_obj->reply,
-	    version,
-	    HTTP_NOT_FOUND,
-	    "Not Found",
-	    "text/plain",
-	    strlen(msgbuf),
-	    squid_curtime,
-	    -2);
-	httpReplySwapOut(entry->mem_obj->reply, entry);
-	storeAppend(entry, msgbuf, strlen(msgbuf));
-	storeComplete(entry);
-    } else {
-	debugObj(76, 1, "internalStart: unknown request:\n",
-	    request, (ObjPackMethod) & httpRequestPack);
-	err = errorCon(ERR_INVALID_REQ, HTTP_NOT_FOUND);
-	err->request = requestLink(request);
-	errorAppendEntry(entry, err);
-    }
-}
-
-int
-internalCheck(const char *urlpath)
-{
-    return (0 == strncmp(urlpath, "/squid-internal-", 16));
-}
-
-int
-internalStaticCheck(const char *urlpath)
-{
-    return (0 == strncmp(urlpath, "/squid-internal-static", 22));
-}
-
-/*
- * makes internal url with a given host and port (remote internal url)
- */
-char *
-internalRemoteUri(const char *host, u_short port, const char *dir, const char *name)
-{
-    static MemBuf mb = MemBufNULL;
-    static char lc_host[SQUIDHOSTNAMELEN];
-    assert(host && name);
-    /* convert host name to lower case */
-    xstrncpy(lc_host, host, SQUIDHOSTNAMELEN - 1);
-    Tolower(lc_host);
-    /*
-     * append the domain in order to mirror the requests with appended
-     * domains
-     */
-    if (Config.appendDomain && !strchr(lc_host, '.'))
-	strncat(lc_host, Config.appendDomain, SQUIDHOSTNAMELEN -
-	    strlen(lc_host) - 1);
-    /* build uri in mb */
-    memBufReset(&mb);
-    memBufPrintf(&mb, "http://%s", lc_host);
-    /* append port if not default */
-    if (port && port != urlDefaultPort(PROTO_HTTP))
-	memBufPrintf(&mb, ":%d", port);
-    if (dir)
-	memBufPrintf(&mb, "%s", dir);
-    memBufPrintf(&mb, "%s", name);
-    /* return a pointer to a local static buffer */
-    return mb.buf;
-}
-
-/*
- * makes internal url with local host and port
- */
-char *
-internalLocalUri(const char *dir, const char *name)
-{
-    return internalRemoteUri(getMyHostname(),
-	getMyPort(), dir, name);
-}
-
-const char *
-internalHostname(void)
-{
-    LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
-    xstrncpy(host, getMyHostname(), SQUIDHOSTNAMELEN);
-    Tolower(host);
-    return host;
-}
-
-int
-internalHostnameIs(const char *arg)
-{
-    wordlist *w;
-    if (0 == strcmp(arg, internalHostname()))
-	return 1;
-    for (w = Config.hostnameAliases; w; w = w->next)
-	if (0 == strcmp(arg, w->key))
-	    return 1;
-    return 0;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/internal.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,153 @@
+
+/*
+ * $Id: internal.cc,v 1.1.2.1 2002/10/11 15:40:53 rbcollins Exp $
+ *
+ * DEBUG: section 76    Internal Squid Object handling
+ * AUTHOR: Duane, Alex, Henrik
+ *
+ * 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"
+#include "Store.h"
+
+/* called when we "miss" on an internal object;
+ * generate known dynamic objects, 
+ * return HTTP_NOT_FOUND for others
+ */
+void
+internalStart(request_t * request, StoreEntry * entry)
+{
+    ErrorState *err;
+    const char *upath = strBuf(request->urlpath);
+    http_version_t version;
+    debug(76, 3) ("internalStart: %s requesting '%s'\n",
+	inet_ntoa(request->client_addr), upath);
+    if (0 == strcmp(upath, "/squid-internal-dynamic/netdb")) {
+	netdbBinaryExchange(entry);
+    } else if (0 == strcmp(upath, "/squid-internal-periodic/store_digest")) {
+#if USE_CACHE_DIGESTS
+	const char *msgbuf = "This cache is currently building its digest.\n";
+#else
+	const char *msgbuf = "This cache does not suport Cache Digests.\n";
+#endif
+	httpBuildVersion(&version, 1, 0);
+	httpReplySetHeaders(entry->mem_obj->reply,
+	    version,
+	    HTTP_NOT_FOUND,
+	    "Not Found",
+	    "text/plain",
+	    strlen(msgbuf),
+	    squid_curtime,
+	    -2);
+	httpReplySwapOut(entry->mem_obj->reply, entry);
+	storeAppend(entry, msgbuf, strlen(msgbuf));
+	storeComplete(entry);
+    } else {
+	debugObj(76, 1, "internalStart: unknown request:\n",
+	    request, (ObjPackMethod) & httpRequestPack);
+	err = errorCon(ERR_INVALID_REQ, HTTP_NOT_FOUND);
+	err->request = requestLink(request);
+	errorAppendEntry(entry, err);
+    }
+}
+
+int
+internalCheck(const char *urlpath)
+{
+    return (0 == strncmp(urlpath, "/squid-internal-", 16));
+}
+
+int
+internalStaticCheck(const char *urlpath)
+{
+    return (0 == strncmp(urlpath, "/squid-internal-static", 22));
+}
+
+/*
+ * makes internal url with a given host and port (remote internal url)
+ */
+char *
+internalRemoteUri(const char *host, u_short port, const char *dir, const char *name)
+{
+    static MemBuf mb = MemBufNULL;
+    static char lc_host[SQUIDHOSTNAMELEN];
+    assert(host && name);
+    /* convert host name to lower case */
+    xstrncpy(lc_host, host, SQUIDHOSTNAMELEN - 1);
+    Tolower(lc_host);
+    /*
+     * append the domain in order to mirror the requests with appended
+     * domains
+     */
+    if (Config.appendDomain && !strchr(lc_host, '.'))
+	strncat(lc_host, Config.appendDomain, SQUIDHOSTNAMELEN -
+	    strlen(lc_host) - 1);
+    /* build uri in mb */
+    memBufReset(&mb);
+    memBufPrintf(&mb, "http://%s", lc_host);
+    /* append port if not default */
+    if (port && port != urlDefaultPort(PROTO_HTTP))
+	memBufPrintf(&mb, ":%d", port);
+    if (dir)
+	memBufPrintf(&mb, "%s", dir);
+    memBufPrintf(&mb, "%s", name);
+    /* return a pointer to a local static buffer */
+    return mb.buf;
+}
+
+/*
+ * makes internal url with local host and port
+ */
+char *
+internalLocalUri(const char *dir, const char *name)
+{
+    return internalRemoteUri(getMyHostname(),
+	getMyPort(), dir, name);
+}
+
+const char *
+internalHostname(void)
+{
+    LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
+    xstrncpy(host, getMyHostname(), SQUIDHOSTNAMELEN);
+    Tolower(host);
+    return host;
+}
+
+int
+internalHostnameIs(const char *arg)
+{
+    wordlist *w;
+    if (0 == strcmp(arg, internalHostname()))
+	return 1;
+    for (w = Config.hostnameAliases; w; w = w->next)
+	if (0 == strcmp(arg, w->key))
+	    return 1;
+    return 0;
+}
--- squid/src/ipc.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,312 +0,0 @@
-
-/*
- * $Id: ipc.c,v 1.9 2002/04/06 11:34:50 squidadm Exp $
- *
- * DEBUG: section 54    Interprocess Communication
- * AUTHOR: Duane Wessels
- *
- * 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 const char *hello_string = "hi there\n";
-#define HELLO_BUF_SZ 32
-static char hello_buf[HELLO_BUF_SZ];
-
-static int
-ipcCloseAllFD(int prfd, int pwfd, int crfd, int cwfd)
-{
-    if (prfd >= 0)
-	comm_close(prfd);
-    if (prfd != pwfd)
-	if (pwfd >= 0)
-	    comm_close(pwfd);
-    if (crfd >= 0)
-	comm_close(crfd);
-    if (crfd != cwfd)
-	if (cwfd >= 0)
-	    comm_close(cwfd);
-    return -1;
-}
-
-int
-ipcCreate(int type, const char *prog, const char *const args[], const char *name, int *rfd, int *wfd)
-{
-    pid_t pid;
-    struct sockaddr_in CS;
-    struct sockaddr_in PS;
-    int crfd = -1;
-    int prfd = -1;
-    int cwfd = -1;
-    int pwfd = -1;
-    int fd;
-    int t1, t2, t3;
-    socklen_t len;
-    int tmp_s;
-#if HAVE_PUTENV
-    char *env_str;
-#endif
-    int x;
-
-#if USE_POLL && defined(_SQUID_OSF_)
-    assert(type != IPC_FIFO);
-#endif
-
-    if (rfd)
-	*rfd = -1;
-    if (wfd)
-	*wfd = -1;
-    if (type == IPC_TCP_SOCKET) {
-	crfd = cwfd = comm_open(SOCK_STREAM,
-	    0,
-	    local_addr,
-	    0,
-	    COMM_NOCLOEXEC,
-	    name);
-	prfd = pwfd = comm_open(SOCK_STREAM,
-	    0,			/* protocol */
-	    local_addr,
-	    0,			/* port */
-	    0,			/* blocking */
-	    name);
-    } else if (type == IPC_UDP_SOCKET) {
-	crfd = cwfd = comm_open(SOCK_DGRAM,
-	    0,
-	    local_addr,
-	    0,
-	    COMM_NOCLOEXEC,
-	    name);
-	prfd = pwfd = comm_open(SOCK_DGRAM,
-	    0,
-	    local_addr,
-	    0,
-	    0,
-	    name);
-    } else if (type == IPC_FIFO) {
-	int p2c[2];
-	int c2p[2];
-	if (pipe(p2c) < 0) {
-	    debug(50, 0) ("ipcCreate: pipe: %s\n", xstrerror());
-	    return -1;
-	}
-	if (pipe(c2p) < 0) {
-	    debug(50, 0) ("ipcCreate: pipe: %s\n", xstrerror());
-	    return -1;
-	}
-	fd_open(prfd = p2c[0], FD_PIPE, "IPC FIFO Parent Read");
-	fd_open(cwfd = p2c[1], FD_PIPE, "IPC FIFO Child Write");
-	fd_open(crfd = c2p[0], FD_PIPE, "IPC FIFO Child Read");
-	fd_open(pwfd = c2p[1], FD_PIPE, "IPC FIFO Parent Write");
-#if HAVE_SOCKETPAIR && defined(AF_UNIX)
-    } else if (type == IPC_UNIX_STREAM) {
-	int fds[2];
-	int buflen = 32768;
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
-	    debug(50, 0) ("ipcCreate: socketpair: %s\n", xstrerror());
-	    return -1;
-	}
-	setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
-	setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
-	setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
-	setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
-	fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX STREAM Parent");
-	fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX STREAM Parent");
-    } else if (type == IPC_UNIX_DGRAM) {
-	int fds[2];
-	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) < 0) {
-	    debug(50, 0) ("ipcCreate: socketpair: %s\n", xstrerror());
-	    return -1;
-	}
-	fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX DGRAM Parent");
-	fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX DGRAM Parent");
-#endif
-    } else {
-	assert(IPC_NONE);
-    }
-    debug(54, 3) ("ipcCreate: prfd FD %d\n", prfd);
-    debug(54, 3) ("ipcCreate: pwfd FD %d\n", pwfd);
-    debug(54, 3) ("ipcCreate: crfd FD %d\n", crfd);
-    debug(54, 3) ("ipcCreate: cwfd FD %d\n", cwfd);
-
-    if (crfd < 0) {
-	debug(54, 0) ("ipcCreate: Failed to create child FD.\n");
-	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-    }
-    if (pwfd < 0) {
-	debug(54, 0) ("ipcCreate: Failed to create server FD.\n");
-	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-    }
-    if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
-	len = sizeof(PS);
-	memset(&PS, '\0', len);
-	if (getsockname(pwfd, (struct sockaddr *) &PS, &len) < 0) {
-	    debug(50, 0) ("ipcCreate: getsockname: %s\n", xstrerror());
-	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-	}
-	debug(54, 3) ("ipcCreate: FD %d sockaddr %s:%d\n",
-	    pwfd, inet_ntoa(PS.sin_addr), ntohs(PS.sin_port));
-	len = sizeof(CS);
-	memset(&CS, '\0', len);
-	if (getsockname(crfd, (struct sockaddr *) &CS, &len) < 0) {
-	    debug(50, 0) ("ipcCreate: getsockname: %s\n", xstrerror());
-	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-	}
-	debug(54, 3) ("ipcCreate: FD %d sockaddr %s:%d\n",
-	    crfd, inet_ntoa(CS.sin_addr), ntohs(CS.sin_port));
-    }
-    if (type == IPC_TCP_SOCKET) {
-	if (listen(crfd, 1) < 0) {
-	    debug(50, 1) ("ipcCreate: listen FD %d: %s\n", crfd, xstrerror());
-	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-	}
-	debug(54, 3) ("ipcCreate: FD %d listening...\n", crfd);
-    }
-    /* flush or else we get dup data if unbuffered_logs is set */
-    logsFlush();
-    if ((pid = fork()) < 0) {
-	debug(50, 1) ("ipcCreate: fork: %s\n", xstrerror());
-	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-    }
-    if (pid > 0) {		/* parent */
-	/* close shared socket with child */
-	comm_close(crfd);
-	if (cwfd != crfd)
-	    comm_close(cwfd);
-	cwfd = crfd = -1;
-	if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
-	    if (comm_connect_addr(pwfd, &CS) == COMM_ERROR)
-		return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-	}
-	memset(hello_buf, '\0', HELLO_BUF_SZ);
-	if (type == IPC_UDP_SOCKET)
-	    x = recv(prfd, hello_buf, HELLO_BUF_SZ - 1, 0);
-	else
-	    x = read(prfd, hello_buf, HELLO_BUF_SZ - 1);
-	if (x < 0) {
-	    debug(50, 0) ("ipcCreate: PARENT: hello read test failed\n");
-	    debug(50, 0) ("--> read: %s\n", xstrerror());
-	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-	} else if (strcmp(hello_buf, hello_string)) {
-	    debug(54, 0) ("ipcCreate: PARENT: hello read test failed\n");
-	    debug(54, 0) ("--> read returned %d\n", x);
-	    debug(54, 0) ("--> got '%s'\n", rfc1738_escape(hello_buf));
-	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-	}
-	commSetTimeout(prfd, -1, NULL, NULL);
-	commSetNonBlocking(prfd);
-	commSetNonBlocking(pwfd);
-	if (rfd)
-	    *rfd = prfd;
-	if (wfd)
-	    *wfd = pwfd;
-	fd_table[prfd].flags.ipc = 1;
-	fd_table[pwfd].flags.ipc = 1;
-	if (Config.sleep_after_fork) {
-	    /* XXX emulation of usleep() */
-	    struct timeval sl;
-	    sl.tv_sec = Config.sleep_after_fork / 1000000;
-	    sl.tv_usec = Config.sleep_after_fork % 1000000;
-	    select(0, NULL, NULL, NULL, &sl);
-	}
-	return pid;
-    }
-    /* child */
-    no_suid();			/* give up extra priviliges */
-    /* close shared socket with parent */
-    close(prfd);
-    if (pwfd != prfd)
-	close(pwfd);
-    pwfd = prfd = -1;
-
-    if (type == IPC_TCP_SOCKET) {
-	debug(54, 3) ("ipcCreate: calling accept on FD %d\n", crfd);
-	if ((fd = accept(crfd, NULL, NULL)) < 0) {
-	    debug(50, 0) ("ipcCreate: FD %d accept: %s\n", crfd, xstrerror());
-	    _exit(1);
-	}
-	debug(54, 3) ("ipcCreate: CHILD accepted new FD %d\n", fd);
-	close(crfd);
-	cwfd = crfd = fd;
-    } else if (type == IPC_UDP_SOCKET) {
-	if (comm_connect_addr(crfd, &PS) == COMM_ERROR)
-	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
-    }
-    if (type == IPC_UDP_SOCKET) {
-	x = send(cwfd, hello_string, strlen(hello_string) + 1, 0);
-	if (x < 0) {
-	    debug(50, 0) ("sendto FD %d: %s\n", cwfd, xstrerror());
-	    debug(50, 0) ("ipcCreate: CHILD: hello write test failed\n");
-	    _exit(1);
-	}
-    } else {
-	if (write(cwfd, hello_string, strlen(hello_string) + 1) < 0) {
-	    debug(50, 0) ("write FD %d: %s\n", cwfd, xstrerror());
-	    debug(50, 0) ("ipcCreate: CHILD: hello write test failed\n");
-	    _exit(1);
-	}
-    }
-#if HAVE_PUTENV
-    env_str = xcalloc((tmp_s = strlen(Config.debugOptions) + 32), 1);
-    snprintf(env_str, tmp_s, "SQUID_DEBUG=%s", Config.debugOptions);
-    putenv(env_str);
-#endif
-    /*
-     * This double-dup stuff avoids problems when one of 
-     *  crfd, cwfd, or debug_log are in the rage 0-2.
-     */
-    do {
-	x = open(_PATH_DEVNULL, 0, 0444);
-	if (x > -1)
-	    commSetCloseOnExec(x);
-    } while (x < 3);
-    t1 = dup(crfd);
-    t2 = dup(cwfd);
-    t3 = dup(fileno(debug_log));
-    assert(t1 > 2 && t2 > 2 && t3 > 2);
-    close(crfd);
-    close(cwfd);
-    close(fileno(debug_log));
-    dup2(t1, 0);
-    dup2(t2, 1);
-    dup2(t3, 2);
-    close(t1);
-    close(t2);
-    close(t3);
-    /* Make sure all other filedescriptors are closed */
-    for (x = 3; x < SQUID_MAXFD; x++)
-	close(x);
-#if HAVE_SETSID
-    setsid();
-#endif
-    execvp(prog, (char *const *) args);
-    debug_log = fdopen(2, "a+");
-    debug(50, 0) ("ipcCreate: %s: %s\n", prog, xstrerror());
-    _exit(1);
-    return 0;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/ipc.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,312 @@
+
+/*
+ * $Id: ipc.cc,v 1.1.2.1 2002/10/11 15:40:54 rbcollins Exp $
+ *
+ * DEBUG: section 54    Interprocess Communication
+ * AUTHOR: Duane Wessels
+ *
+ * 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 const char *hello_string = "hi there\n";
+#define HELLO_BUF_SZ 32
+static char hello_buf[HELLO_BUF_SZ];
+
+static int
+ipcCloseAllFD(int prfd, int pwfd, int crfd, int cwfd)
+{
+    if (prfd >= 0)
+	comm_close(prfd);
+    if (prfd != pwfd)
+	if (pwfd >= 0)
+	    comm_close(pwfd);
+    if (crfd >= 0)
+	comm_close(crfd);
+    if (crfd != cwfd)
+	if (cwfd >= 0)
+	    comm_close(cwfd);
+    return -1;
+}
+
+int
+ipcCreate(int type, const char *prog, const char *const args[], const char *name, int *rfd, int *wfd)
+{
+    pid_t pid;
+    struct sockaddr_in CS;
+    struct sockaddr_in PS;
+    int crfd = -1;
+    int prfd = -1;
+    int cwfd = -1;
+    int pwfd = -1;
+    int fd;
+    int t1, t2, t3;
+    socklen_t len;
+    int tmp_s;
+#if HAVE_PUTENV
+    char *env_str;
+#endif
+    int x;
+
+#if USE_POLL && defined(_SQUID_OSF_)
+    assert(type != IPC_FIFO);
+#endif
+
+    if (rfd)
+	*rfd = -1;
+    if (wfd)
+	*wfd = -1;
+    if (type == IPC_TCP_SOCKET) {
+	crfd = cwfd = comm_open(SOCK_STREAM,
+	    0,
+	    local_addr,
+	    0,
+	    COMM_NOCLOEXEC,
+	    name);
+	prfd = pwfd = comm_open(SOCK_STREAM,
+	    0,			/* protocol */
+	    local_addr,
+	    0,			/* port */
+	    0,			/* blocking */
+	    name);
+    } else if (type == IPC_UDP_SOCKET) {
+	crfd = cwfd = comm_open(SOCK_DGRAM,
+	    0,
+	    local_addr,
+	    0,
+	    COMM_NOCLOEXEC,
+	    name);
+	prfd = pwfd = comm_open(SOCK_DGRAM,
+	    0,
+	    local_addr,
+	    0,
+	    0,
+	    name);
+    } else if (type == IPC_FIFO) {
+	int p2c[2];
+	int c2p[2];
+	if (pipe(p2c) < 0) {
+	    debug(50, 0) ("ipcCreate: pipe: %s\n", xstrerror());
+	    return -1;
+	}
+	if (pipe(c2p) < 0) {
+	    debug(50, 0) ("ipcCreate: pipe: %s\n", xstrerror());
+	    return -1;
+	}
+	fd_open(prfd = p2c[0], FD_PIPE, "IPC FIFO Parent Read");
+	fd_open(cwfd = p2c[1], FD_PIPE, "IPC FIFO Child Write");
+	fd_open(crfd = c2p[0], FD_PIPE, "IPC FIFO Child Read");
+	fd_open(pwfd = c2p[1], FD_PIPE, "IPC FIFO Parent Write");
+#if HAVE_SOCKETPAIR && defined(AF_UNIX)
+    } else if (type == IPC_UNIX_STREAM) {
+	int fds[2];
+	int buflen = 32768;
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+	    debug(50, 0) ("ipcCreate: socketpair: %s\n", xstrerror());
+	    return -1;
+	}
+	setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
+	setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
+	setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
+	setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
+	fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX STREAM Parent");
+	fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX STREAM Parent");
+    } else if (type == IPC_UNIX_DGRAM) {
+	int fds[2];
+	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) < 0) {
+	    debug(50, 0) ("ipcCreate: socketpair: %s\n", xstrerror());
+	    return -1;
+	}
+	fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX DGRAM Parent");
+	fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX DGRAM Parent");
+#endif
+    } else {
+	assert(IPC_NONE);
+    }
+    debug(54, 3) ("ipcCreate: prfd FD %d\n", prfd);
+    debug(54, 3) ("ipcCreate: pwfd FD %d\n", pwfd);
+    debug(54, 3) ("ipcCreate: crfd FD %d\n", crfd);
+    debug(54, 3) ("ipcCreate: cwfd FD %d\n", cwfd);
+
+    if (crfd < 0) {
+	debug(54, 0) ("ipcCreate: Failed to create child FD.\n");
+	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+    if (pwfd < 0) {
+	debug(54, 0) ("ipcCreate: Failed to create server FD.\n");
+	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+    if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
+	len = sizeof(PS);
+	memset(&PS, '\0', len);
+	if (getsockname(pwfd, (struct sockaddr *) &PS, &len) < 0) {
+	    debug(50, 0) ("ipcCreate: getsockname: %s\n", xstrerror());
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+	}
+	debug(54, 3) ("ipcCreate: FD %d sockaddr %s:%d\n",
+	    pwfd, inet_ntoa(PS.sin_addr), ntohs(PS.sin_port));
+	len = sizeof(CS);
+	memset(&CS, '\0', len);
+	if (getsockname(crfd, (struct sockaddr *) &CS, &len) < 0) {
+	    debug(50, 0) ("ipcCreate: getsockname: %s\n", xstrerror());
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+	}
+	debug(54, 3) ("ipcCreate: FD %d sockaddr %s:%d\n",
+	    crfd, inet_ntoa(CS.sin_addr), ntohs(CS.sin_port));
+    }
+    if (type == IPC_TCP_SOCKET) {
+	if (listen(crfd, 1) < 0) {
+	    debug(50, 1) ("ipcCreate: listen FD %d: %s\n", crfd, xstrerror());
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+	}
+	debug(54, 3) ("ipcCreate: FD %d listening...\n", crfd);
+    }
+    /* flush or else we get dup data if unbuffered_logs is set */
+    logsFlush();
+    if ((pid = fork()) < 0) {
+	debug(50, 1) ("ipcCreate: fork: %s\n", xstrerror());
+	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+    if (pid > 0) {		/* parent */
+	/* close shared socket with child */
+	comm_close(crfd);
+	if (cwfd != crfd)
+	    comm_close(cwfd);
+	cwfd = crfd = -1;
+	if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
+	    if (comm_connect_addr(pwfd, &CS) == COMM_ERROR)
+		return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+	}
+	memset(hello_buf, '\0', HELLO_BUF_SZ);
+	if (type == IPC_UDP_SOCKET)
+	    x = recv(prfd, hello_buf, HELLO_BUF_SZ - 1, 0);
+	else
+	    x = read(prfd, hello_buf, HELLO_BUF_SZ - 1);
+	if (x < 0) {
+	    debug(50, 0) ("ipcCreate: PARENT: hello read test failed\n");
+	    debug(50, 0) ("--> read: %s\n", xstrerror());
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+	} else if (strcmp(hello_buf, hello_string)) {
+	    debug(54, 0) ("ipcCreate: PARENT: hello read test failed\n");
+	    debug(54, 0) ("--> read returned %d\n", x);
+	    debug(54, 0) ("--> got '%s'\n", rfc1738_escape(hello_buf));
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+	}
+	commSetTimeout(prfd, -1, NULL, NULL);
+	commSetNonBlocking(prfd);
+	commSetNonBlocking(pwfd);
+	if (rfd)
+	    *rfd = prfd;
+	if (wfd)
+	    *wfd = pwfd;
+	fd_table[prfd].flags.ipc = 1;
+	fd_table[pwfd].flags.ipc = 1;
+	if (Config.sleep_after_fork) {
+	    /* XXX emulation of usleep() */
+	    struct timeval sl;
+	    sl.tv_sec = Config.sleep_after_fork / 1000000;
+	    sl.tv_usec = Config.sleep_after_fork % 1000000;
+	    select(0, NULL, NULL, NULL, &sl);
+	}
+	return pid;
+    }
+    /* child */
+    no_suid();			/* give up extra priviliges */
+    /* close shared socket with parent */
+    close(prfd);
+    if (pwfd != prfd)
+	close(pwfd);
+    pwfd = prfd = -1;
+
+    if (type == IPC_TCP_SOCKET) {
+	debug(54, 3) ("ipcCreate: calling accept on FD %d\n", crfd);
+	if ((fd = accept(crfd, NULL, NULL)) < 0) {
+	    debug(50, 0) ("ipcCreate: FD %d accept: %s\n", crfd, xstrerror());
+	    _exit(1);
+	}
+	debug(54, 3) ("ipcCreate: CHILD accepted new FD %d\n", fd);
+	close(crfd);
+	cwfd = crfd = fd;
+    } else if (type == IPC_UDP_SOCKET) {
+	if (comm_connect_addr(crfd, &PS) == COMM_ERROR)
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+    if (type == IPC_UDP_SOCKET) {
+	x = send(cwfd, hello_string, strlen(hello_string) + 1, 0);
+	if (x < 0) {
+	    debug(50, 0) ("sendto FD %d: %s\n", cwfd, xstrerror());
+	    debug(50, 0) ("ipcCreate: CHILD: hello write test failed\n");
+	    _exit(1);
+	}
+    } else {
+	if (write(cwfd, hello_string, strlen(hello_string) + 1) < 0) {
+	    debug(50, 0) ("write FD %d: %s\n", cwfd, xstrerror());
+	    debug(50, 0) ("ipcCreate: CHILD: hello write test failed\n");
+	    _exit(1);
+	}
+    }
+#if HAVE_PUTENV
+    env_str = (char *)xcalloc((tmp_s = strlen(Config.debugOptions) + 32), 1);
+    snprintf(env_str, tmp_s, "SQUID_DEBUG=%s", Config.debugOptions);
+    putenv(env_str);
+#endif
+    /*
+     * This double-dup stuff avoids problems when one of 
+     *  crfd, cwfd, or debug_log are in the rage 0-2.
+     */
+    do {
+	x = open(_PATH_DEVNULL, 0, 0444);
+	if (x > -1)
+	    commSetCloseOnExec(x);
+    } while (x < 3);
+    t1 = dup(crfd);
+    t2 = dup(cwfd);
+    t3 = dup(fileno(debug_log));
+    assert(t1 > 2 && t2 > 2 && t3 > 2);
+    close(crfd);
+    close(cwfd);
+    close(fileno(debug_log));
+    dup2(t1, 0);
+    dup2(t2, 1);
+    dup2(t3, 2);
+    close(t1);
+    close(t2);
+    close(t3);
+    /* Make sure all other filedescriptors are closed */
+    for (x = 3; x < SQUID_MAXFD; x++)
+	close(x);
+#if HAVE_SETSID
+    setsid();
+#endif
+    execvp(prog, (char *const *) args);
+    debug_log = fdopen(2, "a+");
+    debug(50, 0) ("ipcCreate: %s: %s\n", prog, xstrerror());
+    _exit(1);
+    return 0;
+}
Index: squid/src/net_db.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/net_db.cc,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- squid/src/net_db.cc	9 Oct 2002 05:21:29 -0000	1.1.2.1
+++ squid/src/net_db.cc	11 Oct 2002 15:40:54 -0000	1.1.2.2
@@ -1,6 +1,6 @@
 
 /*
- * $Id: net_db.cc,v 1.1.2.1 2002/10/09 05:21:29 rbcollins Exp $
+ * $Id: net_db.cc,v 1.1.2.2 2002/10/11 15:40:54 rbcollins Exp $
  *
  * DEBUG: section 38    Network Measurement Database
  * AUTHOR: Duane Wessels
@@ -109,7 +109,7 @@
 static void
 netdbHashDelete(const char *key)
 {
-    hash_link *hptr = hash_lookup(addr_table, key);
+    hash_link *hptr = (hash_link *)hash_lookup(addr_table, key);
     if (hptr == NULL) {
 	debug_trap("netdbHashDelete: key not found");
 	return;
@@ -120,7 +120,7 @@
 static void
 netdbHostInsert(netdbEntry * n, const char *hostname)
 {
-    net_db_name *x = memAllocate(MEM_NET_DB_NAME);
+    net_db_name *x = (net_db_name *)memAllocate(MEM_NET_DB_NAME);
     x->hash.key = xstrdup(hostname);
     x->next = n->hosts;
     n->hosts = x;
@@ -180,8 +180,8 @@
 static int
 netdbLRU(const void *A, const void *B)
 {
-    const netdbEntry *const *n1 = A;
-    const netdbEntry *const *n2 = B;
+    const netdbEntry *const *n1 = (const netdbEntry *const *)A;
+    const netdbEntry *const *n2 = (const netdbEntry *const *)B;
     if ((*n1)->last_use_time > (*n2)->last_use_time)
 	return (1);
     if ((*n1)->last_use_time < (*n2)->last_use_time)
@@ -197,7 +197,7 @@
     int k = 0;
     int list_count = 0;
     int removed = 0;
-    list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
+    list = (netdbEntry **)xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
     hash_first(addr_table);
     while ((n = (netdbEntry *) hash_next(addr_table))) {
 	assert(list_count < memInUse(MEM_NETDBENTRY));
@@ -233,7 +233,7 @@
     if (memInUse(MEM_NETDBENTRY) > Config.Netdb.high)
 	netdbPurgeLRU();
     if ((n = netdbLookupAddr(addr)) == NULL) {
-	n = memAllocate(MEM_NETDBENTRY);
+	n = (netdbEntry *)memAllocate(MEM_NETDBENTRY);
 	netdbHashInsert(n, addr);
     }
     return n;
@@ -243,7 +243,7 @@
 netdbSendPing(const ipcache_addrs * ia, void *data)
 {
     struct in_addr addr;
-    char *hostname = ((generic_cbdata *) data)->data;
+    char *hostname = (char *)((generic_cbdata *) data)->data;
     netdbEntry *n;
     netdbEntry *na;
     net_db_name *x;
@@ -320,8 +320,8 @@
 static int
 sortByRtt(const void *A, const void *B)
 {
-    const netdbEntry *const *n1 = A;
-    const netdbEntry *const *n2 = B;
+    const netdbEntry *const *n1 = (const netdbEntry *const *)A;
+    const netdbEntry *const *n2 = (const netdbEntry *const *)B;
     if ((*n1)->rtt > (*n2)->rtt)
 	return 1;
     else if ((*n1)->rtt < (*n2)->rtt)
@@ -358,7 +358,7 @@
 	    n->n_peers_alloc <<= 1;
 	debug(38, 3) ("netdbPeerAdd: Growing peer list for '%s' to %d\n",
 	    n->network, n->n_peers_alloc);
-	n->peers = xcalloc(n->n_peers_alloc, sizeof(net_db_peer));
+	n->peers = (net_db_peer *)xcalloc(n->n_peers_alloc, sizeof(net_db_peer));
 	for (i = 0; i < osize; i++)
 	    *(n->peers + i) = *(o + i);
 	if (osize) {
@@ -374,8 +374,8 @@
 static int
 sortPeerByRtt(const void *A, const void *B)
 {
-    const net_db_peer *p1 = A;
-    const net_db_peer *p2 = B;
+    const net_db_peer *p1 = (net_db_peer *)A;
+    const net_db_peer *p2 = (net_db_peer *)B;
     if (p1->rtt > p2->rtt)
 	return 1;
     else if (p1->rtt < p2->rtt)
@@ -462,7 +462,7 @@
 	file_close(fd);
 	return;
     }
-    t = buf = xcalloc(1, sb.st_size + 1);
+    t = buf = (char *)xcalloc(1, sb.st_size + 1);
     l = FD_READ_METHOD(fd, buf, sb.st_size);
     file_close(fd);
     if (l <= 0)
@@ -503,7 +503,7 @@
 	if ((q = strtok(NULL, w_space)) == NULL)
 	    continue;
 	N.last_use_time = (time_t) atoi(q);
-	n = memAllocate(MEM_NETDBENTRY);
+	n = (netdbEntry *)memAllocate(MEM_NETDBENTRY);
 	xmemcpy(n, &N, sizeof(netdbEntry));
 	netdbHashInsert(n, addr);
 	while ((q = strtok(NULL, w_space)) != NULL) {
@@ -533,7 +533,7 @@
 static void
 netdbFreeNetdbEntry(void *data)
 {
-    netdbEntry *n = data;
+    netdbEntry *n = (netdbEntry *)data;
     safe_free(n->peers);
     memFree(n, MEM_NETDBENTRY);
 }
@@ -541,7 +541,7 @@
 static void
 netdbFreeNameEntry(void *data)
 {
-    net_db_name *x = data;
+    net_db_name *x = (net_db_name *)data;
     xfree(x->hash.key);
     memFree(x, MEM_NET_DB_NAME);
 }
@@ -550,7 +550,7 @@
 static void
 netdbExchangeHandleReply(void *data, StoreIOBuffer recievedData)
 {
-    netdbExchangeState *ex = data;
+    netdbExchangeState *ex = (netdbExchangeState *)data;
     int rec_sz = 0;
     off_t o;
     struct in_addr addr;
@@ -598,7 +598,7 @@
 		netdbExchangeDone(ex);
 		return;
 	    }
-	    assert(ex->buf_ofs >= hdr_sz);
+	    assert((size_t)ex->buf_ofs >= hdr_sz);
 
 	    /*
 	     * Now, point p to the part of the buffer where the data
@@ -725,7 +725,7 @@
 static void
 netdbExchangeDone(void *data)
 {
-    netdbExchangeState *ex = data;
+    netdbExchangeState *ex = (netdbExchangeState *)data;
     debug(38, 3) ("netdbExchangeDone: %s\n", storeUrl(ex->e));
     requestUnlink(ex->r);
     storeUnregister(ex->sc, ex->e, ex);
@@ -784,8 +784,8 @@
     N = ++n->pings_recv;
     if (N > 5)
 	N = 5;
-    if (rtt < 1.0)
-	rtt = 1.0;
+    if (rtt < 1)
+	rtt = 1;
     n->hops = ((n->hops * (N - 1)) + hops) / N;
     n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
     debug(38, 3) ("netdbHandlePingReply: %s; rtt=%5.1f  hops=%4.1f\n",
@@ -841,7 +841,7 @@
 	"RTT",
 	"Hops",
 	"Hostnames");
-    list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
+    list = (netdbEntry **)xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
     i = 0;
     hash_first(addr_table);
     while ((n = (netdbEntry *) hash_next(addr_table)))
@@ -1020,7 +1020,7 @@
     rec_sz += 1 + sizeof(addr.s_addr);
     rec_sz += 1 + sizeof(int);
     rec_sz += 1 + sizeof(int);
-    buf = memAllocate(MEM_4K_BUF);
+    buf = (char *)memAllocate(MEM_4K_BUF);
     i = 0;
     hash_first(addr_table);
     while ((n = (netdbEntry *) hash_next(addr_table))) {
@@ -1071,7 +1071,7 @@
 netdbExchangeStart(void *data)
 {
 #if USE_ICMP
-    peer *p = data;
+    peer *p = (peer *)data;
     char *uri;
     netdbExchangeState *ex;
     StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
--- squid/src/peer_digest.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,982 +0,0 @@
-
-/*
- * $Id: peer_digest.c,v 1.17.2.1 2002/10/04 07:07:27 rbcollins Exp $
- *
- * DEBUG: section 72    Peer Digest Routines
- * AUTHOR: Alex Rousskov
- *
- * 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"
-#include "Store.h"
-
-#if USE_CACHE_DIGESTS
-
-#include "StoreClient.h"
-
-/* local types */
-
-/* local prototypes */
-static time_t peerDigestIncDelay(const PeerDigest * pd);
-static time_t peerDigestNewDelay(const StoreEntry * e);
-static void peerDigestSetCheck(PeerDigest * pd, time_t delay);
-static void peerDigestClean(PeerDigest *);
-static EVH peerDigestCheck;
-static void peerDigestRequest(PeerDigest * pd);
-static STCB peerDigestHandleReply;
-static int peerDigestFetchReply(void *, char *, ssize_t);
-static int peerDigestSwapInHeaders(void *, char *, ssize_t);
-static int peerDigestSwapInCBlock(void *, char *, ssize_t);
-static int peerDigestSwapInMask(void *, char *, ssize_t);
-static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
-static void peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason);
-static void peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason);
-static void peerDigestReqFinish(DigestFetchState * fetch, char *buf, int, int, int, const char *reason, int err);
-static void peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err);
-static void peerDigestFetchFinish(DigestFetchState * fetch, int err);
-static void peerDigestFetchSetStats(DigestFetchState * fetch);
-static int peerDigestSetCBlock(PeerDigest * pd, const char *buf);
-static int peerDigestUseful(const PeerDigest * pd);
-
-
-/* local constants */
-
-#define StoreDigestCBlockSize sizeof(StoreDigestCBlock)
-
-/* min interval for requesting digests from a given peer */
-static const time_t PeerDigestReqMinGap = 5 * 60;	/* seconds */
-/* min interval for requesting digests (cumulative request stream) */
-static const time_t GlobDigestReqMinGap = 1 * 60;	/* seconds */
-
-/* local vars */
-
-static time_t pd_last_req_time = 0;	/* last call to Check */
-
-/* initialize peer digest */
-static void
-peerDigestInit(PeerDigest * pd, peer * p)
-{
-    assert(pd && p);
-
-    memset(pd, 0, sizeof(*pd));
-    pd->peer = p;
-    /* if peer disappears, we will know it's name */
-    stringInit(&pd->host, p->host);
-
-    pd->times.initialized = squid_curtime;
-}
-
-static void
-peerDigestClean(PeerDigest * pd)
-{
-    assert(pd);
-    if (pd->cd)
-	cacheDigestDestroy(pd->cd);
-    stringClean(&pd->host);
-}
-
-CBDATA_TYPE(PeerDigest);
-
-/* allocate new peer digest, call Init, and lock everything */
-PeerDigest *
-peerDigestCreate(peer * p)
-{
-    PeerDigest *pd;
-    assert(p);
-
-    CBDATA_INIT_TYPE(PeerDigest);
-    pd = cbdataAlloc(PeerDigest);
-    peerDigestInit(pd, p);
-
-    /* XXX This does not look right, and the same thing again in the caller */
-    return cbdataReference(pd);
-}
-
-/* call Clean and free/unlock everything */
-static void
-peerDigestDestroy(PeerDigest * pd)
-{
-    peer *p;
-    assert(pd);
-
-    /* inform peer (if any) that we are gone */
-    if (cbdataReferenceValidDone(pd->peer, (void **) &p))
-	peerNoteDigestGone(p);
-
-    peerDigestClean(pd);
-    cbdataFree(pd);
-}
-
-/* called by peer to indicate that somebody actually needs this digest */
-void
-peerDigestNeeded(PeerDigest * pd)
-{
-    assert(pd);
-    assert(!pd->flags.needed);
-    assert(!pd->cd);
-
-    pd->flags.needed = 1;
-    pd->times.needed = squid_curtime;
-    peerDigestSetCheck(pd, 0);	/* check asap */
-}
-
-/* currently we do not have a reason to disable without destroying */
-#if FUTURE_CODE
-/* disables peer for good */
-static void
-peerDigestDisable(PeerDigest * pd)
-{
-    debug(72, 2) ("peerDigestDisable: peer %s disabled for good\n",
-	strBuf(pd->host));
-    pd->times.disabled = squid_curtime;
-    pd->times.next_check = -1;	/* never */
-    pd->flags.usable = 0;
-
-    if (pd->cd) {
-	cacheDigestDestroy(pd->cd);
-	pd->cd = NULL;
-    }
-    /* we do not destroy the pd itself to preserve its "history" and stats */
-}
-#endif
-
-/* increment retry delay [after an unsuccessful attempt] */
-static time_t
-peerDigestIncDelay(const PeerDigest * pd)
-{
-    assert(pd);
-    return pd->times.retry_delay > 0 ?
-	2 * pd->times.retry_delay :	/* exponential backoff */
-	PeerDigestReqMinGap;	/* minimal delay */
-}
-
-/* artificially increases Expires: setting to avoid race conditions 
- * returns the delay till that [increased] expiration time */
-static time_t
-peerDigestNewDelay(const StoreEntry * e)
-{
-    assert(e);
-    if (e->expires > 0)
-	return e->expires + PeerDigestReqMinGap - squid_curtime;
-    return PeerDigestReqMinGap;
-}
-
-/* registers next digest verification */
-static void
-peerDigestSetCheck(PeerDigest * pd, time_t delay)
-{
-    eventAdd("peerDigestCheck", peerDigestCheck, pd, (double) delay, 1);
-    pd->times.next_check = squid_curtime + delay;
-    debug(72, 3) ("peerDigestSetCheck: will check peer %s in %d secs\n",
-	strBuf(pd->host), (int) delay);
-}
-
-/*
- * called when peer is about to disappear or have already disappeared
- */
-void
-peerDigestNotePeerGone(PeerDigest * pd)
-{
-    if (pd->flags.requested) {
-	debug(72, 2) ("peerDigest: peer %s gone, will destroy after fetch.\n",
-	    strBuf(pd->host));
-	/* do nothing now, the fetching chain will notice and take action */
-    } else {
-	debug(72, 2) ("peerDigest: peer %s is gone, destroying now.\n",
-	    strBuf(pd->host));
-	peerDigestDestroy(pd);
-    }
-}
-
-/* callback for eventAdd() (with peer digest locked)
- * request new digest if our copy is too old or if we lack one; 
- * schedule next check otherwise */
-static void
-peerDigestCheck(void *data)
-{
-    PeerDigest *pd = data;
-    time_t req_time;
-
-    assert(!pd->flags.requested);
-
-    pd->times.next_check = 0;	/* unknown */
-
-    if (!cbdataReferenceValid(pd->peer)) {
-	peerDigestNotePeerGone(pd);
-	return;
-    }
-    debug(72, 3) ("peerDigestCheck: peer %s:%d\n", pd->peer->host, pd->peer->http_port);
-    debug(72, 3) ("peerDigestCheck: time: %ld, last received: %ld (%+d)\n",
-	(long int) squid_curtime, (long int) pd->times.received, (int) (squid_curtime - pd->times.received));
-
-    /* decide when we should send the request:
-     * request now unless too close to other requests */
-    req_time = squid_curtime;
-
-    /* per-peer limit */
-    if (req_time - pd->times.received < PeerDigestReqMinGap) {
-	debug(72, 2) ("peerDigestCheck: %s, avoiding close peer requests (%d < %d secs).\n",
-	    strBuf(pd->host), (int) (req_time - pd->times.received),
-	    (int) PeerDigestReqMinGap);
-	req_time = pd->times.received + PeerDigestReqMinGap;
-    }
-    /* global limit */
-    if (req_time - pd_last_req_time < GlobDigestReqMinGap) {
-	debug(72, 2) ("peerDigestCheck: %s, avoiding close requests (%d < %d secs).\n",
-	    strBuf(pd->host), (int) (req_time - pd_last_req_time),
-	    (int) GlobDigestReqMinGap);
-	req_time = pd_last_req_time + GlobDigestReqMinGap;
-    }
-    if (req_time <= squid_curtime)
-	peerDigestRequest(pd);	/* will set pd->flags.requested */
-    else
-	peerDigestSetCheck(pd, req_time - squid_curtime);
-}
-
-CBDATA_TYPE(DigestFetchState);
-
-/* ask store for a digest */
-static void
-peerDigestRequest(PeerDigest * pd)
-{
-    peer *p = pd->peer;
-    StoreEntry *e, *old_e;
-    char *url;
-    const cache_key *key;
-    request_t *req;
-    DigestFetchState *fetch = NULL;
-    StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
-
-    pd->req_result = NULL;
-    pd->flags.requested = 1;
-
-    /* compute future request components */
-    if (p->digest_url)
-	url = xstrdup(p->digest_url);
-    else
-	url = internalRemoteUri(p->host, p->http_port,
-	    "/squid-internal-periodic/", StoreDigestFileName);
-
-    req = urlParse(METHOD_GET, url);
-    assert(req);
-    key = storeKeyPublicByRequest(req);
-    debug(72, 2) ("peerDigestRequest: %s key: %s\n", url, storeKeyText(key));
-
-    /* add custom headers */
-    assert(!req->header.len);
-    httpHeaderPutStr(&req->header, HDR_ACCEPT, StoreDigestMimeStr);
-    httpHeaderPutStr(&req->header, HDR_ACCEPT, "text/html");
-    if (p->login)
-	xstrncpy(req->login, p->login, MAX_LOGIN_SZ);
-    /* create fetch state structure */
-    CBDATA_INIT_TYPE(DigestFetchState);
-    fetch = cbdataAlloc(DigestFetchState);
-    fetch->request = requestLink(req);
-    fetch->pd = cbdataReference(pd);
-    fetch->offset = 0;
-    fetch->state = DIGEST_READ_REPLY;
-
-    /* update timestamps */
-    fetch->start_time = squid_curtime;
-    pd->times.requested = squid_curtime;
-    pd_last_req_time = squid_curtime;
-
-    req->flags.cachable = 1;
-    /* the rest is based on clientProcessExpired() */
-    req->flags.refresh = 1;
-    old_e = fetch->old_entry = storeGet(key);
-    if (old_e) {
-	debug(72, 5) ("peerDigestRequest: found old entry\n");
-	storeLockObject(old_e);
-	storeCreateMemObject(old_e, url, url);
-	fetch->old_sc = storeClientListAdd(old_e, fetch);
-    }
-    e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method);
-    assert(EBIT_TEST(e->flags, KEY_PRIVATE));
-    fetch->sc = storeClientListAdd(e, fetch);
-    /* set lastmod to trigger IMS request if possible */
-    if (old_e)
-	e->lastmod = old_e->lastmod;
-
-    /* push towards peer cache */
-    debug(72, 3) ("peerDigestRequest: forwarding to fwdStart...\n");
-    fwdStart(-1, e, req);
-    tempBuffer.offset = 0;
-    tempBuffer.length =  SM_PAGE_SIZE;
-    tempBuffer.data = fetch->buf;
-    storeClientCopy(fetch->sc, e, tempBuffer,
-	peerDigestHandleReply, fetch);
-}
-
-
-/* Handle the data copying .. */
-
-/*
- * This routine handles the copy data and then redirects the
- * copy to a bunch of subfunctions depending upon the copy state.
- * It also tracks the buffer offset and "seen", since I'm actually
- * not interested in rewriting everything to suit my little idea.
- */
-static void
-peerDigestHandleReply(void *data, StoreIOBuffer recievedData)
-{
-    DigestFetchState *fetch = data;
-    PeerDigest *pd = fetch->pd;
-    int retsize = -1;
-    digest_read_state_t prevstate;
-    int newsize;
-
-    assert(pd && recievedData.data);
-    /* The existing code assumes that the recieved pointer is
-     * where we asked the data to be put
-     */
-    assert(fetch->buf + fetch->bufofs == recievedData.data);
-
-    /* Update the buffer size */
-    fetch->bufofs += recievedData.length;
-
-    assert(fetch->bufofs <= SM_PAGE_SIZE);
-
-    /* If we've fetched enough, return */
-    if (peerDigestFetchedEnough(fetch, fetch->buf, fetch->bufofs, "peerDigestHandleReply"))
-	return;
-
-    /* Call the right function based on the state */
-    /* (Those functions will update the state if needed) */
-
-    /* Give us a temporary reference. Some of the calls we make may
-     * try to destroy the fetch structure, and we like to know if they
-     * do
-     */
-    fetch = cbdataReference(fetch);
-
-    /* Repeat this loop until we're out of data OR the state changes */
-    /* (So keep going if the state has changed and we still have data */
-    do {
-	prevstate = fetch->state;
-	switch (fetch->state) {
-	case DIGEST_READ_REPLY:
-	    retsize = peerDigestFetchReply(data, fetch->buf, fetch->bufofs);
-	    break;
-	case DIGEST_READ_HEADERS:
-	    retsize = peerDigestSwapInHeaders(data, fetch->buf, fetch->bufofs);
-	    break;
-	case DIGEST_READ_CBLOCK:
-	    retsize = peerDigestSwapInCBlock(data, fetch->buf, fetch->bufofs);
-	    break;
-	case DIGEST_READ_MASK:
-	    retsize = peerDigestSwapInMask(data, fetch->buf, fetch->bufofs);
-	    break;
-	case DIGEST_READ_NONE:
-	    break;
-	case DIGEST_READ_DONE:
-	    goto finish;
-	    break;
-	default:
-	    fatal("Bad digest transfer mode!\n");
-	}
-
-	if (retsize < 0)
-	    goto finish;
-	/*
-	 * The returned size indicates how much of the buffer was read -
-	 * so move the remainder of the buffer to the beginning
-	 * and update the bufofs / bufsize
-	 */
-	newsize = fetch->bufofs - retsize;
-	xmemmove(fetch->buf, fetch->buf + retsize, fetch->bufofs - newsize);
-	fetch->bufofs = newsize;
-
-    } while (cbdataReferenceValid(fetch) && prevstate != fetch->state && fetch->bufofs > 0);
-
-    /* Update the copy offset */
-    fetch->offset += recievedData.length;
-
-    /* Schedule another copy */
-    if (cbdataReferenceValid(fetch)) {
-	StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
-	tempBuffer.offset = fetch->offset;
-	tempBuffer.length = SM_PAGE_SIZE - fetch->bufofs;
-	tempBuffer.data = fetch->buf + fetch->bufofs;
-	storeClientCopy(fetch->sc, fetch->entry, tempBuffer,
-	    peerDigestHandleReply, fetch);
-    }
-  finish:
-    /* Get rid of our reference, we've finished with it for now */
-    cbdataReferenceDone(fetch);
-}
-
-
-
-/* wait for full http headers to be received then parse them */
-/*
- * This routine handles parsing the reply line.
- * If the reply line indicates an OK, the same data is thrown
- * to SwapInHeaders(). If the reply line is a NOT_MODIFIED,
- * we simply stop parsing.
- */
-static int
-peerDigestFetchReply(void *data, char *buf, ssize_t size)
-{
-    DigestFetchState *fetch = data;
-    PeerDigest *pd = fetch->pd;
-    size_t hdr_size;
-    assert(pd && buf);
-    assert(!fetch->offset);
-
-    assert(fetch->state == DIGEST_READ_REPLY);
-    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
-	return -1;
-
-    if ((hdr_size = headersEnd(buf, size))) {
-	http_status status;
-	HttpReply *reply = fetch->entry->mem_obj->reply;
-	assert(reply);
-	httpReplyParse(reply, buf, hdr_size);
-	status = reply->sline.status;
-	debug(72, 3) ("peerDigestFetchReply: %s status: %d, expires: %ld (%+d)\n",
-	    strBuf(pd->host), status,
-	    (long int) reply->expires, (int) (reply->expires - squid_curtime));
-
-	/* this "if" is based on clientHandleIMSReply() */
-	if (status == HTTP_NOT_MODIFIED) {
-	    request_t *r = NULL;
-	    /* our old entry is fine */
-	    assert(fetch->old_entry);
-	    if (!fetch->old_entry->mem_obj->request)
-		fetch->old_entry->mem_obj->request = r =
-		    requestLink(fetch->entry->mem_obj->request);
-	    assert(fetch->old_entry->mem_obj->request);
-	    httpReplyUpdateOnNotModified(fetch->old_entry->mem_obj->reply, reply);
-	    storeTimestampsSet(fetch->old_entry);
-	    /* get rid of 304 reply */
-	    storeUnregister(fetch->sc, fetch->entry, fetch);
-	    storeUnlockObject(fetch->entry);
-	    fetch->entry = fetch->old_entry;
-	    fetch->old_entry = NULL;
-	    /* preserve request -- we need its size to update counters */
-	    /* requestUnlink(r); */
-	    /* fetch->entry->mem_obj->request = NULL; */
-	} else if (status == HTTP_OK) {
-	    /* get rid of old entry if any */
-	    if (fetch->old_entry) {
-		debug(72, 3) ("peerDigestFetchReply: got new digest, releasing old one\n");
-		storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
-		storeReleaseRequest(fetch->old_entry);
-		storeUnlockObject(fetch->old_entry);
-		fetch->old_entry = NULL;
-	    }
-	} else {
-	    /* some kind of a bug */
-	    peerDigestFetchAbort(fetch, buf, httpStatusLineReason(&reply->sline));
-	    return -1;		/* XXX -1 will abort stuff in ReadReply! */
-	}
-	/* must have a ready-to-use store entry if we got here */
-	/* can we stay with the old in-memory digest? */
-	if (status == HTTP_NOT_MODIFIED && fetch->pd->cd) {
-	    peerDigestFetchStop(fetch, buf, "Not modified");
-	    fetch->state = DIGEST_READ_DONE;
-	} else {
-	    fetch->state = DIGEST_READ_HEADERS;
-	}
-    } else {
-	/* need more data, do we have space? */
-	if (size >= SM_PAGE_SIZE)
-	    peerDigestFetchAbort(fetch, buf, "reply header too big");
-    }
-
-    /* We don't want to actually ack that we've handled anything,
-     * otherwise SwapInHeaders() won't get the reply line .. */
-    return 0;
-}
-
-/* fetch headers from disk, pass on to SwapInCBlock */
-static int
-peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
-{
-    DigestFetchState *fetch = data;
-    size_t hdr_size;
-
-    assert(fetch->state == DIGEST_READ_HEADERS);
-    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
-	return -1;
-
-    assert(!fetch->offset);
-    if ((hdr_size = headersEnd(buf, size))) {
-	assert(fetch->entry->mem_obj->reply);
-	if (!fetch->entry->mem_obj->reply->sline.status)
-	    httpReplyParse(fetch->entry->mem_obj->reply, buf, hdr_size);
-	if (fetch->entry->mem_obj->reply->sline.status != HTTP_OK) {
-	    debug(72, 1) ("peerDigestSwapInHeaders: %s status %d got cached!\n",
-		strBuf(fetch->pd->host), fetch->entry->mem_obj->reply->sline.status);
-	    peerDigestFetchAbort(fetch, buf, "internal status error");
-	    return -1;
-	}
-	fetch->state = DIGEST_READ_CBLOCK;
-	return hdr_size;	/* Say how much data we read */
-    } else {
-	/* need more data, do we have space? */
-	if (size >= SM_PAGE_SIZE) {
-	    peerDigestFetchAbort(fetch, buf, "stored header too big");
-	    return -1;
-	} else {
-	    return 0;		/* We need to read more to parse .. */
-	}
-    }
-    fatal("peerDigestSwapInHeaders() - shouldn't get here!\n");
-}
-
-static int
-peerDigestSwapInCBlock(void *data, char *buf, ssize_t size)
-{
-    DigestFetchState *fetch = data;
-
-    assert(fetch->state == DIGEST_READ_CBLOCK);
-    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock"))
-	return -1;
-
-    if (size >= StoreDigestCBlockSize) {
-	PeerDigest *pd = fetch->pd;
-	HttpReply *rep = fetch->entry->mem_obj->reply;
-
-	assert(pd && rep);
-	if (peerDigestSetCBlock(pd, buf)) {
-	    /* XXX: soon we will have variable header size */
-	    /* switch to CD buffer and fetch digest guts */
-	    buf = NULL;
-	    assert(pd->cd->mask);
-	    fetch->state = DIGEST_READ_MASK;
-	    return StoreDigestCBlockSize;
-	} else {
-	    peerDigestFetchAbort(fetch, buf, "invalid digest cblock");
-	    return -1;
-	}
-    } else {
-	/* need more data, do we have space? */
-	if (size >= SM_PAGE_SIZE) {
-	    peerDigestFetchAbort(fetch, buf, "digest cblock too big");
-	    return -1;
-	} else {
-	    return 0;		/* We need more data */
-	}
-    }
-    fatal("peerDigestSwapInCBlock(): shouldn't get here!\n");
-}
-
-static int
-peerDigestSwapInMask(void *data, char *buf, ssize_t size)
-{
-    DigestFetchState *fetch = data;
-    PeerDigest *pd;
-
-    pd = fetch->pd;
-    assert(pd->cd && pd->cd->mask);
-
-    /*
-     * NOTENOTENOTENOTENOTE: buf doesn't point to pd->cd->mask anymore!
-     * we need to do the copy ourselves!
-     */
-    xmemcpy(pd->cd->mask + fetch->mask_offset, buf, size);
-
-    /* NOTE! buf points to the middle of pd->cd->mask! */
-    if (peerDigestFetchedEnough(fetch, NULL, size, "peerDigestSwapInMask"))
-	return -1;
-
-    fetch->mask_offset += size;
-    if (fetch->mask_offset >= pd->cd->mask_size) {
-	debug(72, 2) ("peerDigestSwapInMask: Done! Got %d, expected %d\n",
-	    fetch->mask_offset, pd->cd->mask_size);
-	assert(fetch->mask_offset == pd->cd->mask_size);
-	assert(peerDigestFetchedEnough(fetch, NULL, 0, "peerDigestSwapInMask"));
-	return -1;		/* XXX! */
-    } else {
-	/* We always read everything, so return so */
-	return size;
-    }
-    fatal("peerDigestSwapInMask(): shouldn't get here!\n");
-}
-
-static int
-peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name)
-{
-    PeerDigest *pd = NULL;
-    const char *host = "<unknown>";	/* peer host */
-    const char *reason = NULL;	/* reason for completion */
-    const char *no_bug = NULL;	/* successful completion if set */
-    const int pdcb_valid = cbdataReferenceValid(fetch->pd);
-    const int pcb_valid = cbdataReferenceValid(fetch->pd->peer);
-
-    /* test possible exiting conditions (the same for most steps!)
-     * cases marked with '?!' should not happen */
-
-    if (!reason) {
-	if (!(pd = fetch->pd))
-	    reason = "peer digest disappeared?!";
-#if DONT			/* WHY NOT? /HNO */
-	else if (!cbdataReferenceValid(pd))
-	    reason = "invalidated peer digest?!";
-#endif
-	else
-	    host = strBuf(pd->host);
-    }
-    debug(72, 6) ("%s: peer %s, offset: %d size: %d.\n",
-	step_name, host,
-	fetch->offset, size);
-
-    /* continue checking (with pd and host known and valid) */
-    if (!reason) {
-	if (!cbdataReferenceValid(pd->peer))
-	    reason = "peer disappeared";
-	else if (size < 0)
-	    reason = "swap failure";
-	else if (!fetch->entry)
-	    reason = "swap aborted?!";
-	else if (EBIT_TEST(fetch->entry->flags, ENTRY_ABORTED))
-	    reason = "swap aborted";
-    }
-    /* continue checking (maybe-successful eof case) */
-    if (!reason && !size) {
-	if (!pd->cd)
-	    reason = "null digest?!";
-	else if (fetch->mask_offset != pd->cd->mask_size)
-	    reason = "premature end of digest?!";
-	else if (!peerDigestUseful(pd))
-	    reason = "useless digest";
-	else
-	    reason = no_bug = "success";
-    }
-    /* finish if we have a reason */
-    if (reason) {
-	const int level = strstr(reason, "?!") ? 1 : 3;
-	debug(72, level) ("%s: peer %s, exiting after '%s'\n",
-	    step_name, host, reason);
-	peerDigestReqFinish(fetch, buf,
-	    1, pdcb_valid, pcb_valid, reason, !no_bug);
-    } else {
-	/* paranoid check */
-	assert(pdcb_valid && pcb_valid);
-    }
-    return reason != NULL;
-}
-
-/* call this when all callback data is valid and fetch must be stopped but
- * no error has occurred (e.g. we received 304 reply and reuse old digest) */
-static void
-peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason)
-{
-    assert(reason);
-    debug(72, 2) ("peerDigestFetchStop: peer %s, reason: %s\n",
-	strBuf(fetch->pd->host), reason);
-    peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 0);
-}
-
-/* call this when all callback data is valid but something bad happened */
-static void
-peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason)
-{
-    assert(reason);
-    debug(72, 2) ("peerDigestFetchAbort: peer %s, reason: %s\n",
-	strBuf(fetch->pd->host), reason);
-    peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 1);
-}
-
-/* complete the digest transfer, update stats, unlock/release everything */
-static void
-peerDigestReqFinish(DigestFetchState * fetch, char *buf,
-    int fcb_valid, int pdcb_valid, int pcb_valid,
-    const char *reason, int err)
-{
-    assert(reason);
-
-    /* must go before peerDigestPDFinish */
-    if (pdcb_valid) {
-	fetch->pd->flags.requested = 0;
-	fetch->pd->req_result = reason;
-    }
-    /* schedule next check if peer is still out there */
-    if (pcb_valid) {
-	PeerDigest *pd = fetch->pd;
-	if (err) {
-	    pd->times.retry_delay = peerDigestIncDelay(pd);
-	    peerDigestSetCheck(pd, pd->times.retry_delay);
-	} else {
-	    pd->times.retry_delay = 0;
-	    peerDigestSetCheck(pd, peerDigestNewDelay(fetch->entry));
-	}
-    }
-    /* note: order is significant */
-    if (fcb_valid)
-	peerDigestFetchSetStats(fetch);
-    if (pdcb_valid)
-	peerDigestPDFinish(fetch, pcb_valid, err);
-    if (fcb_valid)
-	peerDigestFetchFinish(fetch, err);
-}
-
-
-/* destroys digest if peer disappeared
- * must be called only when fetch and pd cbdata are valid */
-static void
-peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err)
-{
-    PeerDigest *pd = fetch->pd;
-    const char *host = strBuf(pd->host);
-
-    pd->times.received = squid_curtime;
-    pd->times.req_delay = fetch->resp_time;
-    kb_incr(&pd->stats.sent.kbytes, (size_t) fetch->sent.bytes);
-    kb_incr(&pd->stats.recv.kbytes, (size_t) fetch->recv.bytes);
-    pd->stats.sent.msgs += fetch->sent.msg;
-    pd->stats.recv.msgs += fetch->recv.msg;
-
-    if (err) {
-	debug(72, 1) ("%sdisabling (%s) digest from %s\n",
-	    pcb_valid ? "temporary " : "",
-	    pd->req_result, host);
-
-	if (pd->cd) {
-	    cacheDigestDestroy(pd->cd);
-	    pd->cd = NULL;
-	}
-	pd->flags.usable = 0;
-
-	if (!pcb_valid)
-	    peerDigestNotePeerGone(pd);
-    } else {
-	assert(pcb_valid);
-
-	pd->flags.usable = 1;
-
-	/* XXX: ugly condition, but how? */
-	if (fetch->entry->store_status == STORE_OK)
-	    debug(72, 2) ("re-used old digest from %s\n", host);
-	else
-	    debug(72, 2) ("received valid digest from %s\n", host);
-    }
-    cbdataReferenceDone(fetch->pd);
-}
-
-/* free fetch state structures
- * must be called only when fetch cbdata is valid */
-static void
-peerDigestFetchFinish(DigestFetchState * fetch, int err)
-{
-    assert(fetch->entry && fetch->request);
-
-    if (fetch->old_entry) {
-	debug(72, 2) ("peerDigestFetchFinish: deleting old entry\n");
-	storeUnregister(fetch->sc, fetch->old_entry, fetch);
-	storeReleaseRequest(fetch->old_entry);
-	storeUnlockObject(fetch->old_entry);
-	fetch->old_entry = NULL;
-    }
-    /* update global stats */
-    kb_incr(&statCounter.cd.kbytes_sent, (size_t) fetch->sent.bytes);
-    kb_incr(&statCounter.cd.kbytes_recv, (size_t) fetch->recv.bytes);
-    statCounter.cd.msgs_sent += fetch->sent.msg;
-    statCounter.cd.msgs_recv += fetch->recv.msg;
-
-    /* unlock everything */
-    storeUnregister(fetch->sc, fetch->entry, fetch);
-    storeUnlockObject(fetch->entry);
-    requestUnlink(fetch->request);
-    fetch->entry = NULL;
-    fetch->request = NULL;
-    assert(fetch->pd == NULL);
-    cbdataFree(fetch);
-}
-
-/* calculate fetch stats after completion */
-static void
-peerDigestFetchSetStats(DigestFetchState * fetch)
-{
-    MemObject *mem;
-    assert(fetch->entry && fetch->request);
-
-    mem = fetch->entry->mem_obj;
-    assert(mem);
-
-    /* XXX: outgoing numbers are not precise */
-    /* XXX: we must distinguish between 304 hits and misses here */
-    fetch->sent.bytes = httpRequestPrefixLen(fetch->request);
-    fetch->recv.bytes = fetch->entry->store_status == STORE_PENDING ?
-	mem->inmem_hi : mem->object_sz;
-    fetch->sent.msg = fetch->recv.msg = 1;
-    fetch->expires = fetch->entry->expires;
-    fetch->resp_time = squid_curtime - fetch->start_time;
-
-    debug(72, 3) ("peerDigestFetchFinish: recv %d bytes in %d secs\n",
-	fetch->recv.bytes, (int) fetch->resp_time);
-    debug(72, 3) ("peerDigestFetchFinish: expires: %ld (%+d), lmt: %ld (%+d)\n",
-	(long int) fetch->expires, (int) (fetch->expires - squid_curtime),
-	(long int) fetch->entry->lastmod, (int) (fetch->entry->lastmod - squid_curtime));
-}
-
-
-static int
-peerDigestSetCBlock(PeerDigest * pd, const char *buf)
-{
-    StoreDigestCBlock cblock;
-    int freed_size = 0;
-    const char *host = strBuf(pd->host);
-
-    xmemcpy(&cblock, buf, sizeof(cblock));
-    /* network -> host conversions */
-    cblock.ver.current = ntohs(cblock.ver.current);
-    cblock.ver.required = ntohs(cblock.ver.required);
-    cblock.capacity = ntohl(cblock.capacity);
-    cblock.count = ntohl(cblock.count);
-    cblock.del_count = ntohl(cblock.del_count);
-    cblock.mask_size = ntohl(cblock.mask_size);
-    debug(72, 2) ("got digest cblock from %s; ver: %d (req: %d)\n",
-	host, (int) cblock.ver.current, (int) cblock.ver.required);
-    debug(72, 2) ("\t size: %d bytes, e-cnt: %d, e-util: %d%%\n",
-	cblock.mask_size, cblock.count,
-	xpercentInt(cblock.count, cblock.capacity));
-    /* check version requirements (both ways) */
-    if (cblock.ver.required > CacheDigestVer.current) {
-	debug(72, 1) ("%s digest requires version %d; have: %d\n",
-	    host, cblock.ver.required, CacheDigestVer.current);
-	return 0;
-    }
-    if (cblock.ver.current < CacheDigestVer.required) {
-	debug(72, 1) ("%s digest is version %d; we require: %d\n",
-	    host, cblock.ver.current, CacheDigestVer.required);
-	return 0;
-    }
-    /* check consistency */
-    if (cblock.ver.required > cblock.ver.current ||
-	cblock.mask_size <= 0 || cblock.capacity <= 0 ||
-	cblock.bits_per_entry <= 0 || cblock.hash_func_count <= 0) {
-	debug(72, 0) ("%s digest cblock is corrupted.\n", host);
-	return 0;
-    }
-    /* check consistency further */
-    if (cblock.mask_size != cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry)) {
-	debug(72, 0) ("%s digest cblock is corrupted (mask size mismatch: %d ? %d).\n",
-	    host, cblock.mask_size, cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry));
-	return 0;
-    }
-    /* there are some things we cannot do yet */
-    if (cblock.hash_func_count != CacheDigestHashFuncCount) {
-	debug(72, 0) ("%s digest: unsupported #hash functions: %d ? %d.\n",
-	    host, cblock.hash_func_count, CacheDigestHashFuncCount);
-	return 0;
-    }
-    /*
-     * no cblock bugs below this point
-     */
-    /* check size changes */
-    if (pd->cd && cblock.mask_size != pd->cd->mask_size) {
-	debug(72, 2) ("%s digest changed size: %d -> %d\n",
-	    host, cblock.mask_size, pd->cd->mask_size);
-	freed_size = pd->cd->mask_size;
-	cacheDigestDestroy(pd->cd);
-	pd->cd = NULL;
-    }
-    if (!pd->cd) {
-	debug(72, 2) ("creating %s digest; size: %d (%+d) bytes\n",
-	    host, cblock.mask_size, (int) (cblock.mask_size - freed_size));
-	pd->cd = cacheDigestCreate(cblock.capacity, cblock.bits_per_entry);
-	if (cblock.mask_size >= freed_size)
-	    kb_incr(&statCounter.cd.memory, cblock.mask_size - freed_size);
-    }
-    assert(pd->cd);
-    /* these assignments leave us in an inconsistent state until we finish reading the digest */
-    pd->cd->count = cblock.count;
-    pd->cd->del_count = cblock.del_count;
-    return 1;
-}
-
-static int
-peerDigestUseful(const PeerDigest * pd)
-{
-    /* TODO: we should calculate the prob of a false hit instead of bit util */
-    const int bit_util = cacheDigestBitUtil(pd->cd);
-    if (bit_util > 65) {
-	debug(72, 0) ("Warning: %s peer digest has too many bits on (%d%%).\n",
-	    strBuf(pd->host), bit_util);
-	return 0;
-    }
-    return 1;
-}
-
-static int
-saneDiff(time_t diff)
-{
-    return abs(diff) > squid_curtime / 2 ? 0 : diff;
-}
-
-void
-peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e)
-{
-#define f2s(flag) (pd->flags.flag ? "yes" : "no")
-#define appendTime(tm) storeAppendPrintf(e, "%s\t %10ld\t %+d\t %+d\n", \
-    ""#tm, (long int)pd->times.tm, \
-    saneDiff(pd->times.tm - squid_curtime), \
-    saneDiff(pd->times.tm - pd->times.initialized))
-
-    const char *host = pd ? strBuf(pd->host) : NULL;
-    assert(pd);
-
-    storeAppendPrintf(e, "\npeer digest from %s\n", host);
-
-    cacheDigestGuessStatsReport(&pd->stats.guess, e, host);
-
-    storeAppendPrintf(e, "\nevent\t timestamp\t secs from now\t secs from init\n");
-    appendTime(initialized);
-    appendTime(needed);
-    appendTime(requested);
-    appendTime(received);
-    appendTime(next_check);
-
-    storeAppendPrintf(e, "peer digest state:\n");
-    storeAppendPrintf(e, "\tneeded: %3s, usable: %3s, requested: %3s\n",
-	f2s(needed), f2s(usable), f2s(requested));
-    storeAppendPrintf(e, "\n\tlast retry delay: %d secs\n",
-	(int) pd->times.retry_delay);
-    storeAppendPrintf(e, "\tlast request response time: %d secs\n",
-	(int) pd->times.req_delay);
-    storeAppendPrintf(e, "\tlast request result: %s\n",
-	pd->req_result ? pd->req_result : "(none)");
-
-    storeAppendPrintf(e, "\npeer digest traffic:\n");
-    storeAppendPrintf(e, "\trequests sent: %d, volume: %d KB\n",
-	pd->stats.sent.msgs, (int) pd->stats.sent.kbytes.kb);
-    storeAppendPrintf(e, "\treplies recv:  %d, volume: %d KB\n",
-	pd->stats.recv.msgs, (int) pd->stats.recv.kbytes.kb);
-
-    storeAppendPrintf(e, "\npeer digest structure:\n");
-    if (pd->cd)
-	cacheDigestReport(pd->cd, host, e);
-    else
-	storeAppendPrintf(e, "\tno in-memory copy\n");
-}
-
-#endif
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/peer_digest.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,982 @@
+
+/*
+ * $Id: peer_digest.cc,v 1.1.2.1 2002/10/11 15:40:58 rbcollins Exp $
+ *
+ * DEBUG: section 72    Peer Digest Routines
+ * AUTHOR: Alex Rousskov
+ *
+ * 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"
+#include "Store.h"
+
+#if USE_CACHE_DIGESTS
+
+#include "StoreClient.h"
+
+/* local types */
+
+/* local prototypes */
+static time_t peerDigestIncDelay(const PeerDigest * pd);
+static time_t peerDigestNewDelay(const StoreEntry * e);
+static void peerDigestSetCheck(PeerDigest * pd, time_t delay);
+static void peerDigestClean(PeerDigest *);
+static EVH peerDigestCheck;
+static void peerDigestRequest(PeerDigest * pd);
+static STCB peerDigestHandleReply;
+static int peerDigestFetchReply(void *, char *, ssize_t);
+static int peerDigestSwapInHeaders(void *, char *, ssize_t);
+static int peerDigestSwapInCBlock(void *, char *, ssize_t);
+static int peerDigestSwapInMask(void *, char *, ssize_t);
+static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
+static void peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason);
+static void peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason);
+static void peerDigestReqFinish(DigestFetchState * fetch, char *buf, int, int, int, const char *reason, int err);
+static void peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err);
+static void peerDigestFetchFinish(DigestFetchState * fetch, int err);
+static void peerDigestFetchSetStats(DigestFetchState * fetch);
+static int peerDigestSetCBlock(PeerDigest * pd, const char *buf);
+static int peerDigestUseful(const PeerDigest * pd);
+
+
+/* local constants */
+
+#define StoreDigestCBlockSize sizeof(StoreDigestCBlock)
+
+/* min interval for requesting digests from a given peer */
+static const time_t PeerDigestReqMinGap = 5 * 60;	/* seconds */
+/* min interval for requesting digests (cumulative request stream) */
+static const time_t GlobDigestReqMinGap = 1 * 60;	/* seconds */
+
+/* local vars */
+
+static time_t pd_last_req_time = 0;	/* last call to Check */
+
+/* initialize peer digest */
+static void
+peerDigestInit(PeerDigest * pd, peer * p)
+{
+    assert(pd && p);
+
+    memset(pd, 0, sizeof(*pd));
+    pd->peer = p;
+    /* if peer disappears, we will know it's name */
+    stringInit(&pd->host, p->host);
+
+    pd->times.initialized = squid_curtime;
+}
+
+static void
+peerDigestClean(PeerDigest * pd)
+{
+    assert(pd);
+    if (pd->cd)
+	cacheDigestDestroy(pd->cd);
+    stringClean(&pd->host);
+}
+
+CBDATA_TYPE(PeerDigest);
+
+/* allocate new peer digest, call Init, and lock everything */
+PeerDigest *
+peerDigestCreate(peer * p)
+{
+    PeerDigest *pd;
+    assert(p);
+
+    CBDATA_INIT_TYPE(PeerDigest);
+    pd = cbdataAlloc(PeerDigest);
+    peerDigestInit(pd, p);
+
+    /* XXX This does not look right, and the same thing again in the caller */
+    return cbdataReference(pd);
+}
+
+/* call Clean and free/unlock everything */
+static void
+peerDigestDestroy(PeerDigest * pd)
+{
+    peer *p;
+    assert(pd);
+
+    /* inform peer (if any) that we are gone */
+    if (cbdataReferenceValidDone(pd->peer, (void **) &p))
+	peerNoteDigestGone(p);
+
+    peerDigestClean(pd);
+    cbdataFree(pd);
+}
+
+/* called by peer to indicate that somebody actually needs this digest */
+void
+peerDigestNeeded(PeerDigest * pd)
+{
+    assert(pd);
+    assert(!pd->flags.needed);
+    assert(!pd->cd);
+
+    pd->flags.needed = 1;
+    pd->times.needed = squid_curtime;
+    peerDigestSetCheck(pd, 0);	/* check asap */
+}
+
+/* currently we do not have a reason to disable without destroying */
+#if FUTURE_CODE
+/* disables peer for good */
+static void
+peerDigestDisable(PeerDigest * pd)
+{
+    debug(72, 2) ("peerDigestDisable: peer %s disabled for good\n",
+	strBuf(pd->host));
+    pd->times.disabled = squid_curtime;
+    pd->times.next_check = -1;	/* never */
+    pd->flags.usable = 0;
+
+    if (pd->cd) {
+	cacheDigestDestroy(pd->cd);
+	pd->cd = NULL;
+    }
+    /* we do not destroy the pd itself to preserve its "history" and stats */
+}
+#endif
+
+/* increment retry delay [after an unsuccessful attempt] */
+static time_t
+peerDigestIncDelay(const PeerDigest * pd)
+{
+    assert(pd);
+    return pd->times.retry_delay > 0 ?
+	2 * pd->times.retry_delay :	/* exponential backoff */
+	PeerDigestReqMinGap;	/* minimal delay */
+}
+
+/* artificially increases Expires: setting to avoid race conditions 
+ * returns the delay till that [increased] expiration time */
+static time_t
+peerDigestNewDelay(const StoreEntry * e)
+{
+    assert(e);
+    if (e->expires > 0)
+	return e->expires + PeerDigestReqMinGap - squid_curtime;
+    return PeerDigestReqMinGap;
+}
+
+/* registers next digest verification */
+static void
+peerDigestSetCheck(PeerDigest * pd, time_t delay)
+{
+    eventAdd("peerDigestCheck", peerDigestCheck, pd, (double) delay, 1);
+    pd->times.next_check = squid_curtime + delay;
+    debug(72, 3) ("peerDigestSetCheck: will check peer %s in %d secs\n",
+	strBuf(pd->host), (int) delay);
+}
+
+/*
+ * called when peer is about to disappear or have already disappeared
+ */
+void
+peerDigestNotePeerGone(PeerDigest * pd)
+{
+    if (pd->flags.requested) {
+	debug(72, 2) ("peerDigest: peer %s gone, will destroy after fetch.\n",
+	    strBuf(pd->host));
+	/* do nothing now, the fetching chain will notice and take action */
+    } else {
+	debug(72, 2) ("peerDigest: peer %s is gone, destroying now.\n",
+	    strBuf(pd->host));
+	peerDigestDestroy(pd);
+    }
+}
+
+/* callback for eventAdd() (with peer digest locked)
+ * request new digest if our copy is too old or if we lack one; 
+ * schedule next check otherwise */
+static void
+peerDigestCheck(void *data)
+{
+    PeerDigest *pd = (PeerDigest *)data;
+    time_t req_time;
+
+    assert(!pd->flags.requested);
+
+    pd->times.next_check = 0;	/* unknown */
+
+    if (!cbdataReferenceValid(pd->peer)) {
+	peerDigestNotePeerGone(pd);
+	return;
+    }
+    debug(72, 3) ("peerDigestCheck: peer %s:%d\n", pd->peer->host, pd->peer->http_port);
+    debug(72, 3) ("peerDigestCheck: time: %ld, last received: %ld (%+d)\n",
+	(long int) squid_curtime, (long int) pd->times.received, (int) (squid_curtime - pd->times.received));
+
+    /* decide when we should send the request:
+     * request now unless too close to other requests */
+    req_time = squid_curtime;
+
+    /* per-peer limit */
+    if (req_time - pd->times.received < PeerDigestReqMinGap) {
+	debug(72, 2) ("peerDigestCheck: %s, avoiding close peer requests (%d < %d secs).\n",
+	    strBuf(pd->host), (int) (req_time - pd->times.received),
+	    (int) PeerDigestReqMinGap);
+	req_time = pd->times.received + PeerDigestReqMinGap;
+    }
+    /* global limit */
+    if (req_time - pd_last_req_time < GlobDigestReqMinGap) {
+	debug(72, 2) ("peerDigestCheck: %s, avoiding close requests (%d < %d secs).\n",
+	    strBuf(pd->host), (int) (req_time - pd_last_req_time),
+	    (int) GlobDigestReqMinGap);
+	req_time = pd_last_req_time + GlobDigestReqMinGap;
+    }
+    if (req_time <= squid_curtime)
+	peerDigestRequest(pd);	/* will set pd->flags.requested */
+    else
+	peerDigestSetCheck(pd, req_time - squid_curtime);
+}
+
+CBDATA_TYPE(DigestFetchState);
+
+/* ask store for a digest */
+static void
+peerDigestRequest(PeerDigest * pd)
+{
+    peer *p = pd->peer;
+    StoreEntry *e, *old_e;
+    char *url;
+    const cache_key *key;
+    request_t *req;
+    DigestFetchState *fetch = NULL;
+    StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
+
+    pd->req_result = NULL;
+    pd->flags.requested = 1;
+
+    /* compute future request components */
+    if (p->digest_url)
+	url = xstrdup(p->digest_url);
+    else
+	url = internalRemoteUri(p->host, p->http_port,
+	    "/squid-internal-periodic/", StoreDigestFileName);
+
+    req = urlParse(METHOD_GET, url);
+    assert(req);
+    key = storeKeyPublicByRequest(req);
+    debug(72, 2) ("peerDigestRequest: %s key: %s\n", url, storeKeyText(key));
+
+    /* add custom headers */
+    assert(!req->header.len);
+    httpHeaderPutStr(&req->header, HDR_ACCEPT, StoreDigestMimeStr);
+    httpHeaderPutStr(&req->header, HDR_ACCEPT, "text/html");
+    if (p->login)
+	xstrncpy(req->login, p->login, MAX_LOGIN_SZ);
+    /* create fetch state structure */
+    CBDATA_INIT_TYPE(DigestFetchState);
+    fetch = cbdataAlloc(DigestFetchState);
+    fetch->request = requestLink(req);
+    fetch->pd = cbdataReference(pd);
+    fetch->offset = 0;
+    fetch->state = DIGEST_READ_REPLY;
+
+    /* update timestamps */
+    fetch->start_time = squid_curtime;
+    pd->times.requested = squid_curtime;
+    pd_last_req_time = squid_curtime;
+
+    req->flags.cachable = 1;
+    /* the rest is based on clientProcessExpired() */
+    req->flags.refresh = 1;
+    old_e = fetch->old_entry = storeGet(key);
+    if (old_e) {
+	debug(72, 5) ("peerDigestRequest: found old entry\n");
+	storeLockObject(old_e);
+	storeCreateMemObject(old_e, url, url);
+	fetch->old_sc = storeClientListAdd(old_e, fetch);
+    }
+    e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method);
+    assert(EBIT_TEST(e->flags, KEY_PRIVATE));
+    fetch->sc = storeClientListAdd(e, fetch);
+    /* set lastmod to trigger IMS request if possible */
+    if (old_e)
+	e->lastmod = old_e->lastmod;
+
+    /* push towards peer cache */
+    debug(72, 3) ("peerDigestRequest: forwarding to fwdStart...\n");
+    fwdStart(-1, e, req);
+    tempBuffer.offset = 0;
+    tempBuffer.length =  SM_PAGE_SIZE;
+    tempBuffer.data = fetch->buf;
+    storeClientCopy(fetch->sc, e, tempBuffer,
+	peerDigestHandleReply, fetch);
+}
+
+
+/* Handle the data copying .. */
+
+/*
+ * This routine handles the copy data and then redirects the
+ * copy to a bunch of subfunctions depending upon the copy state.
+ * It also tracks the buffer offset and "seen", since I'm actually
+ * not interested in rewriting everything to suit my little idea.
+ */
+static void
+peerDigestHandleReply(void *data, StoreIOBuffer recievedData)
+{
+    DigestFetchState *fetch = (DigestFetchState *)data;
+    PeerDigest *pd = fetch->pd;
+    int retsize = -1;
+    digest_read_state_t prevstate;
+    int newsize;
+
+    assert(pd && recievedData.data);
+    /* The existing code assumes that the recieved pointer is
+     * where we asked the data to be put
+     */
+    assert(fetch->buf + fetch->bufofs == recievedData.data);
+
+    /* Update the buffer size */
+    fetch->bufofs += recievedData.length;
+
+    assert(fetch->bufofs <= SM_PAGE_SIZE);
+
+    /* If we've fetched enough, return */
+    if (peerDigestFetchedEnough(fetch, fetch->buf, fetch->bufofs, "peerDigestHandleReply"))
+	return;
+
+    /* Call the right function based on the state */
+    /* (Those functions will update the state if needed) */
+
+    /* Give us a temporary reference. Some of the calls we make may
+     * try to destroy the fetch structure, and we like to know if they
+     * do
+     */
+    fetch = cbdataReference(fetch);
+
+    /* Repeat this loop until we're out of data OR the state changes */
+    /* (So keep going if the state has changed and we still have data */
+    do {
+	prevstate = fetch->state;
+	switch (fetch->state) {
+	case DIGEST_READ_REPLY:
+	    retsize = peerDigestFetchReply(fetch, fetch->buf, fetch->bufofs);
+	    break;
+	case DIGEST_READ_HEADERS:
+	    retsize = peerDigestSwapInHeaders(fetch, fetch->buf, fetch->bufofs);
+	    break;
+	case DIGEST_READ_CBLOCK:
+	    retsize = peerDigestSwapInCBlock(fetch, fetch->buf, fetch->bufofs);
+	    break;
+	case DIGEST_READ_MASK:
+	    retsize = peerDigestSwapInMask(fetch, fetch->buf, fetch->bufofs);
+	    break;
+	case DIGEST_READ_NONE:
+	    break;
+	case DIGEST_READ_DONE:
+	    goto finish;
+	    break;
+	default:
+	    fatal("Bad digest transfer mode!\n");
+	}
+
+	if (retsize < 0)
+	    goto finish;
+	/*
+	 * The returned size indicates how much of the buffer was read -
+	 * so move the remainder of the buffer to the beginning
+	 * and update the bufofs / bufsize
+	 */
+	newsize = fetch->bufofs - retsize;
+	xmemmove(fetch->buf, fetch->buf + retsize, fetch->bufofs - newsize);
+	fetch->bufofs = newsize;
+
+    } while (cbdataReferenceValid(fetch) && prevstate != fetch->state && fetch->bufofs > 0);
+
+    /* Update the copy offset */
+    fetch->offset += recievedData.length;
+
+    /* Schedule another copy */
+    if (cbdataReferenceValid(fetch)) {
+	StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
+	tempBuffer.offset = fetch->offset;
+	tempBuffer.length = SM_PAGE_SIZE - fetch->bufofs;
+	tempBuffer.data = fetch->buf + fetch->bufofs;
+	storeClientCopy(fetch->sc, fetch->entry, tempBuffer,
+	    peerDigestHandleReply, fetch);
+    }
+  finish:
+    /* Get rid of our reference, we've finished with it for now */
+    cbdataReferenceDone(fetch);
+}
+
+
+
+/* wait for full http headers to be received then parse them */
+/*
+ * This routine handles parsing the reply line.
+ * If the reply line indicates an OK, the same data is thrown
+ * to SwapInHeaders(). If the reply line is a NOT_MODIFIED,
+ * we simply stop parsing.
+ */
+static int
+peerDigestFetchReply(void *data, char *buf, ssize_t size)
+{
+    DigestFetchState *fetch = (DigestFetchState *)data;
+    PeerDigest *pd = fetch->pd;
+    size_t hdr_size;
+    assert(pd && buf);
+    assert(!fetch->offset);
+
+    assert(fetch->state == DIGEST_READ_REPLY);
+    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
+	return -1;
+
+    if ((hdr_size = headersEnd(buf, size))) {
+	http_status status;
+	HttpReply *reply = fetch->entry->mem_obj->reply;
+	assert(reply);
+	httpReplyParse(reply, buf, hdr_size);
+	status = reply->sline.status;
+	debug(72, 3) ("peerDigestFetchReply: %s status: %d, expires: %ld (%+d)\n",
+	    strBuf(pd->host), status,
+	    (long int) reply->expires, (int) (reply->expires - squid_curtime));
+
+	/* this "if" is based on clientHandleIMSReply() */
+	if (status == HTTP_NOT_MODIFIED) {
+	    request_t *r = NULL;
+	    /* our old entry is fine */
+	    assert(fetch->old_entry);
+	    if (!fetch->old_entry->mem_obj->request)
+		fetch->old_entry->mem_obj->request = r =
+		    requestLink(fetch->entry->mem_obj->request);
+	    assert(fetch->old_entry->mem_obj->request);
+	    httpReplyUpdateOnNotModified(fetch->old_entry->mem_obj->reply, reply);
+	    storeTimestampsSet(fetch->old_entry);
+	    /* get rid of 304 reply */
+	    storeUnregister(fetch->sc, fetch->entry, fetch);
+	    storeUnlockObject(fetch->entry);
+	    fetch->entry = fetch->old_entry;
+	    fetch->old_entry = NULL;
+	    /* preserve request -- we need its size to update counters */
+	    /* requestUnlink(r); */
+	    /* fetch->entry->mem_obj->request = NULL; */
+	} else if (status == HTTP_OK) {
+	    /* get rid of old entry if any */
+	    if (fetch->old_entry) {
+		debug(72, 3) ("peerDigestFetchReply: got new digest, releasing old one\n");
+		storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
+		storeReleaseRequest(fetch->old_entry);
+		storeUnlockObject(fetch->old_entry);
+		fetch->old_entry = NULL;
+	    }
+	} else {
+	    /* some kind of a bug */
+	    peerDigestFetchAbort(fetch, buf, httpStatusLineReason(&reply->sline));
+	    return -1;		/* XXX -1 will abort stuff in ReadReply! */
+	}
+	/* must have a ready-to-use store entry if we got here */
+	/* can we stay with the old in-memory digest? */
+	if (status == HTTP_NOT_MODIFIED && fetch->pd->cd) {
+	    peerDigestFetchStop(fetch, buf, "Not modified");
+	    fetch->state = DIGEST_READ_DONE;
+	} else {
+	    fetch->state = DIGEST_READ_HEADERS;
+	}
+    } else {
+	/* need more data, do we have space? */
+	if (size >= SM_PAGE_SIZE)
+	    peerDigestFetchAbort(fetch, buf, "reply header too big");
+    }
+
+    /* We don't want to actually ack that we've handled anything,
+     * otherwise SwapInHeaders() won't get the reply line .. */
+    return 0;
+}
+
+/* fetch headers from disk, pass on to SwapInCBlock */
+static int
+peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
+{
+    DigestFetchState *fetch = (DigestFetchState *)data;
+    size_t hdr_size;
+
+    assert(fetch->state == DIGEST_READ_HEADERS);
+    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
+	return -1;
+
+    assert(!fetch->offset);
+    if ((hdr_size = headersEnd(buf, size))) {
+	assert(fetch->entry->mem_obj->reply);
+	if (!fetch->entry->mem_obj->reply->sline.status)
+	    httpReplyParse(fetch->entry->mem_obj->reply, buf, hdr_size);
+	if (fetch->entry->mem_obj->reply->sline.status != HTTP_OK) {
+	    debug(72, 1) ("peerDigestSwapInHeaders: %s status %d got cached!\n",
+		strBuf(fetch->pd->host), fetch->entry->mem_obj->reply->sline.status);
+	    peerDigestFetchAbort(fetch, buf, "internal status error");
+	    return -1;
+	}
+	fetch->state = DIGEST_READ_CBLOCK;
+	return hdr_size;	/* Say how much data we read */
+    } else {
+	/* need more data, do we have space? */
+	if (size >= SM_PAGE_SIZE) {
+	    peerDigestFetchAbort(fetch, buf, "stored header too big");
+	    return -1;
+	} else {
+	    return 0;		/* We need to read more to parse .. */
+	}
+    }
+    fatal("peerDigestSwapInHeaders() - shouldn't get here!\n");
+}
+
+static int
+peerDigestSwapInCBlock(void *data, char *buf, ssize_t size)
+{
+    DigestFetchState *fetch = (DigestFetchState *)data;
+
+    assert(fetch->state == DIGEST_READ_CBLOCK);
+    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock"))
+	return -1;
+
+    if (size >= (ssize_t)StoreDigestCBlockSize) {
+	PeerDigest *pd = fetch->pd;
+	HttpReply *rep = fetch->entry->mem_obj->reply;
+
+	assert(pd && rep);
+	if (peerDigestSetCBlock(pd, buf)) {
+	    /* XXX: soon we will have variable header size */
+	    /* switch to CD buffer and fetch digest guts */
+	    buf = NULL;
+	    assert(pd->cd->mask);
+	    fetch->state = DIGEST_READ_MASK;
+	    return StoreDigestCBlockSize;
+	} else {
+	    peerDigestFetchAbort(fetch, buf, "invalid digest cblock");
+	    return -1;
+	}
+    } else {
+	/* need more data, do we have space? */
+	if (size >= SM_PAGE_SIZE) {
+	    peerDigestFetchAbort(fetch, buf, "digest cblock too big");
+	    return -1;
+	} else {
+	    return 0;		/* We need more data */
+	}
+    }
+    fatal("peerDigestSwapInCBlock(): shouldn't get here!\n");
+}
+
+static int
+peerDigestSwapInMask(void *data, char *buf, ssize_t size)
+{
+    DigestFetchState *fetch = (DigestFetchState *)data;
+    PeerDigest *pd;
+
+    pd = fetch->pd;
+    assert(pd->cd && pd->cd->mask);
+
+    /*
+     * NOTENOTENOTENOTENOTE: buf doesn't point to pd->cd->mask anymore!
+     * we need to do the copy ourselves!
+     */
+    xmemcpy(pd->cd->mask + fetch->mask_offset, buf, size);
+
+    /* NOTE! buf points to the middle of pd->cd->mask! */
+    if (peerDigestFetchedEnough(fetch, NULL, size, "peerDigestSwapInMask"))
+	return -1;
+
+    fetch->mask_offset += size;
+    if (fetch->mask_offset >= (off_t)pd->cd->mask_size) {
+	debug(72, 2) ("peerDigestSwapInMask: Done! Got %d, expected %d\n",
+	    fetch->mask_offset, pd->cd->mask_size);
+	assert(fetch->mask_offset == (off_t)pd->cd->mask_size);
+	assert(peerDigestFetchedEnough(fetch, NULL, 0, "peerDigestSwapInMask"));
+	return -1;		/* XXX! */
+    } else {
+	/* We always read everything, so return so */
+	return size;
+    }
+    fatal("peerDigestSwapInMask(): shouldn't get here!\n");
+}
+
+static int
+peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name)
+{
+    PeerDigest *pd = NULL;
+    const char *host = "<unknown>";	/* peer host */
+    const char *reason = NULL;	/* reason for completion */
+    const char *no_bug = NULL;	/* successful completion if set */
+    const int pdcb_valid = cbdataReferenceValid(fetch->pd);
+    const int pcb_valid = cbdataReferenceValid(fetch->pd->peer);
+
+    /* test possible exiting conditions (the same for most steps!)
+     * cases marked with '?!' should not happen */
+
+    if (!reason) {
+	if (!(pd = fetch->pd))
+	    reason = "peer digest disappeared?!";
+#if DONT			/* WHY NOT? /HNO */
+	else if (!cbdataReferenceValid(pd))
+	    reason = "invalidated peer digest?!";
+#endif
+	else
+	    host = strBuf(pd->host);
+    }
+    debug(72, 6) ("%s: peer %s, offset: %d size: %d.\n",
+	step_name, host,
+	fetch->offset, size);
+
+    /* continue checking (with pd and host known and valid) */
+    if (!reason) {
+	if (!cbdataReferenceValid(pd->peer))
+	    reason = "peer disappeared";
+	else if (size < 0)
+	    reason = "swap failure";
+	else if (!fetch->entry)
+	    reason = "swap aborted?!";
+	else if (EBIT_TEST(fetch->entry->flags, ENTRY_ABORTED))
+	    reason = "swap aborted";
+    }
+    /* continue checking (maybe-successful eof case) */
+    if (!reason && !size) {
+	if (!pd->cd)
+	    reason = "null digest?!";
+	else if (fetch->mask_offset != (off_t)pd->cd->mask_size)
+	    reason = "premature end of digest?!";
+	else if (!peerDigestUseful(pd))
+	    reason = "useless digest";
+	else
+	    reason = no_bug = "success";
+    }
+    /* finish if we have a reason */
+    if (reason) {
+	const int level = strstr(reason, "?!") ? 1 : 3;
+	debug(72, level) ("%s: peer %s, exiting after '%s'\n",
+	    step_name, host, reason);
+	peerDigestReqFinish(fetch, buf,
+	    1, pdcb_valid, pcb_valid, reason, !no_bug);
+    } else {
+	/* paranoid check */
+	assert(pdcb_valid && pcb_valid);
+    }
+    return reason != NULL;
+}
+
+/* call this when all callback data is valid and fetch must be stopped but
+ * no error has occurred (e.g. we received 304 reply and reuse old digest) */
+static void
+peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason)
+{
+    assert(reason);
+    debug(72, 2) ("peerDigestFetchStop: peer %s, reason: %s\n",
+	strBuf(fetch->pd->host), reason);
+    peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 0);
+}
+
+/* call this when all callback data is valid but something bad happened */
+static void
+peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason)
+{
+    assert(reason);
+    debug(72, 2) ("peerDigestFetchAbort: peer %s, reason: %s\n",
+	strBuf(fetch->pd->host), reason);
+    peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 1);
+}
+
+/* complete the digest transfer, update stats, unlock/release everything */
+static void
+peerDigestReqFinish(DigestFetchState * fetch, char *buf,
+    int fcb_valid, int pdcb_valid, int pcb_valid,
+    const char *reason, int err)
+{
+    assert(reason);
+
+    /* must go before peerDigestPDFinish */
+    if (pdcb_valid) {
+	fetch->pd->flags.requested = 0;
+	fetch->pd->req_result = reason;
+    }
+    /* schedule next check if peer is still out there */
+    if (pcb_valid) {
+	PeerDigest *pd = fetch->pd;
+	if (err) {
+	    pd->times.retry_delay = peerDigestIncDelay(pd);
+	    peerDigestSetCheck(pd, pd->times.retry_delay);
+	} else {
+	    pd->times.retry_delay = 0;
+	    peerDigestSetCheck(pd, peerDigestNewDelay(fetch->entry));
+	}
+    }
+    /* note: order is significant */
+    if (fcb_valid)
+	peerDigestFetchSetStats(fetch);
+    if (pdcb_valid)
+	peerDigestPDFinish(fetch, pcb_valid, err);
+    if (fcb_valid)
+	peerDigestFetchFinish(fetch, err);
+}
+
+
+/* destroys digest if peer disappeared
+ * must be called only when fetch and pd cbdata are valid */
+static void
+peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err)
+{
+    PeerDigest *pd = fetch->pd;
+    const char *host = strBuf(pd->host);
+
+    pd->times.received = squid_curtime;
+    pd->times.req_delay = fetch->resp_time;
+    kb_incr(&pd->stats.sent.kbytes, (size_t) fetch->sent.bytes);
+    kb_incr(&pd->stats.recv.kbytes, (size_t) fetch->recv.bytes);
+    pd->stats.sent.msgs += fetch->sent.msg;
+    pd->stats.recv.msgs += fetch->recv.msg;
+
+    if (err) {
+	debug(72, 1) ("%sdisabling (%s) digest from %s\n",
+	    pcb_valid ? "temporary " : "",
+	    pd->req_result, host);
+
+	if (pd->cd) {
+	    cacheDigestDestroy(pd->cd);
+	    pd->cd = NULL;
+	}
+	pd->flags.usable = 0;
+
+	if (!pcb_valid)
+	    peerDigestNotePeerGone(pd);
+    } else {
+	assert(pcb_valid);
+
+	pd->flags.usable = 1;
+
+	/* XXX: ugly condition, but how? */
+	if (fetch->entry->store_status == STORE_OK)
+	    debug(72, 2) ("re-used old digest from %s\n", host);
+	else
+	    debug(72, 2) ("received valid digest from %s\n", host);
+    }
+    cbdataReferenceDone(fetch->pd);
+}
+
+/* free fetch state structures
+ * must be called only when fetch cbdata is valid */
+static void
+peerDigestFetchFinish(DigestFetchState * fetch, int err)
+{
+    assert(fetch->entry && fetch->request);
+
+    if (fetch->old_entry) {
+	debug(72, 2) ("peerDigestFetchFinish: deleting old entry\n");
+	storeUnregister(fetch->sc, fetch->old_entry, fetch);
+	storeReleaseRequest(fetch->old_entry);
+	storeUnlockObject(fetch->old_entry);
+	fetch->old_entry = NULL;
+    }
+    /* update global stats */
+    kb_incr(&statCounter.cd.kbytes_sent, (size_t) fetch->sent.bytes);
+    kb_incr(&statCounter.cd.kbytes_recv, (size_t) fetch->recv.bytes);
+    statCounter.cd.msgs_sent += fetch->sent.msg;
+    statCounter.cd.msgs_recv += fetch->recv.msg;
+
+    /* unlock everything */
+    storeUnregister(fetch->sc, fetch->entry, fetch);
+    storeUnlockObject(fetch->entry);
+    requestUnlink(fetch->request);
+    fetch->entry = NULL;
+    fetch->request = NULL;
+    assert(fetch->pd == NULL);
+    cbdataFree(fetch);
+}
+
+/* calculate fetch stats after completion */
+static void
+peerDigestFetchSetStats(DigestFetchState * fetch)
+{
+    MemObject *mem;
+    assert(fetch->entry && fetch->request);
+
+    mem = fetch->entry->mem_obj;
+    assert(mem);
+
+    /* XXX: outgoing numbers are not precise */
+    /* XXX: we must distinguish between 304 hits and misses here */
+    fetch->sent.bytes = httpRequestPrefixLen(fetch->request);
+    fetch->recv.bytes = fetch->entry->store_status == STORE_PENDING ?
+	mem->inmem_hi : mem->object_sz;
+    fetch->sent.msg = fetch->recv.msg = 1;
+    fetch->expires = fetch->entry->expires;
+    fetch->resp_time = squid_curtime - fetch->start_time;
+
+    debug(72, 3) ("peerDigestFetchFinish: recv %d bytes in %d secs\n",
+	fetch->recv.bytes, (int) fetch->resp_time);
+    debug(72, 3) ("peerDigestFetchFinish: expires: %ld (%+d), lmt: %ld (%+d)\n",
+	(long int) fetch->expires, (int) (fetch->expires - squid_curtime),
+	(long int) fetch->entry->lastmod, (int) (fetch->entry->lastmod - squid_curtime));
+}
+
+
+static int
+peerDigestSetCBlock(PeerDigest * pd, const char *buf)
+{
+    StoreDigestCBlock cblock;
+    int freed_size = 0;
+    const char *host = strBuf(pd->host);
+
+    xmemcpy(&cblock, buf, sizeof(cblock));
+    /* network -> host conversions */
+    cblock.ver.current = ntohs(cblock.ver.current);
+    cblock.ver.required = ntohs(cblock.ver.required);
+    cblock.capacity = ntohl(cblock.capacity);
+    cblock.count = ntohl(cblock.count);
+    cblock.del_count = ntohl(cblock.del_count);
+    cblock.mask_size = ntohl(cblock.mask_size);
+    debug(72, 2) ("got digest cblock from %s; ver: %d (req: %d)\n",
+	host, (int) cblock.ver.current, (int) cblock.ver.required);
+    debug(72, 2) ("\t size: %d bytes, e-cnt: %d, e-util: %d%%\n",
+	cblock.mask_size, cblock.count,
+	xpercentInt(cblock.count, cblock.capacity));
+    /* check version requirements (both ways) */
+    if (cblock.ver.required > CacheDigestVer.current) {
+	debug(72, 1) ("%s digest requires version %d; have: %d\n",
+	    host, cblock.ver.required, CacheDigestVer.current);
+	return 0;
+    }
+    if (cblock.ver.current < CacheDigestVer.required) {
+	debug(72, 1) ("%s digest is version %d; we require: %d\n",
+	    host, cblock.ver.current, CacheDigestVer.required);
+	return 0;
+    }
+    /* check consistency */
+    if (cblock.ver.required > cblock.ver.current ||
+	cblock.mask_size <= 0 || cblock.capacity <= 0 ||
+	cblock.bits_per_entry <= 0 || cblock.hash_func_count <= 0) {
+	debug(72, 0) ("%s digest cblock is corrupted.\n", host);
+	return 0;
+    }
+    /* check consistency further */
+    if ((size_t)cblock.mask_size != cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry)) {
+	debug(72, 0) ("%s digest cblock is corrupted (mask size mismatch: %d ? %d).\n",
+	    host, cblock.mask_size, cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry));
+	return 0;
+    }
+    /* there are some things we cannot do yet */
+    if (cblock.hash_func_count != CacheDigestHashFuncCount) {
+	debug(72, 0) ("%s digest: unsupported #hash functions: %d ? %d.\n",
+	    host, cblock.hash_func_count, CacheDigestHashFuncCount);
+	return 0;
+    }
+    /*
+     * no cblock bugs below this point
+     */
+    /* check size changes */
+    if (pd->cd && cblock.mask_size != (ssize_t)pd->cd->mask_size) {
+	debug(72, 2) ("%s digest changed size: %d -> %d\n",
+	    host, cblock.mask_size, pd->cd->mask_size);
+	freed_size = pd->cd->mask_size;
+	cacheDigestDestroy(pd->cd);
+	pd->cd = NULL;
+    }
+    if (!pd->cd) {
+	debug(72, 2) ("creating %s digest; size: %d (%+d) bytes\n",
+	    host, cblock.mask_size, (int) (cblock.mask_size - freed_size));
+	pd->cd = cacheDigestCreate(cblock.capacity, cblock.bits_per_entry);
+	if (cblock.mask_size >= freed_size)
+	    kb_incr(&statCounter.cd.memory, cblock.mask_size - freed_size);
+    }
+    assert(pd->cd);
+    /* these assignments leave us in an inconsistent state until we finish reading the digest */
+    pd->cd->count = cblock.count;
+    pd->cd->del_count = cblock.del_count;
+    return 1;
+}
+
+static int
+peerDigestUseful(const PeerDigest * pd)
+{
+    /* TODO: we should calculate the prob of a false hit instead of bit util */
+    const int bit_util = cacheDigestBitUtil(pd->cd);
+    if (bit_util > 65) {
+	debug(72, 0) ("Warning: %s peer digest has too many bits on (%d%%).\n",
+	    strBuf(pd->host), bit_util);
+	return 0;
+    }
+    return 1;
+}
+
+static int
+saneDiff(time_t diff)
+{
+    return abs(diff) > squid_curtime / 2 ? 0 : diff;
+}
+
+void
+peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e)
+{
+#define f2s(flag) (pd->flags.flag ? "yes" : "no")
+#define appendTime(tm) storeAppendPrintf(e, "%s\t %10ld\t %+d\t %+d\n", \
+    ""#tm, (long int)pd->times.tm, \
+    saneDiff(pd->times.tm - squid_curtime), \
+    saneDiff(pd->times.tm - pd->times.initialized))
+
+    const char *host = pd ? strBuf(pd->host) : NULL;
+    assert(pd);
+
+    storeAppendPrintf(e, "\npeer digest from %s\n", host);
+
+    cacheDigestGuessStatsReport(&pd->stats.guess, e, host);
+
+    storeAppendPrintf(e, "\nevent\t timestamp\t secs from now\t secs from init\n");
+    appendTime(initialized);
+    appendTime(needed);
+    appendTime(requested);
+    appendTime(received);
+    appendTime(next_check);
+
+    storeAppendPrintf(e, "peer digest state:\n");
+    storeAppendPrintf(e, "\tneeded: %3s, usable: %3s, requested: %3s\n",
+	f2s(needed), f2s(usable), f2s(requested));
+    storeAppendPrintf(e, "\n\tlast retry delay: %d secs\n",
+	(int) pd->times.retry_delay);
+    storeAppendPrintf(e, "\tlast request response time: %d secs\n",
+	(int) pd->times.req_delay);
+    storeAppendPrintf(e, "\tlast request result: %s\n",
+	pd->req_result ? pd->req_result : "(none)");
+
+    storeAppendPrintf(e, "\npeer digest traffic:\n");
+    storeAppendPrintf(e, "\trequests sent: %d, volume: %d KB\n",
+	pd->stats.sent.msgs, (int) pd->stats.sent.kbytes.kb);
+    storeAppendPrintf(e, "\treplies recv:  %d, volume: %d KB\n",
+	pd->stats.recv.msgs, (int) pd->stats.recv.kbytes.kb);
+
+    storeAppendPrintf(e, "\npeer digest structure:\n");
+    if (pd->cd)
+	cacheDigestReport(pd->cd, host, e);
+    else
+	storeAppendPrintf(e, "\tno in-memory copy\n");
+}
+
+#endif
--- squid/src/peer_select.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,667 +0,0 @@
-
-/*
- * $Id: peer_select.c,v 1.16.6.2 2002/10/08 02:25:37 rbcollins Exp $
- *
- * DEBUG: section 44    Peer Selection Algorithm
- * AUTHOR: Duane Wessels
- *
- * 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"
-#include "Store.h"
-#include "ICP.h"
-
-const char *hier_strings[] =
-{
-    "NONE",
-    "DIRECT",
-    "SIBLING_HIT",
-    "PARENT_HIT",
-    "DEFAULT_PARENT",
-    "SINGLE_PARENT",
-    "FIRST_UP_PARENT",
-    "FIRST_PARENT_MISS",
-    "CLOSEST_PARENT_MISS",
-    "CLOSEST_PARENT",
-    "CLOSEST_DIRECT",
-    "NO_DIRECT_FAIL",
-    "SOURCE_FASTEST",
-    "ROUNDROBIN_PARENT",
-#if USE_CACHE_DIGESTS
-    "CD_PARENT_HIT",
-    "CD_SIBLING_HIT",
-#endif
-#if USE_CARP
-    "CARP",
-#endif
-    "ANY_PARENT",
-    "INVALID CODE"
-};
-
-static struct {
-    int timeouts;
-} PeerStats;
-
-static const char *DirectStr[] =
-{
-    "DIRECT_UNKNOWN",
-    "DIRECT_NO",
-    "DIRECT_MAYBE",
-    "DIRECT_YES"
-};
-
-static void peerSelectFoo(ps_state *);
-static void peerPingTimeout(void *data);
-static void peerSelectCallback(ps_state * psstate);
-static IRCB peerHandlePingReply;
-static void peerSelectStateFree(ps_state * psstate);
-static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
-#if USE_HTCP
-static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
-static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
-#endif
-static int peerCheckNetdbDirect(ps_state * psstate);
-static void peerGetSomeNeighbor(ps_state *);
-static void peerGetSomeNeighborReplies(ps_state *);
-static void peerGetSomeDirect(ps_state *);
-static void peerGetSomeParent(ps_state *);
-static void peerGetAllParents(ps_state *);
-static void peerAddFwdServer(FwdServer **, peer *, hier_code);
-
-static void
-peerSelectStateFree(ps_state * psstate)
-{
-    if (psstate->acl_checklist) {
-	debug(44, 1) ("calling aclChecklistFree() from peerSelectStateFree\n");
-	aclChecklistFree(psstate->acl_checklist);
-    }
-    requestUnlink(psstate->request);
-    psstate->request = NULL;
-    if (psstate->entry) {
-	assert(psstate->entry->ping_status != PING_WAITING);
-	storeUnlockObject(psstate->entry);
-	psstate->entry = NULL;
-    }
-    cbdataFree(psstate);
-}
-
-static int
-peerSelectIcpPing(request_t * request, int direct, StoreEntry * entry)
-{
-    int n;
-    assert(entry);
-    assert(entry->ping_status == PING_NONE);
-    assert(direct != DIRECT_YES);
-    debug(44, 3) ("peerSelectIcpPing: %s\n", storeUrl(entry));
-    if (!request->flags.hierarchical && direct != DIRECT_NO)
-	return 0;
-    if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
-	if (direct != DIRECT_NO)
-	    return 0;
-    n = neighborsCount(request);
-    debug(44, 3) ("peerSelectIcpPing: counted %d neighbors\n", n);
-    return n;
-}
-
-
-void
-peerSelect(request_t * request,
-    StoreEntry * entry,
-    PSC * callback,
-    void *callback_data)
-{
-    ps_state *psstate;
-    if (entry)
-	debug(44, 3) ("peerSelect: %s\n", storeUrl(entry));
-    else
-	debug(44, 3) ("peerSelect: %s\n", RequestMethodStr[request->method]);
-    psstate = cbdataAlloc(ps_state);
-    psstate->request = requestLink(request);
-    psstate->entry = entry;
-    psstate->callback = callback;
-    psstate->callback_data = cbdataReference(callback_data);
-    psstate->direct = DIRECT_UNKNOWN;
-#if USE_CACHE_DIGESTS
-    request->hier.peer_select_start = current_time;
-#endif
-    if (psstate->entry)
-	storeLockObject(psstate->entry);
-    peerSelectFoo(psstate);
-}
-
-static void
-peerCheckNeverDirectDone(int answer, void *data)
-{
-    ps_state *psstate = data;
-    psstate->acl_checklist = NULL;
-    debug(44, 3) ("peerCheckNeverDirectDone: %d\n", answer);
-    psstate->never_direct = answer ? 1 : -1;
-    peerSelectFoo(psstate);
-}
-
-static void
-peerCheckAlwaysDirectDone(int answer, void *data)
-{
-    ps_state *psstate = data;
-    psstate->acl_checklist = NULL;
-    debug(44, 3) ("peerCheckAlwaysDirectDone: %d\n", answer);
-    psstate->always_direct = answer ? 1 : -1;
-    peerSelectFoo(psstate);
-}
-
-static void
-peerSelectCallback(ps_state * psstate)
-{
-    StoreEntry *entry = psstate->entry;
-    FwdServer *fs = psstate->servers;
-    PSC *callback;
-    void *cbdata;
-    if (entry) {
-	debug(44, 3) ("peerSelectCallback: %s\n", storeUrl(entry));
-	if (entry->ping_status == PING_WAITING)
-	    eventDelete(peerPingTimeout, psstate);
-	entry->ping_status = PING_DONE;
-    }
-    if (fs == NULL) {
-	debug(44, 1) ("Failed to select source for '%s'\n", storeUrl(entry));
-	debug(44, 1) ("  always_direct = %d\n", psstate->always_direct);
-	debug(44, 1) ("   never_direct = %d\n", psstate->never_direct);
-	debug(44, 1) ("       timedout = %d\n", psstate->ping.timedout);
-    }
-    psstate->ping.stop = current_time;
-    psstate->request->hier.ping = psstate->ping;
-    callback = psstate->callback;
-    psstate->callback = NULL;
-    if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
-	psstate->servers = NULL;
-	callback(fs, cbdata);
-    }
-    peerSelectStateFree(psstate);
-}
-
-static int
-peerCheckNetdbDirect(ps_state * psstate)
-{
-    peer *p;
-    int myrtt;
-    int myhops;
-    if (psstate->direct == DIRECT_NO)
-	return 0;
-    myrtt = netdbHostRtt(psstate->request->host);
-    debug(44, 3) ("peerCheckNetdbDirect: MY RTT = %d msec\n", myrtt);
-    debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_rtt = %d msec\n",
-	Config.minDirectRtt);
-    if (myrtt && myrtt <= Config.minDirectRtt)
-	return 1;
-    myhops = netdbHostHops(psstate->request->host);
-    debug(44, 3) ("peerCheckNetdbDirect: MY hops = %d\n", myhops);
-    debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_hops = %d\n",
-	Config.minDirectHops);
-    if (myhops && myhops <= Config.minDirectHops)
-	return 1;
-    p = whichPeer(&psstate->closest_parent_miss);
-    if (p == NULL)
-	return 0;
-    debug(44, 3) ("peerCheckNetdbDirect: closest_parent_miss RTT = %d msec\n",
-	psstate->ping.p_rtt);
-    if (myrtt && myrtt <= psstate->ping.p_rtt)
-	return 1;
-    return 0;
-}
-
-static void
-peerSelectFoo(ps_state * ps)
-{
-    StoreEntry *entry = ps->entry;
-    request_t *request = ps->request;
-    debug(44, 3) ("peerSelectFoo: '%s %s'\n",
-	RequestMethodStr[request->method],
-	request->host);
-    if (ps->direct == DIRECT_UNKNOWN) {
-	if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
-	    ps->acl_checklist = aclChecklistCreate(
-		Config.accessList.AlwaysDirect,
-		request,
-		NULL);		/* ident */
-	    aclNBCheck(ps->acl_checklist,
-		peerCheckAlwaysDirectDone,
-		ps);
-	    return;
-	} else if (ps->always_direct > 0) {
-	    ps->direct = DIRECT_YES;
-	} else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
-	    ps->acl_checklist = aclChecklistCreate(
-		Config.accessList.NeverDirect,
-		request,
-		NULL);		/* ident */
-	    aclNBCheck(ps->acl_checklist,
-		peerCheckNeverDirectDone,
-		ps);
-	    return;
-	} else if (ps->never_direct > 0) {
-	    ps->direct = DIRECT_NO;
-	} else if (request->flags.loopdetect) {
-	    ps->direct = DIRECT_YES;
-	} else if (peerCheckNetdbDirect(ps)) {
-	    ps->direct = DIRECT_YES;
-	} else {
-	    ps->direct = DIRECT_MAYBE;
-	}
-	debug(44, 3) ("peerSelectFoo: direct = %s\n",
-	    DirectStr[ps->direct]);
-    }
-    if (entry == NULL) {
-	(void) 0;
-    } else if (entry->ping_status == PING_NONE) {
-	peerGetSomeNeighbor(ps);
-	if (entry->ping_status == PING_WAITING)
-	    return;
-    } else if (entry->ping_status == PING_WAITING) {
-	peerGetSomeNeighborReplies(ps);
-	entry->ping_status = PING_DONE;
-    }
-    switch (ps->direct) {
-    case DIRECT_YES:
-	peerGetSomeDirect(ps);
-	break;
-    case DIRECT_NO:
-	peerGetSomeParent(ps);
-	peerGetAllParents(ps);
-	break;
-    default:
-	if (Config.onoff.prefer_direct)
-	    peerGetSomeDirect(ps);
-	if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct)
-	    peerGetSomeParent(ps);
-	if (!Config.onoff.prefer_direct)
-	    peerGetSomeDirect(ps);
-	break;
-    }
-    peerSelectCallback(ps);
-}
-
-/*
- * peerGetSomeNeighbor
- * 
- * Selects a neighbor (parent or sibling) based on one of the
- * following methods:
- *      Cache Digests
- *      CARP
- *      Netdb RTT estimates
- *      ICP/HTCP queries
- */
-static void
-peerGetSomeNeighbor(ps_state * ps)
-{
-    StoreEntry *entry = ps->entry;
-    request_t *request = ps->request;
-    peer *p;
-    hier_code code = HIER_NONE;
-    assert(entry->ping_status == PING_NONE);
-    if (ps->direct == DIRECT_YES) {
-	entry->ping_status = PING_DONE;
-	return;
-    }
-#if USE_CACHE_DIGESTS
-    if ((p = neighborsDigestSelect(request))) {
-	if (neighborType(p, request) == PEER_PARENT)
-	    code = CD_PARENT_HIT;
-	else
-	    code = CD_SIBLING_HIT;
-    } else
-#endif
-    if ((p = netdbClosestParent(request))) {
-	code = CLOSEST_PARENT;
-    } else if (peerSelectIcpPing(request, ps->direct, entry)) {
-	debug(44, 3) ("peerSelect: Doing ICP pings\n");
-	ps->ping.start = current_time;
-	ps->ping.n_sent = neighborsUdpPing(request,
-	    entry,
-	    peerHandlePingReply,
-	    ps,
-	    &ps->ping.n_replies_expected,
-	    &ps->ping.timeout);
-	if (ps->ping.n_sent == 0)
-	    debug(44, 0) ("WARNING: neighborsUdpPing returned 0\n");
-	debug(44, 3) ("peerSelect: %d ICP replies expected, RTT %d msec\n",
-	    ps->ping.n_replies_expected, ps->ping.timeout);
-	if (ps->ping.n_replies_expected > 0) {
-	    entry->ping_status = PING_WAITING;
-	    eventAdd("peerPingTimeout",
-		peerPingTimeout,
-		ps,
-		0.001 * ps->ping.timeout,
-		0);
-	    return;
-	}
-    }
-    if (code != HIER_NONE) {
-	assert(p);
-	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
-	peerAddFwdServer(&ps->servers, p, code);
-    }
-    entry->ping_status = PING_DONE;
-}
-
-/*
- * peerGetSomeNeighborReplies
- * 
- * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
- */
-static void
-peerGetSomeNeighborReplies(ps_state * ps)
-{
-    request_t *request = ps->request;
-    peer *p = NULL;
-    hier_code code = HIER_NONE;
-    assert(ps->entry->ping_status == PING_WAITING);
-    assert(ps->direct != DIRECT_YES);
-    if (peerCheckNetdbDirect(ps)) {
-	code = CLOSEST_DIRECT;
-	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
-	peerAddFwdServer(&ps->servers, NULL, code);
-	return;
-    }
-    if ((p = ps->hit)) {
-	code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
-    } else
-#if ALLOW_SOURCE_PING
-    if ((p = ps->secho)) {
-	code = SOURCE_FASTEST;
-    } else
-#endif
-    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
-	p = whichPeer(&ps->closest_parent_miss);
-	code = CLOSEST_PARENT_MISS;
-    } else if (ps->first_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
-	p = whichPeer(&ps->first_parent_miss);
-	code = FIRST_PARENT_MISS;
-    }
-    if (p && code != HIER_NONE) {
-	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
-	peerAddFwdServer(&ps->servers, p, code);
-    }
-}
-
-
-/*
- * peerGetSomeDirect
- * 
- * Simply adds a 'direct' entry to the FwdServers list if this
- * request can be forwarded directly to the origin server
- */
-static void
-peerGetSomeDirect(ps_state * ps)
-{
-    if (ps->direct == DIRECT_NO)
-	return;
-    if (ps->request->protocol == PROTO_WAIS)
-	/* Its not really DIRECT, now is it? */
-	peerAddFwdServer(&ps->servers, Config.Wais._peer, DIRECT);
-    else
-	peerAddFwdServer(&ps->servers, NULL, DIRECT);
-}
-
-static void
-peerGetSomeParent(ps_state * ps)
-{
-    peer *p;
-    request_t *request = ps->request;
-    hier_code code = HIER_NONE;
-    debug(44, 3) ("peerGetSomeParent: %s %s\n",
-	RequestMethodStr[request->method],
-	request->host);
-    if (ps->direct == DIRECT_YES)
-	return;
-    if ((p = getDefaultParent(request))) {
-	code = DEFAULT_PARENT;
-#if USE_CARP
-    } else if ((p = carpSelectParent(request))) {
-	code = CARP;
-#endif
-    } else if ((p = getRoundRobinParent(request))) {
-	code = ROUNDROBIN_PARENT;
-    } else if ((p = getWeightedRoundRobinParent(request))) {
-	code = ROUNDROBIN_PARENT;
-    } else if ((p = getFirstUpParent(request))) {
-	code = FIRSTUP_PARENT;
-    } else if ((p = getAnyParent(request))) {
-	code = ANY_OLD_PARENT;
-    }
-    if (code != HIER_NONE) {
-	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
-	peerAddFwdServer(&ps->servers, p, code);
-    }
-}
-
-/* Adds alive parents. Used as a last resort for never_direct.
- */
-static void
-peerGetAllParents(ps_state * ps)
-{
-    peer *p;
-    request_t *request = ps->request;
-    /* Add all alive parents */
-    for (p = Config.peers; p; p = p->next) {
-	/* XXX: neighbors.c lacks a public interface for enumerating
-	 * parents to a request so we have to dig some here..
-	 */
-	if (neighborType(p, request) != PEER_PARENT)
-	    continue;
-	if (!peerHTTPOkay(p, request))
-	    continue;
-	debug(15, 3) ("peerGetAllParents: adding alive parent %s\n", p->host);
-	peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
-    }
-    /* XXX: should add dead parents here, but it is currently
-     * not possible to find out which parents are dead or which
-     * simply are not configured to handle the request.
-     */
-    /* Add default parent as a last resort */
-    if ((p = getDefaultParent(request))) {
-	peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
-    }
-}
-
-static void
-peerPingTimeout(void *data)
-{
-    ps_state *psstate = data;
-    StoreEntry *entry = psstate->entry;
-    if (entry)
-	debug(44, 3) ("peerPingTimeout: '%s'\n", storeUrl(entry));
-    if (!cbdataReferenceValid(psstate->callback_data)) {
-	/* request aborted */
-	entry->ping_status = PING_DONE;
-	cbdataReferenceDone(psstate->callback_data);
-	peerSelectStateFree(psstate);
-	return;
-    }
-    PeerStats.timeouts++;
-    psstate->ping.timedout = 1;
-    peerSelectFoo(psstate);
-}
-
-void
-peerSelectInit(void)
-{
-    memset(&PeerStats, '\0', sizeof(PeerStats));
-    assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *));
-}
-
-static void
-peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
-{
-    int rtt;
-    int hops;
-    if (Config.onoff.query_icmp) {
-	if (header->flags & ICP_FLAG_SRC_RTT) {
-	    rtt = header->pad & 0xFFFF;
-	    hops = (header->pad >> 16) & 0xFFFF;
-	    if (rtt > 0 && rtt < 0xFFFF)
-		netdbUpdatePeer(ps->request, p, rtt, hops);
-	    if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
-		ps->closest_parent_miss = p->in_addr;
-		ps->ping.p_rtt = rtt;
-	    }
-	}
-    }
-    /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
-    if (p->options.closest_only)
-	return;
-    /* set FIRST_MISS if there is no CLOSEST parent */
-    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
-	return;
-    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
-    if (rtt < 1)
-	rtt = 1;
-    if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr ||
-	rtt < ps->ping.w_rtt) {
-	ps->first_parent_miss = p->in_addr;
-	ps->ping.w_rtt = rtt;
-    }
-}
-
-static void
-peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
-{
-    ps_state *psstate = data;
-    icp_opcode op = header->opcode;
-    debug(44, 3) ("peerHandleIcpReply: %s %s\n",
-	icp_opcode_str[op],
-	storeUrl(psstate->entry));
-#if USE_CACHE_DIGESTS && 0
-    /* do cd lookup to count false misses */
-    if (p && request)
-	peerNoteDigestLookup(request, p,
-	    peerDigestLookup(p, request, psstate->entry));
-#endif
-    psstate->ping.n_recv++;
-    if (op == ICP_MISS || op == ICP_DECHO) {
-	if (type == PEER_PARENT)
-	    peerIcpParentMiss(p, header, psstate);
-    } else if (op == ICP_HIT) {
-	psstate->hit = p;
-	psstate->hit_type = type;
-	peerSelectFoo(psstate);
-	return;
-    }
-#if ALLOW_SOURCE_PING
-    else if (op == ICP_SECHO) {
-	psstate->secho = p;
-	peerSelectFoo(psstate);
-	return;
-    }
-#endif
-    if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
-	return;
-    peerSelectFoo(psstate);
-}
-
-#if USE_HTCP
-static void
-peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
-{
-    ps_state *psstate = data;
-    debug(44, 3) ("peerHandleIcpReply: %s %s\n",
-	htcp->hit ? "HIT" : "MISS",
-	storeUrl(psstate->entry));
-    psstate->ping.n_recv++;
-    if (htcp->hit) {
-	psstate->hit = p;
-	psstate->hit_type = type;
-	peerSelectFoo(psstate);
-	return;
-    }
-    if (type == PEER_PARENT)
-	peerHtcpParentMiss(p, htcp, psstate);
-    if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
-	return;
-    peerSelectFoo(psstate);
-}
-
-static void
-peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
-{
-    int rtt;
-    int hops;
-    if (Config.onoff.query_icmp) {
-	if (htcp->cto.rtt > 0) {
-	    rtt = (int) htcp->cto.rtt * 1000;
-	    hops = (int) htcp->cto.hops * 1000;
-	    netdbUpdatePeer(ps->request, p, rtt, hops);
-	    if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
-		ps->closest_parent_miss = p->in_addr;
-		ps->ping.p_rtt = rtt;
-	    }
-	}
-    }
-    /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
-    if (p->options.closest_only)
-	return;
-    /* set FIRST_MISS if there is no CLOSEST parent */
-    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
-	return;
-    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
-    if (rtt < 1)
-	rtt = 1;
-    if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr ||
-	rtt < ps->ping.w_rtt) {
-	ps->first_parent_miss = p->in_addr;
-	ps->ping.w_rtt = rtt;
-    }
-}
-#endif
-
-static void
-peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
-{
-    if (proto == PROTO_ICP)
-	peerHandleIcpReply(p, type, pingdata, data);
-#if USE_HTCP
-    else if (proto == PROTO_HTCP)
-	peerHandleHtcpReply(p, type, pingdata, data);
-#endif
-    else
-	debug(44, 1) ("peerHandlePingReply: unknown protocol_t %d\n", (int) proto);
-}
-
-static void
-peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code)
-{
-    FwdServer *fs = memAllocate(MEM_FWD_SERVER);
-    debug(44, 5) ("peerAddFwdServer: adding %s %s\n",
-	p ? p->host : "DIRECT",
-	hier_strings[code]);
-    fs->_peer = cbdataReference(p);
-    fs->code = code;
-    while (*FS)
-	FS = &(*FS)->next;
-    *FS = fs;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/peer_select.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,667 @@
+
+/*
+ * $Id: peer_select.cc,v 1.1.2.1 2002/10/11 15:40:59 rbcollins Exp $
+ *
+ * DEBUG: section 44    Peer Selection Algorithm
+ * AUTHOR: Duane Wessels
+ *
+ * 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"
+#include "Store.h"
+#include "ICP.h"
+
+const char *hier_strings[] =
+{
+    "NONE",
+    "DIRECT",
+    "SIBLING_HIT",
+    "PARENT_HIT",
+    "DEFAULT_PARENT",
+    "SINGLE_PARENT",
+    "FIRST_UP_PARENT",
+    "FIRST_PARENT_MISS",
+    "CLOSEST_PARENT_MISS",
+    "CLOSEST_PARENT",
+    "CLOSEST_DIRECT",
+    "NO_DIRECT_FAIL",
+    "SOURCE_FASTEST",
+    "ROUNDROBIN_PARENT",
+#if USE_CACHE_DIGESTS
+    "CD_PARENT_HIT",
+    "CD_SIBLING_HIT",
+#endif
+#if USE_CARP
+    "CARP",
+#endif
+    "ANY_PARENT",
+    "INVALID CODE"
+};
+
+static struct {
+    int timeouts;
+} PeerStats;
+
+static const char *DirectStr[] =
+{
+    "DIRECT_UNKNOWN",
+    "DIRECT_NO",
+    "DIRECT_MAYBE",
+    "DIRECT_YES"
+};
+
+static void peerSelectFoo(ps_state *);
+static void peerPingTimeout(void *data);
+static void peerSelectCallback(ps_state * psstate);
+static IRCB peerHandlePingReply;
+static void peerSelectStateFree(ps_state * psstate);
+static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
+#if USE_HTCP
+static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
+static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
+#endif
+static int peerCheckNetdbDirect(ps_state * psstate);
+static void peerGetSomeNeighbor(ps_state *);
+static void peerGetSomeNeighborReplies(ps_state *);
+static void peerGetSomeDirect(ps_state *);
+static void peerGetSomeParent(ps_state *);
+static void peerGetAllParents(ps_state *);
+static void peerAddFwdServer(FwdServer **, peer *, hier_code);
+
+static void
+peerSelectStateFree(ps_state * psstate)
+{
+    if (psstate->acl_checklist) {
+	debug(44, 1) ("calling aclChecklistFree() from peerSelectStateFree\n");
+	aclChecklistFree(psstate->acl_checklist);
+    }
+    requestUnlink(psstate->request);
+    psstate->request = NULL;
+    if (psstate->entry) {
+	assert(psstate->entry->ping_status != PING_WAITING);
+	storeUnlockObject(psstate->entry);
+	psstate->entry = NULL;
+    }
+    cbdataFree(psstate);
+}
+
+static int
+peerSelectIcpPing(request_t * request, int direct, StoreEntry * entry)
+{
+    int n;
+    assert(entry);
+    assert(entry->ping_status == PING_NONE);
+    assert(direct != DIRECT_YES);
+    debug(44, 3) ("peerSelectIcpPing: %s\n", storeUrl(entry));
+    if (!request->flags.hierarchical && direct != DIRECT_NO)
+	return 0;
+    if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
+	if (direct != DIRECT_NO)
+	    return 0;
+    n = neighborsCount(request);
+    debug(44, 3) ("peerSelectIcpPing: counted %d neighbors\n", n);
+    return n;
+}
+
+
+void
+peerSelect(request_t * request,
+    StoreEntry * entry,
+    PSC * callback,
+    void *callback_data)
+{
+    ps_state *psstate;
+    if (entry)
+	debug(44, 3) ("peerSelect: %s\n", storeUrl(entry));
+    else
+	debug(44, 3) ("peerSelect: %s\n", RequestMethodStr[request->method]);
+    psstate = cbdataAlloc(ps_state);
+    psstate->request = requestLink(request);
+    psstate->entry = entry;
+    psstate->callback = callback;
+    psstate->callback_data = cbdataReference(callback_data);
+    psstate->direct = DIRECT_UNKNOWN;
+#if USE_CACHE_DIGESTS
+    request->hier.peer_select_start = current_time;
+#endif
+    if (psstate->entry)
+	storeLockObject(psstate->entry);
+    peerSelectFoo(psstate);
+}
+
+static void
+peerCheckNeverDirectDone(int answer, void *data)
+{
+    ps_state *psstate = (ps_state *) data;
+    psstate->acl_checklist = NULL;
+    debug(44, 3) ("peerCheckNeverDirectDone: %d\n", answer);
+    psstate->never_direct = answer ? 1 : -1;
+    peerSelectFoo(psstate);
+}
+
+static void
+peerCheckAlwaysDirectDone(int answer, void *data)
+{
+    ps_state *psstate = (ps_state *)data;
+    psstate->acl_checklist = NULL;
+    debug(44, 3) ("peerCheckAlwaysDirectDone: %d\n", answer);
+    psstate->always_direct = answer ? 1 : -1;
+    peerSelectFoo(psstate);
+}
+
+static void
+peerSelectCallback(ps_state * psstate)
+{
+    StoreEntry *entry = psstate->entry;
+    FwdServer *fs = psstate->servers;
+    PSC *callback;
+    void *cbdata;
+    if (entry) {
+	debug(44, 3) ("peerSelectCallback: %s\n", storeUrl(entry));
+	if (entry->ping_status == PING_WAITING)
+	    eventDelete(peerPingTimeout, psstate);
+	entry->ping_status = PING_DONE;
+    }
+    if (fs == NULL) {
+	debug(44, 1) ("Failed to select source for '%s'\n", storeUrl(entry));
+	debug(44, 1) ("  always_direct = %d\n", psstate->always_direct);
+	debug(44, 1) ("   never_direct = %d\n", psstate->never_direct);
+	debug(44, 1) ("       timedout = %d\n", psstate->ping.timedout);
+    }
+    psstate->ping.stop = current_time;
+    psstate->request->hier.ping = psstate->ping;
+    callback = psstate->callback;
+    psstate->callback = NULL;
+    if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
+	psstate->servers = NULL;
+	callback(fs, cbdata);
+    }
+    peerSelectStateFree(psstate);
+}
+
+static int
+peerCheckNetdbDirect(ps_state * psstate)
+{
+    peer *p;
+    int myrtt;
+    int myhops;
+    if (psstate->direct == DIRECT_NO)
+	return 0;
+    myrtt = netdbHostRtt(psstate->request->host);
+    debug(44, 3) ("peerCheckNetdbDirect: MY RTT = %d msec\n", myrtt);
+    debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_rtt = %d msec\n",
+	Config.minDirectRtt);
+    if (myrtt && myrtt <= Config.minDirectRtt)
+	return 1;
+    myhops = netdbHostHops(psstate->request->host);
+    debug(44, 3) ("peerCheckNetdbDirect: MY hops = %d\n", myhops);
+    debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_hops = %d\n",
+	Config.minDirectHops);
+    if (myhops && myhops <= Config.minDirectHops)
+	return 1;
+    p = whichPeer(&psstate->closest_parent_miss);
+    if (p == NULL)
+	return 0;
+    debug(44, 3) ("peerCheckNetdbDirect: closest_parent_miss RTT = %d msec\n",
+	psstate->ping.p_rtt);
+    if (myrtt && myrtt <= psstate->ping.p_rtt)
+	return 1;
+    return 0;
+}
+
+static void
+peerSelectFoo(ps_state * ps)
+{
+    StoreEntry *entry = ps->entry;
+    request_t *request = ps->request;
+    debug(44, 3) ("peerSelectFoo: '%s %s'\n",
+	RequestMethodStr[request->method],
+	request->host);
+    if (ps->direct == DIRECT_UNKNOWN) {
+	if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
+	    ps->acl_checklist = aclChecklistCreate(
+		Config.accessList.AlwaysDirect,
+		request,
+		NULL);		/* ident */
+	    aclNBCheck(ps->acl_checklist,
+		peerCheckAlwaysDirectDone,
+		ps);
+	    return;
+	} else if (ps->always_direct > 0) {
+	    ps->direct = DIRECT_YES;
+	} else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
+	    ps->acl_checklist = aclChecklistCreate(
+		Config.accessList.NeverDirect,
+		request,
+		NULL);		/* ident */
+	    aclNBCheck(ps->acl_checklist,
+		peerCheckNeverDirectDone,
+		ps);
+	    return;
+	} else if (ps->never_direct > 0) {
+	    ps->direct = DIRECT_NO;
+	} else if (request->flags.loopdetect) {
+	    ps->direct = DIRECT_YES;
+	} else if (peerCheckNetdbDirect(ps)) {
+	    ps->direct = DIRECT_YES;
+	} else {
+	    ps->direct = DIRECT_MAYBE;
+	}
+	debug(44, 3) ("peerSelectFoo: direct = %s\n",
+	    DirectStr[ps->direct]);
+    }
+    if (entry == NULL) {
+	(void) 0;
+    } else if (entry->ping_status == PING_NONE) {
+	peerGetSomeNeighbor(ps);
+	if (entry->ping_status == PING_WAITING)
+	    return;
+    } else if (entry->ping_status == PING_WAITING) {
+	peerGetSomeNeighborReplies(ps);
+	entry->ping_status = PING_DONE;
+    }
+    switch (ps->direct) {
+    case DIRECT_YES:
+	peerGetSomeDirect(ps);
+	break;
+    case DIRECT_NO:
+	peerGetSomeParent(ps);
+	peerGetAllParents(ps);
+	break;
+    default:
+	if (Config.onoff.prefer_direct)
+	    peerGetSomeDirect(ps);
+	if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct)
+	    peerGetSomeParent(ps);
+	if (!Config.onoff.prefer_direct)
+	    peerGetSomeDirect(ps);
+	break;
+    }
+    peerSelectCallback(ps);
+}
+
+/*
+ * peerGetSomeNeighbor
+ * 
+ * Selects a neighbor (parent or sibling) based on one of the
+ * following methods:
+ *      Cache Digests
+ *      CARP
+ *      Netdb RTT estimates
+ *      ICP/HTCP queries
+ */
+static void
+peerGetSomeNeighbor(ps_state * ps)
+{
+    StoreEntry *entry = ps->entry;
+    request_t *request = ps->request;
+    peer *p;
+    hier_code code = HIER_NONE;
+    assert(entry->ping_status == PING_NONE);
+    if (ps->direct == DIRECT_YES) {
+	entry->ping_status = PING_DONE;
+	return;
+    }
+#if USE_CACHE_DIGESTS
+    if ((p = neighborsDigestSelect(request))) {
+	if (neighborType(p, request) == PEER_PARENT)
+	    code = CD_PARENT_HIT;
+	else
+	    code = CD_SIBLING_HIT;
+    } else
+#endif
+    if ((p = netdbClosestParent(request))) {
+	code = CLOSEST_PARENT;
+    } else if (peerSelectIcpPing(request, ps->direct, entry)) {
+	debug(44, 3) ("peerSelect: Doing ICP pings\n");
+	ps->ping.start = current_time;
+	ps->ping.n_sent = neighborsUdpPing(request,
+	    entry,
+	    peerHandlePingReply,
+	    ps,
+	    &ps->ping.n_replies_expected,
+	    &ps->ping.timeout);
+	if (ps->ping.n_sent == 0)
+	    debug(44, 0) ("WARNING: neighborsUdpPing returned 0\n");
+	debug(44, 3) ("peerSelect: %d ICP replies expected, RTT %d msec\n",
+	    ps->ping.n_replies_expected, ps->ping.timeout);
+	if (ps->ping.n_replies_expected > 0) {
+	    entry->ping_status = PING_WAITING;
+	    eventAdd("peerPingTimeout",
+		peerPingTimeout,
+		ps,
+		0.001 * ps->ping.timeout,
+		0);
+	    return;
+	}
+    }
+    if (code != HIER_NONE) {
+	assert(p);
+	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
+	peerAddFwdServer(&ps->servers, p, code);
+    }
+    entry->ping_status = PING_DONE;
+}
+
+/*
+ * peerGetSomeNeighborReplies
+ * 
+ * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
+ */
+static void
+peerGetSomeNeighborReplies(ps_state * ps)
+{
+    request_t *request = ps->request;
+    peer *p = NULL;
+    hier_code code = HIER_NONE;
+    assert(ps->entry->ping_status == PING_WAITING);
+    assert(ps->direct != DIRECT_YES);
+    if (peerCheckNetdbDirect(ps)) {
+	code = CLOSEST_DIRECT;
+	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
+	peerAddFwdServer(&ps->servers, NULL, code);
+	return;
+    }
+    if ((p = ps->hit)) {
+	code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
+    } else
+#if ALLOW_SOURCE_PING
+    if ((p = ps->secho)) {
+	code = SOURCE_FASTEST;
+    } else
+#endif
+    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
+	p = whichPeer(&ps->closest_parent_miss);
+	code = CLOSEST_PARENT_MISS;
+    } else if (ps->first_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
+	p = whichPeer(&ps->first_parent_miss);
+	code = FIRST_PARENT_MISS;
+    }
+    if (p && code != HIER_NONE) {
+	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
+	peerAddFwdServer(&ps->servers, p, code);
+    }
+}
+
+
+/*
+ * peerGetSomeDirect
+ * 
+ * Simply adds a 'direct' entry to the FwdServers list if this
+ * request can be forwarded directly to the origin server
+ */
+static void
+peerGetSomeDirect(ps_state * ps)
+{
+    if (ps->direct == DIRECT_NO)
+	return;
+    if (ps->request->protocol == PROTO_WAIS)
+	/* Its not really DIRECT, now is it? */
+	peerAddFwdServer(&ps->servers, Config.Wais._peer, DIRECT);
+    else
+	peerAddFwdServer(&ps->servers, NULL, DIRECT);
+}
+
+static void
+peerGetSomeParent(ps_state * ps)
+{
+    peer *p;
+    request_t *request = ps->request;
+    hier_code code = HIER_NONE;
+    debug(44, 3) ("peerGetSomeParent: %s %s\n",
+	RequestMethodStr[request->method],
+	request->host);
+    if (ps->direct == DIRECT_YES)
+	return;
+    if ((p = getDefaultParent(request))) {
+	code = DEFAULT_PARENT;
+#if USE_CARP
+    } else if ((p = carpSelectParent(request))) {
+	code = CARP;
+#endif
+    } else if ((p = getRoundRobinParent(request))) {
+	code = ROUNDROBIN_PARENT;
+    } else if ((p = getWeightedRoundRobinParent(request))) {
+	code = ROUNDROBIN_PARENT;
+    } else if ((p = getFirstUpParent(request))) {
+	code = FIRSTUP_PARENT;
+    } else if ((p = getAnyParent(request))) {
+	code = ANY_OLD_PARENT;
+    }
+    if (code != HIER_NONE) {
+	debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
+	peerAddFwdServer(&ps->servers, p, code);
+    }
+}
+
+/* Adds alive parents. Used as a last resort for never_direct.
+ */
+static void
+peerGetAllParents(ps_state * ps)
+{
+    peer *p;
+    request_t *request = ps->request;
+    /* Add all alive parents */
+    for (p = Config.peers; p; p = p->next) {
+	/* XXX: neighbors.c lacks a public interface for enumerating
+	 * parents to a request so we have to dig some here..
+	 */
+	if (neighborType(p, request) != PEER_PARENT)
+	    continue;
+	if (!peerHTTPOkay(p, request))
+	    continue;
+	debug(15, 3) ("peerGetAllParents: adding alive parent %s\n", p->host);
+	peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
+    }
+    /* XXX: should add dead parents here, but it is currently
+     * not possible to find out which parents are dead or which
+     * simply are not configured to handle the request.
+     */
+    /* Add default parent as a last resort */
+    if ((p = getDefaultParent(request))) {
+	peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
+    }
+}
+
+static void
+peerPingTimeout(void *data)
+{
+    ps_state *psstate = (ps_state *)data;
+    StoreEntry *entry = psstate->entry;
+    if (entry)
+	debug(44, 3) ("peerPingTimeout: '%s'\n", storeUrl(entry));
+    if (!cbdataReferenceValid(psstate->callback_data)) {
+	/* request aborted */
+	entry->ping_status = PING_DONE;
+	cbdataReferenceDone(psstate->callback_data);
+	peerSelectStateFree(psstate);
+	return;
+    }
+    PeerStats.timeouts++;
+    psstate->ping.timedout = 1;
+    peerSelectFoo(psstate);
+}
+
+void
+peerSelectInit(void)
+{
+    memset(&PeerStats, '\0', sizeof(PeerStats));
+    assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *));
+}
+
+static void
+peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
+{
+    int rtt;
+    int hops;
+    if (Config.onoff.query_icmp) {
+	if (header->flags & ICP_FLAG_SRC_RTT) {
+	    rtt = header->pad & 0xFFFF;
+	    hops = (header->pad >> 16) & 0xFFFF;
+	    if (rtt > 0 && rtt < 0xFFFF)
+		netdbUpdatePeer(ps->request, p, rtt, hops);
+	    if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
+		ps->closest_parent_miss = p->in_addr;
+		ps->ping.p_rtt = rtt;
+	    }
+	}
+    }
+    /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
+    if (p->options.closest_only)
+	return;
+    /* set FIRST_MISS if there is no CLOSEST parent */
+    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
+	return;
+    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
+    if (rtt < 1)
+	rtt = 1;
+    if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr ||
+	rtt < ps->ping.w_rtt) {
+	ps->first_parent_miss = p->in_addr;
+	ps->ping.w_rtt = rtt;
+    }
+}
+
+static void
+peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
+{
+    ps_state *psstate = (ps_state *)data;
+    icp_opcode op = header->getOpCode();
+    debug(44, 3) ("peerHandleIcpReply: %s %s\n",
+	icp_opcode_str[op],
+	storeUrl(psstate->entry));
+#if USE_CACHE_DIGESTS && 0
+    /* do cd lookup to count false misses */
+    if (p && request)
+	peerNoteDigestLookup(request, p,
+	    peerDigestLookup(p, request, psstate->entry));
+#endif
+    psstate->ping.n_recv++;
+    if (op == ICP_MISS || op == ICP_DECHO) {
+	if (type == PEER_PARENT)
+	    peerIcpParentMiss(p, header, psstate);
+    } else if (op == ICP_HIT) {
+	psstate->hit = p;
+	psstate->hit_type = type;
+	peerSelectFoo(psstate);
+	return;
+    }
+#if ALLOW_SOURCE_PING
+    else if (op == ICP_SECHO) {
+	psstate->secho = p;
+	peerSelectFoo(psstate);
+	return;
+    }
+#endif
+    if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
+	return;
+    peerSelectFoo(psstate);
+}
+
+#if USE_HTCP
+static void
+peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
+{
+    ps_state *psstate = (ps_state *)data;
+    debug(44, 3) ("peerHandleIcpReply: %s %s\n",
+	htcp->hit ? "HIT" : "MISS",
+	storeUrl(psstate->entry));
+    psstate->ping.n_recv++;
+    if (htcp->hit) {
+	psstate->hit = p;
+	psstate->hit_type = type;
+	peerSelectFoo(psstate);
+	return;
+    }
+    if (type == PEER_PARENT)
+	peerHtcpParentMiss(p, htcp, psstate);
+    if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
+	return;
+    peerSelectFoo(psstate);
+}
+
+static void
+peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
+{
+    int rtt;
+    int hops;
+    if (Config.onoff.query_icmp) {
+	if (htcp->cto.rtt > 0) {
+	    rtt = (int) htcp->cto.rtt * 1000;
+	    hops = (int) htcp->cto.hops * 1000;
+	    netdbUpdatePeer(ps->request, p, rtt, hops);
+	    if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
+		ps->closest_parent_miss = p->in_addr;
+		ps->ping.p_rtt = rtt;
+	    }
+	}
+    }
+    /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
+    if (p->options.closest_only)
+	return;
+    /* set FIRST_MISS if there is no CLOSEST parent */
+    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
+	return;
+    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
+    if (rtt < 1)
+	rtt = 1;
+    if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr ||
+	rtt < ps->ping.w_rtt) {
+	ps->first_parent_miss = p->in_addr;
+	ps->ping.w_rtt = rtt;
+    }
+}
+#endif
+
+static void
+peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
+{
+    if (proto == PROTO_ICP)
+	peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
+#if USE_HTCP
+    else if (proto == PROTO_HTCP)
+	peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data);
+#endif
+    else
+	debug(44, 1) ("peerHandlePingReply: unknown protocol_t %d\n", (int) proto);
+}
+
+static void
+peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code)
+{
+    FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
+    debug(44, 5) ("peerAddFwdServer: adding %s %s\n",
+	p ? p->host : "DIRECT",
+	hier_strings[code]);
+    fs->_peer = cbdataReference(p);
+    fs->code = code;
+    while (*FS)
+	FS = &(*FS)->next;
+    *FS = fs;
+}
--- squid/src/pinger.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,412 +0,0 @@
-
-/*
- * $Id: pinger.c,v 1.4.96.1 2002/10/06 04:01:14 rbcollins Exp $
- *
- * DEBUG: section 42    ICMP Pinger program
- * AUTHOR: Duane Wessels
- *
- * 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"
-
-#if USE_ICMP
-
-#include <netinet/in_systm.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-
-#ifndef _SQUID_LINUX_
-#ifndef _SQUID_CYGWIN_
-#define icmphdr icmp
-#define iphdr ip
-#endif
-#endif
-
-#if defined (_SQUID_LINUX_) || defined (_SQUID_CYGWIN_)
-#ifdef icmp_id
-#undef icmp_id
-#endif
-#ifdef icmp_seq
-#undef icmp_seq
-#endif
-#define icmp_type type
-#define icmp_code code
-#define icmp_cksum checksum
-#define icmp_id un.echo.id
-#define icmp_seq un.echo.sequence
-#define ip_hl ihl
-#define ip_v version
-#define ip_tos tos
-#define ip_len tot_len
-#define ip_id id
-#define ip_off frag_off
-#define ip_ttl ttl
-#define ip_p protocol
-#define ip_sum check
-#define ip_src saddr
-#define ip_dst daddr
-#endif
-
-#if ALLOW_SOURCE_PING
-#define MAX_PKT_SZ 8192
-#define MAX_PAYLOAD (MAX_PKT_SZ - sizeof(struct icmphdr) - sizeof (char) - sizeof(struct timeval) - 1)
-#else
-#define MAX_PAYLOAD SQUIDHOSTNAMELEN
-#define MAX_PKT_SZ (MAX_PAYLOAD + sizeof(struct timeval) + sizeof (char) + sizeof(struct icmphdr) + 1)
-#endif
-
-typedef struct {
-    struct timeval tv;
-    unsigned char opcode;
-    char payload[MAX_PAYLOAD];
-} icmpEchoData;
-
-int icmp_ident = -1;
-int icmp_pkts_sent = 0;
-
-static const char *icmpPktStr[] =
-{
-    "Echo Reply",
-    "ICMP 1",
-    "ICMP 2",
-    "Destination Unreachable",
-    "Source Quench",
-    "Redirect",
-    "ICMP 6",
-    "ICMP 7",
-    "Echo",
-    "ICMP 9",
-    "ICMP 10",
-    "Time Exceeded",
-    "Parameter Problem",
-    "Timestamp",
-    "Timestamp Reply",
-    "Info Request",
-    "Info Reply",
-    "Out of Range Type"
-};
-
-static int in_cksum(unsigned short *ptr, int size);
-static void pingerRecv(void);
-static void pingerLog(struct icmphdr *, struct in_addr, int, int);
-static int ipHops(int ttl);
-static void pingerSendtoSquid(pingerReplyData * preply);
-
-void
-pingerOpen(void)
-{
-    struct protoent *proto = NULL;
-    if ((proto = getprotobyname("icmp")) == 0) {
-	debug(42, 0) ("pingerOpen: unknown protocol: icmp\n");
-	exit(1);
-    }
-    icmp_sock = socket(PF_INET, SOCK_RAW, proto->p_proto);
-    if (icmp_sock < 0) {
-	debug(50, 0) ("pingerOpen: icmp_sock: %s\n", xstrerror());
-	exit(1);
-    }
-    icmp_ident = getpid() & 0xffff;
-    debug(42, 0) ("pinger: ICMP socket opened\n");
-}
-
-void
-pingerClose(void)
-{
-    close(icmp_sock);
-    icmp_sock = -1;
-    icmp_ident = 0;
-}
-
-static void
-pingerSendEcho(struct in_addr to, int opcode, char *payload, int len)
-{
-    LOCAL_ARRAY(char, pkt, MAX_PKT_SZ);
-    struct icmphdr *icmp = NULL;
-    icmpEchoData *echo;
-    int icmp_pktsize = sizeof(struct icmphdr);
-    struct sockaddr_in S;
-    memset(pkt, '\0', MAX_PKT_SZ);
-    icmp = (struct icmphdr *) (void *) pkt;
-
-    /*
-     * cevans - beware signed/unsigned issues in untrusted data from
-     * the network!!
-     */
-    if (len < 0) {
-	len = 0;
-    }
-    icmp->icmp_type = ICMP_ECHO;
-    icmp->icmp_code = 0;
-    icmp->icmp_cksum = 0;
-    icmp->icmp_id = icmp_ident;
-    icmp->icmp_seq = (u_short) icmp_pkts_sent++;
-    echo = (icmpEchoData *) (icmp + 1);
-    echo->opcode = (unsigned char) opcode;
-    echo->tv = current_time;
-    icmp_pktsize += sizeof(icmpEchoData) - MAX_PAYLOAD;
-    if (payload) {
-	if (len > MAX_PAYLOAD)
-	    len = MAX_PAYLOAD;
-	xmemcpy(echo->payload, payload, len);
-	icmp_pktsize += len;
-    }
-    icmp->icmp_cksum = in_cksum((u_short *) icmp, icmp_pktsize);
-    S.sin_family = AF_INET;
-    /*
-     * cevans: alert: trusting to-host, was supplied in network packet
-     */
-    S.sin_addr = to;
-    S.sin_port = 0;
-    assert(icmp_pktsize <= MAX_PKT_SZ);
-    sendto(icmp_sock,
-	pkt,
-	icmp_pktsize,
-	0,
-	(struct sockaddr *) &S,
-	sizeof(struct sockaddr_in));
-    pingerLog(icmp, to, 0, 0);
-}
-
-static void
-pingerRecv(void)
-{
-    int n;
-    socklen_t fromlen;
-    struct sockaddr_in from;
-    int iphdrlen = 20;
-    struct iphdr *ip = NULL;
-    struct icmphdr *icmp = NULL;
-    static char *pkt = NULL;
-    struct timeval now;
-    icmpEchoData *echo;
-    static pingerReplyData preply;
-
-    if (pkt == NULL)
-	pkt = xmalloc(MAX_PKT_SZ);
-    fromlen = sizeof(from);
-    n = recvfrom(icmp_sock,
-	pkt,
-	MAX_PKT_SZ,
-	0,
-	(struct sockaddr *) &from,
-	&fromlen);
-#if GETTIMEOFDAY_NO_TZP
-    gettimeofday(&now);
-#else
-    gettimeofday(&now, NULL);
-#endif
-    debug(42, 9) ("pingerRecv: %d bytes from %s\n", n, inet_ntoa(from.sin_addr));
-    ip = (struct iphdr *) (void *) pkt;
-#if HAVE_STRUCT_IPHDR_IP_HL
-    iphdrlen = ip->ip_hl << 2;
-#else /* HAVE_STRUCT_IPHDR_IP_HL */
-#if WORDS_BIGENDIAN
-    iphdrlen = (ip->ip_vhl >> 4) << 2;
-#else
-    iphdrlen = (ip->ip_vhl & 0xF) << 2;
-#endif
-#endif /* HAVE_STRUCT_IPHDR_IP_HL */
-    icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
-    if (icmp->icmp_type != ICMP_ECHOREPLY)
-	return;
-    if (icmp->icmp_id != icmp_ident)
-	return;
-    echo = (icmpEchoData *) (void *) (icmp + 1);
-    preply.from = from.sin_addr;
-    preply.opcode = echo->opcode;
-    preply.hops = ipHops(ip->ip_ttl);
-    preply.rtt = tvSubMsec(echo->tv, now);
-    preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT_SZ);
-    pingerSendtoSquid(&preply);
-    pingerLog(icmp, from.sin_addr, preply.rtt, preply.hops);
-}
-
-
-static int
-in_cksum(unsigned short *ptr, int size)
-{
-    long sum;
-    unsigned short oddbyte;
-    unsigned short answer;
-    sum = 0;
-    while (size > 1) {
-	sum += *ptr++;
-	size -= 2;
-    }
-    if (size == 1) {
-	oddbyte = 0;
-	*((unsigned char *) &oddbyte) = *(unsigned char *) ptr;
-	sum += oddbyte;
-    }
-    sum = (sum >> 16) + (sum & 0xffff);
-    sum += (sum >> 16);
-    answer = ~sum;
-    return (answer);
-}
-
-static void
-pingerLog(struct icmphdr *icmp, struct in_addr addr, int rtt, int hops)
-{
-    debug(42, 2) ("pingerLog: %9d.%06d %-16s %d %-15.15s %dms %d hops\n",
-	(int) current_time.tv_sec,
-	(int) current_time.tv_usec,
-	inet_ntoa(addr),
-	(int) icmp->icmp_type,
-	icmpPktStr[icmp->icmp_type],
-	rtt,
-	hops);
-}
-
-static int
-ipHops(int ttl)
-{
-    if (ttl < 33)
-	return 33 - ttl;
-    if (ttl < 63)
-	return 63 - ttl;	/* 62 = (64+60)/2 */
-    if (ttl < 65)
-	return 65 - ttl;	/* 62 = (64+60)/2 */
-    if (ttl < 129)
-	return 129 - ttl;
-    if (ttl < 193)
-	return 193 - ttl;
-    return 256 - ttl;
-}
-
-static int
-pingerReadRequest(void)
-{
-    static pingerEchoData pecho;
-    int n;
-    int guess_size;
-    memset(&pecho, '\0', sizeof(pecho));
-    n = recv(0, (char *) &pecho, sizeof(pecho), 0);
-    if (n < 0)
-	return n;
-    if (0 == n) {
-	/* EOF indicator */
-	fprintf(stderr, "EOF encountered\n");
-	errno = 0;
-	return -1;
-    }
-    guess_size = n - (sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ);
-    if (guess_size != pecho.psize) {
-	fprintf(stderr, "size mismatch, guess=%d psize=%d\n",
-	    guess_size, pecho.psize);
-	/* don't process this message, but keep running */
-	return 0;
-    }
-    pingerSendEcho(pecho.to,
-	pecho.opcode,
-	pecho.payload,
-	pecho.psize);
-    return n;
-}
-
-static void
-pingerSendtoSquid(pingerReplyData * preply)
-{
-    int len = sizeof(pingerReplyData) - MAX_PKT_SZ + preply->psize;
-    if (send(1, (char *) preply, len, 0) < 0) {
-	debug(50, 0) ("pinger: send: %s\n", xstrerror());
-	exit(1);
-    }
-}
-
-time_t
-getCurrentTime(void)
-{
-#if GETTIMEOFDAY_NO_TZP
-    gettimeofday(&current_time);
-#else
-    gettimeofday(&current_time, NULL);
-#endif
-    return squid_curtime = current_time.tv_sec;
-}
-
-
-int
-main(int argc, char *argv[])
-{
-    fd_set R;
-    int x;
-    struct timeval tv;
-    const char *debug_args = "ALL,1";
-    char *t;
-    time_t last_check_time = 0;
-
-/*
- * cevans - do this first. It grabs a raw socket. After this we can
- * drop privs
- */
-    pingerOpen();
-    setgid(getgid());
-    setuid(getuid());
-
-    if ((t = getenv("SQUID_DEBUG")))
-	debug_args = xstrdup(t);
-    getCurrentTime();
-    _db_init(NULL, debug_args);
-
-    for (;;) {
-	tv.tv_sec = 10;
-	tv.tv_usec = 0;
-	FD_ZERO(&R);
-	FD_SET(0, &R);
-	FD_SET(icmp_sock, &R);
-	x = select(icmp_sock + 1, &R, NULL, NULL, &tv);
-	getCurrentTime();
-	if (x < 0)
-	    exit(1);
-	if (FD_ISSET(0, &R))
-	    if (pingerReadRequest() < 0) {
-		debug(42, 0) ("Pinger exiting.\n");
-		exit(1);
-	    }
-	if (FD_ISSET(icmp_sock, &R))
-	    pingerRecv();
-	if (10 + last_check_time < squid_curtime) {
-	    if (send(1, (char *) &tv, 0, 0) < 0)
-		exit(1);
-	    last_check_time = squid_curtime;
-	}
-    }
-    /* NOTREACHED */
-}
-
-#else
-#include <stdio.h>
-int
-main(int argc, char *argv[])
-{
-    fprintf(stderr, "%s: ICMP support not compiled in.\n", argv[0]);
-    return 1;
-}
-#endif /* USE_ICMP */
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/pinger.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,412 @@
+
+/*
+ * $Id: pinger.cc,v 1.1.2.1 2002/10/11 15:41:00 rbcollins Exp $
+ *
+ * DEBUG: section 42    ICMP Pinger program
+ * AUTHOR: Duane Wessels
+ *
+ * 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"
+
+#if USE_ICMP
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#ifndef _SQUID_LINUX_
+#ifndef _SQUID_CYGWIN_
+#define icmphdr icmp
+#define iphdr ip
+#endif
+#endif
+
+#if defined (_SQUID_LINUX_) || defined (_SQUID_CYGWIN_)
+#ifdef icmp_id
+#undef icmp_id
+#endif
+#ifdef icmp_seq
+#undef icmp_seq
+#endif
+#define icmp_type type
+#define icmp_code code
+#define icmp_cksum checksum
+#define icmp_id un.echo.id
+#define icmp_seq un.echo.sequence
+#define ip_hl ihl
+#define ip_v version
+#define ip_tos tos
+#define ip_len tot_len
+#define ip_id id
+#define ip_off frag_off
+#define ip_ttl ttl
+#define ip_p protocol
+#define ip_sum check
+#define ip_src saddr
+#define ip_dst daddr
+#endif
+
+#if ALLOW_SOURCE_PING
+#define MAX_PKT_SZ 8192
+#define MAX_PAYLOAD (MAX_PKT_SZ - sizeof(struct icmphdr) - sizeof (char) - sizeof(struct timeval) - 1)
+#else
+#define MAX_PAYLOAD SQUIDHOSTNAMELEN
+#define MAX_PKT_SZ (MAX_PAYLOAD + sizeof(struct timeval) + sizeof (char) + sizeof(struct icmphdr) + 1)
+#endif
+
+typedef struct {
+    struct timeval tv;
+    unsigned char opcode;
+    char payload[MAX_PAYLOAD];
+} icmpEchoData;
+
+int icmp_ident = -1;
+int icmp_pkts_sent = 0;
+
+static const char *icmpPktStr[] =
+{
+    "Echo Reply",
+    "ICMP 1",
+    "ICMP 2",
+    "Destination Unreachable",
+    "Source Quench",
+    "Redirect",
+    "ICMP 6",
+    "ICMP 7",
+    "Echo",
+    "ICMP 9",
+    "ICMP 10",
+    "Time Exceeded",
+    "Parameter Problem",
+    "Timestamp",
+    "Timestamp Reply",
+    "Info Request",
+    "Info Reply",
+    "Out of Range Type"
+};
+
+static int in_cksum(unsigned short *ptr, int size);
+static void pingerRecv(void);
+static void pingerLog(struct icmphdr *, struct in_addr, int, int);
+static int ipHops(int ttl);
+static void pingerSendtoSquid(pingerReplyData * preply);
+
+void
+pingerOpen(void)
+{
+    struct protoent *proto = NULL;
+    if ((proto = getprotobyname("icmp")) == 0) {
+	debug(42, 0) ("pingerOpen: unknown protocol: icmp\n");
+	exit(1);
+    }
+    icmp_sock = socket(PF_INET, SOCK_RAW, proto->p_proto);
+    if (icmp_sock < 0) {
+	debug(50, 0) ("pingerOpen: icmp_sock: %s\n", xstrerror());
+	exit(1);
+    }
+    icmp_ident = getpid() & 0xffff;
+    debug(42, 0) ("pinger: ICMP socket opened\n");
+}
+
+void
+pingerClose(void)
+{
+    close(icmp_sock);
+    icmp_sock = -1;
+    icmp_ident = 0;
+}
+
+static void
+pingerSendEcho(struct in_addr to, int opcode, char *payload, int len)
+{
+    LOCAL_ARRAY(char, pkt, MAX_PKT_SZ);
+    struct icmphdr *icmp = NULL;
+    icmpEchoData *echo;
+    size_t icmp_pktsize = sizeof(struct icmphdr);
+    struct sockaddr_in S;
+    memset(pkt, '\0', MAX_PKT_SZ);
+    icmp = (struct icmphdr *) (void *) pkt;
+
+    /*
+     * cevans - beware signed/unsigned issues in untrusted data from
+     * the network!!
+     */
+    if (len < 0) {
+	len = 0;
+    }
+    icmp->icmp_type = ICMP_ECHO;
+    icmp->icmp_code = 0;
+    icmp->icmp_cksum = 0;
+    icmp->icmp_id = icmp_ident;
+    icmp->icmp_seq = (u_short) icmp_pkts_sent++;
+    echo = (icmpEchoData *) (icmp + 1);
+    echo->opcode = (unsigned char) opcode;
+    echo->tv = current_time;
+    icmp_pktsize += sizeof(icmpEchoData) - MAX_PAYLOAD;
+    if (payload) {
+	if (len > MAX_PAYLOAD)
+	    len = MAX_PAYLOAD;
+	xmemcpy(echo->payload, payload, len);
+	icmp_pktsize += len;
+    }
+    icmp->icmp_cksum = in_cksum((u_short *) icmp, icmp_pktsize);
+    S.sin_family = AF_INET;
+    /*
+     * cevans: alert: trusting to-host, was supplied in network packet
+     */
+    S.sin_addr = to;
+    S.sin_port = 0;
+    assert(icmp_pktsize <= MAX_PKT_SZ);
+    sendto(icmp_sock,
+	pkt,
+	icmp_pktsize,
+	0,
+	(struct sockaddr *) &S,
+	sizeof(struct sockaddr_in));
+    pingerLog(icmp, to, 0, 0);
+}
+
+static void
+pingerRecv(void)
+{
+    int n;
+    socklen_t fromlen;
+    struct sockaddr_in from;
+    int iphdrlen = 20;
+    struct iphdr *ip = NULL;
+    struct icmphdr *icmp = NULL;
+    static char *pkt = NULL;
+    struct timeval now;
+    icmpEchoData *echo;
+    static pingerReplyData preply;
+
+    if (pkt == NULL)
+	pkt = (char *)xmalloc(MAX_PKT_SZ);
+    fromlen = sizeof(from);
+    n = recvfrom(icmp_sock,
+	pkt,
+	MAX_PKT_SZ,
+	0,
+	(struct sockaddr *) &from,
+	&fromlen);
+#if GETTIMEOFDAY_NO_TZP
+    gettimeofday(&now);
+#else
+    gettimeofday(&now, NULL);
+#endif
+    debug(42, 9) ("pingerRecv: %d bytes from %s\n", n, inet_ntoa(from.sin_addr));
+    ip = (struct iphdr *) (void *) pkt;
+#if HAVE_STRUCT_IPHDR_IP_HL
+    iphdrlen = ip->ip_hl << 2;
+#else /* HAVE_STRUCT_IPHDR_IP_HL */
+#if WORDS_BIGENDIAN
+    iphdrlen = (ip->ip_vhl >> 4) << 2;
+#else
+    iphdrlen = (ip->ip_vhl & 0xF) << 2;
+#endif
+#endif /* HAVE_STRUCT_IPHDR_IP_HL */
+    icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
+    if (icmp->icmp_type != ICMP_ECHOREPLY)
+	return;
+    if (icmp->icmp_id != icmp_ident)
+	return;
+    echo = (icmpEchoData *) (void *) (icmp + 1);
+    preply.from = from.sin_addr;
+    preply.opcode = echo->opcode;
+    preply.hops = ipHops(ip->ip_ttl);
+    preply.rtt = tvSubMsec(echo->tv, now);
+    preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT_SZ);
+    pingerSendtoSquid(&preply);
+    pingerLog(icmp, from.sin_addr, preply.rtt, preply.hops);
+}
+
+
+static int
+in_cksum(unsigned short *ptr, int size)
+{
+    long sum;
+    unsigned short oddbyte;
+    unsigned short answer;
+    sum = 0;
+    while (size > 1) {
+	sum += *ptr++;
+	size -= 2;
+    }
+    if (size == 1) {
+	oddbyte = 0;
+	*((unsigned char *) &oddbyte) = *(unsigned char *) ptr;
+	sum += oddbyte;
+    }
+    sum = (sum >> 16) + (sum & 0xffff);
+    sum += (sum >> 16);
+    answer = ~sum;
+    return (answer);
+}
+
+static void
+pingerLog(struct icmphdr *icmp, struct in_addr addr, int rtt, int hops)
+{
+    debug(42, 2) ("pingerLog: %9d.%06d %-16s %d %-15.15s %dms %d hops\n",
+	(int) current_time.tv_sec,
+	(int) current_time.tv_usec,
+	inet_ntoa(addr),
+	(int) icmp->icmp_type,
+	icmpPktStr[icmp->icmp_type],
+	rtt,
+	hops);
+}
+
+static int
+ipHops(int ttl)
+{
+    if (ttl < 33)
+	return 33 - ttl;
+    if (ttl < 63)
+	return 63 - ttl;	/* 62 = (64+60)/2 */
+    if (ttl < 65)
+	return 65 - ttl;	/* 62 = (64+60)/2 */
+    if (ttl < 129)
+	return 129 - ttl;
+    if (ttl < 193)
+	return 193 - ttl;
+    return 256 - ttl;
+}
+
+static int
+pingerReadRequest(void)
+{
+    static pingerEchoData pecho;
+    int n;
+    int guess_size;
+    memset(&pecho, '\0', sizeof(pecho));
+    n = recv(0, (char *) &pecho, sizeof(pecho), 0);
+    if (n < 0)
+	return n;
+    if (0 == n) {
+	/* EOF indicator */
+	fprintf(stderr, "EOF encountered\n");
+	errno = 0;
+	return -1;
+    }
+    guess_size = n - (sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ);
+    if (guess_size != pecho.psize) {
+	fprintf(stderr, "size mismatch, guess=%d psize=%d\n",
+	    guess_size, pecho.psize);
+	/* don't process this message, but keep running */
+	return 0;
+    }
+    pingerSendEcho(pecho.to,
+	pecho.opcode,
+	pecho.payload,
+	pecho.psize);
+    return n;
+}
+
+static void
+pingerSendtoSquid(pingerReplyData * preply)
+{
+    int len = sizeof(pingerReplyData) - MAX_PKT_SZ + preply->psize;
+    if (send(1, (char *) preply, len, 0) < 0) {
+	debug(50, 0) ("pinger: send: %s\n", xstrerror());
+	exit(1);
+    }
+}
+
+time_t
+getCurrentTime(void)
+{
+#if GETTIMEOFDAY_NO_TZP
+    gettimeofday(&current_time);
+#else
+    gettimeofday(&current_time, NULL);
+#endif
+    return squid_curtime = current_time.tv_sec;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    fd_set R;
+    int x;
+    struct timeval tv;
+    const char *debug_args = "ALL,1";
+    char *t;
+    time_t last_check_time = 0;
+
+/*
+ * cevans - do this first. It grabs a raw socket. After this we can
+ * drop privs
+ */
+    pingerOpen();
+    setgid(getgid());
+    setuid(getuid());
+
+    if ((t = getenv("SQUID_DEBUG")))
+	debug_args = xstrdup(t);
+    getCurrentTime();
+    _db_init(NULL, debug_args);
+
+    for (;;) {
+	tv.tv_sec = 10;
+	tv.tv_usec = 0;
+	FD_ZERO(&R);
+	FD_SET(0, &R);
+	FD_SET(icmp_sock, &R);
+	x = select(icmp_sock + 1, &R, NULL, NULL, &tv);
+	getCurrentTime();
+	if (x < 0)
+	    exit(1);
+	if (FD_ISSET(0, &R))
+	    if (pingerReadRequest() < 0) {
+		debug(42, 0) ("Pinger exiting.\n");
+		exit(1);
+	    }
+	if (FD_ISSET(icmp_sock, &R))
+	    pingerRecv();
+	if (10 + last_check_time < squid_curtime) {
+	    if (send(1, (char *) &tv, 0, 0) < 0)
+		exit(1);
+	    last_check_time = squid_curtime;
+	}
+    }
+    /* NOTREACHED */
+}
+
+#else
+#include <stdio.h>
+int
+main(int argc, char *argv[])
+{
+    fprintf(stderr, "%s: ICMP support not compiled in.\n", argv[0]);
+    return 1;
+}
+#endif /* USE_ICMP */
--- squid/src/referer.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,79 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 40    User-Agent and Referer logging
- * AUTHOR: Joe Ramey <ramey@csc.ti.com> (useragent)
- *         Jens-S. Vöckler <voeckler@rvs.uni-hannover.de> (mod 4 referer)
- *
- * 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"
-
-#if USE_REFERER_LOG
-static Logfile *refererlog = NULL;
-#endif
-
-void
-refererOpenLog(void)
-{
-#if USE_REFERER_LOG
-    assert(NULL == refererlog);
-    if (!Config.Log.referer || (0 == strcmp(Config.Log.referer, "none"))) {
-	debug(40, 1) ("Referer logging is disabled.\n");
-	return;
-    }
-    refererlog = logfileOpen(Config.Log.referer, 0, 1);
-#endif
-}
-
-void
-refererRotateLog(void)
-{
-#if USE_REFERER_LOG
-    if (NULL == refererlog)
-	return;
-    logfileRotate(refererlog);
-#endif
-}
-
-void
-logReferer(const char *client, const char *referer, const char *uri)
-{
-#if USE_REFERER_LOG
-    if (NULL == refererlog)
-	return;
-    logfilePrintf(refererlog, "%9d.%03d %s %s %s\n",
-	(int) current_time.tv_sec,
-	(int) current_time.tv_usec / 1000,
-	client,
-	referer,
-	uri ? uri : "-");
-#endif
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/referer.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,79 @@
+
+/*
+ * $Id: referer.cc,v 1.1.2.1 2002/10/11 15:41:00 rbcollins Exp $
+ *
+ * DEBUG: section 40    User-Agent and Referer logging
+ * AUTHOR: Joe Ramey <ramey@csc.ti.com> (useragent)
+ *         Jens-S. Vöckler <voeckler@rvs.uni-hannover.de> (mod 4 referer)
+ *
+ * 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"
+
+#if USE_REFERER_LOG
+static Logfile *refererlog = NULL;
+#endif
+
+void
+refererOpenLog(void)
+{
+#if USE_REFERER_LOG
+    assert(NULL == refererlog);
+    if (!Config.Log.referer || (0 == strcmp(Config.Log.referer, "none"))) {
+	debug(40, 1) ("Referer logging is disabled.\n");
+	return;
+    }
+    refererlog = logfileOpen(Config.Log.referer, 0, 1);
+#endif
+}
+
+void
+refererRotateLog(void)
+{
+#if USE_REFERER_LOG
+    if (NULL == refererlog)
+	return;
+    logfileRotate(refererlog);
+#endif
+}
+
+void
+logReferer(const char *client, const char *referer, const char *uri)
+{
+#if USE_REFERER_LOG
+    if (NULL == refererlog)
+	return;
+    logfilePrintf(refererlog, "%9d.%03d %s %s %s\n",
+	(int) current_time.tv_sec,
+	(int) current_time.tv_usec / 1000,
+	client,
+	referer,
+	uri ? uri : "-");
+#endif
+}
--- squid/src/refresh.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,514 +0,0 @@
-
-/*
- * $Id: refresh.c,v 1.8.14.1 2002/10/04 07:07:27 rbcollins Exp $
- *
- * DEBUG: section 22    Refresh Calculation
- * AUTHOR: Harvest Derived
- *
- * 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.
- *
- */
-
-#ifndef USE_POSIX_REGEX
-#define USE_POSIX_REGEX		/* put before includes; always use POSIX */
-#endif
-
-#include "squid.h"
-#include "Store.h"
-
-typedef enum {
-    rcHTTP,
-    rcICP,
-#if USE_HTCP
-    rcHTCP,
-#endif
-#if USE_CACHE_DIGESTS
-    rcCDigest,
-#endif
-    rcStore,
-    rcCount
-} refreshCountsEnum;
-
-typedef struct {
-    unsigned int expires:1;
-    unsigned int min:1;
-    unsigned int lmfactor:1;
-    unsigned int max;
-} stale_flags;
-
-/*
- * This enumerated list assigns specific values, ala HTTP/FTP status
- * codes.  All Fresh codes are in the range 100-199 and all stale
- * codes are 200-299.  We might want to use these codes in logging,
- * so best to keep them consistent over time.
- */
-enum {
-    FRESH_REQUEST_MAX_STALE_ALL = 100,
-    FRESH_REQUEST_MAX_STALE_VALUE,
-    FRESH_EXPIRES,
-    FRESH_LMFACTOR_RULE,
-    FRESH_MIN_RULE,
-    FRESH_OVERRIDE_EXPIRES,
-    FRESH_OVERRIDE_LASTMOD,
-    STALE_MUST_REVALIDATE = 200,
-    STALE_RELOAD_INTO_IMS,
-    STALE_FORCED_RELOAD,
-    STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
-    STALE_EXPIRES,
-    STALE_MAX_RULE,
-    STALE_LMFACTOR_RULE,
-    STALE_DEFAULT = 299
-};
-
-static struct RefreshCounts {
-    const char *proto;
-    int total;
-    int status[STALE_DEFAULT + 1];
-} refreshCounts[rcCount];
-
-/*
- * Defaults:
- *      MIN     NONE
- *      PCT     20%
- *      MAX     3 days
- */
-#define REFRESH_DEFAULT_MIN	(time_t)0
-#define REFRESH_DEFAULT_PCT	0.20
-#define REFRESH_DEFAULT_MAX	(time_t)259200
-
-static const refresh_t *refreshLimits(const char *);
-static const refresh_t *refreshUncompiledPattern(const char *);
-static OBJH refreshStats;
-static int refreshStaleness(const StoreEntry *, time_t, time_t, const refresh_t *, stale_flags *);
-
-static refresh_t DefaultRefresh;
-
-static const refresh_t *
-refreshLimits(const char *url)
-{
-    const refresh_t *R;
-    for (R = Config.Refresh; R; R = R->next) {
-	if (!regexec(&(R->compiled_pattern), url, 0, 0, 0))
-	    return R;
-    }
-    return NULL;
-}
-
-static const refresh_t *
-refreshUncompiledPattern(const char *pat)
-{
-    const refresh_t *R;
-    for (R = Config.Refresh; R; R = R->next) {
-	if (0 == strcmp(R->pattern, pat))
-	    return R;
-    }
-    return NULL;
-}
-
-/*
- * Calculate how stale the response is (or will be at the check_time).
- * Staleness calculation is based on the following: (1) response
- * expiration time, (2) age greater than configured maximum, (3)
- * last-modified factor, and (4) age less than configured minimum.
- *
- * If the response is fresh, return -1.  Otherwise return its
- * staleness.  NOTE return value of 0 means the response is stale.
- *
- * The 'stale_flags' structure is used to tell the calling function
- * _why_ this response is fresh or stale.  Its used, for example,
- * when the admin wants to override expiration and last-modified
- * times.
- */
-static int
-refreshStaleness(const StoreEntry * entry, time_t check_time, time_t age, const refresh_t * R, stale_flags * sf)
-{
-    /*
-     * Check for an explicit expiration time.
-     */
-    if (entry->expires > -1) {
-	sf->expires = 1;
-	if (entry->expires > check_time) {
-	    debug(22, 3) ("FRESH: expires %d >= check_time %d \n",
-		(int) entry->expires, (int) check_time);
-	    return -1;
-	} else {
-	    debug(22, 3) ("STALE: expires %d < check_time %d \n",
-		(int) entry->expires, (int) check_time);
-	    return (check_time - entry->expires);
-	}
-    }
-    assert(age >= 0);
-    /*
-     * Use local heuristics to determine staleness.  Start with the
-     * max age from the refresh_pattern rule.
-     */
-    if (age > R->max) {
-	debug(22, 3) ("STALE: age %d > max %d \n", (int) age, (int) R->max);
-	sf->max = 1;
-	return (age - R->max);
-    }
-    /*
-     * Try the last-modified factor algorithm.
-     */
-    if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) {
-	/*
-	 * stale_age is the Age of the response when it became/becomes
-	 * stale according to the last-modified factor algorithm.
-	 */
-	time_t stale_age = (entry->timestamp - entry->lastmod) * R->pct;
-	sf->lmfactor = 1;
-	if (age >= stale_age) {
-	    debug(22, 3) ("STALE: age %d > stale_age %d\n",
-		(int) age, (int) stale_age);
-	    return (age - stale_age);
-	} else {
-	    debug(22, 3) ("FRESH: age %d <= stale_age %d\n",
-		(int) age, (int) stale_age);
-	    return -1;
-	}
-    }
-    /*
-     * If we are here, staleness is determined by the refresh_pattern
-     * configured minimum age.
-     */
-    if (age <= R->min) {
-	debug(22, 3) ("FRESH: age %d <= min %d\n", (int) age, (int) R->min);
-	sf->min = 1;
-	return -1;
-    }
-    debug(22, 3) ("STALE: age %d > min %d\n", (int) age, (int) R->min);
-    return (age - R->min);
-}
-
-/*  return 1 if the entry must be revalidated within delta seconds
- *         0 otherwise
- *
- *  note: request maybe null (e.g. for cache digests build)
- */
-static int
-refreshCheck(const StoreEntry * entry, request_t * request, time_t delta)
-{
-    const refresh_t *R;
-    const char *uri = NULL;
-    time_t age = 0;
-    time_t check_time = squid_curtime + delta;
-    int staleness;
-    stale_flags sf;
-    if (entry->mem_obj)
-	uri = entry->mem_obj->url;
-    else if (request)
-	uri = urlCanonical(request);
-
-    debug(22, 3) ("refreshCheck: '%s'\n", uri ? uri : "<none>");
-
-    if (check_time > entry->timestamp)
-	age = check_time - entry->timestamp;
-    R = uri ? refreshLimits(uri) : refreshUncompiledPattern(".");
-    if (NULL == R)
-	R = &DefaultRefresh;
-    memset(&sf, '\0', sizeof(sf));
-    staleness = refreshStaleness(entry, check_time, age, R, &sf);
-    debug(22, 3) ("Staleness = %d\n", staleness);
-
-    debug(22, 3) ("refreshCheck: Matched '%s %d %d%% %d'\n",
-	R->pattern, (int) R->min, (int) (100.0 * R->pct), (int) R->max);
-    debug(22, 3) ("refreshCheck: age = %d\n", (int) age);
-    debug(22, 3) ("\tcheck_time:\t%s\n", mkrfc1123(check_time));
-    debug(22, 3) ("\tentry->timestamp:\t%s\n", mkrfc1123(entry->timestamp));
-
-    if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1) {
-	debug(22, 3) ("refreshCheck: YES: Must revalidate stale response\n");
-	return STALE_MUST_REVALIDATE;
-    }
-    /* request-specific checks */
-    if (request) {
-	HttpHdrCc *cc = request->cache_control;
-#if HTTP_VIOLATIONS
-	if (!request->flags.nocache_hack) {
-	    (void) 0;
-	} else if (R->flags.ignore_reload) {
-	    /* The clients no-cache header is ignored */
-	    debug(22, 3) ("refreshCheck: MAYBE: ignore-reload\n");
-	} else if (R->flags.reload_into_ims || Config.onoff.reload_into_ims) {
-	    /* The clients no-cache header is changed into a IMS query */
-	    debug(22, 3) ("refreshCheck: YES: reload-into-ims\n");
-	    return STALE_RELOAD_INTO_IMS;
-	} else {
-	    /* The clients no-cache header is not overridden on this request */
-	    debug(22, 3) ("refreshCheck: YES: client reload\n");
-	    request->flags.nocache = 1;
-	    return STALE_FORCED_RELOAD;
-	}
-#endif
-	if (NULL != cc) {
-	    if (cc->max_age > -1) {
-#if HTTP_VIOLATIONS
-		if (R->flags.ignore_reload && cc->max_age == 0) {
-		} else
-#endif
-		if (age > cc->max_age) {
-		    debug(22, 3) ("refreshCheck: YES: age > client-max-age\n");
-		    return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE;
-		}
-	    }
-	    if (EBIT_TEST(cc->mask, CC_MAX_STALE) && staleness > -1) {
-		if (cc->max_stale < 0) {
-		    /* max-stale directive without a value */
-		    debug(22, 3) ("refreshCheck: NO: max-stale wildcard\n");
-		    return FRESH_REQUEST_MAX_STALE_ALL;
-		} else if (staleness < cc->max_stale) {
-		    debug(22, 3) ("refreshCheck: NO: staleness < max-stale\n");
-		    return FRESH_REQUEST_MAX_STALE_VALUE;
-		}
-	    }
-	}
-    }
-    if (-1 == staleness) {
-	if (sf.expires)
-	    return FRESH_EXPIRES;
-	assert(!sf.max);
-	if (sf.lmfactor)
-	    return FRESH_LMFACTOR_RULE;
-	assert(sf.min);
-	return FRESH_MIN_RULE;
-    }
-    /*
-     * At this point the response is stale, unless one of
-     * the override options kicks in.
-     */
-    if (sf.expires) {
-#if HTTP_VIOLATIONS
-	if (R->flags.override_expire && age < R->min) {
-	    debug(22, 3) ("refreshCheck: NO: age < min && override-expire\n");
-	    return FRESH_OVERRIDE_EXPIRES;
-	}
-#endif
-	return STALE_EXPIRES;
-    }
-    if (sf.max)
-	return STALE_MAX_RULE;
-    if (sf.lmfactor) {
-#if HTTP_VIOLATIONS
-	if (R->flags.override_lastmod && age < R->min) {
-	    debug(22, 3) ("refreshCheck: NO: age < min && override-lastmod\n");
-	    return FRESH_OVERRIDE_LASTMOD;
-	}
-#endif
-	return STALE_LMFACTOR_RULE;
-    }
-    return STALE_DEFAULT;
-}
-
-int
-refreshIsCachable(const StoreEntry * entry)
-{
-    /*
-     * Don't look at the request to avoid no-cache and other nuisances.
-     * the object should have a mem_obj so the URL will be found there.
-     * 60 seconds delta, to avoid objects which expire almost
-     * immediately, and which can't be refreshed.
-     */
-    int reason = refreshCheck(entry, NULL, 60);
-    refreshCounts[rcStore].total++;
-    refreshCounts[rcStore].status[reason]++;
-    if (reason < 200)
-	/* Does not need refresh. This is certainly cachable */
-	return 1;
-    if (entry->lastmod < 0)
-	/* Last modified is needed to do a refresh */
-	return 0;
-    if (entry->mem_obj == NULL)
-	/* no mem_obj? */
-	return 1;
-    if (entry->mem_obj->reply == NULL)
-	/* no reply? */
-	return 1;
-    if (entry->mem_obj->reply->content_length == 0)
-	/* No use refreshing (caching?) 0 byte objects */
-	return 0;
-    /* This seems to be refreshable. Cache it */
-    return 1;
-}
-
-/* refreshCheck... functions below are protocol-specific wrappers around
- * refreshCheck() function above */
-
-int
-refreshCheckHTTP(const StoreEntry * entry, request_t * request)
-{
-    int reason = refreshCheck(entry, request, 0);
-    refreshCounts[rcHTTP].total++;
-    refreshCounts[rcHTTP].status[reason]++;
-    return (reason < 200) ? 0 : 1;
-}
-
-int
-refreshCheckICP(const StoreEntry * entry, request_t * request)
-{
-    int reason = refreshCheck(entry, request, 30);
-    refreshCounts[rcICP].total++;
-    refreshCounts[rcICP].status[reason]++;
-    return (reason < 200) ? 0 : 1;
-}
-
-#if USE_HTCP
-int
-refreshCheckHTCP(const StoreEntry * entry, request_t * request)
-{
-    int reason = refreshCheck(entry, request, 10);
-    refreshCounts[rcHTCP].total++;
-    refreshCounts[rcHTCP].status[reason]++;
-    return (reason < 200) ? 0 : 1;
-}
-#endif
-
-#if USE_CACHE_DIGESTS
-int
-refreshCheckDigest(const StoreEntry * entry, time_t delta)
-{
-    int reason = refreshCheck(entry,
-	entry->mem_obj ? entry->mem_obj->request : NULL,
-	delta);
-    refreshCounts[rcCDigest].total++;
-    refreshCounts[rcCDigest].status[reason]++;
-    return (reason < 200) ? 0 : 1;
-}
-#endif
-
-time_t
-getMaxAge(const char *url)
-{
-    const refresh_t *R;
-    debug(22, 3) ("getMaxAge: '%s'\n", url);
-    if ((R = refreshLimits(url)))
-	return R->max;
-    else
-	return REFRESH_DEFAULT_MAX;
-}
-
-static void
-refreshCountsStats(StoreEntry * sentry, struct RefreshCounts *rc)
-{
-    int sum = 0;
-    int tot = rc->total;
-
-    storeAppendPrintf(sentry, "\n\n%s histogram:\n", rc->proto);
-    storeAppendPrintf(sentry, "Count\t%%Total\tCategory\n");
-
-#define refreshCountsStatsEntry(code,desc) { \
-	storeAppendPrintf(sentry, "%6d\t%6.2f\t%s\n", \
-	    rc->status[code], xpercent(rc->status[code], tot), desc); \
-    sum += rc->status[code]; \
-}
-
-    refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_ALL,
-	"Fresh: request max-stale wildcard");
-    refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_VALUE,
-	"Fresh: request max-stale value");
-    refreshCountsStatsEntry(FRESH_EXPIRES,
-	"Fresh: expires time not reached");
-    refreshCountsStatsEntry(FRESH_LMFACTOR_RULE,
-	"Fresh: refresh_pattern last-mod factor percentage");
-    refreshCountsStatsEntry(FRESH_MIN_RULE,
-	"Fresh: refresh_pattern min value");
-    refreshCountsStatsEntry(FRESH_OVERRIDE_EXPIRES,
-	"Fresh: refresh_pattern override expires");
-    refreshCountsStatsEntry(FRESH_OVERRIDE_LASTMOD,
-	"Fresh: refresh_pattern override lastmod");
-    refreshCountsStatsEntry(STALE_MUST_REVALIDATE,
-	"Stale: response has must-revalidate");
-    refreshCountsStatsEntry(STALE_RELOAD_INTO_IMS,
-	"Stale: changed reload into IMS");
-    refreshCountsStatsEntry(STALE_FORCED_RELOAD,
-	"Stale: request has no-cache directive");
-    refreshCountsStatsEntry(STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
-	"Stale: age exceeds request max-age value");
-    refreshCountsStatsEntry(STALE_EXPIRES,
-	"Stale: expires time reached");
-    refreshCountsStatsEntry(STALE_MAX_RULE,
-	"Stale: refresh_pattern max age rule");
-    refreshCountsStatsEntry(STALE_LMFACTOR_RULE,
-	"Stale: refresh_pattern last-mod factor percentage");
-    refreshCountsStatsEntry(STALE_DEFAULT,
-	"Stale: by default");
-
-    tot = sum;			/* paranoid: "total" line shows 100% if we forgot nothing */
-    storeAppendPrintf(sentry, "%6d\t%6.2f\tTOTAL\n",
-	rc->total, xpercent(rc->total, tot));
-    \
-	storeAppendPrintf(sentry, "\n");
-}
-
-static void
-refreshStats(StoreEntry * sentry)
-{
-    int i;
-    int total = 0;
-
-    /* get total usage count */
-    for (i = 0; i < rcCount; ++i)
-	total += refreshCounts[i].total;
-
-    /* protocol usage histogram */
-    storeAppendPrintf(sentry, "\nRefreshCheck calls per protocol\n\n");
-    storeAppendPrintf(sentry, "Protocol\t#Calls\t%%Calls\n");
-    for (i = 0; i < rcCount; ++i)
-	storeAppendPrintf(sentry, "%10s\t%6d\t%6.2f\n",
-	    refreshCounts[i].proto,
-	    refreshCounts[i].total,
-	    xpercent(refreshCounts[i].total, total));
-
-    /* per protocol histograms */
-    storeAppendPrintf(sentry, "\n\nRefreshCheck histograms for various protocols\n");
-    for (i = 0; i < rcCount; ++i)
-	refreshCountsStats(sentry, &refreshCounts[i]);
-}
-
-void
-refreshInit(void)
-{
-    memset(refreshCounts, 0, sizeof(refreshCounts));
-    refreshCounts[rcHTTP].proto = "HTTP";
-    refreshCounts[rcICP].proto = "ICP";
-#if USE_HTCP
-    refreshCounts[rcHTCP].proto = "HTCP";
-#endif
-    refreshCounts[rcStore].proto = "On Store";
-#if USE_CACHE_DIGESTS
-    refreshCounts[rcCDigest].proto = "Cache Digests";
-#endif
-    cachemgrRegister("refresh",
-	"Refresh Algorithm Statistics",
-	refreshStats,
-	0,
-	1);
-    memset(&DefaultRefresh, '\0', sizeof(DefaultRefresh));
-    DefaultRefresh.pattern = "<none>";
-    DefaultRefresh.min = REFRESH_DEFAULT_MIN;
-    DefaultRefresh.pct = REFRESH_DEFAULT_PCT;
-    DefaultRefresh.max = REFRESH_DEFAULT_MAX;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/refresh.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,514 @@
+
+/*
+ * $Id: refresh.cc,v 1.1.2.1 2002/10/11 15:41:01 rbcollins Exp $
+ *
+ * DEBUG: section 22    Refresh Calculation
+ * AUTHOR: Harvest Derived
+ *
+ * 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.
+ *
+ */
+
+#ifndef USE_POSIX_REGEX
+#define USE_POSIX_REGEX		/* put before includes; always use POSIX */
+#endif
+
+#include "squid.h"
+#include "Store.h"
+
+typedef enum {
+    rcHTTP,
+    rcICP,
+#if USE_HTCP
+    rcHTCP,
+#endif
+#if USE_CACHE_DIGESTS
+    rcCDigest,
+#endif
+    rcStore,
+    rcCount
+} refreshCountsEnum;
+
+typedef struct {
+    unsigned int expires:1;
+    unsigned int min:1;
+    unsigned int lmfactor:1;
+    unsigned int max;
+} stale_flags;
+
+/*
+ * This enumerated list assigns specific values, ala HTTP/FTP status
+ * codes.  All Fresh codes are in the range 100-199 and all stale
+ * codes are 200-299.  We might want to use these codes in logging,
+ * so best to keep them consistent over time.
+ */
+enum {
+    FRESH_REQUEST_MAX_STALE_ALL = 100,
+    FRESH_REQUEST_MAX_STALE_VALUE,
+    FRESH_EXPIRES,
+    FRESH_LMFACTOR_RULE,
+    FRESH_MIN_RULE,
+    FRESH_OVERRIDE_EXPIRES,
+    FRESH_OVERRIDE_LASTMOD,
+    STALE_MUST_REVALIDATE = 200,
+    STALE_RELOAD_INTO_IMS,
+    STALE_FORCED_RELOAD,
+    STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
+    STALE_EXPIRES,
+    STALE_MAX_RULE,
+    STALE_LMFACTOR_RULE,
+    STALE_DEFAULT = 299
+};
+
+static struct RefreshCounts {
+    const char *proto;
+    int total;
+    int status[STALE_DEFAULT + 1];
+} refreshCounts[rcCount];
+
+/*
+ * Defaults:
+ *      MIN     NONE
+ *      PCT     20%
+ *      MAX     3 days
+ */
+#define REFRESH_DEFAULT_MIN	(time_t)0
+#define REFRESH_DEFAULT_PCT	0.20
+#define REFRESH_DEFAULT_MAX	(time_t)259200
+
+static const refresh_t *refreshLimits(const char *);
+static const refresh_t *refreshUncompiledPattern(const char *);
+static OBJH refreshStats;
+static int refreshStaleness(const StoreEntry *, time_t, time_t, const refresh_t *, stale_flags *);
+
+static refresh_t DefaultRefresh;
+
+static const refresh_t *
+refreshLimits(const char *url)
+{
+    const refresh_t *R;
+    for (R = Config.Refresh; R; R = R->next) {
+	if (!regexec(&(R->compiled_pattern), url, 0, 0, 0))
+	    return R;
+    }
+    return NULL;
+}
+
+static const refresh_t *
+refreshUncompiledPattern(const char *pat)
+{
+    const refresh_t *R;
+    for (R = Config.Refresh; R; R = R->next) {
+	if (0 == strcmp(R->pattern, pat))
+	    return R;
+    }
+    return NULL;
+}
+
+/*
+ * Calculate how stale the response is (or will be at the check_time).
+ * Staleness calculation is based on the following: (1) response
+ * expiration time, (2) age greater than configured maximum, (3)
+ * last-modified factor, and (4) age less than configured minimum.
+ *
+ * If the response is fresh, return -1.  Otherwise return its
+ * staleness.  NOTE return value of 0 means the response is stale.
+ *
+ * The 'stale_flags' structure is used to tell the calling function
+ * _why_ this response is fresh or stale.  Its used, for example,
+ * when the admin wants to override expiration and last-modified
+ * times.
+ */
+static int
+refreshStaleness(const StoreEntry * entry, time_t check_time, time_t age, const refresh_t * R, stale_flags * sf)
+{
+    /*
+     * Check for an explicit expiration time.
+     */
+    if (entry->expires > -1) {
+	sf->expires = 1;
+	if (entry->expires > check_time) {
+	    debug(22, 3) ("FRESH: expires %d >= check_time %d \n",
+		(int) entry->expires, (int) check_time);
+	    return -1;
+	} else {
+	    debug(22, 3) ("STALE: expires %d < check_time %d \n",
+		(int) entry->expires, (int) check_time);
+	    return (check_time - entry->expires);
+	}
+    }
+    assert(age >= 0);
+    /*
+     * Use local heuristics to determine staleness.  Start with the
+     * max age from the refresh_pattern rule.
+     */
+    if (age > R->max) {
+	debug(22, 3) ("STALE: age %d > max %d \n", (int) age, (int) R->max);
+	sf->max = 1;
+	return (age - R->max);
+    }
+    /*
+     * Try the last-modified factor algorithm.
+     */
+    if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) {
+	/*
+	 * stale_age is the Age of the response when it became/becomes
+	 * stale according to the last-modified factor algorithm.
+	 */
+	time_t stale_age = static_cast<time_t>((entry->timestamp - entry->lastmod) * R->pct);
+	sf->lmfactor = 1;
+	if (age >= stale_age) {
+	    debug(22, 3) ("STALE: age %d > stale_age %d\n",
+		(int) age, (int) stale_age);
+	    return (age - stale_age);
+	} else {
+	    debug(22, 3) ("FRESH: age %d <= stale_age %d\n",
+		(int) age, (int) stale_age);
+	    return -1;
+	}
+    }
+    /*
+     * If we are here, staleness is determined by the refresh_pattern
+     * configured minimum age.
+     */
+    if (age <= R->min) {
+	debug(22, 3) ("FRESH: age %d <= min %d\n", (int) age, (int) R->min);
+	sf->min = 1;
+	return -1;
+    }
+    debug(22, 3) ("STALE: age %d > min %d\n", (int) age, (int) R->min);
+    return (age - R->min);
+}
+
+/*  return 1 if the entry must be revalidated within delta seconds
+ *         0 otherwise
+ *
+ *  note: request maybe null (e.g. for cache digests build)
+ */
+static int
+refreshCheck(const StoreEntry * entry, request_t * request, time_t delta)
+{
+    const refresh_t *R;
+    const char *uri = NULL;
+    time_t age = 0;
+    time_t check_time = squid_curtime + delta;
+    int staleness;
+    stale_flags sf;
+    if (entry->mem_obj)
+	uri = entry->mem_obj->url;
+    else if (request)
+	uri = urlCanonical(request);
+
+    debug(22, 3) ("refreshCheck: '%s'\n", uri ? uri : "<none>");
+
+    if (check_time > entry->timestamp)
+	age = check_time - entry->timestamp;
+    R = uri ? refreshLimits(uri) : refreshUncompiledPattern(".");
+    if (NULL == R)
+	R = &DefaultRefresh;
+    memset(&sf, '\0', sizeof(sf));
+    staleness = refreshStaleness(entry, check_time, age, R, &sf);
+    debug(22, 3) ("Staleness = %d\n", staleness);
+
+    debug(22, 3) ("refreshCheck: Matched '%s %d %d%% %d'\n",
+	R->pattern, (int) R->min, (int) (100.0 * R->pct), (int) R->max);
+    debug(22, 3) ("refreshCheck: age = %d\n", (int) age);
+    debug(22, 3) ("\tcheck_time:\t%s\n", mkrfc1123(check_time));
+    debug(22, 3) ("\tentry->timestamp:\t%s\n", mkrfc1123(entry->timestamp));
+
+    if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1) {
+	debug(22, 3) ("refreshCheck: YES: Must revalidate stale response\n");
+	return STALE_MUST_REVALIDATE;
+    }
+    /* request-specific checks */
+    if (request) {
+	HttpHdrCc *cc = request->cache_control;
+#if HTTP_VIOLATIONS
+	if (!request->flags.nocache_hack) {
+	    (void) 0;
+	} else if (R->flags.ignore_reload) {
+	    /* The clients no-cache header is ignored */
+	    debug(22, 3) ("refreshCheck: MAYBE: ignore-reload\n");
+	} else if (R->flags.reload_into_ims || Config.onoff.reload_into_ims) {
+	    /* The clients no-cache header is changed into a IMS query */
+	    debug(22, 3) ("refreshCheck: YES: reload-into-ims\n");
+	    return STALE_RELOAD_INTO_IMS;
+	} else {
+	    /* The clients no-cache header is not overridden on this request */
+	    debug(22, 3) ("refreshCheck: YES: client reload\n");
+	    request->flags.nocache = 1;
+	    return STALE_FORCED_RELOAD;
+	}
+#endif
+	if (NULL != cc) {
+	    if (cc->max_age > -1) {
+#if HTTP_VIOLATIONS
+		if (R->flags.ignore_reload && cc->max_age == 0) {
+		} else
+#endif
+		if (age > cc->max_age) {
+		    debug(22, 3) ("refreshCheck: YES: age > client-max-age\n");
+		    return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE;
+		}
+	    }
+	    if (EBIT_TEST(cc->mask, CC_MAX_STALE) && staleness > -1) {
+		if (cc->max_stale < 0) {
+		    /* max-stale directive without a value */
+		    debug(22, 3) ("refreshCheck: NO: max-stale wildcard\n");
+		    return FRESH_REQUEST_MAX_STALE_ALL;
+		} else if (staleness < cc->max_stale) {
+		    debug(22, 3) ("refreshCheck: NO: staleness < max-stale\n");
+		    return FRESH_REQUEST_MAX_STALE_VALUE;
+		}
+	    }
+	}
+    }
+    if (-1 == staleness) {
+	if (sf.expires)
+	    return FRESH_EXPIRES;
+	assert(!sf.max);
+	if (sf.lmfactor)
+	    return FRESH_LMFACTOR_RULE;
+	assert(sf.min);
+	return FRESH_MIN_RULE;
+    }
+    /*
+     * At this point the response is stale, unless one of
+     * the override options kicks in.
+     */
+    if (sf.expires) {
+#if HTTP_VIOLATIONS
+	if (R->flags.override_expire && age < R->min) {
+	    debug(22, 3) ("refreshCheck: NO: age < min && override-expire\n");
+	    return FRESH_OVERRIDE_EXPIRES;
+	}
+#endif
+	return STALE_EXPIRES;
+    }
+    if (sf.max)
+	return STALE_MAX_RULE;
+    if (sf.lmfactor) {
+#if HTTP_VIOLATIONS
+	if (R->flags.override_lastmod && age < R->min) {
+	    debug(22, 3) ("refreshCheck: NO: age < min && override-lastmod\n");
+	    return FRESH_OVERRIDE_LASTMOD;
+	}
+#endif
+	return STALE_LMFACTOR_RULE;
+    }
+    return STALE_DEFAULT;
+}
+
+int
+refreshIsCachable(const StoreEntry * entry)
+{
+    /*
+     * Don't look at the request to avoid no-cache and other nuisances.
+     * the object should have a mem_obj so the URL will be found there.
+     * 60 seconds delta, to avoid objects which expire almost
+     * immediately, and which can't be refreshed.
+     */
+    int reason = refreshCheck(entry, NULL, 60);
+    refreshCounts[rcStore].total++;
+    refreshCounts[rcStore].status[reason]++;
+    if (reason < 200)
+	/* Does not need refresh. This is certainly cachable */
+	return 1;
+    if (entry->lastmod < 0)
+	/* Last modified is needed to do a refresh */
+	return 0;
+    if (entry->mem_obj == NULL)
+	/* no mem_obj? */
+	return 1;
+    if (entry->mem_obj->reply == NULL)
+	/* no reply? */
+	return 1;
+    if (entry->mem_obj->reply->content_length == 0)
+	/* No use refreshing (caching?) 0 byte objects */
+	return 0;
+    /* This seems to be refreshable. Cache it */
+    return 1;
+}
+
+/* refreshCheck... functions below are protocol-specific wrappers around
+ * refreshCheck() function above */
+
+int
+refreshCheckHTTP(const StoreEntry * entry, request_t * request)
+{
+    int reason = refreshCheck(entry, request, 0);
+    refreshCounts[rcHTTP].total++;
+    refreshCounts[rcHTTP].status[reason]++;
+    return (reason < 200) ? 0 : 1;
+}
+
+int
+refreshCheckICP(const StoreEntry * entry, request_t * request)
+{
+    int reason = refreshCheck(entry, request, 30);
+    refreshCounts[rcICP].total++;
+    refreshCounts[rcICP].status[reason]++;
+    return (reason < 200) ? 0 : 1;
+}
+
+#if USE_HTCP
+int
+refreshCheckHTCP(const StoreEntry * entry, request_t * request)
+{
+    int reason = refreshCheck(entry, request, 10);
+    refreshCounts[rcHTCP].total++;
+    refreshCounts[rcHTCP].status[reason]++;
+    return (reason < 200) ? 0 : 1;
+}
+#endif
+
+#if USE_CACHE_DIGESTS
+int
+refreshCheckDigest(const StoreEntry * entry, time_t delta)
+{
+    int reason = refreshCheck(entry,
+	entry->mem_obj ? entry->mem_obj->request : NULL,
+	delta);
+    refreshCounts[rcCDigest].total++;
+    refreshCounts[rcCDigest].status[reason]++;
+    return (reason < 200) ? 0 : 1;
+}
+#endif
+
+time_t
+getMaxAge(const char *url)
+{
+    const refresh_t *R;
+    debug(22, 3) ("getMaxAge: '%s'\n", url);
+    if ((R = refreshLimits(url)))
+	return R->max;
+    else
+	return REFRESH_DEFAULT_MAX;
+}
+
+static void
+refreshCountsStats(StoreEntry * sentry, struct RefreshCounts *rc)
+{
+    int sum = 0;
+    int tot = rc->total;
+
+    storeAppendPrintf(sentry, "\n\n%s histogram:\n", rc->proto);
+    storeAppendPrintf(sentry, "Count\t%%Total\tCategory\n");
+
+#define refreshCountsStatsEntry(code,desc) { \
+	storeAppendPrintf(sentry, "%6d\t%6.2f\t%s\n", \
+	    rc->status[code], xpercent(rc->status[code], tot), desc); \
+    sum += rc->status[code]; \
+}
+
+    refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_ALL,
+	"Fresh: request max-stale wildcard");
+    refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_VALUE,
+	"Fresh: request max-stale value");
+    refreshCountsStatsEntry(FRESH_EXPIRES,
+	"Fresh: expires time not reached");
+    refreshCountsStatsEntry(FRESH_LMFACTOR_RULE,
+	"Fresh: refresh_pattern last-mod factor percentage");
+    refreshCountsStatsEntry(FRESH_MIN_RULE,
+	"Fresh: refresh_pattern min value");
+    refreshCountsStatsEntry(FRESH_OVERRIDE_EXPIRES,
+	"Fresh: refresh_pattern override expires");
+    refreshCountsStatsEntry(FRESH_OVERRIDE_LASTMOD,
+	"Fresh: refresh_pattern override lastmod");
+    refreshCountsStatsEntry(STALE_MUST_REVALIDATE,
+	"Stale: response has must-revalidate");
+    refreshCountsStatsEntry(STALE_RELOAD_INTO_IMS,
+	"Stale: changed reload into IMS");
+    refreshCountsStatsEntry(STALE_FORCED_RELOAD,
+	"Stale: request has no-cache directive");
+    refreshCountsStatsEntry(STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
+	"Stale: age exceeds request max-age value");
+    refreshCountsStatsEntry(STALE_EXPIRES,
+	"Stale: expires time reached");
+    refreshCountsStatsEntry(STALE_MAX_RULE,
+	"Stale: refresh_pattern max age rule");
+    refreshCountsStatsEntry(STALE_LMFACTOR_RULE,
+	"Stale: refresh_pattern last-mod factor percentage");
+    refreshCountsStatsEntry(STALE_DEFAULT,
+	"Stale: by default");
+
+    tot = sum;			/* paranoid: "total" line shows 100% if we forgot nothing */
+    storeAppendPrintf(sentry, "%6d\t%6.2f\tTOTAL\n",
+	rc->total, xpercent(rc->total, tot));
+    \
+	storeAppendPrintf(sentry, "\n");
+}
+
+static void
+refreshStats(StoreEntry * sentry)
+{
+    int i;
+    int total = 0;
+
+    /* get total usage count */
+    for (i = 0; i < rcCount; ++i)
+	total += refreshCounts[i].total;
+
+    /* protocol usage histogram */
+    storeAppendPrintf(sentry, "\nRefreshCheck calls per protocol\n\n");
+    storeAppendPrintf(sentry, "Protocol\t#Calls\t%%Calls\n");
+    for (i = 0; i < rcCount; ++i)
+	storeAppendPrintf(sentry, "%10s\t%6d\t%6.2f\n",
+	    refreshCounts[i].proto,
+	    refreshCounts[i].total,
+	    xpercent(refreshCounts[i].total, total));
+
+    /* per protocol histograms */
+    storeAppendPrintf(sentry, "\n\nRefreshCheck histograms for various protocols\n");
+    for (i = 0; i < rcCount; ++i)
+	refreshCountsStats(sentry, &refreshCounts[i]);
+}
+
+void
+refreshInit(void)
+{
+    memset(refreshCounts, 0, sizeof(refreshCounts));
+    refreshCounts[rcHTTP].proto = "HTTP";
+    refreshCounts[rcICP].proto = "ICP";
+#if USE_HTCP
+    refreshCounts[rcHTCP].proto = "HTCP";
+#endif
+    refreshCounts[rcStore].proto = "On Store";
+#if USE_CACHE_DIGESTS
+    refreshCounts[rcCDigest].proto = "Cache Digests";
+#endif
+    cachemgrRegister("refresh",
+	"Refresh Algorithm Statistics",
+	refreshStats,
+	0,
+	1);
+    memset(&DefaultRefresh, '\0', sizeof(DefaultRefresh));
+    DefaultRefresh.pattern = "<none>";
+    DefaultRefresh.min = REFRESH_DEFAULT_MIN;
+    DefaultRefresh.pct = REFRESH_DEFAULT_PCT;
+    DefaultRefresh.max = REFRESH_DEFAULT_MAX;
+}
Index: squid/src/snmp_agent.cc
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/snmp_agent.cc,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- squid/src/snmp_agent.cc	9 Oct 2002 00:23:16 -0000	1.1.2.1
+++ squid/src/snmp_agent.cc	11 Oct 2002 15:41:01 -0000	1.1.2.2
@@ -1,6 +1,6 @@
 
 /*
- * $Id: snmp_agent.cc,v 1.1.2.1 2002/10/09 00:23:16 rbcollins Exp $
+ * $Id: snmp_agent.cc,v 1.1.2.2 2002/10/11 15:41:01 rbcollins Exp $
  *
  * DEBUG: section 49     SNMP Interface
  * AUTHOR: Kostas Anagnostakis
@@ -36,6 +36,7 @@
 
 #include "squid.h"
 #include "cache_snmp.h"
+#include "Store.h"
 
 /************************************************************************
 
@@ -63,7 +64,7 @@
 	break;
     case SYS_UPTIME:
 	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    tvSubDsec(squid_start, current_time) * 100,
+	    (int)(tvSubDsec(squid_start, current_time) * 100),
 	    SMI_TIMETICKS);
 	break;
     default:
@@ -299,7 +300,7 @@
 	break;
     case PERF_SYS_NUMOBJCNT:
 	Answer = snmp_var_new_integer(Var->name, Var->name_length,
-	    (snint) memInUse(MEM_STOREENTRY),
+	    (snint) _StoreEntry::inUseCount(),
 	    SMI_COUNTER32);
 	break;
     default:
--- squid/src/snmp_core.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,1071 +0,0 @@
-
-/*
- * $Id: snmp_core.c,v 1.10 2002/02/13 17:24:42 squidadm Exp $
- *
- * DEBUG: section 49    SNMP support
- * AUTHOR: Glenn Chisholm
- *
- * 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"
-#include "cache_snmp.h"
-
-#define SNMP_REQUEST_SIZE 4096
-#define MAX_PROTOSTAT 5
-
-typedef struct _mib_tree_entry mib_tree_entry;
-typedef oid *(instance_Fn) (oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
-
-struct _mib_tree_entry {
-    oid *name;
-    int len;
-    oid_ParseFn *parsefunction;
-    instance_Fn *instancefunction;
-    int children;
-    struct _mib_tree_entry **leaves;
-    struct _mib_tree_entry *parent;
-};
-
-mib_tree_entry *mib_tree_head;
-mib_tree_entry *mib_tree_last;
-
-#if STDC_HEADERS
-static mib_tree_entry *snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, int children,...);
-static oid *snmpCreateOid(int length,...);
-#else
-static mib_tree_entry *snmpAddNode();
-static oid *snmpCreateOid();
-#endif
-extern void (*snmplib_debug_hook) (int, char *);
-static oid *static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
-static oid *time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
-static oid *peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
-static oid *client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
-static void snmpDecodePacket(snmp_request_t * rq);
-static void snmpConstructReponse(snmp_request_t * rq);
-static struct snmp_pdu *snmpAgentResponse(struct snmp_pdu *PDU);
-static oid_ParseFn *snmpTreeNext(oid * Current, snint CurrentLen, oid ** Next, snint * NextLen);
-static oid_ParseFn *snmpTreeGet(oid * Current, snint CurrentLen);
-static mib_tree_entry *snmpTreeEntry(oid entry, snint len, mib_tree_entry * current);
-static mib_tree_entry *snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current);
-static void snmpSnmplibDebug(int lvl, char *buf);
-
-
-/*
- * The functions used during startup:
- * snmpInit
- * snmpConnectionOpen
- * snmpConnectionShutdown
- * snmpConnectionClose
- */
-
-/*
- * Turns the MIB into a Tree structure. Called during the startup process.
- */
-void
-snmpInit(void)
-{
-    debug(49, 5) ("snmpInit: Called.\n");
-
-    debug(49, 5) ("snmpInit: Building SNMP mib tree structure\n");
-
-    snmplib_debug_hook = snmpSnmplibDebug;
-
-    mib_tree_head = snmpAddNode(snmpCreateOid(1, 1),
-	1, NULL, NULL, 1,
-	snmpAddNode(snmpCreateOid(2, 1, 3),
-	    2, NULL, NULL, 1,
-	    snmpAddNode(snmpCreateOid(3, 1, 3, 6),
-		3, NULL, NULL, 1,
-		snmpAddNode(snmpCreateOid(4, 1, 3, 6, 1),
-		    4, NULL, NULL, 1,
-		    snmpAddNode(snmpCreateOid(5, 1, 3, 6, 1, 4),
-			5, NULL, NULL, 1,
-			snmpAddNode(snmpCreateOid(6, 1, 3, 6, 1, 4, 1),
-			    6, NULL, NULL, 1,
-			    snmpAddNode(snmpCreateOid(7, 1, 3, 6, 1, 4, 1, 3495),
-				7, NULL, NULL, 1,
-				snmpAddNode(snmpCreateOid(LEN_SQUIDMIB, SQUIDMIB),
-				    8, NULL, NULL, 5,
-				    snmpAddNode(snmpCreateOid(LEN_SQ_SYS, SQ_SYS),
-					LEN_SQ_SYS, NULL, NULL, 3,
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_SYS, SYSVMSIZ),
-					    LEN_SYS, snmp_sysFn, static_Inst, 0),
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_SYS, SYSSTOR),
-					    LEN_SYS, snmp_sysFn, static_Inst, 0),
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_SYS, SYS_UPTIME),
-					    LEN_SYS, snmp_sysFn, static_Inst, 0)),
-				    snmpAddNode(snmpCreateOid(LEN_SQ_CONF, SQ_CONF),
-					LEN_SQ_CONF, NULL, NULL, 5,
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_ADMIN),
-					    LEN_SYS, snmp_confFn, static_Inst, 0),
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_VERSION),
-					    LEN_SYS, snmp_confFn, static_Inst, 0),
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_VERSION_ID),
-					    LEN_SYS, snmp_confFn, static_Inst, 0),
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_LOG_FAC),
-					    LEN_SYS, snmp_confFn, static_Inst, 0),
-					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_STORAGE),
-					    LEN_SYS, NULL, NULL, 4,
-					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_MMAXSZ),
-						LEN_CONF_ST, snmp_confFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_SWMAXSZ),
-						LEN_CONF_ST, snmp_confFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_SWHIWM),
-						LEN_CONF_ST, snmp_confFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_SWLOWM),
-						LEN_CONF_ST, snmp_confFn, static_Inst, 0))),
-				    snmpAddNode(snmpCreateOid(LEN_SQ_PRF, SQ_PRF),
-					LEN_SQ_PRF, NULL, NULL, 2,
-					snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 1, SQ_PRF, PERF_SYS),
-					    LEN_SQ_PRF + 1, NULL, NULL, 11,
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 1),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 2),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 3),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 4),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 5),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 6),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 7),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 8),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 9),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 10),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 11),
-						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0)),
-					snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 1, SQ_PRF, PERF_PROTO),
-					    LEN_SQ_PRF + 1, NULL, NULL, 2,
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_PROTO, 1),
-						LEN_SQ_PRF + 2, NULL, NULL, 15,
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 1),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 2),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 3),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 4),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 5),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 6),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 7),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 8),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 9),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 10),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 11),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 12),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 13),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 14),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 15),
-						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0)),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_PROTO, 2),
-						LEN_SQ_PRF + 2, NULL, NULL, 1,
-						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 2, 1),
-						    LEN_SQ_PRF + 3, NULL, NULL, 10,
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 1),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 2),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 3),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 4),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 5),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 6),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 7),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 8),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 9),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
-						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 10),
-							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0))))),
-				    snmpAddNode(snmpCreateOid(LEN_SQ_NET, SQ_NET),
-					LEN_SQ_NET, NULL, NULL, 3,
-					snmpAddNode(snmpCreateOid(LEN_SQ_NET + 1, SQ_NET, NET_IP_CACHE),
-					    LEN_SQ_NET + 1, NULL, NULL, 8,
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_ENT),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_REQ),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_HITS),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_PENDHIT),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_NEGHIT),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_MISS),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_GHBN),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_LOC),
-						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0)),
-					snmpAddNode(snmpCreateOid(LEN_SQ_NET + 1, SQ_NET, NET_FQDN_CACHE),
-					    LEN_SQ_NET + 1, NULL, NULL, 7,
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_ENT),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_REQ),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_HITS),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_PENDHIT),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_NEGHIT),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_MISS),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_GHBN),
-						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0)),
-					snmpAddNode(snmpCreateOid(LEN_SQ_NET + 1, SQ_NET, NET_DNS_CACHE),
-					    LEN_SQ_NET + 1, NULL, NULL, 3,
-#if USE_DNSSERVERS
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REQ),
-						LEN_SQ_NET + 2, snmp_netDnsFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REP),
-						LEN_SQ_NET + 2, snmp_netDnsFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_SERVERS),
-						LEN_SQ_NET + 2, snmp_netDnsFn, static_Inst, 0))),
-#else
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REQ),
-						LEN_SQ_NET + 2, snmp_netIdnsFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REP),
-						LEN_SQ_NET + 2, snmp_netIdnsFn, static_Inst, 0),
-					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_SERVERS),
-						LEN_SQ_NET + 2, snmp_netIdnsFn, static_Inst, 0))),
-#endif
-				    snmpAddNode(snmpCreateOid(LEN_SQ_MESH, SQ_MESH),
-					LEN_SQ_MESH, NULL, NULL, 2,
-					snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 1, SQ_MESH, 1),
-					    LEN_SQ_MESH + 1, NULL, NULL, 1,
-					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 1, 1),
-						LEN_SQ_MESH + 2, NULL, NULL, 13,
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 1),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 2),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 3),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 4),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 5),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 6),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 7),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 8),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 9),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 10),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 11),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 12),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 13),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0))),
-					snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 1, SQ_MESH, 2),
-					    LEN_SQ_MESH + 1, NULL, NULL, 1,
-					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 2, 1),
-						LEN_SQ_MESH + 2, NULL, NULL, 9,
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 1),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 2),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 3),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 4),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 5),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 6),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 7),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 8),
-						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
-						(mib_tree_last = snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 9),
-							LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0)))))
-				)
-			    )
-			)
-		    )
-		)
-	    )
-	)
-	);
-
-    debug(49, 9) ("snmpInit: Completed SNMP mib tree structure\n");
-}
-
-void
-snmpConnectionOpen(void)
-{
-    u_short port;
-    struct sockaddr_in xaddr;
-    socklen_t len;
-    int x;
-
-    debug(49, 5) ("snmpConnectionOpen: Called\n");
-    if ((port = Config.Port.snmp) > (u_short) 0) {
-	enter_suid();
-	theInSnmpConnection = comm_open(SOCK_DGRAM,
-	    0,
-	    Config.Addrs.snmp_incoming,
-	    port,
-	    COMM_NONBLOCKING,
-	    "SNMP Port");
-	leave_suid();
-	if (theInSnmpConnection < 0)
-	    fatal("Cannot open snmp Port");
-	commSetSelect(theInSnmpConnection, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
-	debug(1, 1) ("Accepting SNMP messages on port %d, FD %d.\n",
-	    (int) port, theInSnmpConnection);
-	if (Config.Addrs.snmp_outgoing.s_addr != no_addr.s_addr) {
-	    enter_suid();
-	    theOutSnmpConnection = comm_open(SOCK_DGRAM,
-		0,
-		Config.Addrs.snmp_outgoing,
-		port,
-		COMM_NONBLOCKING,
-		"SNMP Port");
-	    leave_suid();
-	    if (theOutSnmpConnection < 0)
-		fatal("Cannot open Outgoing SNMP Port");
-	    commSetSelect(theOutSnmpConnection,
-		COMM_SELECT_READ,
-		snmpHandleUdp,
-		NULL, 0);
-	    debug(1, 1) ("Outgoing SNMP messages on port %d, FD %d.\n",
-		(int) port, theOutSnmpConnection);
-	    fd_note(theOutSnmpConnection, "Outgoing SNMP socket");
-	    fd_note(theInSnmpConnection, "Incoming SNMP socket");
-	} else {
-	    theOutSnmpConnection = theInSnmpConnection;
-	}
-	memset(&theOutSNMPAddr, '\0', sizeof(struct in_addr));
-	len = sizeof(struct sockaddr_in);
-	memset(&xaddr, '\0', len);
-	x = getsockname(theOutSnmpConnection,
-	    (struct sockaddr *) &xaddr, &len);
-	if (x < 0)
-	    debug(51, 1) ("theOutSnmpConnection FD %d: getsockname: %s\n",
-		theOutSnmpConnection, xstrerror());
-	else
-	    theOutSNMPAddr = xaddr.sin_addr;
-    }
-}
-
-void
-snmpConnectionShutdown(void)
-{
-    if (theInSnmpConnection < 0)
-	return;
-    if (theInSnmpConnection != theOutSnmpConnection) {
-	debug(49, 1) ("FD %d Closing SNMP socket\n", theInSnmpConnection);
-	comm_close(theInSnmpConnection);
-    }
-    /*
-     * Here we set 'theInSnmpConnection' to -1 even though the SNMP 'in'
-     * and 'out' sockets might be just one FD.  This prevents this
-     * function from executing repeatedly.  When we are really ready to
-     * exit or restart, main will comm_close the 'out' descriptor.
-     */ theInSnmpConnection = -1;
-    /*
-     * Normally we only write to the outgoing SNMP socket, but we
-     * also have a read handler there to catch messages sent to that
-     * specific interface.  During shutdown, we must disable reading
-     * on the outgoing socket.
-     */
-    assert(theOutSnmpConnection > -1);
-    commSetSelect(theOutSnmpConnection, COMM_SELECT_READ, NULL, NULL, 0);
-}
-
-void
-snmpConnectionClose(void)
-{
-    snmpConnectionShutdown();
-    if (theOutSnmpConnection > -1) {
-	debug(49, 1) ("FD %d Closing SNMP socket\n", theOutSnmpConnection);
-	comm_close(theOutSnmpConnection);
-    }
-}
-
-/*
- * Functions for handling the requests.
- */
-
-/*
- * Accept the UDP packet
- */
-void
-snmpHandleUdp(int sock, void *not_used)
-{
-    LOCAL_ARRAY(char, buf, SNMP_REQUEST_SIZE);
-    struct sockaddr_in from;
-    socklen_t from_len;
-    snmp_request_t *snmp_rq;
-    int len;
-
-    debug(49, 5) ("snmpHandleUdp: Called.\n");
-
-    commSetSelect(sock, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
-    from_len = sizeof(struct sockaddr_in);
-    memset(&from, '\0', from_len);
-    memset(buf, '\0', SNMP_REQUEST_SIZE);
-
-    statCounter.syscalls.sock.recvfroms++;
-
-    len = recvfrom(sock,
-	buf,
-	SNMP_REQUEST_SIZE,
-	0,
-	(struct sockaddr *) &from,
-	&from_len);
-
-    if (len > 0) {
-	buf[len] = '\0';
-	debug(49, 3) ("snmpHandleUdp: FD %d: received %d bytes from %s.\n",
-	    sock,
-	    len,
-	    inet_ntoa(from.sin_addr));
-
-	snmp_rq = xcalloc(1, sizeof(snmp_request_t));
-	snmp_rq->buf = (u_char *) buf;
-	snmp_rq->len = len;
-	snmp_rq->sock = sock;
-	snmp_rq->outbuf = xmalloc(snmp_rq->outlen = SNMP_REQUEST_SIZE);
-	xmemcpy(&snmp_rq->from, &from, sizeof(struct sockaddr_in));
-	snmpDecodePacket(snmp_rq);
-	xfree(snmp_rq->outbuf);
-	xfree(snmp_rq);
-    } else {
-	debug(49, 1) ("snmpHandleUdp: FD %d recvfrom: %s\n", sock, xstrerror());
-    }
-}
-
-/*
- * Turn SNMP packet into a PDU, check available ACL's
- */
-static void
-snmpDecodePacket(snmp_request_t * rq)
-{
-    struct snmp_pdu *PDU;
-    struct snmp_session Session;
-    aclCheck_t checklist;
-    u_char *Community;
-    u_char *buf = rq->buf;
-    int len = rq->len;
-    int allow = 0;
-
-    debug(49, 5) ("snmpDecodePacket: Called.\n");
-    /* Now that we have the data, turn it into a PDU */
-    PDU = snmp_pdu_create(0);
-    Session.Version = SNMP_VERSION_1;
-    Community = snmp_parse(&Session, PDU, buf, len);
-    memset(&checklist, '\0', sizeof(checklist));
-    checklist.src_addr = rq->from.sin_addr;
-    checklist.snmp_community = (char *) Community;
-
-    if (Community)
-	allow = aclCheckFast(Config.accessList.snmp, &checklist);
-    if ((snmp_coexist_V2toV1(PDU)) && (Community) && (allow)) {
-	rq->community = Community;
-	rq->PDU = PDU;
-	debug(49, 5) ("snmpAgentParse: reqid=[%d]\n", PDU->reqid);
-	snmpConstructReponse(rq);
-    } else {
-	debug(49, 1) ("Failed SNMP agent query from : %s.\n",
-	    inet_ntoa(rq->from.sin_addr));
-	snmp_free_pdu(PDU);
-    }
-    if (Community)
-	xfree(Community);
-}
-
-/*
- * Packet OK, ACL Check OK, Create reponse.
- */
-static void
-snmpConstructReponse(snmp_request_t * rq)
-{
-    struct snmp_session Session;
-    struct snmp_pdu *RespPDU;
-
-    debug(49, 5) ("snmpConstructReponse: Called.\n");
-    RespPDU = snmpAgentResponse(rq->PDU);
-    snmp_free_pdu(rq->PDU);
-    if (RespPDU != NULL) {
-	Session.Version = SNMP_VERSION_1;
-	Session.community = rq->community;
-	Session.community_len = strlen((char *) rq->community);
-	snmp_build(&Session, RespPDU, rq->outbuf, &rq->outlen);
-	sendto(rq->sock, rq->outbuf, rq->outlen, 0, (struct sockaddr *) &rq->from, sizeof(rq->from));
-	snmp_free_pdu(RespPDU);
-    }
-}
-
-/*
- * Decide how to respond to the request, construct a response and
- * return the response to the requester.
- */
-static struct snmp_pdu *
-snmpAgentResponse(struct snmp_pdu *PDU)
-{
-    struct snmp_pdu *Answer = NULL;
-    oid_ParseFn *ParseFn = NULL;
-
-    variable_list *VarPtr, *VarNew = NULL, **VarPtrP;
-    int index = 0;
-
-    debug(49, 5) ("snmpAgentResponse: Called.\n");
-
-    if ((Answer = snmp_pdu_create(SNMP_PDU_RESPONSE))) {
-	Answer->reqid = PDU->reqid;
-	Answer->errindex = 0;
-	if (PDU->command == SNMP_PDU_GET) {
-	    variable_list **RespVars;
-
-	    RespVars = &(Answer->variables);
-	    /* Loop through all variables */
-	    for (VarPtrP = &(PDU->variables);
-		*VarPtrP;
-		VarPtrP = &((*VarPtrP)->next_variable)) {
-		VarPtr = *VarPtrP;
-
-		index++;
-
-		/* Find the parsing function for this variable */
-		ParseFn = snmpTreeGet(VarPtr->name, VarPtr->name_length);
-
-		if (ParseFn == NULL) {
-		    Answer->errstat = SNMP_ERR_NOSUCHNAME;
-		    debug(49, 5) ("snmpAgentResponse: No such oid. ");
-		} else
-		    VarNew = (*ParseFn) (VarPtr, (snint *) & (Answer->errstat));
-
-		/* Was there an error? */
-		if ((Answer->errstat != SNMP_ERR_NOERROR) ||
-		    (VarNew == NULL)) {
-		    Answer->errindex = index;
-		    debug(49, 5) ("snmpAgentParse: successful.\n");
-		    /* Just copy the rest of the variables.  Quickly. */
-		    *RespVars = VarPtr;
-		    *VarPtrP = NULL;
-		    return (Answer);
-		}
-		/* No error.  Insert this var at the end, and move on to the next.
-		 */
-		*RespVars = VarNew;
-		RespVars = &(VarNew->next_variable);
-	    }
-	    return (Answer);
-	} else if (PDU->command == SNMP_PDU_GETNEXT) {
-	    oid *NextOidName = NULL;
-	    int NextOidNameLen = 0;
-
-	    ParseFn = snmpTreeNext(PDU->variables->name, PDU->variables->name_length,
-		&(NextOidName), (snint *) & NextOidNameLen);
-
-	    if (ParseFn == NULL) {
-		Answer->errstat = SNMP_ERR_NOSUCHNAME;
-		debug(49, 5) ("snmpAgentResponse: No such oid: ");
-		snmpDebugOid(5, PDU->variables->name, PDU->variables->name_length);
-	    } else {
-		xfree(PDU->variables->name);
-		PDU->variables->name = NextOidName;
-		PDU->variables->name_length = NextOidNameLen;
-		VarNew = (*ParseFn) (PDU->variables, (snint *) & Answer->errstat);
-	    }
-
-	    /* Was there an error? */
-	    if (Answer->errstat != SNMP_ERR_NOERROR) {
-		Answer->errindex = 1;
-		Answer->variables = PDU->variables;
-		PDU->variables = NULL;
-	    } else {
-		Answer->variables = VarNew;
-	    }
-
-	} else {
-	    snmp_free_pdu(Answer);
-	    Answer = NULL;
-	}
-    }
-    return (Answer);
-}
-
-static oid_ParseFn *
-snmpTreeGet(oid * Current, snint CurrentLen)
-{
-    oid_ParseFn *Fn = NULL;
-    mib_tree_entry *mibTreeEntry = NULL;
-    int count = 0;
-
-    debug(49, 5) ("snmpTreeGet: Called\n");
-
-    debug(49, 6) ("snmpTreeGet: Current : \n");
-    snmpDebugOid(6, Current, CurrentLen);
-
-    mibTreeEntry = mib_tree_head;
-    if (Current[count] == mibTreeEntry->name[count]) {
-	count++;
-	while ((mibTreeEntry) && (count < CurrentLen) && (!mibTreeEntry->parsefunction)) {
-	    mibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
-	    count++;
-	}
-    }
-    if (mibTreeEntry && mibTreeEntry->parsefunction)
-	Fn = mibTreeEntry->parsefunction;
-    debug(49, 5) ("snmpTreeGet: return\n");
-    return (Fn);
-}
-
-static oid_ParseFn *
-snmpTreeNext(oid * Current, snint CurrentLen, oid ** Next, snint * NextLen)
-{
-    oid_ParseFn *Fn = NULL;
-    mib_tree_entry *mibTreeEntry = NULL, *nextoid = NULL;
-    int count = 0;
-
-    debug(49, 5) ("snmpTreeNext: Called\n");
-
-    debug(49, 6) ("snmpTreeNext: Current : \n");
-    snmpDebugOid(6, Current, CurrentLen);
-
-    mibTreeEntry = mib_tree_head;
-    if (Current[count] == mibTreeEntry->name[count]) {
-	count++;
-	while ((mibTreeEntry) && (count < CurrentLen) && (!mibTreeEntry->parsefunction)) {
-	    mibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
-	    count++;
-	}
-	debug(49, 5) ("snmpTreeNext: Recursed down to requested object\n");
-    } else {
-	return NULL;
-    }
-    if (mibTreeEntry == mib_tree_last)
-	return (Fn);
-    if ((mibTreeEntry) && (mibTreeEntry->parsefunction)) {
-	*NextLen = CurrentLen;
-	*Next = (*mibTreeEntry->instancefunction) (Current, NextLen, mibTreeEntry, &Fn);
-	if (*Next)
-	    return (Fn);
-    }
-    if ((mibTreeEntry) && (mibTreeEntry->parsefunction)) {
-	count--;
-	nextoid = snmpTreeSiblingEntry(Current[count], count, mibTreeEntry->parent);
-	if (nextoid) {
-	    debug(49, 5) ("snmpTreeNext: Next OID found for sibling\n");
-	    mibTreeEntry = nextoid;
-	    count++;
-	} else {
-	    debug(49, 5) ("snmpTreeNext: Attempting to recurse up for next object\n");
-	    while (!nextoid) {
-		count--;
-		if (mibTreeEntry->parent->parent) {
-		    nextoid = mibTreeEntry->parent;
-		    mibTreeEntry = snmpTreeEntry(Current[count] + 1, count, nextoid->parent);
-		    if (!mibTreeEntry) {
-			mibTreeEntry = nextoid;
-			nextoid = NULL;
-		    }
-		} else {
-		    nextoid = mibTreeEntry;
-		    mibTreeEntry = NULL;
-		}
-	    }
-	}
-    }
-    while ((mibTreeEntry) && (!mibTreeEntry->parsefunction)) {
-	mibTreeEntry = mibTreeEntry->leaves[0];
-    }
-    if (mibTreeEntry) {
-	*NextLen = mibTreeEntry->len;
-	*Next = (*mibTreeEntry->instancefunction) (mibTreeEntry->name, NextLen, mibTreeEntry, &Fn);
-    }
-    if (*Next)
-	return (Fn);
-    else
-	return NULL;
-}
-
-static oid *
-static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
-{
-    oid *instance = NULL;
-
-    if (*len <= current->len) {
-	instance = xmalloc(sizeof(name) * (*len + 1));
-	xmemcpy(instance, name, (sizeof(name) * *len));
-	instance[*len] = 0;
-	*len += 1;
-    }
-    *Fn = current->parsefunction;
-    return (instance);
-}
-
-static oid *
-time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
-{
-    oid *instance = NULL;
-    int identifier = 0, loop = 0;
-    int index[TIME_INDEX_LEN] =
-    {TIME_INDEX};
-
-    if (*len <= current->len) {
-	instance = xmalloc(sizeof(name) * (*len + 1));
-	xmemcpy(instance, name, (sizeof(name) * *len));
-	instance[*len] = *index;
-	*len += 1;
-    } else {
-	identifier = name[*len - 1];
-	while ((identifier != index[loop]) && (loop < TIME_INDEX_LEN))
-	    loop++;
-	if (loop < TIME_INDEX_LEN - 1) {
-	    instance = xmalloc(sizeof(name) * (*len));
-	    xmemcpy(instance, name, (sizeof(name) * *len));
-	    instance[*len - 1] = index[++loop];
-	}
-    }
-    *Fn = current->parsefunction;
-    return (instance);
-}
-
-static oid *
-peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
-{
-    oid *instance = NULL;
-    u_char *cp = NULL;
-    peer *peers = Config.peers;
-    struct in_addr *laddr = NULL;
-    char *host_addr = NULL, *current_addr = NULL, *last_addr = NULL;
-
-    if (peers == NULL) {
-	current = current->parent->parent->parent->leaves[1];
-	while ((current) && (!current->parsefunction))
-	    current = current->leaves[0];
-	instance = client_Inst(current->name, len, current, Fn);
-    } else if (*len <= current->len) {
-	instance = xmalloc(sizeof(name) * (*len + 4));
-	xmemcpy(instance, name, (sizeof(name) * *len));
-	cp = (u_char *) & (peers->in_addr.sin_addr.s_addr);
-	instance[*len] = *cp++;
-	instance[*len + 1] = *cp++;
-	instance[*len + 2] = *cp++;
-	instance[*len + 3] = *cp++;
-	*len += 4;
-    } else {
-	laddr = oid2addr(&name[*len - 4]);
-	host_addr = inet_ntoa(*laddr);
-	last_addr = xmalloc(strlen(host_addr));
-	strncpy(last_addr, host_addr, strlen(host_addr));
-	current_addr = inet_ntoa(peers->in_addr.sin_addr);
-	while ((peers) && (strncmp(last_addr, current_addr, strlen(current_addr)))) {
-	    if (peers->next) {
-		peers = peers->next;
-		current_addr = inet_ntoa(peers->in_addr.sin_addr);
-	    } else {
-		peers = NULL;
-	    }
-	}
-	xfree(last_addr);
-	if (peers) {
-	    if (peers->next) {
-		peers = peers->next;
-		instance = xmalloc(sizeof(name) * (*len));
-		xmemcpy(instance, name, (sizeof(name) * *len));
-		cp = (u_char *) & (peers->in_addr.sin_addr.s_addr);
-		instance[*len - 4] = *cp++;
-		instance[*len - 3] = *cp++;
-		instance[*len - 2] = *cp++;
-		instance[*len - 1] = *cp++;
-	    } else {
-		return (instance);
-	    }
-	} else {
-	    return (instance);
-	}
-    }
-    *Fn = current->parsefunction;
-    return (instance);
-}
-
-static oid *
-client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
-{
-    oid *instance = NULL;
-    u_char *cp = NULL;
-    struct in_addr *laddr = NULL;
-
-    if (*len <= current->len) {
-	instance = xmalloc(sizeof(name) * (*len + 4));
-	xmemcpy(instance, name, (sizeof(name) * *len));
-	laddr = client_entry(NULL);
-	if (laddr) {
-	    cp = (u_char *) & (laddr->s_addr);
-	    instance[*len] = *cp++;
-	    instance[*len + 1] = *cp++;
-	    instance[*len + 2] = *cp++;
-	    instance[*len + 3] = *cp++;
-	    *len += 4;
-	}
-    } else {
-	laddr = oid2addr(&name[*len - 4]);
-	laddr = client_entry(laddr);
-	if (laddr) {
-	    instance = xmalloc(sizeof(name) * (*len));
-	    xmemcpy(instance, name, (sizeof(name) * *len));
-	    cp = (u_char *) & (laddr->s_addr);
-	    instance[*len - 4] = *cp++;
-	    instance[*len - 3] = *cp++;
-	    instance[*len - 2] = *cp++;
-	    instance[*len - 1] = *cp++;
-	}
-    }
-    *Fn = current->parsefunction;
-    return (instance);
-}
-
-
-/*
- * Utility functions
- */
-
-/*
- * Tree utility functions. 
- */
-
-/* 
- * Returns a the sibling object in the tree
- */
-static mib_tree_entry *
-snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current)
-{
-    mib_tree_entry *next = NULL;
-    int count = 0;
-
-    while ((!next) && (count < current->children)) {
-	if (current->leaves[count]->name[len] == entry) {
-	    next = current->leaves[count];
-	}
-	count++;
-    }
-    if (count < current->children) {
-	next = current->leaves[count];
-    } else {
-	next = NULL;
-    }
-    return (next);
-}
-
-/* 
- * Returns the requested child object or NULL if it does not exist
- */
-static mib_tree_entry *
-snmpTreeEntry(oid entry, snint len, mib_tree_entry * current)
-{
-    mib_tree_entry *next = NULL;
-    int count = 0;
-
-    while ((!next) && (count < current->children)) {
-	if (current->leaves[count]->name[len] == entry) {
-	    next = current->leaves[count];
-	}
-	count++;
-    }
-    return (next);
-}
-
-/*
- * Adds a node to the MIB tree structure and adds the appropriate children
- */
-static mib_tree_entry *
-#if STDC_HEADERS
-snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, int children,...)
-#else
-snmpAddNode(va_alist)
-     va_dcl
-#endif
-{
-#if STDC_HEADERS
-    va_list args;
-    int loop;
-    mib_tree_entry *entry = NULL;
-    va_start(args, children);
-#else
-    va_list args;
-    oid *name = NULL;
-    int len = 0, children = 0, loop;
-    oid_ParseFn *parsefunction = NULL;
-    instance_Fn *instancefunction = NULL;
-    mib_tree_entry *entry = NULL;
-    va_start(args);
-    name = va_arg(args, oid *);
-    len = va_arg(args, int);
-    parsefunction = va_arg(args, oid_ParseFn *);
-    instancefunction = va_arg(args, instance_Fn *);
-    children = va_arg(args, int);
-#endif
-
-    debug(49, 6) ("snmpAddNode: Children : %d, Oid : \n", children);
-    snmpDebugOid(6, name, len);
-
-    va_start(args, children);
-    entry = xmalloc(sizeof(mib_tree_entry));
-    entry->name = name;
-    entry->len = len;
-    entry->parsefunction = parsefunction;
-    entry->instancefunction = instancefunction;
-    entry->children = children;
-
-    if (children > 0) {
-	entry->leaves = xmalloc(sizeof(mib_tree_entry *) * children);
-	for (loop = 0; loop < children; loop++) {
-	    entry->leaves[loop] = va_arg(args, mib_tree_entry *);
-	    entry->leaves[loop]->parent = entry;
-	}
-    }
-    return (entry);
-}
-/* End of tree utility functions */
-
-/* 
- * Returns the list of parameters in an oid
- */
-static oid *
-#if STDC_HEADERS
-snmpCreateOid(int length,...)
-#else
-snmpCreateOid(va_alist)
-     va_dcl
-#endif
-{
-#if STDC_HEADERS
-    va_list args;
-    oid *new_oid;
-    int loop;
-    va_start(args, length);
-#else
-    va_list args;
-    int length = 0, loop;
-    oid *new_oid;
-    va_start(args);
-    length va_arg(args, int);
-#endif
-
-    new_oid = xmalloc(sizeof(oid) * length);
-
-    if (length > 0) {
-	for (loop = 0; loop < length; loop++) {
-	    new_oid[loop] = va_arg(args, int);
-	}
-    }
-    return (new_oid);
-}
-
-#if UNUSED_CODE
-/*
- * Allocate space for, and copy, an OID.  Returns new oid.
- */
-static oid *
-snmpOidDup(oid * A, snint ALen)
-{
-    oid *Ans = xmalloc(sizeof(oid) * ALen);
-    xmemcpy(Ans, A, (sizeof(oid) * ALen));
-    return Ans;
-}
-#endif
-
-/*
- * Debug calls, prints out the OID for debugging purposes.
- */
-void
-snmpDebugOid(int lvl, oid * Name, snint Len)
-{
-    char mbuf[16], objid[1024];
-    int x;
-    objid[0] = '\0';
-
-    for (x = 0; x < Len; x++) {
-	snprintf(mbuf, sizeof(mbuf), ".%u", (unsigned int) Name[x]);
-	strncat(objid, mbuf, sizeof(objid));
-    }
-
-    debug(49, lvl) ("   oid = %s\n", objid);
-}
-
-static void
-snmpSnmplibDebug(int lvl, char *buf)
-{
-    debug(49, lvl) ("%s", buf);
-}
-
-void
-addr2oid(struct in_addr addr, oid * Dest)
-{
-    u_char *cp;
-    cp = (u_char *) & (addr.s_addr);
-    Dest[0] = *cp++;
-    Dest[1] = *cp++;
-    Dest[2] = *cp++;
-    Dest[3] = *cp++;
-}
-
-struct in_addr
-       *
-oid2addr(oid * id)
-{
-    static struct in_addr laddr;
-    u_char *cp = (u_char *) & (laddr.s_addr);
-    cp[0] = id[0];
-    cp[1] = id[1];
-    cp[2] = id[2];
-    cp[3] = id[3];
-    return &laddr;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/snmp_core.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,1071 @@
+
+/*
+ * $Id: snmp_core.cc,v 1.1.2.1 2002/10/11 15:41:01 rbcollins Exp $
+ *
+ * DEBUG: section 49    SNMP support
+ * AUTHOR: Glenn Chisholm
+ *
+ * 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"
+#include "cache_snmp.h"
+
+#define SNMP_REQUEST_SIZE 4096
+#define MAX_PROTOSTAT 5
+
+typedef struct _mib_tree_entry mib_tree_entry;
+typedef oid *(instance_Fn) (oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
+
+struct _mib_tree_entry {
+    oid *name;
+    int len;
+    oid_ParseFn *parsefunction;
+    instance_Fn *instancefunction;
+    int children;
+    struct _mib_tree_entry **leaves;
+    struct _mib_tree_entry *parent;
+};
+
+mib_tree_entry *mib_tree_head;
+mib_tree_entry *mib_tree_last;
+
+#if STDC_HEADERS
+static mib_tree_entry *snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, int children,...);
+static oid *snmpCreateOid(int length,...);
+#else
+static mib_tree_entry *snmpAddNode();
+static oid *snmpCreateOid();
+#endif
+extern void (*snmplib_debug_hook) (int, char *);
+static oid *static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
+static oid *time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
+static oid *peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
+static oid *client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
+static void snmpDecodePacket(snmp_request_t * rq);
+static void snmpConstructReponse(snmp_request_t * rq);
+static struct snmp_pdu *snmpAgentResponse(struct snmp_pdu *PDU);
+static oid_ParseFn *snmpTreeNext(oid * Current, snint CurrentLen, oid ** Next, snint * NextLen);
+static oid_ParseFn *snmpTreeGet(oid * Current, snint CurrentLen);
+static mib_tree_entry *snmpTreeEntry(oid entry, snint len, mib_tree_entry * current);
+static mib_tree_entry *snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current);
+static void snmpSnmplibDebug(int lvl, char *buf);
+
+
+/*
+ * The functions used during startup:
+ * snmpInit
+ * snmpConnectionOpen
+ * snmpConnectionShutdown
+ * snmpConnectionClose
+ */
+
+/*
+ * Turns the MIB into a Tree structure. Called during the startup process.
+ */
+void
+snmpInit(void)
+{
+    debug(49, 5) ("snmpInit: Called.\n");
+
+    debug(49, 5) ("snmpInit: Building SNMP mib tree structure\n");
+
+    snmplib_debug_hook = snmpSnmplibDebug;
+
+    mib_tree_head = snmpAddNode(snmpCreateOid(1, 1),
+	1, NULL, NULL, 1,
+	snmpAddNode(snmpCreateOid(2, 1, 3),
+	    2, NULL, NULL, 1,
+	    snmpAddNode(snmpCreateOid(3, 1, 3, 6),
+		3, NULL, NULL, 1,
+		snmpAddNode(snmpCreateOid(4, 1, 3, 6, 1),
+		    4, NULL, NULL, 1,
+		    snmpAddNode(snmpCreateOid(5, 1, 3, 6, 1, 4),
+			5, NULL, NULL, 1,
+			snmpAddNode(snmpCreateOid(6, 1, 3, 6, 1, 4, 1),
+			    6, NULL, NULL, 1,
+			    snmpAddNode(snmpCreateOid(7, 1, 3, 6, 1, 4, 1, 3495),
+				7, NULL, NULL, 1,
+				snmpAddNode(snmpCreateOid(LEN_SQUIDMIB, SQUIDMIB),
+				    8, NULL, NULL, 5,
+				    snmpAddNode(snmpCreateOid(LEN_SQ_SYS, SQ_SYS),
+					LEN_SQ_SYS, NULL, NULL, 3,
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_SYS, SYSVMSIZ),
+					    LEN_SYS, snmp_sysFn, static_Inst, 0),
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_SYS, SYSSTOR),
+					    LEN_SYS, snmp_sysFn, static_Inst, 0),
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_SYS, SYS_UPTIME),
+					    LEN_SYS, snmp_sysFn, static_Inst, 0)),
+				    snmpAddNode(snmpCreateOid(LEN_SQ_CONF, SQ_CONF),
+					LEN_SQ_CONF, NULL, NULL, 5,
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_ADMIN),
+					    LEN_SYS, snmp_confFn, static_Inst, 0),
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_VERSION),
+					    LEN_SYS, snmp_confFn, static_Inst, 0),
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_VERSION_ID),
+					    LEN_SYS, snmp_confFn, static_Inst, 0),
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_LOG_FAC),
+					    LEN_SYS, snmp_confFn, static_Inst, 0),
+					snmpAddNode(snmpCreateOid(LEN_SYS, SQ_CONF, CONF_STORAGE),
+					    LEN_SYS, NULL, NULL, 4,
+					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_MMAXSZ),
+						LEN_CONF_ST, snmp_confFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_SWMAXSZ),
+						LEN_CONF_ST, snmp_confFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_SWHIWM),
+						LEN_CONF_ST, snmp_confFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_CONF_ST, SQ_CONF, CONF_STORAGE, CONF_ST_SWLOWM),
+						LEN_CONF_ST, snmp_confFn, static_Inst, 0))),
+				    snmpAddNode(snmpCreateOid(LEN_SQ_PRF, SQ_PRF),
+					LEN_SQ_PRF, NULL, NULL, 2,
+					snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 1, SQ_PRF, PERF_SYS),
+					    LEN_SQ_PRF + 1, NULL, NULL, 11,
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 1),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 2),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 3),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 4),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 5),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 6),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 7),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 8),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 9),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 10),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_SYS, 11),
+						LEN_SQ_PRF + 2, snmp_prfSysFn, static_Inst, 0)),
+					snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 1, SQ_PRF, PERF_PROTO),
+					    LEN_SQ_PRF + 1, NULL, NULL, 2,
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_PROTO, 1),
+						LEN_SQ_PRF + 2, NULL, NULL, 15,
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 1),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 2),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 3),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 4),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 5),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 6),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 7),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 8),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 9),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 10),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 11),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 12),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 13),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 14),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 1, 15),
+						    LEN_SQ_PRF + 3, snmp_prfProtoFn, static_Inst, 0)),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 2, SQ_PRF, PERF_PROTO, 2),
+						LEN_SQ_PRF + 2, NULL, NULL, 1,
+						snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 3, SQ_PRF, PERF_PROTO, 2, 1),
+						    LEN_SQ_PRF + 3, NULL, NULL, 10,
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 1),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 2),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 3),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 4),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 5),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 6),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 7),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 8),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 9),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0),
+						    snmpAddNode(snmpCreateOid(LEN_SQ_PRF + 4, SQ_PRF, PERF_PROTO, 2, 1, 10),
+							LEN_SQ_PRF + 4, snmp_prfProtoFn, time_Inst, 0))))),
+				    snmpAddNode(snmpCreateOid(LEN_SQ_NET, SQ_NET),
+					LEN_SQ_NET, NULL, NULL, 3,
+					snmpAddNode(snmpCreateOid(LEN_SQ_NET + 1, SQ_NET, NET_IP_CACHE),
+					    LEN_SQ_NET + 1, NULL, NULL, 8,
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_ENT),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_REQ),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_HITS),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_PENDHIT),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_NEGHIT),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_MISS),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_GHBN),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_IP_CACHE, IP_LOC),
+						LEN_SQ_NET + 2, snmp_netIpFn, static_Inst, 0)),
+					snmpAddNode(snmpCreateOid(LEN_SQ_NET + 1, SQ_NET, NET_FQDN_CACHE),
+					    LEN_SQ_NET + 1, NULL, NULL, 7,
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_ENT),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_REQ),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_HITS),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_PENDHIT),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_NEGHIT),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_MISS),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_FQDN_CACHE, FQDN_GHBN),
+						LEN_SQ_NET + 2, snmp_netFqdnFn, static_Inst, 0)),
+					snmpAddNode(snmpCreateOid(LEN_SQ_NET + 1, SQ_NET, NET_DNS_CACHE),
+					    LEN_SQ_NET + 1, NULL, NULL, 3,
+#if USE_DNSSERVERS
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REQ),
+						LEN_SQ_NET + 2, snmp_netDnsFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REP),
+						LEN_SQ_NET + 2, snmp_netDnsFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_SERVERS),
+						LEN_SQ_NET + 2, snmp_netDnsFn, static_Inst, 0))),
+#else
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REQ),
+						LEN_SQ_NET + 2, snmp_netIdnsFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_REP),
+						LEN_SQ_NET + 2, snmp_netIdnsFn, static_Inst, 0),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_NET + 2, SQ_NET, NET_DNS_CACHE, DNS_SERVERS),
+						LEN_SQ_NET + 2, snmp_netIdnsFn, static_Inst, 0))),
+#endif
+				    snmpAddNode(snmpCreateOid(LEN_SQ_MESH, SQ_MESH),
+					LEN_SQ_MESH, NULL, NULL, 2,
+					snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 1, SQ_MESH, 1),
+					    LEN_SQ_MESH + 1, NULL, NULL, 1,
+					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 1, 1),
+						LEN_SQ_MESH + 2, NULL, NULL, 13,
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 1),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 2),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 3),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 4),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 5),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 6),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 7),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 8),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 9),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 10),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 11),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 12),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 13),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0))),
+					snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 1, SQ_MESH, 2),
+					    LEN_SQ_MESH + 1, NULL, NULL, 1,
+					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 2, 1),
+						LEN_SQ_MESH + 2, NULL, NULL, 9,
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 1),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 2),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 3),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 4),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 5),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 6),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 7),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 8),
+						    LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0),
+						(mib_tree_last = snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 2, 1, 9),
+							LEN_SQ_MESH + 3, snmp_meshCtblFn, client_Inst, 0)))))
+				)
+			    )
+			)
+		    )
+		)
+	    )
+	)
+	);
+
+    debug(49, 9) ("snmpInit: Completed SNMP mib tree structure\n");
+}
+
+void
+snmpConnectionOpen(void)
+{
+    u_short port;
+    struct sockaddr_in xaddr;
+    socklen_t len;
+    int x;
+
+    debug(49, 5) ("snmpConnectionOpen: Called\n");
+    if ((port = Config.Port.snmp) > (u_short) 0) {
+	enter_suid();
+	theInSnmpConnection = comm_open(SOCK_DGRAM,
+	    0,
+	    Config.Addrs.snmp_incoming,
+	    port,
+	    COMM_NONBLOCKING,
+	    "SNMP Port");
+	leave_suid();
+	if (theInSnmpConnection < 0)
+	    fatal("Cannot open snmp Port");
+	commSetSelect(theInSnmpConnection, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
+	debug(1, 1) ("Accepting SNMP messages on port %d, FD %d.\n",
+	    (int) port, theInSnmpConnection);
+	if (Config.Addrs.snmp_outgoing.s_addr != no_addr.s_addr) {
+	    enter_suid();
+	    theOutSnmpConnection = comm_open(SOCK_DGRAM,
+		0,
+		Config.Addrs.snmp_outgoing,
+		port,
+		COMM_NONBLOCKING,
+		"SNMP Port");
+	    leave_suid();
+	    if (theOutSnmpConnection < 0)
+		fatal("Cannot open Outgoing SNMP Port");
+	    commSetSelect(theOutSnmpConnection,
+		COMM_SELECT_READ,
+		snmpHandleUdp,
+		NULL, 0);
+	    debug(1, 1) ("Outgoing SNMP messages on port %d, FD %d.\n",
+		(int) port, theOutSnmpConnection);
+	    fd_note(theOutSnmpConnection, "Outgoing SNMP socket");
+	    fd_note(theInSnmpConnection, "Incoming SNMP socket");
+	} else {
+	    theOutSnmpConnection = theInSnmpConnection;
+	}
+	memset(&theOutSNMPAddr, '\0', sizeof(struct in_addr));
+	len = sizeof(struct sockaddr_in);
+	memset(&xaddr, '\0', len);
+	x = getsockname(theOutSnmpConnection,
+	    (struct sockaddr *) &xaddr, &len);
+	if (x < 0)
+	    debug(51, 1) ("theOutSnmpConnection FD %d: getsockname: %s\n",
+		theOutSnmpConnection, xstrerror());
+	else
+	    theOutSNMPAddr = xaddr.sin_addr;
+    }
+}
+
+void
+snmpConnectionShutdown(void)
+{
+    if (theInSnmpConnection < 0)
+	return;
+    if (theInSnmpConnection != theOutSnmpConnection) {
+	debug(49, 1) ("FD %d Closing SNMP socket\n", theInSnmpConnection);
+	comm_close(theInSnmpConnection);
+    }
+    /*
+     * Here we set 'theInSnmpConnection' to -1 even though the SNMP 'in'
+     * and 'out' sockets might be just one FD.  This prevents this
+     * function from executing repeatedly.  When we are really ready to
+     * exit or restart, main will comm_close the 'out' descriptor.
+     */ theInSnmpConnection = -1;
+    /*
+     * Normally we only write to the outgoing SNMP socket, but we
+     * also have a read handler there to catch messages sent to that
+     * specific interface.  During shutdown, we must disable reading
+     * on the outgoing socket.
+     */
+    assert(theOutSnmpConnection > -1);
+    commSetSelect(theOutSnmpConnection, COMM_SELECT_READ, NULL, NULL, 0);
+}
+
+void
+snmpConnectionClose(void)
+{
+    snmpConnectionShutdown();
+    if (theOutSnmpConnection > -1) {
+	debug(49, 1) ("FD %d Closing SNMP socket\n", theOutSnmpConnection);
+	comm_close(theOutSnmpConnection);
+    }
+}
+
+/*
+ * Functions for handling the requests.
+ */
+
+/*
+ * Accept the UDP packet
+ */
+void
+snmpHandleUdp(int sock, void *not_used)
+{
+    LOCAL_ARRAY(char, buf, SNMP_REQUEST_SIZE);
+    struct sockaddr_in from;
+    socklen_t from_len;
+    snmp_request_t *snmp_rq;
+    int len;
+
+    debug(49, 5) ("snmpHandleUdp: Called.\n");
+
+    commSetSelect(sock, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
+    from_len = sizeof(struct sockaddr_in);
+    memset(&from, '\0', from_len);
+    memset(buf, '\0', SNMP_REQUEST_SIZE);
+
+    statCounter.syscalls.sock.recvfroms++;
+
+    len = recvfrom(sock,
+	buf,
+	SNMP_REQUEST_SIZE,
+	0,
+	(struct sockaddr *) &from,
+	&from_len);
+
+    if (len > 0) {
+	buf[len] = '\0';
+	debug(49, 3) ("snmpHandleUdp: FD %d: received %d bytes from %s.\n",
+	    sock,
+	    len,
+	    inet_ntoa(from.sin_addr));
+
+	snmp_rq = (snmp_request_t *)xcalloc(1, sizeof(snmp_request_t));
+	snmp_rq->buf = (u_char *) buf;
+	snmp_rq->len = len;
+	snmp_rq->sock = sock;
+	snmp_rq->outbuf = (unsigned char *)xmalloc(snmp_rq->outlen = SNMP_REQUEST_SIZE);
+	xmemcpy(&snmp_rq->from, &from, sizeof(struct sockaddr_in));
+	snmpDecodePacket(snmp_rq);
+	xfree(snmp_rq->outbuf);
+	xfree(snmp_rq);
+    } else {
+	debug(49, 1) ("snmpHandleUdp: FD %d recvfrom: %s\n", sock, xstrerror());
+    }
+}
+
+/*
+ * Turn SNMP packet into a PDU, check available ACL's
+ */
+static void
+snmpDecodePacket(snmp_request_t * rq)
+{
+    struct snmp_pdu *PDU;
+    struct snmp_session Session;
+    aclCheck_t checklist;
+    u_char *Community;
+    u_char *buf = rq->buf;
+    int len = rq->len;
+    int allow = 0;
+
+    debug(49, 5) ("snmpDecodePacket: Called.\n");
+    /* Now that we have the data, turn it into a PDU */
+    PDU = snmp_pdu_create(0);
+    Session.Version = SNMP_VERSION_1;
+    Community = snmp_parse(&Session, PDU, buf, len);
+    memset(&checklist, '\0', sizeof(checklist));
+    checklist.src_addr = rq->from.sin_addr;
+    checklist.snmp_community = (char *) Community;
+
+    if (Community)
+	allow = aclCheckFast(Config.accessList.snmp, &checklist);
+    if ((snmp_coexist_V2toV1(PDU)) && (Community) && (allow)) {
+	rq->community = Community;
+	rq->PDU = PDU;
+	debug(49, 5) ("snmpAgentParse: reqid=[%d]\n", PDU->reqid);
+	snmpConstructReponse(rq);
+    } else {
+	debug(49, 1) ("Failed SNMP agent query from : %s.\n",
+	    inet_ntoa(rq->from.sin_addr));
+	snmp_free_pdu(PDU);
+    }
+    if (Community)
+	xfree(Community);
+}
+
+/*
+ * Packet OK, ACL Check OK, Create reponse.
+ */
+static void
+snmpConstructReponse(snmp_request_t * rq)
+{
+    struct snmp_session Session;
+    struct snmp_pdu *RespPDU;
+
+    debug(49, 5) ("snmpConstructReponse: Called.\n");
+    RespPDU = snmpAgentResponse(rq->PDU);
+    snmp_free_pdu(rq->PDU);
+    if (RespPDU != NULL) {
+	Session.Version = SNMP_VERSION_1;
+	Session.community = rq->community;
+	Session.community_len = strlen((char *) rq->community);
+	snmp_build(&Session, RespPDU, rq->outbuf, &rq->outlen);
+	sendto(rq->sock, rq->outbuf, rq->outlen, 0, (struct sockaddr *) &rq->from, sizeof(rq->from));
+	snmp_free_pdu(RespPDU);
+    }
+}
+
+/*
+ * Decide how to respond to the request, construct a response and
+ * return the response to the requester.
+ */
+static struct snmp_pdu *
+snmpAgentResponse(struct snmp_pdu *PDU)
+{
+    struct snmp_pdu *Answer = NULL;
+    oid_ParseFn *ParseFn = NULL;
+
+    variable_list *VarPtr, *VarNew = NULL, **VarPtrP;
+    int index = 0;
+
+    debug(49, 5) ("snmpAgentResponse: Called.\n");
+
+    if ((Answer = snmp_pdu_create(SNMP_PDU_RESPONSE))) {
+	Answer->reqid = PDU->reqid;
+	Answer->errindex = 0;
+	if (PDU->command == SNMP_PDU_GET) {
+	    variable_list **RespVars;
+
+	    RespVars = &(Answer->variables);
+	    /* Loop through all variables */
+	    for (VarPtrP = &(PDU->variables);
+		*VarPtrP;
+		VarPtrP = &((*VarPtrP)->next_variable)) {
+		VarPtr = *VarPtrP;
+
+		index++;
+
+		/* Find the parsing function for this variable */
+		ParseFn = snmpTreeGet(VarPtr->name, VarPtr->name_length);
+
+		if (ParseFn == NULL) {
+		    Answer->errstat = SNMP_ERR_NOSUCHNAME;
+		    debug(49, 5) ("snmpAgentResponse: No such oid. ");
+		} else
+		    VarNew = (*ParseFn) (VarPtr, (snint *) & (Answer->errstat));
+
+		/* Was there an error? */
+		if ((Answer->errstat != SNMP_ERR_NOERROR) ||
+		    (VarNew == NULL)) {
+		    Answer->errindex = index;
+		    debug(49, 5) ("snmpAgentParse: successful.\n");
+		    /* Just copy the rest of the variables.  Quickly. */
+		    *RespVars = VarPtr;
+		    *VarPtrP = NULL;
+		    return (Answer);
+		}
+		/* No error.  Insert this var at the end, and move on to the next.
+		 */
+		*RespVars = VarNew;
+		RespVars = &(VarNew->next_variable);
+	    }
+	    return (Answer);
+	} else if (PDU->command == SNMP_PDU_GETNEXT) {
+	    oid *NextOidName = NULL;
+	    int NextOidNameLen = 0;
+
+	    ParseFn = snmpTreeNext(PDU->variables->name, PDU->variables->name_length,
+		&(NextOidName), (snint *) & NextOidNameLen);
+
+	    if (ParseFn == NULL) {
+		Answer->errstat = SNMP_ERR_NOSUCHNAME;
+		debug(49, 5) ("snmpAgentResponse: No such oid: ");
+		snmpDebugOid(5, PDU->variables->name, PDU->variables->name_length);
+	    } else {
+		xfree(PDU->variables->name);
+		PDU->variables->name = NextOidName;
+		PDU->variables->name_length = NextOidNameLen;
+		VarNew = (*ParseFn) (PDU->variables, (snint *) & Answer->errstat);
+	    }
+
+	    /* Was there an error? */
+	    if (Answer->errstat != SNMP_ERR_NOERROR) {
+		Answer->errindex = 1;
+		Answer->variables = PDU->variables;
+		PDU->variables = NULL;
+	    } else {
+		Answer->variables = VarNew;
+	    }
+
+	} else {
+	    snmp_free_pdu(Answer);
+	    Answer = NULL;
+	}
+    }
+    return (Answer);
+}
+
+static oid_ParseFn *
+snmpTreeGet(oid * Current, snint CurrentLen)
+{
+    oid_ParseFn *Fn = NULL;
+    mib_tree_entry *mibTreeEntry = NULL;
+    int count = 0;
+
+    debug(49, 5) ("snmpTreeGet: Called\n");
+
+    debug(49, 6) ("snmpTreeGet: Current : \n");
+    snmpDebugOid(6, Current, CurrentLen);
+
+    mibTreeEntry = mib_tree_head;
+    if (Current[count] == mibTreeEntry->name[count]) {
+	count++;
+	while ((mibTreeEntry) && (count < CurrentLen) && (!mibTreeEntry->parsefunction)) {
+	    mibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
+	    count++;
+	}
+    }
+    if (mibTreeEntry && mibTreeEntry->parsefunction)
+	Fn = mibTreeEntry->parsefunction;
+    debug(49, 5) ("snmpTreeGet: return\n");
+    return (Fn);
+}
+
+static oid_ParseFn *
+snmpTreeNext(oid * Current, snint CurrentLen, oid ** Next, snint * NextLen)
+{
+    oid_ParseFn *Fn = NULL;
+    mib_tree_entry *mibTreeEntry = NULL, *nextoid = NULL;
+    int count = 0;
+
+    debug(49, 5) ("snmpTreeNext: Called\n");
+
+    debug(49, 6) ("snmpTreeNext: Current : \n");
+    snmpDebugOid(6, Current, CurrentLen);
+
+    mibTreeEntry = mib_tree_head;
+    if (Current[count] == mibTreeEntry->name[count]) {
+	count++;
+	while ((mibTreeEntry) && (count < CurrentLen) && (!mibTreeEntry->parsefunction)) {
+	    mibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
+	    count++;
+	}
+	debug(49, 5) ("snmpTreeNext: Recursed down to requested object\n");
+    } else {
+	return NULL;
+    }
+    if (mibTreeEntry == mib_tree_last)
+	return (Fn);
+    if ((mibTreeEntry) && (mibTreeEntry->parsefunction)) {
+	*NextLen = CurrentLen;
+	*Next = (*mibTreeEntry->instancefunction) (Current, NextLen, mibTreeEntry, &Fn);
+	if (*Next)
+	    return (Fn);
+    }
+    if ((mibTreeEntry) && (mibTreeEntry->parsefunction)) {
+	count--;
+	nextoid = snmpTreeSiblingEntry(Current[count], count, mibTreeEntry->parent);
+	if (nextoid) {
+	    debug(49, 5) ("snmpTreeNext: Next OID found for sibling\n");
+	    mibTreeEntry = nextoid;
+	    count++;
+	} else {
+	    debug(49, 5) ("snmpTreeNext: Attempting to recurse up for next object\n");
+	    while (!nextoid) {
+		count--;
+		if (mibTreeEntry->parent->parent) {
+		    nextoid = mibTreeEntry->parent;
+		    mibTreeEntry = snmpTreeEntry(Current[count] + 1, count, nextoid->parent);
+		    if (!mibTreeEntry) {
+			mibTreeEntry = nextoid;
+			nextoid = NULL;
+		    }
+		} else {
+		    nextoid = mibTreeEntry;
+		    mibTreeEntry = NULL;
+		}
+	    }
+	}
+    }
+    while ((mibTreeEntry) && (!mibTreeEntry->parsefunction)) {
+	mibTreeEntry = mibTreeEntry->leaves[0];
+    }
+    if (mibTreeEntry) {
+	*NextLen = mibTreeEntry->len;
+	*Next = (*mibTreeEntry->instancefunction) (mibTreeEntry->name, NextLen, mibTreeEntry, &Fn);
+    }
+    if (*Next)
+	return (Fn);
+    else
+	return NULL;
+}
+
+static oid *
+static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
+{
+    oid *instance = NULL;
+
+    if (*len <= current->len) {
+	instance = (oid *)xmalloc(sizeof(name) * (*len + 1));
+	xmemcpy(instance, name, (sizeof(name) * *len));
+	instance[*len] = 0;
+	*len += 1;
+    }
+    *Fn = current->parsefunction;
+    return (instance);
+}
+
+static oid *
+time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
+{
+    oid *instance = NULL;
+    int identifier = 0, loop = 0;
+    int index[TIME_INDEX_LEN] =
+    {TIME_INDEX};
+
+    if (*len <= current->len) {
+	instance = (oid *)xmalloc(sizeof(name) * (*len + 1));
+	xmemcpy(instance, name, (sizeof(name) * *len));
+	instance[*len] = *index;
+	*len += 1;
+    } else {
+	identifier = name[*len - 1];
+	while ((identifier != index[loop]) && (loop < TIME_INDEX_LEN))
+	    loop++;
+	if (loop < TIME_INDEX_LEN - 1) {
+	    instance = (oid *)xmalloc(sizeof(name) * (*len));
+	    xmemcpy(instance, name, (sizeof(name) * *len));
+	    instance[*len - 1] = index[++loop];
+	}
+    }
+    *Fn = current->parsefunction;
+    return (instance);
+}
+
+static oid *
+peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
+{
+    oid *instance = NULL;
+    u_char *cp = NULL;
+    peer *peers = Config.peers;
+    struct in_addr *laddr = NULL;
+    char *host_addr = NULL, *current_addr = NULL, *last_addr = NULL;
+
+    if (peers == NULL) {
+	current = current->parent->parent->parent->leaves[1];
+	while ((current) && (!current->parsefunction))
+	    current = current->leaves[0];
+	instance = client_Inst(current->name, len, current, Fn);
+    } else if (*len <= current->len) {
+	instance = (oid *)xmalloc(sizeof(name) * (*len + 4));
+	xmemcpy(instance, name, (sizeof(name) * *len));
+	cp = (u_char *) & (peers->in_addr.sin_addr.s_addr);
+	instance[*len] = *cp++;
+	instance[*len + 1] = *cp++;
+	instance[*len + 2] = *cp++;
+	instance[*len + 3] = *cp++;
+	*len += 4;
+    } else {
+	laddr = oid2addr(&name[*len - 4]);
+	host_addr = inet_ntoa(*laddr);
+	last_addr = (char *)xmalloc(strlen(host_addr));
+	strncpy(last_addr, host_addr, strlen(host_addr));
+	current_addr = inet_ntoa(peers->in_addr.sin_addr);
+	while ((peers) && (strncmp(last_addr, current_addr, strlen(current_addr)))) {
+	    if (peers->next) {
+		peers = peers->next;
+		current_addr = inet_ntoa(peers->in_addr.sin_addr);
+	    } else {
+		peers = NULL;
+	    }
+	}
+	xfree(last_addr);
+	if (peers) {
+	    if (peers->next) {
+		peers = peers->next;
+		instance = (oid *)xmalloc(sizeof(name) * (*len));
+		xmemcpy(instance, name, (sizeof(name) * *len));
+		cp = (u_char *) & (peers->in_addr.sin_addr.s_addr);
+		instance[*len - 4] = *cp++;
+		instance[*len - 3] = *cp++;
+		instance[*len - 2] = *cp++;
+		instance[*len - 1] = *cp++;
+	    } else {
+		return (instance);
+	    }
+	} else {
+	    return (instance);
+	}
+    }
+    *Fn = current->parsefunction;
+    return (instance);
+}
+
+static oid *
+client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
+{
+    oid *instance = NULL;
+    u_char *cp = NULL;
+    struct in_addr *laddr = NULL;
+
+    if (*len <= current->len) {
+	instance = (oid *)xmalloc(sizeof(name) * (*len + 4));
+	xmemcpy(instance, name, (sizeof(name) * *len));
+	laddr = client_entry(NULL);
+	if (laddr) {
+	    cp = (u_char *) & (laddr->s_addr);
+	    instance[*len] = *cp++;
+	    instance[*len + 1] = *cp++;
+	    instance[*len + 2] = *cp++;
+	    instance[*len + 3] = *cp++;
+	    *len += 4;
+	}
+    } else {
+	laddr = oid2addr(&name[*len - 4]);
+	laddr = client_entry(laddr);
+	if (laddr) {
+	    instance = (oid *)xmalloc(sizeof(name) * (*len));
+	    xmemcpy(instance, name, (sizeof(name) * *len));
+	    cp = (u_char *) & (laddr->s_addr);
+	    instance[*len - 4] = *cp++;
+	    instance[*len - 3] = *cp++;
+	    instance[*len - 2] = *cp++;
+	    instance[*len - 1] = *cp++;
+	}
+    }
+    *Fn = current->parsefunction;
+    return (instance);
+}
+
+
+/*
+ * Utility functions
+ */
+
+/*
+ * Tree utility functions. 
+ */
+
+/* 
+ * Returns a the sibling object in the tree
+ */
+static mib_tree_entry *
+snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current)
+{
+    mib_tree_entry *next = NULL;
+    int count = 0;
+
+    while ((!next) && (count < current->children)) {
+	if (current->leaves[count]->name[len] == entry) {
+	    next = current->leaves[count];
+	}
+	count++;
+    }
+    if (count < current->children) {
+	next = current->leaves[count];
+    } else {
+	next = NULL;
+    }
+    return (next);
+}
+
+/* 
+ * Returns the requested child object or NULL if it does not exist
+ */
+static mib_tree_entry *
+snmpTreeEntry(oid entry, snint len, mib_tree_entry * current)
+{
+    mib_tree_entry *next = NULL;
+    int count = 0;
+
+    while ((!next) && (count < current->children)) {
+	if (current->leaves[count]->name[len] == entry) {
+	    next = current->leaves[count];
+	}
+	count++;
+    }
+    return (next);
+}
+
+/*
+ * Adds a node to the MIB tree structure and adds the appropriate children
+ */
+static mib_tree_entry *
+#if STDC_HEADERS
+snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, int children,...)
+#else
+snmpAddNode(va_alist)
+     va_dcl
+#endif
+{
+#if STDC_HEADERS
+    va_list args;
+    int loop;
+    mib_tree_entry *entry = NULL;
+    va_start(args, children);
+#else
+    va_list args;
+    oid *name = NULL;
+    int len = 0, children = 0, loop;
+    oid_ParseFn *parsefunction = NULL;
+    instance_Fn *instancefunction = NULL;
+    mib_tree_entry *entry = NULL;
+    va_start(args);
+    name = va_arg(args, oid *);
+    len = va_arg(args, int);
+    parsefunction = va_arg(args, oid_ParseFn *);
+    instancefunction = va_arg(args, instance_Fn *);
+    children = va_arg(args, int);
+#endif
+
+    debug(49, 6) ("snmpAddNode: Children : %d, Oid : \n", children);
+    snmpDebugOid(6, name, len);
+
+    va_start(args, children);
+    entry = (mib_tree_entry *)xmalloc(sizeof(mib_tree_entry));
+    entry->name = name;
+    entry->len = len;
+    entry->parsefunction = parsefunction;
+    entry->instancefunction = instancefunction;
+    entry->children = children;
+
+    if (children > 0) {
+	entry->leaves = (mib_tree_entry **)xmalloc(sizeof(mib_tree_entry *) * children);
+	for (loop = 0; loop < children; loop++) {
+	    entry->leaves[loop] = va_arg(args, mib_tree_entry *);
+	    entry->leaves[loop]->parent = entry;
+	}
+    }
+    return (entry);
+}
+/* End of tree utility functions */
+
+/* 
+ * Returns the list of parameters in an oid
+ */
+static oid *
+#if STDC_HEADERS
+snmpCreateOid(int length,...)
+#else
+snmpCreateOid(va_alist)
+     va_dcl
+#endif
+{
+#if STDC_HEADERS
+    va_list args;
+    oid *new_oid;
+    int loop;
+    va_start(args, length);
+#else
+    va_list args;
+    int length = 0, loop;
+    oid *new_oid;
+    va_start(args);
+    length va_arg(args, int);
+#endif
+
+    new_oid = (oid *)xmalloc(sizeof(oid) * length);
+
+    if (length > 0) {
+	for (loop = 0; loop < length; loop++) {
+	    new_oid[loop] = va_arg(args, int);
+	}
+    }
+    return (new_oid);
+}
+
+#if UNUSED_CODE
+/*
+ * Allocate space for, and copy, an OID.  Returns new oid.
+ */
+static oid *
+snmpOidDup(oid * A, snint ALen)
+{
+    oid *Ans = xmalloc(sizeof(oid) * ALen);
+    xmemcpy(Ans, A, (sizeof(oid) * ALen));
+    return Ans;
+}
+#endif
+
+/*
+ * Debug calls, prints out the OID for debugging purposes.
+ */
+void
+snmpDebugOid(int lvl, oid * Name, snint Len)
+{
+    char mbuf[16], objid[1024];
+    int x;
+    objid[0] = '\0';
+
+    for (x = 0; x < Len; x++) {
+	snprintf(mbuf, sizeof(mbuf), ".%u", (unsigned int) Name[x]);
+	strncat(objid, mbuf, sizeof(objid));
+    }
+
+    debug(49, lvl) ("   oid = %s\n", objid);
+}
+
+static void
+snmpSnmplibDebug(int lvl, char *buf)
+{
+    debug(49, lvl) ("%s", buf);
+}
+
+void
+addr2oid(struct in_addr addr, oid * Dest)
+{
+    u_char *cp;
+    cp = (u_char *) & (addr.s_addr);
+    Dest[0] = *cp++;
+    Dest[1] = *cp++;
+    Dest[2] = *cp++;
+    Dest[3] = *cp++;
+}
+
+struct in_addr
+       *
+oid2addr(oid * id)
+{
+    static struct in_addr laddr;
+    u_char *cp = (u_char *) & (laddr.s_addr);
+    cp[0] = id[0];
+    cp[1] = id[1];
+    cp[2] = id[2];
+    cp[3] = id[3];
+    return &laddr;
+}
--- squid/src/ssl_support.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,356 +0,0 @@
-
-/*
- * $Id$
- *
- * AUTHOR: Benno Rice
- * DEBUG: section 83    SSL accelerator support
- *
- * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from the
- *  Internet community.  Development is led by Duane Wessels of the
- *  National Laboratory for Applied Network Research and funded by the
- *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
- *  Duane Wessels and the University of California San Diego.  Please
- *  see the COPYRIGHT file for full details.  Squid incorporates
- *  software developed and/or copyrighted by other sources.  Please 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"
-
-extern int commUnsetNonBlocking(int fd);
-extern int commSetNonBlocking(int fd);
-
-void clientNegotiateSSL(int fd, void *data);
-void clientReadSSLRequest(int fd, void *data);
-
-static RSA *
-ssl_temp_rsa_cb(SSL * ssl, int export, int keylen)
-{
-    static RSA *rsa = NULL;
-
-    if (rsa == NULL)
-	rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
-    return rsa;
-}
-
-static int
-ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
-{
-    char buffer[256];
-
-    X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buffer,
-	sizeof(buffer));
-    if (ok)
-	debug(83, 5) ("SSL Certificate OK: %s\n", buffer);
-    else {
-	switch (ctx->error) {
-	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-	    debug(83, 5) ("SSL Certficate error: CA not known: %s\n", buffer);
-	    break;
-	case X509_V_ERR_CERT_NOT_YET_VALID:
-	    debug(83, 5) ("SSL Certficate not yet valid: %s\n", buffer);
-	    break;
-	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
-	    debug(83, 5) ("SSL Certificate has illegal \'not before\' field: %s\n", buffer);
-	    break;
-	case X509_V_ERR_CERT_HAS_EXPIRED:
-	    debug(83, 5) ("SSL Certificate expired: %s\n", buffer);
-	    break;
-	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
-	    debug(83, 5) ("SSL Certificate has invalid \'not after\' field: %s\n", buffer);
-	    break;
-	default:
-	    debug(83, 5) ("SSL unknown certificate error %d in %s\n",
-		ctx->error, buffer);
-	    break;
-	}
-    }
-    return ok;
-}
-
-static struct ssl_option {
-    const char *name;
-    long value;
-} ssl_options[] = {
-
-    {
-	"MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG
-    },
-    {
-	"NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG
-    },
-    {
-	"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
-    },
-    {
-	"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
-    },
-    {
-	"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
-    },
-    {
-	"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING
-    },
-    {
-	"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
-    },
-    {
-	"TLS_D5_BUG", SSL_OP_TLS_D5_BUG
-    },
-    {
-	"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
-    },
-    {
-	"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
-    },
-    {
-	"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
-    },
-    {
-	"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
-    },
-    {
-	"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
-    },
-    {
-	"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
-    },
-    {
-	"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
-    },
-    {
-	"NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
-    },
-    {
-	"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
-    },
-    {
-	"ALL", SSL_OP_ALL
-    },
-    {
-	"NO_SSLv2", SSL_OP_NO_SSLv2
-    },
-    {
-	"NO_SSLv3", SSL_OP_NO_SSLv3
-    },
-    {
-	"NO_TLSv1", SSL_OP_NO_TLSv1
-    },
-    {
-	"", 0
-    },
-    {
-	NULL, 0
-    }
-};
-
-static long
-ssl_parse_options(const char *options)
-{
-    long op = SSL_OP_ALL;
-    char *tmp;
-    char *option;
-
-    if (!options)
-	goto no_options;
-
-    tmp = xstrdup(options);
-    option = strtok(tmp, ":,");
-    while (option) {
-	struct ssl_option *opt = NULL, *opttmp;
-	long value = 0;
-	enum {
-	    MODE_ADD, MODE_REMOVE
-	} mode;
-	switch (*option) {
-	case '!':
-	case '-':
-	    mode = MODE_REMOVE;
-	    option++;
-	    break;
-	case '+':
-	    mode = MODE_ADD;
-	    option++;
-	    break;
-	default:
-	    mode = MODE_ADD;
-	    break;
-	}
-	for (opttmp = ssl_options; opttmp->name; opttmp++) {
-	    if (strcmp(opttmp->name, option) == 0) {
-		opt = opttmp;
-		break;
-	    }
-	}
-	if (opt)
-	    value = opt->value;
-	else if (strncmp(option, "0x", 2) == 0) {
-	    /* Special case.. hex specification */
-	    value = strtol(option + 2, NULL, 16);
-	} else {
-	    fatalf("Unknown SSL option '%s'", option);
-	    value = 0;		/* Keep GCC happy */
-	}
-	switch (mode) {
-	case MODE_ADD:
-	    op |= value;
-	    break;
-	case MODE_REMOVE:
-	    op &= ~value;
-	    break;
-	}
-	option = strtok(NULL, ":,");
-    }
-
-    safe_free(tmp);
-  no_options:
-    return op;
-}
-
-SSL_CTX *
-sslCreateContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options)
-{
-    int ssl_error;
-    SSL_METHOD *method;
-    SSL_CTX *sslContext;
-    static int ssl_initialized = 0;
-    if (!ssl_initialized) {
-	ssl_initialized = 1;
-	SSL_load_error_strings();
-	SSLeay_add_ssl_algorithms();
-    }
-    if (!keyfile)
-	keyfile = certfile;
-    if (!certfile)
-	certfile = keyfile;
-
-    debug(83, 1) ("Initialising SSL.\n");
-    switch (version) {
-    case 2:
-	debug(83, 5) ("Using SSLv2.\n");
-	method = SSLv2_server_method();
-	break;
-    case 3:
-	debug(83, 5) ("Using SSLv3.\n");
-	method = SSLv3_server_method();
-	break;
-    case 4:
-	debug(83, 5) ("Using TLSv1.\n");
-	method = TLSv1_server_method();
-	break;
-    case 1:
-    default:
-	debug(83, 5) ("Using SSLv2/SSLv3.\n");
-	method = SSLv23_server_method();
-	break;
-    }
-
-    sslContext = SSL_CTX_new(method);
-    if (sslContext == NULL) {
-	ssl_error = ERR_get_error();
-	fatalf("Failed to allocate SSL context: %s\n",
-	    ERR_error_string(ssl_error, NULL));
-    }
-    SSL_CTX_set_options(sslContext, ssl_parse_options(options));
-
-    if (cipher) {
-	debug(83, 5) ("Using chiper suite %s.\n", cipher);
-	if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
-	    ssl_error = ERR_get_error();
-	    fatalf("Failed to set SSL cipher suite: %s\n",
-		ERR_error_string(ssl_error, NULL));
-	}
-    }
-    debug(83, 1) ("Using certificate in %s\n", certfile);
-    if (!SSL_CTX_use_certificate_file(sslContext, certfile, SSL_FILETYPE_PEM)) {
-	ssl_error = ERR_get_error();
-	fatalf("Failed to acquire SSL certificate: %s\n",
-	    ERR_error_string(ssl_error, NULL));
-    }
-    debug(83, 1) ("Using private key in %s\n", keyfile);
-    if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) {
-	ssl_error = ERR_get_error();
-	fatalf("Failed to acquire SSL private key: %s\n",
-	    ERR_error_string(ssl_error, NULL));
-    }
-    debug(83, 5) ("Comparing private and public SSL keys.\n");
-    if (!SSL_CTX_check_private_key(sslContext))
-	fatal("SSL private key does not match public key: %s\n");
-
-    debug(83, 9) ("Setting RSA key generation callback.\n");
-    SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
-
-    debug(83, 9) ("Setting certificate verification callback.\n");
-    SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, ssl_verify_cb);
-
-    debug(83, 9) ("Setting default CA certificate location.\n");
-    if (!SSL_CTX_set_default_verify_paths(sslContext)) {
-	ssl_error = ERR_get_error();
-	debug(83, 1) ("Error error setting default CA certificate location: %s\n",
-	    ERR_error_string(ssl_error, NULL));
-	debug(83, 1) ("continuing anyway...\n");
-    }
-    debug(83, 9) ("Set client certifying authority list.\n");
-    SSL_CTX_set_client_CA_list(sslContext, SSL_load_client_CA_file(certfile));
-    return sslContext;
-}
-
-int
-ssl_read_method(fd, buf, len)
-     int fd;
-     char *buf;
-     int len;
-{
-    int i;
-
-    i = SSL_read(fd_table[fd].ssl, buf, len);
-
-    if (i > 0 && SSL_pending(fd_table[fd].ssl) > 0) {
-	debug(83, 2) ("SSL fd %d is pending\n", fd);
-	fd_table[fd].flags.read_pending = 1;
-    } else
-	fd_table[fd].flags.read_pending = 0;
-
-    return i;
-}
-
-int
-ssl_write_method(fd, buf, len)
-     int fd;
-     const char *buf;
-     int len;
-{
-    return (SSL_write(fd_table[fd].ssl, buf, len));
-}
-
-void
-ssl_shutdown_method(fd)
-{
-    SSL *ssl = fd_table[fd].ssl;
-    if (!fd_table[fd].ssl_shutdown) {
-	fd_table[fd].ssl_shutdown = 1;
-	if (Config.SSL.unclean_shutdown)
-	    SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
-	else
-	    SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
-    }
-    SSL_shutdown(ssl);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/ssl_support.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,350 @@
+
+/*
+ * $Id: ssl_support.cc,v 1.1.2.1 2002/10/11 15:41:02 rbcollins Exp $
+ *
+ * AUTHOR: Benno Rice
+ * DEBUG: section 83    SSL accelerator support
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please 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"
+
+extern int commUnsetNonBlocking(int fd);
+extern int commSetNonBlocking(int fd);
+
+void clientNegotiateSSL(int fd, void *data);
+void clientReadSSLRequest(int fd, void *data);
+
+static RSA *
+ssl_temp_rsa_cb(SSL * ssl, int anInt, int keylen)
+{
+    static RSA *rsa = NULL;
+
+    if (rsa == NULL)
+	rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
+    return rsa;
+}
+
+static int
+ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
+{
+    char buffer[256];
+
+    X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buffer,
+	sizeof(buffer));
+    if (ok)
+	debug(83, 5) ("SSL Certificate OK: %s\n", buffer);
+    else {
+	switch (ctx->error) {
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+	    debug(83, 5) ("SSL Certficate error: CA not known: %s\n", buffer);
+	    break;
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+	    debug(83, 5) ("SSL Certficate not yet valid: %s\n", buffer);
+	    break;
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+	    debug(83, 5) ("SSL Certificate has illegal \'not before\' field: %s\n", buffer);
+	    break;
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+	    debug(83, 5) ("SSL Certificate expired: %s\n", buffer);
+	    break;
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+	    debug(83, 5) ("SSL Certificate has invalid \'not after\' field: %s\n", buffer);
+	    break;
+	default:
+	    debug(83, 5) ("SSL unknown certificate error %d in %s\n",
+		ctx->error, buffer);
+	    break;
+	}
+    }
+    return ok;
+}
+
+static struct ssl_option {
+    const char *name;
+    long value;
+} ssl_options[] = {
+
+    {
+	"MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG
+    },
+    {
+	"NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG
+    },
+    {
+	"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+    },
+    {
+	"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+    },
+    {
+	"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
+    },
+    {
+	"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING
+    },
+    {
+	"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+    },
+    {
+	"TLS_D5_BUG", SSL_OP_TLS_D5_BUG
+    },
+    {
+	"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
+    },
+    {
+	"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
+    },
+    {
+	"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
+    },
+    {
+	"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
+    },
+    {
+	"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
+    },
+    {
+	"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
+    },
+    {
+	"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
+    },
+    {
+	"NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
+    },
+    {
+	"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
+    },
+    {
+	"ALL", SSL_OP_ALL
+    },
+    {
+	"NO_SSLv2", SSL_OP_NO_SSLv2
+    },
+    {
+	"NO_SSLv3", SSL_OP_NO_SSLv3
+    },
+    {
+	"NO_TLSv1", SSL_OP_NO_TLSv1
+    },
+    {
+	"", 0
+    },
+    {
+	NULL, 0
+    }
+};
+
+static long
+ssl_parse_options(const char *options)
+{
+    long op = SSL_OP_ALL;
+    char *tmp;
+    char *option;
+
+    if (!options)
+	goto no_options;
+
+    tmp = xstrdup(options);
+    option = strtok(tmp, ":,");
+    while (option) {
+	struct ssl_option *opt = NULL, *opttmp;
+	long value = 0;
+	enum {
+	    MODE_ADD, MODE_REMOVE
+	} mode;
+	switch (*option) {
+	case '!':
+	case '-':
+	    mode = MODE_REMOVE;
+	    option++;
+	    break;
+	case '+':
+	    mode = MODE_ADD;
+	    option++;
+	    break;
+	default:
+	    mode = MODE_ADD;
+	    break;
+	}
+	for (opttmp = ssl_options; opttmp->name; opttmp++) {
+	    if (strcmp(opttmp->name, option) == 0) {
+		opt = opttmp;
+		break;
+	    }
+	}
+	if (opt)
+	    value = opt->value;
+	else if (strncmp(option, "0x", 2) == 0) {
+	    /* Special case.. hex specification */
+	    value = strtol(option + 2, NULL, 16);
+	} else {
+	    fatalf("Unknown SSL option '%s'", option);
+	    value = 0;		/* Keep GCC happy */
+	}
+	switch (mode) {
+	case MODE_ADD:
+	    op |= value;
+	    break;
+	case MODE_REMOVE:
+	    op &= ~value;
+	    break;
+	}
+	option = strtok(NULL, ":,");
+    }
+
+    safe_free(tmp);
+  no_options:
+    return op;
+}
+
+SSL_CTX *
+sslCreateContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options)
+{
+    int ssl_error;
+    SSL_METHOD *method;
+    SSL_CTX *sslContext;
+    static int ssl_initialized = 0;
+    if (!ssl_initialized) {
+	ssl_initialized = 1;
+	SSL_load_error_strings();
+	SSLeay_add_ssl_algorithms();
+    }
+    if (!keyfile)
+	keyfile = certfile;
+    if (!certfile)
+	certfile = keyfile;
+
+    debug(83, 1) ("Initialising SSL.\n");
+    switch (version) {
+    case 2:
+	debug(83, 5) ("Using SSLv2.\n");
+	method = SSLv2_server_method();
+	break;
+    case 3:
+	debug(83, 5) ("Using SSLv3.\n");
+	method = SSLv3_server_method();
+	break;
+    case 4:
+	debug(83, 5) ("Using TLSv1.\n");
+	method = TLSv1_server_method();
+	break;
+    case 1:
+    default:
+	debug(83, 5) ("Using SSLv2/SSLv3.\n");
+	method = SSLv23_server_method();
+	break;
+    }
+
+    sslContext = SSL_CTX_new(method);
+    if (sslContext == NULL) {
+	ssl_error = ERR_get_error();
+	fatalf("Failed to allocate SSL context: %s\n",
+	    ERR_error_string(ssl_error, NULL));
+    }
+    SSL_CTX_set_options(sslContext, ssl_parse_options(options));
+
+    if (cipher) {
+	debug(83, 5) ("Using chiper suite %s.\n", cipher);
+	if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
+	    ssl_error = ERR_get_error();
+	    fatalf("Failed to set SSL cipher suite: %s\n",
+		ERR_error_string(ssl_error, NULL));
+	}
+    }
+    debug(83, 1) ("Using certificate in %s\n", certfile);
+    if (!SSL_CTX_use_certificate_file(sslContext, certfile, SSL_FILETYPE_PEM)) {
+	ssl_error = ERR_get_error();
+	fatalf("Failed to acquire SSL certificate: %s\n",
+	    ERR_error_string(ssl_error, NULL));
+    }
+    debug(83, 1) ("Using private key in %s\n", keyfile);
+    if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) {
+	ssl_error = ERR_get_error();
+	fatalf("Failed to acquire SSL private key: %s\n",
+	    ERR_error_string(ssl_error, NULL));
+    }
+    debug(83, 5) ("Comparing private and public SSL keys.\n");
+    if (!SSL_CTX_check_private_key(sslContext))
+	fatal("SSL private key does not match public key: %s\n");
+
+    debug(83, 9) ("Setting RSA key generation callback.\n");
+    SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
+
+    debug(83, 9) ("Setting certificate verification callback.\n");
+    SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, ssl_verify_cb);
+
+    debug(83, 9) ("Setting default CA certificate location.\n");
+    if (!SSL_CTX_set_default_verify_paths(sslContext)) {
+	ssl_error = ERR_get_error();
+	debug(83, 1) ("Error error setting default CA certificate location: %s\n",
+	    ERR_error_string(ssl_error, NULL));
+	debug(83, 1) ("continuing anyway...\n");
+    }
+    debug(83, 9) ("Set client certifying authority list.\n");
+    SSL_CTX_set_client_CA_list(sslContext, SSL_load_client_CA_file(certfile));
+    return sslContext;
+}
+
+int
+ssl_read_method(int fd, char *buf, int len)
+{
+    int i;
+
+    i = SSL_read(fd_table[fd].ssl, buf, len);
+
+    if (i > 0 && SSL_pending(fd_table[fd].ssl) > 0) {
+	debug(83, 2) ("SSL fd %d is pending\n", fd);
+	fd_table[fd].flags.read_pending = 1;
+    } else
+	fd_table[fd].flags.read_pending = 0;
+
+    return i;
+}
+
+int
+ssl_write_method(int fd, const char *buf, int len)
+{
+    return (SSL_write(fd_table[fd].ssl, buf, len));
+}
+
+void
+ssl_shutdown_method(int fd)
+{
+    SSL *ssl = fd_table[fd].ssl;
+    if (!fd_table[fd].ssl_shutdown) {
+	fd_table[fd].ssl_shutdown = 1;
+	if (Config.SSL.unclean_shutdown)
+	    SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+	else
+	    SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+    }
+    SSL_shutdown(ssl);
+}
--- squid/src/store_key_md5.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,188 +0,0 @@
-
-/*
- * $Id: store_key_md5.c,v 1.6 2001/04/14 00:31:02 squidadm Exp $
- *
- * DEBUG: section 20    Storage Manager MD5 Cache Keys
- * AUTHOR: Duane Wessels
- *
- * 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 cache_key null_key[MD5_DIGEST_CHARS];
-
-const char *
-storeKeyText(const unsigned char *key)
-{
-    static MemBuf mb = MemBufNULL;
-    int i;
-    memBufReset(&mb);
-    for (i = 0; i < MD5_DIGEST_CHARS; i++)
-	memBufPrintf(&mb, "%02X", *(key + i));
-    return mb.buf;
-}
-
-const cache_key *
-storeKeyScan(const char *buf)
-{
-    static unsigned char digest[MD5_DIGEST_CHARS];
-    int i;
-    int j = 0;
-    char t[3];
-    for (i = 0; i < MD5_DIGEST_CHARS; i++) {
-	t[0] = *(buf + (j++));
-	t[1] = *(buf + (j++));
-	t[2] = '\0';
-	*(digest + i) = (unsigned char) strtol(t, NULL, 16);
-    }
-    return digest;
-}
-
-int
-storeKeyHashCmp(const void *a, const void *b)
-{
-    const unsigned char *A = a;
-    const unsigned char *B = b;
-    int i;
-    for (i = 0; i < MD5_DIGEST_CHARS; i++) {
-	if (A[i] < B[i])
-	    return -1;
-	if (A[i] > B[i])
-	    return 1;
-    }
-    return 0;
-}
-
-unsigned int
-storeKeyHashHash(const void *key, unsigned int n)
-{
-    /* note, n must be a power of 2! */
-    const unsigned char *digest = key;
-    unsigned int i = digest[0]
-    | digest[1] << 8
-    | digest[2] << 16
-    | digest[3] << 24;
-    return (i & (--n));
-}
-
-const cache_key *
-storeKeyPrivate(const char *url, method_t method, int id)
-{
-    static cache_key digest[MD5_DIGEST_CHARS];
-    MD5_CTX M;
-    assert(id > 0);
-    debug(20, 3) ("storeKeyPrivate: %s %s\n",
-	RequestMethodStr[method], url);
-    MD5Init(&M);
-    MD5Update(&M, (unsigned char *) &id, sizeof(id));
-    MD5Update(&M, (unsigned char *) &method, sizeof(method));
-    MD5Update(&M, (unsigned char *) url, strlen(url));
-    MD5Final(digest, &M);
-    return digest;
-}
-
-const cache_key *
-storeKeyPublic(const char *url, const method_t method)
-{
-    static cache_key digest[MD5_DIGEST_CHARS];
-    unsigned char m = (unsigned char) method;
-    MD5_CTX M;
-    MD5Init(&M);
-    MD5Update(&M, &m, sizeof(m));
-    MD5Update(&M, (unsigned char *) url, strlen(url));
-    MD5Final(digest, &M);
-    return digest;
-}
-
-const cache_key *
-storeKeyPublicByRequest(request_t * request)
-{
-    return storeKeyPublicByRequestMethod(request, request->method);
-}
-
-const cache_key *
-storeKeyPublicByRequestMethod(request_t * request, const method_t method)
-{
-    static cache_key digest[MD5_DIGEST_CHARS];
-    unsigned char m = (unsigned char) method;
-    const char *url = urlCanonical(request);
-    MD5_CTX M;
-    MD5Init(&M);
-    MD5Update(&M, &m, sizeof(m));
-    MD5Update(&M, (unsigned char *) url, strlen(url));
-    if (request->vary_headers)
-	MD5Update(&M, (unsigned char *) request->vary_headers, strlen(request->vary_headers));
-    MD5Final(digest, &M);
-    return digest;
-}
-
-cache_key *
-storeKeyDup(const cache_key * key)
-{
-    cache_key *dup = memAllocate(MEM_MD5_DIGEST);
-    xmemcpy(dup, key, MD5_DIGEST_CHARS);
-    return dup;
-}
-
-cache_key *
-storeKeyCopy(cache_key * dst, const cache_key * src)
-{
-    xmemcpy(dst, src, MD5_DIGEST_CHARS);
-    return dst;
-}
-
-void
-storeKeyFree(const cache_key * key)
-{
-    memFree((void *) key, MEM_MD5_DIGEST);
-}
-
-int
-storeKeyHashBuckets(int nbuckets)
-{
-    int n = 0x2000;
-    while (n < nbuckets)
-	n <<= 1;
-    return n;
-}
-
-int
-storeKeyNull(const cache_key * key)
-{
-    if (memcmp(key, null_key, MD5_DIGEST_CHARS) == 0)
-	return 1;
-    else
-	return 0;
-}
-
-void
-storeKeyInit(void)
-{
-    memset(null_key, '\0', MD5_DIGEST_CHARS);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/store_key_md5.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,188 @@
+
+/*
+ * $Id: store_key_md5.cc,v 1.1.2.1 2002/10/11 15:41:02 rbcollins Exp $
+ *
+ * DEBUG: section 20    Storage Manager MD5 Cache Keys
+ * AUTHOR: Duane Wessels
+ *
+ * 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 cache_key null_key[MD5_DIGEST_CHARS];
+
+const char *
+storeKeyText(const unsigned char *key)
+{
+    static MemBuf mb = MemBufNULL;
+    int i;
+    memBufReset(&mb);
+    for (i = 0; i < MD5_DIGEST_CHARS; i++)
+	memBufPrintf(&mb, "%02X", *(key + i));
+    return mb.buf;
+}
+
+const cache_key *
+storeKeyScan(const char *buf)
+{
+    static unsigned char digest[MD5_DIGEST_CHARS];
+    int i;
+    int j = 0;
+    char t[3];
+    for (i = 0; i < MD5_DIGEST_CHARS; i++) {
+	t[0] = *(buf + (j++));
+	t[1] = *(buf + (j++));
+	t[2] = '\0';
+	*(digest + i) = (unsigned char) strtol(t, NULL, 16);
+    }
+    return digest;
+}
+
+int
+storeKeyHashCmp(const void *a, const void *b)
+{
+    const unsigned char *A = (const unsigned char *)a;
+    const unsigned char *B = (const unsigned char *)b;
+    int i;
+    for (i = 0; i < MD5_DIGEST_CHARS; i++) {
+	if (A[i] < B[i])
+	    return -1;
+	if (A[i] > B[i])
+	    return 1;
+    }
+    return 0;
+}
+
+unsigned int
+storeKeyHashHash(const void *key, unsigned int n)
+{
+    /* note, n must be a power of 2! */
+    const unsigned char *digest = (const unsigned char *)key;
+    unsigned int i = digest[0]
+    | digest[1] << 8
+    | digest[2] << 16
+    | digest[3] << 24;
+    return (i & (--n));
+}
+
+const cache_key *
+storeKeyPrivate(const char *url, method_t method, int id)
+{
+    static cache_key digest[MD5_DIGEST_CHARS];
+    MD5_CTX M;
+    assert(id > 0);
+    debug(20, 3) ("storeKeyPrivate: %s %s\n",
+	RequestMethodStr[method], url);
+    MD5Init(&M);
+    MD5Update(&M, (unsigned char *) &id, sizeof(id));
+    MD5Update(&M, (unsigned char *) &method, sizeof(method));
+    MD5Update(&M, (unsigned char *) url, strlen(url));
+    MD5Final(digest, &M);
+    return digest;
+}
+
+const cache_key *
+storeKeyPublic(const char *url, const method_t method)
+{
+    static cache_key digest[MD5_DIGEST_CHARS];
+    unsigned char m = (unsigned char) method;
+    MD5_CTX M;
+    MD5Init(&M);
+    MD5Update(&M, &m, sizeof(m));
+    MD5Update(&M, (unsigned char *) url, strlen(url));
+    MD5Final(digest, &M);
+    return digest;
+}
+
+const cache_key *
+storeKeyPublicByRequest(request_t * request)
+{
+    return storeKeyPublicByRequestMethod(request, request->method);
+}
+
+const cache_key *
+storeKeyPublicByRequestMethod(request_t * request, const method_t method)
+{
+    static cache_key digest[MD5_DIGEST_CHARS];
+    unsigned char m = (unsigned char) method;
+    const char *url = urlCanonical(request);
+    MD5_CTX M;
+    MD5Init(&M);
+    MD5Update(&M, &m, sizeof(m));
+    MD5Update(&M, (unsigned char *) url, strlen(url));
+    if (request->vary_headers)
+	MD5Update(&M, (unsigned char *) request->vary_headers, strlen(request->vary_headers));
+    MD5Final(digest, &M);
+    return digest;
+}
+
+cache_key *
+storeKeyDup(const cache_key * key)
+{
+    cache_key *dup = (cache_key *)memAllocate(MEM_MD5_DIGEST);
+    xmemcpy(dup, key, MD5_DIGEST_CHARS);
+    return dup;
+}
+
+cache_key *
+storeKeyCopy(cache_key * dst, const cache_key * src)
+{
+    xmemcpy(dst, src, MD5_DIGEST_CHARS);
+    return dst;
+}
+
+void
+storeKeyFree(const cache_key * key)
+{
+    memFree((void *) key, MEM_MD5_DIGEST);
+}
+
+int
+storeKeyHashBuckets(int nbuckets)
+{
+    int n = 0x2000;
+    while (n < nbuckets)
+	n <<= 1;
+    return n;
+}
+
+int
+storeKeyNull(const cache_key * key)
+{
+    if (memcmp(key, null_key, MD5_DIGEST_CHARS) == 0)
+	return 1;
+    else
+	return 0;
+}
+
+void
+storeKeyInit(void)
+{
+    memset(null_key, '\0', MD5_DIGEST_CHARS);
+}
--- squid/src/store_rebuild.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,197 +0,0 @@
-
-/*
- * $Id: store_rebuild.c,v 1.9.44.2 2002/10/04 21:57:52 rbcollins Exp $
- *
- * DEBUG: section 20    Store Rebuild Routines
- * AUTHOR: Duane Wessels
- *
- * 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"
-#include "Store.h"
-
-static struct _store_rebuild_data counts;
-static struct timeval rebuild_start;
-static void storeCleanup(void *);
-
-typedef struct {
-    /* total number of "swap.state" entries that will be read */
-    int total;
-    /* number of entries read so far */
-    int scanned;
-} store_rebuild_progress;
-
-static store_rebuild_progress *RebuildProgress = NULL;
-
-static int
-storeCleanupDoubleCheck(StoreEntry * e)
-{
-    SwapDir *SD = &Config.cacheSwap.swapDirs[e->swap_dirn];
-    return (SD->dblcheck(SD, e));
-}
-
-static void
-storeCleanup(void *datanotused)
-{
-    static int bucketnum = -1;
-    static int validnum = 0;
-    static int store_errors = 0;
-    int validnum_start;
-    StoreEntry *e;
-    hash_link *link_ptr = NULL;
-    hash_link *link_next = NULL;
-    validnum_start = validnum;
-    while (validnum - validnum_start < 500) {
-	if (++bucketnum >= store_hash_buckets) {
-	    debug(20, 1) ("  Completed Validation Procedure\n");
-	    debug(20, 1) ("  Validated %d Entries\n", validnum);
-	    debug(20, 1) ("  store_swap_size = %luk\n", store_swap_size);
-	    store_dirs_rebuilding--;
-	    assert(0 == store_dirs_rebuilding);
-	    if (opt_store_doublecheck)
-		assert(store_errors == 0);
-	    if (store_digest)
-		storeDigestNoteStoreReady();
-	    return;
-	}
-	link_next = hash_get_bucket(store_table, bucketnum);
-	while (NULL != (link_ptr = link_next)) {
-	    link_next = link_ptr->next;
-	    e = (StoreEntry *) link_ptr;
-	    if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
-		continue;
-	    /*
-	     * Calling storeRelease() has no effect because we're
-	     * still in 'store_rebuilding' state
-	     */
-	    if (e->swap_filen < 0)
-		continue;
-	    if (opt_store_doublecheck)
-		if (storeCleanupDoubleCheck(e))
-		    store_errors++;
-	    EBIT_SET(e->flags, ENTRY_VALIDATED);
-	    /*
-	     * Only set the file bit if we know its a valid entry
-	     * otherwise, set it in the validation procedure
-	     */
-	    storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1);
-	    if ((++validnum & 0x3FFFF) == 0)
-		debug(20, 1) ("  %7d Entries Validated so far.\n", validnum);
-	}
-    }
-    eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
-}
-
-/* meta data recreated from disk image in swap directory */
-void
-storeRebuildComplete(struct _store_rebuild_data *dc)
-{
-    double dt;
-    counts.objcount += dc->objcount;
-    counts.expcount += dc->expcount;
-    counts.scancount += dc->scancount;
-    counts.clashcount += dc->clashcount;
-    counts.dupcount += dc->dupcount;
-    counts.cancelcount += dc->cancelcount;
-    counts.invalid += dc->invalid;
-    counts.badflags += dc->badflags;
-    counts.bad_log_op += dc->bad_log_op;
-    counts.zero_object_sz += dc->zero_object_sz;
-    /*
-     * When store_dirs_rebuilding == 1, it means we are done reading
-     * or scanning all cache_dirs.  Now report the stats and start
-     * the validation (storeCleanup()) thread.
-     */
-    if (store_dirs_rebuilding > 1)
-	return;
-    dt = tvSubDsec(rebuild_start, current_time);
-    debug(20, 1) ("Finished rebuilding storage from disk.\n");
-    debug(20, 1) ("  %7d Entries scanned\n", counts.scancount);
-    debug(20, 1) ("  %7d Invalid entries.\n", counts.invalid);
-    debug(20, 1) ("  %7d With invalid flags.\n", counts.badflags);
-    debug(20, 1) ("  %7d Objects loaded.\n", counts.objcount);
-    debug(20, 1) ("  %7d Objects expired.\n", counts.expcount);
-    debug(20, 1) ("  %7d Objects cancelled.\n", counts.cancelcount);
-    debug(20, 1) ("  %7d Duplicate URLs purged.\n", counts.dupcount);
-    debug(20, 1) ("  %7d Swapfile clashes avoided.\n", counts.clashcount);
-    debug(20, 1) ("  Took %3.1f seconds (%6.1f objects/sec).\n", dt,
-	(double) counts.objcount / (dt > 0.0 ? dt : 1.0));
-    debug(20, 1) ("Beginning Validation Procedure\n");
-    eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
-    xfree(RebuildProgress);
-    RebuildProgress = NULL;
-}
-
-/*
- * this is ugly.  We don't actually start any rebuild threads here,
- * but only initialize counters, etc.  The rebuild threads are
- * actually started by the filesystem "fooDirInit" function.
- */
-void
-storeRebuildStart(void)
-{
-    memset(&counts, '\0', sizeof(counts));
-    rebuild_start = current_time;
-    /*
-     * Note: store_dirs_rebuilding is initialized to 1 in globals.c.
-     * This prevents us from trying to write clean logs until we
-     * finished rebuilding for sure.  The corresponding decrement
-     * occurs in storeCleanup(), when it is finished.
-     */
-    RebuildProgress = xcalloc(Config.cacheSwap.n_configured,
-	sizeof(store_rebuild_progress));
-}
-
-/*
- * A fs-specific rebuild procedure periodically reports its
- * progress.
- */
-void
-storeRebuildProgress(int sd_index, int total, int sofar)
-{
-    static time_t last_report = 0;
-    double n = 0.0;
-    double d = 0.0;
-    if (sd_index < 0)
-	return;
-    if (sd_index >= Config.cacheSwap.n_configured)
-	return;
-    if (NULL == RebuildProgress)
-	return;
-    RebuildProgress[sd_index].total = total;
-    RebuildProgress[sd_index].scanned = sofar;
-    if (squid_curtime - last_report < 15)
-	return;
-    for (sd_index = 0; sd_index < Config.cacheSwap.n_configured; sd_index++) {
-	n += (double) RebuildProgress[sd_index].scanned;
-	d += (double) RebuildProgress[sd_index].total;
-    }
-    debug(20, 1) ("Store rebuilding is %4.1f%% complete\n", 100.0 * n / d);
-    last_report = squid_curtime;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/store_rebuild.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,197 @@
+
+/*
+ * $Id: store_rebuild.cc,v 1.1.2.1 2002/10/11 15:41:02 rbcollins Exp $
+ *
+ * DEBUG: section 20    Store Rebuild Routines
+ * AUTHOR: Duane Wessels
+ *
+ * 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"
+#include "Store.h"
+
+static struct _store_rebuild_data counts;
+static struct timeval rebuild_start;
+static void storeCleanup(void *);
+
+typedef struct {
+    /* total number of "swap.state" entries that will be read */
+    int total;
+    /* number of entries read so far */
+    int scanned;
+} store_rebuild_progress;
+
+static store_rebuild_progress *RebuildProgress = NULL;
+
+static int
+storeCleanupDoubleCheck(StoreEntry * e)
+{
+    SwapDir *SD = &Config.cacheSwap.swapDirs[e->swap_dirn];
+    return (SD->dblcheck(SD, e));
+}
+
+static void
+storeCleanup(void *datanotused)
+{
+    static int bucketnum = -1;
+    static int validnum = 0;
+    static int store_errors = 0;
+    int validnum_start;
+    StoreEntry *e;
+    hash_link *link_ptr = NULL;
+    hash_link *link_next = NULL;
+    validnum_start = validnum;
+    while (validnum - validnum_start < 500) {
+	if (++bucketnum >= store_hash_buckets) {
+	    debug(20, 1) ("  Completed Validation Procedure\n");
+	    debug(20, 1) ("  Validated %d Entries\n", validnum);
+	    debug(20, 1) ("  store_swap_size = %luk\n", store_swap_size);
+	    store_dirs_rebuilding--;
+	    assert(0 == store_dirs_rebuilding);
+	    if (opt_store_doublecheck)
+		assert(store_errors == 0);
+	    if (store_digest)
+		storeDigestNoteStoreReady();
+	    return;
+	}
+	link_next = hash_get_bucket(store_table, bucketnum);
+	while (NULL != (link_ptr = link_next)) {
+	    link_next = link_ptr->next;
+	    e = (StoreEntry *) link_ptr;
+	    if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
+		continue;
+	    /*
+	     * Calling storeRelease() has no effect because we're
+	     * still in 'store_rebuilding' state
+	     */
+	    if (e->swap_filen < 0)
+		continue;
+	    if (opt_store_doublecheck)
+		if (storeCleanupDoubleCheck(e))
+		    store_errors++;
+	    EBIT_SET(e->flags, ENTRY_VALIDATED);
+	    /*
+	     * Only set the file bit if we know its a valid entry
+	     * otherwise, set it in the validation procedure
+	     */
+	    storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1);
+	    if ((++validnum & 0x3FFFF) == 0)
+		debug(20, 1) ("  %7d Entries Validated so far.\n", validnum);
+	}
+    }
+    eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
+}
+
+/* meta data recreated from disk image in swap directory */
+void
+storeRebuildComplete(struct _store_rebuild_data *dc)
+{
+    double dt;
+    counts.objcount += dc->objcount;
+    counts.expcount += dc->expcount;
+    counts.scancount += dc->scancount;
+    counts.clashcount += dc->clashcount;
+    counts.dupcount += dc->dupcount;
+    counts.cancelcount += dc->cancelcount;
+    counts.invalid += dc->invalid;
+    counts.badflags += dc->badflags;
+    counts.bad_log_op += dc->bad_log_op;
+    counts.zero_object_sz += dc->zero_object_sz;
+    /*
+     * When store_dirs_rebuilding == 1, it means we are done reading
+     * or scanning all cache_dirs.  Now report the stats and start
+     * the validation (storeCleanup()) thread.
+     */
+    if (store_dirs_rebuilding > 1)
+	return;
+    dt = tvSubDsec(rebuild_start, current_time);
+    debug(20, 1) ("Finished rebuilding storage from disk.\n");
+    debug(20, 1) ("  %7d Entries scanned\n", counts.scancount);
+    debug(20, 1) ("  %7d Invalid entries.\n", counts.invalid);
+    debug(20, 1) ("  %7d With invalid flags.\n", counts.badflags);
+    debug(20, 1) ("  %7d Objects loaded.\n", counts.objcount);
+    debug(20, 1) ("  %7d Objects expired.\n", counts.expcount);
+    debug(20, 1) ("  %7d Objects cancelled.\n", counts.cancelcount);
+    debug(20, 1) ("  %7d Duplicate URLs purged.\n", counts.dupcount);
+    debug(20, 1) ("  %7d Swapfile clashes avoided.\n", counts.clashcount);
+    debug(20, 1) ("  Took %3.1f seconds (%6.1f objects/sec).\n", dt,
+	(double) counts.objcount / (dt > 0.0 ? dt : 1.0));
+    debug(20, 1) ("Beginning Validation Procedure\n");
+    eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
+    xfree(RebuildProgress);
+    RebuildProgress = NULL;
+}
+
+/*
+ * this is ugly.  We don't actually start any rebuild threads here,
+ * but only initialize counters, etc.  The rebuild threads are
+ * actually started by the filesystem "fooDirInit" function.
+ */
+void
+storeRebuildStart(void)
+{
+    memset(&counts, '\0', sizeof(counts));
+    rebuild_start = current_time;
+    /*
+     * Note: store_dirs_rebuilding is initialized to 1 in globals.c.
+     * This prevents us from trying to write clean logs until we
+     * finished rebuilding for sure.  The corresponding decrement
+     * occurs in storeCleanup(), when it is finished.
+     */
+    RebuildProgress = (store_rebuild_progress *)xcalloc(Config.cacheSwap.n_configured,
+	sizeof(store_rebuild_progress));
+}
+
+/*
+ * A fs-specific rebuild procedure periodically reports its
+ * progress.
+ */
+void
+storeRebuildProgress(int sd_index, int total, int sofar)
+{
+    static time_t last_report = 0;
+    double n = 0.0;
+    double d = 0.0;
+    if (sd_index < 0)
+	return;
+    if (sd_index >= Config.cacheSwap.n_configured)
+	return;
+    if (NULL == RebuildProgress)
+	return;
+    RebuildProgress[sd_index].total = total;
+    RebuildProgress[sd_index].scanned = sofar;
+    if (squid_curtime - last_report < 15)
+	return;
+    for (sd_index = 0; sd_index < Config.cacheSwap.n_configured; sd_index++) {
+	n += (double) RebuildProgress[sd_index].scanned;
+	d += (double) RebuildProgress[sd_index].total;
+    }
+    debug(20, 1) ("Store rebuilding is %4.1f%% complete\n", 100.0 * n / d);
+    last_report = squid_curtime;
+}
--- squid/src/store_swapin.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,102 +0,0 @@
-
-/*
- * $Id: store_swapin.c,v 1.7.2.1 2002/10/04 07:07:29 rbcollins Exp $
- *
- * DEBUG: section 20    Storage Manager Swapin Functions
- * AUTHOR: Duane Wessels
- *
- * 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"
-#include "StoreClient.h"
-#include "Store.h"
-
-static STIOCB storeSwapInFileClosed;
-static STFNCB storeSwapInFileNotify;
-
-void
-storeSwapInStart(store_client * sc)
-{
-    StoreEntry *e = sc->entry;
-    storeIOState *sio;
-    assert(e->mem_status == NOT_IN_MEMORY);
-    if (!EBIT_TEST(e->flags, ENTRY_VALIDATED)) {
-	/* We're still reloading and haven't validated this entry yet */
-	return;
-    }
-    debug(20, 3) ("storeSwapInStart: called for %d %08X %s \n",
-	e->swap_dirn, e->swap_filen, storeKeyText(e->hash.key));
-    if (e->swap_status != SWAPOUT_WRITING && e->swap_status != SWAPOUT_DONE) {
-	debug(20, 1) ("storeSwapInStart: bad swap_status (%s)\n",
-	    swapStatusStr[e->swap_status]);
-	return;
-    }
-    if (e->swap_filen < 0) {
-	debug(20, 1) ("storeSwapInStart: swap_filen < 0\n");
-	return;
-    }
-    assert(e->mem_obj != NULL);
-    debug(20, 3) ("storeSwapInStart: Opening fileno %08X\n",
-	e->swap_filen);
-    sio = storeOpen(e, storeSwapInFileNotify, storeSwapInFileClosed, sc);
-    sc->swapin_sio = cbdataReference(sio);
-}
-
-static void
-storeSwapInFileClosed(void *data, int errflag, storeIOState * sio)
-{
-    store_client *sc = data;
-    StoreIOBuffer result =
-    {
-	{0}, 0, 0, sc->copyInto.data};
-    STCB *callback;
-    debug(20, 3) ("storeSwapInFileClosed: sio=%p, errflag=%d\n",
-	sio, errflag);
-    if (errflag)
-	result.flags.error = 1;
-    cbdataReferenceDone(sc->swapin_sio);
-    if ((callback = sc->callback)) {
-	assert(errflag <= 0);
-	sc->callback = NULL;
-	callback(sc->callback_data, result);
-    }
-    statCounter.swap.ins++;
-}
-
-static void
-storeSwapInFileNotify(void *data, int errflag, storeIOState * sio)
-{
-    store_client *sc = data;
-    StoreEntry *e = sc->entry;
-
-    debug(1, 3) ("storeSwapInFileNotify: changing %d/%d to %d/%d\n", e->swap_filen, e->swap_dirn, sio->swap_filen, sio->swap_dirn);
-
-    e->swap_filen = sio->swap_filen;
-    e->swap_dirn = sio->swap_dirn;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/store_swapin.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,102 @@
+
+/*
+ * $Id: store_swapin.cc,v 1.1.2.1 2002/10/11 15:41:03 rbcollins Exp $
+ *
+ * DEBUG: section 20    Storage Manager Swapin Functions
+ * AUTHOR: Duane Wessels
+ *
+ * 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"
+#include "StoreClient.h"
+#include "Store.h"
+
+static STIOCB storeSwapInFileClosed;
+static STFNCB storeSwapInFileNotify;
+
+void
+storeSwapInStart(store_client * sc)
+{
+    StoreEntry *e = sc->entry;
+    storeIOState *sio;
+    assert(e->mem_status == NOT_IN_MEMORY);
+    if (!EBIT_TEST(e->flags, ENTRY_VALIDATED)) {
+	/* We're still reloading and haven't validated this entry yet */
+	return;
+    }
+    debug(20, 3) ("storeSwapInStart: called for %d %08X %s \n",
+	e->swap_dirn, e->swap_filen, storeKeyText((const cache_key *)e->hash.key));
+    if (e->swap_status != SWAPOUT_WRITING && e->swap_status != SWAPOUT_DONE) {
+	debug(20, 1) ("storeSwapInStart: bad swap_status (%s)\n",
+	    swapStatusStr[e->swap_status]);
+	return;
+    }
+    if (e->swap_filen < 0) {
+	debug(20, 1) ("storeSwapInStart: swap_filen < 0\n");
+	return;
+    }
+    assert(e->mem_obj != NULL);
+    debug(20, 3) ("storeSwapInStart: Opening fileno %08X\n",
+	e->swap_filen);
+    sio = storeOpen(e, storeSwapInFileNotify, storeSwapInFileClosed, sc);
+    sc->swapin_sio = cbdataReference(sio);
+}
+
+static void
+storeSwapInFileClosed(void *data, int errflag, storeIOState * sio)
+{
+    store_client *sc = (store_client *)data;
+    StoreIOBuffer result =
+    {
+	{0}, 0, 0, sc->copyInto.data};
+    STCB *callback;
+    debug(20, 3) ("storeSwapInFileClosed: sio=%p, errflag=%d\n",
+	sio, errflag);
+    if (errflag)
+	result.flags.error = 1;
+    cbdataReferenceDone(sc->swapin_sio);
+    if ((callback = sc->callback)) {
+	assert(errflag <= 0);
+	sc->callback = NULL;
+	callback(sc->callback_data, result);
+    }
+    statCounter.swap.ins++;
+}
+
+static void
+storeSwapInFileNotify(void *data, int errflag, storeIOState * sio)
+{
+    store_client *sc = (store_client *)data;
+    StoreEntry *e = sc->entry;
+
+    debug(1, 3) ("storeSwapInFileNotify: changing %d/%d to %d/%d\n", e->swap_filen, e->swap_dirn, sio->swap_filen, sio->swap_dirn);
+
+    e->swap_filen = sio->swap_filen;
+    e->swap_dirn = sio->swap_dirn;
+}
--- squid/src/store_swapout.c	Wed Feb 14 01:07:40 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,386 +0,0 @@
-
-/*
- * $Id: store_swapout.c,v 1.14.2.1 2002/10/04 07:07:29 rbcollins Exp $
- *
- * DEBUG: section 20    Storage Manager Swapout Functions
- * AUTHOR: Duane Wessels
- *
- * 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"
-#include "StoreClient.h"
-#include "Store.h"
-
-static off_t storeSwapOutObjectBytesOnDisk(const MemObject *);
-static void storeSwapOutStart(StoreEntry * e);
-static STIOCB storeSwapOutFileClosed;
-static STIOCB storeSwapOutFileNotify;
-
-/* start swapping object to disk */
-static void
-storeSwapOutStart(StoreEntry * e)
-{
-    generic_cbdata *c;
-    MemObject *mem = e->mem_obj;
-    int swap_hdr_sz = 0;
-    tlv *tlv_list;
-    char *buf;
-    storeIOState *sio;
-    assert(mem);
-    /* Build the swap metadata, so the filesystem will know how much
-     * metadata there is to store
-     */
-    debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to dirno %d, fileno %08X\n",
-	storeUrl(e), e->swap_dirn, e->swap_filen);
-    e->swap_status = SWAPOUT_WRITING;
-    tlv_list = storeSwapMetaBuild(e);
-    buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
-    storeSwapTLVFree(tlv_list);
-    mem->swap_hdr_sz = (size_t) swap_hdr_sz;
-    /* Create the swap file */
-    c = cbdataAlloc(generic_cbdata);
-    c->data = e;
-    sio = storeCreate(e, storeSwapOutFileNotify, storeSwapOutFileClosed, c);
-    mem->swapout.sio = cbdataReference(sio);
-    if (NULL == mem->swapout.sio) {
-	e->swap_status = SWAPOUT_NONE;
-	cbdataFree(c);
-	xfree(buf);
-	storeLog(STORE_LOG_SWAPOUTFAIL, e);
-	return;
-    }
-    storeLockObject(e);		/* Don't lock until after create, or the replacement
-				 * code might get confused */
-    /* Pick up the file number if it was assigned immediately */
-    e->swap_filen = mem->swapout.sio->swap_filen;
-    e->swap_dirn = mem->swapout.sio->swap_dirn;
-    /* write out the swap metadata */
-    storeWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree);
-}
-
-static void
-storeSwapOutFileNotify(void *data, int errflag, storeIOState * sio)
-{
-    generic_cbdata *c = data;
-    StoreEntry *e = c->data;
-    MemObject *mem = e->mem_obj;
-    assert(e->swap_status == SWAPOUT_WRITING);
-    assert(mem);
-    assert(mem->swapout.sio == sio);
-    assert(errflag == 0);
-    e->swap_filen = mem->swapout.sio->swap_filen;
-    e->swap_dirn = mem->swapout.sio->swap_dirn;
-}
-
-void
-storeSwapOut(StoreEntry * e)
-{
-    MemObject *mem = e->mem_obj;
-    off_t lowest_offset;
-    off_t new_mem_lo;
-    off_t on_disk = 0;
-    ssize_t swapout_size;
-    ssize_t swap_buf_len;
-    if (mem == NULL)
-	return;
-    /* should we swap something out to disk? */
-    debug(20, 7) ("storeSwapOut: %s\n", storeUrl(e));
-    debug(20, 7) ("storeSwapOut: store_status = %s\n",
-	storeStatusStr[e->store_status]);
-    if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
-	assert(EBIT_TEST(e->flags, RELEASE_REQUEST));
-	storeSwapOutFileClose(e);
-	return;
-    }
-    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
-	debug(20, 3) ("storeSwapOut: %s SPECIAL\n", storeUrl(e));
-	return;
-    }
-    debug(20, 7) ("storeSwapOut: mem->inmem_lo = %d\n",
-	(int) mem->inmem_lo);
-    debug(20, 7) ("storeSwapOut: mem->inmem_hi = %d\n",
-	(int) mem->inmem_hi);
-    debug(20, 7) ("storeSwapOut: swapout.queue_offset = %d\n",
-	(int) mem->swapout.queue_offset);
-    if (mem->swapout.sio)
-	debug(20, 7) ("storeSwapOut: storeOffset() = %d\n",
-	    (int) storeOffset(mem->swapout.sio));
-    assert(mem->inmem_hi >= mem->swapout.queue_offset);
-    lowest_offset = storeLowestMemReaderOffset(e);
-    debug(20, 7) ("storeSwapOut: lowest_offset = %d\n",
-	(int) lowest_offset);
-    /*
-     * Grab the swapout_size and check to see whether we're going to defer
-     * the swapout based upon size
-     */
-    swapout_size = (ssize_t) (mem->inmem_hi - mem->swapout.queue_offset);
-    if ((e->store_status != STORE_OK) && (swapout_size < store_maxobjsize)) {
-	/*
-	 * NOTE: the store_maxobjsize here is the max of optional
-	 * max-size values from 'cache_dir' lines.  It is not the
-	 * same as 'maximum_object_size'.  By default, store_maxobjsize
-	 * will be set to -1.  However, I am worried that this
-	 * deferance may consume a lot of memory in some cases.
-	 * It would be good to make this decision based on reply
-	 * content-length, rather than wait to accumulate huge
-	 * amounts of object data in memory.
-	 */
-	debug(20, 5) ("storeSwapOut: Deferring starting swapping out\n");
-	return;
-    }
-    /*
-     * Careful.  lowest_offset can be greater than inmem_hi, such
-     * as in the case of a range request.
-     */
-    if (mem->inmem_hi < lowest_offset)
-	new_mem_lo = lowest_offset;
-    else if (mem->inmem_hi - mem->inmem_lo > Config.Store.maxInMemObjSize)
-	new_mem_lo = lowest_offset;
-    else
-	new_mem_lo = mem->inmem_lo;
-    assert(new_mem_lo >= mem->inmem_lo);
-    if (storeSwapOutAble(e)) {
-	/*
-	 * We should only free up to what we know has been written
-	 * to disk, not what has been queued for writing.  Otherwise
-	 * there will be a chunk of the data which is not in memory
-	 * and is not yet on disk.
-	 * The -1 makes sure the page isn't freed until storeSwapOut has
-	 * walked to the next page. (mem->swapout.memnode)
-	 */
-	if ((on_disk = storeSwapOutObjectBytesOnDisk(mem)) - 1 < new_mem_lo)
-	    new_mem_lo = on_disk - 1;
-	if (new_mem_lo == -1)
-	    new_mem_lo = 0;	/* the above might become -1 */
-    } else if (new_mem_lo > 0) {
-	/*
-	 * Its not swap-able, and we're about to delete a chunk,
-	 * so we must make it PRIVATE.  This is tricky/ugly because
-	 * for the most part, we treat swapable == cachable here.
-	 */
-	storeReleaseRequest(e);
-    }
-    stmemFreeDataUpto(&mem->data_hdr, new_mem_lo);
-    mem->inmem_lo = new_mem_lo;
-#if SIZEOF_OFF_T == 4
-    if (mem->inmem_hi > 0x7FFF0000) {
-	debug(20, 0) ("WARNING: preventing off_t overflow for %s\n", storeUrl(e));
-	storeAbort(e);
-	return;
-    }
-#endif
-    if (e->swap_status == SWAPOUT_WRITING)
-	assert(mem->inmem_lo <= on_disk);
-    if (!storeSwapOutAble(e))
-	return;
-    debug(20, 7) ("storeSwapOut: swapout_size = %d\n",
-	(int) swapout_size);
-    if (swapout_size == 0) {
-	if (e->store_status == STORE_OK)
-	    storeSwapOutFileClose(e);
-	return;			/* Nevermore! */
-    }
-    if (e->store_status == STORE_PENDING) {
-	/* wait for a full block to write */
-	if (swapout_size < SM_PAGE_SIZE)
-	    return;
-	/*
-	 * Wait until we are below the disk FD limit, only if the
-	 * next server-side read won't be deferred.
-	 */
-	if (storeTooManyDiskFilesOpen() && !fwdCheckDeferRead(-1, e))
-	    return;
-    }
-    /* Ok, we have stuff to swap out.  Is there a swapout.sio open? */
-    if (e->swap_status == SWAPOUT_NONE) {
-	assert(mem->swapout.sio == NULL);
-	assert(mem->inmem_lo == 0);
-	if (storeCheckCachable(e))
-	    storeSwapOutStart(e);
-	else
-	    return;
-	/* ENTRY_CACHABLE will be cleared and we'll never get here again */
-    }
-    if (NULL == mem->swapout.sio)
-	return;
-    do {
-	/*
-	 * Evil hack time.
-	 * We are paging out to disk in page size chunks. however, later on when
-	 * we update the queue position, we might not have a page (I *think*),
-	 * so we do the actual page update here.
-	 */
-
-	if (mem->swapout.memnode == NULL) {
-	    /* We need to swap out the first page */
-	    mem->swapout.memnode = mem->data_hdr.head;
-	} else {
-	    /* We need to swap out the next page */
-	    mem->swapout.memnode = mem->swapout.memnode->next;
-	}
-	/*
-	 * Get the length of this buffer. We are assuming(!) that the buffer
-	 * length won't change on this buffer, or things are going to be very
-	 * strange. I think that after the copy to a buffer is done, the buffer
-	 * size should stay fixed regardless so that this code isn't confused,
-	 * but we can look at this at a later date or whenever the code results
-	 * in bad swapouts, whichever happens first. :-)
-	 */
-	swap_buf_len = mem->swapout.memnode->len;
-
-	debug(20, 3) ("storeSwapOut: swap_buf_len = %d\n", (int) swap_buf_len);
-	assert(swap_buf_len > 0);
-	debug(20, 3) ("storeSwapOut: swapping out %ld bytes from %ld\n",
-	    (long int) swap_buf_len, (long int) mem->swapout.queue_offset);
-	mem->swapout.queue_offset += swap_buf_len;
-	storeWrite(mem->swapout.sio, mem->swapout.memnode->data, swap_buf_len, -1, NULL);
-	/* the storeWrite() call might generate an error */
-	if (e->swap_status != SWAPOUT_WRITING)
-	    break;
-	swapout_size = (ssize_t) (mem->inmem_hi - mem->swapout.queue_offset);
-	if (e->store_status == STORE_PENDING)
-	    if (swapout_size < SM_PAGE_SIZE)
-		break;
-    } while (swapout_size > 0);
-    if (NULL == mem->swapout.sio)
-	/* oops, we're not swapping out any more */
-	return;
-    if (e->store_status == STORE_OK) {
-	/*
-	 * If the state is STORE_OK, then all data must have been given
-	 * to the filesystem at this point because storeSwapOut() is
-	 * not going to be called again for this entry.
-	 */
-	assert(mem->inmem_hi == mem->swapout.queue_offset);
-	storeSwapOutFileClose(e);
-    }
-}
-
-void
-storeSwapOutFileClose(StoreEntry * e)
-{
-    MemObject *mem = e->mem_obj;
-    assert(mem != NULL);
-    debug(20, 3) ("storeSwapOutFileClose: %s\n", storeKeyText(e->hash.key));
-    debug(20, 3) ("storeSwapOutFileClose: sio = %p\n", mem->swapout.sio);
-    if (mem->swapout.sio == NULL)
-	return;
-    storeClose(mem->swapout.sio);
-}
-
-static void
-storeSwapOutFileClosed(void *data, int errflag, storeIOState * sio)
-{
-    generic_cbdata *c = data;
-    StoreEntry *e = c->data;
-    MemObject *mem = e->mem_obj;
-    assert(e->swap_status == SWAPOUT_WRITING);
-    cbdataFree(c);
-    if (errflag) {
-	debug(20, 1) ("storeSwapOutFileClosed: dirno %d, swapfile %08X, errflag=%d\n\t%s\n",
-	    e->swap_dirn, e->swap_filen, errflag, xstrerror());
-	if (errflag == DISK_NO_SPACE_LEFT) {
-	    storeDirDiskFull(e->swap_dirn);
-	    storeDirConfigure();
-	    storeConfigure();
-	}
-	if (e->swap_filen > 0)
-	    storeUnlink(e);
-	e->swap_filen = -1;
-	e->swap_dirn = -1;
-	e->swap_status = SWAPOUT_NONE;
-	storeReleaseRequest(e);
-    } else {
-	/* swapping complete */
-	debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %d, %08X\n",
-	    storeUrl(e), e->swap_dirn, e->swap_filen);
-	e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz;
-	e->swap_status = SWAPOUT_DONE;
-	storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1);
-	if (storeCheckCachable(e)) {
-	    storeLog(STORE_LOG_SWAPOUT, e);
-	    storeDirSwapLog(e, SWAP_LOG_ADD);
-	}
-	statCounter.swap.outs++;
-    }
-    debug(20, 3) ("storeSwapOutFileClosed: %s:%d\n", __FILE__, __LINE__);
-    cbdataReferenceDone(mem->swapout.sio);
-    storeUnlockObject(e);
-}
-
-/*
- * How much of the object data is on the disk?
- */
-static off_t
-storeSwapOutObjectBytesOnDisk(const MemObject * mem)
-{
-    /*
-     * NOTE: storeOffset() represents the disk file size,
-     * not the amount of object data on disk.
-     * 
-     * If we don't have at least 'swap_hdr_sz' bytes
-     * then none of the object data is on disk.
-     *
-     * This should still be safe if swap_hdr_sz == 0,
-     * meaning we haven't even opened the swapout file
-     * yet.
-     */
-    off_t nwritten;
-    if (mem->swapout.sio == NULL)
-	return 0;
-    nwritten = storeOffset(mem->swapout.sio);
-    if (nwritten <= mem->swap_hdr_sz)
-	return 0;
-    return nwritten - mem->swap_hdr_sz;
-}
-
-/*
- * Is this entry a candidate for writing to disk?
- */
-int
-storeSwapOutAble(const StoreEntry * e)
-{
-    dlink_node *node;
-    if (e->mem_obj->swapout.sio != NULL)
-	return 1;
-    if (e->mem_obj->inmem_lo > 0)
-	return 0;
-    /*
-     * If there are DISK clients, we must write to disk
-     * even if its not cachable
-     */
-    for (node = e->mem_obj->clients.head; node; node = node->next) {
-	if (((store_client *) node->data)->type == STORE_DISK_CLIENT)
-	    return 1;
-    }
-    /* Don't pollute the disk with icons and other special entries */
-    if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
-	return 0;
-    return EBIT_TEST(e->flags, ENTRY_CACHABLE);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/store_swapout.cc	Wed Feb 14 01:07:40 2007
@@ -0,0 +1,386 @@
+
+/*
+ * $Id: store_swapout.cc,v 1.1.2.1 2002/10/11 15:41:05 rbcollins Exp $
+ *
+ * DEBUG: section 20    Storage Manager Swapout Functions
+ * AUTHOR: Duane Wessels
+ *
+ * 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"
+#include "StoreClient.h"
+#include "Store.h"
+
+static off_t storeSwapOutObjectBytesOnDisk(const MemObject *);
+static void storeSwapOutStart(StoreEntry * e);
+static STIOCB storeSwapOutFileClosed;
+static STIOCB storeSwapOutFileNotify;
+
+/* start swapping object to disk */
+static void
+storeSwapOutStart(StoreEntry * e)
+{
+    generic_cbdata *c;
+    MemObject *mem = e->mem_obj;
+    int swap_hdr_sz = 0;
+    tlv *tlv_list;
+    char *buf;
+    storeIOState *sio;
+    assert(mem);
+    /* Build the swap metadata, so the filesystem will know how much
+     * metadata there is to store
+     */
+    debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to dirno %d, fileno %08X\n",
+	storeUrl(e), e->swap_dirn, e->swap_filen);
+    e->swap_status = SWAPOUT_WRITING;
+    tlv_list = storeSwapMetaBuild(e);
+    buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
+    storeSwapTLVFree(tlv_list);
+    mem->swap_hdr_sz = (size_t) swap_hdr_sz;
+    /* Create the swap file */
+    c = cbdataAlloc(generic_cbdata);
+    c->data = e;
+    sio = storeCreate(e, storeSwapOutFileNotify, storeSwapOutFileClosed, c);
+    mem->swapout.sio = cbdataReference(sio);
+    if (NULL == mem->swapout.sio) {
+	e->swap_status = SWAPOUT_NONE;
+	cbdataFree(c);
+	xfree(buf);
+	storeLog(STORE_LOG_SWAPOUTFAIL, e);
+	return;
+    }
+    storeLockObject(e);		/* Don't lock until after create, or the replacement
+				 * code might get confused */
+    /* Pick up the file number if it was assigned immediately */
+    e->swap_filen = mem->swapout.sio->swap_filen;
+    e->swap_dirn = mem->swapout.sio->swap_dirn;
+    /* write out the swap metadata */
+    storeWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree);
+}
+
+static void
+storeSwapOutFileNotify(void *data, int errflag, storeIOState * sio)
+{
+    generic_cbdata *c = (generic_cbdata *)data;
+    StoreEntry *e = (StoreEntry *)c->data;
+    MemObject *mem = e->mem_obj;
+    assert(e->swap_status == SWAPOUT_WRITING);
+    assert(mem);
+    assert(mem->swapout.sio == sio);
+    assert(errflag == 0);
+    e->swap_filen = mem->swapout.sio->swap_filen;
+    e->swap_dirn = mem->swapout.sio->swap_dirn;
+}
+
+void
+storeSwapOut(StoreEntry * e)
+{
+    MemObject *mem = e->mem_obj;
+    off_t lowest_offset;
+    off_t new_mem_lo;
+    off_t on_disk = 0;
+    ssize_t swapout_size;
+    ssize_t swap_buf_len;
+    if (mem == NULL)
+	return;
+    /* should we swap something out to disk? */
+    debug(20, 7) ("storeSwapOut: %s\n", storeUrl(e));
+    debug(20, 7) ("storeSwapOut: store_status = %s\n",
+	storeStatusStr[e->store_status]);
+    if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
+	assert(EBIT_TEST(e->flags, RELEASE_REQUEST));
+	storeSwapOutFileClose(e);
+	return;
+    }
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+	debug(20, 3) ("storeSwapOut: %s SPECIAL\n", storeUrl(e));
+	return;
+    }
+    debug(20, 7) ("storeSwapOut: mem->inmem_lo = %d\n",
+	(int) mem->inmem_lo);
+    debug(20, 7) ("storeSwapOut: mem->inmem_hi = %d\n",
+	(int) mem->inmem_hi);
+    debug(20, 7) ("storeSwapOut: swapout.queue_offset = %d\n",
+	(int) mem->swapout.queue_offset);
+    if (mem->swapout.sio)
+	debug(20, 7) ("storeSwapOut: storeOffset() = %d\n",
+	    (int) storeOffset(mem->swapout.sio));
+    assert(mem->inmem_hi >= mem->swapout.queue_offset);
+    lowest_offset = storeLowestMemReaderOffset(e);
+    debug(20, 7) ("storeSwapOut: lowest_offset = %d\n",
+	(int) lowest_offset);
+    /*
+     * Grab the swapout_size and check to see whether we're going to defer
+     * the swapout based upon size
+     */
+    swapout_size = (ssize_t) (mem->inmem_hi - mem->swapout.queue_offset);
+    if ((e->store_status != STORE_OK) && (swapout_size < store_maxobjsize)) {
+	/*
+	 * NOTE: the store_maxobjsize here is the max of optional
+	 * max-size values from 'cache_dir' lines.  It is not the
+	 * same as 'maximum_object_size'.  By default, store_maxobjsize
+	 * will be set to -1.  However, I am worried that this
+	 * deferance may consume a lot of memory in some cases.
+	 * It would be good to make this decision based on reply
+	 * content-length, rather than wait to accumulate huge
+	 * amounts of object data in memory.
+	 */
+	debug(20, 5) ("storeSwapOut: Deferring starting swapping out\n");
+	return;
+    }
+    /*
+     * Careful.  lowest_offset can be greater than inmem_hi, such
+     * as in the case of a range request.
+     */
+    if (mem->inmem_hi < lowest_offset)
+	new_mem_lo = lowest_offset;
+    else if (mem->inmem_hi - mem->inmem_lo > (ssize_t)Config.Store.maxInMemObjSize)
+	new_mem_lo = lowest_offset;
+    else
+	new_mem_lo = mem->inmem_lo;
+    assert(new_mem_lo >= mem->inmem_lo);
+    if (storeSwapOutAble(e)) {
+	/*
+	 * We should only free up to what we know has been written
+	 * to disk, not what has been queued for writing.  Otherwise
+	 * there will be a chunk of the data which is not in memory
+	 * and is not yet on disk.
+	 * The -1 makes sure the page isn't freed until storeSwapOut has
+	 * walked to the next page. (mem->swapout.memnode)
+	 */
+	if ((on_disk = storeSwapOutObjectBytesOnDisk(mem)) - 1 < new_mem_lo)
+	    new_mem_lo = on_disk - 1;
+	if (new_mem_lo == -1)
+	    new_mem_lo = 0;	/* the above might become -1 */
+    } else if (new_mem_lo > 0) {
+	/*
+	 * Its not swap-able, and we're about to delete a chunk,
+	 * so we must make it PRIVATE.  This is tricky/ugly because
+	 * for the most part, we treat swapable == cachable here.
+	 */
+	storeReleaseRequest(e);
+    }
+    stmemFreeDataUpto(&mem->data_hdr, new_mem_lo);
+    mem->inmem_lo = new_mem_lo;
+#if SIZEOF_OFF_T == 4
+    if (mem->inmem_hi > 0x7FFF0000) {
+	debug(20, 0) ("WARNING: preventing off_t overflow for %s\n", storeUrl(e));
+	storeAbort(e);
+	return;
+    }
+#endif
+    if (e->swap_status == SWAPOUT_WRITING)
+	assert(mem->inmem_lo <= on_disk);
+    if (!storeSwapOutAble(e))
+	return;
+    debug(20, 7) ("storeSwapOut: swapout_size = %d\n",
+	(int) swapout_size);
+    if (swapout_size == 0) {
+	if (e->store_status == STORE_OK)
+	    storeSwapOutFileClose(e);
+	return;			/* Nevermore! */
+    }
+    if (e->store_status == STORE_PENDING) {
+	/* wait for a full block to write */
+	if (swapout_size < SM_PAGE_SIZE)
+	    return;
+	/*
+	 * Wait until we are below the disk FD limit, only if the
+	 * next server-side read won't be deferred.
+	 */
+	if (storeTooManyDiskFilesOpen() && !fwdCheckDeferRead(-1, e))
+	    return;
+    }
+    /* Ok, we have stuff to swap out.  Is there a swapout.sio open? */
+    if (e->swap_status == SWAPOUT_NONE) {
+	assert(mem->swapout.sio == NULL);
+	assert(mem->inmem_lo == 0);
+	if (storeCheckCachable(e))
+	    storeSwapOutStart(e);
+	else
+	    return;
+	/* ENTRY_CACHABLE will be cleared and we'll never get here again */
+    }
+    if (NULL == mem->swapout.sio)
+	return;
+    do {
+	/*
+	 * Evil hack time.
+	 * We are paging out to disk in page size chunks. however, later on when
+	 * we update the queue position, we might not have a page (I *think*),
+	 * so we do the actual page update here.
+	 */
+
+	if (mem->swapout.memnode == NULL) {
+	    /* We need to swap out the first page */
+	    mem->swapout.memnode = mem->data_hdr.head;
+	} else {
+	    /* We need to swap out the next page */
+	    mem->swapout.memnode = mem->swapout.memnode->next;
+	}
+	/*
+	 * Get the length of this buffer. We are assuming(!) that the buffer
+	 * length won't change on this buffer, or things are going to be very
+	 * strange. I think that after the copy to a buffer is done, the buffer
+	 * size should stay fixed regardless so that this code isn't confused,
+	 * but we can look at this at a later date or whenever the code results
+	 * in bad swapouts, whichever happens first. :-)
+	 */
+	swap_buf_len = mem->swapout.memnode->len;
+
+	debug(20, 3) ("storeSwapOut: swap_buf_len = %d\n", (int) swap_buf_len);
+	assert(swap_buf_len > 0);
+	debug(20, 3) ("storeSwapOut: swapping out %ld bytes from %ld\n",
+	    (long int) swap_buf_len, (long int) mem->swapout.queue_offset);
+	mem->swapout.queue_offset += swap_buf_len;
+	storeWrite(mem->swapout.sio, mem->swapout.memnode->data, swap_buf_len, -1, NULL);
+	/* the storeWrite() call might generate an error */
+	if (e->swap_status != SWAPOUT_WRITING)
+	    break;
+	swapout_size = (ssize_t) (mem->inmem_hi - mem->swapout.queue_offset);
+	if (e->store_status == STORE_PENDING)
+	    if (swapout_size < SM_PAGE_SIZE)
+		break;
+    } while (swapout_size > 0);
+    if (NULL == mem->swapout.sio)
+	/* oops, we're not swapping out any more */
+	return;
+    if (e->store_status == STORE_OK) {
+	/*
+	 * If the state is STORE_OK, then all data must have been given
+	 * to the filesystem at this point because storeSwapOut() is
+	 * not going to be called again for this entry.
+	 */
+	assert(mem->inmem_hi == mem->swapout.queue_offset);
+	storeSwapOutFileClose(e);
+    }
+}
+
+void
+storeSwapOutFileClose(StoreEntry * e)
+{
+    MemObject *mem = e->mem_obj;
+    assert(mem != NULL);
+    debug(20, 3) ("storeSwapOutFileClose: %s\n", storeKeyText((const cache_key *)e->hash.key));
+    debug(20, 3) ("storeSwapOutFileClose: sio = %p\n", mem->swapout.sio);
+    if (mem->swapout.sio == NULL)
+	return;
+    storeClose(mem->swapout.sio);
+}
+
+static void
+storeSwapOutFileClosed(void *data, int errflag, storeIOState * sio)
+{
+    generic_cbdata *c = (generic_cbdata *)data;
+    StoreEntry *e = (StoreEntry *)c->data;
+    MemObject *mem = e->mem_obj;
+    assert(e->swap_status == SWAPOUT_WRITING);
+    cbdataFree(c);
+    if (errflag) {
+	debug(20, 1) ("storeSwapOutFileClosed: dirno %d, swapfile %08X, errflag=%d\n\t%s\n",
+	    e->swap_dirn, e->swap_filen, errflag, xstrerror());
+	if (errflag == DISK_NO_SPACE_LEFT) {
+	    storeDirDiskFull(e->swap_dirn);
+	    storeDirConfigure();
+	    storeConfigure();
+	}
+	if (e->swap_filen > 0)
+	    storeUnlink(e);
+	e->swap_filen = -1;
+	e->swap_dirn = -1;
+	e->swap_status = SWAPOUT_NONE;
+	storeReleaseRequest(e);
+    } else {
+	/* swapping complete */
+	debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %d, %08X\n",
+	    storeUrl(e), e->swap_dirn, e->swap_filen);
+	e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz;
+	e->swap_status = SWAPOUT_DONE;
+	storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1);
+	if (storeCheckCachable(e)) {
+	    storeLog(STORE_LOG_SWAPOUT, e);
+	    storeDirSwapLog(e, SWAP_LOG_ADD);
+	}
+	statCounter.swap.outs++;
+    }
+    debug(20, 3) ("storeSwapOutFileClosed: %s:%d\n", __FILE__, __LINE__);
+    cbdataReferenceDone(mem->swapout.sio);
+    storeUnlockObject(e);
+}
+
+/*
+ * How much of the object data is on the disk?
+ */
+static off_t
+storeSwapOutObjectBytesOnDisk(const MemObject * mem)
+{
+    /*
+     * NOTE: storeOffset() represents the disk file size,
+     * not the amount of object data on disk.
+     * 
+     * If we don't have at least 'swap_hdr_sz' bytes
+     * then none of the object data is on disk.
+     *
+     * This should still be safe if swap_hdr_sz == 0,
+     * meaning we haven't even opened the swapout file
+     * yet.
+     */
+    off_t nwritten;
+    if (mem->swapout.sio == NULL)
+	return 0;
+    nwritten = storeOffset(mem->swapout.sio);
+    if (nwritten <= (off_t)mem->swap_hdr_sz)
+	return 0;
+    return nwritten - mem->swap_hdr_sz;
+}
+
+/*
+ * Is this entry a candidate for writing to disk?
+ */
+int
+storeSwapOutAble(const StoreEntry * e)
+{
+    dlink_node *node;
+    if (e->mem_obj->swapout.sio != NULL)
+	return 1;
+    if (e->mem_obj->inmem_lo > 0)
+	return 0;
+    /*
+     * If there are DISK clients, we must write to disk
+     * even if its not cachable
+     */
+    for (node = e->mem_obj->clients.head; node; node = node->next) {
+	if (((store_client *) node->data)->type == STORE_DISK_CLIENT)
+	    return 1;
+    }
+    /* Don't pollute the disk with icons and other special entries */
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
+	return 0;
+    return EBIT_TEST(e->flags, ENTRY_CACHABLE);
+}
Index: squid/src/structs.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/structs.h,v
retrieving revision 1.70.2.8
retrieving revision 1.70.2.9
diff -u -r1.70.2.8 -r1.70.2.9
--- squid/src/structs.h	9 Oct 2002 14:14:30 -0000	1.70.2.8
+++ squid/src/structs.h	11 Oct 2002 15:41:06 -0000	1.70.2.9
@@ -1,6 +1,6 @@
 
 /*
- * $Id: structs.h,v 1.70.2.8 2002/10/09 14:14:30 rbcollins Exp $
+ * $Id: structs.h,v 1.70.2.9 2002/10/11 15:41:06 rbcollins Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -658,6 +658,14 @@
     int weak;			/* true if it is a weak validator */
 };
 
+struct _fde_disk {
+    DWCB *wrt_handle;
+    void *wrt_handle_data;
+    dwrite_q *write_q;
+    dwrite_q *write_q_tail;
+    off_t offset;
+};
+
 struct _fde {
     unsigned int type;
     u_short local_port;
@@ -683,13 +691,7 @@
     int bytes_read;
     int bytes_written;
     int uses;			/* ie # req's over persistent conn */
-    struct _fde_disk {
-	DWCB *wrt_handle;
-	void *wrt_handle_data;
-	dwrite_q *write_q;
-	dwrite_q *write_q_tail;
-	off_t offset;
-    } disk;
+    struct _fde_disk disk;
     PF *read_handler;
     void *read_data;
     PF *write_handler;
