This patch is generated from the cygwin branch of HEAD in squid
Wed Apr  6 02:18:08 2005 GMT
See http://devel.squid-cache.org/

Index: squid/acinclude.m4
diff -u squid/acinclude.m4:1.6 squid/acinclude.m4:1.1.2.6
--- squid/acinclude.m4:1.6	Sun Oct 13 04:43:36 2002
+++ squid/acinclude.m4	Sun Oct 20 06:45:45 2002
@@ -1,3 +1,123 @@
+dnl COMPILER WIN32 support ====================================
+# figure out how to run CC with access to the win32 api if present
+# configure that as the CC program, 
+# WIN32 may be present with WINE, under cygwin, or under mingw,
+# or cross compilers targeting those same three targets.
+# as it happens, I can only test cygwin, so extra input here will be appreciated
+# send bug reports to Robert Collins <rbtcollins@hotmail.com>
+#
+# logic: is CC already configured? if not, call AC_PROG_CC.
+# if so - try it. If that doesn't work ,try -mwin32. If that doesn't work, fail
+#
+# 2001-03-15 - Changed from yes/no to true/false -suggested by Lars J Aas<larsa@sim.no>
+# 	* Change true to : - suggest by Alexandre Oliva <oliva@lsd.ic.unicamp.br>
+#	* changed layout on the basis of autoconf mailing list:
+#		there are now two interfaces, a language specific one which sets
+#		or clears WIN32 && WIN32FLAGS as appropriate
+#	* m4 Syntax fixup: Akim Demaille <akim@epita.fr>
+#
+# All faults& bugs are mine - Robert
+
+AC_DEFUN([AC_PROG_CC_WIN32], [
+dnl AC_REQUIRE([AC_PROG_CC])dnl
+AC_MSG_CHECKING([how to access the Win32 API])
+WIN32FLAGS=
+AC_TRY_COMPILE(,[
+#ifndef WIN32
+# ifndef _WIN32
+#  error WIN32 or _WIN32 not defined
+# endif
+#endif], [
+dnl found windows.h with the current config.
+AC_MSG_RESULT([present by default])
+], [
+dnl try -mwin32
+ac_compile_save="$ac_compile"
+dnl we change CC so config.log looks correct
+save_CC="$CC"
+ac_compile="$ac_compile -mwin32"
+CC="$CC -mwin32"
+AC_TRY_COMPILE(,[
+#ifndef WIN32
+# ifndef _WIN32
+#  error WIN32 or _WIN32 not defined
+# endif
+#endif], [
+dnl found windows.h using -mwin32
+AC_MSG_RESULT([found via -mwin32])
+ac_compile="$ac_compile_save"
+CC="$save_CC"
+WIN32FLAGS="-mwin32"
+], [
+ac_compile="$ac_compile_save"
+CC="$save_CC"
+AC_MSG_RESULT([not found])
+])
+])
+
+])
+
+
+# figure out how to run CXX with access to the win32 api if present
+# configure that as the CXX program,
+# WIN32 may be present with WINE, under cygwin, or under mingw,
+# or cross compilers targeting those same three targets.
+# as it happens, I can only test cygwin, so extra input here will be appreciated
+# send bug reports to Robert Collins <rbtcollins@hotmail.com>
+
+AC_DEFUN([AC_PROG_CXX_WIN32], [
+dnl AC_REQUIRE([AC_PROG_CXX])dnl
+AC_MSG_CHECKING([how to access the Win32 API])
+WIN32FLAGS=
+AC_TRY_COMPILE(,[
+#ifndef WIN32
+# ifndef _WIN32
+#  error WIN32 or _WIN32 not defined
+# endif
+#endif], [
+dnl found windows.h with the current config.
+AC_MSG_RESULT([present by default])
+], [
+dnl try -mwin32
+ac_compile_save="$ac_compile"
+dnl we change CC so config.log looks correct
+save_CXX="$CXX"
+ac_compile="$ac_compile -mwin32"
+CXX="$CXX -mwin32"
+AC_TRY_COMPILE(,[
+#ifndef WIN32
+# ifndef _WIN32
+#  error WIN32 or _WIN32 not defined
+# endif
+#endif], [
+dnl found windows.h using -mwin32
+AC_MSG_RESULT([found via -mwin32])
+ac_compile="$ac_compile_save"
+CXX="$save_CXX"
+WIN32FLAGS="-mwin32"
+], [
+ac_compile="$ac_compile_save"
+CXX="$save_CXX"
+AC_MSG_RESULT([not found])
+])
+])
+
+])
+
+
+# high level interface for finding out compiler support for win32.
+AC_DEFUN([AC_API_WIN32], [
+
+AC_LANG_CASE(
+	    [C],	AC_PROG_CC_WIN32 [CFLAGS="$WIN32FLAGS $CFLAGS"
+	CPPFLAGS="$WIN32FLAGS $CPPFLAGS"],
+            [C++],	AC_PROG_CXX_WIN32 [CXXFLAGS="$WIN32FLAGS $CXXFLAGS"
+	CPPFLAGS="$WIN32FLAGS $CPPFLAGS"],
+             [AC_FATAL([No macro support for WIN32 with ] _AC_LANG) ])
+])
+
+dnl end compiler WIN32 support ===========================================
+
 dnl This encapsulates the nasty mess of headers we need to check when 
 dnl checking types.
 AC_DEFUN(SQUID_DEFAULT_INCLUDES,[[
Index: squid/configure.in
diff -u squid/configure.in:1.80 squid/configure.in:1.8.2.51
--- squid/configure.in:1.80	Sat Feb  8 19:15:56 2003
+++ squid/configure.in	Wed Feb 12 01:56:27 2003
@@ -91,6 +91,9 @@
 
 AC_DEFINE_UNQUOTED(CONFIG_HOST_TYPE, "$host",[Host type from configure])
 
+dnl Can the CC program be asked to use the Win32 API?
+AC_API_WIN32
+
 AC_DEFINE_UNQUOTED(SQUID_CONFIGURE_OPTIONS, "$ac_configure_args", [configure command line used to configure Squid])
 
 dnl Check for GNU cc
@@ -2386,6 +2389,7 @@
 	icons/Makefile \
 	errors/Makefile \
 	src/fs/aufs/Makefile \
+	src/fs/awin32/Makefile \
 	src/fs/coss/Makefile \
 	src/fs/diskd/Makefile \
 	src/fs/null/Makefile \
@@ -2400,6 +2404,7 @@
 	helpers/basic_auth/NCSA/Makefile \
 	helpers/basic_auth/PAM/Makefile \
 	helpers/basic_auth/SMB/Makefile \
+	helpers/basic_auth/win32_locallogon/Makefile \
 	helpers/basic_auth/YP/Makefile \
 	helpers/basic_auth/getpwnam/Makefile \
 	helpers/basic_auth/multi-domain-NTLM/Makefile \
@@ -2412,6 +2417,7 @@
 	helpers/ntlm_auth/no_check/Makefile \
 	helpers/ntlm_auth/SMB/Makefile \
 	helpers/ntlm_auth/SMB/smbval/Makefile \
+	helpers/ntlm_auth/NTLMSSP-WIN32/Makefile \
 	helpers/ntlm_auth/winbind/Makefile \
 	helpers/external_acl/Makefile \
 	helpers/external_acl/ip_user/Makefile \
@@ -2419,5 +2425,6 @@
 	helpers/external_acl/unix_group/Makefile \
 	helpers/external_acl/wbinfo_group/Makefile \
 	helpers/external_acl/winbind_group/Makefile \
+	helpers/external_acl/win32_group/Makefile \
 ])
 AC_OUTPUT
Index: squid/doc/win32-relnotes.txt
diff -u /dev/null squid/doc/win32-relnotes.txt:1.1.2.5
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/doc/win32-relnotes.txt	Sun Aug 11 09:45:09 2002
@@ -0,0 +1,38 @@
+WIN32 Squid Relese Notes
+
+
+
+PSAPI.DLL (Process Status Helper) Considerations
+
+The process status helper functions make it easier for you to obtain information about
+processes and device drivers running on Microsoft® Windows NT®/Windows® 2000. These
+functions are available in PSAPI.DLL, which is distributed in the Microsoft® Platform
+Software Development Kit (SDK). The same information is generally available through the
+performance data in the registry, but it is more difficult to get to it. PSAPI.DLL is
+freely redistributable.
+
+PSAPI.DLL is available only on Windows NT, 2000 and XP. The implementation in Squid is
+aware of this, and try to use it only on the rigth platform.
+
+On Windows NT PSAPI.DLL can be found as component of many applications, if you need it,
+you can find it on Windows NT Resource KIT. If you have problem, you can You can 
+download it here: 
+http://download.microsoft.com/download/platformsdk/Redist/4.0.1371.1/NT4/EN-US/psinst.EXE
+
+On Windows 2000 it is available installing the Windows 2000 Support Tools, located on the
+Support\Tools folder of the installation Windows 2000 CD-ROM.
+
+
+Registry DNS lookup
+
+On Windows platforms, if no value is specified in the dns_nameservers option on 
+squid.conf or in the /etc/resolv.conf file, the list of DNS name servers are
+taken from the Windows registry, both static and dynamic DHCP configurations
+are supported.
+
+
+Awin32 fs module
+
+awin32 is a native WIN32 Async I/O Squid fs module derived from aufs. Configuration
+in squid.conf is identical to aufs, but You nedd to specify awin32 instead aufs as 
+cache type.
Index: squid/helpers/basic_auth/Makefile.am
diff -u squid/helpers/basic_auth/Makefile.am:1.2 squid/helpers/basic_auth/Makefile.am:1.2.12.2
--- squid/helpers/basic_auth/Makefile.am:1.2	Fri Jun 28 07:59:08 2002
+++ squid/helpers/basic_auth/Makefile.am	Tue Jul 16 06:34:35 2002
@@ -3,5 +3,5 @@
 #  $Id$
 #
 
-DIST_SUBDIRS	= getpwnam LDAP MSNT multi-domain-NTLM NCSA PAM SMB YP SASL winbind
+DIST_SUBDIRS	= getpwnam LDAP MSNT multi-domain-NTLM NCSA PAM SMB YP SASL winbind win32_locallogon
 SUBDIRS		= @BASIC_AUTH_HELPERS@
Index: squid/helpers/basic_auth/win32_locallogon/.cvsignore
diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/.cvsignore:1.1.6.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/basic_auth/win32_locallogon/.cvsignore	Sat Jun 29 14:14:51 2002
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/helpers/basic_auth/win32_locallogon/Makefile.am
diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/Makefile.am:1.1.6.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/basic_auth/win32_locallogon/Makefile.am	Tue Jul 16 06:34:36 2002
@@ -0,0 +1,17 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id$
+#
+#  Uncomment and customize the following to suit your needs:
+#
+
+
+libexec_PROGRAMS = nt_auth
+
+nt_auth_SOURCES = NT_auth.c valid.c
+
+LDADD	= -L$(top_builddir)/lib -lnetapi32 -ladvapi32 -lsspwin32 -lmiscutil
+
+INCLUDES      = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
+		-I$(top_srcdir)/src/
Index: squid/helpers/basic_auth/win32_locallogon/NT_auth.c
diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/NT_auth.c:1.1.6.4
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/basic_auth/win32_locallogon/NT_auth.c	Sun Sep 29 09:31:17 2002
@@ -0,0 +1,172 @@
+/*
+  NT_auth -  Version 2.0
+
+  Modified to act as a Squid authenticator module.
+  Removed all Pike stuff.
+  Returns OK for a successful authentication, or ERR upon error.
+
+  Guido Serassio, Torino - Italy
+
+  Uses code from -
+    Antonino Iannella 2000
+    Andrew Tridgell 1997
+    Richard Sharpe 1996
+    Bill Welliver 1999
+
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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"
+
+/* Check if we try to compile on a Windows Platform */
+#if defined(_SQUID_CYGWIN_) || defined(_SQUID_MSWIN_)
+
+#include "valid.h"
+
+static char NTGroup[256];
+char * NTAllowedGroup;
+char * NTDisAllowedGroup;
+int UseDisallowedGroup = 0;
+int UseAllowedGroup = 0;
+
+/*
+ * options:
+ * -a can specify a Windows Local Group name allowed to authenticate.
+ * -d can specify a Windows Local Group name not allowed to authenticate.
+ * -D can specify the default Domain against to authenticate.
+ */
+char *my_program_name = NULL;
+
+void
+usage()
+{
+    fprintf(stderr,
+	"%s usage:\n%s [-a UserGroup] [-d UserGroup] [-D DefaultDomain] \n"
+	"-a can specify a Windows Local Group name allowed to authenticate\n"
+	"-d can specify a Windows Local Group name not allowed to authenticate\n"
+	"-D can specify the default Domain against to authenticate\n"
+	"-h this message\n\n",
+	my_program_name, my_program_name);
+}
+
+void
+process_options(int argc, char *argv[])
+{
+    int opt, had_error = 0;
+    while (-1 != (opt = getopt(argc, argv, "ha:d:D:"))) {
+	switch (opt) {
+	case 'a':
+	    safe_free(NTAllowedGroup);
+	    NTAllowedGroup=xstrdup(optarg);
+	    UseAllowedGroup = 1;
+	    break;
+	case 'd':
+	    safe_free(NTDisAllowedGroup);
+	    NTDisAllowedGroup=xstrdup(optarg);
+	    UseDisallowedGroup = 1;
+	    break;
+	case 'D':
+	    strcpy(Default_NTDomain, optarg);
+	    break;
+	case 'h':
+	    usage(argv[0]);
+	    exit(0);
+	case '?':
+	    opt = optopt;
+	    /* fall thru to default */
+	default:
+	    fprintf(stderr, "Unknown option: -%c. Exiting\n", opt);
+	    had_error = 1;
+	}
+    }
+    if (had_error) {
+	usage();
+	exit(1);
+    }
+}
+
+/* Main program for simple authentication.
+   Scans and checks for Squid input, and attempts to validate the user.
+*/
+
+int
+main(int argc, char **argv)
+
+{
+    char wstr[256];
+    char username[256];
+    char password[256];
+    char *p;
+    int err = 0;
+
+    my_program_name = argv[0];
+    process_options(argc, argv);
+
+    if (LoadSecurityDll(SSP_BASIC) == NULL) {
+	fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
+	exit(1);
+    }
+    atexit(UnloadSecurityDll);
+
+        /* initialize FDescs */
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    while (1) {
+	/* Read whole line from standard input. Terminate on break. */
+	if (fgets(wstr, 255, stdin) == NULL)
+	    break;
+
+	if (NULL == strchr(wstr, '\n')) {
+	    err = 1;
+	    continue;
+	}
+	if (err) {
+	    fprintf(stderr, "Oversized message\n");
+	    goto error;
+	}
+	
+	if ((p = strchr(wstr, '\n')) != NULL)
+	    *p = '\0';		/* strip \n */
+	if ((p = strchr(wstr, '\r')) != NULL)
+	    *p = '\0';		/* strip \r */
+	/* Clear any current settings */
+	username[0] = '\0';
+	password[0] = '\0';
+	sscanf(wstr, "%s %s", username, password);	/* Extract parameters */
+	
+	/* Check for invalid or blank entries */
+	if ((username[0] == '\0') || (password[0] == '\0')) {
+	    fprintf(stderr, "Invalid Request\n");
+	    puts("ERR");
+	    fflush(stdout);
+	    continue;
+	}
+	rfc1738_unescape(username);
+	rfc1738_unescape(password);
+	if (Valid_User(username, password, NTGroup) == NTV_NO_ERROR)
+	    puts("OK");
+	else
+error:
+        puts("ERR");
+	err = 0;
+	fflush(stdout);
+    }
+    return 0;
+}
+
+#else  /* NON Windows Platform !!! */
+
+#error NON WINDOWS PLATFORM
+
+#endif
Index: squid/helpers/basic_auth/win32_locallogon/README.txt
diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/README.txt:1.1.6.4
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/basic_auth/win32_locallogon/README.txt	Sun Sep 29 09:31:17 2002
@@ -0,0 +1,95 @@
+This is a simple authentication module for the Squid proxy server running on Windows NT
+to authenticate users on an NT domain in native WIN32 mode.
+
+Usage is simple. It accepts a username and password on standard input
+and will return OK if the username/password is valid for the domain/machine,
+or ERR if there was some problem.
+It's possible to authenticate against NT trusted domains specifyng the username 
+in the domain\\username Microsoft notation. 
+
+
+==============
+Program Syntax
+==============
+
+nt_auth [-a UserGroup] [-d UserGroup] [-D DefaultDomain]
+
+-a can specify a Windows Local Group name allowed to authenticate.
+-d can specify a Windows Local Group name not allowed to authenticate.
+-D can specify the default Domain against to authenticate.
+
+This is released under the GNU General Public License and
+is available from http://www.serassio.it.
+
+
+==============
+Allowing Users
+==============
+
+Users that are allowed to access the web proxy must have the Windows NT
+User Rights "logon from the network" and must be included in the NT LOCAL User Groups 
+specified in the Authenticator's command line. 
+This can be accomplished creating a local user group on the NT machine, grant the privilege,
+and adding users to it.
+
+Refer to Squid documentation for the required changes to squid.conf.
+
+
+============
+Installation
+============
+
+Type 'make', then 'make install', then 'make clean'.
+
+The default is to install 'nt_auth' into /usr/local/squid/libexec.
+
+Refer to Squid documentation for the required changes to squid.conf.
+You will need to set the following line to enable the authenticator:
+
+authenticate_program /usr/local/squid/bin/NT_auth <yourNTgroup>
+
+You will need to set the following lines to enable authentication for
+your access list -
+
+  acl <yourACL> proxy_auth REQUIRED
+  http_access allow <yourACL>
+
+You will need to specify the absolute path to NT_auth in the 
+authenticate_program directive, and check the authenticate_children
+and authenticate_ttl.
+
+
+==================
+Compilation issues
+==================
+
+The Makefile assumes that GCC is in the current PATH.
+NT_auth compile ONLY on CygWin Environment or MS VC++.
+
+
+=======
+Testing
+=======
+
+I strongly urge that NT_auth is tested prior to being used in a 
+production environment. It may behave differently on different platforms.
+To test it, run it from the command line. Enter username and password
+pairs separated by a space. Press ENTER to get an OK or ERR message.
+Make sure pressing <CTRL><D> behaves the same as a carriage return.
+Make sure pressing <CTRL><C> aborts the program.
+
+Test that entering no details does not result in an OK or ERR message.
+Test that entering an invalid username and password results in an ERR message.
+Note that if NT guest user access is allowed on the PDC, an OK message
+may be returned instead of ERR.
+Test that entering an valid username and password results in an OK message.
+Test that entering a guest username and password returns the correct
+response for the site's access policy.
+
+
+===============
+Contact details
+===============
+
+To contact the maintainer of this package, e-mail on squidnt@serassio.it.
+The latest version may be found on http://www.serassio.it/SquidNT.htm.
Index: squid/helpers/basic_auth/win32_locallogon/valid.c
diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/valid.c:1.1.6.3
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/basic_auth/win32_locallogon/valid.c	Sun Sep 29 09:31:17 2002
@@ -0,0 +1,164 @@
+/*
+  NT_auth -  Version 2.0
+
+  Modified to act as a Squid authenticator module.
+  Removed all Pike stuff.
+  Returns OK for a successful authentication, or ERR upon error.
+
+  Guido Serassio, Torino - Italy
+
+  Uses code from -
+    Antonino Iannella 2000
+    Andrew Tridgell 1997
+    Richard Sharpe 1996
+    Bill Welliver 1999
+
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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"
+
+/* Check if we try to compile on a Windows Platform */
+#if defined(_SQUID_CYGWIN_) || defined(_SQUID_MSWIN_)
+
+#if defined(_SQUID_CYGWIN_)
+#include <wchar.h>
+#endif
+#include <windows.h>
+#include <lm.h>
+#include "valid.h"
+
+char Default_NTDomain[256] = NTV_DEFAULT_DOMAIN;
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Group(char *UserName, char *Group)
+{
+    int result = FALSE;
+    WCHAR wszUserName[256];	// Unicode user name
+    WCHAR wszGroup[256];	// Unicode Group
+
+    LPLOCALGROUP_USERS_INFO_0 pBuf = NULL;
+    LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
+    DWORD dwLevel = 0;
+    DWORD dwFlags = LG_INCLUDE_INDIRECT;
+    DWORD dwPrefMaxLen = -1;
+    DWORD dwEntriesRead = 0;
+    DWORD dwTotalEntries = 0;
+    NET_API_STATUS nStatus;
+    DWORD i;
+    DWORD dwTotalCount = 0;
+
+/* Convert ANSI User Name and Group to Unicode */
+
+    MultiByteToWideChar(CP_ACP, 0, UserName,
+	strlen(UserName) + 1, wszUserName,
+	sizeof(wszUserName) / sizeof(wszUserName[0]));
+    MultiByteToWideChar(CP_ACP, 0, Group,
+	strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
+
+    /*
+     * Call the NetUserGetLocalGroups function 
+	 * specifying information level 0.
+	 * 
+	 * The LG_INCLUDE_INDIRECT flag specifies that the 
+	 * function should also return the names of the local 
+	 * groups in which the user is indirectly a member.
+	 */
+	nStatus = NetUserGetLocalGroups(NULL,
+	    wszUserName,
+	    dwLevel,
+	    dwFlags,
+	    (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries);
+	/*
+	 * If the call succeeds,
+	 */
+    if (nStatus == NERR_Success) {
+	if ((pTmpBuf = pBuf) != NULL) {
+	    for (i = 0; i < dwEntriesRead; i++) {
+		if (pTmpBuf == NULL) {
+		    result = FALSE;
+		    break;
+		}
+		if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) {
+		    result = TRUE;
+		    break;
+		}
+		pTmpBuf++;
+		dwTotalCount++;
+	    }
+	}
+    } else
+	    result = FALSE;
+/*
+ * Free the allocated memory.
+ */
+    if (pBuf != NULL)
+	NetApiBufferFree(pBuf);
+    return result;
+}
+
+/* Valid_User return codes -
+   0 - User authenticated successfully.
+   1 - Server error.
+   2 - Protocol error.
+   3 - Logon error; Incorrect password or username given.
+*/
+
+int
+Valid_User(char *UserName, char *Password, char *Group)
+{
+    int result = NTV_LOGON_ERROR;
+    char NTDomain[256];
+    char *domain_qualify;
+    char DomainUser[256];
+    char User[256];
+
+    strcpy(NTDomain, UserName);
+    if ((domain_qualify = strchr(NTDomain, '\\')) == NULL) {
+	strcpy(User, NTDomain);
+	strcpy(NTDomain, Default_NTDomain);
+    } else {
+	strcpy(User, domain_qualify + 1);
+	domain_qualify[0] = '\0';
+    }
+    /* Log the client on to the local computer. */
+    if (!SSPLogonUser(User, Password, NTDomain)) {
+	result = NTV_LOGON_ERROR;
+    } else {
+	result = NTV_NO_ERROR;
+	if (strcmp(NTDomain, NTV_DEFAULT_DOMAIN) == 0)
+	    strcpy(DomainUser, User);
+	else {
+	    strcpy(DomainUser, NTDomain);
+	    strcat(DomainUser, "\\");
+	    strcat(DomainUser, User);
+	}
+	if (UseAllowedGroup) {
+	    if (!Valid_Group(DomainUser, NTAllowedGroup)) {
+		result = NTV_LOGON_ERROR;
+	    }
+	}
+	if (UseDisallowedGroup) {
+	    if (Valid_Group(DomainUser, NTDisAllowedGroup)) {
+		result = NTV_LOGON_ERROR;
+	    }
+	}
+    }
+    return result;
+}
+#else  /* NON Windows Platform !!! */
+
+#error NON WINDOWS PLATFORM
+
+#endif
Index: squid/helpers/basic_auth/win32_locallogon/valid.h
diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/valid.h:1.1.6.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/basic_auth/win32_locallogon/valid.h	Tue Jul 16 06:34:36 2002
@@ -0,0 +1,55 @@
+/*
+  NT_auth -  Version 2.0
+
+  Modified to act as a Squid authenticator module.
+  Removed all Pike stuff.
+  Returns OK for a successful authentication, or ERR upon error.
+
+  Guido Serassio, Torino - Italy
+
+  Uses code from -
+    Antonino Iannella 2000
+    Andrew Tridgell 1997
+    Richard Sharpe 1996
+    Bill Welliver 1999
+
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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 _VALID_H_
+#define _VALID_H_
+
+#include "sspwin32.h"
+
+/* SMB User verification function */
+
+#define NTV_NO_ERROR 0
+#define NTV_SERVER_ERROR 1
+#define NTV_PROTOCOL_ERROR 2
+#define NTV_LOGON_ERROR 3
+
+#ifndef LOGON32_LOGON_NETWORK
+#define LOGON32_LOGON_NETWORK       3
+#endif
+
+#define NTV_DEFAULT_DOMAIN "."
+
+extern char * NTAllowedGroup;
+extern char * NTDisAllowedGroup;
+extern int UseDisallowedGroup;
+extern int UseAllowedGroup;
+extern char Default_NTDomain[256];
+
+int Valid_User(char *,char *, char *);
+
+#endif
Index: squid/helpers/basic_auth/winbind/wb_basic_auth.c
diff -u squid/helpers/basic_auth/winbind/wb_basic_auth.c:1.6 squid/helpers/basic_auth/winbind/wb_basic_auth.c:1.2.12.5
--- squid/helpers/basic_auth/winbind/wb_basic_auth.c:1.6	Sun Aug 11 18:49:45 2002
+++ squid/helpers/basic_auth/winbind/wb_basic_auth.c	Sat Aug 24 00:08:18 2002
@@ -113,8 +113,8 @@
 	
     c=memchr(buf,'\n',BUFFER_SIZE);
     if (c) {
-	*c = '\0';
-	length = c-buf;
+	*c='\0';
+	length=c-buf;
     } else {
 	err = 1;
 	return;
Index: squid/helpers/external_acl/Makefile.am
diff -u squid/helpers/external_acl/Makefile.am:1.2 squid/helpers/external_acl/Makefile.am:1.2.12.2
--- squid/helpers/external_acl/Makefile.am:1.2	Sun Jul  7 11:43:53 2002
+++ squid/helpers/external_acl/Makefile.am	Tue Jul 16 06:34:36 2002
@@ -3,5 +3,5 @@
 #  $Id$
 #
 
-DIST_SUBDIRS	= ip_user ldap_group unix_group wbinfo_group winbind_group
+DIST_SUBDIRS	= ip_user ldap_group unix_group wbinfo_group winbind_group win32_group
 SUBDIRS		= @EXTERNAL_ACL_HELPERS@
Index: squid/helpers/external_acl/win32_group/.cvsignore
diff -u /dev/null squid/helpers/external_acl/win32_group/.cvsignore:1.1.6.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/external_acl/win32_group/.cvsignore	Tue Jul 16 06:34:36 2002
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/helpers/external_acl/win32_group/Makefile.am
diff -u /dev/null squid/helpers/external_acl/win32_group/Makefile.am:1.1.2.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/external_acl/win32_group/Makefile.am	Tue Jul 16 06:34:36 2002
@@ -0,0 +1,17 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id$
+#
+#  Uncomment and customize the following to suit your needs:
+#
+
+
+libexec_PROGRAMS = win32_check_group
+
+win32_check_group_SOURCES = win32_check_group.c 
+
+INCLUDES = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
+    -I$(top_srcdir)/src
+
+LDADD	= -L$(top_builddir)/lib -lmiscutil -lnetapi32 -ladvapi32
Index: squid/helpers/external_acl/win32_group/readme.txt
diff -u /dev/null squid/helpers/external_acl/win32_group/readme.txt:1.1.2.3
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/external_acl/win32_group/readme.txt	Sun Sep 29 09:31:17 2002
@@ -0,0 +1,71 @@
+
+This is the readme.txt file for win32_check_group, an external
+helper fo the External ACL Scheme for Squid.
+
+
+This helper must be used in with an authentication scheme, tipcally basic
+or NTLM based on Windows NT/2000 domain users. 
+It reads from the standard input the domain username and a list of groups
+and tries to match it against the groups membership of the specified username.
+
+
+==============
+Program Syntax
+==============
+
+win32_check_group [-Gdh]
+
+-G start helper in Global Group mode
+-d enable debug mode
+-h this message
+
+
+================
+squid.conf usage
+================
+
+external_acl_type NT_global_group %LOGIN /usr/local/squid/libexec/win32_check_group -G
+external_acl_type NT_local_group %LOGIN /usr/local/squid/libexec/win32_check_group
+
+acl GProxyUsers external NT_global_group GProxyUsers
+acl LProxyUsers external NT_local_group LProxyUsers
+acl password proxy_auth REQUIRED
+
+http_access allow password GProxyUsers
+http_access allow password LProxyUsers
+http_access deny all
+
+In the previous example all validated NT users member of GProxyUsers Global 
+domain group or member of LProxyUsers machine local group are allowed to 
+use the cache.
+
+Groups with spaces in name must be quoted, for example "Domain Users"
+
+NOTES: 
+- The group name comparation is case sensitive, so group name
+  must be specified with same case as in the NT/2000 Domain.
+- Native WIN32 NTLM and Basic Helpers must be used without the
+  -a & -d switches.
+
+Refer to Squid documentation for the more details on squid.conf.
+
+
+=======
+Testing
+=======
+
+I strongly urge that win32_check_group is tested prior to being used in a 
+production environment. It may behave differently on different platforms.
+To test it, run it from the command line. Enter username and group
+pairs separated by a space (username must entered with domain\\username
+syntax). Press ENTER to get an OK or ERR message.
+Make sure pressing <CTRL><D> behaves the same as a carriage return.
+Make sure pressing <CTRL><C> aborts the program.
+
+Test that entering no details does not result in an OK or ERR message.
+Test that entering an invalid username and group results in an ERR message.
+Test that entering an valid username and group results in an OK message.
+
+--
+Serassio Guido
+squidnt@serassio.it
Index: squid/helpers/external_acl/win32_group/win32_check_group.c
diff -u /dev/null squid/helpers/external_acl/win32_group/win32_check_group.c:1.1.2.5
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/external_acl/win32_group/win32_check_group.c	Tue Oct 22 12:33:25 2002
@@ -0,0 +1,523 @@
+/*
+ * $Id$
+ *
+ * This is a helper for the external ACL interface for Squid Cache
+ * Copyright (C) 2002 Guido Serassio <squidnt@serassio.it>
+ * Based on previous work of Rodrigo Albani de Campos
+ * 
+ * It reads from the standard input the domain username and a list of groups
+ * and tries to match it against the groups membership of the specified username.
+ * Returns `OK' if the user belongs to a group or `ERR' otherwise, as 
+ * described on http://devel.squid-cache.org/external_acl/config.html
+ *
+ *  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 <wchar.h>
+#endif
+#undef assert
+#include <assert.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntsecapi.h>
+
+#define BUFSIZE 8192		/* the stdin buffer size */
+int use_global = 0;
+char debug_enabled = 0;
+char *myname;
+pid_t mypid;
+char * machinedomain;
+
+#include "win32_check_group.h"
+
+char *
+strwordtok(char *buf, char **t)
+{
+    unsigned char *word = NULL;
+    unsigned char *p = (unsigned char *) buf;
+    unsigned char *d;
+    unsigned char ch;
+    int quoted = 0;
+    if (!p)
+	p = (unsigned char *) *t;
+    if (!p)
+	goto error;
+    while (*p && isspace(*p))
+	p++;
+    if (!*p)
+	goto error;
+    word = d = p;
+    while ((ch = *p)) {
+	switch (ch) {
+	case '\\':
+	    p++;
+	    *d++ = ch = *p;
+	    if (ch)
+		p++;
+	    break;
+	case '"':
+	    quoted = !quoted;
+	    p++;
+	    break;
+	default:
+	    if (!quoted && isspace(*p)) {
+		p++;
+		goto done;
+	    }
+	    *d++ = *p++;
+	    break;
+	}
+    }
+  done:
+    *d++ = '\0';
+  error:
+    *t = (char *) p;
+    return (char *) word;
+}
+
+
+char * AllocStrFromLSAStr(LSA_UNICODE_STRING LsaStr)
+{
+    size_t len;
+    static char * target;
+
+    len = LsaStr.Length/sizeof(WCHAR) + 1;
+
+    /* allocate buffer for str + null termination */
+    safe_free(target);
+    target = (char *)xmalloc(len);
+    if (target == NULL)
+	return NULL;
+
+    /* copy unicode buffer */
+    WideCharToMultiByte(CP_ACP, 0, LsaStr.Buffer, LsaStr.Length, target, len, NULL, NULL );
+
+    /* add null termination */
+    target[len-1] = '\0';
+    return target;
+}
+
+
+char * GetDomainName(void)
+
+{
+    LSA_HANDLE PolicyHandle;
+    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS status;
+    PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo;
+    PWKSTA_INFO_100 pwkiWorkstationInfo;
+    DWORD netret;
+    char * DomainName = NULL;
+
+    /* 
+     * Always initialize the object attributes to all zeroes.
+     */ 
+    memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes));
+
+    /* 
+     * You need the local workstation name. Use NetWkstaGetInfo at level
+     * 100 to retrieve a WKSTA_INFO_100 structure.
+     * 
+     * The wki100_computername field contains a pointer to a UNICODE
+     * string containing the local computer name.
+     */ 
+    netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo);
+    if (netret == NERR_Success) {
+	/* 
+	 * We have the workstation name in:
+	 * pwkiWorkstationInfo->wki100_computername
+	 * 
+	 * Next, open the policy object for the local system using
+	 * the LsaOpenPolicy function.
+	 */ 
+	status = LsaOpenPolicy(
+                 NULL,
+                 &ObjectAttributes,
+                 GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION,
+                 &PolicyHandle
+                );
+	
+	/* 
+	 * Error checking.
+	 */ 
+	if (status) {
+	    debug("OpenPolicy Error: %ld\n", status);
+	} else {
+
+	    /* 
+	     * You have a handle to the policy object. Now, get the
+	     * domain information using LsaQueryInformationPolicy.
+	     */ 
+	    status = LsaQueryInformationPolicy(PolicyHandle,
+		PolicyPrimaryDomainInformation,
+		(PVOID *)&ppdiDomainInfo);
+	    if (status) {
+		debug("LsaQueryInformationPolicy Error: %ld\n", status);
+	    } else  {
+
+		/* Get name in useable format */
+		DomainName = AllocStrFromLSAStr(ppdiDomainInfo->Name);
+
+		/* 
+		 * Check the Sid pointer, if it is null, the
+		 * workstation is either a stand-alone computer
+		 * or a member of a workgroup.
+		 */ 
+		if (ppdiDomainInfo->Sid) {
+
+		    /* 
+		     * Member of a domain. Display it in debug mode.
+		     */ 
+		    debug("Member of Domain %s\n",DomainName);
+		} else {
+		    DomainName = NULL;
+                }
+	    }
+	}
+
+	/* 
+	 * Clean up all the memory buffers created by the LSA and
+	 * Net* APIs.
+	 */ 
+	NetApiBufferFree(pwkiWorkstationInfo);
+	LsaFreeMemory((LPVOID)ppdiDomainInfo);
+    } else 
+	debug("NetWkstaGetInfo Error: %ld\n", netret);
+    return DomainName;
+}
+
+/* returns 0 on match, -1 if no match */
+static int wcstrcmparray(const wchar_t *str, const char **array)
+{
+    WCHAR wszGroup[256];	// Unicode Group
+
+    while (*array) {
+	MultiByteToWideChar(CP_ACP, 0, *array,
+	    strlen(*array) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
+    	debug("Windows group: %S, Squid group: %S\n", str, wszGroup);
+	if (wcscmp(str, wszGroup) == 0)
+	    return 0;
+	array++;
+    }
+    return -1;
+}
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Local_Groups(char *UserName, const char **Groups)
+{
+    int result = 0;
+    WCHAR wszUserName[256];	// Unicode user name
+
+    LPLOCALGROUP_USERS_INFO_0 pBuf = NULL;
+    LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
+    DWORD dwLevel = 0;
+    DWORD dwFlags = LG_INCLUDE_INDIRECT;
+    DWORD dwPrefMaxLen = -1;
+    DWORD dwEntriesRead = 0;
+    DWORD dwTotalEntries = 0;
+    NET_API_STATUS nStatus;
+    DWORD i;
+    DWORD dwTotalCount = 0;
+
+/* Convert ANSI User Name and Group to Unicode */
+
+    MultiByteToWideChar(CP_ACP, 0, UserName,
+	strlen(UserName) + 1, wszUserName, sizeof(wszUserName) / sizeof(wszUserName[0]));
+
+    /*
+     * Call the NetUserGetLocalGroups function 
+	 * specifying information level 0.
+	 * 
+	 * The LG_INCLUDE_INDIRECT flag specifies that the 
+	 * function should also return the names of the local 
+	 * groups in which the user is indirectly a member.
+	 */
+    nStatus = NetUserGetLocalGroups(
+	NULL,
+        wszUserName,
+        dwLevel,
+        dwFlags,
+        (LPBYTE *) & pBuf,
+	dwPrefMaxLen,
+	&dwEntriesRead,
+	&dwTotalEntries);
+	/*
+	 * If the call succeeds,
+	 */
+    if (nStatus == NERR_Success) {
+	if ((pTmpBuf = pBuf) != NULL) {
+	    for (i = 0; i < dwEntriesRead; i++) {
+		assert(pTmpBuf != NULL);
+		if (pTmpBuf == NULL) {
+		    result = 0;
+		    break;
+		}
+		if (wcstrcmparray(pTmpBuf->lgrui0_name, Groups) == 0) {
+		    result = 1;
+		    break;
+		}
+		pTmpBuf++;
+		dwTotalCount++;
+	    }
+	}
+    } else
+        result = 0;
+/*
+ * Free the allocated memory.
+ */
+    if (pBuf != NULL)
+	NetApiBufferFree(pBuf);
+    return result;
+}
+
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Global_Groups(char *UserName, const char **Groups)
+{
+    int result = 0;
+    WCHAR wszUserName[256];	// Unicode user name
+    WCHAR wszLocalDomain[256];	// Unicode Local Domain
+    WCHAR wszUserDomain[256];	// Unicode User Domain
+
+    char NTDomain[256];
+    char *domain_qualify;
+    char User[256];
+
+    LPCWSTR LclDCptr = NULL;
+    LPCWSTR UsrDCptr = NULL;
+    LPGROUP_USERS_INFO_0 pBuf = NULL;
+    LPGROUP_USERS_INFO_0 pTmpBuf;
+    DWORD dwLevel = 0;
+    DWORD dwPrefMaxLen = -1;
+    DWORD dwEntriesRead = 0;
+    DWORD dwTotalEntries = 0;
+    NET_API_STATUS nStatus;
+    DWORD i;
+    DWORD dwTotalCount = 0;
+
+    strcpy(NTDomain, UserName);
+    if ((domain_qualify = strchr(NTDomain, '\\')) == NULL) {
+	return result;
+    } else {
+	strcpy(User, domain_qualify + 1);
+	domain_qualify[0] = '\0';
+	_strlwr(NTDomain);
+    }
+
+/* Convert ANSI User Name and Group to Unicode */
+
+    MultiByteToWideChar(CP_ACP, 0, User,
+	strlen(User) + 1, wszUserName,
+	sizeof(wszUserName) / sizeof(wszUserName[0]));
+    MultiByteToWideChar(CP_ACP, 0, machinedomain,
+	strlen(machinedomain) + 1, wszLocalDomain, sizeof(wszLocalDomain) / sizeof(wszLocalDomain[0]));
+
+    nStatus = NetGetAnyDCName(
+	NULL,
+	wszLocalDomain,
+	(LPBYTE *) & LclDCptr);
+
+    if (nStatus == NERR_Success) {
+	if (strcmp(NTDomain, machinedomain) != 0) {
+	    MultiByteToWideChar(CP_ACP, 0, NTDomain,
+		strlen(NTDomain) + 1, wszUserDomain, sizeof(wszUserDomain) / sizeof(wszUserDomain[0]));
+	    nStatus = NetGetAnyDCName(
+		LclDCptr,
+		wszUserDomain,
+		(LPBYTE *) & UsrDCptr);
+	    if (nStatus != NERR_Success) {
+		fprintf(stderr, "%s Can't find DC for domain %s\n", myname, NTDomain);
+		if (LclDCptr != NULL)
+		    NetApiBufferFree((LPVOID) LclDCptr);
+		if (UsrDCptr != NULL)
+		    NetApiBufferFree((LPVOID) UsrDCptr);
+		return result;
+	    }
+	} else
+	    UsrDCptr = LclDCptr;
+
+	/*
+	 * Call the NetUserGetLocalGroups function 
+	     * specifying information level 0.
+	     * 
+	     * The LG_INCLUDE_INDIRECT flag specifies that the 
+	     * function should also return the names of the local 
+	     * groups in which the user is indirectly a member.
+	     */
+	    nStatus = NetUserGetGroups(UsrDCptr,
+		wszUserName,
+		dwLevel,
+		(LPBYTE *) & pBuf,
+		dwPrefMaxLen,
+		&dwEntriesRead,
+		&dwTotalEntries);
+	    /*
+	     * If the call succeeds,
+	     */
+	if (nStatus == NERR_Success) {
+	    if ((pTmpBuf = pBuf) != NULL) {
+		for (i = 0; i < dwEntriesRead; i++) {
+		    assert(pTmpBuf != NULL);
+		    if (pTmpBuf == NULL) {
+			result = 0;
+			break;
+		    }
+		    if (wcstrcmparray(pTmpBuf->grui0_name, Groups) == 0) {
+			result = 1;
+			break;
+		    }
+		    pTmpBuf++;
+		    dwTotalCount++;
+		}
+	    }
+	} else
+		result = 0;
+    } else {
+	fprintf(stderr, "%s Can't find DC for domain %s\n", myname, machinedomain);
+    }
+    /*
+     * Free the allocated memory.
+     */
+    if (pBuf != NULL)
+        NetApiBufferFree(pBuf);
+    if (LclDCptr != NULL)
+        NetApiBufferFree((LPVOID) LclDCptr);
+    if (UsrDCptr != NULL)
+        NetApiBufferFree((LPVOID) UsrDCptr);
+    return result;
+}
+
+static void
+usage(char *program)
+{
+    fprintf(stderr,"Usage: %s [-d][-G][-h]\n"
+	    	" -d      enable debugging\n"
+	    	" -G      enable Domain Global group mode\n"
+		" -h      this message\n",
+		program);
+}
+
+void
+process_options(int argc, char *argv[])
+{
+    int opt;
+
+    opterr = 0;
+    while (-1 != (opt = getopt(argc, argv, "Gdh"))) {
+	switch (opt) {
+	case 'G':
+	    use_global = 1;
+	    break;
+	case 'd':
+	    debug_enabled = 1;
+	    break;
+	case 'h':
+	    usage(argv[0]);
+	    exit(0);
+	case '?':
+	    opt = optopt;
+	    /* fall thru to default */
+	default:
+	    fprintf(stderr, "%s Unknown option: -%c. Exiting\n", myname, opt);
+	    usage(argv[0]);
+	    exit(1);
+	    break;		/* not reached */
+	}
+    }
+    return;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+    char *p, *t;
+    char buf[BUFSIZE];
+    char *username;
+    char *group;
+    int err = 0;
+    const char *groups[512];
+    int n;
+
+    if (argc > 0) {	/* should always be true */
+	myname=strrchr(argv[0],'/');
+	if (myname==NULL)
+	    myname=argv[0];
+    } else {
+        myname="(unknown)";
+    }
+    mypid=getpid();
+
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    /* Check Command Line */
+    process_options(argc, argv);
+
+    if (use_global) {
+        if ((machinedomain = GetDomainName()) == NULL) {
+	    fprintf(stderr, "%s Can't read machine domain\n", myname);
+	    exit(1);
+	}
+	_strlwr(machinedomain);
+    }
+
+    debug("External ACL win32 group helper build " __DATE__ ", " __TIME__
+    " starting up...\n");
+
+    /* Main Loop */
+    while (fgets (buf, BUFSIZE, stdin))
+    {
+	if (NULL == strchr(buf, '\n')) {
+	    err = 1;
+	    continue;
+	}
+	if (err) {
+	    fprintf(stderr, "Oversized message\n");
+	    goto error;
+	}
+	
+	if ((p = strchr(buf, '\n')) != NULL)
+	    *p = '\0';		/* strip \n */
+	if ((p = strchr(buf, '\r')) != NULL)
+	    *p = '\0';		/* strip \r */
+
+	debug("Got '%s' from Squid (length: %d).\n",buf,sizeof(buf));
+
+	if (buf[0] == '\0') {
+	    fprintf(stderr, "Invalid Request\n");
+	    goto error;
+	}
+
+	username = strwordtok(buf, &t);
+	for (n = 0; (group = strwordtok(NULL, &t)) != NULL; n++)
+	    groups[n] = group;
+	groups[n] = NULL;
+
+	if ((use_global ? Valid_Global_Groups(username, groups) : Valid_Local_Groups(username, groups))) {
+	    printf ("OK\n");
+	} else {
+error:
+	    printf ("ERR\n");
+	}
+	err = 0;
+    }
+    return 0;
+}
Index: squid/helpers/external_acl/win32_group/win32_check_group.h
diff -u /dev/null squid/helpers/external_acl/win32_group/win32_check_group.h:1.1.8.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/external_acl/win32_group/win32_check_group.h	Sun Sep 29 09:31:17 2002
@@ -0,0 +1,77 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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.
+ */
+
+#undef debug
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+/************* END CONFIGURATION ***************/
+
+#include <sys/types.h>
+
+/* Debugging stuff */
+
+#ifdef __GNUC__			/* this is really a gcc-ism */
+#ifdef DEBUG
+#include <stdio.h>
+#include <unistd.h>
+static char *__foo;
+extern char debug_enabled;
+#define debug(X...) if (debug_enabled) { \
+                    fprintf(stderr,"%s[%d](%s:%d): ", myname, mypid, \
+                    ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\
+                    __LINE__);\
+                    fprintf(stderr,X); }
+#else /* DEBUG */
+#define debug(X...)		/* */
+#endif /* DEBUG */
+#else /* __GNUC__ */
+extern char debug_enabled;
+static void
+debug(char *format,...) 
+{
+#ifdef DEBUG
+#ifdef _SQUID_MSWIN_
+    if (debug_enabled) {
+	va_list args;
+
+	va_start(args,format);
+	fprintf(stderr, "%s[%d]: ", myname, mypid);
+	vfprintf(stderr, format, args);
+	fprintf(stderr, "\n");
+	va_end(args);
+    }
+#endif /* _SQUID_MSWIN_ */
+#endif /* DEBUG */
+}
+#endif /* __GNUC__ */
+
+
+/* A couple of harmless helper macros */
+#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n");
+#ifdef __GNUC__
+#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#else
+/* no gcc, no debugging. varargs macros are a gcc extension */
+#define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#endif
Index: squid/helpers/ntlm_auth/Makefile.am
diff -u squid/helpers/ntlm_auth/Makefile.am:1.2 squid/helpers/ntlm_auth/Makefile.am:1.2.12.2
--- squid/helpers/ntlm_auth/Makefile.am:1.2	Fri Jun 28 08:00:33 2002
+++ squid/helpers/ntlm_auth/Makefile.am	Tue Jul 16 06:22:02 2002
@@ -3,5 +3,5 @@
 #  $Id$
 #
 
-DIST_SUBDIRS	= fakeauth no_check SMB winbind
+DIST_SUBDIRS	= fakeauth no_check SMB winbind NTLMSSP-WIN32
 SUBDIRS		= @NTLM_AUTH_HELPERS@
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore
diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore:1.1.8.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore	Tue Jul 16 06:22:02 2002
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am
diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am:1.1.8.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am	Tue Jul 16 06:34:36 2002
@@ -0,0 +1,14 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id$
+#
+
+libexec_PROGRAMS = ntlm_auth
+
+ntlm_auth_SOURCES = libntlmssp.c ntlm_auth.c ntlm.h
+
+INCLUDES      = -I. -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+LDADD		= -L$(top_builddir)/lib -lntlmauth -lsspwin32 -lnetapi32 \
+		  -ladvapi32 -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c
diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c:1.1.8.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c	Sun Sep 29 09:31:18 2002
@@ -0,0 +1,380 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli and Robert Collins
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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.
+ */
+
+typedef unsigned char uchar;
+
+#include "squid.h"
+#include "ntlm.h"
+#include <lm.h>
+#include <ntsecapi.h>
+
+#ifdef DEBUG
+#define debug_dump_ntlmssp_flags dump_ntlmssp_flags
+#else /* DEBUG */
+#define debug_dump_ntlmssp_flags(X)	/* empty */
+#endif /* DEBUG */
+
+#define ENCODED_PASS_LEN 24
+static char challenge[NONCE_LEN];
+static char lmencoded_empty_pass[ENCODED_PASS_LEN],
+	ntencoded_empty_pass[ENCODED_PASS_LEN];
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Group(char *UserName, char *Group)
+{
+    int result = FALSE;
+    WCHAR wszUserName[256];	// Unicode user name
+    WCHAR wszGroup[256];	// Unicode Group
+
+    LPLOCALGROUP_USERS_INFO_0 pBuf = NULL;
+    LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
+    DWORD dwLevel = 0;
+    DWORD dwFlags = LG_INCLUDE_INDIRECT;
+    DWORD dwPrefMaxLen = -1;
+    DWORD dwEntriesRead = 0;
+    DWORD dwTotalEntries = 0;
+    NET_API_STATUS nStatus;
+    DWORD i;
+    DWORD dwTotalCount = 0;
+
+/* Convert ANSI User Name and Group to Unicode */
+
+    MultiByteToWideChar(CP_ACP, 0, UserName,
+	strlen(UserName) + 1, wszUserName,
+	sizeof(wszUserName) / sizeof(wszUserName[0]));
+    MultiByteToWideChar(CP_ACP, 0, Group,
+	strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
+
+    /*
+     * Call the NetUserGetLocalGroups function 
+	 * specifying information level 0.
+	 * 
+	 * The LG_INCLUDE_INDIRECT flag specifies that the 
+	 * function should also return the names of the local 
+	 * groups in which the user is indirectly a member.
+	 */
+	nStatus = NetUserGetLocalGroups(NULL,
+	    wszUserName,
+	    dwLevel,
+	    dwFlags,
+	    (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries);
+	/*
+	 * If the call succeeds,
+	 */
+    if (nStatus == NERR_Success) {
+	if ((pTmpBuf = pBuf) != NULL) {
+	    for (i = 0; i < dwEntriesRead; i++) {
+		if (pTmpBuf == NULL) {
+		    result = FALSE;
+		    break;
+		}
+		if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) {
+		    result = TRUE;
+		    break;
+		}
+		pTmpBuf++;
+		dwTotalCount++;
+	    }
+	}
+    } else
+	    result = FALSE;
+/*
+ * Free the allocated memory.
+ */
+    if (pBuf != NULL)
+	NetApiBufferFree(pBuf);
+    return result;
+}
+
+/* returns 0 on success, > 0 on failure */
+static int
+init_challenge(void)
+{
+    static unsigned hash;
+    int r;
+    int i;
+
+    r = (int) rand();
+    r = (hash ^ r) + r;
+    for (i = 0; i < NONCE_LEN; i++) {
+	challenge[i] = r;
+	r = (r >> 2) ^ r;
+    }
+    hash = r;
+    return 0;
+}
+
+char * AllocStrFromLSAStr(LSA_UNICODE_STRING LsaStr)
+{
+    size_t len;
+    static char * target;
+
+    len = LsaStr.Length/sizeof(WCHAR) + 1;
+
+    /* allocate buffer for str + null termination */
+    safe_free(target);
+    target = (char *)xmalloc(len);
+    if (target == NULL)
+	return NULL;
+
+    /* copy unicode buffer */
+    WideCharToMultiByte(CP_ACP, 0, LsaStr.Buffer, LsaStr.Length, target, len, NULL, NULL );
+
+    /* add null termination */
+    target[len-1] = '\0';
+    return target;
+}
+
+char * GetDomainName(void)
+
+{
+    LSA_HANDLE PolicyHandle;
+    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS status;
+    PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo;
+    PWKSTA_INFO_100 pwkiWorkstationInfo;
+    DWORD netret;
+    char * DomainName = NULL;
+
+    /* 
+     * Always initialize the object attributes to all zeroes.
+     */ 
+    memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes));
+
+    /* 
+     * You need the local workstation name. Use NetWkstaGetInfo at level
+     * 100 to retrieve a WKSTA_INFO_100 structure.
+     * 
+     * The wki100_computername field contains a pointer to a UNICODE
+     * string containing the local computer name.
+     */ 
+    netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo);
+    if (netret == NERR_Success) {
+	/* 
+	 * We have the workstation name in:
+	 * pwkiWorkstationInfo->wki100_computername
+	 * 
+	 * Next, open the policy object for the local system using
+	 * the LsaOpenPolicy function.
+	 */ 
+	status = LsaOpenPolicy(
+                 NULL,
+                 &ObjectAttributes,
+                 GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION,
+                 &PolicyHandle
+                );
+	
+	/* 
+	 * Error checking.
+	 */ 
+	if (status) {
+	    debug("OpenPolicy Error: %ld\n", status);
+	} else {
+
+	    /* 
+	     * You have a handle to the policy object. Now, get the
+	     * domain information using LsaQueryInformationPolicy.
+	     */ 
+	    status = LsaQueryInformationPolicy(PolicyHandle,
+		PolicyPrimaryDomainInformation,
+		(void **)&ppdiDomainInfo);
+	    if (status) {
+		debug("LsaQueryInformationPolicy Error: %ld\n", status);
+	    } else  {
+
+		/* Get name in useable format */
+		DomainName = AllocStrFromLSAStr(ppdiDomainInfo->Name);
+
+		/* 
+		 * Check the Sid pointer, if it is null, the
+		 * workstation is either a stand-alone computer
+		 * or a member of a workgroup.
+		 */ 
+		if (ppdiDomainInfo->Sid) {
+
+		    /* 
+		     * Member of a domain. Display it in debug mode.
+		     */ 
+		    debug("Member of Domain %s\n",DomainName);
+		} else {
+		    DomainName = NULL;
+                }
+	    }
+	}
+
+	/* 
+	 * Clean up all the memory buffers created by the LSA and
+	 * Net* APIs.
+	 */ 
+	NetApiBufferFree(pwkiWorkstationInfo);
+	LsaFreeMemory((LPVOID)ppdiDomainInfo);
+    } else 
+	debug("NetWkstaGetInfo Error: %ld\n", netret);
+    return DomainName;
+}
+
+const char *
+make_challenge(void)
+{
+    static char ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+    static char * DomainName;
+    DWORD CNsize = MAX_COMPUTERNAME_LENGTH + 1;
+
+    GetComputerName(ComputerName, &CNsize);
+    uc(ComputerName);
+    if ((DomainName=GetDomainName()) == NULL)
+	return NULL;
+    uc(DomainName);
+    debug("ComputerName:%s DomainName: %s",ComputerName,DomainName);
+    if (init_challenge() > 0)
+	return NULL;
+    return ntlm_make_challenge(DomainName, ComputerName, challenge, NONCE_LEN);
+}
+
+int ntlm_errno;
+static char credentials[1024];	/* we can afford to waste */
+
+
+/* Fetches the user's credentials from the challenge.
+ * Returns NULL if domain or user is not defined
+ * No identity control is performed.
+ * WARNING! The result is static storage, shared with ntlm_check_auth
+ */
+char *
+fetch_credentials(ntlm_authenticate * auth, int auth_length)
+{
+    char *p = credentials;
+    lstring tmp;
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain);
+    if (tmp.str == NULL)
+	return NULL;
+    memcpy(p, tmp.str, tmp.l);
+    p += tmp.l;
+    *p++ = '\\';
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user);
+    if (tmp.str == NULL)
+	return NULL;
+    *(p + tmp.l) = '\0';
+    return credentials;
+}
+
+/* returns NULL on failure, or a pointer to
+ * the user's credentials (domain\\username)
+ * upon success. WARNING. It's pointing to static storage.
+ * In case of problem sets as side-effect ntlm_errno to one of the
+ * codes defined in ntlm.h
+ */
+char *
+ntlm_check_auth(ntlm_authenticate * auth, int auth_length)
+{
+    int rv;
+    char pass[25] /*, encrypted_pass[40] */;
+    char *domain = credentials;
+    char *user;
+    lstring tmp;
+
+/*      debug("fetching domain\n"); */
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain);
+    if (tmp.str == NULL || tmp.l == 0) {
+	debug("No domain supplied. Returning no-auth\n");
+	ntlm_errno = NTLM_LOGON_ERROR;
+	return NULL;
+    }
+    memcpy(domain, tmp.str, tmp.l);
+    user = domain + tmp.l;
+    *user++ = '\0';
+
+/*      debug("fetching user name\n"); */
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user);
+    if (tmp.str == NULL || tmp.l == 0) {
+	debug("No username supplied. Returning no-auth\n");
+	ntlm_errno = NTLM_LOGON_ERROR;
+	return NULL;
+    }
+    memcpy(user, tmp.str, tmp.l);
+    *(user + tmp.l) = '\0';
+
+		
+		/* Authenticating against the NT response doesn't seem to work... */
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->lmresponse);
+    if (tmp.str == NULL || tmp.l == 0) {
+	fprintf(stderr, "No auth at all. Returning no-auth\n");
+	ntlm_errno = NTLM_LOGON_ERROR;
+	return NULL;
+    }
+		
+    memcpy(pass, tmp.str, tmp.l);
+    pass[25] = '\0';
+
+#if 1
+		debug ("Empty LM pass detection: user: '%s', ours:'%s', his: '%s'"
+					 "(length: %d)\n",
+					 user,lmencoded_empty_pass,tmp.str,tmp.l);
+		if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) {
+			fprintf(stderr,"Empty LM password supplied for user %s\\%s. "
+							"No-auth\n",domain,user);
+			ntlm_errno=NTLM_LOGON_ERROR;
+			return NULL;
+		}
+		
+		tmp = ntlm_fetch_string ((char *) auth, auth_length, &auth->ntresponse);
+		if (tmp.str != NULL && tmp.l != 0) {
+			debug ("Empty NT pass detection: user: '%s', ours:'%s', his: '%s'"
+						 "(length: %d)\n",
+						 user,ntencoded_empty_pass,tmp.str,tmp.l);
+			if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) {
+				fprintf(stderr,"Empty NT password supplied for user %s\\%s. "
+								"No-auth\n",domain,user);
+				ntlm_errno=NTLM_LOGON_ERROR;
+				return NULL;
+			}
+		}
+#endif
+
+		/* TODO: check against empty password!!!!! */
+		
+
+
+    debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass);
+    rv = SSPLogonUser(user, pass, domain);
+
+    debug("Login attempt had result %d\n", rv);
+
+    if (!rv) {			/* failed */
+	ntlm_errno = ss;
+	return NULL;
+    }
+    *(user - 1) = '\\';		/* hack. Performing, but ugly. */
+    
+    if (UseAllowedGroup) {
+	if (!Valid_Group(credentials, NTAllowedGroup)) {
+	    ntlm_errno = NTLM_BAD_NTGROUP;
+	    debug("User %s not in allowed Group %s\n", credentials, NTAllowedGroup);
+	    return NULL;
+	}
+    }
+    if (UseDisallowedGroup) {
+	if (Valid_Group(credentials, NTDisAllowedGroup)) {
+	    ntlm_errno = NTLM_BAD_NTGROUP;
+	    debug("User %s is in denied Group %s\n", credentials, NTDisAllowedGroup);
+	    return NULL;
+	}
+    }
+
+    debug("credentials: %s\n", credentials);
+    return credentials;
+}
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h
diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h:1.1.8.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h	Sun Aug 11 09:45:09 2002
@@ -0,0 +1,108 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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 _NTLM_H_
+#define _NTLM_H_
+
+#include "sspwin32.h"
+#include <windows.h>
+#include <sspi.h>
+#include <security.h>
+#include "ntlmauth.h"
+#undef debug
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+/************* END CONFIGURATION ***************/
+
+#include <sys/types.h>
+
+/* Debugging stuff */
+
+#ifdef __GNUC__			/* this is really a gcc-ism */
+#ifdef DEBUG
+#include <stdio.h>
+#include <unistd.h>
+static char *__foo;
+extern char debug_enabled;
+#define debug(X...) if (debug_enabled) { \
+                    fprintf(stderr,"ntlm-auth[%d](%s:%d): ", getpid(), \
+                    ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\
+                    __LINE__);\
+                    fprintf(stderr,X); }
+#else /* DEBUG */
+#define debug(X...)		/* */
+#endif /* DEBUG */
+#else /* __GNUC__ */
+extern char debug_enabled;
+static void
+debug(char *format,...) 
+{
+#ifdef DEBUG
+#ifdef _SQUID_MSWIN_
+    if (debug_enabled) {
+	va_list args;
+
+	va_start(args,format);
+	fprintf(stderr, "ntlm-auth[%d]: ",getpid());
+	vfprintf(stderr, format, args);
+	fprintf(stderr, "\n");
+	va_end(args);
+    }
+#endif /* _SQUID_MSWIN_ */
+#endif /* DEBUG */
+}
+#endif /* __GNUC__ */
+
+
+/* A couple of harmless helper macros */
+#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n");
+#ifdef __GNUC__
+#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#else
+/* no gcc, no debugging. varargs macros are a gcc extension */
+#define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#endif
+
+extern char * NTAllowedGroup;
+extern char * NTDisAllowedGroup;
+extern int UseDisallowedGroup;
+extern int UseAllowedGroup;
+extern int ntlm_errno;
+
+#define NTLM_NO_ERROR SEC_E_OK
+#define NTLM_SERVER_ERROR SEC_E_NOT_SUPPORTED
+#define NTLM_PROTOCOL_ERROR SEC_E_INCOMPLETE_MESSAGE
+#define NTLM_LOGON_ERROR SEC_E_LOGON_DENIED
+#define NTLM_UNTRUSTED_DOMAIN SEC_E_NO_AUTHENTICATING_AUTHORITY
+#define NTLM_BAD_PROTOCOL SEC_E_BAD_PKGID
+#define NTLM_NOT_CONNECTED SEC_E_SECPKG_NOT_FOUND
+#define NTLM_BAD_NTGROUP -1
+
+extern void uc(char *);
+
+const char *make_challenge(void);
+extern char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length);
+extern char *fetch_credentials(ntlm_authenticate * auth, int auth_length);
+
+#endif /* _NTLM_H_ */
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c
diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c:1.1.8.4
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c	Sun Sep 29 09:31:18 2002
@@ -0,0 +1,320 @@
+/*
+ * $Id$
+ *
+ * This is a helper for NTLM Authentication for Squid Cache
+ * Copyright (C) 2002 Guido Serassio <squidnt@serassio.it>
+ * Based on previous work of Francesco Chemolli and Robert Collins
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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 "ntlm.h"
+
+#define BUFFER_SIZE 10240
+
+
+#ifdef DEBUG
+char error_messages_buffer[BUFFER_SIZE];
+#endif
+
+char load_balance = 0, protocol_pedantic = 0;
+#ifdef NTLM_FAIL_OPEN
+char last_ditch_enabled = 0;
+#endif
+
+char debug_enabled=0;
+
+
+char * NTAllowedGroup;
+char * NTDisAllowedGroup;
+int UseDisallowedGroup = 0;
+int UseAllowedGroup = 0;
+
+/* makes a null-terminated string upper-case. Changes CONTENTS! */
+void
+uc(char *string)
+{
+    char *p = string, c;
+    while ((c = *p)) {
+	*p = toupper(c);
+	p++;
+    }
+}
+
+/* makes a null-terminated string lower-case. Changes CONTENTS! */
+static void
+lc(char *string)
+{
+    char *p = string, c;
+    while ((c = *p)) {
+	*p = tolower(c);
+	p++;
+    }
+}
+
+void
+helperfail(const char *reason)
+{
+    SEND2("BH %s", reason);
+}
+
+void
+send_bh_or_ld(char *bhmessage, ntlm_authenticate * failedauth, int authlen)
+{
+#ifdef NTLM_FAIL_OPEN
+    char *creds = NULL;
+    if (last_ditch_enabled) {
+	creds = fetch_credentials(failedauth, authlen);
+	if (creds) {
+	    lc(creds);
+	    SEND2("LD %s", creds);
+	} else {
+	    SEND("NA last-ditch on, but no credentials");
+	}
+    } else {
+#endif
+	SEND2("BH %s", bhmessage);
+#ifdef NTLM_FAIL_OPEN
+    }
+#endif
+}
+
+/*
+ * options:
+ * -v enables debugging statements if DEBUG was defined at build-time.
+ * -l if specified, changes behavior on failures to last-ditch.
+ * -a can specify a Windows Local Group name allowed to authenticate.
+ * -d can specify a Windows Local Group name not allowed to authenticate.
+ * domain\controller ...
+ */
+char *my_program_name = NULL;
+
+void
+usage()
+{
+    fprintf(stderr,
+#ifdef NTLM_FAIL_OPEN
+	"%s usage:\n%s [-v] [-a UserGroup] [-d UserGroup] [-l] [-h]\n"
+#else
+	"%s usage:\n%s [-v] [-a UserGroup] [-d UserGroup] [-h]\n"
+#endif
+	"-v enables verbose debugging.\n"
+#ifdef NTLM_FAIL_OPEN
+	"-l if specified, changes behavior on failures to last-ditch\n"
+#endif
+	"-a specify a Windows Local Group name allowed to authenticate\n"
+	"-d specify a Windows Local Group name not allowed to authenticate\n"
+	"-h this message\n\n",
+	my_program_name, my_program_name);
+}
+
+
+void
+process_options(int argc, char *argv[])
+{
+    int opt, had_error = 0;
+
+    opterr =0;
+#ifdef NTLM_FAIL_OPEN
+    while (-1 != (opt = getopt(argc, argv, "hvla:d:"))) {
+#else
+    while (-1 != (opt = getopt(argc, argv, "hva:d:"))) {
+#endif
+	switch (opt) {
+	case 'a':
+	    safe_free(NTAllowedGroup);
+	    NTAllowedGroup=xstrdup(optarg);
+	    UseAllowedGroup = 1;
+	    break;
+	case 'd':
+	    safe_free(NTDisAllowedGroup);
+	    NTDisAllowedGroup=xstrdup(optarg);
+	    UseDisallowedGroup = 1;
+	    break;
+#ifdef NTLM_FAIL_OPEN
+	case 'l':
+	    last_ditch_enabled = 1;
+	    break;
+#endif
+	case 'v':
+	    debug_enabled=1;
+	    break;
+	case 'h':
+	    usage();
+	    exit(0);
+	case '?':
+	    opt = optopt;
+	    /* fall thru to default */
+	default:
+	    fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
+	    usage();
+	    had_error = 1;
+	}
+    }
+    if (had_error)
+	exit(1);
+}
+
+/* tries connecting to the domain controllers in the "controllers" ring,
+ * with failover if the adequate option is specified.
+ */
+const char *
+obtain_challenge()
+{
+    const char *ch = NULL;
+
+    debug("attempting challenge retrieval\n");
+    ch = make_challenge();
+    debug("make_challenge returned %p\n", ch);
+    if (ch) {
+	debug("Got it\n");
+	return ch;		/* All went OK, returning */
+    }
+    return NULL;
+}
+
+
+void
+manage_request()
+{
+    ntlmhdr *fast_header;
+    char buf[BUFFER_SIZE];
+    char *c, *decoded, *cred;
+    int plen;
+    int oversized = 0;
+
+try_again:
+    if (fgets(buf, BUFFER_SIZE, stdin) == NULL) {
+	debug("fgets() failed! dying..... errno=%d (%s)\n", errno,
+	    strerror(errno));
+	exit(1);		/* BIIG buffer */
+    }
+    c = memchr(buf, '\n', BUFFER_SIZE);	/* safer against overrun than strchr */
+    if (c) {
+	if (oversized) {
+	    helperfail("illegal request received");
+	    fprintf(stderr, "Illegal request received: '%s'\n", buf);
+	    return;
+	}
+	*c = '\0';
+    } else {
+	fprintf(stderr, "No newline in '%s'\n", buf);
+	oversized = 1;
+	goto try_again;
+    }
+
+    debug("Got '%s' from Squid\n", buf);
+
+    if (memcmp(buf, "YR", 2) == 0) {	/* refresh-request */
+	c = (char *) obtain_challenge();
+	SEND2("TT %s", c);
+	return;
+    }
+    if (memcmp(buf, "KK ", 3) == 0) {	/* authenticate-request */
+	/* figure out what we got */
+	decoded = base64_decode(buf + 3);
+	/* Note: we don't need to manage memory at this point, since
+	 *  base64_decode returns a pointer to static storage.
+	 */
+
+	if (!decoded) {		/* decoding failure, return error */
+	    SEND("NA Packet format error, couldn't base64-decode");
+	    return;
+	}
+	/* fast-track-decode request type. */
+	fast_header = (struct _ntlmhdr *) decoded;
+
+	/* sanity-check: it IS a NTLMSSP packet, isn't it? */
+	if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
+	    SEND("NA Broken authentication packet");
+	    return;
+	}
+	switch (fast_header->type) {
+	case NTLM_NEGOTIATE:
+	    SEND("NA Invalid negotiation request received");
+	    return;
+	    /* notreached */
+	case NTLM_CHALLENGE:
+	    SEND
+		("NA Got a challenge. We refuse to have our authority disputed");
+	    return;
+	    /* notreached */
+	case NTLM_AUTHENTICATE:
+	    /* check against SSPI */
+	    plen = strlen(buf) * 3 / 4;		/* we only need it here. Optimization */
+	    cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
+	    if (cred == NULL) {
+		switch (ntlm_errno) {
+		case NTLM_LOGON_ERROR:
+		    SEND("NA Logon Failure");
+		    return;
+		case NTLM_BAD_NTGROUP:
+		    SEND("NA Incorrect Group Membership");
+		    return;
+		case NTLM_UNTRUSTED_DOMAIN:
+		    SEND("NA No authority could be contacted for authentication");
+		    return;
+		default:
+		    SEND("NA SSPI Error");
+		    return;
+		}
+	    }
+	    lc(cred);		/* let's lowercase them for our convenience */
+	    SEND2("AF %s", cred);
+	    return;
+	default:
+	    helperfail("unknown authentication packet type");
+	    return;
+	}
+	return;
+    } else  {	/* not an auth-request */
+	helperfail("illegal request received");
+	fprintf(stderr, "Illegal request received: '%s'\n", buf);
+	return;
+    }
+    helperfail("detected protocol error");
+    return;
+/********* END ********/
+}
+
+int
+main(int argc, char *argv[])
+{
+    
+    debug("ntlm_auth build " __DATE__ ", " __TIME__ " starting up...\n");
+
+    my_program_name = argv[0];
+    process_options(argc, argv);
+    debug("options processed OK\n");
+    srand( (unsigned)time( NULL ) );
+
+    if (LoadSecurityDll(SSP_NTLM) == NULL) {
+	fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
+	exit(1);
+    }
+    debug("SSPI initialized OK\n");
+
+    atexit(UnloadSecurityDll);
+
+    /* initialize FDescs */
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    while (1) {
+	manage_request();
+    }
+    return 0;
+}
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt
diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt:1.1.8.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt	Sun Sep 29 09:31:18 2002
@@ -0,0 +1,36 @@
+ntlm_auth.exe
+
+Native Windows NTLM authenticator for Squid 2.5
+
+=====
+Usage
+=====
+
+ntlm_auth [-v] [-a UserGroup] [-d UserGroup] [-l]
+
+-v enables debugging statements.
+-l if specified, changes behavior on failures to last-ditch.
+-a specify a Windows Local Group name allowed to authenticate.
+-d specify a Windows Local Group name not allowed to authenticate.
+
+This is released under the GNU General Public License
+
+==============
+Allowing Users
+==============
+
+Users that are allowed to access the web proxy must have the Windows NT
+User Rights "logon from the network" and must be included in the NT LOCAL User Groups 
+specified in the Authenticator's command line. 
+This can be accomplished creating a local user group on the NT machine, grant the privilege,
+and adding users to it.
+
+Refer to Squid documentation for the required changes to squid.conf.
+
+
+===============
+Contact details
+===============
+
+To contact the maintainer of this package, e-mail on squidnt@serassio.it.
+The latest version may be found on http://www.serassio.it/SquidNT.htm.
Index: squid/include/sspwin32.h
diff -u /dev/null squid/include/sspwin32.h:1.1.6.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/include/sspwin32.h	Tue Jul 16 06:23:57 2002
@@ -0,0 +1,45 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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 _LIBSSPWIN32_H_
+#define _LIBSSPWIN32_H_
+#define SECURITY_WIN32
+#define NTLM_PACKAGE_NAME "NTLM"
+
+#ifdef _SQUID_CYGWIN_
+#include <wchar.h>
+#define _T(x) TEXT(x)
+#else
+#include <tchar.h>
+#endif
+#include <windows.h>
+#include <security.h>
+#include <sspi.h>
+
+#define WINNT_SECURITY_DLL "security.dll"
+#define WIN9X_SECURITY_DLL "secur32.dll"
+
+#define SSP_BASIC 1
+#define SSP_NTLM 2
+
+HMODULE LoadSecurityDll(int);
+void UnloadSecurityDll(void);
+BOOL WINAPI SSPLogonUser(PTSTR, PTSTR, PTSTR);
+
+extern SECURITY_STATUS ss;
+
+#endif /* LIBSSPWIN32_H_ */
Index: squid/include/util.h
diff -u squid/include/util.h:1.11 squid/include/util.h:1.4.20.5
--- squid/include/util.h:1.11	Sat Apr  6 03:34:49 2002
+++ squid/include/util.h	Tue Oct 22 12:52:32 2002
@@ -68,7 +68,6 @@
 extern char *xstrdup(const char *);
 extern char *xstrndup(const char *, size_t);
 extern const char *xstrerror(void);
-extern const char *xbstrerror(int);
 extern int tvSubMsec(struct timeval, struct timeval);
 extern int tvSubUsec(struct timeval, struct timeval);
 extern double tvSubDsec(struct timeval, struct timeval);
Index: squid/lib/Makefile.am
diff -u squid/lib/Makefile.am:1.6 squid/lib/Makefile.am:1.2.6.6
--- squid/lib/Makefile.am:1.6	Wed Oct  2 04:10:43 2002
+++ squid/lib/Makefile.am	Wed Oct  2 11:57:19 2002
@@ -27,6 +27,7 @@
 	@LIBDLMALLOC@ \
 	libmiscutil.a \
 	libntlmauth.a \
+	libsspwin32.a \
 	@LIBREGEX@
 EXTRA_libmiscutil_a_SOURCES = \
 	md5.c \
@@ -66,5 +67,7 @@
 	ntlmauth.c
 libntlmauth_a_LIBADD = \
 	@LIBOBJS@
+libsspwin32_a_SOURCES = \
+	sspwin32.c
 
-INCLUDES	= -I$(top_builddir)/include -I$(top_srcdir)/include
+INCLUDES	= -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
Index: squid/lib/sspwin32.c
diff -u /dev/null squid/lib/sspwin32.c:1.1.6.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/lib/sspwin32.c	Sun Sep 29 09:31:18 2002
@@ -0,0 +1,409 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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 "sspwin32.h"
+
+typedef struct _AUTH_SEQ {
+    BOOL fInitialized;
+    BOOL fHaveCredHandle;
+    BOOL fHaveCtxtHandle;
+    CredHandle hcred;
+    struct _SecHandle hctxt;
+} AUTH_SEQ, *PAUTH_SEQ;
+
+static HMODULE hModule;
+static int NTLM_mode = SSP_BASIC;
+SECURITY_STATUS ss = SEC_E_OK;
+
+/* Function pointers */
+ACCEPT_SECURITY_CONTEXT_FN _AcceptSecurityContext = NULL;
+ACQUIRE_CREDENTIALS_HANDLE_FN _AcquireCredentialsHandle = NULL;
+COMPLETE_AUTH_TOKEN_FN _CompleteAuthToken = NULL;
+DELETE_SECURITY_CONTEXT_FN _DeleteSecurityContext = NULL;
+FREE_CONTEXT_BUFFER_FN _FreeContextBuffer = NULL;
+FREE_CREDENTIALS_HANDLE_FN _FreeCredentialsHandle = NULL;
+INITIALIZE_SECURITY_CONTEXT_FN _InitializeSecurityContext = NULL;
+QUERY_SECURITY_PACKAGE_INFO_FN _QuerySecurityPackageInfo = NULL;
+
+
+void UnloadSecurityDll(void)
+{
+    if (hModule)
+	FreeLibrary(hModule);
+    _AcceptSecurityContext      = NULL;
+    _AcquireCredentialsHandle   = NULL;
+    _CompleteAuthToken          = NULL;
+    _DeleteSecurityContext      = NULL;
+    _FreeContextBuffer          = NULL;
+    _FreeCredentialsHandle      = NULL;
+    _InitializeSecurityContext  = NULL;
+    _QuerySecurityPackageInfo   = NULL;
+    hModule = NULL;
+}
+
+
+HMODULE LoadSecurityDll(int mode) 
+{
+    TCHAR lpszDLL[MAX_PATH];
+    OSVERSIONINFO VerInfo;
+/* 
+ *  Find out which security DLL to use, depending on
+ *  whether we are on NT or Win95 or 2000 or XP or .NET Server
+ *  We have to use security.dll on Windows NT 4.0.
+ *  All other operating systems, we have to use Secur32.dll
+ */ 
+    hModule = NULL;
+    if ((mode != SSP_BASIC) && (mode != SSP_NTLM))
+	return hModule;
+    NTLM_mode = mode;
+    VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+    if (!GetVersionEx (&VerInfo)) {   /* If this fails, something has gone wrong */
+	return hModule;
+    }
+    if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+	VerInfo.dwMajorVersion == 4 &&
+	VerInfo.dwMinorVersion == 0)
+    {
+	lstrcpy (lpszDLL, _T(WINNT_SECURITY_DLL));
+    } else {
+	lstrcpy (lpszDLL, _T(WIN9X_SECURITY_DLL));
+    }
+    hModule = LoadLibrary(lpszDLL);
+    if (!hModule)
+	return hModule;
+    _AcceptSecurityContext = (ACCEPT_SECURITY_CONTEXT_FN) 
+	GetProcAddress(hModule, "AcceptSecurityContext");
+    if (!_AcceptSecurityContext) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+#ifdef UNICODE
+    _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN)
+	GetProcAddress(hModule, "AcquireCredentialsHandleW");
+#else
+    _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN)
+	GetProcAddress(hModule, "AcquireCredentialsHandleA");
+#endif
+    if (!_AcquireCredentialsHandle) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+/* CompleteAuthToken is not present on Windows 9x Secur32.dll
+ * Do not check for the availablity of the function if it is NULL
+ */
+    _CompleteAuthToken = (COMPLETE_AUTH_TOKEN_FN) 
+	GetProcAddress(hModule, "CompleteAuthToken");
+    _DeleteSecurityContext = (DELETE_SECURITY_CONTEXT_FN) 
+	GetProcAddress(hModule, "DeleteSecurityContext");
+    if (!_DeleteSecurityContext) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+    _FreeContextBuffer = (FREE_CONTEXT_BUFFER_FN) 
+	GetProcAddress(hModule, "FreeContextBuffer");
+    if (!_FreeContextBuffer) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+    _FreeCredentialsHandle = (FREE_CREDENTIALS_HANDLE_FN) 
+	GetProcAddress(hModule, "FreeCredentialsHandle");
+    if (!_FreeCredentialsHandle) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+#ifdef UNICODE
+    _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN)
+	GetProcAddress(hModule, "InitializeSecurityContextW");
+#else
+    _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN) 
+	GetProcAddress(hModule, "InitializeSecurityContextA");
+#endif
+    if (!_InitializeSecurityContext) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+#ifdef UNICODE
+    _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN) 
+	GetProcAddress(hModule, "QuerySecurityPackageInfoW");
+#else
+    _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN)
+	GetProcAddress(hModule, "QuerySecurityPackageInfoA");
+#endif
+    if (!_QuerySecurityPackageInfo) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    }
+    return hModule;
+}
+
+
+BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
+		      PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone)
+{
+/*
+ *  Routine Description:
+ *
+ *  Optionally takes an input buffer coming from the server and returns
+ *  a buffer of information to send back to the server. Also returns
+ *  an indication of whether or not the context is complete.
+ *
+ *  Return Value:
+ *  Returns TRUE if successful; otherwise FALSE.
+ */ 
+    TimeStamp       tsExpiry;
+    SecBufferDesc   sbdOut;
+    SecBuffer       sbOut;
+    SecBufferDesc   sbdIn;
+    SecBuffer       sbIn;
+    ULONG           fContextAttr;
+
+    if (!pAS->fInitialized) {
+	ss = _AcquireCredentialsHandle(NULL, _T(NTLM_PACKAGE_NAME), 
+	    SECPKG_CRED_OUTBOUND, NULL, (NTLM_mode == SSP_NTLM) ? NULL : pAuthIdentity, NULL, NULL,
+	    &pAS->hcred, &tsExpiry);
+	if (ss < 0) {
+	    fprintf(stderr, "AcquireCredentialsHandle failed with %08lX\n", ss);
+	    return FALSE;
+	}
+	pAS->fHaveCredHandle = TRUE;
+    }
+    
+    /* Prepare output buffer */
+    sbdOut.ulVersion = 0;
+    sbdOut.cBuffers = 1;
+    sbdOut.pBuffers = &sbOut;
+    sbOut.cbBuffer = *pcbOut;
+    sbOut.BufferType = SECBUFFER_TOKEN;
+    sbOut.pvBuffer = pOut;
+    
+    /* Prepare input buffer */
+    if (pAS->fInitialized)  {
+	sbdIn.ulVersion = 0;
+	sbdIn.cBuffers = 1;
+	sbdIn.pBuffers = &sbIn;
+	sbIn.cbBuffer = cbIn;
+	sbIn.BufferType = SECBUFFER_TOKEN;
+	sbIn.pvBuffer = pIn;
+    }
+    ss = _InitializeSecurityContext(&pAS->hcred, 
+	pAS->fInitialized ? &pAS->hctxt : NULL, NULL, 0, 0, 
+	SECURITY_NATIVE_DREP, pAS->fInitialized ? &sbdIn : NULL,
+	0, &pAS->hctxt, &sbdOut, &fContextAttr, &tsExpiry);
+    if (ss < 0)  { 
+	// <winerror.h>
+	fprintf(stderr, "InitializeSecurityContext failed with %08lX\n", ss);
+	return FALSE;
+    }
+    pAS->fHaveCtxtHandle = TRUE;
+    
+    /* If necessary, complete token */
+    if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {
+	if (_CompleteAuthToken) {
+	    ss = _CompleteAuthToken(&pAS->hctxt, &sbdOut);
+	    if (ss < 0) {
+		fprintf(stderr, "CompleteAuthToken failed with %08lX\n", ss);
+		return FALSE;
+	    }
+	} else {
+	    fprintf (stderr, "CompleteAuthToken not supported.\n");
+	    return FALSE;
+	}
+    }
+    *pcbOut = sbOut.cbBuffer;
+    if (!pAS->fInitialized)
+	pAS->fInitialized = TRUE;
+    *pfDone = !(ss == SEC_I_CONTINUE_NEEDED 
+	|| ss == SEC_I_COMPLETE_AND_CONTINUE );
+    return TRUE;
+}
+
+
+BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut, 
+		      PDWORD pcbOut, PBOOL pfDone)
+{
+/*
+ *   Routine Description:
+ *
+ *   Takes an input buffer coming from the client and returns a buffer
+ *   to be sent to the client.  Also returns an indication of whether or
+ *   not the context is complete.
+ *
+ *   Return Value:
+ *
+ *   Returns TRUE if successful; otherwise FALSE.
+ */
+
+    TimeStamp       tsExpiry;
+    SecBufferDesc   sbdOut;
+    SecBuffer       sbOut;
+    SecBufferDesc   sbdIn;
+    SecBuffer       sbIn;
+    ULONG           fContextAttr;
+
+    if (!pAS->fInitialized)  {
+	ss = _AcquireCredentialsHandle(NULL, _T("NTLM"), 
+	    SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &pAS->hcred, 
+	    &tsExpiry);
+	if (ss < 0) {
+	    fprintf(stderr, "AcquireCredentialsHandle failed with %08lX\n", ss);
+	    return FALSE;
+	}
+	pAS->fHaveCredHandle = TRUE;
+    }
+    
+    /* Prepare output buffer */
+    sbdOut.ulVersion = 0;
+    sbdOut.cBuffers = 1;
+    sbdOut.pBuffers = &sbOut;
+    sbOut.cbBuffer = *pcbOut;
+    sbOut.BufferType = SECBUFFER_TOKEN;
+    sbOut.pvBuffer = pOut;
+
+    /* Prepare input buffer */
+    sbdIn.ulVersion = 0;
+    sbdIn.cBuffers = 1;
+    sbdIn.pBuffers = &sbIn;
+    sbIn.cbBuffer = cbIn;
+    sbIn.BufferType = SECBUFFER_TOKEN;
+    sbIn.pvBuffer = pIn;
+    ss = _AcceptSecurityContext(&pAS->hcred, 
+	pAS->fInitialized ? &pAS->hctxt : NULL, &sbdIn, (NTLM_mode == SSP_NTLM) ? ASC_REQ_DELEGATE : 0, 
+	SECURITY_NATIVE_DREP, &pAS->hctxt, &sbdOut, &fContextAttr, 
+	&tsExpiry);
+    if (ss < 0) {
+	fprintf(stderr, "AcceptSecurityContext failed with %08lX\n", ss);
+	return FALSE;
+    }
+    pAS->fHaveCtxtHandle = TRUE;
+    
+    /* If necessary, complete token */
+    if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {
+	if (_CompleteAuthToken) {
+	    ss = _CompleteAuthToken(&pAS->hctxt, &sbdOut);
+	    if (ss < 0) {
+		fprintf(stderr, "CompleteAuthToken failed with %08lX\n", ss);
+		return FALSE;
+	    }
+	} else {
+	    fprintf (stderr, "CompleteAuthToken not supported.\n");
+	    return FALSE;
+	}
+    }
+    *pcbOut = sbOut.cbBuffer;
+    if (!pAS->fInitialized)
+	pAS->fInitialized = TRUE;
+    *pfDone = !(ss = SEC_I_CONTINUE_NEEDED 
+	|| ss == SEC_I_COMPLETE_AND_CONTINUE);
+    return TRUE;
+}
+
+
+BOOL WINAPI SSPLogonUser(PTSTR szUser, PTSTR szPassword, PTSTR szDomain) 
+{
+    AUTH_SEQ    asServer   = {0};
+    AUTH_SEQ    asClient   = {0};
+    BOOL        fDone      = FALSE;
+    BOOL        fResult    = FALSE;
+    DWORD       cbOut      = 0;
+    DWORD       cbIn       = 0;
+    DWORD       cbMaxToken = 0;
+    PVOID       pClientBuf = NULL;
+    PVOID       pServerBuf = NULL;
+    PSecPkgInfo pSPI       = NULL;
+    
+    SEC_WINNT_AUTH_IDENTITY ai;
+
+    do {
+	if (!hModule)
+	    break;
+	/* Get max token size */
+	_QuerySecurityPackageInfo(_T("NTLM"), &pSPI);
+	cbMaxToken = pSPI->cbMaxToken;
+	_FreeContextBuffer(pSPI);
+
+	/* Allocate buffers for client and server messages */
+	pClientBuf = xcalloc(cbMaxToken, sizeof(char));
+	pServerBuf = xcalloc(cbMaxToken, sizeof(char));
+
+	/* Initialize auth identity structure */
+	ZeroMemory(&ai, sizeof(ai));
+	ai.Domain = szDomain;
+	ai.DomainLength = lstrlen(szDomain);
+	ai.User = szUser;
+	ai.UserLength = lstrlen(szUser);
+	ai.Password = szPassword;
+	ai.PasswordLength = lstrlen(szPassword);
+#if defined(UNICODE) || defined(_UNICODE)
+	ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else      
+	ai.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+
+	/* Prepare client message (negotiate) */
+	cbOut = cbMaxToken;
+	if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone))
+	    break;
+
+	/* Prepare server message (challenge) */
+	cbIn = cbOut;
+	cbOut = cbMaxToken;
+	if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, 
+	    &fDone))
+	    break;
+/* Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
+ * in the case of bad szUser or szPassword.
+ * Unexpected Result: Logon will succeed if you pass in a bad szUser and 
+ * the guest account is enabled in the specified domain.
+ */
+
+	/* Prepare client message (authenticate) */
+	cbIn = cbOut;
+	cbOut = cbMaxToken;
+	if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut,
+	    &fDone))
+	    break;
+
+	/* Prepare server message (authentication) */
+	cbIn = cbOut;
+	cbOut = cbMaxToken;
+	if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, 
+	    &fDone))
+	    break;
+	fResult = TRUE;
+    } while(0);
+
+    /* Clean up resources */
+    if (asClient.fHaveCtxtHandle)
+	_DeleteSecurityContext(&asClient.hctxt);
+    if (asClient.fHaveCredHandle)
+	_FreeCredentialsHandle(&asClient.hcred);
+    if (asServer.fHaveCtxtHandle)
+	_DeleteSecurityContext(&asServer.hctxt);
+    if (asServer.fHaveCredHandle)
+	_FreeCredentialsHandle(&asServer.hcred);
+    xfree(pClientBuf);
+    xfree(pServerBuf);
+
+    return fResult;
+}
Index: squid/src/Makefile.am
diff -u squid/src/Makefile.am:1.31 squid/src/Makefile.am:1.2.6.23
--- squid/src/Makefile.am:1.31	Sun Nov 10 14:41:02 2002
+++ squid/src/Makefile.am	Mon Nov 11 13:21:25 2002
@@ -68,7 +68,7 @@
 WIN32SOURCE = 
 endif
 
-AM_CFLAGS = -Werror -Wall
+AM_CFLAGS = -Wall
 
 SUBDIRS		= fs repl auth
 
Index: squid/src/cf.data.pre
diff -u squid/src/cf.data.pre:1.91 squid/src/cf.data.pre:1.10.2.46
--- squid/src/cf.data.pre:1.91	Wed Feb  5 19:18:14 2003
+++ squid/src/cf.data.pre	Wed Feb 12 01:56:28 2003
@@ -822,6 +822,16 @@
 
 	see argument descriptions under ufs above
 
+	The awin32 store type:
+
+	"awin32" uses the same storage format as "ufs", utilizing
+	WIN32-threads to avoid blocking the main Squid process on
+	disk-I/O. This was formerly known in Squid as async-io.
+
+	cache_dir awin32 Directory-Name Mbytes L1 L2 [options]
+
+	see argument descriptions under ufs above
+
 	The diskd store type:
 
 	"diskd" uses the same storage format as "ufs", utilizing a
@@ -1910,7 +1920,7 @@
 DEFAULT: 10 seconds
 DOC_START
 	Maximum time to wait for IDENT lookups to complete.
-	
+
 	If this is too high, and you enabled IDENT lookups from untrusted
 	users, then you might be susceptible to denial-of-service by having
 	many ident requests going at once.
Index: squid/src/client_side_request.c
diff -u squid/src/client_side_request.c:1.6 squid/src/client_side_request.c:1.3.2.3
--- squid/src/client_side_request.c:1.6	Thu Oct  3 05:55:28 2002
+++ squid/src/client_side_request.c	Sun Oct 20 06:46:10 2002
@@ -1,10 +1,10 @@
 
 /*
  * $Id$
- * 
- * DEBUG: section 85    Client-side Request Routines AUTHOR: Robert Collins
- * (Originally Duane Wessels in client_side.c)
- * 
+ *
+ * DEBUG: section 85    Client-side Request Routines
+ * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
+ *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
  * ----------------------------------------------------------
  * 
Index: squid/src/defines.h
diff -u squid/src/defines.h:1.25 squid/src/defines.h:1.3.22.26
--- squid/src/defines.h:1.25	Sun Sep 15 04:06:31 2002
+++ squid/src/defines.h	Mon Sep 16 00:25:32 2002
@@ -313,4 +313,5 @@
 #endif
 
 #define	HTTP_REQBUF_SZ	4096
+
 #endif /* SQUID_DEFINES_H */
Index: squid/src/pinger.c
diff -u squid/src/pinger.c:1.5 squid/src/pinger.c:1.3.28.3
--- squid/src/pinger.c:1.5	Sat Oct  5 19:33:41 2002
+++ squid/src/pinger.c	Sun Oct 20 06:46:13 2002
@@ -37,19 +37,92 @@
 
 #if USE_ICMP
 
+/* Native Windows port doesn't have netinet support, so we emulate it.
+   At this time, Cygwin lacks icmp support in its include files, so we need
+   to use the native Windows port definitions.
+ */
+
+#if !defined(_SQUID_MSWIN_) && !defined(_SQUID_CYGWIN_)
+
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 
+#define PINGER_TIMEOUT 10
+
+static int socket_from_squid = 0;
+static int socket_to_squid = 1;
+
+#else /* _SQUID_MSWIN_ or _SQUID_CYGWIN_ */
+
+#ifdef _SQUID_MSWIN_
+
+#include <winsock2.h>
+#include <process.h>
+
+#define PINGER_TIMEOUT 5
+
+static SOCKET socket_to_squid = -1;
+#define socket_from_squid socket_to_squid
+
+#else /* _SQUID_CYGWIN_ */
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#define PINGER_TIMEOUT 10
+
+static int socket_from_squid = 0;
+static int socket_to_squid = 1;
+
+#endif
+
+#define ICMP_ECHO 8
+#define ICMP_ECHOREPLY 0
+
+typedef struct iphdr
+{
+    u_int8_t  ip_vhl:4;		/* Length of the header in dwords */
+    u_int8_t  version:4;	/* Version of IP                  */
+    u_int8_t  tos;		/* Type of service                */
+    u_int16_t total_len;	/* Length of the packet in dwords */
+    u_int16_t ident;		/* unique identifier              */
+    u_int16_t flags;		/* Flags                          */
+    u_int8_t  ip_ttl;		/* Time to live                   */
+    u_int8_t  proto;		/* Protocol number (TCP, UDP etc) */
+    u_int16_t checksum;		/* IP checksum                    */
+    u_int32_t source_ip;
+    u_int32_t dest_ip;
+}
+iphdr;
+
+/* ICMP header */
+typedef struct icmphdr
+{
+    u_int8_t  icmp_type;	/* ICMP packet type                 */
+    u_int8_t  icmp_code;	/* Type sub code                    */
+    u_int16_t icmp_cksum;
+    u_int16_t icmp_id;
+    u_int16_t icmp_seq;
+    u_int32_t timestamp;	/* not part of ICMP, but we need it */
+}
+icmphdr;
+
+#endif	/* _SQUID_MSWIN_ */
+
 #ifndef _SQUID_LINUX_
 #ifndef _SQUID_CYGWIN_
+#ifndef _SQUID_MSWIN_
 #define icmphdr icmp
 #define iphdr ip
 #endif
 #endif
+#endif
 
-#if defined (_SQUID_LINUX_) || defined (_SQUID_CYGWIN_)
+#if defined (_SQUID_LINUX_)
 #ifdef icmp_id
 #undef icmp_id
 #endif
@@ -118,11 +191,45 @@
 static void pingerLog(struct icmphdr *, struct in_addr, int, int);
 static int ipHops(int ttl);
 static void pingerSendtoSquid(pingerReplyData * preply);
+static void pingerOpen(void);
+static void pingerClose(void);
 
 void
 pingerOpen(void)
 {
     struct protoent *proto = NULL;
+#ifdef _SQUID_MSWIN_
+    WSADATA wsaData;
+    WSAPROTOCOL_INFO wpi;
+    char buf[sizeof(wpi)];
+    int x;
+    struct sockaddr_in PS;
+
+    WSAStartup(2, &wsaData);
+
+    getCurrentTime();
+    _db_init(NULL, "ALL,1");
+    setmode(0, O_BINARY);
+    setmode(1, O_BINARY);
+    x = read(0, buf, sizeof(wpi));
+    if (x < sizeof(wpi)) {
+	getCurrentTime();
+	debug(42, 0) ("pingerOpen: read: FD 0: %s\n", xstrerror());
+	write(1, "ERR\n", 4);
+	exit(1);
+    }
+    xmemcpy(&wpi, buf, sizeof(wpi));
+
+    write(1, "OK\n", 3);
+    x = read(0, buf, sizeof(PS));
+    if (x < sizeof(PS)) {
+	getCurrentTime();
+	debug(42, 0) ("pingerOpen: read: FD 0: %s\n", xstrerror());
+	write(1, "ERR\n", 4);
+	exit(1);
+    }
+    xmemcpy(&PS, buf, sizeof(PS));
+#endif
     if ((proto = getprotobyname("icmp")) == 0) {
 	debug(42, 0) ("pingerOpen: unknown protocol: icmp\n");
 	exit(1);
@@ -134,12 +241,50 @@
     }
     icmp_ident = getpid() & 0xffff;
     debug(42, 0) ("pinger: ICMP socket opened\n");
+#ifdef _SQUID_MSWIN_
+    socket_to_squid =
+	WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
+	&wpi, 0, 0);
+    if (socket_to_squid == INVALID_SOCKET) {
+	getCurrentTime();
+	debug(42, 0) ("pingerOpen: WSASocket: %s\n", xstrerror());
+	write(1, "ERR\n", 4);
+	exit(1);
+    }
+    x = connect(socket_to_squid, (struct sockaddr *) &PS, sizeof(PS));
+    if (SOCKET_ERROR == x) {
+	getCurrentTime();
+	debug(42, 0) ("pingerOpen: connect: %s\n", xstrerror());
+	write(1, "ERR\n", 4);
+	exit(1);
+    }
+    write(1, "OK\n", 3);
+    memset(buf, 0, sizeof(buf));
+    x = recv(socket_to_squid, buf, sizeof(buf), 0);
+    if (x < 3) {
+	debug(42, 0) ("icmpOpen: recv: %s\n", xstrerror());
+	exit(1);
+    }
+    x = send(socket_to_squid, buf, strlen(buf), 0);
+    if (x < 3 || strncmp("OK\n", buf, 3)) {
+	debug(42, 0) ("icmpOpen: recv: %s\n", xstrerror());
+	exit(1);
+    }
+
+    getCurrentTime();
+    debug(42, 0) ("pinger: Squid socket opened\n");
+#endif
 }
 
 void
 pingerClose(void)
 {
     close(icmp_sock);
+#ifdef _SQUID_MSWIN_
+    shutdown(socket_to_squid, SD_BOTH);
+    close(socket_to_squid);
+    socket_to_squid = -1;
+#endif
     icmp_sock = -1;
     icmp_ident = 0;
 }
@@ -267,7 +412,7 @@
     }
     sum = (sum >> 16) + (sum & 0xffff);
     sum += (sum >> 16);
-    answer = ~sum;
+    answer = (unsigned short) ~sum;
     return (answer);
 }
 
@@ -307,7 +452,7 @@
     int n;
     int guess_size;
     memset(&pecho, '\0', sizeof(pecho));
-    n = recv(0, (char *) &pecho, sizeof(pecho), 0);
+    n = recv(socket_from_squid, (char *) &pecho, sizeof(pecho), 0);
     if (n < 0)
 	return n;
     if (0 == n) {
@@ -334,8 +479,9 @@
 pingerSendtoSquid(pingerReplyData * preply)
 {
     int len = sizeof(pingerReplyData) - MAX_PKT_SZ + preply->psize;
-    if (send(1, (char *) preply, len, 0) < 0) {
+    if (send(socket_to_squid, (char *) preply, len, 0) < 0) {
 	debug(50, 0) ("pinger: send: %s\n", xstrerror());
+	pingerClose();
 	exit(1);
     }
 }
@@ -376,25 +522,30 @@
     _db_init(NULL, debug_args);
 
     for (;;) {
-	tv.tv_sec = 10;
+	tv.tv_sec = PINGER_TIMEOUT;
 	tv.tv_usec = 0;
 	FD_ZERO(&R);
-	FD_SET(0, &R);
+	FD_SET(socket_from_squid, &R);
 	FD_SET(icmp_sock, &R);
 	x = select(icmp_sock + 1, &R, NULL, NULL, &tv);
 	getCurrentTime();
-	if (x < 0)
+	if (x < 0) {
+	    pingerClose();
 	    exit(1);
-	if (FD_ISSET(0, &R))
+	}
+	if (FD_ISSET(socket_from_squid, &R))
 	    if (pingerReadRequest() < 0) {
 		debug(42, 0) ("Pinger exiting.\n");
+		pingerClose();
 		exit(1);
 	    }
 	if (FD_ISSET(icmp_sock, &R))
 	    pingerRecv();
-	if (10 + last_check_time < squid_curtime) {
-	    if (send(1, (char *) &tv, 0, 0) < 0)
+	if (PINGER_TIMEOUT + last_check_time < squid_curtime) {
+	    if (send(socket_to_squid, (char *) &tv, 0, 0) < 0) {
+		pingerClose();
 		exit(1);
+	    }
 	    last_check_time = squid_curtime;
 	}
     }
Index: squid/src/ufscommon.c
diff -u squid/src/ufscommon.c:1.3 squid/src/ufscommon.c:1.2.12.3
--- squid/src/ufscommon.c:1.3	Thu Jan  9 03:34:05 2003
+++ squid/src/ufscommon.c	Thu Jan  9 12:55:37 2003
@@ -1281,6 +1281,8 @@
 int
 commonUfsDirIs(SwapDir * sd)
 {
+    if (strncmp(sd->type, "awin32", 6) == 0)
+	return 1;
     if (strncmp(sd->type, "aufs", 4) == 0)
 	return 1;
     if (strncmp(sd->type, "diskd", 5) == 0)
Index: squid/src/win32.c
diff -u squid/src/win32.c:1.6 squid/src/win32.c:1.1.50.19
--- squid/src/win32.c:1.6	Wed Jun 26 10:28:32 2002
+++ squid/src/win32.c	Tue Jul 16 03:04:20 2002
@@ -22,6 +22,9 @@
  *
  */
 
+#ifndef WIN32_C
+#define WIN32_C
+
 #include "squid.h"
 
 /* This code compiles only CygWin & Windows NT Port */
@@ -34,6 +37,7 @@
 /* LOCAL FUNCTIONS */
 /* ====================================================================== */
 
+
 static unsigned int
 GetOSVersion()
 {
@@ -108,3 +112,6 @@
     return 0;
 }
 #endif
+
+#endif /* WIN32_C */
+
Index: squid/src/fs/Makefile.am
diff -u squid/src/fs/Makefile.am:1.3 squid/src/fs/Makefile.am:1.2.6.3
--- squid/src/fs/Makefile.am:1.3	Mon May 20 23:58:31 2002
+++ squid/src/fs/Makefile.am	Tue May 21 02:26:38 2002
@@ -5,14 +5,16 @@
 
 AUTOMAKE_OPTIONS = subdir-objects
 
-DIST_SUBDIRS	= aufs coss diskd null ufs
+DIST_SUBDIRS	= aufs awin32 coss diskd null ufs
 SUBDIRS		= @STORE_MODULE_SUBDIRS@
 
-EXTRA_LIBRARIES = libaufs.a libcoss.a libdiskd.a libnull.a libufs.a
+EXTRA_LIBRARIES = libaufs.a libawin32.a libcoss.a libdiskd.a libnull.a libufs.a
 noinst_LIBRARIES = @STORE_LIBS@
 
 libaufs_a_SOURCES = aufs/aiops.c aufs/async_io.c aufs/store_asyncufs.h \
 	aufs/store_dir_aufs.c aufs/store_io_aufs.c
+libawin32_a_SOURCES = awin32/aiops.c awin32/async_io.c awin32/store_asyncufs.h \
+	awin32/store_dir_aufs.c awin32/store_io_aufs.c
 libcoss_a_SOURCES = coss/store_coss.h   coss/store_io_coss.c coss/store_dir_coss.c \
 	coss/async_io.c coss/async_io.h
 libdiskd_a_SOURCES = diskd/diskd.c diskd/store_dir_diskd.c diskd/store_diskd.h \
@@ -29,6 +31,8 @@
 ## targets below to emulate distributed makefiles
 aufs/all: libaufs.a
 aufs/clean: clean
+awin32/all: libawin32.a
+awin32/clean: clean
 coss/all: libcoss.a
 coss/clean: clean
 null/all: libnull.a
Index: squid/src/fs/awin32/.cvsignore
diff -u /dev/null squid/src/fs/awin32/.cvsignore:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/.cvsignore	Fri Oct 19 12:24:13 2001
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/src/fs/awin32/Makefile.am
diff -u /dev/null squid/src/fs/awin32/Makefile.am:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/Makefile.am	Fri Oct 19 12:24:13 2001
@@ -0,0 +1,2 @@
+all clean:
+	@cd .. && $(MAKE) $(MFLAGS) awin32/$@
Index: squid/src/fs/awin32/aiops.c
diff -u /dev/null squid/src/fs/awin32/aiops.c:1.1.2.7
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/aiops.c	Tue Oct 22 12:52:34 2002
@@ -0,0 +1,1012 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 43    AIOPS
+ * AUTHOR: Stewart Forster <slf@connect.com.au>
+ *
+ * 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 <windows.h>
+#include "store_asyncufs.h"
+
+#include	<stdio.h>
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<fcntl.h>
+#include	<errno.h>
+#include	<dirent.h>
+#include	<signal.h>
+
+#define RIDICULOUS_LENGTH	4096
+
+enum _squidaio_thread_status {
+    _THREAD_STARTING = 0,
+    _THREAD_WAITING,
+    _THREAD_BUSY,
+    _THREAD_FAILED,
+    _THREAD_DONE
+};
+typedef enum _squidaio_thread_status squidaio_thread_status;
+
+typedef struct squidaio_request_t {
+    struct squidaio_request_t *next;
+    squidaio_request_type request_type;
+    int cancelled;
+    char *path;
+    int oflag;
+    mode_t mode;
+    int fd;
+    char *bufferp;
+    char *tmpbufp;
+    int buflen;
+    off_t offset;
+    int whence;
+    int ret;
+    int err;
+    struct stat *tmpstatp;
+    struct stat *statp;
+    squidaio_result_t *resultp;
+} squidaio_request_t;
+
+typedef struct squidaio_request_queue_t {
+    HANDLE mutex; 
+    HANDLE cond; /* See Event objects */
+    squidaio_request_t *volatile head;
+    squidaio_request_t *volatile *volatile tailp;
+    unsigned long requests;
+    unsigned long blocked;	/* main failed to lock the queue */
+} squidaio_request_queue_t;
+
+typedef struct squidaio_thread_t squidaio_thread_t;
+struct squidaio_thread_t {
+    squidaio_thread_t *next;
+    HANDLE thread;
+    DWORD dwThreadId; /* thread ID */
+    squidaio_thread_status status;
+    struct squidaio_request_t *current_req;
+    unsigned long requests;
+    int volatile exit;
+};
+
+static void squidaio_init(void);
+static void squidaio_queue_request(squidaio_request_t *);
+static void squidaio_cleanup_request(squidaio_request_t *);
+static DWORD WINAPI squidaio_thread_loop( LPVOID lpParam );
+static void squidaio_do_open(squidaio_request_t *);
+static void squidaio_do_read(squidaio_request_t *);
+static void squidaio_do_write(squidaio_request_t *);
+static void squidaio_do_close(squidaio_request_t *);
+static void squidaio_do_stat(squidaio_request_t *);
+static void squidaio_do_unlink(squidaio_request_t *);
+static void squidaio_do_truncate(squidaio_request_t *);
+#if AIO_OPENDIR
+static void *squidaio_do_opendir(squidaio_request_t *);
+#endif
+static void squidaio_debug(squidaio_request_t *);
+static void squidaio_poll_queues(void);
+
+static squidaio_thread_t *threads = NULL;
+static int squidaio_initialised = 0;
+
+
+#define AIO_LARGE_BUFS  16384
+#define AIO_MEDIUM_BUFS	AIO_LARGE_BUFS >> 1
+#define AIO_SMALL_BUFS	AIO_LARGE_BUFS >> 2
+#define AIO_TINY_BUFS	AIO_LARGE_BUFS >> 3
+#define AIO_MICRO_BUFS	128
+
+static MemPool *squidaio_large_bufs = NULL;	/* 16K */
+static MemPool *squidaio_medium_bufs = NULL;	/* 8K */
+static MemPool *squidaio_small_bufs = NULL;	/* 4K */
+static MemPool *squidaio_tiny_bufs = NULL;	/* 2K */
+static MemPool *squidaio_micro_bufs = NULL;	/* 128K */
+
+static int request_queue_len = 0;
+static MemPool *squidaio_request_pool = NULL;
+static MemPool *squidaio_thread_pool = NULL;
+static squidaio_request_queue_t request_queue;
+static struct {
+    squidaio_request_t *head, **tailp;
+} request_queue2 = {
+
+    NULL, &request_queue2.head
+};
+static squidaio_request_queue_t done_queue;
+static struct {
+    squidaio_request_t *head, **tailp;
+} done_requests = {
+
+    NULL, &done_requests.head
+};
+
+static HANDLE main_thread;
+
+static MemPool *
+squidaio_get_pool(int size)
+{
+    MemPool *p;
+    if (size <= AIO_LARGE_BUFS) {
+	if (size <= AIO_MICRO_BUFS)
+	    p = squidaio_micro_bufs;
+	else if (size <= AIO_TINY_BUFS)
+	    p = squidaio_tiny_bufs;
+	else if (size <= AIO_SMALL_BUFS)
+	    p = squidaio_small_bufs;
+	else if (size <= AIO_MEDIUM_BUFS)
+	    p = squidaio_medium_bufs;
+	else
+	    p = squidaio_large_bufs;
+    } else
+	p = NULL;
+    return p;
+}
+
+static void *
+squidaio_xmalloc(int size)
+{
+    void *p;
+    MemPool *pool;
+
+    if ((pool = squidaio_get_pool(size)) != NULL) {
+	p = memPoolAlloc(pool);
+    } else
+	p = xmalloc(size);
+
+    return p;
+}
+
+static char *
+squidaio_xstrdup(const char *str)
+{
+    char *p;
+    int len = strlen(str) + 1;
+
+    p = squidaio_xmalloc(len);
+    strncpy(p, str, len);
+
+    return p;
+}
+
+static void
+squidaio_xfree(void *p, int size)
+{
+    MemPool *pool;
+
+    if ((pool = squidaio_get_pool(size)) != NULL) {
+	memPoolFree(pool, p);
+    } else
+	xfree(p);
+}
+
+static void
+squidaio_xstrfree(char *str)
+{
+    MemPool *pool;
+    int len = strlen(str) + 1;
+
+    if ((pool = squidaio_get_pool(len)) != NULL) {
+	memPoolFree(pool, str);
+    } else
+	xfree(str);
+}
+
+static void
+squidaio_init(void)
+{
+    int i;
+    squidaio_thread_t *threadp;
+
+    if (squidaio_initialised)
+	return;
+
+    if (!DuplicateHandle(GetCurrentProcess(),	/* pseudo handle, don't close */
+			    GetCurrentThread(),	/* pseudo handle to copy */
+			    GetCurrentProcess(),/* pseudo handle, don't close */
+			    &main_thread,
+			    0,			/* required access */
+			    FALSE,		/* child process's don't inherit the handle */
+			    DUPLICATE_SAME_ACCESS)) {
+	/* spit errors */
+	fatal("couldn't get current thread handle\n");
+    }
+    /* Initialize request queue */
+    if ((request_queue.mutex = CreateMutex(NULL,    /* no inheritance */
+				FALSE,		    /* start unowned (as per mutex_init) */
+				NULL)		    /* no name */
+				) == NULL) { 
+	fatal("failed to create mutex\n");
+    }
+    if ((request_queue.cond = CreateEvent(NULL,	    /* no inheritance */
+				FALSE,		    /* auto signal reset - which I think is pthreads like ? */
+				FALSE,		    /* start non signaled */
+				NULL)		    /* no name */
+				) == NULL) {
+        fatal("failed to create condition event variable.\n");
+    }
+    request_queue.head = NULL;
+    request_queue.tailp = &request_queue.head;
+    request_queue.requests = 0;
+    request_queue.blocked = 0;
+
+    /* Initialize done queue */
+    if ((done_queue.mutex = CreateMutex(NULL,	/* no inheritance */
+					FALSE,	/* start unowned (as per mutex_init) */
+					NULL)	/* no name */ 
+					) == NULL) {
+        fatal("failed to create mutex\n");
+    }
+    if ((done_queue.cond = CreateEvent(NULL,	/* no inheritance */
+					TRUE,	/* manually signaled - which I think is pthreads like ? */
+					FALSE,	/* start non signaled */
+					NULL)	/* no name */
+					) == NULL) {
+        fatal("failed to create condition event variable.\n");
+    }
+    done_queue.head = NULL;
+    done_queue.tailp = &done_queue.head;
+    done_queue.requests = 0;
+    done_queue.blocked = 0;
+
+    /* Create threads and get them to sit in their wait loop */
+    squidaio_thread_pool = memPoolCreate("aio_thread", sizeof(squidaio_thread_t));
+    assert(NUMTHREADS);
+    for (i = 0; i < NUMTHREADS; i++) {
+	threadp = memPoolAlloc(squidaio_thread_pool);
+	threadp->status = _THREAD_STARTING;
+	threadp->current_req = NULL;
+	threadp->requests = 0;
+	threadp->next = threads;
+	threads = threadp;
+	if ((threadp->thread = CreateThread(NULL,	/* no security attributes */
+				0,			/* use default stack size */
+				squidaio_thread_loop,	/* thread function */
+				threadp,		/* argument to thread function */
+				0,			/* use default creation flags */
+				&(threadp->dwThreadId))	/* returns the thread identifier */
+				) == NULL) {
+	    fprintf(stderr, "Thread creation failed\n");
+	    threadp->status = _THREAD_FAILED;
+	    continue;
+	}
+	/* Set the new thread priority above parent process */
+	SetThreadPriority(threadp->thread,THREAD_PRIORITY_ABOVE_NORMAL);
+    }
+
+    /* Create request pool */
+    squidaio_request_pool = memPoolCreate("aio_request", sizeof(squidaio_request_t));
+    squidaio_large_bufs = memPoolCreate("squidaio_large_bufs", AIO_LARGE_BUFS);
+    squidaio_medium_bufs = memPoolCreate("squidaio_medium_bufs", AIO_MEDIUM_BUFS);
+    squidaio_small_bufs = memPoolCreate("squidaio_small_bufs", AIO_SMALL_BUFS);
+    squidaio_tiny_bufs = memPoolCreate("squidaio_tiny_bufs", AIO_TINY_BUFS);
+    squidaio_micro_bufs = memPoolCreate("squidaio_micro_bufs", AIO_MICRO_BUFS);
+
+    squidaio_initialised = 1;
+}
+
+void
+squidaio_shutdown(void)
+{
+    squidaio_thread_t *threadp;
+    int i;
+    HANDLE * hthreads;
+
+    if (!squidaio_initialised)
+	return;
+
+    hthreads = (HANDLE *) xcalloc (NUMTHREADS, sizeof (HANDLE));
+    threadp = threads;
+    for (i = 0; i < NUMTHREADS; i++) {
+	threadp->exit = 1;
+	hthreads[i] = threadp->thread;
+	threadp = threadp->next;
+    }
+    ReleaseMutex(request_queue.mutex);
+    ResetEvent(request_queue.cond);
+    ReleaseMutex(done_queue.mutex);
+    ResetEvent(done_queue.cond);
+    Sleep(0);
+
+    WaitForMultipleObjects(NUMTHREADS, hthreads, TRUE, 2000);
+    for (i = 0; i < NUMTHREADS; i++) {
+	CloseHandle(hthreads[i]);
+    }
+    CloseHandle(main_thread);
+    squidaio_initialised = 0;
+    xfree(hthreads);
+}
+
+static DWORD WINAPI 
+squidaio_thread_loop(LPVOID lpParam)
+{
+    squidaio_thread_t *threadp = lpParam;
+    squidaio_request_t *request;
+    HANDLE cond; /* local copy of the event queue because win32 event handles
+		  * don't atomically release the mutex as cond variables do. */
+
+    /* lock the thread info */
+    if (WAIT_FAILED == WaitForSingleObject(request_queue.mutex, INFINITE)) {
+	fatal("Can't get ownership of mutex\n");
+    }
+    /* duplicate the handle */
+    if (!DuplicateHandle(GetCurrentProcess(),	    /* pseudo handle, don't close */
+			    request_queue.cond,	    /* handle to copy */
+			    GetCurrentProcess(),    /* pseudo handle, don't close */
+			    &cond,
+			    0,			    /* required access */
+			    FALSE,		    /* child process's don't inherit the handle */
+			    DUPLICATE_SAME_ACCESS)) 
+        fatal("Can't duplicate mutex handle\n");
+   if (!ReleaseMutex(request_queue.mutex)) {
+	CloseHandle(cond);
+	fatal("Can't release mutex\n");
+    }
+    Sleep(0);
+
+    while (1) {
+	DWORD rv;
+	threadp->current_req = request = NULL;
+	request = NULL;
+	/* Get a request to process */
+	threadp->status = _THREAD_WAITING;
+	if (threadp->exit) {
+	    CloseHandle(request_queue.mutex);
+	    CloseHandle(cond);
+	    return 0;
+	}
+	rv = WaitForSingleObject(request_queue.mutex, INFINITE);
+	if (rv == WAIT_FAILED) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+
+	while (!request_queue.head) {
+	if (!ReleaseMutex(request_queue.mutex)) {
+		CloseHandle(cond);
+		threadp->status = _THREAD_FAILED;
+		return 1;
+	    }
+	    Sleep(0);
+	    rv = WaitForSingleObject(cond, INFINITE);
+	    if (rv == WAIT_FAILED) {
+		CloseHandle(cond);
+		return 1;
+	    }
+	    rv = WaitForSingleObject(request_queue.mutex, INFINITE);
+	    if (rv == WAIT_FAILED) {
+		CloseHandle(cond);
+		return 1;
+	    }
+	}
+	request = request_queue.head;
+	if (request)
+	    request_queue.head = request->next;
+	if (!request_queue.head)
+	    request_queue.tailp = &request_queue.head;
+	if (!ReleaseMutex(request_queue.mutex)) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+	Sleep(0);
+	/* process the request */
+	threadp->status = _THREAD_BUSY;
+	request->next = NULL;
+	threadp->current_req = request;
+	errno = 0;
+	if (!request->cancelled) {
+	    switch (request->request_type) {
+	    case _AIO_OP_OPEN:
+		squidaio_do_open(request);
+		break;
+	    case _AIO_OP_READ:
+		squidaio_do_read(request);
+		break;
+	    case _AIO_OP_WRITE:
+		squidaio_do_write(request);
+		break;
+	    case _AIO_OP_CLOSE:
+		squidaio_do_close(request);
+		break;
+	    case _AIO_OP_UNLINK:
+		squidaio_do_unlink(request);
+		break;
+	    case _AIO_OP_TRUNCATE:
+		squidaio_do_truncate(request);
+		break;
+#if AIO_OPENDIR			/* Opendir not implemented yet */
+	    case _AIO_OP_OPENDIR:
+		squidaio_do_opendir(request);
+		break;
+#endif
+	    case _AIO_OP_STAT:
+		squidaio_do_stat(request);
+		break;
+	    default:
+		request->ret = -1;
+		request->err = EINVAL;
+		break;
+	    }
+	} else {		/* cancelled */
+	    request->ret = -1;
+	    request->err = EINTR;
+	}
+	threadp->status = _THREAD_DONE;
+	/* put the request in the done queue */
+	rv = WaitForSingleObject(done_queue.mutex, INFINITE);
+	if (rv == WAIT_FAILED) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+	*done_queue.tailp = request;
+	done_queue.tailp = &request->next;
+	if (!ReleaseMutex(done_queue.mutex)) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+	Sleep(0);
+	threadp->requests++;
+    }				/* while forever */
+    CloseHandle(cond);
+    return 0;
+}				/* squidaio_thread_loop */
+
+static void
+squidaio_queue_request(squidaio_request_t * request)
+{
+    static int high_start = 0;
+    debug(43, 9) ("squidaio_queue_request: %p type=%d result=%p\n",
+	request, request->request_type, request->resultp);
+    /* Mark it as not executed (failing result, no error) */
+    request->ret = -1;
+    request->err = 0;
+    /* Internal housekeeping */
+    request_queue_len += 1;
+    request->resultp->_data = request;
+    /* Play some tricks with the request_queue2 queue */
+    request->next = NULL;
+    if (WaitForSingleObject(request_queue.mutex, 0) == WAIT_OBJECT_0) {
+	if (request_queue2.head) {
+	    /* Grab blocked requests */
+	    *request_queue.tailp = request_queue2.head;
+	    request_queue.tailp = request_queue2.tailp;
+	}
+	/* Enqueue request */
+	*request_queue.tailp = request;
+	request_queue.tailp = &request->next;
+	if (!SetEvent(request_queue.cond))
+	    fatal("couldn't push queue\n");
+	if (!ReleaseMutex(request_queue.mutex)) {
+	    /* unexpected error */
+	    fatal("couldn't push queue\n");
+	}
+	Sleep(0);
+	if (request_queue2.head) {
+	    /* Clear queue of blocked requests */
+	    request_queue2.head = NULL;
+	    request_queue2.tailp = &request_queue2.head;
+	}
+    } else {
+	/* Oops, the request queue is blocked, use request_queue2 */
+	*request_queue2.tailp = request;
+	request_queue2.tailp = &request->next;
+    }
+    if (request_queue2.head) {
+	static int filter = 0;
+	static int filter_limit = 8;
+	if (++filter >= filter_limit) {
+	    filter_limit += filter;
+	    filter = 0;
+	    debug(43, 1) ("squidaio_queue_request: WARNING - Queue congestion\n");
+	}
+    }
+    /* Warn if out of threads */
+    if (request_queue_len > MAGIC1) {
+	static int last_warn = 0;
+	static int queue_high, queue_low;
+	if (high_start == 0) {
+	    high_start = squid_curtime;
+	    queue_high = request_queue_len;
+	    queue_low = request_queue_len;
+	}
+	if (request_queue_len > queue_high)
+	    queue_high = request_queue_len;
+	if (request_queue_len < queue_low)
+	    queue_low = request_queue_len;
+	if (squid_curtime >= (last_warn + 15) &&
+	    squid_curtime >= (high_start + 5)) {
+	    debug(43, 1) ("squidaio_queue_request: WARNING - Disk I/O overloading\n");
+	    if (squid_curtime >= (high_start + 15))
+		debug(43, 1) ("squidaio_queue_request: Queue Length: current=%d, high=%d, low=%d, duration=%ld\n",
+		    request_queue_len, queue_high, queue_low, (long int) (squid_curtime - high_start));
+	    last_warn = squid_curtime;
+	}
+    } else {
+	high_start = 0;
+    }
+    /* Warn if seriously overloaded */
+    if (request_queue_len > RIDICULOUS_LENGTH) {
+	debug(43, 0) ("squidaio_queue_request: Async request queue growing uncontrollably!\n");
+	debug(43, 0) ("squidaio_queue_request: Syncing pending I/O operations.. (blocking)\n");
+	squidaio_sync();
+	debug(43, 0) ("squidaio_queue_request: Synced\n");
+    }
+}				/* squidaio_queue_request */
+
+static void
+squidaio_cleanup_request(squidaio_request_t * requestp)
+{
+    squidaio_result_t *resultp = requestp->resultp;
+    int cancelled = requestp->cancelled;
+
+    /* Free allocated structures and copy data back to user space if the */
+    /* request hasn't been cancelled */
+    switch (requestp->request_type) {
+    case _AIO_OP_STAT:
+	if (!cancelled && requestp->ret == 0)
+	    xmemcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat));
+	squidaio_xfree(requestp->tmpstatp, sizeof(struct stat));
+	squidaio_xstrfree(requestp->path);
+	break;
+    case _AIO_OP_OPEN:
+	if (cancelled && requestp->ret >= 0)
+	    /* The open() was cancelled but completed */
+	    close(requestp->ret);
+	squidaio_xstrfree(requestp->path);
+	break;
+    case _AIO_OP_CLOSE:
+	if (cancelled && requestp->ret < 0)
+	    /* The close() was cancelled and never got executed */
+	    close(requestp->fd);
+	break;
+    case _AIO_OP_UNLINK:
+    case _AIO_OP_TRUNCATE:
+    case _AIO_OP_OPENDIR:
+	squidaio_xstrfree(requestp->path);
+	break;
+    case _AIO_OP_READ:
+	if (!cancelled && requestp->ret > 0)
+	    xmemcpy(requestp->bufferp, requestp->tmpbufp, requestp->ret);
+	squidaio_xfree(requestp->tmpbufp, requestp->buflen);
+	break;
+    case _AIO_OP_WRITE:
+	squidaio_xfree(requestp->tmpbufp, requestp->buflen);
+	break;
+    default:
+	break;
+    }
+    if (resultp != NULL && !cancelled) {
+	resultp->aio_return = requestp->ret;
+	resultp->aio_errno = requestp->err;
+    }
+    memPoolFree(squidaio_request_pool, requestp);
+}				/* squidaio_cleanup_request */
+
+
+int
+squidaio_cancel(squidaio_result_t * resultp)
+{
+    squidaio_request_t *request = resultp->_data;
+
+    if (request && request->resultp == resultp) {
+	debug(43, 9) ("squidaio_cancel: %p type=%d result=%p\n",
+	    request, request->request_type, request->resultp);
+	request->cancelled = 1;
+	request->resultp = NULL;
+	resultp->_data = NULL;
+	resultp->result_type = _AIO_OP_NONE;
+	return 0;
+    }
+    return 1;
+}				/* squidaio_cancel */
+
+
+int
+squidaio_open(const char *path, int oflag, mode_t mode, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = (char *) squidaio_xstrdup(path);
+    requestp->oflag = oflag;
+    requestp->mode = mode;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_OPEN;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_OPEN;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_open(squidaio_request_t * requestp)
+{
+    requestp->ret = open(requestp->path, requestp->oflag, requestp->mode);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_read(int fd, char *bufp, int bufs, off_t offset, int whence, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->bufferp = bufp;
+    requestp->tmpbufp = (char *) squidaio_xmalloc(bufs);
+    requestp->buflen = bufs;
+    requestp->offset = offset;
+    requestp->whence = whence;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_READ;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_READ;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_read(squidaio_request_t * requestp)
+{
+    lseek(requestp->fd, requestp->offset, requestp->whence);
+#ifdef _SQUID_MSWIN_
+    if (!ReadFile((HANDLE)_get_osfhandle(requestp->fd), requestp->tmpbufp, 
+	requestp->buflen, (LPDWORD)&requestp->ret, NULL)) {
+	WIN32_maperror(GetLastError());
+	requestp->ret = -1;
+    }
+#else
+    requestp->ret = read(requestp->fd, requestp->tmpbufp, requestp->buflen);
+#endif
+    requestp->err = errno;
+}
+
+
+int
+squidaio_write(int fd, char *bufp, int bufs, off_t offset, int whence, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->tmpbufp = (char *) squidaio_xmalloc(bufs);
+    xmemcpy(requestp->tmpbufp, bufp, bufs);
+    requestp->buflen = bufs;
+    requestp->offset = offset;
+    requestp->whence = whence;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_WRITE;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_WRITE;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_write(squidaio_request_t * requestp)
+{
+#ifdef _SQUID_MSWIN_
+    if (!WriteFile((HANDLE)_get_osfhandle(requestp->fd), requestp->tmpbufp, 
+	requestp->buflen, (LPDWORD)&requestp->ret, NULL)) {
+	WIN32_maperror(GetLastError());
+	requestp->ret = -1;
+    }
+#else
+    requestp->ret = write(requestp->fd, requestp->tmpbufp, requestp->buflen);
+#endif
+    requestp->err = errno;
+}
+
+
+int
+squidaio_close(int fd, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_CLOSE;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_CLOSE;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_close(squidaio_request_t * requestp)
+{
+    if((requestp->ret = close(requestp->fd)) < 0) {
+	debug(43, 0) ("squidaio_do_close: FD %d, errno %d\n", requestp->fd, errno);
+	close(requestp->fd);
+    }
+    requestp->err = errno;
+}
+
+
+int
+squidaio_stat(const char *path, struct stat *sb, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = (char *) squidaio_xstrdup(path);
+    requestp->statp = sb;
+    requestp->tmpstatp = (struct stat *) squidaio_xmalloc(sizeof(struct stat));
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_STAT;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_STAT;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_stat(squidaio_request_t * requestp)
+{
+    requestp->ret = stat(requestp->path, requestp->tmpstatp);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_unlink(const char *path, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = squidaio_xstrdup(path);
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_UNLINK;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_UNLINK;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_unlink(squidaio_request_t * requestp)
+{
+    requestp->ret = unlink(requestp->path);
+    requestp->err = errno;
+}
+
+int
+squidaio_truncate(const char *path, off_t length, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = (char *) squidaio_xstrdup(path);
+    requestp->offset = length;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_TRUNCATE;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_TRUNCATE;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_truncate(squidaio_request_t * requestp)
+{
+    requestp->ret = truncate(requestp->path, requestp->offset);
+    requestp->err = errno;
+}
+
+
+#if AIO_OPENDIR
+/* XXX squidaio_opendir NOT implemented yet.. */
+
+int
+squidaio_opendir(const char *path, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+    int len;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    resultp->result_type = _AIO_OP_OPENDIR;
+    return -1;
+}
+
+static void
+squidaio_do_opendir(squidaio_request_t * requestp)
+{
+    /* NOT IMPLEMENTED */
+}
+
+#endif
+
+static void
+squidaio_poll_queues(void)
+{
+    /* kick "overflow" request queue */
+    if (request_queue2.head &&
+	(WaitForSingleObject(request_queue.mutex, 0)==WAIT_OBJECT_0)) {
+	*request_queue.tailp = request_queue2.head;
+	request_queue.tailp = request_queue2.tailp;
+	if (!SetEvent(request_queue.cond))
+		fatal("couldn't push queue\n");
+	if (!ReleaseMutex(request_queue.mutex)) {
+	    /* unexpected error */
+	}
+	Sleep(0);
+	request_queue2.head = NULL;
+	request_queue2.tailp = &request_queue2.head;
+    }
+    /* poll done queue */
+    if (done_queue.head && 
+	(WaitForSingleObject(done_queue.mutex, 0)==WAIT_OBJECT_0)) {
+	struct squidaio_request_t *requests = done_queue.head;
+	done_queue.head = NULL;
+	done_queue.tailp = &done_queue.head;
+	if (!ReleaseMutex(done_queue.mutex)) {
+	    /* unexpected error */
+	}
+	Sleep(0);
+	*done_requests.tailp = requests;
+	request_queue_len -= 1;
+	while (requests->next) {
+	    requests = requests->next;
+	    request_queue_len -= 1;
+	}
+	done_requests.tailp = &requests->next;
+    }
+}
+
+squidaio_result_t *
+squidaio_poll_done(void)
+{
+    squidaio_request_t *request;
+    squidaio_result_t *resultp;
+    int cancelled;
+    int polled = 0;
+
+  AIO_REPOLL:
+    request = done_requests.head;
+    if (request == NULL && !polled) {
+	squidaio_poll_queues();
+	polled = 1;
+	request = done_requests.head;
+    }
+    if (!request) {
+	return NULL;
+    }
+    debug(43, 9) ("squidaio_poll_done: %p type=%d result=%p\n",
+	request, request->request_type, request->resultp);
+    done_requests.head = request->next;
+    if (!done_requests.head)
+	done_requests.tailp = &done_requests.head;
+    resultp = request->resultp;
+    cancelled = request->cancelled;
+    squidaio_debug(request);
+    debug(43, 5) ("DONE: %d -> %d\n", request->ret, request->err);
+    squidaio_cleanup_request(request);
+    if (cancelled)
+	goto AIO_REPOLL;
+    return resultp;
+}				/* squidaio_poll_done */
+
+int
+squidaio_operations_pending(void)
+{
+    return request_queue_len + (done_requests.head ? 1 : 0);
+}
+
+int
+squidaio_sync(void)
+{
+    /* XXX This might take a while if the queue is large.. */
+    do {
+	squidaio_poll_queues();
+    } while (request_queue_len > 0);
+    return squidaio_operations_pending();
+}
+
+int
+squidaio_get_queue_len(void)
+{
+    return request_queue_len;
+}
+
+static void
+squidaio_debug(squidaio_request_t * request)
+{
+    switch (request->request_type) {
+    case _AIO_OP_OPEN:
+	debug(43, 5) ("OPEN of %s to FD %d\n", request->path, request->ret);
+	break;
+    case _AIO_OP_READ:
+	debug(43, 5) ("READ on fd: %d\n", request->fd);
+	break;
+    case _AIO_OP_WRITE:
+	debug(43, 5) ("WRITE on fd: %d\n", request->fd);
+	break;
+    case _AIO_OP_CLOSE:
+	debug(43, 5) ("CLOSE of fd: %d\n", request->fd);
+	break;
+    case _AIO_OP_UNLINK:
+	debug(43, 5) ("UNLINK of %s\n", request->path);
+	break;
+    case _AIO_OP_TRUNCATE:
+	debug(43, 5) ("UNLINK of %s\n", request->path);
+	break;
+    default:
+	break;
+    }
+}
+
+void
+squidaio_stats(StoreEntry * sentry)
+{
+    squidaio_thread_t *threadp;
+    int i;
+    
+    if (!squidaio_initialised)
+	return;
+
+    storeAppendPrintf(sentry, "\n\nThreads Status:\n");
+    storeAppendPrintf(sentry, "#\tID\t# Requests\n");
+
+    threadp = threads;
+    for (i = 0; i < NUMTHREADS; i++) {
+	storeAppendPrintf(sentry, "%i\t0x%lx\t%ld\n", i + 1, threadp->dwThreadId, threadp->requests);
+	threadp = threadp->next;
+    }
+}
Index: squid/src/fs/awin32/async_io.c
diff -u /dev/null squid/src/fs/awin32/async_io.c:1.1.2.4
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/async_io.c	Sun Oct 20 06:46:20 2002
@@ -0,0 +1,383 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 32    Asynchronous Disk I/O
+ * AUTHOR: Pete Bentley <pete@demon.net>
+ * AUTHOR: Stewart Forster <slf@connect.com.au>
+ *
+ * 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_asyncufs.h"
+
+#define _AIO_OPEN	0
+#define _AIO_READ	1
+#define _AIO_WRITE	2
+#define _AIO_CLOSE	3
+#define _AIO_UNLINK	4
+#define _AIO_TRUNCATE	4
+#define _AIO_OPENDIR	5
+#define _AIO_STAT	6
+
+typedef struct squidaio_ctrl_t {
+    struct squidaio_ctrl_t *next;
+    int fd;
+    int operation;
+    AIOCB *done_handler;
+    void *done_handler_data;
+    squidaio_result_t result;
+    char *bufp;
+    FREE *free_func;
+    dlink_node node;
+} squidaio_ctrl_t;
+
+static struct {
+     int open_start;
+     int open_finish;
+     int close_start;
+     int close_finish;
+     int cancel;
+     int write_start;
+     int write_finish;
+     int read_start;
+     int read_finish;
+     int stat_start;
+     int stat_finish;
+     int unlink_start;
+     int unlink_finish;
+     int check_callback;
+} squidaio_counts;
+
+typedef struct squidaio_unlinkq_t {
+    char *path;
+    struct squidaio_unlinkq_t *next;
+} squidaio_unlinkq_t;
+
+static dlink_list used_list;
+static int initialised = 0;
+static OBJH aioStats;
+static MemPool *squidaio_ctrl_pool;
+static void aioFDWasClosed(int fd);
+
+static void
+aioFDWasClosed(int fd)
+{
+    if (fd_table[fd].flags.closing)
+	fd_close(fd);
+}
+
+void
+aioInit(void)
+{
+    if (initialised)
+	return;
+    squidaio_ctrl_pool = memPoolCreate("aio_ctrl", sizeof(squidaio_ctrl_t));
+    cachemgrRegister("squidaio_counts", "Async IO Function Counters",
+	aioStats, 0, 1);
+    initialised = 1;
+    comm_quick_poll_required();
+}
+
+void
+aioDone(void)
+{
+    if (!initialised)
+	return;
+    squidaio_shutdown();
+    memPoolDestroy(&squidaio_ctrl_pool);
+    initialised = 0;
+}
+
+void
+aioOpen(const char *path, int oflag, mode_t mode, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    squidaio_counts.open_start++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_OPEN;
+    ctrlp->result.data = ctrlp;
+    squidaio_open(path, oflag, mode, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}
+
+void
+aioClose(int fd)
+{
+    squidaio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    squidaio_counts.close_start++;
+    aioCancel(fd);
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = NULL;
+    ctrlp->done_handler_data = NULL;
+    ctrlp->operation = _AIO_CLOSE;
+    ctrlp->result.data = ctrlp;
+    squidaio_close(fd, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}
+
+void
+aioCancel(int fd)
+{
+    squidaio_ctrl_t *curr;
+    dlink_node *m, *next;
+
+    assert(initialised);
+    squidaio_counts.cancel++;
+    for (m = used_list.head; m; m = next) {
+	next = m->next;
+	curr = m->data;
+	if (curr->fd != fd)
+	    continue;
+
+	squidaio_cancel(&curr->result);
+
+	if (curr->done_handler) {
+	    AIOCB *callback = curr->done_handler;
+	    void *cbdata;
+	    curr->done_handler = NULL;
+	    debug(32, 2) ("this be aioCancel\n");
+	    if (cbdataReferenceValidDone(curr->done_handler_data, &cbdata))
+		callback(fd, cbdata, -2, -2);
+	}
+	dlinkDelete(m, &used_list);
+	memPoolFree(squidaio_ctrl_pool, curr);
+    }
+}
+
+
+void
+aioWrite(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data, FREE * free_func)
+{
+    squidaio_ctrl_t *ctrlp;
+    int seekmode;
+
+    assert(initialised);
+    squidaio_counts.write_start++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_WRITE;
+    ctrlp->bufp = bufp;
+    ctrlp->free_func = free_func;
+    if (offset >= 0)
+	seekmode = SEEK_SET;
+    else {
+	seekmode = SEEK_END;
+	offset = 0;
+    }
+    ctrlp->result.data = ctrlp;
+    squidaio_write(fd, bufp, len, offset, seekmode, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+}				/* aioWrite */
+
+
+void
+aioRead(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+    int seekmode;
+
+    assert(initialised);
+    squidaio_counts.read_start++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_READ;
+    if (offset >= 0)
+	seekmode = SEEK_SET;
+    else {
+	seekmode = SEEK_CUR;
+	offset = 0;
+    }
+    ctrlp->result.data = ctrlp;
+    squidaio_read(fd, bufp, len, offset, seekmode, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}				/* aioRead */
+
+void
+aioStat(char *path, struct stat *sb, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    squidaio_counts.stat_start++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_STAT;
+    ctrlp->result.data = ctrlp;
+    squidaio_stat(path, sb, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}				/* aioStat */
+
+void
+aioUnlink(const char *path, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+    assert(initialised);
+    squidaio_counts.unlink_start++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_UNLINK;
+    ctrlp->result.data = ctrlp;
+    squidaio_unlink(path, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+}				/* aioUnlink */
+
+void
+aioTruncate(const char *path, off_t length, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+    assert(initialised);
+    squidaio_counts.unlink_start++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_TRUNCATE;
+    ctrlp->result.data = ctrlp;
+    squidaio_truncate(path, length, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+}				/* aioTruncate */
+
+
+int
+aioCheckCallbacks(SwapDir * SD)
+{
+    squidaio_result_t *resultp;
+    squidaio_ctrl_t *ctrlp;
+    int retval = 0;
+
+    assert(initialised);
+    squidaio_counts.check_callback++;
+    for (;;) {
+	if ((resultp = squidaio_poll_done()) == NULL)
+	    break;
+	ctrlp = (squidaio_ctrl_t *) resultp->data;
+	switch (resultp->result_type){
+	    case _AIO_OP_NONE:
+	    case _AIO_OP_TRUNCATE:
+	    case _AIO_OP_OPENDIR:
+		 break;
+	    case _AIO_OP_OPEN:
+		 ++squidaio_counts.open_finish;
+		 break;
+	    case _AIO_OP_READ:
+		 ++squidaio_counts.read_finish;
+		 break;
+	    case _AIO_OP_WRITE:
+		 ++squidaio_counts.write_finish;
+		 break;
+	    case _AIO_OP_CLOSE:
+		 ++squidaio_counts.close_finish;
+		 break;
+	    case _AIO_OP_UNLINK:
+		 ++squidaio_counts.unlink_finish;
+		 break;
+	    case _AIO_OP_STAT:
+		 ++squidaio_counts.stat_finish;
+		 break;
+	}
+	if (ctrlp == NULL)
+	    continue;		/* XXX Should not happen */
+	dlinkDelete(&ctrlp->node, &used_list);
+	if (ctrlp->done_handler) {
+	    AIOCB *callback = ctrlp->done_handler;
+	    void *cbdata;
+	    ctrlp->done_handler = NULL;
+	    if (cbdataReferenceValidDone(ctrlp->done_handler_data, &cbdata)) {
+		retval = 1;	/* Return that we've actually done some work */
+		callback(ctrlp->fd, cbdata,
+		    ctrlp->result.aio_return, ctrlp->result.aio_errno);
+	    }
+	}
+	/* free data if requested to aioWrite() */
+	if (ctrlp->free_func)
+	    ctrlp->free_func(ctrlp->bufp);
+	if (ctrlp->operation == _AIO_CLOSE)
+	    aioFDWasClosed(ctrlp->fd);
+	memPoolFree(squidaio_ctrl_pool, ctrlp);
+    }
+    return retval;
+}
+
+void
+aioStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "ASYNC IO Counters:\n");
+    storeAppendPrintf(sentry, "Operation\t# Requests\tNumber serviced\n");
+    storeAppendPrintf(sentry, "open\t%d\t%d\n", squidaio_counts.open_start,squidaio_counts.open_finish);
+    storeAppendPrintf(sentry, "close\t%d\t%d\n", squidaio_counts.close_start,squidaio_counts.close_finish);
+    storeAppendPrintf(sentry, "cancel\t%d\t-\n", squidaio_counts.cancel);
+    storeAppendPrintf(sentry, "write\t%d\t%d\n", squidaio_counts.write_start,squidaio_counts.write_finish);
+    storeAppendPrintf(sentry, "read\t%d\t%d\n", squidaio_counts.read_start,squidaio_counts.read_finish);
+    storeAppendPrintf(sentry, "stat\t%d\t%d\n", squidaio_counts.stat_start,squidaio_counts.stat_finish);
+    storeAppendPrintf(sentry, "unlink\t%d\t%d\n", squidaio_counts.unlink_start,squidaio_counts.unlink_finish);
+    storeAppendPrintf(sentry, "check_callback\t%d\t-\n", squidaio_counts.check_callback);
+    storeAppendPrintf(sentry, "queue\t%d\t-\n", squidaio_get_queue_len());
+    squidaio_stats(sentry);
+}
+
+/* Flush all pending I/O */
+void
+aioSync(SwapDir * SD)
+{
+    if (!initialised)
+	return;			/* nothing to do then */
+    /* Flush all pending operations */
+    debug(32, 1) ("aioSync: flushing pending I/O operations\n");
+    do {
+	aioCheckCallbacks(SD);
+    } while (squidaio_sync());
+    debug(32, 1) ("aioSync: done\n");
+}
+
+int
+aioQueueSize(void)
+{
+    return memPoolInUseCount(squidaio_ctrl_pool);
+}
Index: squid/src/fs/awin32/store_asyncufs.h
diff -u /dev/null squid/src/fs/awin32/store_asyncufs.h:1.1.2.4
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/store_asyncufs.h	Sun Oct 20 06:46:20 2002
@@ -0,0 +1,131 @@
+/*
+ * store_aufs.h
+ *
+ * Internal declarations for the aufs routines
+ */
+
+#ifndef __STORE_ASYNCUFS_H__
+#define __STORE_ASYNCUFS_H__
+
+#ifdef AUFS_IO_THREADS
+#define NUMTHREADS AUFS_IO_THREADS
+#else
+#define NUMTHREADS (Config.cacheSwap.n_configured*16)
+#endif
+
+/* Queue limit where swapouts are deferred (load calculation) */
+#define MAGIC1 (NUMTHREADS*Config.cacheSwap.n_configured*5)
+/* Queue limit where swapins are deferred (open/create fails) */
+#define MAGIC2 (NUMTHREADS*Config.cacheSwap.n_configured*20)
+
+/* Which operations to run async */
+#define ASYNC_OPEN 1
+#define ASYNC_CLOSE 0
+#define ASYNC_CREATE 1
+#define ASYNC_WRITE 0
+#define ASYNC_READ 1
+
+enum _squidaio_request_type {
+    _AIO_OP_NONE = 0,
+    _AIO_OP_OPEN,
+    _AIO_OP_READ,
+    _AIO_OP_WRITE,
+    _AIO_OP_CLOSE,
+    _AIO_OP_UNLINK,
+    _AIO_OP_TRUNCATE,
+    _AIO_OP_OPENDIR,
+    _AIO_OP_STAT
+};
+typedef enum _squidaio_request_type squidaio_request_type;
+
+struct _squidaio_result_t {
+    int aio_return;
+    int aio_errno;
+    enum _squidaio_request_type result_type;
+    void *_data;		/* Internal housekeeping */
+    void *data;			/* Available to the caller */
+};
+
+typedef struct _squidaio_result_t squidaio_result_t;
+
+typedef void AIOCB(int fd, void *, int aio_return, int aio_errno);
+
+int squidaio_cancel(squidaio_result_t *);
+int squidaio_open(const char *, int, mode_t, squidaio_result_t *);
+int squidaio_read(int, char *, int, off_t, int, squidaio_result_t *);
+int squidaio_write(int, char *, int, off_t, int, squidaio_result_t *);
+int squidaio_close(int, squidaio_result_t *);
+int squidaio_stat(const char *, struct stat *, squidaio_result_t *);
+int squidaio_unlink(const char *, squidaio_result_t *);
+int squidaio_truncate(const char *, off_t length, squidaio_result_t *);
+int squidaio_opendir(const char *, squidaio_result_t *);
+squidaio_result_t *squidaio_poll_done(void);
+int squidaio_operations_pending(void);
+int squidaio_sync(void);
+int squidaio_get_queue_len(void);
+void squidaio_shutdown(void);
+void squidaio_stats(StoreEntry *);
+
+void aioInit(void);
+void aioDone(void);
+void aioCancel(int);
+void aioOpen(const char *, int, mode_t, AIOCB *, void *);
+void aioClose(int);
+void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *);
+void aioRead(int, int offset, char *, int size, AIOCB *, void *);
+void aioStat(char *, struct stat *, AIOCB *, void *);
+void aioUnlink(const char *, AIOCB *, void *);
+void aioTruncate(const char *, off_t length, AIOCB *, void *);
+int aioCheckCallbacks(SwapDir *);
+void aioSync(SwapDir *);
+int aioQueueSize(void);
+
+struct _squidaiostate_t {
+    int fd;
+    struct {
+	unsigned int close_request:1;
+	unsigned int reading:1;
+	unsigned int writing:1;
+	unsigned int opening:1;
+	unsigned int write_kicking:1;
+	unsigned int read_kicking:1;
+	unsigned int inreaddone:1;
+    } flags;
+    const char *read_buf;
+    link_list *pending_writes;
+    link_list *pending_reads;
+};
+
+struct _queued_write {
+    char *buf;
+    size_t size;
+    off_t offset;
+    FREE *free_func;
+};
+
+struct _queued_read {
+    char *buf;
+    size_t size;
+    off_t offset;
+    STRCB *callback;
+    void *callback_data;
+};
+
+typedef struct _squidaiostate_t squidaiostate_t;
+
+/* The squidaio_state memory pools */
+extern MemPool *squidaio_state_pool;
+extern MemPool *aufs_qread_pool;
+extern MemPool *aufs_qwrite_pool;
+
+/*
+ * Store IO stuff
+ */
+extern STOBJCREATE storeAufsCreate;
+extern STOBJOPEN storeAufsOpen;
+extern STOBJCLOSE storeAufsClose;
+extern STOBJREAD storeAufsRead;
+extern STOBJWRITE storeAufsWrite;
+extern STOBJUNLINK storeAufsUnlink;
+
+#endif
Index: squid/src/fs/awin32/store_dir_aufs.c
diff -u /dev/null squid/src/fs/awin32/store_dir_aufs.c:1.1.2.6
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/store_dir_aufs.c	Sun Oct 20 06:46:20 2002
@@ -0,0 +1,250 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 47    Store Directory 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_asyncufs.h"
+#include "ufscommon.h"
+
+MemPool *squidaio_state_pool = NULL;
+MemPool *aufs_qread_pool = NULL;
+MemPool *aufs_qwrite_pool = NULL;
+static int asyncufs_initialised = 0;
+
+static STDUMP storeAufsDirDump;
+static STCHECKOBJ storeAufsDirCheckObj;
+static void storeAufsDirIOUnlinkFile(char *path);
+
+
+/* The MAIN externally visible function */
+STSETUP storeFsSetup_awin32;
+
+/*
+ * storeAufsDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. AUFS filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeAufsDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    int loadav;
+    int ql;
+
+#if OLD_UNUSED_CODE
+    if (storeAufsDirExpiredReferenceAge(SD) < 300) {
+	debug(47, 3) ("storeAufsDirCheckObj: NO: LRU Age = %d\n",
+	    storeAufsDirExpiredReferenceAge(SD));
+	/* store_check_cachable_hist.no.lru_age_too_low++; */
+	return -1;
+    }
+#endif
+    ql = aioQueueSize();
+    if (ql == 0)
+	loadav = 0;
+    loadav = ql * 1000 / MAGIC1;
+    debug(47, 9) ("storeAufsDirCheckObj: load=%d\n", loadav);
+    return loadav;
+}
+
+void
+storeAufsDirIOUnlinkFile(char *path)
+{
+#if USE_TRUNCATE_NOT_UNLINK
+    aioTruncate(path, NULL, NULL);
+#else
+    aioUnlink(path, NULL, NULL);
+#endif
+}
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+static struct cache_dir_option options[] =
+{
+#if NOT_YET_DONE
+    {"L1", storeAufsDirParseL1, storeAufsDirDumpL1},
+    {"L2", storeAufsDirParseL2, storeAufsDirDumpL2},
+#endif
+    {NULL, NULL}
+};
+
+/*
+ * storeAufsDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+static void
+storeAufsDirReconfigure(SwapDir * sd, int index, char *path)
+{
+    int i;
+    int size;
+    int l1;
+    int l2;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to kbytes */
+    if (size <= 0)
+	fatal("storeAufsDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeAufsDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeAufsDirReconfigure: invalid level 2 directories value");
+
+    /* just reconfigure it */
+    if (size == sd->max_size)
+	debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n",
+	    path, size);
+    else
+	debug(3, 1) ("Cache dir '%s' size changed to %d KB\n",
+	    path, size);
+    sd->max_size = size;
+
+    parse_cachedir_options(sd, options, 0);
+
+    return;
+}
+
+void
+storeAufsDirDump(StoreEntry * entry, SwapDir * s)
+{
+    commonUfsDirDump (entry, s);
+    dump_cachedir_options(entry, options, s);
+}
+
+/*
+ * storeAufsDirParse *
+ * Called when a *new* fs is being setup.
+ */
+static void
+storeAufsDirParse(SwapDir * sd, int index, char *path)
+{
+    int i;
+    int size;
+    int l1;
+    int l2;
+    squidufsinfo_t *aioinfo;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to kbytes */
+    if (size <= 0)
+	fatal("storeAufsDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeAufsDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeAufsDirParse: invalid level 2 directories value");
+
+    aioinfo = xmalloc(sizeof(squidufsinfo_t));
+    if (aioinfo == NULL)
+	fatal("storeAufsDirParse: couldn't xmalloc() squidufsinfo_t!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = aioinfo;
+    aioinfo->l1 = l1;
+    aioinfo->l2 = l2;
+    aioinfo->swaplog_fd = -1;
+    aioinfo->map = NULL;	/* Debugging purposes */
+    aioinfo->suggest = 0;
+    aioinfo->io.storeDirUnlinkFile = storeAufsDirIOUnlinkFile;
+    sd->init = commonUfsDirInit;
+    sd->newfs = commonUfsDirNewfs;
+    sd->dump = storeAufsDirDump;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
+    sd->statfs = commonUfsDirStats;
+    sd->maintainfs = commonUfsDirMaintain;
+    sd->checkobj = storeAufsDirCheckObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
+    sd->callback = aioCheckCallbacks;
+    sd->sync = aioSync;
+    sd->obj.create = storeAufsCreate;
+    sd->obj.open = storeAufsOpen;
+    sd->obj.close = storeAufsClose;
+    sd->obj.read = storeAufsRead;
+    sd->obj.write = storeAufsWrite;
+    sd->obj.unlink = storeAufsUnlink;
+    sd->log.open = commonUfsDirOpenSwapLog;
+    sd->log.close = commonUfsDirCloseSwapLog;
+    sd->log.write = commonUfsDirSwapLog;
+    sd->log.clean.start = commonUfsDirWriteCleanStart;
+    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
+    sd->log.clean.done = commonUfsDirWriteCleanDone;
+
+    parse_cachedir_options(sd, options, 0);
+
+    /* Initialise replacement policy stuff */
+    sd->repl = createRemovalPolicy(Config.replPolicy);
+}
+
+/*
+ * Initial setup / end destruction
+ */
+static void
+storeAufsDirDone(void)
+{
+    aioDone();
+    memPoolDestroy(&squidaio_state_pool);
+    memPoolDestroy(&aufs_qread_pool);
+    memPoolDestroy(&aufs_qwrite_pool);
+    asyncufs_initialised = 0;
+}
+
+void
+storeFsSetup_awin32(storefs_entry_t * storefs)
+{
+    assert(!asyncufs_initialised);
+    storefs->parsefunc = storeAufsDirParse;
+    storefs->reconfigurefunc = storeAufsDirReconfigure;
+    storefs->donefunc = storeAufsDirDone;
+    squidaio_state_pool = memPoolCreate("AWIN32 IO State data", sizeof(squidaiostate_t));
+    aufs_qread_pool = memPoolCreate("AWIN32 Queued read data",
+	sizeof(queued_read));
+    aufs_qwrite_pool = memPoolCreate("AWIN32 Queued write data",
+	sizeof(queued_write));
+
+    asyncufs_initialised = 1;
+    aioInit();
+}
Index: squid/src/fs/awin32/store_io_aufs.c
diff -u /dev/null squid/src/fs/awin32/store_io_aufs.c:1.1.2.5
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/fs/awin32/store_io_aufs.c	Sun Oct 20 06:46:20 2002
@@ -0,0 +1,473 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 79    Storage Manager awin32 Interface
+ * AUTHOR: Robert Collins
+ *
+ * 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_asyncufs.h"
+#include "ufscommon.h"
+
+#if ASYNC_READ
+static AIOCB storeAufsReadDone;
+#else
+static DRCB storeAufsReadDone;
+#endif
+#if ASYNC_WRITE
+static AIOCB storeAufsWriteDone;
+#else
+static DWCB storeAufsWriteDone;
+#endif
+static void storeAufsIOCallback(storeIOState * sio, int errflag);
+static AIOCB storeAufsOpenDone;
+static int storeAufsSomethingPending(storeIOState *);
+static int storeAufsKickWriteQueue(storeIOState * sio);
+static CBDUNL storeAufsIOFreeEntry;
+
+CBDATA_TYPE(storeIOState);
+
+/* === PUBLIC =========================================================== */
+
+/* open for reading */
+storeIOState *
+storeAufsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
+    STIOCB * callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    char *path = commonUfsDirFullPath(SD, f, NULL);
+    storeIOState *sio;
+#if !ASYNC_OPEN
+    int fd;
+#endif
+    debug(79, 3) ("storeAufsOpen: fileno %08X\n", f);
+    /*
+     * we should detect some 'too many files open' condition and return
+     * NULL here.
+     */
+#ifdef MAGIC2
+    if (aioQueueSize() > MAGIC2)
+	return NULL;
+#endif
+#if !ASYNC_OPEN
+    fd = file_open(path, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+	debug(79, 3) ("storeAufsOpen: got failure (%d)\n", errno);
+	return NULL;
+    }
+#endif
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeAufsIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = memPoolAlloc(squidaio_state_pool);
+    ((squidaiostate_t *) (sio->fsstate))->fd = -1;
+    ((squidaiostate_t *) (sio->fsstate))->flags.opening = 1;
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY | O_BINARY;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = e;
+    Opening_FD++;
+#if ASYNC_OPEN
+    aioOpen(path, O_RDONLY | O_BINARY, 0644, storeAufsOpenDone, sio);
+#else
+    storeAufsOpenDone(fd, sio, fd, 0);
+#endif
+    return sio;
+}
+
+/* open for creating */
+storeIOState *
+storeAufsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
+{
+    char *path;
+    storeIOState *sio;
+    sfileno filn;
+    sdirno dirn;
+#if !ASYNC_CREATE
+    int fd;
+#endif
+
+    /* Allocate a number */
+    dirn = SD->index;
+    filn = commonUfsDirMapBitAllocate(SD);
+    path = commonUfsDirFullPath(SD, filn, NULL);
+
+    debug(79, 3) ("storeAufsCreate: fileno %08X\n", filn);
+    /*
+     * we should detect some 'too many files open' condition and return
+     * NULL here.
+     */
+#ifdef MAGIC2
+    if (aioQueueSize() > MAGIC2)
+	return NULL;
+#endif
+#if !ASYNC_CREATE
+    fd = file_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (fd < 0) {
+	debug(79, 3) ("storeAufsCreate: got failure (%d)\n", errno);
+	return NULL;
+    }
+#endif
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeAufsIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = memPoolAlloc(squidaio_state_pool);
+    ((squidaiostate_t *) (sio->fsstate))->fd = -1;
+    ((squidaiostate_t *) (sio->fsstate))->flags.opening = 1;
+    sio->swap_filen = filn;
+    sio->swap_dirn = dirn;
+    sio->mode = O_WRONLY | O_BINARY;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = (StoreEntry *) e;
+    Opening_FD++;
+#if ASYNC_CREATE
+    aioOpen(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644, storeAufsOpenDone, sio);
+#else
+    storeAufsOpenDone(fd, sio, fd, 0);
+#endif
+
+    /* now insert into the replacement policy */
+    commonUfsDirReplAdd(SD, e);
+    return sio;
+
+}
+
+
+
+/* Close */
+void
+storeAufsClose(SwapDir * SD, storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 3) ("storeAufsClose: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    if (storeAufsSomethingPending(sio)) {
+	aiostate->flags.close_request = 1;
+	return;
+    }
+    storeAufsIOCallback(sio, DISK_OK);
+}
+
+
+/* Read */
+void
+storeAufsRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    assert(!aiostate->flags.reading);
+    if (aiostate->fd < 0) {
+	struct _queued_read *q;
+	debug(79, 3) ("storeAufsRead: queueing read because FD < 0\n");
+	assert(aiostate->flags.opening);
+	assert(aiostate->pending_reads == NULL);
+	q = memPoolAlloc(aufs_qread_pool);
+	q->buf = buf;
+	q->size = size;
+	q->offset = offset;
+	q->callback = callback;
+	q->callback_data = callback_data;
+	linklistPush(&(aiostate->pending_reads), q);
+	return;
+    }
+    sio->read.callback = callback;
+    sio->read.callback_data = cbdataReference(callback_data);
+    aiostate->read_buf = buf;
+    debug(79, 3) ("storeAufsRead: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    sio->offset = offset;
+    aiostate->flags.reading = 1;
+#if ASYNC_READ
+    aioRead(aiostate->fd, offset, buf, size, storeAufsReadDone, sio);
+#else
+    file_read(aiostate->fd, buf, size, offset, storeAufsReadDone, sio);
+#endif
+}
+
+
+/* Write */
+void
+storeAufsWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 3) ("storeAufsWrite: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    if (aiostate->fd < 0) {
+	/* disk file not opened yet */
+	struct _queued_write *q;
+	assert(aiostate->flags.opening);
+	q = memPoolAlloc(aufs_qwrite_pool);
+	q->buf = buf;
+	q->size = size;
+	q->offset = offset;
+	q->free_func = free_func;
+	linklistPush(&(aiostate->pending_writes), q);
+	return;
+    }
+#if ASYNC_WRITE
+    if (aiostate->flags.writing) {
+	struct _queued_write *q;
+	debug(79, 3) ("storeAufsWrite: queuing write\n");
+	q = memPoolAlloc(aufs_qwrite_pool);
+	q->buf = buf;
+	q->size = size;
+	q->offset = offset;
+	q->free_func = free_func;
+	linklistPush(&(aiostate->pending_writes), q);
+	return;
+    }
+    aiostate->flags.writing = 1;
+    aioWrite(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio,
+	free_func);
+#else
+    file_write(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio,
+	free_func);
+#endif
+}
+
+/* Unlink */
+void
+storeAufsUnlink(SwapDir * SD, StoreEntry * e)
+{
+    debug(79, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen);
+    commonUfsDirReplRemove(e);
+    commonUfsDirMapBitReset(SD, e->swap_filen);
+    commonUfsDirUnlinkFile(SD, e->swap_filen);
+}
+
+/*  === STATIC =========================================================== */
+
+static int
+storeAufsKickWriteQueue(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    struct _queued_write *q = linklistShift(&aiostate->pending_writes);
+    if (NULL == q)
+	return 0;
+    debug(79, 3) ("storeAufsKickWriteQueue: writing queued chunk of %ld bytes\n",
+	(long int) q->size);
+    storeAufsWrite(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->free_func);
+    memPoolFree(aufs_qwrite_pool, q);
+    return 1;
+}
+
+static int
+storeAufsKickReadQueue(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    struct _queued_read *q = linklistShift(&(aiostate->pending_reads));
+    if (NULL == q)
+	return 0;
+    debug(79, 3) ("storeAufsKickReadQueue: reading queued request of %ld bytes\n",
+	(long int) q->size);
+    storeAufsRead(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->callback, q->callback_data);
+    memPoolFree(aufs_qread_pool, q);
+    return 1;
+}
+
+static void
+storeAufsOpenDone(int unused, void *my_data, int fd, int errflag)
+{
+    storeIOState *sio = my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag);
+    Opening_FD--;
+    aiostate->flags.opening = 0;
+    if (errflag || fd < 0) {
+	errno = errflag;
+	debug(79, 0) ("storeAufsOpenDone: %s\n", xstrerror());
+	debug(79, 1) ("\t%s\n", commonUfsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+	storeAufsIOCallback(sio, DISK_ERROR);
+	return;
+    }
+    store_open_disk_fd++;
+    aiostate->fd = fd;
+    commSetCloseOnExec(fd);
+    fd_open(fd, FD_FILE, commonUfsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+    if (FILE_MODE(sio->mode) == O_WRONLY) {
+	if (storeAufsKickWriteQueue(sio))
+	    return;
+    } else if (FILE_MODE(sio->mode) == O_RDONLY) {
+	if (storeAufsKickReadQueue(sio))
+	    return;
+    }
+    if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+    debug(79, 3) ("storeAufsOpenDone: exiting\n");
+}
+
+#if ASYNC_READ
+static void
+storeAufsReadDone(int fd, void *my_data, int len, int errflag)
+#else
+static void
+storeAufsReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
+#endif
+{
+    storeIOState *sio = my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    STRCB *callback = sio->read.callback;
+    void *cbdata;
+    ssize_t rlen;
+    debug(79, 3) ("storeAufsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
+	sio->swap_dirn, sio->swap_filen, fd, len);
+    aiostate->flags.inreaddone = 1;
+    aiostate->flags.reading = 0;
+    if (errflag) {
+	debug(79, 3) ("storeAufsReadDone: got failure (%d)\n", errflag);
+	rlen = -1;
+    } else {
+	rlen = (ssize_t) len;
+	sio->offset += len;
+    }
+#if ASYNC_READ
+    /* translate errflag from errno to Squid disk error */
+    errno = errflag;
+    if (errflag)
+	errflag = DISK_ERROR;
+    else
+	errflag = DISK_OK;
+#else
+    if (errflag == DISK_EOF)
+	errflag = DISK_OK;	/* EOF is signalled by len == 0, not errors... */
+#endif
+    assert(callback);
+    sio->read.callback = NULL;
+    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
+	callback(cbdata, aiostate->read_buf, rlen);
+    aiostate->flags.inreaddone = 0;
+    if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+}
+
+#if ASYNC_WRITE
+static void
+storeAufsWriteDone(int fd, void *my_data, int len, int errflag)
+#else
+static void
+storeAufsWriteDone(int fd, int errflag, size_t len, void *my_data)
+#endif
+{
+    static int loop_detect = 0;
+    storeIOState *sio = my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 3) ("storeAufsWriteDone: dirno %d, fileno %08X, FD %d, len %ld, err=%d\n",
+	sio->swap_dirn, sio->swap_filen, fd, (long int) len, errflag);
+#if ASYNC_WRITE
+    /* Translate from errno to Squid disk error */
+    errno = errflag;
+    if (errflag)
+	errflag = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
+    else
+	errflag = DISK_OK;
+#endif
+    assert(++loop_detect < 10);
+    aiostate->flags.writing = 0;
+    if (errflag) {
+	debug(79, 0) ("storeAufsWriteDone: got failure (%d)\n", errflag);
+	storeAufsIOCallback(sio, errflag);
+	loop_detect--;
+	return;
+    }
+    sio->offset += len;
+#if ASYNC_WRITE
+    if (!storeAufsKickWriteQueue(sio))
+	0;
+    else if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+#else
+    if (!aiostate->flags.write_kicking) {
+	aiostate->flags.write_kicking = 1;
+	while (storeAufsKickWriteQueue(sio))
+	    (void) 0;
+	aiostate->flags.write_kicking = 0;
+	if (aiostate->flags.close_request)
+	    storeAufsIOCallback(sio, errflag);
+    }
+#endif
+    loop_detect--;
+}
+
+static void
+storeAufsIOCallback(storeIOState * sio, int errflag)
+{
+    STIOCB *callback = sio->callback;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    int fd = aiostate->fd;
+    debug(79, 3) ("storeAufsIOCallback: errflag=%d\n", errflag);
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+    if (callback) {
+	void *cbdata;
+	sio->callback = NULL;
+	if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
+	    callback(cbdata, errflag, sio);
+    }
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+    aiostate->fd = -1;
+    cbdataFree(sio);
+    if (fd < 0)
+	return;
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+    aioClose(fd);
+    fd_close(fd);
+    store_open_disk_fd--;
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+}
+
+
+static int
+storeAufsSomethingPending(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    if (aiostate->flags.reading)
+	return 1;
+    if (aiostate->flags.writing)
+	return 1;
+    if (aiostate->flags.opening)
+	return 1;
+    if (aiostate->flags.inreaddone)
+	return 1;
+    return 0;
+}
+
+
+/*      
+ * Clean up references from the SIO before it gets released.
+ * The actuall SIO is managed by cbdata so we do not need
+ * to bother with that.
+ */
+static void
+storeAufsIOFreeEntry(void *sio)
+{
+    memPoolFree(squidaio_state_pool, ((storeIOState *) sio)->fsstate);
+}
Index: squid/src/fs/ufs/store_io_ufs.c
diff -u squid/src/fs/ufs/store_io_ufs.c:1.10 squid/src/fs/ufs/store_io_ufs.c:1.2.24.8
--- squid/src/fs/ufs/store_io_ufs.c:1.10	Sat Oct 12 02:58:27 2002
+++ squid/src/fs/ufs/store_io_ufs.c	Sun Oct 20 06:46:23 2002
@@ -78,7 +78,7 @@
     ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
     ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
     if (fstat(fd, &sb) == 0)
-	sio->st_size = sb.st_size;
+	sio->st_size = (size_t) sb.st_size;
     store_open_disk_fd++;
 
     /* We should update the heap/dlink position here ! */