Merge g_with_canl_1_0 branch to master.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Wed, 16 Jan 2013 11:01:55 +0000 (12:01 +0100)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 26 Feb 2013 14:26:28 +0000 (15:26 +0100)
13 files changed:
org.gridsite.core/CHANGES
org.gridsite.core/VERSION
org.gridsite.core/project/debian.control
org.gridsite.core/project/debian.rules
org.gridsite.core/project/version.properties
org.gridsite.core/src/Makefile
org.gridsite.core/src/canl_mod_gridsite.c [new file with mode: 0644]
org.gridsite.core/src/canl_mod_ssl-private.h [new file with mode: 0644]
org.gridsite.core/src/grst_canl_x509.c [new file with mode: 0644]
org.gridsite.core/src/htcp.c
org.gridsite.core/src/htproxyput.c
org.gridsite.core/src/make-debian-files
org.gridsite.core/src/make-gridsite-spec

index f8c0a15..93d82e6 100644 (file)
@@ -1,13 +1,23 @@
-- ==== GridSite version 1.7.24 ====
-* Mon Dec 03 2012 Zdenek Sustr <sustr4@cesnet.cz>
-- Allowing exceptions for length constraints in proxy certificates (GGUS #87573)
-- ==== GridSite version 1.7.24 ====
-* Tue Oct 30 2012 Zdenek Sustr <sustr4@cesnet.cz>
-- Fixed Memory Leak
-- ==== GridSite version 1.7.23 ====
-* Tue Oct 09 2012 Zdenek Sustr <sustr4@cesnet.cz>
-- Fixed Memory Leak
-- Packaging fixes for SL6
+- ==== GridSite version 2.0.3 ====
+* Wed Nov 14 2012 Zdeněk Šustr <sustr4@cesnet.cz>
+- Segmentation fault in htproxyput fixed (occurred if run by non-root)
+- ==== GridSite version 2.0.2 ====
+* Tue Nov 06 2012 Zdeněk Šustr <sustr4@cesnet.cz>
+- Certificates made available in the GRST structure
+- ==== GridSite version 2.0.1 ====
+* Fri Oct 22 2012 František Dvořák <valtri@civ.zcu.cz>
+- one more update of the packaging for Debian
+* Fri Oct 19 2012 František Dvořák <valtri@civ.zcu.cz>
+- update of the packaging for Debian due to major version bump
+- add DESTDIR to install target
+* Thu Oct 18 2012 František Dvořák <valtri@civ.zcu.cz>
+- fix packaging for SL6 and Fedora (curl-devel -> libcurl-devel)
+- big library versions cleanup, using libtool to compile and link
+- ==== GridSite version 2.0.0 ====
+* Wed Oct 17 2012 Marcel Poul <marcel.poul@cern.ch>
+- Internals rewritten to use caNl
+* Wed Oct 17 2012 František Dvořák <valtri@civ.zcu.cz>
+- Unused tools removed
 - ==== GridSite version 1.7.22 ====
 * Tue Jul 24 2012 František Dvořák <valtri@civ.zcu.cz>
 - Proper obsoletes in rpm packages (versioned, not platform dependent)
index 3097f0a..f0b87da 100644 (file)
@@ -1,5 +1,5 @@
-MAJOR_VERSION=1
-MINOR_VERSION=1.7
-PATCH_VERSION=1.7.25
-DEFVERSION=0x010725
+MAJOR_VERSION=2
+MINOR_VERSION=2.0
+PATCH_VERSION=2.0.3
+DEFVERSION=0x020003
 VERSION=$(PATCH_VERSION)
index eae58f5..0a22564 100644 (file)
@@ -5,13 +5,14 @@ Maintainer: EMI CESNET security <emi-lb@metacentrum.cz>
 Build-Depends: debhelper (>= 7.0.50~),
        apache2-prefork-dev,
        gsoap,
+       libcanl-c-dev,
        libglobus-gssapi-gsi-dev,
        libcurl4-openssl-dev,
        doxygen,
-       libfuse-dev,
        libxml2,
        libxml2-dev,
        libssl-dev,
+       libtool,
        pkg-config
 Standards-Version: 3.9.1
 Homepage: http://gridsite.org
@@ -25,7 +26,7 @@ Depends: \${shlibs:Depends}, \${misc:Depends}
 Description: GridSite mod_gridsite module and CGI binaries for Apache httpd
  GridSite Apache module and CGI binaries.
 
-Package: libgridsite${MINOR_VERSION}
+Package: libgridsite2
 Section: libs
 Architecture: any
 Depends: \${shlibs:Depends}, \${misc:Depends}, libssl-dev
@@ -38,7 +39,7 @@ Description: GridSite libraries and documentation
 Package: libgridsite-dev
 Section: libdevel
 Architecture: any
-Depends: libgridsite${MINOR_VERSION} (= \${binary:Version}),
+Depends: libgridsite2 (= \${binary:Version}),
          \${misc:Depends}
 Description: GridSite static libraries and headers
  Development files for GridSite - .a libraries and .h headers.
@@ -53,27 +54,17 @@ Description: HTTP(S) read/write client and other GridSite commands
  onto remote servers using HTTPS. htcp is similar to scp(1), but uses
  HTTP/HTTPS rather than ssh as its transfer protocol.
 
-Package: gridsite-gsexec
-Section: web
-Architecture: any
-Depends: \${shlibs:Depends}, \${misc:Depends}
-Description: gsexec binary for the Apache HTTP server
- This package includes the /usr/sbin/gsexec binary which can be installed
- to allow the Apache HTTP server to run CGI programs (and any programs
- executed by SSI pages) as a user other than the 'apache' user. gsexec
- is a drop-in replacement for suexec, with extended functionality for use
- with GridSite and Grid Security credentials.
-
-Package: gridsite-slashgrid
-Section: web
-Architecture: any
-Depends: \${shlibs:Depends}, \${misc:Depends}, libcurl3 (>=7.12.1), fuse-utils
-Description: SlashGrid daemon
- SlashGrid provides remote virtual filesystems under /grid (\"slash grid\").
-
 Package: gridsite-service-clients
 Section: web
 Architecture: any
 Depends: \${shlibs:Depends}, \${misc:Depends}
 Description: GridSite WS htproxyput
  GridSite WS delegation client, htproxyput
+
+Package: gridsite-dbg
+Section: debug
+Architecture: any
+Priority: extra
+Depends: libgridsite2 (= \${binary:Version}), \${misc:Depends}
+Description: Debugging symbols for GridSite
+ This package contains debugging symbols for GridSite library and programs.
index 8ecc773..67d6692 100644 (file)
@@ -1,10 +1,10 @@
 #! /usr/bin/make -f
 
 override_dh_auto_build:
-       \$(MAKE) -C src prefix=/usr build gridsite-delegation.cgi htproxyput slashgrid
+       \$(MAKE) -C src prefix=/usr build gridsite-delegation.cgi htproxyput
 
 override_dh_auto_install:
-       \$(MAKE) -C src prefix=../debian/tmp/usr httpd_name=apache2 RPM_BUILD_ROOT=../debian/tmp install install-ws install-slashgrid post-install-debian
+       \$(MAKE) -C src prefix=/usr httpd_name=apache2 DESTDIR=\$(shell pwd)/debian/tmp install install-ws post-install-debian
 
 %:
-       dh -Smakefile -Dsrc \$@
+       dh -Smakefile -Dsrc --dbg-package=gridsite-dbg \$@
index c294750..3a05859 100644 (file)
@@ -61,9 +61,11 @@ GSOAP_CFLAGS=`pkg-config gsoap --cflags`
 GSOAP_LIBS=`pkg-config gsoap --libs`
 GSOAPSSL_CFLAGS=`pkg-config gsoapssl --cflags`
 GSOAPSSL_LIBS=`pkg-config gsoapssl --libs`
+CANL_C_CFLAGS=
+CANL_C_LIBS=-lcanl_c
 
 ifndef MYCFLAGS
-export MYCFLAGS=-I. -I../interface -fPIC -DLINUX=2 -D_REENTRANT -D_LARGEFILE64_SOURCE $(HTTPD_FLAGS) 
+export MYCFLAGS=-I. -I../interface -DPIC -fPIC -DLINUX=2 -D_REENTRANT -D_LARGEFILE64_SOURCE $(HTTPD_FLAGS)
 endif
 
 ifndef MYLDFLAGS
@@ -72,243 +74,251 @@ endif
 
 -include Makefile.inc
 
+# In order to use libtool versioning correcty, we must have:
+#
+# current = major + minor + offset
+# revision = patch
+# age = minor
+#
+# where offset is a sum of maximal released minor's of all previous major's
+#
+
+# counted minors: -
+offset=0
+
+version_info:=-version-info $(shell \
+       perl -e '$$,=":"; @F=split "\\.","$(PATCH_VERSION)"; print $$F[0]+$$F[1]+${offset},$$F[2],$$F[1]' )
+
 ifdef OPENSSL_GLOBUS_LIBS
 PC_FILES=gridsite-openssl.pc gridsite-globus.pc
 else
 PC_FILES=gridsite-openssl.pc
 endif
 
+# caNl adoption part
+# caNl library and headers have to be in standard system places.
+# Print all warnings
+ifeq ($(GRIDSITE_WITH_CANL),no)
+MOD_GRIDSITE_FILE=mod_gridsite.c
+GRST_X509_OBJS=grst_x509.lo
+MOD_SSLPRIVATE_HEADER=mod_ssl-private.h
+MYCANLLDFLAGS=
+else
+GRST_X509_OBJS=grst_canl_x509.lo
+MOD_GRIDSITE_FILE=canl_mod_gridsite.c
+MOD_SSLPRIVATE_HEADER=canl_mod_ssl-private.h
+MYCANLLDFLAGS=$(CANL_C_LIBS)
+endif
+
+ifeq ($(WALL),yes)
+MYCFLAGS += -Wall
+endif
+
+CC=gcc
+COMPILE=libtool --mode=compile $(CC) $(CFLAGS)
+LINK=libtool --mode=link $(CC) $(LDFLAGS)
+INSTALL=libtool --mode=install install
+
+GRIDSITE_OBJS=grst_err.lo $(GRST_X509_OBJS) grst_gacl.lo grst_xacml.lo grst_http.lo grst_asn1.lo grst_htcp.lo
+GRIDSITE_GLOBUS_OBJS=grst_err_globus.lo grst_x509_globus.lo grst_gacl_globus.lo grst_xacml_globus.lo  grst_http_globus.lo grst_asn1_globus.lo grst_htcp_globus.lo
+GRIDSITE_NOSSL_OBJS=grst_err_nossl.lo grst_gacl_nossl.lo grst_http_nossl.lo grst_xacml_nossl.lo grst_htcp_nossl.lo
+
 #
 # Build
 #
 
-build: apidoc build-lib \
-       htcp gridsite-copy.cgi gridsite-storage.cgi mod_gridsite.so \
-       urlencode findproxyfile gsexec real-gridsite-admin.cgi
+build: apidoc build-lib htcp mod_gridsite.so urlencode findproxyfile \
+       real-gridsite-admin.cgi gridsite-delegation.cgi
 
-build-lib: libgridsite_globus.so.$(VERSION) libgridsite_globus.a \
-           libgridsite.so.$(VERSION) libgridsite.a \
-           libgridsite_nossl.so.$(VERSION) libgridsite_nossl.a $(PC_FILES)
+build-lib: libgridsite_globus.la libgridsite_globus.a \
+           libgridsite.la libgridsite.a \
+           libgridsite_nossl.la libgridsite_nossl.a $(PC_FILES)
 
 # First, normal versions using system OpenSSL rather than Globus OpenSSL
 
-libgridsite.so.$(VERSION): grst_err.o grst_x509.o grst_gacl.o grst_xacml.o \
-                           grst_http.o grst_asn1.o grst_htcp.o
-       gcc -shared -Wl,-soname,libgridsite.so.$(MINOR_VERSION) \
-         -o libgridsite.so.$(PATCH_VERSION) \
-         grst_err.o grst_x509.o grst_gacl.o grst_xacml.o grst_http.o \
-         grst_asn1.o grst_htcp.o -lcrypto $(XML2_LIBS)
-       ln -sf libgridsite.so.$(VERSION) libgridsite.so
-       ln -sf libgridsite.so.$(VERSION) libgridsite.so.$(MINOR_VERSION)
+libgridsite.a: libgridsite.la
 
-libgridsite.a: grst_err.o grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o
-       ar src libgridsite.a grst_err.o grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o
+libgridsite.la: $(GRIDSITE_OBJS)
+       $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \
+          -o $@ $+ -lcrypto ${MYCANLLDFLAGS} $(XML2_LIBS)
 
-grst_err.o: grst_err.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
+grst_err.lo: grst_err.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
            -I/usr/kerberos/include \
-           -c grst_err.c
+           -c $< -o $@
+
+grst_x509.lo: grst_x509.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
 
-grst_x509.o: grst_x509.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
-            -I/usr/kerberos/include -c grst_x509.c
+grst_canl_x509.lo: grst_canl_x509.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(CANL_C_CFLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
 
-grst_gacl.o: grst_gacl.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
-            -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_gacl.c
+grst_gacl.lo: grst_gacl.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
+            -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@
 
-grst_xacml.o: grst_xacml.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
-            -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_xacml.c
+grst_xacml.lo: grst_xacml.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
+            -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@
 
-grst_http.o: grst_http.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
-                        -I/usr/kerberos/include -c grst_http.c
+grst_http.lo: grst_http.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
+                        -I/usr/kerberos/include -c $< -o $@
 
-grst_asn1.o: grst_asn1.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
-            -I/usr/kerberos/include -c grst_asn1.c
+grst_asn1.lo: grst_asn1.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
 
-grst_htcp.o: grst_htcp.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) \
-            -I/usr/kerberos/include -c grst_htcp.c
+grst_htcp.lo: grst_htcp.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
 
 # Then build versions using Globus OpenSSL if configured
 
 ifdef OPENSSL_GLOBUS_LIBS
 
-libgridsite_globus.so.$(VERSION): grst_err_globus.o \
-               grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o \
-               grst_asn1_globus.o grst_xacml_globus.o grst_htcp_globus.o 
-       gcc -shared -Wl,-soname,libgridsite_globus.so.$(MINOR_VERSION) \
-         -o libgridsite_globus.so.$(PATCH_VERSION) \
-         grst_err_globus.o grst_x509_globus.o grst_gacl_globus.o grst_xacml_globus.o \
-         grst_http_globus.o grst_asn1_globus.o $(XML2_LIBS) $(OPENSSL_GLOBUS_LIBS)
-       ln -sf libgridsite_globus.so.$(VERSION) libgridsite_globus.so
-
-libgridsite_globus.a: grst_err_globus.o grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o grst_asn1_globus.o
-       ar src libgridsite_globus.a \
-               grst_err_globus.o grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o grst_asn1_globus.o
-
-grst_err_globus.o: grst_err.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-           -I/usr/kerberos/include \
-           -c grst_err.c \
-           -o grst_err_globus.o
-
-grst_x509_globus.o: grst_x509.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-            -I/usr/kerberos/include -c grst_x509.c \
-            -o grst_x509_globus.o
-
-grst_gacl_globus.o: grst_gacl.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-            -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_gacl.c \
-            -o grst_gacl_globus.o
-
-grst_xacml_globus.o: grst_xacml.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-            -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_xacml.c \
-            -o grst_xacml_globus.o
-
-grst_http_globus.o: grst_http.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-            -I/usr/kerberos/include -c grst_http.c \
-            -o grst_http_globus.o
-
-grst_asn1_globus.o: grst_asn1.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-            -I/usr/kerberos/include -c grst_asn1.c \
-            -o grst_asn1_globus.o
-
-grst_htcp_globus.o: grst_htcp.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
-            -I/usr/kerberos/include -c grst_htcp.c \
-            -o grst_htcp_globus.o
+libgridsite_globus.a: libgridsite_globus.la
+
+libgridsite_globus.la: $(GRIDSITE_GLOBUS_OBJS)
+       $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \
+         -o $@ $+  $(XML2_LIBS) $(OPENSSL_GLOBUS_LIBS)
+
+grst_err_globus.lo: grst_err.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+           -I/usr/kerberos/include -c $< -o $@
+
+grst_x509_globus.lo: grst_x509.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
+
+grst_gacl_globus.lo: grst_gacl.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+            -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@
+
+grst_xacml_globus.lo: grst_xacml.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+            -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@
+
+grst_http_globus.lo: grst_http.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
+
+grst_asn1_globus.lo: grst_asn1.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
+
+grst_htcp_globus.lo: grst_htcp.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \
+            -I/usr/kerberos/include -c $< -o $@
 
 else
 
-libgridsite_globus.so.$(VERSION): libgridsite.so.$(VERSION)
-       cp -f libgridsite.so.$(VERSION) libgridsite_globus.so.$(VERSION)
+libgridsite_globus.a: libgridsite_globus.la
 
-libgridsite_globus.a: libgridsite.a
-       cp -f libgridsite.a libgridsite_globus.a
+libgridsite_globus.la: $(GRIDSITE_OBJS)
+       $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \
+         -o $@ $+ -lcrypto ${MYCANLLDFLAGS} $(XML2_LIBS)
 
 endif
 
 # then build versions without OpenSSL
 
-libgridsite_nossl.so.$(VERSION): grst_err_nossl.o \
-               grst_gacl_nossl.o grst_http_nossl.o \
-               grst_xacml_nossl.o grst_htcp_nossl.o 
-       gcc -shared -Wl,-soname,libgridsite_nossl.so.$(MINOR_VERSION) \
-         -o libgridsite_nossl.so.$(PATCH_VERSION) \
-         grst_err_nossl.o grst_gacl_nossl.o grst_xacml_nossl.o \
-         grst_http_nossl.o grst_htcp_nossl.o $(XML2_LIBS)
-       ln -sf libgridsite_nossl.so.$(VERSION) libgridsite_nossl.so
-
-libgridsite_nossl.a: grst_err_nossl.o grst_gacl_nossl.o grst_http_nossl.o grst_htcp_nossl.o
-       ar src libgridsite_nossl.a \
-               grst_err_nossl.o grst_gacl_nossl.o grst_http_nossl.o grst_htcp_nossl.o
-
-grst_err_nossl.o: grst_err.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \
-           -I/usr/kerberos/include \
-           -c grst_err.c \
-           -o grst_err_nossl.o
+libgridsite_nossl.a: libgridsite_nossl.la
+
+libgridsite_nossl.la: $(GRIDSITE_NOSSL_OBJS)
+       $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \
+         -o $@ $+ $(XML2_LIBS)
+
+grst_err_nossl.lo: grst_err.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) -DGRST_NO_OPENSSL \
+           -I/usr/kerberos/include -c $< -o $@
 
-grst_gacl_nossl.o: grst_gacl.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \
-            -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_gacl.c \
-            -o grst_gacl_nossl.o
+grst_gacl_nossl.lo: grst_gacl.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) -DGRST_NO_OPENSSL \
+            -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@
 
-grst_xacml_nossl.o: grst_xacml.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \
-            -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_xacml.c \
-            -o grst_xacml_nossl.o
+grst_xacml_nossl.lo: grst_xacml.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) -DGRST_NO_OPENSSL \
+            -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@
 
-grst_http_nossl.o: grst_http.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \
-            -I/usr/kerberos/include -c grst_http.c \
-            -o grst_http_nossl.o
+grst_http_nossl.lo: grst_http.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) -DGRST_NO_OPENSSL \
+            -I/usr/kerberos/include -c $< -o $@
 
-grst_htcp_nossl.o: grst_htcp.c ../interface/gridsite.h
-       gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \
-            -I/usr/kerberos/include -c grst_htcp.c \
-            -o grst_htcp_nossl.o
+grst_htcp_nossl.lo: grst_htcp.c ../interface/gridsite.h
+       $(COMPILE)  $(MYCFLAGS) -DGRST_NO_OPENSSL \
+            -I/usr/kerberos/include -c $< -o $@
 
 # now the binary exectuables
 
-gsexec:        gsexec.c gsexec.h
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
-           -o gsexec gsexec.c
+gsexec.lo urlencode.lo gridsite-copy.lo findproxyfile.lo showx509exts.lo:
+       $(COMPILE) -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
+           -o $@ -c $(subst .lo,.c,$@)
 
-urlencode: urlencode.c libgridsite.so.$(VERSION)
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
-            -o urlencode urlencode.c -L. \
-            -I/usr/kerberos/include \
-            -lgridsite
+htcp.lo:
+       $(COMPILE) -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
+           -o $@ -c $(subst .lo,.c,$@) $(CURL_CFLAGS)
 
-htcp: htcp.c libgridsite.so.$(VERSION)
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
-           -o htcp htcp.c -L. \
-           -I/usr/kerberos/include \
-            $(CURL_CFLAGS) $(CURL_LIBS) \
-            -lgridsite
+slashgrid.lo: slashgrid.c
+       $(COMPILE) -o $@ -c $< $(MYCFLAGS) $(XML2_CFLAGS) -D_FILE_OFFSET_BITS=64 \
+          -D_REENTRANT -DFUSE_USE_VERSION=22 -I/usr/kerberos/include \
+          $(CURL_CFLAGS)
 
-htcp-static: htcp.c libgridsite.a
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
-           -o htcp-static htcp.c -L. \
-           -I/usr/kerberos/include \
-            $(CURL_CFLAGS) $(CURL_LIBS) \
-            -lgridsite -static
+gsexec:        gsexec.lo gsexec.h
+       $(LINK) $< -o $@
+
+urlencode: urlencode.lo libgridsite.la
+       $(LINK) -o $@ $< -L. -lgridsite
+
+htcp: htcp.lo libgridsite.la
+       $(LINK) -o $@ $+ $(CURL_LIBS)
 
-gridsite-copy.cgi: gridsite-copy.c libgridsite.so.$(VERSION)
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
-           -o gridsite-copy.cgi gridsite-copy.c -L. \
+htcp-static: htcp.lo libgridsite.a
+       $(LINK) -o $@ -L. $< \
            -I/usr/kerberos/include \
-            $(CURL_CFLAGS) $(CURL_LIBS) \
-            $(MYFCGILIBS) -lgridsite
+            $(CURL_LIBS) -static -lgridsite
 
-mod_gridsite.so: mod_gridsite.c mod_ssl-private.h libgridsite.so.$(VERSION)
+gridsite-copy.cgi: gridsite-copy.lo libgridsite.la
+       $(LINK) -o $@ $< -L. $(CURL_LIBS) $(MYFCGILIBS) -lgridsite
+
+mod_gridsite.so: ${MOD_GRIDSITE_FILE} ${MOD_SSLPRIVATE_HEADER} \
+                 libgridsite.la
        gcc -g $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \
            -I/usr/kerberos/include \
-           $(XML2_CFLAGS) -lcrypto -lssl \
+           $(XML2_CFLAGS) -lcrypto -lssl ${MYCANLLDFLAGS} \
            -DVERSION=\"$(VERSION)\" -o mod_gridsite.so \
-           mod_gridsite.c $(MYLDFLAGS) -lgridsite
+           ${MOD_GRIDSITE_FILE} -L./.libs -lgridsite
 
 mod_gridsite_example.so: mod_gridsite_example.c 
        gcc -g -shared -Wl,-soname=gridsite_example_module \
-          -I/usr/include/httpd -I/usr/include/apr-0 \
+          -fPIC $(HTTPD_FLAGS) \
            -DVERSION=\"$(VERSION)\" -o mod_gridsite_example.so \
            mod_gridsite_example.c 
 
-real-gridsite-admin.cgi: grst_admin_main.c grst_admin_gacl.c \
-                         grst_admin_file.c grst_admin.h
-       gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o real-gridsite-admin.cgi \
-            grst_admin_main.c \
-            grst_admin_gacl.c \
-            grst_admin_file.c \
-            -I/usr/kerberos/include \
-            -DVERSION=\"$(VERSION)\" -lgridsite
-
-findproxyfile: findproxyfile.c libgridsite.so.$(VERSION)
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \
-            -o findproxyfile findproxyfile.c -L. \
-            -I/usr/kerberos/include -lgridsite
-
-showx509exts: showx509exts.c libgridsite.so.$(VERSION)
-       gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \
-            -o showx509exts showx509exts.c -L. \
-            -I/usr/kerberos/include \
-            -lgridsite \
-            -lssl -lcrypto $(XML2_LIBS) -lz -lm
+grst_admin_main.lo grst_admin_gacl.lo grst_admin_file.lo:
+       $(COMPILE) $(MYCFLAGS) -I/usr/kerberos/include \
+          -DVERSION=\"$(VERSION)\" -o $@ -c $(subst .lo,.c,$@)
+
+real-gridsite-admin.cgi: grst_admin_main.lo grst_admin_gacl.lo \
+                         grst_admin_file.lo grst_admin.h
+       $(LINK) $(MYLDFLAGS) -o real-gridsite-admin.cgi \
+            grst_admin_main.lo \
+            grst_admin_gacl.lo \
+            grst_admin_file.lo \
+            -lgridsite
+
+findproxyfile: findproxyfile.lo libgridsite.la
+       $(LINK) $(MYLDFLAGS) \
+            -o $@ $< -L. -lgridsite
+
+showx509exts: showx509exts.lo libgridsite.la
+       $(LINK) $(MYLDFLAGS) \
+            -o $@ $< -L. -lgridsite -lssl -lcrypto $(XML2_LIBS) -lz -lm
  
-slashgrid: slashgrid.c libgridsite.so.$(VERSION)
-       gcc -g -o slashgrid -lfuse -lpthread slashgrid.c \
-          $(MYCFLAGS) $(MYLDFLAGS) $(XML2_CFLAGS) \
-          -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=22 \
-          -I/usr/kerberos/include $(CURL_CFLAGS) \
+slashgrid: slashgrid.lo libgridsite.la
+       $(LINK) $< -o $@ -lfuse $(MYLDFLAGS) \
           -L. $(CURL_LIBS) -lgridsite -lpthread
 
 # This target is used by make-gridsite-spec to test for FUSE include+libs
@@ -325,15 +335,15 @@ apidoc:
        cd ../doc ; for i in *.1 *.8 ; do ../src/roffit < $$i \
           > $$i.html ; done
 
-gaclexample: gaclexample.c libgridsite.a
-       gcc -g -o gaclexample gaclexample.c -I../interface -L. \
-            -I/usr/kerberos/include -lgridsite \
-            -lssl -lcrypto $(XML2_LIBS) -lz -lm
+gaclexample.lo xacmlexample.lo: gaclexample.c xacmlexample.c
+       $(COMPILE) -c $(subst .lo,.c,$@) -o $@ -I../interface \
+          -I/usr/kerberos/include
+
+gaclexample: gaclexample.lo libgridsite.la
+       $(LINK) -o $@ $< -L. -lgridsite -lssl -lcrypto $(XML2_LIBS) -lz -lm
            
-xacmlexample: xacmlexample.c libgridsite.a
-       gcc -g -o xacmlexample xacmlexample.c -I../interface -L. \
-            -I/usr/kerberos/include -lgridsite \
-            -lssl -lcrypto $(XML2_LIBS) -lz -lm
+xacmlexample: xacmlexample.lo libgridsite.a
+       $(LINK) -o $@ $< -L. -lgridsite -lssl -lcrypto $(XML2_LIBS) -lz -lm
 #
 # Delegation machinery, including SOAP delegation portType. To build this
 # you either need to use the gLite build environment and set REPOSITORY
@@ -354,28 +364,29 @@ DelegationService.wsdl: delegation.h
        $(GSOAPDIR)/bin/soapcpp2 -c delegation.h
 
 gridsite-delegation.cgi: grst-delegation.c delegation.h \
-                         DelegationService.wsdl libgridsite.so.$(VERSION)
+                         DelegationService.wsdl libgridsite.la
        gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-delegation.cgi \
             grst-delegation.c \
             -I/usr/kerberos/include -I. $(GSOAP_CFLAGS) \
             -I$(GRIDSITEDIR)/include \
-            -DVERSION=\"$(VERSION)\" -L. \
+            -DVERSION=\"$(VERSION)\" -L./.libs \
             soapC.c soapServer.c \
-            -L$(GRIDSITEDIR)/$(libdir)  $(GSOAP_LIBS) \
-            -lgridsite
+            $(GSOAP_LIBS) -lgridsite
+#            -L$(GRIDSITEDIR)/$(libdir)  $(GSOAP_LIBS) \
+ #           -lgridsite
 
-htproxyput: htproxyput.c delegation.h DelegationService.wsdl libgridsite.so.$(VERSION)
+htproxyput: htproxyput.c delegation.h DelegationService.wsdl libgridsite.la
        gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o htproxyput \
             htproxyput.c \
             -I/usr/kerberos/include -I. \
             -g -DVERSION=\"$(VERSION)\" \
             $(GSOAPSSL_CFLAGS) \
             -I$(GRIDSITEDIR)/include \
-            -L. \
+            -L./.libs \
             $(STDSOAP2) \
             soapC.c soapClient.c $(GSOAPSSL_LIBS) \
             -lgridsite -lssl -lcrypto
-        
+
 # This target is used by make-gridsite-spec to test for gSOAP include+libs
 gsoap-test: gsoap-test.c 
        gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gsoap-test \
@@ -386,146 +397,130 @@ gsoap-test: gsoap-test.c
             -I$(GRIDSITEDIR)/include \
             $(STDSOAP2) -L$(GRIDSITEDIR)/$(libdir) \
             $(GSOAPSSL_LIBS) -lz -lssl -lcrypto $(XML2_LIBS) -lm
-        
-gridsite-storage.cgi: gridsite-storage.c libgridsite.so.$(VERSION)
-       gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-storage.cgi \
-            gridsite-storage.c \
+
+gridsite-storage.lo: gridsite-storage.c
+       $(COMPILE) -o $@ -c $<  $(MYCFLAGS) \
             -I/usr/kerberos/include -I.\
             -I$(GRIDSITEDIR)/include \
             -DVERSION=\"$(VERSION)\" -L. \
+            $(CURL_CFLAGS)
+
+gridsite-storage.cgi: gridsite-storage.lo libgridsite.la
+       $(LINK) $(MYLDFLAGS) -o $@ $< \
             -L$(GRIDSITEDIR)/$(libdir) \
-            -lgridsite $(CURL_CFLAGS) $(CURL_LIBS)
+            -lgridsite $(CURL_LIBS)
 
 %.pc: %.pc.in
        sed -e "s/@version@/$(VERSION)/" -e "s,@prefix@,$(prefix)," -e "s/@libdir@/$(libdir)/" $< > $@
 
 clean:
        rm -rvf doxygen
+       rm -rvf .libs
        rm -vf DelegationSoapBinding.* soapC*.c soapH*.h soapS*.c soapStub.h ns.xsd
        rm -vf fuse-test.c gsoap-test.c gridsite.spec
-       rm -vf libgridsite*.so* *.cgi mod_gridsite*.so *.a *.o
+       rm -vf libgridsite*.so* *.cgi mod_gridsite*.so *.a *.o *.la *.lo
        rm -vf gsexec urlencode htcp htcp-static findproxyfile showx509exts slashgrid fuse-test gaclexample xacmlexample htproxyput gsoap-test
        rm -vf gridsite-openssl.pc gridsite-globus.pc
 
+distclean:
+       rm -rfv ../dist ../gridsite-*.tar.gz $(MYRPMDIR)
+
 #
 # Install
 #
 
 install: apidoc install-lib
-       mkdir -p $(prefix)/include \
-                 $(prefix)/$(libdir)/$(httpd_name)/modules \
-                 $(prefix)/bin \
-                 $(prefix)/sbin \
-                 $(prefix)/share/man/man1 \
-                 $(prefix)/share/man/man8 \
-                 $(prefix)/share/doc/gridsite-$(MINOR_VERSION)
+       mkdir -p $(DESTDIR)$(prefix)/include \
+                 $(DESTDIR)$(prefix)/$(libdir)/$(httpd_name)/modules \
+                 $(DESTDIR)$(prefix)/bin \
+                 $(DESTDIR)$(prefix)/sbin \
+                 $(DESTDIR)$(prefix)/share/man/man1 \
+                 $(DESTDIR)$(prefix)/share/man/man8 \
+                 $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)
        sed 's/^\(#define GRST_VERSION\).*$$/\1 $(DEFVERSION)/' \
-             ../interface/gridsite.h > $(prefix)/include/gridsite.h
-       cp -f ../interface/gridsite-gacl.h $(prefix)/include
-       cp -f urlencode $(prefix)/bin
-       cp -f findproxyfile $(prefix)/bin
-       cp -f real-gridsite-admin.cgi $(prefix)/sbin
-       cp -f gridsite-copy.cgi $(prefix)/sbin
-       cp -f gridsite-storage.cgi $(prefix)/sbin
+             ../interface/gridsite.h > $(DESTDIR)$(prefix)/include/gridsite.h
+       cp -f ../interface/gridsite-gacl.h $(DESTDIR)$(prefix)/include
+       $(INSTALL) urlencode $(DESTDIR)$(prefix)/bin
+       $(INSTALL) findproxyfile $(DESTDIR)$(prefix)/bin
+       $(INSTALL) real-gridsite-admin.cgi $(DESTDIR)$(prefix)/sbin
        cp -f ../CHANGES ../README ../INSTALL ../LICENSE ../VERSION \
-               $(prefix)/share/doc/gridsite-$(MINOR_VERSION)
+               $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)
        cp -f ../doc/index.html ../doc/*.conf ../doc/*.sh ../doc/*.spec \
               mod_gridsite_example.c \
-               $(prefix)/share/doc/gridsite-$(MINOR_VERSION)
+               $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)
        for i in htcp.1 htfind.1 htll.1 htls.1 htmkdir.1 htmv.1 htping.1 \
         htrm.1 urlencode.1 findproxyfile.1 ; do \
-        cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/man/man1 ; \
-         gzip -f $(prefix)/share/man/man1/$$i ; done
-       for i in mod_gridsite.8 gsexec.8 ; do \
-        cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/man/man8 ; \
-         gzip -f $(prefix)/share/man/man8/$$i ; done
-       cp -f htcp $(prefix)/bin
-       ln -sf htcp $(prefix)/bin/htls
-       ln -sf htcp $(prefix)/bin/htll
-       ln -sf htcp $(prefix)/bin/htrm
-       ln -sf htcp $(prefix)/bin/htmkdir
-       ln -sf htcp $(prefix)/bin/htmv
-       ln -sf htcp $(prefix)/bin/htping
-       ln -sf htcp $(prefix)/bin/htfind
-       cp -f gsexec $(prefix)/sbin
-       cp -f mod_gridsite.so $(prefix)/$(libdir)/$(httpd_name)/modules
+        cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man1 ; \
+         gzip -f $(DESTDIR)$(prefix)/share/man/man1/$$i ; done
+       for i in mod_gridsite.8 ; do \
+        cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man8 ; \
+         gzip -f $(DESTDIR)$(prefix)/share/man/man8/$$i ; done
+       $(INSTALL) htcp $(DESTDIR)$(prefix)/bin
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htls
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htll
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htrm
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htmkdir
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htmv
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htping
+       ln -sf htcp $(DESTDIR)$(prefix)/bin/htfind
+       cp -f mod_gridsite.so $(DESTDIR)$(prefix)/$(libdir)/$(httpd_name)/modules
 
 install-lib:
-       mkdir -p $(prefix)/$(libdir)/pkgconfig
-       cp -f  libgridsite.a $(prefix)/$(libdir)
-       cp -f  libgridsite.so.$(PATCH_VERSION) $(prefix)/$(libdir)
-       ln -sf libgridsite.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite.so
-       ln -sf libgridsite.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite.so.$(MAJOR_VERSION)
-       ln -sf libgridsite.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite.so.$(MINOR_VERSION)
-       cp -f  libgridsite_globus.a $(prefix)/$(libdir)
-       cp -f  libgridsite_globus.so.$(PATCH_VERSION) $(prefix)/$(libdir)
-       ln -sf libgridsite_globus.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite_globus.so
-       ln -sf libgridsite_globus.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite_globus.so.$(MAJOR_VERSION)
-       ln -sf libgridsite_globus.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite_globus.so.$(MINOR_VERSION)
-       cp -f  libgridsite_nossl.a $(prefix)/$(libdir)
-       cp -f  libgridsite_nossl.so.$(PATCH_VERSION) $(prefix)/$(libdir)
-       ln -sf libgridsite_nossl.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite_nossl.so
-       ln -sf libgridsite_nossl.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite_nossl.so.$(MAJOR_VERSION)
-       ln -sf libgridsite_nossl.so.$(PATCH_VERSION) \
-                                 $(prefix)/$(libdir)/libgridsite_nossl.so.$(MINOR_VERSION)
-       cp -f $(PC_FILES) $(prefix)/$(libdir)/pkgconfig
+       mkdir -p $(DESTDIR)$(prefix)/$(libdir)/pkgconfig
+       $(INSTALL) libgridsite.la $(DESTDIR)$(prefix)/$(libdir)
+       $(INSTALL) libgridsite_globus.la $(DESTDIR)$(prefix)/$(libdir)
+       $(INSTALL) libgridsite_nossl.la $(DESTDIR)$(prefix)/$(libdir)
+       rm -f $(DESTDIR)$(prefix)/$(libdir)/*.la
+       cp -f $(PC_FILES) $(DESTDIR)$(prefix)/$(libdir)/pkgconfig
 
 install-slashgrid: slashgrid
-       -mkdir -p $(RPM_BUILD_ROOT)/etc/rc.d/init.d
-       -mkdir -p $(prefix)/share/doc/gridsite-$(MINOR_VERSION)
-       -mkdir -p $(prefix)/share/man/man8
-       cp -f slashgrid $(prefix)/sbin
-       cp -f slashgrid.init $(RPM_BUILD_ROOT)/etc/rc.d/init.d/slashgrid
-       cp -f ../doc/slashgrid.8.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-       cp -f ../doc/slashgrid.8 $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-       cp -f ../doc/slashgrid.8 $(prefix)/share/man/man8
-       gzip -f $(prefix)/share/man/man8/slashgrid.8
-       mkdir -p $(RPM_BUILD_ROOT)/var/spool/slashgrid
+       -mkdir -p $(DESTDIR)/etc/rc.d/init.d
+       -mkdir -p $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)
+       -mkdir -p $(DESTDIR)$(prefix)/share/man/man8
+       $(INSTALL) slashgrid $(DESTDIR)$(prefix)/sbin
+       cp -f slashgrid.init $(DESTDIR)/etc/rc.d/init.d/slashgrid
+       cp -f ../doc/slashgrid.8.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+       cp -f ../doc/slashgrid.8 $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+       cp -f ../doc/slashgrid.8 $(DESTDIR)$(prefix)/share/man/man8
+       gzip -f $(DESTDIR)$(prefix)/share/man/man8/slashgrid.8
+       mkdir -p $(DESTDIR)/var/spool/slashgrid
 
 install-ws: gridsite-delegation.cgi htproxyput
-       mkdir -p $(prefix)/include \
-                 $(prefix)/bin \
-                 $(prefix)/sbin \
-                 $(prefix)/share/man/man1 \
-                 $(prefix)/share/man/man8 \
-                 $(prefix)/share/doc/gridsite-$(MINOR_VERSION)
-       cp -f ../doc/*.wsdl $(prefix)/share/doc/gridsite-$(MINOR_VERSION)
+       mkdir -p $(DESTDIR)$(prefix)/include \
+                 $(DESTDIR)$(prefix)/bin \
+                 $(DESTDIR)$(prefix)/sbin \
+                 $(DESTDIR)$(prefix)/share/man/man1 \
+                 $(DESTDIR)$(prefix)/share/man/man8 \
+                 $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)
+       cp -f ../doc/*.wsdl $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)
        for i in htproxyput.1 htproxytime.1 htproxyrenew.1 htproxydestroy.1 \
         htproxyunixtime.1 htproxyinfo.1 ; do \
-        cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/man/man1 ; \
-         gzip -f $(prefix)/share/man/man1/$$i ; done
+        cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man1 ; \
+         gzip -f $(DESTDIR)$(prefix)/share/man/man1/$$i ; done
        for i in gridsite-delegation.8 ; do \
-        cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
-         cp -f ../doc/$$i $(prefix)/share/man/man8 ; \
-         gzip -f $(prefix)/share/man/man8/$$i ; done
-       cp -f htproxyput $(prefix)/bin
-       ln -sf htproxyput $(prefix)/bin/htproxydestroy
-       ln -sf htproxyput $(prefix)/bin/htproxytime
-       ln -sf htproxyput $(prefix)/bin/htproxyunixtime
-       ln -sf htproxyput $(prefix)/bin/htproxyrenew
-       ln -sf htproxyput $(prefix)/bin/htproxyinfo
-       cp -f gridsite-delegation.cgi $(prefix)/sbin
+        cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \
+         cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man8 ; \
+         gzip -f $(DESTDIR)$(prefix)/share/man/man8/$$i ; done
+       cp -f htproxyput $(DESTDIR)$(prefix)/bin
+       ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxydestroy
+       ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxytime
+       ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxyunixtime
+       ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxyrenew
+       ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxyinfo
+       cp -f gridsite-delegation.cgi $(DESTDIR)$(prefix)/sbin
 
 post-install-debian:
        sed -i \
                -e 's,^\(ServerRoot\) .*,\1 "/etc/$(httpd_name)",' \
                -e 's,/usr/lib/httpd/modules,/usr/$(libdir)/$(httpd_name)/modules,' \
-               $(prefix)/share/doc/gridsite-$(MINOR_VERSION)/*.conf
+               $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)/*.conf
 
 #
 # Distributions
@@ -546,6 +541,7 @@ dist:
        cp -f Makefile grst*.c htcp.c slashgrid.c slashgrid.init \
                  urlencode.c findproxyfile.c gaclexample.c mod_gridsite*.c \
                  htproxyput.c grst_admin.h mod_ssl-private.h \
+                 canl_mod_gridsite.c canl_mod_ssl-private.h \
                  gsexec.c gsexec.h gridsite-copy.c gridsite-storage.c \
                  delegation.h \
                  roffit make-gridsite-spec make-debian-files \
@@ -568,7 +564,7 @@ htcp-bin: htcp
        mkdir -p ../htcp-bin-$(PATCH_VERSION)/bin \
                  ../htcp-bin-$(PATCH_VERSION)/man/man1
        cp -f ../doc/README.htcp-bin ../htcp-bin-$(PATCH_VERSION)
-       cp -f htcp ../htcp-bin-$(PATCH_VERSION)/bin
+       $(INSTALL) htcp ../htcp-bin-$(PATCH_VERSION)/bin
        cp -f ../doc/htcp.1 ../doc/htrm.1 ../doc/htls.1 ../doc/htmkdir.1 \
               ../doc/htll.1 ../doc/htmv.1 ../doc/htping.1 ../doc/htfind.1 \
               ../htcp-bin-$(PATCH_VERSION)/man/man1
@@ -590,8 +586,7 @@ rpm-prepare: dist
         ./make-gridsite-spec
 
 rpm: rpm-prepare
-       rm -Rf $(MYRPMDIR)
-       mkdir -p $(MYRPMDIR)/SOURCES $(MYRPMDIR)/SPECS $(MYRPMDIR)/BUILD \
+       -mkdir -p $(MYRPMDIR)/SOURCES $(MYRPMDIR)/SPECS $(MYRPMDIR)/BUILD \
              $(MYRPMDIR)/SRPMS $(MYRPMDIR)/RPMS/i386 $(MYRPMDIR)/BUILDROOT
        cp -f ../gridsite-$(PATCH_VERSION).src.tar.gz $(MYRPMDIR)/SOURCES
        cp -f gridsite.spec $(MYRPMDIR)/SPECS
@@ -607,14 +602,13 @@ deb-prepare: dist
         ./make-debian-files
 
 deb: deb-prepare
-       rm -Rf $(MYRPMDIR)
-       mkdir -p $(MYRPMDIR)
+       -mkdir -p $(MYRPMDIR)
        tar -xC $(MYRPMDIR) -f ../gridsite-$(PATCH_VERSION).src.tar.gz
        cp ../gridsite-$(PATCH_VERSION).src.tar.gz $(MYRPMDIR)/gridsite_$(PATCH_VERSION).orig.tar.gz
        cp -rf ../debian/ $(MYRPMDIR)/gridsite-$(PATCH_VERSION)/
        (cd $(MYRPMDIR)/gridsite-$(PATCH_VERSION) && \
        pwd && \
-       dpkg-buildpackage -S -d -uc -us)
+       dpkg-buildpackage -S -d -nc -uc -us)
 
 wtf: 
        pwd
@@ -624,4 +618,4 @@ wtf:
 #      ls -lR /usr/local/
 #      ls -lR $(GSOAPDIR)
 
-.PHONY: build build-lib apidoc clean install install-lib install-slashgrid install-ws dist htcp-bin rpm deb wtf post-install-debian
+.PHONY: build build-lib apidoc clean distclean install install-lib install-slashgrid install-ws dist htcp-bin rpm deb wtf post-install-debian
diff --git a/org.gridsite.core/src/canl_mod_gridsite.c b/org.gridsite.core/src/canl_mod_gridsite.c
new file mode 100644 (file)
index 0000000..8b8de55
--- /dev/null
@@ -0,0 +1,4101 @@
+/*
+   Copyright (c) 2003-10, Andrew McNab, Shiv Kaushal, Joseph Dada,
+   and Yibiao Li, University of Manchester. All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+     o Redistributions of source code must retain the above
+       copyright notice, this list of conditions and the following
+       disclaimer. 
+     o Redistributions in binary form must reproduce the above
+       copyright notice, this list of conditions and the following
+       disclaimer in the documentation and/or other materials
+       provided with the distribution. 
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+
+
+   This program includes code from dav_parse_range() from Apache mod_dav.c,
+   and associated code contributed by  David O Callaghan
+   
+   Copyright 2000-2005 The Apache Software Foundation or its licensors, as
+   applicable.
+   
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+   
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+   This work has been partially funded by the EU Commission (contract 
+   INFSO-RI-222667) under the EGEE-III collaboration.
+*/
+
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
+
+#ifndef VERSION
+#define VERSION "x.x.x"
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <apr_strings.h>
+#include <apr_tables.h>
+#include <apr_network_io.h>
+
+#include <ap_config.h>
+#include <httpd.h>
+#include <http_config.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_protocol.h>
+#include <http_request.h>
+
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#endif
+
+#include <unixd.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>              
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/select.h> 
+#include <sys/socket.h> 
+#include <netinet/in.h> 
+#include <arpa/inet.h> 
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <openssl/x509v3.h>
+
+#include "canl_mod_ssl-private.h"
+
+#include "gridsite.h"
+
+#include <canl.h>
+#include <canl_ssl.h>
+
+#ifndef IPV6_ADD_MEMBERSHIP
+#ifdef  IPV6_JOIN_GROUP
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+#endif
+
+#ifndef UNSET
+#define UNSET -1
+#endif
+
+#define GRST_SESSIONS_DIR "/var/www/sessions"
+
+module AP_MODULE_DECLARE_DATA gridsite_module;
+
+#define GRST_SITECAST_GROUPS 32
+
+struct sitecast_group
+   { char *address; int port; };
+
+#define GRST_SITECAST_ALIASES 32
+   
+struct sitecast_alias
+   { const char *sitecast_url; const char *scheme; int port; 
+     const char *local_path; const char *local_hostname; };
+
+/* Globals, defined by main server directives in httpd.conf  
+   These are assigned default values in create_gridsite_srv_config() */
+
+int gridhttpport = 0; /* set by create_gridsite_srv_config, used as flag */
+char                    *sessionsdir = NULL;
+char                   *sitecastdnlists = NULL;
+char                   *ocspmodes = NULL;
+struct sitecast_group  sitecastgroups[GRST_SITECAST_GROUPS+1];
+struct sitecast_alias  sitecastaliases[GRST_SITECAST_ALIASES];
+
+ /* This global records whether the SSLSrvConfigRec struct will have 
+    the extra  BOOL insecure_reneg  member */
+int                     mod_ssl_with_insecure_reneg = 0;
+
+struct sitecast_sockets {
+    fd_set fds;
+    int max_fd;
+} sitecast_sockets;
+
+typedef struct
+{
+   int                 auth;
+   int                  autopasscode;
+   int                 requirepasscode;
+   int                 zoneslashes;
+   int                 envs;
+   int                 format;
+   int                 indexes;
+   char                        *indexheader;
+   int                 gridsitelink;
+   char                        *adminfile;
+   char                        *adminuri;
+   char                        *helpuri;
+   char                        *loginuri;
+   char                        *dnlists;
+   char                        *dnlistsuri;
+   char                        *adminlist;
+   int                 gsiproxylimit;
+   char                        *unzip;
+   char                        *methods;
+   char                        *editable;
+   char                        *headfile;
+   char                        *footfile;
+   int                 gridhttp;
+   char                        *aclformat;
+   char                        *aclpath;
+   char                        *execmethod;
+   char                        *delegationuri;
+   ap_unix_identity_t  execugid;
+   apr_fileperms_t     diskmode;
+}  mod_gridsite_dir_cfg; /* per-directory config choices */
+
+
+/*
+ *   parse_content_range() is loosely 
+ *   based on modules/dav/main/mod_dav.c from Apache
+ */
+
+int parse_content_range(request_rec *r, apr_off_t *range_start, 
+                        apr_off_t *range_end, apr_off_t *range_length)
+{
+// this all needs verifying to be ok for large (>2GB, >4GB) files 
+
+    const char *range_c;
+    char *range;
+    char *dash;
+    char *slash;
+
+    range_c = apr_table_get(r->headers_in, "content-range");
+    if (range_c == NULL) return 0;
+    
+    range = apr_pstrdup(r->pool, range_c);
+
+    if ((strncasecmp(range, "bytes ", 6) != 0) ||
+        ((dash = ap_strchr(range, '-')) == NULL) ||
+        ((slash = ap_strchr(range, '/')) == NULL)) 
+      {        
+        return 0; /* malformed header. ignore it (per S14.16 of RFC2616) */
+      }
+
+    *dash = *slash = '\0';
+    
+    // Check for GridSite-specific Content-Range: bytes *-*/LENGTH form
+    
+    if ((range[6] == '*') && (dash[1] == '*'))
+      {
+        if (slash[1] == '*') return 0; /* invalid truncation length */
+        
+        *range_length = apr_atoi64(&slash[1]);
+        *range_start  = 0;
+        *range_end    = 0;
+        
+        return 1; /* a valid (truncation) length */
+      }          
+    
+    *range_length = 0;
+    *range_start  = apr_atoi64(&range[6]);
+    *range_end    = apr_atoi64(&dash[1]);
+
+    if ((*range_end < *range_start) || 
+        ((slash[1] != '*') && (apr_atoi64(&slash[1]) <= *range_end)))
+            return 0; /* ignore invalid ranges */
+
+    /* we now have a valid range */
+    return 1;
+}
+
+char *html_escape(apr_pool_t *pool, char *s)
+{
+    int    htmlspecials, i;
+    char  *escaped, *p;
+
+    for (htmlspecials=0,p=s; *p != '\0'; ++p) 
+      if ((*p == '<') || (*p == '>') || (*p == '&') || (*p == '"')) 
+          ++htmlspecials;
+
+    escaped = apr_palloc(pool, strlen(s) + htmlspecials * 6 + 1);
+        
+    for (i=0,p=s; *p != '\0'; ++p)
+       {
+             if      (*p == '<') 
+                { 
+                  strcpy(&escaped[i], "&lt;");
+                  i += 4;
+                }
+            else if (*p == '>') 
+                {
+                  strcpy(&escaped[i], "&gt;");
+                  i += 4;
+                }
+            else if (*p == '&') 
+                {
+                  strcpy(&escaped[i], "&amp;");
+                  i += 5;
+                }
+            else if (*p == '"') 
+                {
+                  strcpy(&escaped[i], "&quot;");
+                  i += 6;
+                }
+            else 
+                {
+                  escaped[i] = *p;
+                  ++i;
+                }                  
+       }
+
+    escaped[i] = '\0';
+   
+    return escaped;
+}
+
+char *make_admin_footer(request_rec *r, mod_gridsite_dir_cfg *conf,
+                        int isdirectory)
+/*
+    make string holding last modified text and admin links
+*/
+{
+    char     *out, *https, *p, *dn = NULL, *file = NULL, *permstr = NULL, 
+             *temp, modified[99], *dir_uri, *grst_cred_auri_0 = NULL;
+    GRSTgaclPerm  perm = GRST_PERM_NONE;
+    struct tm mtime_tm;
+    time_t    mtime_time;
+
+    https = (char *) apr_table_get(r->subprocess_env, "HTTPS");
+
+    dir_uri = apr_pstrdup(r->pool, r->uri);
+    p = rindex(dir_uri, '/');
+
+    if (p == NULL) return "";
+    
+    file = apr_pstrdup(r->pool, &p[1]);
+    p[1] = '\0';
+    /* dir_uri always gets both a leading and a trailing slash */
+       
+    out = apr_pstrdup(r->pool, "<p>\n");
+
+    if (!isdirectory)
+      {
+        mtime_time = apr_time_sec(r->finfo.mtime);
+
+        localtime_r(&mtime_time, &mtime_tm);
+        strftime(modified, sizeof(modified), 
+                 "%a&nbsp;%e&nbsp;%B&nbsp;%Y", &mtime_tm);    
+        temp = apr_psprintf(r->pool,"<hr><small>Last modified %s\n", modified);
+        out = apr_pstrcat(r->pool, out, temp, NULL);
+
+        if ((conf->adminuri != NULL) &&
+            (conf->adminuri[0] != '\0') &&
+            (conf->adminfile != NULL) &&
+            (conf->adminfile[0] != '\0') &&
+            (strncmp(file, GRST_HIST_PREFIX, sizeof(GRST_HIST_PREFIX)-1) != 0))
+          {
+            temp = apr_psprintf(r->pool, 
+                            ". <a href=\"%s?cmd=history&amp;file=%s\">"
+                            "View&nbsp;page&nbsp;history</a>\n",
+                            conf->adminfile, file);
+            out = apr_pstrcat(r->pool, out, temp, NULL);
+          }
+          
+        out = apr_pstrcat(r->pool, out, "</small>", NULL);
+      }
+
+    out = apr_pstrcat(r->pool, out, "<hr><small>", NULL);
+
+    if (r->connection->notes != NULL)
+      {
+        grst_cred_auri_0 = (char *) 
+                  apr_table_get(r->notes, "GRST_CRED_AURI_0");
+      }                       
+
+    if ((grst_cred_auri_0 != NULL) && 
+        (strncmp(grst_cred_auri_0, "dn:", 3) == 0))
+      {
+         dn = GRSThttpUrlDecode(&grst_cred_auri_0[3]);
+         if (dn[0] == '\0') 
+           {
+             free(dn);
+             dn = NULL;
+           }         
+      }
+  
+    if (dn != NULL) 
+      {
+        temp = apr_psprintf(r->pool, 
+                            "You are %s<br>\n", html_escape(r->pool,dn));
+        out = apr_pstrcat(r->pool, out, temp, NULL);
+               
+        if (r->notes != NULL)
+                permstr = (char *) apr_table_get(r->notes, "GRST_PERM");
+
+        if ((permstr != NULL) &&
+            (conf->adminuri != NULL) &&
+            (conf->adminuri[0] != '\0') &&
+            (conf->adminfile != NULL) &&
+            (conf->adminfile[0] != '\0'))
+          {
+            sscanf(permstr, "%d", &perm);
+            
+            if (!isdirectory &&
+                GRSTgaclPermHasWrite(perm) &&
+                (strncmp(file, GRST_HIST_PREFIX,
+                         sizeof(GRST_HIST_PREFIX) - 1) != 0))
+              {
+                temp = apr_psprintf(r->pool, 
+                     "<a href=\"%s?cmd=edit&amp;file=%s\">"
+                     "Edit&nbsp;page</a> .\n", conf->adminfile, file);
+                out = apr_pstrcat(r->pool, out, temp, NULL);
+              }
+                 
+            if (GRSTgaclPermHasList(perm) || GRSTgaclPermHasWrite(perm))
+              {
+                temp = apr_psprintf(r->pool, 
+                 "<a href=\"%s%s?cmd=managedir\">Manage&nbsp;directory</a> .\n",
+                 dir_uri, conf->adminfile);
+
+                out = apr_pstrcat(r->pool, out, temp, NULL);
+              }                 
+          }
+          
+        free(dn);
+      }
+    
+    if ((https != NULL) && (strcasecmp(https, "on") == 0))
+         temp = apr_psprintf(r->pool,
+                   "<a href=\"http://%s%s\">Switch&nbsp;to&nbsp;HTTP</a> \n", 
+                   r->server->server_hostname, r->unparsed_uri);
+    else temp = apr_psprintf(r->pool,
+                   "<a href=\"https://%s%s\">Switch&nbsp;to&nbsp;HTTPS</a> \n",
+                   r->server->server_hostname, r->unparsed_uri);
+    
+    out = apr_pstrcat(r->pool, out, temp, NULL);
+
+    if ((conf->loginuri != NULL) && (conf->loginuri[0] != '\0'))
+      {
+        temp = apr_psprintf(r->pool,
+                   ". <a href=\"%s%s\">Login/Logout</a>\n", 
+                   conf->loginuri, r->unparsed_uri);
+        out = apr_pstrcat(r->pool, out, temp, NULL);
+      }
+
+    if ((conf->helpuri != NULL) && (conf->helpuri[0] != '\0'))
+      {
+        temp = apr_psprintf(r->pool,
+                   ". <a href=\"%s\">Website&nbsp;Help</a>\n", conf->helpuri);
+        out = apr_pstrcat(r->pool, out, temp, NULL);
+      }
+
+    if ((!isdirectory) &&
+        (conf->adminuri != NULL) &&
+        (conf->adminuri[0] != '\0') &&
+        (conf->adminfile != NULL) &&
+        (conf->adminfile[0] != '\0'))
+      {
+        temp = apr_psprintf(r->pool, ". <a href=\"%s?cmd=print&amp;file=%s\">"
+               "Print&nbsp;View</a>\n", conf->adminfile, file);
+        out = apr_pstrcat(r->pool, out, temp, NULL);
+      }
+
+    if (conf->gridsitelink)
+      {
+        temp = apr_psprintf(r->pool,
+           ". Built with <a href=\"http://www.gridsite.org/\">"
+           "GridSite</a>&nbsp;%s\n", VERSION);
+        out = apr_pstrcat(r->pool, out, temp, NULL);
+      }
+
+    out = apr_pstrcat(r->pool, out, "\n</small>\n", NULL);
+
+    return out;
+}
+
+void delegation_header(request_rec *r, mod_gridsite_dir_cfg *conf)
+{
+  apr_table_add(r->headers_out,
+                apr_pstrdup(r->pool, "Proxy-Delegation-Service"),
+                apr_psprintf(r->pool,"https://%s%s", r->hostname, conf->delegationuri));
+  return;
+
+}
+
+int html_format(request_rec *r, mod_gridsite_dir_cfg *conf)
+/*
+    try to do GridSite formatting of .html files (NOT .shtml etc)
+*/
+{
+    int   fd;
+    char  *buf, *p, *file, *s, *head_formatted, *header_formatted,
+          *body_formatted, *admin_formatted, *footer_formatted;
+    size_t length;
+    struct stat statbuf;
+    apr_file_t *fp;
+
+    if (r->finfo.filetype == APR_NOFILE) return HTTP_NOT_FOUND;
+
+    if (apr_file_open(&fp, r->filename, APR_READ, 0, r->pool) != 0)
+                                     return HTTP_INTERNAL_SERVER_ERROR;
+
+
+    /* Put in Delegation service header if required */
+    if (conf->delegationuri) delegation_header(r, conf);
+
+    file = rindex(r->uri, '/');
+    if (file != NULL) ++file; /* file points to name without path */
+
+    buf = apr_palloc(r->pool, (size_t)(r->finfo.size + 1));
+    length = r->finfo.size;
+    apr_file_read(fp, buf, &length);
+    buf[r->finfo.size] = '\0';
+    apr_file_close(fp);
+
+    /* **** try to find a header file in this or parent directories **** */
+
+    fd = -1;
+
+    if (conf->headfile[0] == '/') /* try absolute */
+      {
+        fd = open(conf->headfile, O_RDONLY);
+      }
+    else /* try relative */
+      {
+        /* first make a buffer big enough to hold path names we want to try */
+        s = apr_palloc(r->pool, 
+                       strlen(r->filename) + strlen(conf->headfile) + 1);
+        strcpy(s, r->filename);
+
+        for (;;)
+           {
+             p = rindex(s, '/');
+             if (p == NULL) break; /* failed to find one */
+             p[1] = '\0';
+             strcat(p, conf->headfile);
+
+             fd = open(s, O_RDONLY);
+             if (fd != -1) break; /* found one */
+
+             *p = '\0';
+           }
+      }
+
+    if (fd == -1) /* not found, so set up not to output one */
+      {
+        head_formatted   = apr_pstrdup(r->pool, "");
+        header_formatted = apr_pstrdup(r->pool, "");
+        body_formatted   = buf;
+      }
+    else /* found a header file, so set up head and body to surround it */
+      {
+        fstat(fd, &statbuf);
+        header_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+        read(fd, header_formatted, statbuf.st_size);
+        header_formatted[statbuf.st_size] = '\0';
+        close(fd);
+
+        p = strstr(buf, "<body");
+        if (p == NULL) p = strstr(buf, "<BODY");
+        if (p == NULL) p = strstr(buf, "<Body");
+
+        if (p == NULL)
+          {
+            head_formatted = apr_pstrdup(r->pool, "");
+            body_formatted = buf;
+          }
+        else
+          {
+            *p = '\0';
+            head_formatted = buf;
+            ++p;
+
+            while ((*p != '>') && (*p != '\0')) ++p;
+
+            if (*p == '\0')
+              {
+                body_formatted = p;
+              }
+            else
+              {
+                *p = '\0';
+                ++p;
+                body_formatted = p;
+              }
+          }
+      }
+
+    /* **** remove closing </body> tag from body **** */
+
+    p = strstr(body_formatted, "</body");
+    if (p == NULL) p = strstr(body_formatted, "</BODY");
+    if (p == NULL) p = strstr(body_formatted, "</Body");
+
+    if (p != NULL) *p = '\0';
+
+    /* **** set up dynamic part of footer to go at end of body **** */
+
+    admin_formatted = make_admin_footer(r, conf, FALSE);
+
+    /* **** try to find a footer file in this or parent directories **** */
+
+    fd = -1;
+
+    if (conf->footfile[0] == '/') /* try absolute */
+      {
+        fd = open(conf->footfile, O_RDONLY);
+      }
+    else /* try relative */
+      {
+        /* first make a buffer big enough to hold path names we want to try */
+        s = apr_palloc(r->pool, 
+                       strlen(r->filename) + strlen(conf->footfile) + 1);
+        strcpy(s, r->filename);
+
+        for (;;)
+           {
+             p = rindex(s, '/');
+             if (p == NULL) break; /* failed to find one */
+
+             p[1] = '\0';
+             strcat(p, conf->footfile);
+
+             fd = open(s, O_RDONLY);
+             if (fd != -1) break; /* found one */
+
+             *p = '\0';
+           }
+       }
+
+    if (fd == -1) /* failed to find a footer, so set up empty default */
+      {
+        footer_formatted = apr_pstrdup(r->pool, "");
+      }
+    else /* found a footer, so set up to use it */
+      {
+        fstat(fd, &statbuf);
+        footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+        read(fd, footer_formatted, statbuf.st_size);
+        footer_formatted[statbuf.st_size] = '\0';
+        close(fd);
+      }
+
+    /* **** can now calculate the Content-Length and output headers **** */
+
+    length = strlen(head_formatted) + strlen(header_formatted) +
+             strlen(body_formatted) + strlen(admin_formatted)  +
+             strlen(footer_formatted);
+
+    ap_set_content_length(r, length);
+    ap_set_content_type(r, "text/html");
+
+    /* ** output the HTTP body (HTML Head+Body) ** */
+
+    ap_rputs(head_formatted,   r);
+    ap_rputs(header_formatted, r);
+    ap_rputs(body_formatted,   r);
+    ap_rputs(admin_formatted,  r);
+    ap_rputs(footer_formatted, r);
+
+    return OK;
+}
+
+int html_dir_list(request_rec *r, mod_gridsite_dir_cfg *conf)
+/* 
+    output HTML directory listing, with level of formatting controlled
+    by GridSiteHtmlFormat/conf->format
+*/
+{
+    int   fd, n, nn;
+    char  *p, *s, *head_formatted, *header_formatted,
+          *body_formatted, *admin_formatted, *footer_formatted, *temp,
+           modified[999], *d_namepath, *indexheaderpath, *indexheadertext,
+           *encoded, *escaped;
+    size_t length;
+    struct stat statbuf;
+    struct tm   mtime_tm;
+    struct dirent **namelist;
+    
+    if (r->finfo.filetype == APR_NOFILE) return HTTP_NOT_FOUND;
+
+    /* Put in Delegation service header if required */
+    if (conf->delegationuri) delegation_header(r, conf);
+
+    head_formatted = apr_psprintf(r->pool,
+      "<head><title>Directory listing %s</title></head>\n", r->uri);
+
+    if (conf->format)
+      {
+        /* **** try to find a header file in this or parent directories **** */
+
+        /* first make a buffer big enough to hold path names we want to try */
+        fd = -1;
+        s = apr_palloc(r->pool, 
+                       strlen(r->filename) + strlen(conf->headfile) + 1);
+        strcpy(s, r->filename);
+
+        for (;;)
+           {
+             p = rindex(s, '/');
+             if (p == NULL) break; /* failed to find one */
+             p[1] = '\0';
+             strcat(p, conf->headfile);
+    
+             fd = open(s, O_RDONLY);
+             if (fd != -1) break; /* found one */
+
+             *p = '\0';
+           }
+            
+        if (fd == -1) /* not found, so set up to output sensible default */
+          {
+            header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+          }
+        else /* found a header file, so set up head and body to surround it */
+          {
+            fstat(fd, &statbuf);
+            header_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+            read(fd, header_formatted, statbuf.st_size);
+            header_formatted[statbuf.st_size] = '\0';
+            close(fd);
+          }
+      }
+    else header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+            
+    body_formatted = apr_psprintf(r->pool, 
+      "<h1>Directory listing %s</h1>\n", r->uri);
+      
+    if (conf->indexheader != NULL)
+      {
+        indexheaderpath = apr_psprintf(r->pool, "%s/%s", r->filename, 
+                                                         conf->indexheader);
+        fd = open(indexheaderpath, O_RDONLY);
+        if (fd != -1)
+          {
+            fstat(fd, &statbuf);
+            indexheadertext = apr_palloc(r->pool, statbuf.st_size + 1);
+            read(fd, indexheadertext, statbuf.st_size);
+            indexheadertext[statbuf.st_size] = '\0';
+            close(fd);
+            
+            body_formatted = apr_pstrcat(r->pool, body_formatted,
+                                         indexheadertext, NULL);
+          }
+      }
+
+    body_formatted = apr_pstrcat(r->pool, body_formatted, "<p><table>\n", NULL);
+
+    if (r->unparsed_uri[1] != '\0')
+     body_formatted = apr_pstrcat(r->pool, body_formatted, 
+        "<tr><td colspan=3>[<a href=\"../\">Parent directory</a>]</td></tr>\n", 
+         NULL);
+      
+    nn = scandir(r->filename, &namelist, 0, versionsort);
+    for (n=0; n < nn; ++n)
+         {
+           if ((namelist[n]->d_name[0] != '.') && 
+               ((conf->indexheader == NULL) || 
+                (strcmp(conf->indexheader, namelist[n]->d_name) != 0)))
+             {
+               d_namepath = apr_psprintf(r->pool, "%s/%s", r->filename,
+                                                  namelist[n]->d_name);
+               stat(d_namepath, &statbuf);
+               
+               localtime_r(&(statbuf.st_mtime), &mtime_tm);
+               strftime(modified, sizeof(modified), 
+              "<td align=right>%R</td><td align=right>%e&nbsp;%b&nbsp;%y</td>",
+                        &mtime_tm);    
+
+               encoded = GRSThttpUrlEncode(namelist[n]->d_name);
+               escaped = html_escape(r->pool, namelist[n]->d_name);
+
+               if (S_ISDIR(statbuf.st_mode))
+                    temp = apr_psprintf(r->pool, 
+                      "<tr><td><a href=\"%s/\" content-length=\"%ld\" "
+                      "last-modified=\"%ld\">"
+                      "%s/</a></td>"
+                      "<td align=right>%ld</td>%s</tr>\n", 
+                      encoded, statbuf.st_size, statbuf.st_mtime,
+                      escaped, 
+                      statbuf.st_size, modified);
+               else temp = apr_psprintf(r->pool, 
+                      "<tr><td><a href=\"%s\" content-length=\"%ld\" "
+                      "last-modified=\"%ld\">"
+                      "%s</a></td>"
+                      "<td align=right>%ld</td>%s</tr>\n", 
+                      encoded, statbuf.st_size, statbuf.st_mtime,
+                      escaped, 
+                      statbuf.st_size, modified);
+                      
+               free(encoded);
+               /* escaped done with pool so no free() */
+
+               body_formatted = apr_pstrcat(r->pool,body_formatted,temp,NULL);
+             }
+
+           free(namelist[n]);
+         }
+                 
+    free(namelist);
+    
+    body_formatted = apr_pstrcat(r->pool, body_formatted, "</table>\n", NULL);
+
+    if (conf->format)
+      {
+        /* **** set up dynamic part of footer to go at end of body **** */
+
+        admin_formatted = make_admin_footer(r, conf, TRUE);
+    
+        /* **** try to find a footer file in this or parent directories **** */
+
+        /* first make a buffer big enough to hold path names we want to try */
+        fd = -1;
+        s = apr_palloc(r->pool, 
+                       strlen(r->filename) + strlen(conf->footfile) + 1);
+        strcpy(s, r->filename);
+    
+        for (;;)
+           {
+             p = rindex(s, '/');
+             if (p == NULL) break; /* failed to find one */
+    
+             p[1] = '\0';
+             strcat(p, conf->footfile);
+    
+             fd = open(s, O_RDONLY);
+             if (fd != -1) break; /* found one */
+
+             *p = '\0';
+           }
+            
+        if (fd == -1) /* failed to find a footer, so use standard default */
+          {
+            footer_formatted = apr_pstrdup(r->pool, "</body>");
+          }
+        else /* found a footer, so set up to use it */
+          {
+            fstat(fd, &statbuf);
+            footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+            read(fd, footer_formatted, statbuf.st_size);
+            footer_formatted[statbuf.st_size] = '\0';
+            close(fd);
+          }
+      }
+    else
+      {
+        admin_formatted = apr_pstrdup(r->pool, "");
+        footer_formatted = apr_pstrdup(r->pool, "</body>");
+      }
+
+    /* **** can now calculate the Content-Length and output headers **** */
+      
+    length = strlen(head_formatted) + strlen(header_formatted) + 
+             strlen(body_formatted) + strlen(admin_formatted)  +
+             strlen(footer_formatted);
+
+    ap_set_content_length(r, length);
+    ap_set_content_type(r, "text/html");
+
+    /* ** output the HTTP body (HTML Head+Body) ** */
+
+    ap_rputs(head_formatted,   r);
+    ap_rputs(header_formatted, r);
+    ap_rputs(body_formatted,   r);
+    ap_rputs(admin_formatted,  r);
+    ap_rputs(footer_formatted, r);
+
+    return OK;
+}
+
+char *make_passcode_file(request_rec *r, mod_gridsite_dir_cfg *conf, 
+                         char *path, apr_time_t expires_time)
+{
+    int           i;
+    char         *filetemplate, *notename_i, *grst_cred_i, *cookievalue=NULL;
+    apr_uint64_t  gridauthcookie;
+    apr_file_t   *fp;
+
+    /* create random for use in GRIDHTTP_PASSCODE cookies and file name */
+
+    if (apr_generate_random_bytes((char *) &gridauthcookie, 
+                                  sizeof(gridauthcookie))
+         != APR_SUCCESS) return NULL;
+    
+    filetemplate = apr_psprintf(r->pool, "%s/passcode-%016lxXXXXXX", 
+     ap_server_root_relative(r->pool,
+     sessionsdir),
+     gridauthcookie);
+
+    if (apr_file_mktemp(&fp, 
+                        filetemplate, 
+                        APR_CREATE | APR_WRITE | APR_EXCL,
+                        r->pool)
+                      != APR_SUCCESS) return NULL;
+                      
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "Created passcode file %s", filetemplate);
+
+    if (expires_time > 0) apr_file_printf(fp, "expires=%lu\n",
+                                      (time_t) apr_time_sec(expires_time));
+
+    apr_file_printf(fp, "domain=%s\npath=%s\n", r->hostname, path);
+
+    for (i=0; ; ++i)
+       {
+         notename_i = apr_psprintf(r->pool, "GRST_CRED_AURI_%d", i);
+         if (grst_cred_i = (char *)
+                           apr_table_get(r->connection->notes, notename_i))
+           {
+             apr_file_printf(fp, "%s=%s\n", notename_i, grst_cred_i);
+           }
+         else break; /* GRST_CRED_AURI_i are numbered consecutively */
+
+         notename_i = apr_psprintf(r->pool, "GRST_CRED_VALID_%d", i);
+         if (grst_cred_i = (char *)
+                           apr_table_get(r->connection->notes, notename_i))
+           {
+             apr_file_printf(fp, "%s=%s\n", notename_i, grst_cred_i);
+           }
+         else break; /* GRST_CRED_VALID_i are numbered consecutively */
+       }
+
+    if (apr_file_close(fp) != APR_SUCCESS) 
+      {
+        apr_file_remove(filetemplate, r->pool); /* try to clean up */
+        return NULL;
+      }
+      
+    cookievalue = rindex(filetemplate, '-');
+    if (cookievalue != NULL) 
+      {
+        ++cookievalue;
+        return cookievalue;
+      }
+    else return NULL;
+}
+
+int http_gridhttp(request_rec *r, mod_gridsite_dir_cfg *conf)
+{ 
+    char        *httpurl, *cookievalue, expires_str[APR_RFC822_DATE_LEN];
+    apr_time_t   expires_time;
+
+    /* passcode cookies are valid for only 5 mins! */
+    expires_time = apr_time_now() + apr_time_from_sec(300);
+
+    /* try to generate passcode and make passcode file */
+    cookievalue = make_passcode_file(r, conf, r->uri, expires_time);
+    
+    if (cookievalue == NULL) return HTTP_INTERNAL_SERVER_ERROR;
+    
+    /* send redirection header back to client */
+       
+    apr_rfc822_date(expires_str, expires_time);
+
+    apr_table_add(r->headers_out, 
+                  apr_pstrdup(r->pool, "Set-Cookie"),
+                  apr_psprintf(r->pool,
+                  "GRIDHTTP_PASSCODE=%s; "
+                  "expires=%s; "
+                  "domain=%s; "
+                  "path=%s",
+                  cookievalue, expires_str, r->hostname, r->uri));
+
+    if (gridhttpport != DEFAULT_HTTP_PORT)
+         httpurl = apr_psprintf(r->pool, "http://%s:%d%s", r->hostname,
+                                gridhttpport, ap_escape_uri(r->pool, r->uri));
+    else httpurl = apr_pstrcat(r->pool, "http://", r->hostname,
+                                ap_escape_uri(r->pool, r->uri), NULL);
+
+    apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), httpurl);
+
+    r->status = HTTP_MOVED_TEMPORARILY;  
+    return OK;
+}
+
+int http_put_method(request_rec *r, mod_gridsite_dir_cfg *conf)
+{
+  char        buf[2048], *filename, *dirname, *basename;
+  const char  *p;
+  size_t      block_length, length_sent;
+  int         retcode, stat_ret;
+  apr_file_t *fp;
+  struct stat statbuf;
+  int       has_range = 0, is_done = 0;
+  apr_off_t range_start, range_end, range_length, length_to_send, length = 0;
+  
+  /* ***  check if directory creation: PUT /.../  *** */
+
+  if ((r->unparsed_uri    != NULL) && 
+      (r->unparsed_uri[0] != '\0') &&
+      (r->unparsed_uri[strlen(r->unparsed_uri) - 1] == '/'))
+    {
+      if (apr_dir_make(r->filename, 
+                       conf->diskmode 
+                       | APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE, 
+                       r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR;
+
+      /* we force the permissions, rather than accept any existing ones */
+
+      apr_file_perms_set(r->filename, conf->diskmode
+                             | APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE);
+                             
+      ap_set_content_length(r, 0);
+      ap_set_content_type(r, "text/html");
+      return OK;
+    }
+
+  /* ***  otherwise assume trying to create a regular file *** */
+
+  stat_ret = stat(r->filename, &statbuf);
+
+  /* find if a range is specified */
+
+  has_range = parse_content_range(r, &range_start, &range_end, &range_length);
+
+  if (has_range)
+    {
+       if ((range_start == 0) && (range_end == 0)) /* truncate? */
+         {
+           if (stat_ret != 0) return HTTP_NOT_FOUND;
+          
+           if (truncate(r->filename, range_length) != 0)
+                return HTTP_INTERNAL_SERVER_ERROR;
+           else return OK;
+         }
+    
+       filename = r->filename;
+
+       if (apr_file_open(&fp, filename, APR_WRITE | APR_CREATE | APR_BUFFERED,
+            conf->diskmode, r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR;
+    }
+  else /* use temporary file if not a partial transfer */ 
+    {
+      dirname = apr_pstrdup(r->pool, r->filename);
+      basename = rindex(dirname, '/');
+      if (basename == NULL) return HTTP_INTERNAL_SERVER_ERROR;
+        
+      *basename = '\0';
+      ++basename;
+
+      filename = apr_psprintf(r->pool,
+                             "%s/.grsttmp-%s-XXXXXX", dirname, basename);
+
+      if (apr_file_mktemp(&fp, filename,
+                    APR_CREATE | APR_WRITE | APR_BUFFERED | APR_EXCL, r->pool)
+                    != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR;
+/*
+      p = apr_table_get(r->headers_in, "Content-Length");
+      if (p != NULL) 
+        {
+          length = (apr_off_t) atol(p);
+          if (length > 16384)
+            {
+              if (apr_file_seek(fp, APR_SET, &length) == 0)
+                {
+                  block_length = 1;
+                  apr_file_write(fp, "0", &block_length);
+                }
+
+              apr_file_seek(fp, APR_SET, 0);
+            }
+        }
+*/
+    }
+
+  /* we force the permissions, rather than accept any existing ones */
+
+  apr_file_perms_set(filename, conf->diskmode);
+
+  if (has_range)
+    {
+      if (apr_file_seek(fp, APR_SET, &range_start) != 0) 
+        {
+          retcode = HTTP_INTERNAL_SERVER_ERROR;
+          return retcode;
+        }
+
+      length_to_send = range_end - range_start + 1;
+    }
+
+  retcode = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
+  if (retcode == OK)
+    {
+      if (has_range) length_sent = 0;
+
+      if (ap_should_client_block(r))
+          while ((block_length = ap_get_client_block(r, buf, sizeof(buf))) > 0)
+            {
+              if (has_range && (length_sent + block_length > length_to_send))
+                {
+                  block_length = length_to_send - length_sent;
+                  is_done = 1;
+                }
+
+              if (apr_file_write(fp, buf, &block_length) != 0) 
+                {
+                  retcode = HTTP_INTERNAL_SERVER_ERROR;
+                  break;
+                }
+
+              if (has_range)
+                {
+                  if (is_done) break;
+                  else length_sent += block_length;
+                }
+            }
+      ap_set_content_length(r, 0);
+      ap_set_content_type(r, "text/html");
+    }
+
+  if ((apr_file_close(fp) != 0) || (retcode == HTTP_INTERNAL_SERVER_ERROR))
+    {
+      if (strcmp(filename, r->filename) != 0) remove(filename);
+      return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+  if ((strcmp(filename, r->filename) != 0) &&
+      (apr_file_rename(filename, r->filename, r->pool) != 0))
+      return HTTP_FORBIDDEN; /* best guess as to the problem ... */
+
+  if ((retcode == OK) && (stat_ret != 0))
+    {
+      retcode = HTTP_CREATED;
+      ap_custom_response(r, HTTP_CREATED, "");
+    }
+
+  return retcode;
+}
+
+int http_delete_method(request_rec *r, mod_gridsite_dir_cfg *conf)
+{
+  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "Try remove(%s)", r->filename);
+
+  if (remove(r->filename) != 0) return HTTP_FORBIDDEN;
+       
+  ap_set_content_length(r, 0);
+  ap_set_content_type(r, "text/html");
+
+  return OK;
+}
+
+int http_move_method(request_rec *r, mod_gridsite_dir_cfg *conf)
+{
+  char *destination_translated = NULL;
+  
+  if (r->notes != NULL) destination_translated = 
+            (char *) apr_table_get(r->notes, "GRST_DESTINATION_TRANSLATED");
+
+  if (destination_translated == NULL) return HTTP_BAD_REQUEST;
+  
+  if (strcmp(r->filename, destination_translated) == 0)
+                                      return HTTP_FORBIDDEN;
+  
+  if (apr_file_rename(r->filename, destination_translated, r->pool) != 0)
+                                                       return HTTP_FORBIDDEN;
+
+  ap_set_content_length(r, 0);
+  ap_set_content_type(r, "text/html");
+
+  return OK;
+}
+
+static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_dir_cfg *conf)
+/*
+   handler switch for directories
+*/
+{
+    /* *** is this a write method? only possible if  GridSiteAuth on *** */
+
+    if (conf->auth)
+      {
+        if ((r->method_number == M_PUT) && 
+            (conf->methods != NULL) &&
+            (strstr(conf->methods, " PUT "   ) != NULL))
+                                           return http_put_method(r, conf);
+
+        if ((r->method_number == M_DELETE) &&
+            (conf->methods != NULL) &&
+            (strstr(conf->methods, " DELETE ") != NULL)) 
+                                           return http_delete_method(r, conf);
+      }
+      
+    /* *** directory listing? *** */
+    if ((r->method_number == M_GET) && (conf->indexes))       
+                       return html_dir_list(r, conf); /* directory listing */
+    
+    return DECLINED; /* *** nothing to see here, move along *** */
+}
+
+static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_dir_cfg *conf)
+/*
+   one big handler switch for everything other than directories, since we 
+   might be responding to MIME * / * for local PUT, MOVE, COPY and DELETE, 
+   and GET inside ghost directories.
+*/
+{
+    char *upgradeheader, *upgradespaced, *p;
+    const char *https_env;
+
+    /* *** is this a write method or GridHTTP HTTPS->HTTP redirection? 
+           only possible if  GridSiteAuth on *** */
+    
+    if (conf->auth)
+      {
+        if ((conf->gridhttp) &&
+            ((r->method_number == M_GET) || 
+             ((r->method_number == M_PUT) && 
+              (strstr(conf->methods, " PUT ") != NULL))) &&
+            ((upgradeheader = (char *) apr_table_get(r->headers_in,
+                                                     "Upgrade")) != NULL) &&
+            ((https_env=apr_table_get(r->subprocess_env,"HTTPS")) != NULL) &&
+            (strcasecmp(https_env, "on") == 0))
+          {
+            upgradespaced = apr_psprintf(r->pool, " %s ", upgradeheader);
+
+            for (p=upgradespaced; *p != '\0'; ++p)
+             if ((*p == ',') || (*p == '\t')) *p = ' ';
+
+// TODO: what if we're pointing at a CGI or some dynamic content???
+            if (strstr(upgradespaced, " GridHTTP/1.0 ") != NULL)
+                                            return http_gridhttp(r, conf);
+          }
+
+        if ((r->method_number == M_PUT) && 
+            (conf->methods != NULL) &&
+            (strstr(conf->methods, " PUT "   ) != NULL))
+                                           return http_put_method(r, conf);
+
+        if ((r->method_number == M_DELETE) &&
+            (conf->methods != NULL) &&
+            (strstr(conf->methods, " DELETE ") != NULL)) 
+                                           return http_delete_method(r, conf);
+
+        if ((r->method_number == M_MOVE) &&
+            (conf->methods != NULL) &&
+            (strstr(conf->methods, " MOVE ") != NULL)) 
+                                           return http_move_method(r, conf);
+      }
+
+    /* *** check if a special ghost admin CGI *** */
+      
+    if (conf->adminfile && conf->adminuri &&
+        (strlen(r->filename) > strlen(conf->adminfile) + 1) &&
+        (strcmp(&(r->filename[strlen(r->filename) - strlen(conf->adminfile)]),
+                                                    conf->adminfile) == 0) &&
+        (r->filename[strlen(r->filename)-strlen(conf->adminfile)-1] == '/') &&
+        ((r->method_number == M_POST) ||
+         (r->method_number == M_GET))) 
+      {
+        ap_internal_redirect(conf->adminuri, r);
+        return OK;
+      }
+      
+    /* *** finally look for .html files that we should format *** */
+
+    if ((conf->format) &&  /* conf->format set by  GridSiteHtmlFormat on */ 
+        (strlen(r->filename) > 5) &&
+        (strcmp(&(r->filename[strlen(r->filename)-5]), ".html") == 0) &&
+        (r->method_number == M_GET)) return html_format(r, conf);
+     
+    return DECLINED; /* *** nothing to see here, move along *** */
+}
+
+static void recurse4dirlist(char *dirname, time_t *dirs_time,
+                             char *fulluri, int fullurilen,
+                             char *encfulluri, int enclen,
+                             request_rec *r, char **body,
+                             int recurse_level)
+/* try to find DN Lists in dir[] and its subdirs that match the fulluri[]
+   prefix. add blobs of HTML to body as they are found. */
+{
+   char          *unencname, modified[99], *oneline, *d_namepath,
+                 *mildencoded;
+   DIR           *oneDIR;
+   struct dirent *onedirent;
+   struct tm      mtime_tm;
+   struct stat    statbuf;
+
+   if ((stat(dirname, &statbuf) != 0) ||
+       (!S_ISDIR(statbuf.st_mode)) ||
+       ((oneDIR = opendir(dirname)) == NULL)) return;
+
+   if (statbuf.st_mtime > *dirs_time) *dirs_time = statbuf.st_mtime;
+
+   while ((onedirent = readdir(oneDIR)) != NULL)
+        {
+          if (onedirent->d_name[0] == '.') continue;
+        
+          d_namepath = apr_psprintf(r->pool, "%s/%s", dirname, onedirent->d_name);
+
+          if (stat(d_namepath, &statbuf) != 0) continue;
+
+          if (S_ISDIR(statbuf.st_mode))
+            {
+              if (recurse_level < GRST_RECURS_LIMIT)
+                 recurse4dirlist(d_namepath, dirs_time, fulluri,
+                                 fullurilen, encfulluri, enclen, 
+                                 r, body, recurse_level + 1);
+            }
+          else if ((strncmp(onedirent->d_name, encfulluri, enclen) == 0) &&
+                   (onedirent->d_name[strlen(onedirent->d_name) - 1] != '~'))
+            {
+              unencname = GRSThttpUrlDecode(onedirent->d_name);
+                    
+              if (strncmp(unencname, fulluri, fullurilen) == 0)
+                {
+                  if (statbuf.st_mtime > *dirs_time) 
+                                                *dirs_time = statbuf.st_mtime;
+
+                  localtime_r(&(statbuf.st_mtime), &mtime_tm);
+                  strftime(modified, sizeof(modified), 
+              "<td align=right>%R</td><td align=right>%e&nbsp;%b&nbsp;%y</td>",
+                       &mtime_tm);
+                  
+                  mildencoded = GRSThttpUrlMildencode(&unencname[fullurilen]);
+                 
+                  oneline = apr_psprintf(r->pool,
+                                     "<tr><td><a href=\"%s\" "
+                                     "content-length=\"%ld\" "
+                                     "last-modified=\"%ld\">"
+                                     "%s</a></td>"
+                                     "<td align=right>%ld</td>%s</tr>\n", 
+                                     mildencoded, statbuf.st_size, 
+                                     statbuf.st_mtime, 
+                                     html_escape(r->pool, unencname), 
+                                     statbuf.st_size, modified);
+
+                  free(mildencoded);
+
+                  *body = apr_pstrcat(r->pool, *body, oneline, NULL);
+                }      
+                      
+              free(unencname); /* libgridsite doesnt use pools */
+            }
+        }
+        
+   closedir(oneDIR);
+}
+
+static int mod_gridsite_dnlistsuri_dir_handler(request_rec *r, 
+                                               mod_gridsite_dir_cfg *conf)
+/*
+    virtual DN-list file lister: make all DN lists on the dn-lists
+    path of this server appear to be in the dn-lists directory itself
+    (ie where they appear in the DN lists path doesnt matter, as long
+    as their name matches)
+*/
+{
+    int            enclen, fullurilen, fd;
+    char          *fulluri, *encfulluri, *dn_list_ptr, *dirname,
+                  *body, *oneline, *p, *s,
+                  *head_formatted, *header_formatted, *footer_formatted,
+                  *permstr = NULL;
+    struct stat    statbuf;
+    size_t         length;
+    time_t         dirs_time = 0;
+    GRSTgaclPerm   perm = GRST_PERM_NONE;
+        
+    if (r->notes != NULL)
+           permstr = (char *) apr_table_get(r->notes, "GRST_PERM");
+
+    if (permstr != NULL) sscanf(permstr, "%d", &perm);
+
+    fulluri = apr_psprintf(r->pool, "https://%s%s",
+                                    r->hostname, conf->dnlistsuri);
+    fullurilen = strlen(fulluri);
+
+    encfulluri = GRSThttpUrlEncode(fulluri);
+    enclen     = strlen(encfulluri);
+
+    if (conf->dnlists != NULL) p = conf->dnlists;
+    else p = getenv("GRST_DN_LISTS");
+
+    if (p == NULL) p = GRST_DN_LISTS;
+    dn_list_ptr = apr_pstrdup(r->pool, p);
+
+    head_formatted = apr_psprintf(r->pool, 
+      "<head><title>Directory listing %s</title></head>\n", r->uri);
+
+    if (conf->format)
+      {
+        /* **** try to find a header file in this or parent directories **** */
+
+        fd = -1;
+
+        if (conf->headfile[0] == '/') /* try absolute */
+          {
+            fd = open(conf->headfile, O_RDONLY);
+          }
+        else /* try relative */
+          {
+            /* first make a buffer big enough to hold path names we want to try */
+            s = malloc(strlen(r->filename) + strlen(conf->headfile) + 1);
+            strcpy(s, r->filename);
+    
+            for (;;)
+               {
+                 p = rindex(s, '/');
+                 if (p == NULL) break; /* failed to find one */
+                 p[1] = '\0';
+                 strcat(p, conf->headfile);
+    
+                 fd = open(s, O_RDONLY);
+                 if (fd != -1) break; /* found one */
+
+                 *p = '\0';
+               }
+            
+             free(s);
+           }
+           
+        if (fd == -1) /* not found, so set up to output sensible default */
+          {
+            header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+          }
+        else /* found a header file, so set up head and body to surround it */
+          {
+            fstat(fd, &statbuf);
+            header_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+            read(fd, header_formatted, statbuf.st_size);
+            header_formatted[statbuf.st_size] = '\0';
+            close(fd);
+          }
+      }
+    else header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+            
+    body = apr_psprintf(r->pool, 
+      "<h1>Directory listing %s</h1>\n<table>", r->uri);
+
+    if ((r->uri)[1] != '\0')
+     body = apr_pstrcat(r->pool, body, 
+       "<tr><td>[<a href=\"../\">Parent directory</a>]</td></tr>\n",
+       NULL);
+
+    while ((dirname = strsep(&dn_list_ptr, ":")) != NULL)
+        recurse4dirlist(dirname, &dirs_time, fulluri, fullurilen,
+                                 encfulluri, enclen, r, &body, 0);
+
+    p = (char *) apr_table_get(r->subprocess_env, "HTTPS");
+    if ((p != NULL) && (strcmp(p, "on") == 0))
+      {
+        oneline = apr_psprintf(r->pool,
+           "<form action=\"%s%s\" method=post>\n"
+           "<input type=hidden name=cmd value=managednlists>"
+           "<tr><td colspan=4 align=center><small><input type=submit "
+           "value=\"Manage DN lists\"></small></td></tr></form>\n",
+           r->uri, conf->adminfile);
+          
+        body = apr_pstrcat(r->pool, body, oneline, NULL);
+      }
+
+    body = apr_pstrcat(r->pool, body, "</table>\n", NULL);
+
+    free(encfulluri); /* libgridsite doesnt use pools */
+
+    if (conf->format)
+      {
+        /* **** try to find a footer file in this or parent directories **** */
+
+        fd = -1;
+
+        if (conf->headfile[0] == '/') /* try absolute */
+          {
+            fd = open(conf->headfile, O_RDONLY);
+          }
+        else /* try relative */
+          {
+            /* first make a buffer big enough to hold path names we want to try */
+            s  = malloc(strlen(r->filename) + strlen(conf->footfile));
+            strcpy(s, r->filename);
+    
+            for (;;)
+               {
+                 p = rindex(s, '/');
+                 if (p == NULL) break; /* failed to find one */
+
+                 p[1] = '\0';
+                 strcat(p, conf->footfile);
+    
+                 fd = open(s, O_RDONLY);
+                 if (fd != -1) break; /* found one */
+
+                 *p = '\0';
+               }
+            
+            free(s);
+          }
+
+        if (fd == -1) /* failed to find a footer, so use standard default */
+          {
+            footer_formatted = apr_pstrdup(r->pool, "</body>");
+          }
+        else /* found a footer, so set up to use it */
+          {
+            fstat(fd, &statbuf);
+            footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+            read(fd, footer_formatted, statbuf.st_size);
+            footer_formatted[statbuf.st_size] = '\0';
+            close(fd);
+          }
+      }
+    else footer_formatted = apr_pstrdup(r->pool, "</body>");
+
+    /* **** can now calculate the Content-Length and output headers **** */
+      
+    length = strlen(head_formatted) + strlen(header_formatted) + 
+             strlen(body) + strlen(footer_formatted);
+
+    ap_set_content_length(r, length);
+    r->mtime = apr_time_from_sec(dirs_time);
+    ap_set_last_modified(r);
+    ap_set_content_type(r, "text/html");
+
+    /* ** output the HTTP body (HTML Head+Body) ** */
+    ap_rputs(head_formatted,   r);
+    ap_rputs(header_formatted, r);
+    ap_rputs(body,                r);
+    ap_rputs(footer_formatted, r);
+
+    return OK;
+}
+
+static char *recurse4file(char *dir, char *file, apr_pool_t *pool, 
+                          int recurse_level)
+/* try to find file[] in dir[]. try subdirs if not found.
+   return full path to first found version or NULL on failure */
+{
+    char          *fullfilename, *fulldirname;
+    struct stat    statbuf;
+    DIR           *dirDIR;
+    struct dirent *file_ent;
+
+    /* try to find in current directory */
+
+    fullfilename = apr_psprintf(pool, "%s/%s", dir, file);
+
+    if (stat(fullfilename, &statbuf) == 0) return fullfilename;
+
+    /* maybe search in subdirectories */
+
+    if (recurse_level >= GRST_RECURS_LIMIT) return NULL;
+
+    dirDIR = opendir(dir);
+
+    if (dirDIR == NULL) return NULL;
+
+    while ((file_ent = readdir(dirDIR)) != NULL)
+       {
+         if (file_ent->d_name[0] == '.') continue;
+
+         fulldirname = apr_psprintf(pool, "%s/%s", dir, file_ent->d_name);
+         if ((stat(fulldirname, &statbuf) == 0) &&
+             S_ISDIR(statbuf.st_mode) &&
+             ((fullfilename = recurse4file(fulldirname, file,
+                                           pool, recurse_level + 1)) != NULL))
+           {
+             closedir(dirDIR);
+             return fullfilename;
+           }
+       }
+
+    closedir(dirDIR);
+
+    return NULL;
+}
+
+static int mod_gridsite_dnlistsuri_handler(request_rec *r, 
+                                           mod_gridsite_dir_cfg *conf)
+/*
+    virtual DN-list file generator
+*/
+{
+    int          fd;
+    char        *fulluri, *encfulluri, *dn_list_ptr, *filename, *dirname, *p,
+                *buf;
+    struct stat  statbuf;
+    
+    /* *** check if a special ghost admin CGI *** */
+      
+    if (conf->adminfile && conf->adminuri &&
+        (strlen(r->filename) > strlen(conf->adminfile) + 1) &&
+        (strcmp(&(r->filename[strlen(r->filename) - strlen(conf->adminfile)]),
+                                                    conf->adminfile) == 0) &&
+        (r->filename[strlen(r->filename)-strlen(conf->adminfile)-1] == '/') &&
+        ((r->method_number == M_POST) ||
+         (r->method_number == M_GET))) 
+      {
+        ap_internal_redirect(conf->adminuri, r);
+        return OK;
+      }
+      
+    if (r->uri[strlen(r->uri) - 1] == '/') 
+      {
+        apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), 
+                                       apr_pstrdup(r->pool, conf->dnlistsuri));
+
+        r->status = HTTP_MOVED_TEMPORARILY;
+        return OK;                   
+      }
+
+    fulluri = apr_psprintf(r->pool, "https://%s%s", 
+                                    r->hostname, r->uri);
+
+    encfulluri = GRSThttpUrlEncode(fulluri);
+    
+    if (conf->dnlists != NULL) p = conf->dnlists;
+    else p = getenv("GRST_DN_LISTS");
+    if (p == NULL) p = GRST_DN_LISTS;
+    dn_list_ptr = apr_pstrdup(r->pool, p);
+
+    while ((dirname = strsep(&dn_list_ptr, ":")) != NULL)
+       {
+         filename = recurse4file(dirname, encfulluri, r->pool, 0);
+
+         if (filename == NULL) continue;
+    
+         fd = open(filename, O_RDONLY);
+
+         if (fd == -1) continue;
+
+         fstat(fd, &statbuf);         
+         ap_set_content_length(r, (apr_off_t) statbuf.st_size);
+         r->mtime = apr_time_from_sec(statbuf.st_mtime);
+         ap_set_content_type(r, "text/plain");
+         ap_set_last_modified(r);
+
+         buf = apr_palloc(r->pool, statbuf.st_size + 1);
+         read(fd, buf, statbuf.st_size);
+         buf[statbuf.st_size] = '\0';
+            
+         ap_rputs(buf, r);
+
+         close(fd);
+
+         return OK;
+       }
+
+    return HTTP_NOT_FOUND;
+}
+
+static void *create_gridsite_srv_config(apr_pool_t *p, server_rec *s)
+{
+    int i;
+
+    /* only run once (in base server) */
+    if (!(s->is_virtual) && (gridhttpport == 0))
+      {
+        gridhttpport = GRST_HTTP_PORT;
+      
+        sessionsdir = apr_pstrdup(p, GRST_SESSIONS_DIR);
+                                      /* GridSiteSessionsDir dir-path   */
+
+        sitecastdnlists = NULL;
+
+        sitecastgroups[0].port  = GRST_HTCP_PORT;
+                                      /* GridSiteCastUniPort udp-port */
+
+        for (i=1; i <= GRST_SITECAST_GROUPS; ++i)
+           {
+             sitecastgroups[i].port = 0; /* GridSiteCastGroup mcast-list */
+           }
+
+        for (i=0; i <= GRST_SITECAST_ALIASES; ++i)
+           {
+             sitecastaliases[i].sitecast_url   = NULL;
+             sitecastaliases[i].port           = 0;
+             sitecastaliases[i].scheme         = NULL;
+             sitecastaliases[i].local_path     = NULL;
+             sitecastaliases[i].local_hostname = NULL;
+           }                              /* GridSiteCastAlias url path */
+      }
+
+    return NULL;
+}
+
+static void *create_gridsite_dir_config(apr_pool_t *p, char *path)
+{
+    mod_gridsite_dir_cfg *conf = apr_palloc(p, sizeof(*conf));
+
+    if (path == NULL) /* set up document root defaults */
+      {
+        conf->auth          = 0;     /* GridSiteAuth          on/off       */
+        conf->autopasscode  = 1;     /* GridSiteAutoPasscode  on/off       */
+        conf->requirepasscode = 0;   /* GridSiteRequirePasscode on/off     */
+        conf->zoneslashes   = 1;     /* GridSiteZoneSlashes   number       */
+        conf->envs          = 1;     /* GridSiteEnvs          on/off       */
+        conf->format        = 0;     /* GridSiteHtmlFormat    on/off       */
+        conf->indexes       = 0;     /* GridSiteIndexes       on/off       */
+        conf->indexheader   = NULL;  /* GridSiteIndexHeader   File-value   */
+        conf->gridsitelink  = 1;     /* GridSiteLink          on/off       */
+        conf->adminfile     = apr_pstrdup(p, GRST_ADMIN_FILE);
+                                /* GridSiteAdminFile      File-value   */
+        conf->adminuri      = NULL;  /* GridSiteAdminURI      URI-value    */
+        conf->helpuri       = NULL;  /* GridSiteHelpURI       URI-value    */
+        conf->loginuri      = NULL;  /* GridSiteLoginURI      URI-value    */
+        conf->dnlists       = NULL;  /* GridSiteDNlists       Search-path  */
+        conf->dnlistsuri    = NULL;  /* GridSiteDNlistsURI    URI-value    */
+        conf->adminlist     = NULL;  /* GridSiteAdminList     URI-value    */
+        conf->gsiproxylimit = 1000;  /* GridSiteGSIProxyLimit number       */
+        conf->unzip         = NULL;  /* GridSiteUnzip         file-path    */
+
+        conf->methods    = apr_pstrdup(p, " GET ");
+                                        /* GridSiteMethods      methods    */
+
+        conf->editable = apr_pstrdup(p, " txt shtml html htm css js php jsp ");
+                                        /* GridSiteEditable     types   */
+
+        conf->headfile = apr_pstrdup(p, GRST_HEADFILE);
+        conf->footfile = apr_pstrdup(p, GRST_FOOTFILE);
+               /* GridSiteHeadFile and GridSiteFootFile  file name */
+
+        conf->gridhttp      = 0;     /* GridSiteGridHTTP      on/off       */
+       conf->aclformat     = apr_pstrdup(p, "GACL");
+                                     /* GridSiteACLFormat     gacl/xacml   */
+       conf->aclpath       = NULL;  /* GridSiteACLPath       acl-path     */
+       conf->delegationuri = NULL;  /* GridSiteDelegationURI URI-value    */
+       conf->execmethod    = NULL;
+               /* GridSiteExecMethod  nosetuid/suexec/X509DN/directory */
+               
+        conf->execugid.uid     = 0;    /* GridSiteUserGroup User Group */
+        conf->execugid.gid     = 0;    /* ditto */
+        conf->execugid.userdir = 0;    /* ditto */
+        
+        conf->diskmode = APR_UREAD | APR_UWRITE; 
+              /* GridSiteDiskMode group-mode world-mode
+                 GroupNone | GroupRead | GroupWrite   WorldNone | WorldRead */
+      }
+    else
+      {
+        conf->auth          = UNSET; /* GridSiteAuth          on/off       */
+        conf->autopasscode  = UNSET; /* GridSiteAutoPasscode  on/off       */
+        conf->requirepasscode = UNSET; /* GridSiteRequirePasscode on/off   */
+        conf->zoneslashes   = UNSET; /* GridSiteZoneSlashes   number       */
+        conf->envs          = UNSET; /* GridSiteEnvs          on/off       */
+        conf->format        = UNSET; /* GridSiteHtmlFormat    on/off       */
+        conf->indexes       = UNSET; /* GridSiteIndexes       on/off       */
+        conf->indexheader   = NULL;  /* GridSiteIndexHeader   File-value   */
+        conf->gridsitelink  = UNSET; /* GridSiteLink          on/off       */
+        conf->adminfile     = NULL;  /* GridSiteAdminFile     File-value   */
+        conf->adminuri      = NULL;  /* GridSiteAdminURI      URI-value    */
+        conf->helpuri       = NULL;  /* GridSiteHelpURI       URI-value    */
+        conf->loginuri      = NULL;  /* GridSiteLoginURI      URI-value    */
+        conf->dnlists       = NULL;  /* GridSiteDNlists       Search-path  */
+        conf->dnlistsuri    = NULL;  /* GridSiteDNlistsURI    URI-value    */
+        conf->adminlist     = NULL;  /* GridSiteAdminList     URI-value    */
+        conf->gsiproxylimit = UNSET; /* GridSiteGSIProxyLimit number       */
+        conf->unzip         = NULL;  /* GridSiteUnzip         file-path    */
+        conf->methods       = NULL;  /* GridSiteMethods       methods      */
+        conf->editable      = NULL;  /* GridSiteEditable      types        */
+        conf->headfile      = NULL;  /* GridSiteHeadFile      file name    */
+        conf->footfile      = NULL;  /* GridSiteFootFile      file name    */
+        conf->gridhttp      = UNSET; /* GridSiteGridHTTP      on/off       */
+       conf->aclformat     = NULL;  /* GridSiteACLFormat     gacl/xacml   */
+       conf->aclpath       = NULL;  /* GridSiteACLPath       acl-path     */
+       conf->delegationuri = NULL;  /* GridSiteDelegationURI URI-value    */
+       conf->execmethod    = NULL;  /* GridSiteExecMethod */
+        conf->execugid.uid     = UNSET;        /* GridSiteUserGroup User Group */
+        conf->execugid.gid     = UNSET; /* ditto */
+        conf->execugid.userdir = UNSET; /* ditto */
+        conf->diskmode     = UNSET; /* GridSiteDiskMode group world */
+      }
+
+    return conf;
+}
+
+static void *merge_gridsite_dir_config(apr_pool_t *p, void *vserver,
+                                                      void *vdirect)
+/* merge directory with server-wide directory configs */
+{
+    mod_gridsite_dir_cfg *conf, *server, *direct;
+
+    server = (mod_gridsite_dir_cfg *) vserver;
+    direct = (mod_gridsite_dir_cfg *) vdirect;
+    conf = apr_palloc(p, sizeof(*conf));
+
+    if (direct->auth != UNSET) conf->auth = direct->auth;
+    else                       conf->auth = server->auth;
+
+    if (direct->autopasscode != UNSET) conf->autopasscode = direct->autopasscode;
+    else                               conf->autopasscode = server->autopasscode;
+
+    if (direct->requirepasscode != UNSET) conf->requirepasscode = direct->requirepasscode;
+    else                               conf->requirepasscode = server->requirepasscode;
+
+    if (direct->zoneslashes != UNSET) conf->zoneslashes = direct->zoneslashes;
+    else                              conf->zoneslashes = server->zoneslashes;
+
+    if (direct->envs != UNSET) conf->envs = direct->envs;
+    else                       conf->envs = server->envs;
+        
+    if (direct->format != UNSET) conf->format = direct->format;
+    else                         conf->format = server->format;
+        
+    if (direct->indexes != UNSET) conf->indexes = direct->indexes;
+    else                          conf->indexes = server->indexes;
+        
+    if (direct->gridsitelink != UNSET) conf->gridsitelink=direct->gridsitelink;
+    else                               conf->gridsitelink=server->gridsitelink;
+
+    if (direct->indexheader != NULL) conf->indexheader = direct->indexheader;
+    else                             conf->indexheader = server->indexheader;
+        
+    if (direct->adminfile != NULL) conf->adminfile = direct->adminfile;
+    else                           conf->adminfile = server->adminfile;
+        
+    if (direct->adminuri != NULL) conf->adminuri = direct->adminuri;
+    else                          conf->adminuri = server->adminuri;
+        
+    if (direct->helpuri != NULL) conf->helpuri = direct->helpuri;
+    else                         conf->helpuri = server->helpuri;
+        
+    if (direct->loginuri != NULL) conf->loginuri = direct->loginuri;
+    else                          conf->loginuri = server->loginuri;
+
+    if (direct->dnlists != NULL) conf->dnlists = direct->dnlists;
+    else                         conf->dnlists = server->dnlists;
+        
+    if (direct->dnlistsuri != NULL) conf->dnlistsuri = direct->dnlistsuri;
+    else                            conf->dnlistsuri = server->dnlistsuri;
+
+    if (direct->adminlist != NULL) conf->adminlist = direct->adminlist;
+    else                           conf->adminlist = server->adminlist;
+
+    if (direct->gsiproxylimit != UNSET)
+                         conf->gsiproxylimit = direct->gsiproxylimit;
+    else                 conf->gsiproxylimit = server->gsiproxylimit;
+
+    if (direct->unzip != NULL) conf->unzip = direct->unzip;
+    else                       conf->unzip = server->unzip;
+
+    if (direct->methods != NULL) conf->methods = direct->methods;
+    else                         conf->methods = server->methods;
+
+    if (direct->editable != NULL) conf->editable = direct->editable;
+    else                          conf->editable = server->editable;
+
+    if (direct->headfile != NULL) conf->headfile = direct->headfile;
+    else                          conf->headfile = server->headfile;
+
+    if (direct->footfile != NULL) conf->footfile = direct->footfile;
+    else                          conf->footfile = server->footfile;
+
+    if (direct->gridhttp != UNSET) conf->gridhttp = direct->gridhttp;
+    else                           conf->gridhttp = server->gridhttp;
+        
+    if (direct->aclformat != NULL) conf->aclformat = direct->aclformat;
+    else                           conf->aclformat = server->aclformat;
+
+    if (direct->aclpath != NULL)   conf->aclpath = direct->aclpath;
+    else                           conf->aclpath = server->aclpath;
+
+    if (direct->delegationuri != NULL) conf->delegationuri = direct->delegationuri;
+    else                               conf->delegationuri = server->delegationuri;
+
+    if (direct->execmethod != NULL) conf->execmethod = direct->execmethod;
+    else                            conf->execmethod = server->execmethod;
+
+    if (direct->execugid.uid != UNSET)
+      { conf->execugid.uid = direct->execugid.uid;
+        conf->execugid.gid = direct->execugid.gid;
+        conf->execugid.userdir = direct->execugid.userdir; }
+    else
+      { conf->execugid.uid = server->execugid.uid;
+        conf->execugid.gid = server->execugid.gid;
+        conf->execugid.userdir = server->execugid.userdir; }
+
+    if (direct->diskmode != UNSET) conf->diskmode = direct->diskmode;
+    else                            conf->diskmode = server->diskmode;
+        
+    return conf;
+}
+
+static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg,
+                                           const char *parm)
+{
+    int   n, i;
+    char *p;
+  
+    if (strcasecmp(a->cmd->name, "GridSiteSessionsDir") == 0)
+    {
+      if (a->server->is_virtual)
+       return "GridSiteSessionsDir cannot be used inside a virtual server";
+    
+      sessionsdir = apr_pstrdup(a->pool, parm);
+    }
+/* GridSiteOnetimesDir is deprecated in favour of GridSiteSessionsDir */
+    else if (strcasecmp(a->cmd->name, "GridSiteOnetimesDir") == 0)
+    {
+      if (a->server->is_virtual)
+       return "GridSiteOnetimesDir cannot be used inside a virtual server";
+    
+      sessionsdir = apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteZoneSlashes") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->zoneslashes = atoi(parm);
+      
+      if (((mod_gridsite_dir_cfg *) cfg)->zoneslashes < 1)
+       return "GridSiteZoneSlashes must be greater than 0";
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteGridHTTPport") == 0)
+    {
+      gridhttpport = atoi(parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteCastDNlists") == 0)
+    {
+      if (a->server->is_virtual)
+       return "GridSiteDNlists cannot be used inside a virtual server";
+    
+      sitecastdnlists = apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteCastUniPort") == 0)
+    {
+      if (a->server->is_virtual)
+       return "GridSiteCastUniPort cannot be used inside a virtual server";
+
+      if (sscanf(parm, "%d", &(sitecastgroups[0].port)) != 1)
+        return "Failed parsing GridSiteCastUniPort numeric value";
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteCastGroup") == 0)
+    {
+      if (a->server->is_virtual)
+       return "GridSiteCastGroup cannot be used inside a virtual server";
+
+      for (i=1; i <= GRST_SITECAST_GROUPS; ++i)
+         {
+           if (sitecastgroups[i].port == 0) /* a free slot */
+             {
+               sitecastgroups[i].port = GRST_HTCP_PORT;
+             
+               if (sscanf(parm, "%s:%d",
+                          &(sitecastgroups[i].address), 
+                          &(sitecastgroups[i].port)) < 1)
+                 return "Failed parsing GridSiteCastGroup";
+                 
+               break;
+             }
+         }
+         
+      if (i > GRST_SITECAST_GROUPS)
+                     return "Maximum GridSiteCastGroup groups reached";
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteAdminFile") == 0)
+    {
+      if (index(parm, '/') != NULL) 
+           return "/ not permitted in GridSiteAdminFile";
+     
+      ((mod_gridsite_dir_cfg *) cfg)->adminfile =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteAdminURI") == 0)
+    {
+      if (*parm != '/') return "GridSiteAdminURI must begin with /";
+     
+      ((mod_gridsite_dir_cfg *) cfg)->adminuri =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteHelpURI") == 0)
+    {
+      if (*parm != '/') return "GridSiteHelpURI must begin with /";
+
+      ((mod_gridsite_dir_cfg *) cfg)->helpuri =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteDNlists") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->dnlists =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteDNlistsURI") == 0)
+    {
+      if (*parm != '/') return "GridSiteDNlistsURI must begin with /";
+
+      if ((*parm != '\0') && (parm[strlen(parm) - 1] == '/'))
+       ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri =
+        apr_pstrdup(a->pool, parm);
+      else
+       ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri =
+        apr_pstrcat(a->pool, parm, "/", NULL);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteAdminList") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->adminlist =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteGSIProxyLimit") == 0)
+    {
+      n = -1;
+    
+      if ((sscanf(parm, "%d", &n) == 1) && (n >= 0)) {
+                 if (n == 0)
+                     n = 1000; /* thousand is an African for "unlimited" */
+                  ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit = n;
+      }
+      else return "GridSiteGSIProxyLimit must be a number >= 0";     
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteUnzip") == 0)
+    {
+      if (*parm != '/') return "GridSiteUnzip must begin with /";
+     
+      ((mod_gridsite_dir_cfg *) cfg)->unzip =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteMethods") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->methods =
+        apr_psprintf(a->pool, " %s ", parm);
+       
+      for (p = ((mod_gridsite_dir_cfg *) cfg)->methods;
+           *p != '\0';
+           ++p) if (*p == '\t') *p = ' ';
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteOCSP") == 0)
+    {
+      ocspmodes = apr_psprintf(a->pool, " %s ", parm);
+       
+      for (p = ocspmodes; *p != '\0'; ++p)
+               if (*p == '\t') *p = ' ';
+               else *p = tolower(*p);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteEditable") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->editable =
+        apr_psprintf(a->pool, " %s ", parm);
+     
+      for (p = ((mod_gridsite_dir_cfg *) cfg)->editable;
+           *p != '\0';
+           ++p) if (*p == '\t') *p = ' ';
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteHeadFile") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->headfile =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteFootFile") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->footfile =
+        apr_pstrdup(a->pool, parm);
+    }  
+    else if (strcasecmp(a->cmd->name, "GridSiteIndexHeader") == 0)
+    {
+      if (index(parm, '/') != NULL) 
+           return "/ not permitted in GridSiteIndexHeader";
+
+      ((mod_gridsite_dir_cfg *) cfg)->indexheader =
+        apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteACLFormat") == 0)
+    {
+      if ((strcasecmp(parm,"GACL") != 0) &&
+          (strcasecmp(parm,"XACML") != 0))
+          return "GridsiteACLFormat must be either GACL or XACML";
+      
+      ((mod_gridsite_dir_cfg *) cfg)->aclformat = apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteACLPath") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->aclpath = apr_pstrdup(a->pool, parm);
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteDelegationURI") == 0)
+    {
+      if (*parm != '/') return "GridSiteDelegationURI must begin with /";
+
+      if (*parm != '\0')
+       ((mod_gridsite_dir_cfg *) cfg)->delegationuri =
+        apr_pstrdup(a->pool, parm);
+
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteExecMethod") == 0)
+    {
+      if (strcasecmp(parm, "nosetuid") == 0)
+        {
+          ((mod_gridsite_dir_cfg *) cfg)->execmethod = NULL;
+          return NULL;
+        }
+
+      if ((strcasecmp(parm, "suexec")    != 0) &&
+          (strcasecmp(parm, "X509DN")    != 0) &&
+          (strcasecmp(parm, "directory") != 0))
+          return "GridsiteExecMethod must be nosetuid, suexec, X509DN or directory";
+
+      ((mod_gridsite_dir_cfg *) cfg)->execmethod = apr_pstrdup(a->pool, parm);
+    }
+
+    return NULL;
+}
+
+static const char *mod_gridsite_take2_cmds(cmd_parms *a, void *cfg,
+                                       const char *parm1, const char *parm2)
+{
+    int   i;
+    char *p, *q, buf[APRMAXHOSTLEN + 1] = "localhost";
+    
+    if (strcasecmp(a->cmd->name, "GridSiteUserGroup") == 0)
+    {
+      if (!(unixd_config.suexec_enabled))
+          return "Using GridSiteUserGroup will "
+                 "require rebuilding Apache with suexec support!";
+    
+      /* NB ap_uname2id/ap_gname2id are NOT thread safe - but OK
+         as long as not used in .htaccess, just at server start time */
+
+      ((mod_gridsite_dir_cfg *) cfg)->execugid.uid = ap_uname2id(parm1);
+      ((mod_gridsite_dir_cfg *) cfg)->execugid.gid = ap_gname2id(parm2);
+      ((mod_gridsite_dir_cfg *) cfg)->execugid.userdir = 0;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteDiskMode") == 0)
+    {
+      if ((strcasecmp(parm1, "GroupNone" ) != 0) &&
+          (strcasecmp(parm1, "GroupRead" ) != 0) &&
+          (strcasecmp(parm1, "GroupWrite") != 0))
+        return "First parameter of GridSiteDiskMode must be "
+               "GroupNone, GroupRead or GroupWrite!";
+          
+      if ((strcasecmp(parm2, "WorldNone" ) != 0) &&
+          (strcasecmp(parm2, "WorldRead" ) != 0))
+        return "Second parameter of GridSiteDiskMode must be "
+               "WorldNone or WorldRead!";
+          
+      ((mod_gridsite_dir_cfg *) cfg)->diskmode = 
+       APR_UREAD | APR_UWRITE 
+       | ( APR_GREAD               * (strcasecmp(parm1, "GroupRead") == 0))
+       | ((APR_GREAD | APR_GWRITE) * (strcasecmp(parm1, "GroupWrite") == 0))
+       | ((APR_GREAD | APR_WREAD)  * (strcasecmp(parm2, "WorldRead") == 0));
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteCastAlias") == 0)
+    {
+      if ((parm1[strlen(parm1)-1] != '/') || (parm2[strlen(parm2)-1] != '/'))
+        return "GridSiteCastAlias URL and path must end with /";
+    
+      for (i=0; i < GRST_SITECAST_ALIASES; ++i) /* look for free slot */
+         {
+           if (sitecastaliases[i].sitecast_url == NULL)
+             {
+               sitecastaliases[i].scheme = apr_pstrdup(a->pool, parm1);
+
+               if (((p = index(sitecastaliases[i].scheme, ':')) == NULL)
+                   || (p[1] != '/') || (p[2] != '/'))
+                 return "GridSiteCastAlias URL must begin with scheme (http/https/gsiftp/...) and ://";
+
+               *p = '\0';
+               ++p;
+               while (*p == '/') ++p;
+             
+               if ((q = index(p, '/')) == NULL)
+                return "GridSiteCastAlias URL must be of form scheme://domain:port/dirs";
+
+               *q = '\0';
+
+               p = index(p, ':');
+               if (p == NULL)
+                 {
+                   return "GridSiteCastAlias URL must include the port number";
+                 }
+
+               if (sscanf(p, ":%d", &(sitecastaliases[i].port)) != 1)
+                 return "Unable to parse numeric port number in GridSiteCastAlias";
+
+               sitecastaliases[i].sitecast_url   = apr_pstrdup(a->pool, parm1);
+               sitecastaliases[i].local_path     = apr_pstrdup(a->pool, parm2);
+               
+               if (a->server->server_hostname == NULL)
+                 {
+                   apr_gethostname(buf, APRMAXHOSTLEN + 1, a->pool);
+                   sitecastaliases[i].local_hostname = apr_pstrdup(a->pool, buf);
+                 }
+               else sitecastaliases[i].local_hostname = apr_pstrdup(a->pool, 
+                                                   a->server->server_hostname);
+
+               break;
+             }
+         }
+    }
+    
+    return NULL;
+}
+
+static const char *mod_gridsite_flag_cmds(cmd_parms *a, void *cfg,
+                                      int flag)
+{
+    if      (strcasecmp(a->cmd->name, "GridSiteAuth") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->auth = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteAutoPasscode") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->autopasscode = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteRequirePasscode") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->requirepasscode = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteEnvs") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->envs = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteHtmlFormat") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->format = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteIndexes") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->indexes = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteLink") == 0)
+    {
+      ((mod_gridsite_dir_cfg *) cfg)->gridsitelink = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteGridHTTP") == 0)
+    {
+// TODO: return error if try this on non-HTTPS virtual server
+
+      ((mod_gridsite_dir_cfg *) cfg)->gridhttp = flag;
+    }
+
+    return NULL;
+}
+
+static const command_rec mod_gridsite_cmds[] =
+{
+// TODO: need to check and document valid contexts for each command!
+
+    AP_INIT_FLAG("GridSiteAuth", mod_gridsite_flag_cmds, 
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_FLAG("GridSiteAutoPasscode", mod_gridsite_flag_cmds,
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_FLAG("GridSiteRequirePasscode", mod_gridsite_flag_cmds,
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_FLAG("GridSiteEnvs", mod_gridsite_flag_cmds, 
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_FLAG("GridSiteHtmlFormat", mod_gridsite_flag_cmds, 
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_FLAG("GridSiteIndexes", mod_gridsite_flag_cmds, 
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_FLAG("GridSiteLink", mod_gridsite_flag_cmds, 
+                 NULL, OR_FILEINFO, "on or off"),
+                 
+    AP_INIT_TAKE1("GridSiteAdminFile", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "Ghost per-directory admin CGI"),
+    AP_INIT_TAKE1("GridSiteAdminURI", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "URI of real gridsite-admin.cgi"),
+    AP_INIT_TAKE1("GridSiteHelpURI", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "URI of Website Help pages"),
+    AP_INIT_TAKE1("GridSiteLoginURI", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "URI prefix of login/logout page"),
+    AP_INIT_TAKE1("GridSiteDNlists", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "DN Lists directories search path"),
+    AP_INIT_TAKE1("GridSiteDNlistsURI", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "URI of published DN lists"),
+    AP_INIT_TAKE1("GridSiteAdminList", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "URI of admin DN List"),
+    AP_INIT_TAKE1("GridSiteGSIProxyLimit", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "Max level of GSI proxy validity"),
+    AP_INIT_TAKE1("GridSiteUnzip", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "Absolute path to unzip command"),
+
+    AP_INIT_RAW_ARGS("GridSiteMethods", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "permitted HTTP methods"),
+    AP_INIT_RAW_ARGS("GridSiteOCSP", mod_gridsite_take1_cmds,
+                 NULL, RSRC_CONF, "Set OCSP lookups"),
+    AP_INIT_RAW_ARGS("GridSiteEditable", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "editable file extensions"),
+    AP_INIT_TAKE1("GridSiteHeadFile", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "filename of HTML header"),
+    AP_INIT_TAKE1("GridSiteFootFile", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "filename of HTML footer"),
+    AP_INIT_TAKE1("GridSiteIndexHeader", mod_gridsite_take1_cmds,
+                   NULL, OR_FILEINFO, "filename of directory header"),
+    
+    AP_INIT_FLAG("GridSiteGridHTTP", mod_gridsite_flag_cmds,
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_TAKE1("GridSiteGridHTTPport", mod_gridsite_take1_cmds,
+                   NULL, RSRC_CONF, "GridHTTP port"),
+    AP_INIT_TAKE1("GridSiteSessionsDir", mod_gridsite_take1_cmds,
+                 NULL, RSRC_CONF, "directory with GridHTTP passcodes and SSL session creds"),
+/* GridSiteOnetimesDir is deprecated in favour of GridSiteSessionsDir */
+    AP_INIT_TAKE1("GridSiteOnetimesDir", mod_gridsite_take1_cmds,
+                 NULL, RSRC_CONF, "directory with GridHTTP passcodes"),
+    AP_INIT_TAKE1("GridSiteZoneSlashes", mod_gridsite_take1_cmds,
+                 NULL, OR_FILEINFO, "number of slashes in passcode cookie paths"),
+
+    AP_INIT_TAKE1("GridSiteCastDNlists", mod_gridsite_take1_cmds,
+                 NULL, RSRC_CONF, "DN Lists directories search path for SiteCast"),
+    AP_INIT_TAKE1("GridSiteCastUniPort", mod_gridsite_take1_cmds,
+                 NULL, RSRC_CONF, "UDP port for unicast/replies"),
+    AP_INIT_TAKE1("GridSiteCastGroup", mod_gridsite_take1_cmds,
+                 NULL, RSRC_CONF, "multicast group[:port] to listen for HTCP on"),
+    AP_INIT_TAKE2("GridSiteCastAlias", mod_gridsite_take2_cmds,
+                 NULL, RSRC_CONF, "URL and local path mapping"),
+
+    AP_INIT_TAKE1("GridSiteACLFormat", mod_gridsite_take1_cmds,
+                 NULL, OR_FILEINFO, "format to save access control lists in"),
+    AP_INIT_TAKE1("GridSiteACLPath", mod_gridsite_take1_cmds,
+                 NULL, OR_FILEINFO, "explicit location of access control file"),
+
+    AP_INIT_TAKE1("GridSiteDelegationURI", mod_gridsite_take1_cmds,
+                 NULL, OR_FILEINFO, "URI of the delegation service CGI"),
+
+    AP_INIT_TAKE1("GridSiteExecMethod", mod_gridsite_take1_cmds,
+                 NULL, OR_FILEINFO, "execution strategy used by gsexec"),
+                 
+    AP_INIT_TAKE2("GridSiteUserGroup", mod_gridsite_take2_cmds, 
+                  NULL, OR_FILEINFO,
+                  "user and group of gsexec processes in suexec mode"),
+          
+    AP_INIT_TAKE2("GridSiteDiskMode", mod_gridsite_take2_cmds, 
+                  NULL, OR_FILEINFO,
+                  "group and world file modes for new files/directories"),
+          
+    {NULL}
+};
+
+/*  Blank unset these HTTP headers, to prevent injection attacks.
+    This is run before mod_shib's check_user_id hook, which may
+    legitimately create such headers.                           */
+
+static int mod_gridsite_check_user_id(request_rec *r)
+{
+    apr_table_unset(r->headers_in, "User-Distinguished-Name");
+#if 0
+    apr_table_unset(r->headers_in, "User-Distinguished-Name-2");
+#endif
+    apr_table_unset(r->headers_in, "Nist-LoA");
+    apr_table_unset(r->headers_in, "LoA");
+    apr_table_unset(r->headers_in, "VOMS-Attribute");
+
+    return DECLINED; /* ie carry on processing request */
+}
+
+static int mod_gridsite_first_fixups(request_rec *r)
+{
+    mod_gridsite_dir_cfg *conf;
+
+    if (r->finfo.filetype != APR_DIR) return DECLINED;
+
+    conf = (mod_gridsite_dir_cfg *)
+                    ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+    /* we handle DN Lists as regular files, even if they also match
+       directory names  */
+
+    if ((conf != NULL) &&
+        (conf->dnlistsuri != NULL) &&
+        (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0) &&
+        (strcmp(r->uri, conf->dnlistsuri) != 0))
+      {
+        r->finfo.filetype = APR_REG; 
+      }
+
+    return DECLINED;
+}  
+
+
+int GRST_get_session_id(SSL *ssl, char *session_id, size_t len)
+{
+   int          i;
+   SSL_SESSION *session;
+
+   if (((session = SSL_get_session(ssl)) == NULL) ||
+       (session->session_id_length == 0)) return GRST_RET_FAILED;
+   
+   if (2 * session->session_id_length + 1 > len) return GRST_RET_FAILED;
+
+   for (i=0; i < (int) session->session_id_length; ++i)
+    sprintf(&(session_id[i*2]), "%02X", (unsigned char) session->session_id[i]);
+
+   session_id[i*2] = '\0';
+   
+   return GRST_RET_OK;
+}
+
+int GRST_load_ssl_creds(SSL *ssl, conn_rec *conn)
+{
+   char session_id[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2+1], *sessionfile = NULL,
+        line[512], *p;
+   apr_file_t  *fp = NULL;
+   int i;
+      
+   if (GRST_get_session_id(ssl, session_id, sizeof(session_id)) != GRST_RET_OK)
+     return GRST_RET_FAILED;
+   
+   sessionfile = apr_psprintf(conn->pool, "%s/sslcreds-%s",
+                         ap_server_root_relative(conn->pool, sessionsdir),
+                         session_id);
+
+   if (apr_file_open(&fp, sessionfile, APR_READ, 0, conn->pool) != APR_SUCCESS)
+       return GRST_RET_FAILED;
+   
+   while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS)
+        {
+          if (sscanf(line, "GRST_CRED_AURI_%d=", &i) == 1)
+            {
+              if ((p = index(line, '\n')) != NULL) *p = '\0';              
+              p = index(line, '=');
+
+              apr_table_setn(conn->notes,
+                         apr_psprintf(conn->pool, "GRST_CRED_AURI_%d", i),
+                         apr_pstrdup(conn->pool, &p[1]));
+            }
+          else if (sscanf(line, "GRST_CRED_VALID_%d=", &i) == 1)
+            {
+              if ((p = index(line, '\n')) != NULL) *p = '\0';              
+              p = index(line, '=');
+
+              apr_table_setn(conn->notes,
+                         apr_psprintf(conn->pool, "GRST_CRED_VALID_%d", i),
+                         apr_pstrdup(conn->pool, &p[1]));
+            }
+          else if (sscanf(line, "GRST_OCSP_URL_%d=", &i) == 1)
+            {
+              if ((p = index(line, '\n')) != NULL) *p = '\0';              
+              p = index(line, '=');
+
+              apr_table_setn(conn->notes,
+                         apr_psprintf(conn->pool, "GRST_OCSP_URL_%d", i),
+                         apr_pstrdup(conn->pool, &p[1]));
+            }
+        }
+        
+   apr_file_close(fp);
+
+   /* connection notes created by GRST_save_ssl_creds() are now reloaded */
+   apr_table_set(conn->notes, "GRST_save_ssl_creds", "yes");
+
+   return GRST_RET_OK;
+}
+
+/*
+    Save result of AURIs and validity info from chain into connection notes,
+    and write out in an SSL session creds file.
+*/
+
+void GRST_save_ssl_creds(conn_rec *conn, GRSTx509Chain *grst_chain)
+{
+   int          i, lowest_voms_delegation = 65535;
+   char        *tempfile = NULL, *encoded,
+               *sessionfile, session_id[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2];
+   apr_file_t  *fp = NULL;
+   SSL         *ssl;
+   SSLConnRec  *sslconn;
+   GRSTx509Cert  *grst_cert = NULL;
+
+   /* check if already done */
+
+   if ((grst_chain != NULL) && (conn->notes != NULL) &&
+       (apr_table_get(conn->notes, "GRST_save_ssl_creds") != NULL)) return;
+
+   /* we at least need to say we've been run - even if creds not save-able*/
+
+   apr_table_set(conn->notes, "GRST_save_ssl_creds", "yes");
+   ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server,
+                                            "set GRST_save_ssl_creds");
+
+   sslconn = (SSLConnRec *)ap_get_module_config(conn->conn_config,&ssl_module);
+
+   if ((sslconn != NULL) && 
+       ((ssl = sslconn->ssl) != NULL) &&
+       (GRST_get_session_id(ssl,session_id,sizeof(session_id)) == GRST_RET_OK))
+     {
+       sessionfile = apr_psprintf(conn->pool, "%s/sslcreds-%s",
+                         ap_server_root_relative(conn->pool, sessionsdir),
+                         session_id);
+
+       tempfile = apr_pstrcat(conn->pool, 
+                          ap_server_root_relative(conn->pool, sessionsdir), 
+                          "/tmp-XXXXXX", NULL);
+   
+       if ((tempfile != NULL) && (tempfile[0] != '\0'))
+               apr_file_mktemp(&fp, tempfile, 
+                               APR_CREATE | APR_WRITE | APR_EXCL, conn->pool);
+     }
+
+   i=0;
+   
+   for (grst_cert = grst_chain->firstcert;
+        grst_cert != NULL; grst_cert = grst_cert->next)
+      {
+        if (grst_cert->errors) continue;
+        
+        if (grst_cert->type == GRST_CERT_TYPE_VOMS)
+          {
+            /* want to record the delegation level 
+               of the last proxy with VOMS attributes */
+          
+            lowest_voms_delegation = grst_cert->delegation;
+          }
+        else if ((grst_cert->type == GRST_CERT_TYPE_EEC) ||
+                 (grst_cert->type == GRST_CERT_TYPE_PROXY))
+          {
+            encoded = GRSThttpUrlMildencode(grst_cert->dn);
+          
+            apr_table_setn(conn->notes,
+                   apr_psprintf(conn->pool, "GRST_CRED_AURI_%d", i),
+                   apr_pstrcat(conn->pool, "dn:", encoded, NULL));
+
+            if (fp != NULL) apr_file_printf(fp, "GRST_CRED_AURI_%d=dn:%s\n",
+                                                i, encoded);
+
+            apr_table_setn(conn->notes,
+                   apr_psprintf(conn->pool, "GRST_CRED_VALID_%d", i),
+                   apr_psprintf(conn->pool, 
+                      "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", 
+                      grst_cert->notbefore,
+                      grst_cert->notafter,
+                      grst_cert->delegation, 0));
+
+            if (fp != NULL) apr_file_printf(fp, 
+  "GRST_CRED_VALID_%d=notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",
+                                            i, grst_cert->notbefore,
+                                               grst_cert->notafter, 
+                                               grst_cert->delegation, 0);
+
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server,
+                      "store GRST_CRED_AURI_%d=dn:%s", i, encoded);
+
+            free(encoded);
+
+            ++i;
+          }
+      }
+
+   for (grst_cert = grst_chain->firstcert; 
+        grst_cert != NULL; grst_cert = grst_cert->next)
+      {
+        if (grst_cert->errors) continue;
+        
+        if ((grst_cert->type == GRST_CERT_TYPE_VOMS) &&
+            (grst_cert->delegation == lowest_voms_delegation))
+          {
+            /* only export attributes from the last proxy to contain them */
+
+            encoded = GRSThttpUrlMildencode(grst_cert->value);
+          
+            apr_table_setn(conn->notes,
+                   apr_psprintf(conn->pool, "GRST_CRED_AURI_%d", i),
+                   apr_pstrcat(conn->pool, "fqan:", encoded, NULL));
+
+            if (fp != NULL) apr_file_printf(fp, "GRST_CRED_AURI_%d=fqan:%s\n",
+                                                i, encoded);
+
+            apr_table_setn(conn->notes,
+                   apr_psprintf(conn->pool, "GRST_CRED_VALID_%d", i),
+                   apr_psprintf(conn->pool, 
+                      "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", 
+                      grst_cert->notbefore,
+                      grst_cert->notafter, 
+                      grst_cert->delegation, 0));
+
+            if (fp != NULL) apr_file_printf(fp, 
+  "GRST_CRED_VALID_%d=notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",
+                                            i, grst_cert->notbefore,
+                                               grst_cert->notafter,
+                                               grst_cert->delegation, 0);
+
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server,
+                      "store GRST_CRED_AURI_%d=fqan:%s", i, encoded);
+
+            free(encoded);
+
+            ++i;
+          }
+      }
+
+   /* this needs to be merged into grst_x509? */
+#if 0
+   if (ocspmodes != NULL)
+   {
+     int   j;
+     const char *ex_sn;
+     char s[80];
+     X509 *cert;     
+     X509_EXTENSION *ex;
+     
+     for (j=sk_X509_num(certstack)-1; j >= 0; --j)
+        {
+          cert = sk_X509_value(certstack, j);
+          
+          for (i=0; i < X509_get_ext_count(cert); ++i)
+             {
+               ex = X509_get_ext(cert, i);
+
+               OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 0);
+
+               if (strcmp(s, "authorityInfoAccess") == 0) /* OCSP */
+                 {
+                   apr_table_setn(conn->notes, "GRST_OCSP_URL",
+                                  (const char *) X509_EXTENSION_get_data(ex));
+
+                   /* strategy is to remove what has been checked, 
+                      for this connnection */
+                   apr_table_set(conn->notes, "GRST_OCSP_UNCHECKED",
+                                 ocspmodes);
+
+                   ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server,
+                "store GRST_OCSP_URL_%d=%s", i, X509_EXTENSION_get_data(ex));
+
+                   if (fp != NULL) apr_file_printf(fp, "GRST_OCSP_URL_%d=%s\n",
+                                             i, X509_EXTENSION_get_data(ex));
+                 }
+             }
+        }   
+   }
+#endif
+   /* end of bit that needs to go into grst_x509 */
+     
+   if (fp != NULL)
+     {
+       apr_file_close(fp);
+       apr_file_rename(tempfile, sessionfile, conn->pool);
+     }
+}
+
+static char *get_aclpath_component(request_rec *r, int n)
+/*
+    Get the nth component of REQUEST_URI, or component 0
+    which is the server name.
+
+*/
+{
+    int ii, i, nn;
+
+    if (n == 0) return r->server->server_hostname;
+
+    if (r->uri == NULL) return NULL; /* some kind of internal error? */
+
+    i  = 1; /* start of first component */
+    nn = 1;
+    
+    for (ii=1; r->uri[ii] != '\0'; ++ii) /* look for this component */
+       {
+         if (r->uri[ii] == '/') /* end of a component */
+           {
+             if (nn == n) break;
+             
+             ++nn;
+             i = ii + 1;
+           }
+         else if ((r->uri[ii] == '.') && (r->uri[ii+1] == '.'))
+           {
+             return NULL; /* can this happen? dont allow anyway */
+           }         
+       }
+       
+    if (nn != n) return NULL; /* no component for this number */
+    
+    return apr_psprintf(r->pool, "%.*s", ii - i, &(r->uri[i]));
+}
+
+static char *make_aclpath(request_rec *r, char *format)
+{
+    int i, n;
+    char *formatted, *p;
+    
+    formatted = apr_pstrdup(r->pool, format);
+
+    while (1)
+         {
+           for (i=0; (formatted[i] != '\0') && (formatted[i] != '%'); ++i) ;
+    
+           if (formatted[i] == '\0') break;
+           
+           if ((formatted[i] == '%') && (formatted[i+1] == '%')) 
+             {
+               ++i;
+               continue;
+             }
+            
+           if (sscanf(&formatted[i+1], "%d", &n) != 1)
+             {
+               return NULL; /* not %% or %0,%1,... */
+             }
+           
+           formatted[i] = '\0';
+           
+           for (i++; isdigit(formatted[i]); ++i) ;
+           
+           if ((p = get_aclpath_component(r, n)) == NULL) return NULL;
+           
+           formatted = apr_pstrcat(r->pool, formatted, p, &formatted[i],NULL);                                   
+           i += strlen(p);
+         }
+            
+    return ap_server_root_relative(r->pool, formatted);
+}
+
+static int mod_gridsite_perm_handler(request_rec *r)
+/*
+    Do authentication/authorization here rather than in the normal module
+    auth functions since the results of mod_ssl are available.
+
+    We also publish environment variables here if requested by GridSiteEnv.
+*/
+{
+    int          retcode = DECLINED, i, j, n, file_is_acl = 0, cc_delegation,
+                 destination_is_acl = 0, ishttps = 0, nist_loa, delegation,
+                 from_cookie = 0;
+    char        *p, *q, envname1[30], envname2[30], 
+                *grst_cred_auri_0 = NULL, *dir_path,
+                *remotehost, *grst_cred_auri_i, *cookies, *file,
+                *cookiefile, oneline[1025], *decoded,
+                *destination = NULL, *destination_uri = NULL, *querytmp, 
+                *destination_prefix = NULL, *destination_translated = NULL,
+                *aclpath = NULL, *grst_cred_valid_0 = NULL, *grst_cred_valid_i,
+                *gridauthpasscode = NULL;
+    const char  *content_type;
+    time_t      notbefore, notafter;
+    apr_table_t *env;
+    apr_finfo_t  cookiefile_info;
+    apr_file_t  *fp;
+    request_rec *destreq;
+    GRSTgaclCred    *cred = NULL, *cred_0 = NULL;
+    GRSTgaclUser    *user = NULL;
+    GRSTgaclPerm     perm = GRST_PERM_NONE, destination_perm = GRST_PERM_NONE;
+    GRSTgaclAcl     *acl = NULL;
+    mod_gridsite_dir_cfg *cfg;
+    SSLConnRec      *sslconn;
+
+    cfg = (mod_gridsite_dir_cfg *)
+                    ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+    if (cfg == NULL) return DECLINED;
+
+    if ((cfg->auth == 0) && (cfg->envs == 0))
+               return DECLINED; /* if not turned on, look invisible */
+
+    env = r->subprocess_env;
+
+    p = (char *) apr_table_get(env, "HTTPS");
+    if ((p != NULL) && (strcmp(p, "on") == 0)) ishttps = 1;
+
+    delegation = ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit + 1;
+
+    /* reload per-connection (SSL) cred variables? (TO CONNECTION) */
+
+    sslconn = (SSLConnRec *) ap_get_module_config(r->connection->conn_config, 
+                                                  &ssl_module);
+    if ((user == NULL) &&
+        (sslconn != NULL) && 
+        (sslconn->ssl != NULL) &&
+        (sslconn->ssl->session != NULL) &&
+        (r->connection->notes != NULL) &&
+        (apr_table_get(r->connection->notes, "GRST_save_ssl_creds") == NULL))
+      {
+        if (GRST_load_ssl_creds(sslconn->ssl, r->connection) == GRST_RET_OK)
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "Restored SSL session data from session cache file");
+      }
+
+    /* look for GRIDHTTP_PASSCODE in QUERY_STRING ie after ? */
+      
+    if ((r->parsed_uri.query != NULL) && (r->parsed_uri.query[0] != '\0'))
+      {
+        querytmp = apr_pstrcat(r->pool,"&",r->parsed_uri.query,"&",NULL);
+            
+        gridauthpasscode = strstr(querytmp, "&GRIDHTTP_PASSCODE=");
+        if (gridauthpasscode != NULL)
+          {
+            gridauthpasscode = &gridauthpasscode[19];
+
+            for (p = gridauthpasscode; (*p != '\0') && (*p != '&'); ++p)
+                                                if (!isalnum(*p)) *p = '\0';
+          }
+      }
+
+    /* then look for GRIDHTTP_PASSCODE cookie */
+      
+    if ((gridauthpasscode == NULL) &&
+        ((q = (char *) apr_table_get(r->headers_in, "Cookie")) != NULL))
+      {
+        cookies = apr_pstrcat(r->pool, " ", q, NULL);
+        gridauthpasscode = strstr(cookies, " GRIDHTTP_PASSCODE=");
+
+        if (gridauthpasscode != NULL)
+          {
+            gridauthpasscode = &gridauthpasscode[19];
+          
+            for (p = gridauthpasscode; 
+                 (*p != '\0') && (*p != ';'); ++p)
+                                      if (!isalnum(*p)) *p = '\0';
+
+            if (gridauthpasscode[0] != '\0') from_cookie = 1;
+          }
+      }
+
+    /* try to load user structure from passcode file */
+
+    if ((user == NULL) && 
+        (gridauthpasscode != NULL) &&
+        (gridauthpasscode[0] != '\0'))
+      {
+        cookiefile = apr_psprintf(r->pool, "%s/passcode-%s",
+                 ap_server_root_relative(r->pool,
+                 sessionsdir),
+                 gridauthpasscode);
+                                      
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "Opening GridHTTP passcode file %s", cookiefile);
+              
+        if ((apr_stat(&cookiefile_info, cookiefile, 
+                          APR_FINFO_TYPE, r->pool) == APR_SUCCESS) &&
+            (cookiefile_info.filetype == APR_REG) &&
+            (apr_file_open(&fp, cookiefile, APR_READ, 0, r->pool)
+                                                         == APR_SUCCESS))
+              {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "Reading GridHTTP passcode file %s", cookiefile);
+               
+                i = -1;
+                cred = NULL;
+              
+                while (apr_file_gets(oneline, 
+                                     sizeof(oneline), fp) == APR_SUCCESS)
+                     {
+                       p = index(oneline, '\n');
+                       if (p != NULL) *p = '\0';
+                       
+                       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                                    "%s: %s", cookiefile, oneline);
+
+                       if ((strncmp(oneline, "expires=", 8) == 0) &&
+                           (apr_time_from_sec(atoll(&oneline[8])) < 
+                                                       apr_time_now()))
+                         {
+                           if (user != NULL) 
+                             {
+                               ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 
+                                            r->server, "Bad expires");
+                               GRSTgaclUserFree(user);
+                               user = NULL;
+                             }
+                           break;
+                         }
+                       else if ((strncmp(oneline, "domain=", 7) == 0) &&
+                                (strcmp(&oneline[7], r->hostname) != 0))
+                         {
+                           if (user != NULL) 
+                             {
+                               ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 
+                                            r->server, "Bad domain/host");
+                               GRSTgaclUserFree(user);
+                               user = NULL;
+                             }
+                           break;
+                         }
+                       else if (strncmp(oneline, "path=", 5) == 0)
+                         {
+                           /* count number of slashes in Request URI */
+                           
+                           for (n=0,p=r->uri; *p != '\0'; ++p)
+                                                     if (*p == '/') ++n;
+
+                           /* if too few slashes or path mismatch, then stop */
+                              
+                           if ((n < ((mod_gridsite_dir_cfg *) cfg)->zoneslashes) ||
+                               (strncmp(&oneline[5], r->uri, strlen(&oneline[5])) != 0))
+                             {
+                               if (user != NULL)
+                                 {
+                                   ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 
+                                            r->server, "Bad path");
+                                   GRSTgaclUserFree(user);
+                                   user = NULL;
+                                 }
+
+                               break;
+                             }
+                         }
+                       else if ((sscanf(oneline,"GRST_CRED_AURI_%d=",&j) == 1)
+                                && (j == i+1)
+                                && ((p = index(oneline, '=')) != NULL))
+                         {
+                           cred = GRSTgaclCredCreate(&p[1], NULL);
+                           
+                           if (cred != NULL) ++i;
+                           
+                           if (user == NULL) user = GRSTgaclUserNew(cred);
+                           else GRSTgaclUserAddCred(user, cred);                           
+                         }
+                       else if ((sscanf(oneline,"GRST_CRED_VALID_%d=",&j) == 1)
+                                && (j == i)
+                                && ((p = index(oneline, '=')) != NULL)
+                                && (sscanf(&p[1], 
+                       "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", 
+                                  &notbefore, &notafter, &delegation, 
+                                  &nist_loa) == 4))
+                         {
+                           if ((i == 0) && 
+             (delegation > ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit))
+                             {
+                               ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 
+                                            r->server, "Bad delegation");
+                               if (user != NULL) GRSTgaclUserFree(user);
+                               user = NULL;
+                               break;
+                             }
+                         
+                           GRSTgaclCredSetNotBefore( cred, notbefore);
+                           GRSTgaclCredSetNotAfter(  cred, notafter);
+                           GRSTgaclCredSetDelegation(cred, delegation);
+                           if (delegation == 0) GRSTgaclCredSetNistLoa(cred, 3);
+                           else                 GRSTgaclCredSetNistLoa(cred, 2);
+                         }
+                     }
+
+                apr_file_close(fp);
+
+                /* delete passcode file if used over HTTP not HTTPS */
+                if (!ishttps) remove(cookiefile);
+
+                /* if successful and we got passcode from a cookie, then
+                   we put cookie value into environment variables, so
+                   can be used for double-submit cookie CSRF protection */
+
+                if ((user != NULL) && 
+                    from_cookie && 
+                    ((mod_gridsite_dir_cfg *) cfg)->envs)
+                        apr_table_setn(env, "GRST_PASSCODE_COOKIE",
+                                            gridauthpasscode);
+              }
+      }
+
+    /* 
+        if not succeeded from passcode file, try from connection notes
+        if a GSI Proxy or have  GridSiteAutoPasscode on  (the default)
+        If  GridSiteAutoPasscode off  and  GridSiteRequirePasscode on
+        then interactive websites must use a login script to make passcode
+        and file instead.
+    */
+    
+    if ((user == NULL) && 
+        (r->connection->notes != NULL) &&
+        ((grst_cred_auri_0 = (char *) 
+         apr_table_get(r->connection->notes, "GRST_CRED_AURI_0")) != NULL) &&
+        (strncmp(grst_cred_auri_0, "dn:", 3) == 0) &&
+        ((grst_cred_valid_0 = (char *) 
+         apr_table_get(r->connection->notes, "GRST_CRED_VALID_0")) != NULL) &&
+        (sscanf(grst_cred_valid_0, 
+                "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", 
+                &notbefore, &notafter, &delegation, &nist_loa) == 4) &&
+        (delegation <= ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit) &&
+        ((delegation > 0) || 
+         ((mod_gridsite_dir_cfg *) cfg)->autopasscode ||
+         !(((mod_gridsite_dir_cfg *) cfg)->requirepasscode)))
+      {
+        cred_0 = GRSTgaclCredCreate(grst_cred_auri_0, NULL);
+        if (cred_0 != NULL)
+          {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "Using identity %s from SSL/TLS", grst_cred_auri_0);
+
+            GRSTgaclCredSetNotBefore( cred_0, notbefore);
+            GRSTgaclCredSetNotAfter(  cred_0, notafter);
+            GRSTgaclCredSetDelegation(cred_0, delegation);
+
+            if (delegation == 0) GRSTgaclCredSetNistLoa(cred_0, 3);
+            else                 GRSTgaclCredSetNistLoa(cred_0, 2);
+
+            user = GRSTgaclUserNew(cred_0);
+
+            /* check for VOMS etc in GRST_CRED_AURI_i too */
+  
+            for (i=1; ; ++i)
+               {
+                 snprintf(envname1, sizeof(envname1), "GRST_CRED_AURI_%d", i);
+                 snprintf(envname2, sizeof(envname2), "GRST_CRED_VALID_%d", i);
+
+                 if ((grst_cred_auri_i = (char *) 
+                         apr_table_get(r->connection->notes,envname1)) &&
+                     (grst_cred_valid_i = (char *) 
+                         apr_table_get(r->connection->notes,envname2)))
+                   { 
+                     cred = GRSTgaclCredCreate(grst_cred_auri_i, NULL);
+                     if (cred != NULL) 
+                       {
+                         notbefore  = 0;
+                         notafter   = 0;
+                         delegation = 0;
+                         nist_loa   = 0;
+                       
+                         sscanf(grst_cred_valid_i, 
+                       "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", 
+                                &notbefore, &notafter, &delegation, &nist_loa);
+                        
+                         GRSTgaclCredSetNotBefore( cred, notbefore);
+                         GRSTgaclCredSetNotAfter(  cred, notafter);
+                         GRSTgaclCredSetDelegation(cred, delegation);
+                         GRSTgaclCredSetDelegation(cred, nist_loa);
+
+                         GRSTgaclUserAddCred(user, cred);
+                       }
+                   }
+                 else break; /* GRST_CRED_AURI_i are numbered consecutively */
+               }
+          }
+
+         /* if user from SSL ok and not a GSI Proxy and have 
+            GridSiteAutoPasscode on  we create passcode and file
+            automatically, and return cookie to client. 
+            (if  GridSiteAutoPasscode off  then the site must use
+            a login script to make passcode and file instead.) */
+
+         if (((mod_gridsite_dir_cfg *) cfg)->autopasscode &&
+             (user != NULL) &&
+             (GRSTgaclCredGetDelegation(cred_0) == 0))
+           {
+             n = 0; /* number of slashes seen */
+
+             for (i=0; r->uri[i] != '\0'; ++i)
+                {
+                  if (n >= ((mod_gridsite_dir_cfg *) cfg)->zoneslashes) break;
+
+                  if (r->uri[i] == '/') ++n;
+                }
+             if ((n >= ((mod_gridsite_dir_cfg *) cfg)->zoneslashes)
+                 && (i > 0))
+               {
+                 p = apr_pstrdup(r->pool, r->uri);
+                 p[i] = '\0';
+               
+                 /* try to generate passcode and make passcode file */
+                 gridauthpasscode = make_passcode_file(r, cfg, p, 0);
+
+                 if (gridauthpasscode != NULL)
+                   {
+                     apr_table_add(r->headers_out,
+                        apr_pstrdup(r->pool, "Set-Cookie"),
+                        apr_psprintf(r->pool,
+                        "GRIDHTTP_PASSCODE=%s; "
+                        "domain=%s; "
+                        "path=%s; "
+                        "secure", gridauthpasscode, r->hostname, p));
+                   }
+               }
+           }
+      }
+
+    /* 
+       GridSite passcode files don't include groups, IP or DNS so we add
+       them last so they're not written to passcode files by GridSite.
+
+       (site-supplied login scripts might create passcode files with 
+       optional or additional AURIs. for example, valid roles selected by
+       the user on the login page.)
+       
+    */
+      
+    /* first add groups from DN lists - ie non-optional attributes */
+
+    if ((user != NULL) && ((mod_gridsite_dir_cfg *) cfg)->dnlists)
+          GRSTgaclUserLoadDNlists(user, ((mod_gridsite_dir_cfg *) cfg)->dnlists);
+
+    /* then add DNS credential */
+    
+    remotehost = (char *) ap_get_remote_host(r->connection,
+                                  r->per_dir_config, REMOTE_DOUBLE_REV, NULL);
+    if ((remotehost != NULL) && (*remotehost != '\0'))
+      {
+        cred = GRSTgaclCredCreate("dns:", remotehost);
+        GRSTgaclCredSetNotAfter(cred, GRST_MAX_TIME_T);
+
+        if (user == NULL) user = GRSTgaclUserNew(cred);
+        else              GRSTgaclUserAddCred(user, cred);
+      }
+
+    /* finally add IP credential */
+    
+    if (r->connection->remote_ip)
+      {
+        cred = GRSTgaclCredCreate("ip:", r->connection->remote_ip);
+        GRSTgaclCredSetNotAfter(cred, GRST_MAX_TIME_T);
+
+        if (user == NULL) user = GRSTgaclUserNew(cred);
+        else              GRSTgaclUserAddCred(user, cred);
+      }
+
+    /* write contents of user to per-request environment variables */
+
+    if (((mod_gridsite_dir_cfg *) cfg)->envs && (user != NULL))
+      {    
+        cred = user->firstcred;
+        
+        /* old-style Compact Credentials have the same delegation level
+           for all credentials. eg Using EEC delegation=0; using 1st GSI
+           Proxy then delegation=1 for X509USER _and_ GSIPROXY credentials. 
+           So we remember the delegation level of any X509USER here */
+        if (cred != NULL) cc_delegation = cred->delegation;
+      
+        for (i=0; (cred != NULL) && (cred->auri != NULL); ++i)
+             {                                    
+               if (strncmp(cred->auri, "dn:", 3) == 0)
+                 {
+                   decoded = GRSThttpUrlDecode(&(cred->auri[3]));
+                   apr_table_setn(env, 
+                                  apr_psprintf(r->pool, "GRST_CRED_%d", i),
+                                  apr_psprintf(r->pool, 
+                                               "%s %ld %ld %d %s",
+                                               (i==0) ? "X509USER" : "GSIPROXY",
+                                               cred->notbefore,
+                                               cred->notafter,
+                                               cc_delegation, 
+                                               decoded));
+                   free(decoded);
+                 }
+               else if (strncmp(cred->auri, "fqan:", 5) == 0)
+                 {
+                   decoded = GRSThttpUrlDecode(&(cred->auri[5]));
+                   apr_table_setn(env, 
+                                  apr_psprintf(r->pool, "GRST_CRED_%d", i),
+                                  apr_psprintf(r->pool, 
+                                                  "VOMS %ld %ld 0 %s",
+                                                  notbefore, notafter, 
+                                                  decoded));
+                   free(decoded);
+                 }
+
+               apr_table_setn(env,
+                              apr_psprintf(r->pool, "GRST_CRED_AURI_%d", i),
+                              apr_pstrdup(r->pool, cred->auri));
+
+               apr_table_setn(env, 
+                              apr_psprintf(r->pool, "GRST_CRED_VALID_%d", i),
+                              apr_psprintf(r->pool,
+                       "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d",
+                                           cred->notbefore,
+                                           cred->notafter,
+                                           cred->delegation,
+                                           cred->nist_loa));
+                 
+               cred = cred->next;
+             }    
+      }
+
+    /* check for Destination: header and evaluate if present */
+
+    if ((destination = (char *) apr_table_get(r->headers_in,
+                                              "Destination")) != NULL)
+      {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "Destination header found, value=%s", destination);
+
+        destination_prefix = apr_psprintf(r->pool, "https://%s:%d/", 
+                         r->server->server_hostname, (int) r->server->port);
+
+        if (strncmp(destination_prefix, destination,
+                    strlen(destination_prefix)) == 0) 
+           destination_uri = &destination[strlen(destination_prefix)-1];
+        else if ((int) r->server->port == 443)
+          {
+            destination_prefix = apr_psprintf(r->pool, "https://%s/", 
+                                              r->server->server_hostname);
+
+            if (strncmp(destination_prefix, destination,
+                                strlen(destination_prefix)) == 0)
+              destination_uri = &destination[strlen(destination_prefix)-1];
+          }
+          
+        if (destination_uri != NULL)
+          {
+            destreq = ap_sub_req_method_uri("GET", destination_uri, r, NULL);
+
+            if ((destreq != NULL) && (destreq->filename != NULL) 
+                                  && (destreq->path_info != NULL))
+              {
+                destination_translated = apr_pstrcat(r->pool, 
+                               destreq->filename, destreq->path_info, NULL);
+
+                apr_table_setn(r->notes, "GRST_DESTINATION_TRANSLATED", 
+                               destination_translated);
+                             
+                if (((mod_gridsite_dir_cfg *) cfg)->envs)
+                        apr_table_setn(env, "GRST_DESTINATION_TRANSLATED", 
+                                                  destination_translated);
+                                                  
+                 p = rindex(destination_translated, '/');
+                 if ((p != NULL) && (strcmp(&p[1], GRST_ACL_FILE) == 0))
+                                                    destination_is_acl = 1;
+              }
+          }
+      }
+    
+    if ((((mod_gridsite_dir_cfg *) cfg)->adminlist != NULL) && (user != NULL))
+      {    
+        cred = user->firstcred;
+      
+        while ((cred != NULL) && (cred->auri != NULL))
+             {
+               if (strcmp(((mod_gridsite_dir_cfg *) cfg)->adminlist,
+                          cred->auri) == 0)
+                 {
+                   perm = GRST_PERM_ALL;
+                   if (destination_translated != NULL) 
+                          destination_perm = GRST_PERM_ALL;
+                   break;
+                 }
+                 
+               cred = cred->next;
+             }    
+      }
+    
+    if (perm != GRST_PERM_ALL) /* cannot improve on perfection... */
+      {
+        if (((mod_gridsite_dir_cfg *) cfg)->aclpath != NULL)
+          {
+            aclpath = make_aclpath(r,((mod_gridsite_dir_cfg *) cfg)->aclpath);
+          
+            if (aclpath != NULL) 
+              {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                        "Examine ACL file %s (from ACL path %s)",
+                        aclpath, ((mod_gridsite_dir_cfg *) cfg)->aclpath);
+
+                acl = GRSTgaclAclLoadFile(aclpath);
+              }
+            else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                        "Failed to make ACL file from ACL path %s, URI %s)",
+                        ((mod_gridsite_dir_cfg *) cfg)->aclpath, r->uri);
+          }
+        else if ((((mod_gridsite_dir_cfg *) cfg)->dnlistsuri == NULL) ||
+                 (strncmp(r->uri,
+                          ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri,
+                          strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)) != 0) ||
+                 (strlen(r->uri) <= strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)))
+          {
+            acl = GRSTgaclAclLoadforFile(r->filename);
+          }
+
+        if (acl != NULL) perm = GRSTgaclAclTestUser(acl, user);
+        GRSTgaclAclFree(acl);
+        
+        if (destination_translated != NULL)
+          {
+            acl = GRSTgaclAclLoadforFile(destination_translated);
+            if (acl != NULL) destination_perm = GRSTgaclAclTestUser(acl, user);
+            GRSTgaclAclFree(acl);
+
+            apr_table_setn(r->notes, "GRST_DESTINATION_PERM",
+                              apr_psprintf(r->pool, "%d", destination_perm));
+          
+            if (((mod_gridsite_dir_cfg *) cfg)->envs)
+              apr_table_setn(env, "GRST_DESTINATION_PERM",
+                              apr_psprintf(r->pool, "%d", destination_perm));
+          }
+      }
+    
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "After GACL/Onetime evaluation, GRST_PERM=%d", perm);
+
+    /* add permission and first AURI to request notes */
+    
+    apr_table_setn(r->notes, "GRST_PERM", apr_psprintf(r->pool, "%d", perm));
+
+    cred = NULL;
+    if (user)
+       cred = user->firstcred;
+    if ((cred != NULL) && (strncmp(cred->auri, "dn:", 3) == 0))
+      {
+        apr_table_setn(r->notes, "GRST_CRED_AURI_0",
+                       apr_psprintf(r->pool, "%s", cred->auri));
+      }
+        
+
+    if (((mod_gridsite_dir_cfg *) cfg)->envs)
+      {
+        /* copy any credentials from (SSL) connection to environment */
+        
+        for (i=0; ; ++i) 
+           {
+             snprintf(envname1, sizeof(envname1), "GRST_CRED_AURI_%d",  i);
+             snprintf(envname2, sizeof(envname2), "GRST_CRED_VALID_%d", i);
+
+             if ((grst_cred_auri_i = (char *) 
+                         apr_table_get(r->connection->notes,envname1)) &&
+                 (grst_cred_valid_i = (char *) 
+                         apr_table_get(r->connection->notes,envname2)))
+               { 
+                 apr_table_setn(env,
+                                apr_psprintf(r->pool, "GRST_CONN_AURI_%d", i),
+                                apr_pstrdup(r->pool, grst_cred_auri_i));
+
+                 apr_table_setn(env,
+                                apr_psprintf(r->pool, "GRST_CONN_VALID_%d", i),
+                                apr_pstrdup(r->pool, grst_cred_valid_i));
+               }
+             else break;
+           }
+
+        apr_table_setn(env, "GRST_PERM", apr_psprintf(r->pool, "%d", perm));
+
+        if (((mod_gridsite_dir_cfg *) cfg)->requirepasscode == 0)
+             apr_table_set(env, "GRST_REQUIRE_PASSCODE", "off");
+        else apr_table_set(env, "GRST_REQUIRE_PASSCODE", "on");
+
+        if (((dir_path = apr_pstrdup(r->pool, r->filename)) != NULL) &&
+            ((p = rindex(dir_path, '/')) != NULL))
+          {
+            *p = '\0';
+            apr_table_setn(env, "GRST_DIR_PATH", dir_path);
+          }
+
+        if (((mod_gridsite_dir_cfg *) cfg)->helpuri != NULL)
+                  apr_table_setn(env, "GRST_HELP_URI",
+                              ((mod_gridsite_dir_cfg *) cfg)->helpuri);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->loginuri != NULL)
+                  apr_table_setn(env, "GRST_LOGIN_URI",
+                              ((mod_gridsite_dir_cfg *) cfg)->loginuri);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->adminfile != NULL)
+                  apr_table_setn(env, "GRST_ADMIN_FILE",
+                              ((mod_gridsite_dir_cfg *) cfg)->adminfile);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->editable != NULL)
+                 apr_table_setn(env, "GRST_EDITABLE",
+                              ((mod_gridsite_dir_cfg *) cfg)->editable);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->headfile != NULL)
+                 apr_table_setn(env, "GRST_HEAD_FILE",
+                              ((mod_gridsite_dir_cfg *) cfg)->headfile);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->footfile != NULL)
+                 apr_table_setn(env, "GRST_FOOT_FILE",
+                              ((mod_gridsite_dir_cfg *) cfg)->footfile);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->dnlists != NULL)
+                 apr_table_setn(env, "GRST_DN_LISTS",
+                              ((mod_gridsite_dir_cfg *) cfg)->dnlists);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->dnlistsuri != NULL)
+                 apr_table_setn(env, "GRST_DN_LISTS_URI",
+                              ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->adminlist != NULL)
+                 apr_table_setn(env, "GRST_ADMIN_LIST",
+                              ((mod_gridsite_dir_cfg *) cfg)->adminlist);
+
+       apr_table_setn(env, "GRST_GSIPROXY_LIMIT",
+                            apr_psprintf(r->pool, "%d",
+                                  ((mod_gridsite_dir_cfg *)cfg)->gsiproxylimit));
+
+        if (((mod_gridsite_dir_cfg *) cfg)->unzip != NULL)
+                 apr_table_setn(env, "GRST_UNZIP",
+                              ((mod_gridsite_dir_cfg *) cfg)->unzip);
+
+        if (!(((mod_gridsite_dir_cfg *) cfg)->gridsitelink))
+                  apr_table_setn(env, "GRST_NO_LINK", "1");
+
+        if (((mod_gridsite_dir_cfg *) cfg)->aclformat != NULL)
+                 apr_table_setn(env, "GRST_ACL_FORMAT",
+                              ((mod_gridsite_dir_cfg *) cfg)->aclformat);
+
+        if (((mod_gridsite_dir_cfg *) cfg)->aclpath != NULL)
+                 apr_table_setn(env, "GRST_ACL_PATH",
+                              ((mod_gridsite_dir_cfg *) cfg)->aclpath);
+
+       if (((mod_gridsite_dir_cfg *) cfg)->delegationuri != NULL)
+                 apr_table_setn(env, "GRST_DELEGATION_URI",
+                              ((mod_gridsite_dir_cfg *) cfg)->delegationuri);
+
+
+        if (((mod_gridsite_dir_cfg *) cfg)->execmethod != NULL)
+          {
+           apr_table_setn(env, "GRST_EXEC_METHOD",
+                              ((mod_gridsite_dir_cfg *) cfg)->execmethod);
+                              
+            if ((strcasecmp(((mod_gridsite_dir_cfg *) cfg)->execmethod,  
+                           "directory") == 0) && (r->filename != NULL))
+              {
+                if ((r->content_type != NULL) && 
+                    (strcmp(r->content_type, DIR_MAGIC_TYPE) == 0))
+                  apr_table_setn(env, "GRST_EXEC_DIRECTORY", r->filename);
+                else
+                  {
+                    file = apr_pstrdup(r->pool, r->filename);
+                    p = rindex(file, '/');
+                    if (p != NULL)
+                      {
+                        *p = '\0';
+                        apr_table_setn(env, "GRST_EXEC_DIRECTORY", file);
+                      }                    
+                  }                 
+              }
+          }
+
+        apr_table_setn(env, "GRST_DISK_MODE",
+                            apr_psprintf(r->pool, "0x%04x",
+                             ((mod_gridsite_dir_cfg *)cfg)->diskmode));
+      }
+
+    if (((mod_gridsite_dir_cfg *) cfg)->auth)
+      {
+        /* *** Check HTTP method to decide which perm bits to check *** */
+
+        if ((r->filename != NULL) && 
+            ((p = rindex(r->filename, '/')) != NULL) &&
+            (strcmp(&p[1], GRST_ACL_FILE) == 0)) file_is_acl = 1;
+
+        content_type = r->content_type;
+        if ((content_type != NULL) &&
+            (strcmp(content_type, DIR_MAGIC_TYPE) == 0) &&
+            (((mod_gridsite_dir_cfg *) cfg)->dnlistsuri != NULL) &&
+            (strncmp(r->uri,
+                     ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri,
+                     strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)) == 0) &&
+            (strlen(r->uri) > strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)))
+            content_type = "text/html";
+
+        if ( GRSTgaclPermHasNone(perm) ||
+
+            /* first two M_GET conditions make the subtle distinction
+               between .../ that maps to .../index.html (governed by
+               Read perm) or to dir list (governed by List perm);
+               third M_GET condition deals with typeless CGI requests */
+
+            ((r->method_number == M_GET) &&
+             !GRSTgaclPermHasRead(perm)  &&
+             (content_type != NULL)   &&
+             (strcmp(content_type, DIR_MAGIC_TYPE) != 0)) ||
+
+            ((r->method_number == M_GET) &&
+             !GRSTgaclPermHasList(perm)  &&
+             (content_type != NULL)   &&
+             (strcmp(content_type, DIR_MAGIC_TYPE) == 0)) ||
+
+            ((r->method_number == M_GET) &&
+             !GRSTgaclPermHasRead(perm)  &&
+             (content_type == NULL))      ||
+
+            ((r->method_number == M_POST) && !GRSTgaclPermHasRead(perm) ) ||
+
+            (((r->method_number == M_PUT) || 
+              (r->method_number == M_DELETE)) &&
+             !GRSTgaclPermHasWrite(perm) && !file_is_acl) ||
+
+            ((r->method_number == M_MOVE) &&
+             ((!GRSTgaclPermHasWrite(perm) && !file_is_acl) || 
+              (!GRSTgaclPermHasAdmin(perm) && file_is_acl)  ||
+              (!GRSTgaclPermHasWrite(destination_perm) 
+                                    && !destination_is_acl) || 
+              (!GRSTgaclPermHasAdmin(destination_perm) 
+                                     && destination_is_acl)) ) ||
+
+            (((r->method_number == M_PUT) || 
+              (r->method_number == M_DELETE)) &&
+             !GRSTgaclPermHasAdmin(perm) && file_is_acl) ||
+
+            /* for WebDAV/Subversion */
+             
+            (((r->method_number == M_PROPFIND) ||
+              (r->method_number == M_REPORT)) &&
+             !GRSTgaclPermHasRead(perm)) ||
+
+            (((r->method_number == M_CHECKOUT) ||
+              (r->method_number == M_MERGE) ||
+              (r->method_number == M_MKACTIVITY) ||
+              (r->method_number == M_MKCOL) ||
+              (r->method_number == M_LOCK) ||
+              (r->method_number == M_UNLOCK)) &&
+             !GRSTgaclPermHasWrite(perm))
+             
+             ) retcode = HTTP_FORBIDDEN;
+      }
+
+    if (user != NULL) GRSTgaclUserFree(user);
+
+    return retcode;
+}
+
+int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx)
+{
+   SSL *ssl            = (SSL *) X509_STORE_CTX_get_app_data(ctx);
+   conn_rec *conn      = (conn_rec *) SSL_get_app_data(ssl);
+   int errnum          = X509_STORE_CTX_get_error(ctx);
+   int errdepth        = X509_STORE_CTX_get_error_depth(ctx);
+   int returned_ok;
+   STACK_OF(X509) *certstack;
+   GRSTx509Chain *grst_chain;
+
+   /* Call caNl callback directly */
+   returned_ok = canl_direct_pv_clb(NULL, ctx, ok);
+
+   /* in case ssl_callback_SSLVerify changed it */
+   errnum = X509_STORE_CTX_get_error(ctx); 
+
+   if ((errdepth == 0) && (errnum == X509_V_OK))
+       /*
+        * We've now got the last certificate - the identity being used for
+        * this connection. At this point we check the whole chain for valid
+        * CAs or, failing that, GSI-proxy validity using GRSTx509CheckChain.
+        */
+   {
+       certstack = (STACK_OF(X509) *) X509_STORE_CTX_get_chain(ctx);
+
+       errnum = GRSTx509ChainLoad(&grst_chain, certstack, NULL,
+               "/etc/grid-security/certificates",
+               "/etc/grid-security/vomsdir");
+
+       if (returned_ok)
+           /* Put result of GRSTx509ChainLoadCheck into connection notes */
+           GRST_save_ssl_creds(conn, grst_chain);
+       if (grst_chain)
+           GRSTx509ChainFree(grst_chain);
+   }
+
+   return returned_ok;
+}
+
+void sitecast_handle_NOP_request(server_rec *main_server, 
+                                 GRSThtcpMessage *htcp_mesg, int s,
+                                 struct sockaddr *client_addr_ptr,
+                                socklen_t client_addr_len)
+{
+  int  outbuf_len;
+  char *outbuf;
+  char host[INET6_ADDRSTRLEN];
+  char serv[8];
+  
+  if (GRSThtcpNOPresponseMake(&outbuf, &outbuf_len,
+                              htcp_mesg->trans_id) == GRST_RET_OK)
+    {
+      getnameinfo(client_addr_ptr, client_addr_len,
+                 host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST);
+      ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+            "SiteCast sends NOP response to %s:%s",
+            host, serv);
+
+      sendto(s, outbuf, outbuf_len, 0,
+             client_addr_ptr, client_addr_len);
+                 
+      free(outbuf);
+    }
+}
+
+void sitecast_handle_TST_GET(server_rec *main_server, 
+                             GRSThtcpMessage *htcp_mesg, int s,
+                             struct sockaddr *client_addr_ptr,
+                            socklen_t client_addr_len)
+{
+  int             outbuf_len, ialias;
+  char            *filename, *outbuf, *location;
+  struct stat     statbuf;
+  char host[INET6_ADDRSTRLEN];
+  char serv[8];
+  
+  getnameinfo(client_addr_ptr, client_addr_len,
+             host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST);
+  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+        "SiteCast responder received TST GET with uri %s", 
+        htcp_mesg->uri->text, GRSThtcpCountstrLen(htcp_mesg->uri));
+
+  /* find if any GridSiteCastAlias lines match */
+
+  for (ialias=0; ialias < GRST_SITECAST_ALIASES ; ++ialias)
+     {
+       if (sitecastaliases[ialias].sitecast_url == NULL) 
+         {
+           ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+              "SiteCast responder does not handle %*s requested by %s:%s",
+                        GRSThtcpCountstrLen(htcp_mesg->uri),
+                        htcp_mesg->uri->text,
+                       host, serv);
+      
+           return; /* no match */
+         }
+                             
+       if ((strlen(sitecastaliases[ialias].sitecast_url)
+                                <= GRSThtcpCountstrLen(htcp_mesg->uri)) &&
+           (strncmp(sitecastaliases[ialias].sitecast_url,
+                    htcp_mesg->uri->text,
+                    strlen(sitecastaliases[ialias].sitecast_url))==0)) break;
+     }
+
+  if (ialias == GRST_SITECAST_ALIASES) 
+    {
+      ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+              "SiteCast responder does not handle %*s requested by %s:%s",
+                        GRSThtcpCountstrLen(htcp_mesg->uri),
+                        htcp_mesg->uri->text,
+                        host, serv);
+      
+      return; /* no match */
+    }
+    
+  /* convert URL to filename, using alias mapping */
+
+  asprintf(&filename, "%s%*s", 
+           sitecastaliases[ialias].local_path,
+           GRSThtcpCountstrLen(htcp_mesg->uri) 
+                        - strlen(sitecastaliases[ialias].sitecast_url),
+           &(htcp_mesg->uri->text[strlen(sitecastaliases[ialias].sitecast_url)]) );
+
+  if (stat(filename, &statbuf) == 0) /* found file */
+    {
+      asprintf(&location, "Location: %s://%s:%d/%s\r\n",
+                  sitecastaliases[ialias].scheme,
+                  sitecastaliases[ialias].local_hostname,
+                  sitecastaliases[ialias].port,
+      &(htcp_mesg->uri->text[strlen(sitecastaliases[ialias].sitecast_url)]) );
+
+      ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+            "SiteCast finds %*s at %s, redirects with %s",
+            GRSThtcpCountstrLen(htcp_mesg->uri),
+            htcp_mesg->uri->text, filename, location);
+
+      if (GRSThtcpTSTresponseMake(&outbuf, &outbuf_len,
+                                  htcp_mesg->trans_id,
+                                  location, "", "") == GRST_RET_OK)
+        {
+          ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+            "SiteCast sends TST response to %s:%s",
+           host, serv);
+
+          sendto(s, outbuf, outbuf_len, 0,
+                 client_addr_ptr, client_addr_len);
+                 
+          free(outbuf);
+        }
+
+      free(location);
+    }
+  else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+            "SiteCast does not find %*s (would be at %s)",
+            GRSThtcpCountstrLen(htcp_mesg->uri),
+            htcp_mesg->uri->text, filename);
+
+
+  free(filename);                      
+}
+
+void sitecast_handle_request(server_rec *main_server, 
+                             char *reqbuf, int reqbuf_len,
+                            int s,
+                             struct sockaddr *client_addr_ptr,
+                            socklen_t client_addr_len)
+{
+  GRSThtcpMessage htcp_mesg;
+  char host[INET6_ADDRSTRLEN];
+  char serv[8];
+
+  getnameinfo(client_addr_ptr, client_addr_len,
+             host, sizeof(host),
+             serv, sizeof(serv), NI_NUMERICHOST);
+  if (GRSThtcpMessageParse(&htcp_mesg,reqbuf,reqbuf_len) != GRST_RET_OK)
+    {
+      ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+              "SiteCast responder rejects format of UDP message from %s:%s",
+                        host, serv);
+      return;
+    }
+
+  if (htcp_mesg.rr != 0) /* ignore HTCP responses: we just do requests */
+    {
+      ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+              "SiteCast responder ignores HTCP response from %s:%s",
+                        host, serv);
+      return;
+    }
+
+  if (htcp_mesg.opcode == GRSThtcpNOPop)
+    {
+      sitecast_handle_NOP_request(main_server, &htcp_mesg, s,
+                                  client_addr_ptr, client_addr_len);
+      return;
+    }
+
+  if (htcp_mesg.opcode == GRSThtcpTSTop)
+    {
+      if (((GRSThtcpCountstrLen(htcp_mesg.method) == 3) &&
+           (strncmp(htcp_mesg.method->text, "GET", 3) == 0)) ||
+          ((GRSThtcpCountstrLen(htcp_mesg.method) == 4) &&
+           (strncmp(htcp_mesg.method->text, "HEAD", 4) == 0)))
+        {
+          sitecast_handle_TST_GET(main_server, &htcp_mesg, s,
+                                  client_addr_ptr, client_addr_len);
+          return;
+        }
+        
+      ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+          "SiteCast responder rejects method %*s in TST message from %s:%s",
+          GRSThtcpCountstrLen(htcp_mesg.method), htcp_mesg.method->text,
+          host, serv);
+      return;
+    }
+
+  ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+          "SiteCast does not implement HTCP op-code %d in message from %s:%s",
+          htcp_mesg.opcode,
+          host, serv);
+}
+
+static int
+bind_sitecast_sockets(server_rec *main_server, const char *node,
+                     unsigned int port, int is_unicast)
+{
+    int s, open, ret;
+    struct addrinfo *ai;
+    struct addrinfo hints;
+    struct ipv6_mreq mreq6;
+    struct ip_mreq mreq;
+    char serv[8];
+    struct addrinfo *a;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+    if (!is_unicast)
+       hints.ai_flags |= AI_NUMERICHOST;
+
+    snprintf(serv, sizeof(serv), "%u", port);
+
+    ret = getaddrinfo(node, serv, &hints, &ai);
+    if (ret) {
+       ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+               "%s UDP Responder fails to look up %s",
+               (is_unicast) ? "Unicast" : "Multicast", node);
+       return -1;
+    }
+
+    open = 0;
+    for (a = ai; a != NULL; a = a->ai_next) {
+       s = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+       if (s < 0)
+           continue;
+
+       ret = bind(s, a->ai_addr, a->ai_addrlen);
+       if (ret < 0) {
+           close (s);
+           continue;
+       }
+
+       if (!is_unicast) {
+           switch (a->ai_family) {
+               case AF_INET:
+                   bzero(&mreq, sizeof(mreq));
+                   mreq.imr_multiaddr = ((struct sockaddr_in *)(a->ai_addr))->sin_addr;
+                   mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+                   ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                                    &mreq, sizeof(mreq));
+                   break;
+               case AF_INET6:
+                   mreq6.ipv6mr_multiaddr = 
+                           ((struct sockaddr_in6 *)a->ai_addr)->sin6_addr;
+                   mreq6.ipv6mr_interface = 
+                           ((struct sockaddr_in6 *)a->ai_addr)->sin6_scope_id;
+                   ret = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
+                                    &mreq6, sizeof(mreq6));
+                   break;
+               default:
+                   continue;
+           }
+           if (ret < 0) {
+               ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+                            "SiteCast UDP Responder fails on setting multicast (%s)",
+                            strerror(errno));
+               continue;
+           }
+       }
+
+       FD_SET(s, &sitecast_sockets.fds);
+       if (s > sitecast_sockets.max_fd)
+           sitecast_sockets.max_fd = s;
+       open = 1;
+    }
+    freeaddrinfo(ai);
+
+    if (!open) {
+      ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+                  "mod_gridsite: sitecast responder fails on unicast");
+      return -1;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+                 "SiteCast UDP %s responder on %s:%s",
+                (is_unicast) ? "unicast" : "multicast",
+                node, serv);
+    return 0;
+}
+
+void sitecast_responder(server_rec *main_server)
+{
+#define GRST_SITECAST_MAXBUF 8192
+  char   reqbuf[GRST_SITECAST_MAXBUF];
+  int    reqbuf_len, ret, retval, i;
+  struct sockaddr client_addr;
+  socklen_t client_addr_len;
+  fd_set readsckts;
+  int s;
+  char host[INET6_ADDRSTRLEN];
+  char serv[8];
+
+  strcpy((char *) main_server->process->argv[0], "GridSiteCast UDP responder");
+
+  FD_ZERO(&sitecast_sockets.fds);
+  sitecast_sockets.max_fd = -1;
+
+  /* initialise unicast/replies socket first */
+  ret =  bind_sitecast_sockets(main_server, main_server->server_hostname, sitecastgroups[0].port, 1);
+  if (ret)
+      return;
+
+  /* initialise multicast listener sockets next */
+
+  for (i=1; (i <= GRST_SITECAST_GROUPS) && 
+            (sitecastgroups[i].port != 0); ++i)
+     {
+       ret = bind_sitecast_sockets(main_server, sitecastgroups[i].address, sitecastgroups[i].port, 0);
+       if (ret)
+          continue;
+
+       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+        "SiteCast UDP Responder listening on %s:%d",
+       sitecastgroups[i].address,
+        sitecastgroups[i].port);
+     }
+
+  for (i=0; (i < GRST_SITECAST_ALIASES) &&
+            (sitecastaliases[i].sitecast_url != NULL) ; ++i)
+     {
+       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+                          "SiteCast alias for %s (%s,%d) to %s (%s)",
+                          sitecastaliases[i].sitecast_url,
+                          sitecastaliases[i].scheme,
+                          sitecastaliases[i].port,
+                          sitecastaliases[i].local_path,
+                          sitecastaliases[i].local_hostname);
+     }
+
+  while (1) /* **** main listening loop **** */
+       {
+         /* set up bitmasks for select */
+       
+         readsckts = sitecast_sockets.fds;
+
+         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+                      "SiteCast UDP Responder waiting for requests");
+
+         if ((retval = select(sitecast_sockets.max_fd + 1, &readsckts, NULL, NULL, NULL)) < 1)
+                                   continue; /* < 1 on timeout or error */
+
+        for (s = 0; s <= sitecast_sockets.max_fd; s++) {
+            if (FD_ISSET(s, &readsckts))
+                break;
+        }
+        if (s > sitecast_sockets.max_fd)
+            continue;
+
+        client_addr_len = sizeof(client_addr);
+        reqbuf_len = recvfrom(s, reqbuf, GRST_SITECAST_MAXBUF, 0,
+                              &client_addr, &client_addr_len);
+        if (reqbuf_len < 0)
+            continue;
+
+        getnameinfo(&client_addr, client_addr_len,
+                    host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+                     "SiteCast receives UDP message from %s:%s",
+                     host, serv);
+
+        sitecast_handle_request(main_server, reqbuf, reqbuf_len, s,
+                                &client_addr, client_addr_len);
+       } /* **** end of main listening loop **** */
+}
+
+static int mod_gridsite_server_post_config(apr_pool_t *pPool,
+                  apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *main_server)
+{
+   SSL_CTX         *ctx;
+   SSLSrvConfigRec *sc;
+   int              i = 0;
+   server_rec      *this_server;
+   apr_proc_t      *procnew = NULL;
+   apr_status_t     status;
+   char            *path;   
+   const char *userdata_key   = "sitecast_init";
+   const char *insecure_reneg = "SSLInsecureRenegotiation";
+   canl_ctx c_ctx = NULL;
+
+   c_ctx = canl_create_ctx();
+   if (!c_ctx){
+           ap_log_error(APLOG_MARK, APLOG_CRIT, status, main_server,
+              "mod_gridsite: Failed to create caNl context.");
+       return HTTP_INTERNAL_SERVER_ERROR;
+   }
+
+   apr_pool_userdata_get((void **) &procnew, userdata_key, 
+                         main_server->process->pool);
+
+   /* we only fork responder if one not already forked and we have at
+      least one GridSiteCastAlias defined. This means it is possible
+      to run a responder with no groups - listening on unicast only! */
+
+   if ((procnew == NULL) &&
+       (sitecastaliases[0].sitecast_url != NULL))
+     {
+       /* UDP multicast responder required but not yet started */
+
+       procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew));
+       apr_pool_userdata_set((const void *) procnew, userdata_key,
+                     apr_pool_cleanup_null, main_server->process->pool);
+
+       status = apr_proc_fork(procnew, pPool);
+
+       if (status < 0)
+         {
+           ap_log_error(APLOG_MARK, APLOG_CRIT, status, main_server,
+              "mod_gridsite: Failed to spawn SiteCast responder process");
+           return HTTP_INTERNAL_SERVER_ERROR;
+         }
+       else if (status == APR_INCHILD)
+         {
+           ap_log_error(APLOG_MARK, APLOG_NOTICE, status, main_server,
+              "mod_gridsite: Spawning SiteCast responder process");
+           sitecast_responder(main_server);
+           exit(-1);
+         }
+
+       apr_pool_note_subprocess(main_server->process->pool,
+                                procnew, APR_KILL_AFTER_TIMEOUT);
+     }
+
+   /* continue with normal HTTP/HTTPS servers */
+
+   ap_add_version_component(pPool,
+                            apr_psprintf(pPool, "mod_gridsite/%s", VERSION));
+
+   /* look for a SSLInsecureRenegotiation flag - if it exists then the mod_ssl
+      internal variable 'SSLSrvConfigRec' is different */
+   while ( ssl_module.cmds[i].name && !mod_ssl_with_insecure_reneg)
+   {
+       mod_ssl_with_insecure_reneg = (strncmp( ssl_module.cmds[i].name, 
+                                      insecure_reneg, sizeof(insecure_reneg) ) == 0);
+       i++;
+   }
+
+   ap_log_error(APLOG_MARK, APLOG_NOTICE, status, main_server,
+              "mod_gridsite: mod_ssl_with_insecure_reneg = %d", mod_ssl_with_insecure_reneg);
+
+   for (this_server = main_server; 
+        this_server != NULL; 
+        this_server = this_server->next)
+      {
+        /* we do some GridSite OpenSSL magic for HTTPS servers */
+     
+        sc = ap_get_module_config(this_server->module_config, &ssl_module);
+        
+        if ((sc                                  != NULL)  &&
+            (sc->enabled)                                  &&
+            (SSLSrvConfigRec_server(sc)          != NULL)  &&
+            (SSLSrvConfigRec_server(sc)->ssl_ctx != NULL))
+          {
+            ctx = SSLSrvConfigRec_server(sc)->ssl_ctx;
+
+            /* Use default caNl callbacks to verify certificates*/
+            canl_ssl_ctx_set_clb(c_ctx, ctx, ctx->verify_mode,
+                    GRST_callback_SSLVerify_wrapper);
+
+            if (main_server->loglevel >= APLOG_DEBUG)
+                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+                      "Set mod_ssl verify callbacks to GridSite wrappers: %s",
+                      canl_get_error_message(c_ctx));
+          }
+      }
+
+   /* create sessions directory if necessary */
+
+   path = ap_server_root_relative(pPool, sessionsdir);
+   apr_dir_make_recursive(path, APR_UREAD | APR_UWRITE | APR_UEXECUTE, pPool);
+   chown(path, unixd_config.user_id, unixd_config.group_id);
+
+   canl_free_ctx(c_ctx);
+   return OK;
+}
+
+static server_rec *mod_gridsite_log_func_server;
+static int mod_gridsite_log_func(char *file, int line, int level,
+                                                    char *fmt, ...)
+{
+   char *mesg;
+   va_list ap;
+
+   va_start(ap, fmt);
+   vasprintf(&mesg, fmt, ap);
+   va_end(ap);
+
+   ap_log_error(file, line, level, 
+                0, mod_gridsite_log_func_server, "%s", mesg);
+   
+   free(mesg);
+   return 0;
+}
+      
+static void mod_gridsite_child_init(apr_pool_t *pPool, server_rec *pServer)
+{
+   apr_time_t cutoff_time;
+   apr_dir_t *dir;
+   char *filename;
+   apr_finfo_t finfo;
+   SSLSrvConfigRec *sc = ap_get_module_config(pServer->module_config,
+                                                        &ssl_module);
+   GRSTgaclInit();
+   mod_gridsite_log_func_server = pServer;
+   GRSTerrorLogFunc = mod_gridsite_log_func;
+
+   /* expire old ssl creds files */
+                                    
+   if (sc != NULL)
+     {
+       cutoff_time = apr_time_now() 
+                      - apr_time_from_sec(sc->session_cache_timeout);
+
+       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServer,
+                        "Cutoff time for ssl creds cache: %ld", 
+                        (long) apr_time_sec(cutoff_time));
+
+       if (apr_dir_open(&dir, 
+           ap_server_root_relative(pPool, sessionsdir), pPool) == APR_SUCCESS)
+         {
+           while (apr_dir_read(&finfo, 
+                        APR_FINFO_CTIME | APR_FINFO_NAME, dir) == APR_SUCCESS)
+                {
+                  if ((finfo.ctime < cutoff_time) &&
+                      (strncmp(finfo.name, "sslcreds-", 9) == 0))
+                    {
+                      filename = apr_pstrcat(pPool, 
+                                   ap_server_root_relative(pPool, sessionsdir),
+                                   "/", finfo.name, NULL);
+                    
+                      ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServer,
+                        "Remove %s from ssl creds cache", filename);
+
+                      apr_file_remove(filename, pPool);
+                    }
+                }
+
+           apr_dir_close(dir);
+         }       
+     }
+}
+
+static int mod_gridsite_handler(request_rec *r)
+{
+   mod_gridsite_dir_cfg *conf;
+    
+   conf = (mod_gridsite_dir_cfg *)
+                    ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+   if (conf->dnlistsuri != NULL)
+     {
+       if (strcmp(r->uri, conf->dnlistsuri) == 0)
+              return mod_gridsite_dnlistsuri_dir_handler(r, conf);
+
+       if (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0)
+              return mod_gridsite_dnlistsuri_handler(r, conf);
+
+       if ((strncmp(r->uri, conf->dnlistsuri, strlen(r->uri)) == 0)
+           && (strlen(r->uri) == strlen(conf->dnlistsuri) - 1))
+         {
+           apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), 
+                          apr_pstrcat(r->pool, r->uri, "/", NULL));
+
+           r->status = HTTP_MOVED_TEMPORARILY;  
+           return OK;           
+         }      
+     }
+
+   if (strcmp(r->handler, DIR_MAGIC_TYPE) == 0)
+                   return mod_gridsite_dir_handler(r, conf);
+   
+   return mod_gridsite_nondir_handler(r, conf);
+}
+
+static ap_unix_identity_t *mod_gridsite_get_suexec_id_doer(const request_rec *r)
+{
+   mod_gridsite_dir_cfg *conf;
+    
+   conf = (mod_gridsite_dir_cfg *)
+                    ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+   if ((conf->execugid.uid != UNSET) && 
+       (conf->execmethod != NULL)) 
+     {
+     
+     /* also push GRST_EXEC_DIRECTORY into request environment here too */
+     
+       return &(conf->execugid);
+     }
+              
+   return NULL;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+    /* config and handler stuff */
+    static const char * const aszPre[] = { "mod_ssl.c", NULL };
+
+    ap_hook_post_config(mod_gridsite_server_post_config, NULL, NULL, 
+                                                              APR_HOOK_LAST);
+    ap_hook_child_init(mod_gridsite_child_init, aszPre, NULL, APR_HOOK_MIDDLE);
+    
+    ap_hook_check_user_id(mod_gridsite_check_user_id, NULL, NULL, 
+                                                      APR_HOOK_REALLY_FIRST);
+
+    ap_hook_fixups(mod_gridsite_first_fixups,NULL,NULL,APR_HOOK_FIRST);
+    
+    ap_hook_fixups(mod_gridsite_perm_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
+    
+    ap_hook_handler(mod_gridsite_handler, NULL, NULL, APR_HOOK_FIRST);    
+    
+    ap_hook_get_suexec_identity(mod_gridsite_get_suexec_id_doer,
+                                NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA gridsite_module =
+{
+    STANDARD20_MODULE_STUFF,
+    create_gridsite_dir_config, /* dir config creater */
+    merge_gridsite_dir_config,  /* dir merger */
+    create_gridsite_srv_config, /* create server config */
+    NULL,                      /* merge server config */
+    mod_gridsite_cmds,          /* command apr_table_t */
+    register_hooks              /* register hooks */
+};
diff --git a/org.gridsite.core/src/canl_mod_ssl-private.h b/org.gridsite.core/src/canl_mod_ssl-private.h
new file mode 100644 (file)
index 0000000..d6e47f6
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+   Copyright (c) 2003-8, Andrew McNab, University of Manchester
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+     o Redistributions of source code must retain the above
+       copyright notice, this list of conditions and the following
+       disclaimer. 
+     o Redistributions in binary form must reproduce the above
+       copyright notice, this list of conditions and the following
+       disclaimer in the documentation and/or other materials
+       provided with the distribution. 
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+
+ Portions of this code are derived from Apache mod_ssl, and are covered
+ by the Apache Software License:
+
+ * Copyright 2001-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+   This work has been partially funded by the EU Commission (contract 
+   INFSO-RI-222667) under the EGEE-III collaboration.
+*/
+
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
+
+
+/*
+ * After 2.0.49, Apache mod_ssl has most of the mod_ssl structures defined
+ * in ssl_private.h, which is not installed along with httpd-devel (eg in
+ * the FC2 RPM.) This include file provides SIMPLIFIED structures for use
+ * by mod_gridsite: for example, pointers to unused structures are replaced
+ * by  void *  and some of the structures are truncated when only the early
+ * members are used.
+ *
+ * CLEARLY, THIS WILL BREAK IF THERE ARE MAJOR CHANGES TO ssl_private.h!!!
+ */
+
+#include <openssl/ssl.h>
+
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+
+typedef enum {
+    SSL_SHUTDOWN_TYPE_UNSET,
+    SSL_SHUTDOWN_TYPE_STANDARD,
+    SSL_SHUTDOWN_TYPE_UNCLEAN,
+    SSL_SHUTDOWN_TYPE_ACCURATE
+} ssl_shutdown_type_e;
+
+typedef enum {
+    SSL_ENABLED_UNSET    = -1,
+    SSL_ENABLED_FALSE    = 0,
+    SSL_ENABLED_TRUE     = 1,
+    SSL_ENABLED_OPTIONAL = 3
+} ssl_enabled_t;
+
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+typedef enum {
+    SSL_CVERIFY_UNSET           = -1,
+    SSL_CVERIFY_NONE            = 0,
+    SSL_CVERIFY_OPTIONAL        = 1,
+    SSL_CVERIFY_REQUIRE         = 2,
+    SSL_CVERIFY_OPTIONAL_NO_CA  = 3
+} ssl_verify_t;
+
+#endif
+
+typedef struct {
+  SSL *ssl;
+  const char *client_dn;
+  X509 *client_cert;
+  ssl_shutdown_type_e shutdown_type;
+  const char *verify_info;
+  const char *verify_error;
+  int verify_depth;
+  int is_proxy;
+  int disabled;
+  int non_ssl_request;
+} SSLConnRec;
+
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+typedef struct {
+    const char  *ca_cert_path;
+    const char  *ca_cert_file;
+
+    const char  *cipher_suite;
+
+    int          verify_depth;
+    ssl_verify_t verify_mode;
+} modssl_auth_ctx_t;
+#endif
+
+typedef struct {
+  void    *sc; /* pointer back to server config */
+  SSL_CTX *ssl_ctx;
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+  void *pks;
+  void *pkp;
+
+  int  protocol;
+
+  int           pphrase_dialog_type;
+  const char   *pphrase_dialog_path;
+
+  const char  *cert_chain;
+
+  const char  *crl_path;
+  const char  *crl_file;
+  X509_STORE  *crl;
+
+  modssl_auth_ctx_t auth;
+#endif
+} modssl_ctx_t;
+
+/* original SSLSrvConfigRec */
+typedef struct {
+  void            *mc;
+  BOOL            enabled;
+  BOOL            proxy_enabled;
+  const char      *vhost_id;
+  int              vhost_id_len;
+  int              session_cache_timeout;
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+  BOOL             cipher_server_pref;
+#endif
+  modssl_ctx_t    *server;
+  modssl_ctx_t    *proxy;
+} SSLSrvConfigRec;
+
+/* SSLSrvConfigRec after mod_ssl patch for CVE-2009-3555 */
+typedef struct {
+  void            *mc;
+  unsigned int     enabled;
+  unsigned int     proxy_enabled;
+  const char      *vhost_id;
+  int              vhost_id_len;
+  int              session_cache_timeout;
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+  BOOL             cipher_server_pref;
+#endif
+  /* this is the member that was added */
+  int              insecure_reneg;
+  modssl_ctx_t    *server;
+  modssl_ctx_t    *proxy;
+} SSLSrvConfigRec2;
+
+/* The server and proxy members of SSLSrvConfigRec must only be accessed
+   using these macros: */
+#define SSLSrvConfigRec_server(sc) (mod_ssl_with_insecure_reneg ? (((SSLSrvConfigRec2 *) sc)->server) : (((SSLSrvConfigRec *) sc)->server))
+#define SSLSrvConfigRec_proxy(sc) (mod_ssl_with_insecure_reneg ? (((SSLSrvConfigRec2 *) sc)->proxy) : (((SSLSrvConfigRec *) sc)->proxy))
+
+#if AP_MODULE_MAGIC_AT_LEAST(20051115,0)
+typedef struct {
+    BOOL          bSSLRequired;
+    apr_array_header_t *aRequirement;
+    int                  nOptions;
+    int           nOptionsAdd;
+    int           nOptionsDel;
+    const char   *szCipherSuite;
+    ssl_verify_t  nVerifyClient;
+    int           nVerifyDepth;
+    const char   *szCACertificatePath;
+    const char   *szCACertificateFile;
+    const char   *szUserName;
+} SSLDirConfigRec;
+#endif
+
+extern module AP_MODULE_DECLARE_DATA ssl_module;
diff --git a/org.gridsite.core/src/grst_canl_x509.c b/org.gridsite.core/src/grst_canl_x509.c
new file mode 100644 (file)
index 0000000..4d2b238
--- /dev/null
@@ -0,0 +1,2474 @@
+/*
+   Copyright (c) 2002-10, Andrew McNab, University of Manchester
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   o Redistributions of source code must retain the above
+     copyright notice, this list of conditions and the following
+     disclaimer. 
+   o Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials
+     provided with the distribution. 
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+
+   ---------------------------------------------------------------
+    For more information about GridSite: http://www.gridsite.org/
+   ---------------------------------------------------------------
+*/ 
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>       
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <string.h>
+#include <pwd.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef GRST_NO_OPENSSL
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>    
+#include <openssl/des.h>    
+#include <openssl/rand.h>
+#endif
+
+#include <canl.h>
+#include <canl_cred.h>
+
+#include "gridsite.h"
+
+#define GRST_KEYSIZE           512
+#define GRST_PROXYCACHE                "/../proxycache/"
+#define GRST_MAX_CHAIN_LEN     9
+#define GRST_BACKDATE_SECONDS  300
+
+int
+GRSTasn1FindField(const char *oid, char *coords,
+        char *asn1string,
+        struct GRSTasn1TagList taglist[], int lasttag,
+        int *result);
+
+/// Compare X509 Distinguished Name strings
+int GRSTx509NameCmp(char *a, char *b)
+///
+/// This function attempts to do with string representations what
+/// would ideally be done with OIDs/values. In particular, we equate
+/// "/Email=" == "/emailAddress=" to deal with this important change
+/// between OpenSSL 0.9.6 and 0.9.7. 
+/// Other than that, it is currently the same as ordinary strcasecmp(3)
+/// (for consistency with EDG/LCG/EGEE gridmapdir case insensitivity.)
+{
+   int   ret;
+   char *aa, *bb, *p;
+
+   if ((a == NULL) || (b == NULL)) return 1; /* NULL never matches */
+
+   aa = strdup(a);
+   while ((p = strstr(aa, "/emailAddress=")) != NULL)
+        {
+          memmove(&p[6], &p[13], strlen(&p[13]) + 1);
+          p[1] = 'E';
+        }
+
+   bb = strdup(b);
+   while ((p = strstr(bb, "/emailAddress=")) != NULL)
+        {
+          memmove(&p[6], &p[13], strlen(&p[13]) + 1);
+          p[1] = 'E';
+        }
+
+   ret = strcasecmp(aa, bb);
+
+   free(aa);
+   free(bb);
+                                                                                
+   return ret;
+}
+
+
+/// Check critical extensions
+/*TODO MBD*/
+int GRSTx509KnownCriticalExts(X509 *cert)
+///
+/// Returning GRST_RET_OK if all of extensions are known to us or 
+/// OpenSSL; GRST_REF_FAILED otherwise.   
+///
+/// Since this function relies on functionality (X509_supported_extension)
+/// introduced in 0.9.7, then we do nothing and report an error 
+/// (GRST_RET_FAILED) if one of the associated defines 
+/// (X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) is absent.
+{
+   int  i;
+   char s[80];
+   X509_EXTENSION *ex;
+   
+#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION        
+   for (i = 0; i < X509_get_ext_count(cert); ++i)
+      {
+        ex = X509_get_ext(cert, i);
+
+        if (X509_EXTENSION_get_critical(ex) &&
+                                 !X509_supported_extension(ex))
+          {
+            OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1);
+
+            if ((strcmp(s, GRST_PROXYCERTINFO_OID)     != 0) &&
+                (strcmp(s, GRST_PROXYCERTINFO_OLD_OID) != 0))
+              return GRST_RET_FAILED;
+          }
+      }
+
+   return GRST_RET_OK;
+#else
+   return GRST_RET_FAILED;
+#endif
+}
+
+/// Check if certificate can be used as a CA to sign standard X509 certs
+int GRSTx509IsCA(X509 *cert)
+///
+/// Return GRST_RET_OK if true; GRST_RET_FAILED if not.
+{
+   int purpose_id;
+
+   purpose_id = X509_PURPOSE_get_by_sname("sslclient");
+
+   /* final argument to X509_check_purpose() is whether to check for CAness */   
+
+   if (X509_check_purpose(cert, purpose_id + X509_PURPOSE_MIN, 1))
+        return GRST_RET_OK;
+   else return GRST_RET_FAILED;
+}   
+
+int GRSTx509ChainFree(GRSTx509Chain *chain)
+{
+   GRSTx509Cert *grst_cert, *next_grst_cert;
+
+   if (chain == NULL) return GRST_RET_OK;
+
+   next_grst_cert = chain->firstcert; 
+   
+   while (next_grst_cert != NULL)
+      {
+        grst_cert = next_grst_cert;
+        
+        if (grst_cert->issuer != NULL) free(grst_cert->issuer);
+        if (grst_cert->dn     != NULL) free(grst_cert->dn);
+        if (grst_cert->value  != NULL) free(grst_cert->value);
+        if (grst_cert->ocsp   != NULL) free(grst_cert->ocsp);
+        
+        next_grst_cert = grst_cert->next;
+        free(grst_cert);        
+      }
+   
+   free(chain);
+
+   return GRST_RET_OK;
+}
+
+/// Check a specific signature against a specific (VOMS) cert
+static int GRSTx509VerifySig(time_t *time1_time, time_t *time2_time,
+                             unsigned char *txt, int txt_len,
+                             unsigned char *sig, int sig_len, 
+                             X509 *cert, EVP_MD *md_type)
+///
+/// Returns GRST_RET_OK if signature is ok, other values if not.
+{   
+   int            ret;
+   EVP_PKEY      *prvkey;
+   EVP_MD_CTX     ctx;
+   time_t         voms_service_time1, voms_service_time2;
+
+   prvkey = X509_extract_key(cert);
+   if (prvkey == NULL) return GRST_RET_FAILED;
+            
+   OpenSSL_add_all_digests();
+#if OPENSSL_VERSION_NUMBER >= 0x0090701fL
+   EVP_MD_CTX_init(&ctx);
+   EVP_VerifyInit_ex(&ctx, md_type, NULL);
+#else
+   EVP_VerifyInit(&ctx, md_type);
+#endif
+          
+   EVP_VerifyUpdate(&ctx, txt, txt_len);
+
+   ret = EVP_VerifyFinal(&ctx, sig, sig_len, prvkey);
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090701fL
+   EVP_MD_CTX_cleanup(&ctx);      
+#endif
+   EVP_PKEY_free(prvkey);
+
+   if (ret != 1) return GRST_RET_FAILED;
+
+   voms_service_time1 = 
+           GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0);
+          if (voms_service_time1 > *time1_time) 
+                             *time1_time = voms_service_time1; 
+           
+   voms_service_time2 = 
+           GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0);
+          if (voms_service_time2 < *time1_time) 
+                             *time2_time = voms_service_time2; 
+            
+   return GRST_RET_OK ; /* verified */
+}
+
+/// Check the signature of the VOMS attributes
+static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time,
+                                 unsigned char *asn1string, 
+                                 struct GRSTasn1TagList taglist[], 
+                                 int lasttag,
+                                 char *vomsdir, int acnumber)
+///
+/// Returns GRST_RET_OK if signature is ok, other values if not.
+{   
+#define GRST_ASN1_COORDS_VOMS_DN   "-1-1-%d-1-3-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1"
+#define GRST_ASN1_COORDS_VOMS_HASH "-1-1-%d-2-1"
+#define GRST_ASN1_COORDS_VOMS_SIG  "-1-1-%d-3"
+   int           ihash, isig, iinfo;
+   char          *certpath, *certpath2, acvomsdn[200], dn_coords[200],
+                  info_coords[200], sig_coords[200], hash_coords[200];
+   unsigned char *p;
+   DIR           *vomsDIR, *vomsDIR2;
+   struct dirent *vomsdirent, *vomsdirent2;
+   X509          *cert;
+   FILE          *fp;
+   EVP_MD       *md_type = NULL;
+   struct stat    statbuf;
+   time_t         voms_service_time1 = GRST_MAX_TIME_T, voms_service_time2 = 0,
+                  tmp_time1, tmp_time2;
+   ASN1_OBJECT   *hash_obj = NULL;
+
+   if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED;
+
+   snprintf(dn_coords, sizeof(dn_coords), 
+            GRST_ASN1_COORDS_VOMS_DN, acnumber);
+   
+   if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords,
+         asn1string, taglist, lasttag) != GRST_RET_OK) return GRST_RET_FAILED;
+         
+   snprintf(info_coords, sizeof(info_coords), 
+            GRST_ASN1_COORDS_VOMS_INFO, acnumber);
+   iinfo = GRSTasn1SearchTaglist(taglist, lasttag, info_coords);
+
+   snprintf(hash_coords, sizeof(hash_coords), 
+            GRST_ASN1_COORDS_VOMS_HASH, acnumber);
+   ihash  = GRSTasn1SearchTaglist(taglist, lasttag, hash_coords);
+
+   snprintf(sig_coords, sizeof(sig_coords), 
+            GRST_ASN1_COORDS_VOMS_SIG, acnumber);
+   isig  = GRSTasn1SearchTaglist(taglist, lasttag, sig_coords);
+
+   if ((iinfo < 0) || (ihash < 0) || (isig < 0)) return GRST_RET_FAILED;
+   
+   /* determine hash algorithm's type */
+
+   p = &asn1string[taglist[ihash].start];
+   
+   d2i_ASN1_OBJECT(&hash_obj, &p, 
+                   taglist[ihash].length+taglist[ihash].headerlength);
+
+   md_type = EVP_get_digestbyname(OBJ_nid2sn(OBJ_obj2nid(hash_obj)));
+   
+   if (md_type == NULL) return GRST_RET_FAILED;
+   
+   
+   vomsDIR = opendir(vomsdir);
+   if (vomsDIR == NULL) return GRST_RET_FAILED;
+   
+   while ((vomsdirent = readdir(vomsDIR)) != NULL)
+        {        
+          if (vomsdirent->d_name[0] == '.') continue;
+        
+          asprintf(&certpath, "%s/%s", vomsdir, vomsdirent->d_name);
+          stat(certpath, &statbuf);
+          
+          if (S_ISDIR(statbuf.st_mode))
+            {
+              vomsDIR2 = opendir(certpath);
+              GRSTerrorLog(GRST_LOG_DEBUG, 
+                           "Descend VOMS subdirectory %s", certpath);
+              free(certpath);
+              
+              if (vomsDIR2 == NULL) continue;
+
+              while ((vomsdirent2 = readdir(vomsDIR2)) != NULL)
+                {
+                  if (vomsdirent2->d_name[0] == '.') continue;
+                  
+                  asprintf(&certpath2, "%s/%s/%s", 
+                           vomsdir, vomsdirent->d_name, vomsdirent2->d_name);
+                
+                  fp = fopen(certpath2, "r");
+                  GRSTerrorLog(GRST_LOG_DEBUG, 
+                               "Examine VOMS cert %s", certpath2);
+                  free(certpath2);
+                  if (fp == NULL) continue;
+
+                  cert = PEM_read_X509(fp, NULL, NULL, NULL);
+                  fclose(fp);
+                  if (cert == NULL) continue;
+
+                  tmp_time1 = 0;
+                  tmp_time2 = GRST_MAX_TIME_T;
+
+                  if (GRSTx509VerifySig(&tmp_time1, &tmp_time2,
+                            &asn1string[taglist[iinfo].start], 
+                            taglist[iinfo].length+taglist[iinfo].headerlength,
+                            &asn1string[taglist[isig].start+
+                                                taglist[isig].headerlength+1],
+                            taglist[isig].length - 1,
+                            cert, md_type) == GRST_RET_OK)
+                    {
+                      GRSTerrorLog(GRST_LOG_DEBUG, "Matched VOMS cert file %s", vomsdirent2->d_name);
+
+                      /* Store more permissive time ranges for now */
+
+                      if (tmp_time1 < voms_service_time1) 
+                                         voms_service_time1 = tmp_time1;
+                        
+                      if (tmp_time2 > voms_service_time2)
+                                         voms_service_time2 = tmp_time2;                        
+                    }
+            
+                  X509_free(cert);
+                }
+                
+              closedir(vomsDIR2);
+            }
+          else
+            {
+              fp = fopen(certpath, "r");
+              GRSTerrorLog(GRST_LOG_DEBUG, "Examine VOMS cert %s", certpath);
+              free(certpath);
+              if (fp == NULL) continue;
+
+              cert = PEM_read_X509(fp, NULL, NULL, NULL);
+              fclose(fp);
+              if (cert == NULL) continue;
+
+              tmp_time1 = 0;
+              tmp_time2 = GRST_MAX_TIME_T;
+
+              if (GRSTx509VerifySig(&tmp_time1, &tmp_time2,
+                            &asn1string[taglist[iinfo].start], 
+                            taglist[iinfo].length+taglist[iinfo].headerlength,
+                            &asn1string[taglist[isig].start+
+                                                taglist[isig].headerlength+1],
+                            taglist[isig].length - 1,
+                            cert, md_type) == GRST_RET_OK)
+                {
+                  GRSTerrorLog(GRST_LOG_DEBUG, "Matched VOMS cert file %s", vomsdirent->d_name);
+
+                  /* Store more permissive time ranges for now */
+
+                  if (tmp_time1 < voms_service_time1) 
+                                         voms_service_time1 = tmp_time1;
+                        
+                  if (tmp_time2 > voms_service_time2)
+                                         voms_service_time2 = tmp_time2;
+                }
+            
+              X509_free(cert);
+            }
+        }
+
+   closedir(vomsDIR);   
+   
+   if ((voms_service_time1 == GRST_MAX_TIME_T) || (voms_service_time2 == 0))
+     return GRST_RET_FAILED;
+
+   /* now we tighten up the VOMS AC time range using the most permissive
+      pair of VOMS certificate ranges (in case of multiple, possibly 
+      overlapping, matching certificates in the VOMS certs store) */
+     
+   if (voms_service_time1 > *time1_time) *time1_time = voms_service_time1;
+     
+   if (voms_service_time2 < *time2_time) *time2_time = voms_service_time2;
+     
+   return GRST_RET_OK;
+}
+
+/// Check the signature of the VOMS attributes using the LSC file cert
+static int GRSTx509VerifyVomsSigCert(time_t *time1_time, time_t *time2_time,
+                                     unsigned char *asn1string, 
+                                     struct GRSTasn1TagList taglist[], 
+                                     int lasttag,
+                                     char *vomsdir, int acnumber,
+                                     int ivomscert,
+                                     char *capath,
+                                     char *acvomsdn,
+                                     char *voname)
+///
+/// Returns GRST_RET_OK if signature is ok, other values if not.
+{
+#define GRST_ASN1_COORDS_VOMS_DN   "-1-1-%d-1-3-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1"
+#define GRST_ASN1_COORDS_VOMS_SIG  "-1-1-%d-3"
+   int            ret, isig, iinfo, chain_errors = GRST_RET_OK,
+                  cadn_len, vomsdn_len, lsc_found = 0, ihash;
+   char          *lscpath, *p, *cacertpath,
+                 *vodir, *vomscert_cadn, *vomscert_vomsdn, 
+                 *lsc_cadn, *lsc_vomsdn;
+   unsigned char *q;
+   unsigned long  issuerhash = 0;
+   DIR           *voDIR;
+   struct dirent *vodirent;
+   X509          *cacert = NULL, *vomscert = NULL;
+   FILE          *fp;
+   struct stat    statbuf;
+   time_t         tmp_time;
+   ASN1_OBJECT   *hash_obj = NULL;
+   char                  coords[200];
+   EVP_MD        *md_type = NULL;
+   time_t        voms_service_time1, voms_service_time2;
+
+   if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED;
+
+   q = &asn1string[taglist[ivomscert].start + 12];
+
+   vomscert = d2i_X509(NULL, (const unsigned char **) &q, 
+                       taglist[ivomscert].length - 8);
+
+   if (vomscert == NULL) 
+     {
+       GRSTerrorLog(GRST_LOG_DEBUG, "Failed to read included VOMS cert in GRSTx509VerifyVomsSigCert()");
+       return GRST_RET_FAILED;
+     }
+
+   GRSTerrorLog(GRST_LOG_DEBUG, "Found included VOMS cert in GRSTx509VerifyVomsSigCert()");
+
+   snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_INFO, acnumber);
+   iinfo = GRSTasn1SearchTaglist(taglist, lasttag, coords);
+
+   snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_HASH, acnumber);
+   ihash  = GRSTasn1SearchTaglist(taglist, lasttag, coords);
+
+   snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_SIG, acnumber);
+   isig  = GRSTasn1SearchTaglist(taglist, lasttag, coords);
+
+   if ((iinfo < 0) || (ihash < 0) || (isig < 0)) return GRST_RET_FAILED;
+
+   q = &asn1string[taglist[ihash].start];
+   d2i_ASN1_OBJECT(&hash_obj, &q,
+                  taglist[ihash].length+taglist[ihash].headerlength);
+
+   md_type = EVP_get_digestbyname(OBJ_nid2sn(OBJ_obj2nid(hash_obj)));
+   if (md_type == NULL) return GRST_RET_FAILED;
+
+   /* check issuer CA certificate */
+
+   issuerhash = X509_NAME_hash(X509_get_issuer_name(vomscert));
+   asprintf(&cacertpath, "%s/%.8x.0", capath, issuerhash);
+
+   /* check voms cert DN matches DN from AC */
+
+   vomscert_vomsdn = X509_NAME_oneline(X509_get_subject_name(vomscert),NULL,0);
+
+   if (GRSTx509NameCmp(vomscert_vomsdn, acvomsdn) != 0)
+     {
+       free(vomscert_vomsdn);
+       
+       GRSTerrorLog(GRST_LOG_DEBUG, "Included VOMS cert DN does not match AC issuer DN!");
+       return GRST_RET_FAILED;     
+     }
+
+   free(vomscert_vomsdn);
+
+   GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath);
+
+   fp = fopen(cacertpath, "r");
+   free(cacertpath);
+
+   if (fp == NULL) return GRST_RET_FAILED;
+   else
+     {
+       cacert = PEM_read_X509(fp, NULL, NULL, NULL);
+       fclose(fp);
+       if (cacert != NULL) 
+         {
+           GRSTerrorLog(GRST_LOG_DEBUG, " Loaded CA root cert from file");
+         }
+       else
+         {         
+           GRSTerrorLog(GRST_LOG_DEBUG, " Failed to load CA root cert file");
+           return GRST_RET_FAILED;
+         }
+     }
+   
+   /* check times CA cert times, and reject if necessary */
+
+   tmp_time = GRSTasn1TimeToTimeT(
+                   ASN1_STRING_data(X509_get_notBefore(cacert)), 0);
+   if (tmp_time > *time1_time) chain_errors |= GRST_CERT_BAD_TIME;
+
+   tmp_time = GRSTasn1TimeToTimeT(
+                   ASN1_STRING_data(X509_get_notAfter(cacert)), 0);
+   if (tmp_time < *time2_time) chain_errors |= GRST_CERT_BAD_TIME;
+   
+   /* check times VOMS cert times, and tighten if necessary */
+
+   tmp_time = GRSTasn1TimeToTimeT(
+                   ASN1_STRING_data(X509_get_notBefore(vomscert)), 0);
+   if (tmp_time > *time1_time) chain_errors |= GRST_CERT_BAD_TIME;
+
+   tmp_time = GRSTasn1TimeToTimeT(
+                   ASN1_STRING_data(X509_get_notAfter(vomscert)), 0);
+   if (tmp_time < *time2_time) chain_errors |= GRST_CERT_BAD_TIME;
+   
+   ret = X509_check_issued(cacert, vomscert);
+   GRSTerrorLog(GRST_LOG_DEBUG, "X509_check_issued returns %d", ret);
+
+   vomscert_cadn = X509_NAME_oneline(X509_get_issuer_name(vomscert),NULL,0);
+
+
+   if (ret != X509_V_OK) return (chain_errors | GRST_CERT_BAD_SIG);
+
+   asprintf(&vodir, "%s/%s", vomsdir, voname);
+   
+   voDIR = opendir(vodir);
+   if (voDIR == NULL) return GRST_RET_FAILED;
+   
+   cadn_len   = strlen(vomscert_cadn);
+   vomsdn_len = strlen(acvomsdn);
+   
+   lsc_cadn   = malloc(cadn_len   + 2);
+   lsc_vomsdn = malloc(vomsdn_len + 2);
+   
+   while (((vodirent = readdir(voDIR)) != NULL) && !lsc_found)
+        {        
+          if (vodirent->d_name[0] == '.') continue;
+          
+          if (strlen(vodirent->d_name) < 5) continue;
+          
+          if (strcmp(&(vodirent->d_name[strlen(vodirent->d_name)-4]), ".lsc") != 0) continue;
+        
+          asprintf(&lscpath, "%s/%s", vodir, vodirent->d_name);
+          stat(lscpath, &statbuf);
+
+          GRSTerrorLog(GRST_LOG_DEBUG, "Check LSC file %s for %s,%s",
+                       lscpath, acvomsdn, vomscert_cadn);
+
+          if ((fp = fopen(lscpath, "r")) != NULL)
+            {
+              lsc_cadn[0]   = '\0';
+              lsc_vomsdn[0] = '\0';
+            
+              if ((fgets(lsc_vomsdn, vomsdn_len + 2, fp) != NULL)
+                  && (fgets(lsc_cadn, cadn_len + 2, fp) != NULL))
+                {
+                  if ((p = index(lsc_cadn, '\n')) != NULL) *p = '\0';
+
+                  if ((p = index(lsc_vomsdn, '\n')) != NULL) *p = '\0';
+                  
+                  if ((GRSTx509NameCmp(lsc_cadn, vomscert_cadn) == 0) &&
+                      (GRSTx509NameCmp(lsc_vomsdn, acvomsdn) == 0)) 
+                    {
+                      GRSTerrorLog(GRST_LOG_DEBUG, "Matched LSC file %s", lscpath);
+                      lsc_found = 1;
+                    }                      
+                }
+              
+              fclose(fp);
+            }
+
+          free(lscpath);
+        }
+
+   closedir(voDIR);   
+   free(vodir);
+   free(vomscert_cadn);
+   free(lsc_cadn);
+   free(lsc_vomsdn);   
+   
+   if (!lsc_found) {
+       chain_errors |= GRST_CERT_BAD_SIG;
+       goto end;
+   }
+
+   ret = GRSTx509VerifySig(&voms_service_time1, &voms_service_time2,
+               &asn1string[taglist[iinfo].start],
+               taglist[iinfo].length+taglist[iinfo].headerlength,
+               &asn1string[taglist[isig].start+
+                                       taglist[isig].headerlength+1],
+               taglist[isig].length - 1,
+               vomscert, md_type);
+   if (ret != GRST_RET_OK) {
+       chain_errors |= GRST_CERT_BAD_SIG;
+       goto end;
+   }
+
+   if (voms_service_time1 > *time1_time) *time1_time = voms_service_time1;
+   if (voms_service_time2 < *time2_time) *time2_time = voms_service_time2;
+
+end:
+   if (cacert)
+       X509_free(cacert);
+   if (vomscert)
+       X509_free(vomscert);
+
+   return (chain_errors ? GRST_RET_FAILED : GRST_RET_OK);
+}
+
+/// Get the VOMS attributes in the given extension
+static int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert, 
+                         time_t time1_time, time_t time2_time,
+                        int delegation,
+                         X509_EXTENSION *ex, 
+                         GRSTx509Cert *user_cert, char *vomsdir, char *capath)
+///
+/// Add any VOMS credentials found into the chain. Always returns GRST_RET_OK
+/// - even for invalid credentials, which are flagged in errors field
+{
+#define MAXTAG 500
+#define GRST_ASN1_COORDS_FQAN          "-1-1-%d-1-7-1-2-1-2-%d"
+#define GRST_ASN1_COORDS_ISSUER_DN     "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_ISSUER_SERIAL "-1-1-%d-1-2-1-2"
+#define GRST_ASN1_COORDS_VOMS_DN       "-1-1-%d-1-3-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_TIME1         "-1-1-%d-1-6-1"
+#define GRST_ASN1_COORDS_TIME2         "-1-1-%d-1-6-2"
+#define GRST_ASN1_COORDS_VOMSCERT      "-1-1-%d-1-8-%%d-%%d"
+
+   ASN1_OCTET_STRING *asn1data;
+   char              *asn1string, acissuerdn[200], acvomsdn[200],
+                      dn_coords[200], fqan_coords[200], time1_coords[200],
+                      time2_coords[200], vomscert_coords[200], *voname = NULL,
+                      serial_coords[200];
+   long               asn1length;
+   int                lasttag=-1, itag, i, j, acnumber = 1, chain_errors = 0,
+                      ivomscert, tmp_chain_errors, ret;
+   char              *acissuerserial = NULL;
+   struct GRSTasn1TagList taglist[MAXTAG+1];
+   time_t             actime1 = 0, actime2 = 0, time_now,
+                      tmp_time1, tmp_time2;
+   ASN1_INTEGER              acissuerserialASN1;
+
+   asn1data   = X509_EXTENSION_get_data(ex);
+   asn1string = ASN1_STRING_data(asn1data);
+   asn1length = ASN1_STRING_length(asn1data);
+
+   GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag);
+
+   for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */
+      {
+        chain_errors = 0;
+      
+        snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_ISSUER_DN, acnumber);
+        if (GRSTasn1GetX509Name(acissuerdn, sizeof(acissuerdn), dn_coords,
+                                asn1string, taglist, lasttag) != GRST_RET_OK)
+                             break;
+
+        snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_VOMS_DN, acnumber);
+        if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords,
+                                asn1string, taglist, lasttag) != GRST_RET_OK)
+                             break;
+
+        if ((GRSTx509NameCmp(user_cert->dn, acissuerdn) != 0) &&   /* old */
+            (GRSTx509NameCmp(user_cert->issuer, acissuerdn) != 0)) /* new */
+                             chain_errors |= GRST_CERT_BAD_CHAIN;
+
+        /* check serial numbers */
+
+        snprintf(serial_coords, sizeof(serial_coords), 
+                 GRST_ASN1_COORDS_ISSUER_SERIAL, acnumber);
+
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, serial_coords);
+
+        if (itag > -1) 
+          {
+            acissuerserialASN1.length = taglist[itag].length;
+            acissuerserialASN1.type   = V_ASN1_INTEGER;
+            acissuerserialASN1.data   = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+
+            acissuerserial = i2s_ASN1_INTEGER(NULL, &acissuerserialASN1);
+/*
+            p = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+          
+            if (taglist[itag].length == 2)
+             acissuerserial = p[1] + p[0] * 0x100;
+            else if (taglist[itag].length == 3)
+             acissuerserial = p[2] + p[1] * 0x100 + p[0] * 0x10000;
+            else if (taglist[itag].length == 4)
+             acissuerserial = p[3] + p[2] * 0x100 + p[1] * 0x10000 +
+                              p[0] * 0x1000000;
+*/
+          }
+
+        if (strcmp(acissuerserial, user_cert->serial) != 0)
+                               chain_errors |= GRST_CERT_BAD_CHAIN;
+
+        /* get times */
+
+        snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber);
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords);
+        
+        if (itag > -1) actime1 = GRSTasn1TimeToTimeT(
+                                   &asn1string[taglist[itag].start+
+                                               taglist[itag].headerlength],
+                                   taglist[itag].length);
+        else actime1 = 0;
+        
+        snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber);
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords);
+        
+        if (itag > -1) actime2 = GRSTasn1TimeToTimeT(
+                                   &asn1string[taglist[itag].start+
+                                               taglist[itag].headerlength],
+                                   taglist[itag].length);
+        else actime2 = 0;
+        
+        if (actime1 > time1_time) time1_time = actime1;
+        if (actime2 < time2_time) time2_time = actime2;
+
+        time(&time_now);
+        if ((time1_time > time_now + 300) || (time2_time < time_now))
+               chain_errors |= GRST_CERT_BAD_TIME;
+
+        /* get first FQAN and use to get VO name */
+
+        snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, 1);
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords);
+
+        if ((itag > -1) && 
+            (asn1string[taglist[itag].start+taglist[itag].headerlength] == '/'))
+          {
+            for (j=1; 
+                 (asn1string[taglist[itag].start+taglist[itag].headerlength+j] != '/') && 
+                 (j < taglist[itag].length);
+                 ++j) ;
+                               
+            asprintf(&voname, "%.*s", j-1, 
+                     &(asn1string[taglist[itag].start+taglist[itag].headerlength+1]));
+          }
+
+        snprintf(vomscert_coords, sizeof(vomscert_coords), 
+                 GRST_ASN1_COORDS_VOMSCERT, acnumber);
+       ret = GRSTasn1FindField(GRST_VOMS_PK_CERT_LIST_OID, vomscert_coords, asn1string,
+                               taglist, lasttag, &ivomscert);
+
+        /* try using internal VOMS issuer cert */
+        tmp_chain_errors = GRST_CERT_BAD_SIG;
+        tmp_time1 = time1_time;
+        tmp_time2 = time2_time;
+        if ((ivomscert > -1) &&
+            (voname != NULL) &&
+            (GRSTx509VerifyVomsSigCert(&tmp_time1, &tmp_time2,
+                      asn1string, taglist, lasttag, vomsdir, acnumber,
+                      ivomscert, capath, acvomsdn, 
+                      voname) == GRST_RET_OK)) 
+          {          
+            tmp_chain_errors = 0;
+            time1_time = tmp_time1;
+            time2_time = tmp_time2;
+          }
+
+        if (voname != NULL)
+          {
+            free(voname);
+            voname = NULL;
+          }
+
+        if ((tmp_chain_errors != 0) &&
+            (GRSTx509VerifyVomsSig(&time1_time, &time2_time,
+                        asn1string, taglist, lasttag, vomsdir, acnumber)
+                        != GRST_RET_OK))
+                     chain_errors |= GRST_CERT_BAD_SIG;
+
+        for (i=1; ; ++i) /* now go through FQANs */
+           {
+             snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i);
+             itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords);
+
+             if (itag > -1)
+               {
+                 (*grst_cert)->next = malloc(sizeof(GRSTx509Cert));
+                 *grst_cert = (*grst_cert)->next;
+                 bzero(*grst_cert, sizeof(GRSTx509Cert));
+               
+                 (*grst_cert)->notbefore = time1_time;
+                 (*grst_cert)->notafter  = time2_time;
+                 asprintf(&((*grst_cert)->value), "%.*s",
+                          taglist[itag].length,
+                          &asn1string[taglist[itag].start+
+                                      taglist[itag].headerlength]);
+                                      
+                 (*grst_cert)->errors = chain_errors; /* ie may be invalid */
+                 (*grst_cert)->type = GRST_CERT_TYPE_VOMS;
+                 (*grst_cert)->issuer = strdup(acvomsdn);
+                 (*grst_cert)->dn = strdup(user_cert->dn);
+                (*grst_cert)->delegation = delegation;
+               }
+             else break;
+           }
+      }
+      
+   return GRST_RET_OK;
+}
+int GRSTx509ChainLoad(GRSTx509Chain **chain,
+                           STACK_OF(X509) *certstack, X509 *lastcert,
+                           char *capath, char *vomsdir)
+{
+   X509 *cert;                  /* Points to the current cert in the loop */
+   X509 *cacert = NULL;         /* The CA root cert */
+   int depth = 0;               /* Depth of cert chain */
+   int chain_errors = 0;       /* records previous errors */
+   int first_non_ca;           /* number of the EEC issued to user by CA */
+   size_t len,len2;             /* Lengths of issuer and cert DN */
+   int IsCA;                    /* Holds whether cert is allowed to sign */
+   int prevIsCA;                /* Holds whether previous cert in chain is 
+                                   allowed to sign */
+   int prevIsLimited;          /* previous cert was proxy and limited */
+   int i,j,ret;                 /* Iteration/temp variables */
+   char *proxy_part_DN;         /* Pointer to end part of current-cert-in-chain
+                                   maybe eg "/CN=proxy" */
+   char s[80], *p;
+   char *cacertpath;
+   unsigned long subjecthash = 0;      /* hash of the name of first cert */
+   unsigned long issuerhash = 0;       /* hash of issuer name of first cert */
+   FILE *fp;
+   X509_EXTENSION *ex;
+   time_t now;
+   GRSTx509Cert *grst_cert, *new_grst_cert, *user_cert = NULL;
+   
+   GRSTerrorLog(GRST_LOG_DEBUG, "GRSTx509ChainLoadCheck() starts");
+
+   time(&now);
+
+   first_non_ca = 0; /* set to something predictable if things fail */
+   /* Set necessary preliminary values */
+   IsCA          = TRUE;           /* =prevIsCA - start from a CA */
+   prevIsLimited = 0;
+   /* Get the client cert chain */
+   if (certstack != NULL) 
+     depth = sk_X509_num(certstack); /* How deep is that chain? */
+   
+   if ((depth == 0) && (lastcert == NULL)) 
+     {
+       *chain = NULL;
+       return GRST_RET_FAILED;
+     }
+
+   cert = sk_X509_value(certstack, depth - 1);
+   subjecthash = X509_NAME_hash(X509_get_subject_name(cert));
+   issuerhash = X509_NAME_hash(X509_get_issuer_name(cert));
+   asprintf(&cacertpath, "%s/%.8x.0", capath, issuerhash);
+   
+   GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath);
+
+   fp = fopen(cacertpath, "r");
+   free(cacertpath);
+
+   if (fp == NULL) chain_errors |= GRST_CERT_BAD_CHAIN;
+   else
+     {
+       cacert = PEM_read_X509(fp, NULL, NULL, NULL);
+       fclose(fp);
+       if (cacert != NULL) 
+        GRSTerrorLog(GRST_LOG_DEBUG, " Loaded CA root cert from file");
+       else
+        GRSTerrorLog(GRST_LOG_DEBUG, " Failed to load CA root cert file");
+     }
+
+   *chain = malloc(sizeof(GRSTx509Chain));
+   bzero(*chain, sizeof(GRSTx509Chain));
+       
+   /* Check the client chain */
+   for (i = depth - ((subjecthash == issuerhash) ? 1 : 0);
+        i >= ((lastcert == NULL) ? 0 : -1); 
+        --i) 
+      /* loop through client-presented chain starting at CA end */
+      {
+        GRSTerrorLog(GRST_LOG_DEBUG, "Process cert at depth %d in chain", i);
+
+        prevIsCA=IsCA;
+
+        new_grst_cert = malloc(sizeof(GRSTx509Cert));
+        bzero(new_grst_cert, sizeof(GRSTx509Cert));
+        new_grst_cert->errors = chain_errors;
+        
+        if ((*chain)->firstcert == NULL)
+          {
+            GRSTerrorLog(GRST_LOG_DEBUG, "Initialise chain");
+            (*chain)->firstcert = new_grst_cert;
+          }
+        else grst_cert->next = new_grst_cert;
+
+        grst_cert = new_grst_cert;
+
+        /* Choose X509 certificate and point to it with 'cert' */
+        if (i < 0) cert = lastcert;
+        else if (i == depth)
+             cert = cacert; /* the self-signed CA from the store*/
+        else if ((i == depth - 1) && (subjecthash == issuerhash))
+             cert = cacert; /* ie claims to be a copy of a self-signed CA */
+        else cert = sk_X509_value(certstack, i);
+
+        if (cert != NULL)
+          {
+            if ((i == depth - 1) && (subjecthash != issuerhash))
+              {
+                /* if first cert does not claim to be a self-signed copy 
+                   of a CA root cert in the store, we check the signature */
+
+                if (cacert == NULL)
+                  {
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                    ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+                  }
+                else 
+                  {
+                    ret = X509_check_issued(cacert, cert);
+
+                    GRSTerrorLog(GRST_LOG_DEBUG, 
+                             "Cert sig check %d returns %d", i, ret);
+
+                    if (ret != X509_V_OK) 
+                             new_grst_cert->errors |= GRST_CERT_BAD_SIG;
+                  }
+              }
+            else if ((i == depth - 2) && (subjecthash == issuerhash))
+              {
+                /* first cert claimed to be a self-signed copy of a CA root
+                cert in the store, we check the signature of the second
+                cert, using OUR copy of the CA cert DIRECT from the store */
+
+                if (cacert == NULL)
+                  {
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                    ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+                  }
+                else 
+                  {
+                    ret = X509_check_issued(cacert, cert);
+                
+                    GRSTerrorLog(GRST_LOG_DEBUG, 
+                             "Cert sig check %d returns %d", i, ret);
+                
+                    if (ret != X509_V_OK)
+                             new_grst_cert->errors |= GRST_CERT_BAD_SIG;
+                  }
+              }
+            else if (i < depth - 1)
+              {
+                /* otherwise a normal part of the chain: note that if the
+                   first cert claims to be a self-signed copy of a CA root
+                   cert in the store, we never use it for sig checking */
+              
+                ret = X509_check_issued(sk_X509_value(certstack, i + 1), cert);
+                
+                GRSTerrorLog(GRST_LOG_DEBUG, 
+                             "Cert sig check %d returns %d", i, ret);
+
+                if ((ret != X509_V_OK) &&
+                    (ret != X509_V_ERR_KEYUSAGE_NO_CERTSIGN))                
+                          new_grst_cert->errors |= GRST_CERT_BAD_SIG;
+                          
+                /* NO_CERTSIGN can still be ok due to Proxy Certificates */
+              }
+
+            p = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert));
+            strncpy(new_grst_cert->serial, p, GRST_X509_SERIAL_DIGITS);
+            new_grst_cert->serial[GRST_X509_SERIAL_DIGITS] = '\0';
+            free(p);
+            
+            new_grst_cert->notbefore = GRSTasn1TimeToTimeT(
+                               ASN1_STRING_data(X509_get_notBefore(cert)), 0);
+            new_grst_cert->notafter  = GRSTasn1TimeToTimeT(
+                               ASN1_STRING_data(X509_get_notAfter(cert)), 0);
+          
+            /* we check times and record if invalid */
+          
+            if (now < new_grst_cert->notbefore)
+                 new_grst_cert->errors |= GRST_CERT_BAD_TIME;
+
+            if (now > new_grst_cert->notafter)
+                 new_grst_cert->errors |= GRST_CERT_BAD_TIME;
+
+            new_grst_cert->dn = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
+            new_grst_cert->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0);
+            len       = strlen(new_grst_cert->dn);
+            len2      = strlen(new_grst_cert->issuer);
+
+            /* always treat a first cert from the CA files as a 
+               CA: this is really for lousy CAs that dont create 
+               proper v3 root certificates */
+                
+            if (i == depth) IsCA = TRUE;
+            else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+
+            /* If any forebear certificate is not allowed to sign we must 
+               assume all decendents are proxies and cannot sign either */
+            if (prevIsCA)
+              {
+                if (IsCA)
+                  {               
+                    new_grst_cert->type = GRST_CERT_TYPE_CA;
+                  }
+                else 
+                  {
+                    new_grst_cert->type = GRST_CERT_TYPE_EEC;
+                    first_non_ca = i;
+                    user_cert = new_grst_cert;
+                    new_grst_cert->delegation 
+                       = (lastcert == NULL) ? i : i + 1;
+                  }
+              } 
+            else 
+              {
+                new_grst_cert->type = GRST_CERT_TYPE_PROXY;
+
+                IsCA = FALSE;
+                /* Force proxy check next iteration. Important because I can
+                   sign any CA I create! */
+
+                new_grst_cert->delegation = (lastcert == NULL) ? i : i + 1;
+              }
+
+            if (!prevIsCA)
+              {
+                /* issuer didn't have CA status, so this is (at best) a proxy:
+                   check for bad proxy extension*/
+
+                if (prevIsLimited) /* we reject proxies of limited proxies! */
+                  {
+                    new_grst_cert->errors |= GRST_CERT_BAD_CHAIN;
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                  }
+              
+                /* User not allowed to sign shortened DN */
+                if (len2 > len) 
+                  {
+                    new_grst_cert->errors |= GRST_CERT_BAD_CHAIN;
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                  }
+                  
+                /* Proxy subject must begin with issuer. */
+                if (strncmp(new_grst_cert->dn, new_grst_cert->issuer, len2) != 0) 
+                  {
+                    new_grst_cert->errors  |= GRST_CERT_BAD_CHAIN;
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                  }
+
+                /* Set pointer to end of base DN in cert_DN */
+                proxy_part_DN = &(new_grst_cert->dn[len2]);
+
+                /* First attempt at support for Old and New style GSI
+                   proxies: /CN=anything is ok for now */
+                if (strncmp(proxy_part_DN, "/CN=", 4) != 0)
+                  {
+                    new_grst_cert->errors  |= GRST_CERT_BAD_CHAIN;
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                  }
+
+                if (strncmp(proxy_part_DN, "/CN=limited proxy", 17) == 0)
+                        prevIsLimited = 1; /* ready for next cert ... */
+
+                for (j=0; j < X509_get_ext_count(cert); ++j)
+                   {
+                     ex = X509_get_ext(cert, j);
+                     OBJ_obj2txt(s,sizeof(s),X509_EXTENSION_get_object(ex),1);
+
+                     if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */
+                       {
+                         GRSTx509ChainVomsAdd(&grst_cert, 
+                                              new_grst_cert->notbefore,
+                                              new_grst_cert->notafter,
+                                             (lastcert == NULL) ? i : i+1,
+                                              ex,
+                                              user_cert,
+                                              vomsdir,
+                                              capath);
+                       }
+                   }                     
+              } 
+          }
+          
+
+      } /* end of for loop */
+
+   if (cacert != NULL) X509_free(cacert);
+   return GRST_RET_OK;
+}
+
+/// Check certificate chain for GSI proxy acceptability.
+int GRSTx509ChainLoadCheck(GRSTx509Chain **chain, 
+                           STACK_OF(X509) *certstack, X509 *lastcert,
+                           char *capath, char *vomsdir)
+///
+/// Returns GRST_RET_OK if valid; caNl errors otherwise.
+///
+/// The GridSite version handles old and new style Globus proxies, and
+/// proxies derived from user certificates issued with "X509v3 Basic
+/// Constraints: CA:FALSE" (eg UK e-Science CA)
+///
+/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions
+///       (although via GRSTx509KnownCriticalExts() we can accept them.)
+{
+    canl_ctx cctx = NULL;
+    int err = 0;
+
+    cctx = canl_create_ctx();
+    if (cctx == NULL)
+        return GRST_RET_FAILED;
+
+    /* Use caNl to check the certificate*/
+    err = canl_verify_chain(cctx, NULL, certstack, capath);
+
+    /* Then fetch info about chain into grst structures */
+    GRSTx509ChainLoad(chain, certstack, lastcert, capath, vomsdir);
+
+    canl_free_ctx(cctx);
+
+    return err;
+}
+
+/* Check certificate chain for GSI proxy acceptability. */
+int GRSTx509CheckChain(int *first_non_ca, X509_STORE_CTX *store_ctx)
+/* Returns X509_V_OK/GRST_RET_OK if valid; caNl errors otherwise.
+   We do not check chain links between certs here: this is done by
+   GRST_check_issued/X509_check_issued in mod_ssl's ssl_engine_init.c */
+{
+    canl_err_code ret = 0;      /* Canl error code */
+
+    canl_ctx cctx = NULL;
+
+    /* Look for the store context */
+    if (!store_ctx)
+        return X509_V_ERR_INVALID_CA; /* TODO really not clever*/
+
+    cctx = canl_create_ctx();
+    if (cctx == NULL)
+        return GRST_RET_FAILED;
+
+    /* Verify chain using caNl, without openssl part */
+    ret = canl_verify_chain_wo_ossl(cctx, NULL, store_ctx);
+
+    canl_free_ctx(cctx);
+
+    return ret; /* this is also GRST_RET_OK, of course - by choice */
+}
+
+/// Example VerifyCallback routine
+int GRSTx509VerifyCallback (int ok, X509_STORE_CTX *ctx)
+{
+   int errnum   = X509_STORE_CTX_get_error(ctx);
+   int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+   int first_non_ca;
+
+#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34
+#endif
+
+   if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
+     {
+       if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx))
+           == GRST_RET_OK)
+         {
+           ok = TRUE;
+           errnum = X509_V_OK;
+           X509_STORE_CTX_set_error(ctx, errnum);
+         }                               
+     }
+   else if ((errdepth == 0)       && 
+            (errnum == X509_V_OK) && 
+            (GRSTx509CheckChain(&first_non_ca, ctx) != X509_V_OK)) ok = FALSE;
+   
+   
+   return ok;
+  
+// check this 
+   
+//   if (ok) return GRST_RET_OK;
+//   else    return GRST_RET_FAILED;
+}
+
+/// Get the VOMS attributes in the given extension
+int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen, 
+                         char *creds, time_t time1_time, time_t time2_time,
+                         X509_EXTENSION *ex, 
+                         char *ucuserdn, char *ucissuerdn, char *ucserial, 
+                         char *vomsdir)
+///
+/// Puts any VOMS credentials found into the Compact Creds string array
+/// starting at *creds. Always returns GRST_RET_OK - even for invalid
+/// credentials, which are just ignored.
+{
+#define MAXTAG 500
+#define GRST_ASN1_COORDS_FQAN    "-1-1-%d-1-7-1-2-1-2-%d"
+#define GRST_ASN1_COORDS_ISSUER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_TIME1   "-1-1-%d-1-6-1"
+#define GRST_ASN1_COORDS_TIME2   "-1-1-%d-1-6-2"
+   ASN1_OCTET_STRING *asn1data;
+   char              *asn1string, acissuerdn[200],
+                      dn_coords[200], fqan_coords[200], time1_coords[200],
+                      time2_coords[200], serial_coords[200];
+   long               asn1length;
+   int                lasttag=-1, itag, i, acnumber = 1;
+   char              *acissuerserial = NULL;
+   struct GRSTasn1TagList taglist[MAXTAG+1];
+   time_t             actime1, actime2, time_now;
+   ASN1_INTEGER       acissuerserialASN1;
+
+   asn1data   = X509_EXTENSION_get_data(ex);
+   asn1string = ASN1_STRING_data(asn1data);
+   asn1length = ASN1_STRING_length(asn1data);
+
+   GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag);
+
+   for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */
+      {
+        /* check names */
+      
+        snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_ISSUER_DN, acnumber);
+        if (GRSTasn1GetX509Name(acissuerdn, sizeof(acissuerdn), dn_coords,
+                       asn1string, taglist, lasttag) != GRST_RET_OK) break;
+
+        if ((GRSTx509NameCmp(ucuserdn, acissuerdn) != 0) && /* old */
+            (GRSTx509NameCmp(ucissuerdn, acissuerdn) != 0)) /* new */
+             continue;
+
+        /* check serial numbers */
+
+        snprintf(serial_coords, sizeof(serial_coords), 
+                 GRST_ASN1_COORDS_ISSUER_SERIAL, acnumber);
+
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, serial_coords);
+        
+        if (itag > -1) 
+          {
+            acissuerserialASN1.length = taglist[itag].length;
+            acissuerserialASN1.type   = V_ASN1_INTEGER;
+            acissuerserialASN1.data   = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+
+            acissuerserial = i2s_ASN1_INTEGER(NULL, &acissuerserialASN1);
+/*          
+            p = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+            
+            if (taglist[itag].length == 2)
+             acissuerserial = p[1] + p[0] * 0x100;
+            else if (taglist[itag].length == 3)
+             acissuerserial = p[2] + p[1] * 0x100 + p[0] * 0x10000;
+            else if (taglist[itag].length == 4)
+             acissuerserial = p[3] + p[2] * 0x100 + p[1] * 0x10000 +
+                              p[0] * 0x1000000;
+*/
+          }
+        
+        if (strcmp(acissuerserial, ucserial) != 0) continue;
+
+        if (GRSTx509VerifyVomsSig(&time1_time, &time2_time,
+                             asn1string, taglist, lasttag, vomsdir, acnumber)
+                             != GRST_RET_OK) continue;
+
+        snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber);
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords);
+        actime1 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+
+                                             taglist[itag].headerlength],
+                                 taglist[itag].length);
+        if (actime1 > time1_time) time1_time = actime1;
+
+        snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber);
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords);
+        actime2 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+
+                                             taglist[itag].headerlength],
+                                             taglist[itag].length);
+        if (actime2 < time2_time) time2_time = actime2;
+
+        time(&time_now);
+        if ((time1_time > time_now + 300) || (time2_time < time_now))
+               continue; /* expiration isnt invalidity ...? */
+
+        for (i=1; ; ++i)
+           {
+             snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i);
+             itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords);
+
+             if (itag > -1)
+               {
+                 if (*lastcred < maxcreds - 1)
+                   {
+                     ++(*lastcred);
+                     snprintf(&creds[*lastcred * (credlen + 1)], credlen+1,
+                           "VOMS %010lu %010lu 0 %.*s", 
+                           time1_time, time2_time, 
+                           taglist[itag].length,
+                           &asn1string[taglist[itag].start+
+                                       taglist[itag].headerlength]);
+                   }            
+               }
+             else break;
+           }
+      }
+      
+   return GRST_RET_OK;
+}
+
+/// Get the VOMS attributes in the extensions to the given cert stack
+int GRSTx509GetVomsCreds(int *lastcred, int maxcreds, size_t credlen, 
+                         char *creds, X509 *usercert, STACK_OF(X509) *certstack,
+                         char *vomsdir)
+///
+/// Puts any VOMS credentials found into the Compact Creds string array
+/// starting at *creds. Always returns GRST_RET_OK.
+{
+   int  i, j;
+   char s[80], *ucserial;
+   unsigned char  *ucuser, *ucissuer;
+   X509_EXTENSION *ex;
+   X509           *cert;
+   time_t          time1_time = 0, time2_time = 0, uctime1_time, uctime2_time;
+
+   uctime1_time = 
+        GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0);
+   uctime2_time =       
+        GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0);
+   ucuser =
+        X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0);
+   ucissuer =
+        X509_NAME_oneline(X509_get_issuer_name(usercert), NULL, 0);
+   ucserial = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(usercert));
+
+   for (j=sk_X509_num(certstack)-1; j >= 0; --j)
+    {
+      cert = sk_X509_value(certstack, j);
+
+      time1_time =
+          GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0);
+      uctime1_time = (time1_time > uctime1_time) ? time1_time:uctime1_time;
+
+      time2_time =
+          GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0);
+      uctime2_time = (time2_time < uctime2_time) ? time2_time:uctime2_time;
+
+      for (i=0; i < X509_get_ext_count(cert); ++i)
+         {
+           ex = X509_get_ext(cert, i);
+           OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1);
+
+           if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */
+             {
+               GRSTx509ParseVomsExt(lastcred, maxcreds, credlen, creds,
+                                 uctime1_time, uctime2_time,
+                                 ex, ucuser, ucissuer, ucserial,
+                                 vomsdir);
+             }
+         }
+    }
+
+   return GRST_RET_OK;
+}
+
+/// Turn a Compact Cred line into a GRSTgaclCred object
+GRSTgaclCred *GRSTx509CompactToCred(char *grst_cred)
+///
+/// Returns pointer to created GRSTgaclCred or NULL or failure.
+{
+   int       delegation;
+   char     *p, *encoded;
+   time_t    now, notbefore, notafter;
+   GRSTgaclCred *cred = NULL;
+
+   time(&now);
+
+   if (grst_cred == NULL) return NULL; /* just in case */
+
+   if (strncmp(grst_cred, "X509USER ", 9) == 0)
+     {
+       if ((sscanf(grst_cred, "X509USER %lu %lu %d", 
+                              &notbefore, &notafter, &delegation) == 3)
+            && (now >= notbefore)
+            && (now <= notafter)
+            && (p = index(grst_cred, ' '))
+            && (p = index(++p, ' '))
+            && (p = index(++p, ' '))
+            && (p = index(++p, ' ')))
+         {
+           encoded = GRSThttpUrlMildencode(&p[1]);
+           cred = GRSTgaclCredCreate("dn:", encoded);
+           free(encoded);
+           GRSTgaclCredSetDelegation(cred, delegation);
+         }
+
+       return cred;
+     }
+
+   if (strncmp(grst_cred, "VOMS ", 5) == 0)
+     {
+       if ((sscanf(grst_cred, "VOMS %lu %lu %d",
+                              &notbefore, &notafter, &delegation) == 3)
+            && (now >= notbefore)
+            && (now <= notafter)
+            && (p = index(grst_cred, ' '))
+            && (p = index(++p, ' '))
+            && (p = index(++p, ' '))
+            && (p = index(++p, ' ')))
+         {
+           /* include /VO/group/subgroup/Role=role/Capability=cap */
+
+           if (p[1] != '/') return NULL; /* must begin with / */
+
+           encoded = GRSThttpUrlMildencode(&p[1]);
+           cred = GRSTgaclCredCreate("fqan:", encoded);
+           free(encoded);
+           GRSTgaclCredSetDelegation(cred, delegation);
+         }
+
+       return cred;
+     }
+
+   return NULL; /* dont recognise this credential type */
+}
+
+/// Get the credentials in an X509 cert/GSI proxy, including any VOMS
+int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen, 
+                         char *creds, STACK_OF(X509) *certstack, char *vomsdir, 
+                         X509 *peercert)
+///
+/// Credentials are placed in Compact Creds string array at *creds.
+///
+/// Function returns GRST_RET_OK on success, or GRST_RET_FAILED if
+/// some inconsistency found in certificate.
+{   
+   int   i, delegation = 0;
+   char  credtemp[credlen+1];
+   X509 *cert, *usercert = NULL, *gsiproxycert = NULL;
+
+   *lastcred = -1;
+
+   for (i = sk_X509_num(certstack) - 1; i >= 0; --i) 
+      {
+         cert = sk_X509_value(certstack, i);
+
+         if (usercert != NULL) 
+           {           /* found a (GSI proxy) cert after the user cert */
+             gsiproxycert = cert;
+             ++delegation;
+           }
+           
+         if ((usercert == NULL) && 
+             (i < sk_X509_num(certstack) - 1) &&
+             (GRSTx509IsCA(cert) != GRST_RET_OK)) usercert = cert;
+                                          /* found the 1st non-CA cert */
+      }
+
+   if (peercert != NULL)
+     {
+       if (usercert != NULL) /* found a (GSI proxy) cert after user cert */
+         {
+           gsiproxycert = peercert;
+           ++delegation;
+         }
+
+       if ((usercert == NULL) && 
+           (GRSTx509IsCA(peercert) != GRST_RET_OK)) usercert = peercert;
+                                          /* found the 1st non-CA cert */
+     }
+
+   if ((usercert == NULL) /* if no usercert ("EEC"), we're not interested */
+       ||
+       (snprintf(credtemp, credlen+1, "X509USER %010lu %010lu %d %s",
+          GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0),
+          GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0),
+          delegation,
+     X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0)) >= credlen+1)
+       || 
+       (*lastcred >= maxcreds-1))
+     {
+       *lastcred = -1;  /* just in case the caller looks at it */
+       return GRST_RET_FAILED; /* tell caller that things didn't work out */
+     }
+
+   ++(*lastcred);
+   strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
+
+   if ((gsiproxycert != NULL) 
+       &&
+       (snprintf(credtemp, credlen+1, "GSIPROXY %010lu %010lu %d %s",
+     GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(gsiproxycert)),0), 
+     GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(gsiproxycert)),0),
+     delegation,
+  X509_NAME_oneline(X509_get_subject_name(gsiproxycert), NULL, 0)) < credlen+1)
+       &&
+       (*lastcred < maxcreds-1))
+     {
+       ++(*lastcred);
+       strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
+
+       GRSTx509GetVomsCreds(lastcred, maxcreds, credlen, creds, 
+                            usercert, certstack, vomsdir);
+
+     }
+         
+   return GRST_RET_OK;
+}
+
+/// Find proxy file name of the current user
+char *GRSTx509FindProxyFileName(void)
+///
+/// Return a string with the proxy file name or NULL if not present.
+/// This function does not check if the proxy has expired.
+{
+  char *p;
+  
+  p = getenv("X509_USER_PROXY");
+  
+  if (p != NULL) return strdup(p);
+  
+  p = malloc(sizeof("/tmp/x509up_uXYYYXXXYYY"));
+  
+  sprintf(p, "/tmp/x509up_u%d", getuid());  
+
+  return p;
+}
+
+static void mpcerror(FILE *debugfp, char *msg)
+{
+  if (debugfp != NULL)
+    {
+      fputs(msg, debugfp);
+      ERR_print_errors_fp(debugfp);
+    }
+}
+
+/// Make a GSI Proxy chain from a request, certificate and private key
+int GRSTx509MakeProxyCert(char **proxychain, FILE *debugfp, 
+                          char *reqtxt, char *cert, char *key, int minutes)
+///
+/// The proxy chain is returned in *proxychain. If debugfp is non-NULL,
+/// errors are output to that file pointer. The proxy will expired in
+/// the given number of minutes starting from the current time.
+{
+  char *ptr = NULL, *certchain = NULL;
+  static unsigned char pci_str[] = { 0x30, 0x0c, 0x30, 0x0a, 0x06, 0x08,
+    0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x15, 0x01, 0 },
+    kyu_str[] = { 0x03, 0x02, 0x03, 
+                  X509v3_KU_DIGITAL_SIGNATURE | 
+                  X509v3_KU_KEY_ENCIPHERMENT  | 
+                  X509v3_KU_KEY_AGREEMENT, 
+                  0 };
+  int i = 0, ncerts = 0, any_rfc_proxies = 0;
+  long ptrlen = 0;
+    EVP_PKEY *pkey = NULL, *signer_pkey = NULL;
+    X509 *certs[GRST_MAX_CHAIN_LEN];
+    X509_REQ *req = NULL;
+    ASN1_OBJECT *pci_obj = NULL, *kyu_obj = NULL;
+    ASN1_OCTET_STRING *pci_oct = NULL, *kyu_oct = NULL;
+    FILE *fp = NULL;
+    BIO *reqmem = NULL, *certmem = NULL;
+    canl_ctx ctx = NULL;
+    int retval = 1, ret = 0;
+    canl_cred proxy_cert = NULL, signer = NULL;
+
+    ctx = canl_create_ctx();
+    if (ctx == NULL) {
+        fprintf(debugfp, "GRSTx509MakeProxyCert(): Failed to create"
+               " caNl library context\n");
+        return 1;
+    }
+    
+    /* read in the request */
+    reqmem = BIO_new(BIO_s_mem());
+    BIO_puts(reqmem, reqtxt);
+
+    if (!(req = PEM_read_bio_X509_REQ(reqmem, NULL, NULL, NULL))) {
+        fprintf(debugfp, "GRSTx509MakeProxyCert(): error reading"
+                " request from BIO memory\n");
+        goto end;
+    }
+    BIO_free(reqmem);
+    reqmem = NULL;
+    
+    /* load req into canl cred. context */
+    ret = canl_cred_new(ctx, &proxy_cert);
+    if (ret) {
+        fprintf(debugfp, "GRSTx509MakeProxyCert(): caNl cred. context"
+                " cannot be created: %s\n", canl_get_error_message(ctx));
+        goto end;
+    }
+    ret = canl_cred_load_req(ctx, proxy_cert, req);
+    if (ret) {
+        fprintf(stderr, "GRSTx509MakeProxyCert(): Failed to load certificate "
+                "request container: %s\n", canl_get_error_message(ctx));
+        goto end;
+    }
+
+    /*TODO MP Should proxy sognature verification be in caN???*/
+    /* verify signature on the request */
+    if (!(pkey = X509_REQ_get_pubkey(req))) {
+        mpcerror(debugfp, "GRSTx509MakeProxyCert(): error getting public"
+                " key from request\n");
+    }
+
+    if (X509_REQ_verify(req, pkey) != 1) {
+        mpcerror(debugfp, "GRSTx509MakeProxyCert(): error verifying signature"
+                " on certificate\n");
+        goto end;
+    }
+    EVP_PKEY_free(pkey);
+    pkey = NULL;
+    X509_REQ_free(req);
+    req = NULL;
+
+    /* read in the signing certificate */
+    if (!(fp = fopen(cert, "r"))) {
+        mpcerror(debugfp, "GRSTx509MakeProxyCert(): error opening"
+                " signing certificate file\n");
+        goto end;
+    }
+    /* load req signer cert into caNl cred. context*/
+    ret = canl_cred_new(ctx, &signer);
+    if (ret) {
+        fprintf(debugfp, "GRSTx509MakeProxyCert(): caNl cred. context"
+                " cannot be created: %s\n", canl_get_error_message(ctx));
+        goto end;
+    }
+
+    for (ncerts = 1; ncerts < GRST_MAX_CHAIN_LEN; ++ncerts)
+        if ((certs[ncerts] = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL)
+            break;
+    /* zeroth cert will be new proxy cert */
+    if (ncerts == 1) {
+        mpcerror(debugfp, "GRSTx509MakeProxyCert(): error reading"
+                " signing certificate file\n");
+        goto end;
+    }
+    fclose(fp);
+    fp = NULL;
+
+    /* read in the signer certificate*/
+    ret = canl_cred_load_cert_file(ctx, signer, cert);
+    if (ret){
+        fprintf(stderr, "[DELEGATION] Cannot load signer's certificate"
+                ": %s\n", canl_get_error_message(ctx));
+        goto end;
+    }
+
+
+    /* read in the signer private key */
+    if (!(fp = fopen(key, "r"))) {
+        mpcerror(debugfp, "GRSTx509MakeProxyCert(): error reading" 
+                " signing private key file\n");
+        goto end;
+    }    
+
+    if (!(signer_pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL))) {
+        mpcerror(debugfp, "GRSTx509MakeProxyCert(): error reading "
+                "signing private key in file\n");
+        goto end;
+    }    
+    fclose(fp);
+    fp = NULL;
+    canl_cred_load_priv_key(ctx, signer, signer_pkey);
+    EVP_PKEY_free(signer_pkey);
+    signer_pkey = NULL;
+
+    /*TODO MP Compare with VOMS in caNl (orig gridsite proxyver = 2L)*/
+    /* set version number for the certificate (X509v3) and the serial number
+       We now use 2 = v3 for the GSI proxy, rather than the old Globus
+       behaviour of 3 = v4. See Savannah Bug #53721 */
+
+    /* TODO MP what about serial numbers? Should caNl provide API function for
+     * setting custom serial number?*/
+    /*  ASN1_INTEGER_set(X509_get_serialNumber(certs[0]), (long) time(NULL));*/
+
+    /* TODO MP use some default timeout?*/
+    if (minutes >= 0) {
+        ret = canl_cred_set_lifetime(ctx, proxy_cert, 60 * minutes);
+        if (ret)
+            fprintf(debugfp, "[DELEGATION] Failed set new cert lifetime"
+                    ": %s\n", canl_get_error_message(ctx));
+    }
+
+    /* go through chain making sure this proxy is not longer lived */
+
+    pci_obj = OBJ_txt2obj(GRST_PROXYCERTINFO_OID, 0);
+
+    /* TODO MP is this necessary? caNl test if new proxy timeout
+     * is longer than signer cert proxy timeout */
+    for (i=1; i < ncerts; ++i)
+        if (X509_get_ext_by_OBJ(certs[i], pci_obj, -1) > 0) 
+            any_rfc_proxies = 1;
+
+   /* if any earlier proxies are RFC 3820, then new proxy must be 
+      an RFC 3820 proxy too with the required extensions */ 
+    if (any_rfc_proxies) {
+        X509_EXTENSION *pci_ex = NULL, *kyu_ex = NULL;
+        /* key usage */
+        kyu_obj = OBJ_txt2obj(GRST_KEYUSAGE_OID, 0);
+        kyu_ex = X509_EXTENSION_new();
+
+        X509_EXTENSION_set_object(kyu_ex, kyu_obj);
+        ASN1_OBJECT_free(kyu_obj);
+        X509_EXTENSION_set_critical(kyu_ex, 1);
+
+        kyu_oct = ASN1_OCTET_STRING_new();
+        ASN1_OCTET_STRING_set(kyu_oct, kyu_str, strlen(kyu_str));
+        X509_EXTENSION_set_data(kyu_ex, kyu_oct);
+        ASN1_OCTET_STRING_free(kyu_oct);
+
+        canl_cred_set_extension(ctx, proxy_cert, kyu_ex);
+        X509_EXTENSION_free(kyu_ex);
+
+        /* proxy certificate info */
+        pci_ex = X509_EXTENSION_new();
+        X509_EXTENSION_set_object(pci_ex, pci_obj);
+        X509_EXTENSION_set_critical(pci_ex, 1);
+
+        pci_oct = ASN1_OCTET_STRING_new();
+        ASN1_OCTET_STRING_set(pci_oct, pci_str, strlen(pci_str));
+        X509_EXTENSION_set_data(pci_ex, pci_oct);
+        ASN1_OCTET_STRING_free(pci_oct);
+
+        canl_cred_set_extension(ctx, proxy_cert, kyu_ex);
+        X509_EXTENSION_free(pci_ex);
+    }
+    ASN1_OBJECT_free(pci_obj);
+    pci_obj = NULL;
+
+    /* Sign the proxy */
+    ret = canl_cred_sign_proxy(ctx, signer, proxy_cert);
+    if (ret){
+        fprintf(stderr, "[DELEGATION] Cannot sign new proxy"
+                ": %s\n", canl_get_error_message(ctx));
+        goto end;
+    }
+
+    ret = canl_cred_save_cert(ctx, proxy_cert, &certs[0]);
+    if (ret) {
+        fprintf(stderr, "GRSTx509MakeProxyCert(): Cannot save new cert file"
+                ": %s\n", canl_get_error_message(ctx));
+        goto end;
+    }
+    canl_free_ctx(ctx);
+    ctx = NULL;
+
+    /* store the completed certificate chain */
+    certchain = strdup("");
+    for (i=0; i < ncerts; ++i) {
+        certmem = BIO_new(BIO_s_mem());
+
+        if (PEM_write_bio_X509(certmem, certs[i]) != 1) {
+            mpcerror(debugfp, "GRSTx509MakeProxyCert(): error writing"
+                    " certificate to memory BIO\n");            
+            goto end;
+        }
+        ptrlen = BIO_get_mem_data(certmem, &ptr);
+        certchain = realloc(certchain, strlen(certchain) + ptrlen + 1);
+        strncat(certchain, ptr, ptrlen);
+
+        BIO_free(certmem);
+        certmem = NULL;
+        X509_free(certs[i]);
+        certs[i] = NULL;
+    }
+    ncerts = 0;
+    *proxychain = certchain;  
+    retval = GRST_RET_OK;
+
+end:
+    if (reqmem)
+        BIO_free(reqmem);
+    if (pkey)
+        EVP_PKEY_free(pkey);
+    if (signer_pkey)
+        EVP_PKEY_free(signer_pkey);
+    if (req)
+        X509_REQ_free(req);
+    if (pci_obj)
+        ASN1_OBJECT_free(pci_obj);
+    if (fp) {
+        fclose(fp);
+        fp = NULL;
+    }
+    if (certmem)
+        BIO_free(certmem);
+    if (ctx)
+        canl_free_ctx(ctx);
+    for (i=0; i < ncerts; ++i) {
+        if (certs[i])
+            X509_free(certs[i]);
+    }
+    if (proxy_cert)
+        canl_cred_free(ctx, proxy_cert);
+    if (signer)
+        canl_cred_free(ctx, signer);
+    return retval;
+}
+
+/// Find a proxy file in the proxy cache
+char *GRSTx509CachedProxyFind(char *proxydir, char *delegation_id, 
+                              char *user_dn)
+///
+/// Returns the full path and file name of proxy file associated
+/// with given delegation ID and user DN.
+///
+/// Return a pointer to a malloc'd string with the full path of the 
+/// proxy file corresponding to the given delegation_id, or NULL
+/// if not found.
+{
+  char *user_dn_enc, *proxyfile;
+  struct stat statbuf;
+
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+  asprintf(&proxyfile, "%s/%s/%s/userproxy.pem",
+           proxydir, user_dn_enc, delegation_id);
+           
+  free(user_dn_enc);
+
+  if ((stat(proxyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode))
+    {
+      free(proxyfile);
+      return NULL;
+    }
+    
+  return proxyfile;
+}
+
+/// Find a temporary proxy private key file in the proxy cache
+char *GRSTx509CachedProxyKeyFind(char *proxydir, char *delegation_id, 
+                                 char *user_dn)
+///
+/// Returns the full path and file name of the private key file associated
+/// with given delegation ID and user DN.
+///
+/// Return a pointer to a malloc'd string with the full path of the 
+/// private proxy key corresponding to the given delegation_id, or NULL
+/// if not found.
+{
+  char *user_dn_enc, *prvkeyfile;
+  struct stat statbuf;
+
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+  asprintf(&prvkeyfile, "%s/cache/%s/%s/userkey.pem",
+           proxydir, user_dn_enc, delegation_id);
+           
+  free(user_dn_enc);
+
+  if ((stat(prvkeyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode))
+    {
+      free(prvkeyfile);
+      return NULL;
+    }
+    
+  return prvkeyfile;
+}
+
+static void mkdir_printf(mode_t mode, char *fmt, ...)
+{
+  int   ret;
+  char *path;
+  va_list ap;
+  
+  va_start(ap, fmt);
+  vasprintf(&path, fmt, ap);
+  va_end(ap);
+
+  ret = mkdir(path, mode);
+
+  free(path);
+}
+
+/// Create a X.509 request for a GSI proxy and its private key
+int GRSTx509CreateProxyRequest(char **reqtxt, char **keytxt, char *ocspurl)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise. Request string
+/// and private key are PEM encoded strings
+{
+    char            *ptr = 0;
+    size_t           ptrlen = 0;
+    EVP_PKEY        *pkey = NULL;
+    X509_REQ        *req = NULL;
+    BIO             *reqmem = NULL, *keymem = NULL;
+    canl_cred proxy_bob = NULL;
+    int retval = 0;
+    canl_ctx c_ctx = NULL;
+    int ret = 0;
+
+    /*Make new canl_ctx, TODO MP how to initialize it only once?*/
+    c_ctx = canl_create_ctx();
+    if (c_ctx == NULL) {
+        return 10; /* TODO MP we can use caNl error codes now */
+    }
+
+    ret = canl_cred_new(c_ctx, &proxy_bob);
+    if (ret){
+        retval = 11;
+        goto end;
+    }
+
+    /*use caNl to generate a X509 request*/
+    ret = canl_cred_new_req(c_ctx, proxy_bob, GRST_KEYSIZE);
+    if (ret) {
+        retval = 12;
+        goto end;
+    }
+
+    ret = canl_cred_save_req(c_ctx, proxy_bob, &req);
+    if (ret) {
+        retval = 13;
+        goto end;
+    }
+
+    /*Convert request into a string*/
+    reqmem = BIO_new(BIO_s_mem());
+    PEM_write_bio_X509_REQ(reqmem, req);
+    ptrlen = BIO_get_mem_data(reqmem, &ptr);
+
+    *reqtxt = malloc(ptrlen + 1);
+    memcpy(*reqtxt, ptr, ptrlen);
+    (*reqtxt)[ptrlen] = '\0';
+
+    /* Put keypair in a PEM string */
+    ret = canl_cred_save_priv_key(c_ctx, proxy_bob, &pkey);
+    if (ret){
+        retval = 15;
+        goto end;
+    }
+    keymem = BIO_new(BIO_s_mem());
+    if (!PEM_write_bio_PrivateKey(keymem, pkey, NULL, NULL, 0, NULL, NULL))
+    {
+        BIO_free(keymem);
+        return 3;
+    }
+
+    ptrlen = BIO_get_mem_data(keymem, &ptr);
+    *keytxt = malloc(ptrlen + 1);
+    memcpy(*keytxt, ptr, ptrlen);
+    (*keytxt)[ptrlen] = '\0';
+
+end:
+    if (proxy_bob)
+        canl_cred_free(c_ctx, proxy_bob);
+    if (pkey)
+        EVP_PKEY_free(pkey);
+    if (reqmem)
+        BIO_free(reqmem);
+    if (keymem)
+        BIO_free(keymem);
+    if (req)
+        X509_REQ_free(req);
+    if (c_ctx)
+        canl_free_ctx(c_ctx);
+
+    return retval;
+}
+
+/// Make and store a X.509 request for a GSI proxy
+int GRSTx509MakeProxyRequest(char **reqtxt, char *proxydir, 
+                             char *delegation_id, char *user_dn)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise. Request string
+/// is PEM encoded, and the key is stored in the temporary cache under
+/// proxydir
+{
+    char *prvkeyfile = NULL, *ptr = NULL, *user_dn_enc = NULL;
+    size_t ptrlen = 0;
+    FILE *fp = NULL;
+    EVP_PKEY *pkey = NULL;
+    BIO *reqmem = NULL;
+    canl_ctx c_ctx = NULL;
+    canl_cred proxy_bob = NULL;
+    X509_REQ *req = NULL;
+    int retval = GRST_RET_OK;
+    int ret = 0;
+
+    if (strcmp(user_dn, "cache") == 0)
+        return GRST_RET_FAILED;
+
+    user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+    /* create directories if necessary */
+
+    mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, 
+            "%s/cache",       proxydir);
+    mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, 
+            "%s/cache/%s",    proxydir, user_dn_enc);
+    mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, 
+            "%s/cache/%s/%s", proxydir, user_dn_enc, delegation_id);
+
+    /* make the new proxy private key */
+
+    asprintf(&prvkeyfile, "%s/cache/%s/%s/userkey.pem",
+            proxydir, user_dn_enc, delegation_id);
+
+    if (prvkeyfile == NULL){
+        free(user_dn_enc);
+        return GRST_RET_FAILED;
+    }
+
+    /*Make new canl_ctx, TODO MP how to initialize it only once?*/
+    c_ctx = canl_create_ctx();
+    if (c_ctx == NULL) {
+        free(prvkeyfile);
+        return 10; /* TODO MP we can use caNl error codes now */
+    }
+
+    ret = canl_cred_new(c_ctx, &proxy_bob);
+    if (ret){
+        retval = 11;
+        goto end;
+    }
+
+    /*use caNl to generate X509 request*/
+    ret = canl_cred_new_req(c_ctx, proxy_bob, GRST_KEYSIZE);
+    if (ret) {
+        retval = 12;
+        goto end;
+    }
+
+    ret = canl_cred_save_req(c_ctx, proxy_bob, &req);
+    if (ret) {
+        retval = 13;
+        goto end;
+    }
+
+    if ((fp = fopen(prvkeyfile, "w")) == NULL) 
+    {
+        retval = 14;
+        goto end;
+    }
+
+    /*Save private key in cache*/  
+    chmod(prvkeyfile, S_IRUSR | S_IWUSR);
+    free(prvkeyfile);
+    prvkeyfile = NULL;
+    free(user_dn_enc);
+    user_dn_enc = NULL;
+
+    ret = canl_cred_save_priv_key(c_ctx, proxy_bob, &pkey);
+    if (ret) {
+        retval = 15;
+        goto end;
+    }
+
+    if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) {
+        retval = 3;
+        goto end;
+    }
+
+    if (fclose(fp) != 0){
+        retval = 4;
+        goto end;
+    }
+
+    /* TODO MP ask. "Dummy" vs "proxy" in sslutils.c from Voms 
+       ent=X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"), 
+       MBSTRING_ASC, "Dummy", -1);
+       TODO MD5 vs SHA1 defaults.
+     */  
+
+    /*Convert request into string*/
+    reqmem = BIO_new(BIO_s_mem());
+    PEM_write_bio_X509_REQ(reqmem, req);
+    ptrlen = BIO_get_mem_data(reqmem, &ptr);
+
+    *reqtxt = malloc(ptrlen + 1);
+    memcpy(*reqtxt, ptr, ptrlen);
+    (*reqtxt)[ptrlen] = '\0';
+end:
+    if (proxy_bob)
+        canl_cred_free(c_ctx, proxy_bob);
+    if (reqmem)
+        BIO_free(reqmem);
+    if (req)
+        X509_REQ_free(req);
+    if (prvkeyfile)
+        free(prvkeyfile);
+    if (user_dn_enc)
+        free(user_dn_enc);
+    if (c_ctx)
+        canl_free_ctx(c_ctx);
+
+  return retval;
+}
+
+/// Destroy stored GSI proxy files
+int GRSTx509ProxyDestroy(char *proxydir, char *delegation_id, char *user_dn)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise.
+/// (Including GRST_RET_NO_SUCH_FILE if the private key or cert chain
+///  were not found.)
+{
+  int              ret = GRST_RET_OK;
+  char             *filename, *user_dn_enc;
+
+  if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+    
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+  /* proxy file */
+  
+  asprintf(&filename, "%s/%s/%s/userproxy.pem",
+           proxydir, user_dn_enc, delegation_id);
+
+  if (filename == NULL)  
+    {
+      free(user_dn_enc);
+      return GRST_RET_FAILED;
+    }
+
+  if (unlink(filename) != 0) ret = GRST_RET_NO_SUCH_FILE;  
+  free(filename);
+
+  /* voms file */
+  
+  asprintf(&filename, "%s/%s/%s/voms.attributes",
+           proxydir, user_dn_enc, delegation_id);
+
+  if (filename == NULL)  
+    {
+      free(user_dn_enc);
+      return GRST_RET_FAILED;
+    }
+
+  unlink(filename);
+  free(filename);
+  
+  return ret;
+}
+
+/// Get start and finish validity times of stored GSI proxy file
+int GRSTx509ProxyGetTimes(char *proxydir, char *delegation_id, char *user_dn, 
+                          time_t *start, time_t *finish)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise.
+/// (Including GRST_RET_NO_SUCH_FILE if the cert chain was not found.)
+{
+  char  *docroot, *filename, *user_dn_enc;
+  FILE  *fp;
+  X509  *cert;
+
+  if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+    
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+  
+  asprintf(&filename, "%s/%s/%s/userproxy.pem",
+           proxydir, user_dn_enc, delegation_id);
+           
+  free(user_dn_enc);
+
+  if (filename == NULL) return GRST_RET_FAILED;
+
+  fp = fopen(filename, "r");
+  free(filename);
+  
+  if (fp == NULL) return GRST_RET_NO_SUCH_FILE;
+
+  cert = PEM_read_X509(fp, NULL, NULL, NULL); /* first cert is X.509 PC */
+
+  fclose(fp);
+  
+  *start  = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0);
+  *finish = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0);
+
+  X509_free(cert);
+  
+  return GRST_RET_OK;
+}
+
+/// Create a stack of X509 certificate from a PEM-encoded string
+int GRSTx509StringToChain(STACK_OF(X509) **certstack, char *certstring)
+///
+/// Creates a dynamically allocated stack of X509 certificate objects
+/// by walking through the PEM-encoded X509 certificates.
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise.
+{   
+  STACK_OF(X509_INFO) *sk=NULL;
+  BIO *certbio;
+  X509_INFO *xi;
+
+  *certstack = sk_X509_new_null();
+  if (*certstack == NULL) return GRST_RET_FAILED;
+
+  certbio = BIO_new_mem_buf(certstring, -1);
+  
+  if (!(sk=PEM_X509_INFO_read_bio(certbio, NULL, NULL, NULL)))
+    {
+      BIO_free(certbio);
+      sk_X509_INFO_free(sk);
+      sk_X509_free(*certstack);
+      return GRST_RET_FAILED;
+    }
+      
+  while (sk_X509_INFO_num(sk))
+       {
+         xi=sk_X509_INFO_shift(sk);
+         if (xi->x509 != NULL)
+           {
+             sk_X509_push(*certstack, xi->x509);
+             xi->x509=NULL;
+           }
+         X509_INFO_free(xi);
+       }
+       
+   if (!sk_X509_num(*certstack))
+     {
+       BIO_free(certbio);
+       sk_X509_INFO_free(sk);
+       sk_X509_free(*certstack);
+       return GRST_RET_FAILED;
+     }
+
+   BIO_free(certbio);
+   sk_X509_INFO_free(sk);
+   
+   return GRST_RET_OK;
+}
+
+/// Returns a Delegation ID based on hash of GRST_CRED_0, ...
+char *GRSTx509MakeDelegationID(void)
+///
+/// Returns a malloc'd string with Delegation ID made by SHA1-hashing the
+/// values of the compact credentials exported by mod_gridsite
+{ 
+  unsigned char hash_delegation_id[EVP_MAX_MD_SIZE];        
+  int  i, delegation_id_len;
+  char cred_name[14], *cred_value, *delegation_id;
+  const EVP_MD *m;
+  EVP_MD_CTX ctx;
+
+  OpenSSL_add_all_digests();
+
+  m = EVP_sha1();
+  if (m == NULL) return NULL;
+
+  EVP_DigestInit(&ctx, m);
+
+  for (i=0; i <= 999; ++i)
+     {
+       snprintf(cred_name, sizeof(cred_name), "GRST_CRED_%d", i);       
+       if ((cred_value = getenv(cred_name)) == NULL) break;
+       
+       EVP_DigestUpdate(&ctx, cred_value, strlen(cred_value));
+     }
+     
+  EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len);
+
+  delegation_id = malloc(17);
+
+  for (i=0; i <=7; ++i)
+   sprintf(&delegation_id[i*2], "%02x", hash_delegation_id[i]);
+
+  delegation_id[16] = '\0';
+
+  return delegation_id;
+}
+
+#if 0
+/// Return the short file name for the given delegation_id and user_dn
+char *GRSTx509MakeProxyFileName(char *delegation_id,
+                                STACK_OF(X509) *certstack)
+///
+/// Returns a malloc'd string with the short file name (no paths) that
+/// derived from the hashed delegation_id and user_dn
+///
+/// File name is SHA1_HASH(DelegationID)+"-"+SHA1_HASH(DN) where DN
+/// is DER encoded version of user_dn with any trailing CN=proxy removed
+/// Hashes are the most significant 8 bytes, in lowercase hexadecimal.
+{ 
+  int        i, depth, prevIsCA = 1, IsCA, hash_name_len, delegation_id_len,
+                 der_name_len;
+  unsigned char *der_name, *buf, hash_name[EVP_MAX_MD_SIZE],
+                 hash_delegation_id[EVP_MAX_MD_SIZE],
+                 filename[34];
+  X509_NAME *subject_name;
+  X509      *cert;
+  const EVP_MD *m;
+  EVP_MD_CTX ctx;
+
+  depth = sk_X509_num(certstack);  
+  
+  for (i=depth-1; i >= 0; --i)
+        /* loop through the proxy chain starting at CA end */
+     {
+       if (cert = sk_X509_value(certstack, i))
+         {
+           IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+
+           if (prevIsCA && !IsCA) /* the full certificate of the user */
+             {
+               break;
+             }
+         }
+     }
+
+  if (i < 0) return NULL; /* not found: something wrong with the chain */
+
+  if ((subject_name = X509_get_subject_name(cert)) == NULL) return NULL;
+  
+  der_name_len = i2d_X509_NAME(X509_get_subject_name(cert), NULL);
+  if (der_name_len == 0) return NULL;
+  
+  buf = OPENSSL_malloc(der_name_len);
+  der_name = buf;
+
+
+  if (!i2d_X509_NAME(X509_get_subject_name(cert), &der_name))
+    {
+      OPENSSL_free(der_name);
+      return NULL;
+    }
+
+  OpenSSL_add_all_digests();
+
+  m = EVP_sha1();
+  if (m == NULL)
+    {
+      OPENSSL_free(der_name);
+      return NULL;
+    }
+
+
+  EVP_DigestInit(&ctx, m);
+  EVP_DigestUpdate(&ctx, delegation_id, strlen(delegation_id));
+  EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len);
+
+  /* lots of nasty hard coded numbers: 
+     "8bytes/16chars delegation ID" + "-" + "8bytes/16chars DN" */
+
+  for (i=0; i <=7; ++i)
+   sprintf(&filename[i*2], "%02x", hash_delegation_id[i]);
+
+  filename[16] = '-';
+
+  EVP_DigestInit(&ctx, m);
+  EVP_DigestUpdate(&ctx, buf, der_name_len);
+  EVP_DigestFinal(&ctx, hash_name, &hash_name_len);
+
+  for (i=0; i <=7; ++i)
+   sprintf(&filename[17 + i*2], "%02x", hash_name[i]);
+
+  return strdup(filename);
+}
+#endif
+
+/// Store a GSI proxy chain in the proxy cache, along with the private key
+int GRSTx509CacheProxy(char *proxydir, char *delegation_id, 
+                                       char *user_dn, char *proxychain)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise. The existing
+/// private key with the same delegation ID and user DN is moved out of
+/// the temporary cache.
+{
+    int ret = 0;
+    int retval = GRST_RET_FAILED;
+    char *user_dn_enc, *prvkeyfile, *proxyfile;
+    STACK_OF(X509) *certstack;
+    canl_ctx c_ctx = NULL;
+    canl_cred proxy_bob = NULL;
+
+    if (strcmp(user_dn, "cache") == 0)
+        return GRST_RET_FAILED;
+
+
+    /* get the X509 stack */
+    if (GRSTx509StringToChain(&certstack, proxychain) != GRST_RET_OK){
+        return GRST_RET_FAILED;/* TODO MP we can use caNl error codes now */
+    }
+    /*Make new canl_ctx, TODO MP how to initialize it only once?*/
+    c_ctx = canl_create_ctx();
+    if (c_ctx == NULL) 
+        goto end;
+
+    ret = canl_cred_new(c_ctx, &proxy_bob);
+    if (ret){
+        goto end; /* TODO MP we can use caNl error codes now */
+    }
+
+    /*Use caNl to load certstack into proxy_bob*/
+    ret = canl_cred_load_chain(c_ctx, proxy_bob, certstack);
+    if (ret){
+        goto end;
+    }
+
+    /* create directories if necessary, and set proxy filename */
+    user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+    mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, 
+            "%s/%s",    proxydir, user_dn_enc);
+    mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, 
+            "%s/%s/%s", proxydir, user_dn_enc, delegation_id);
+
+    asprintf(&proxyfile, "%s/%s/%s/userproxy.pem",
+            proxydir, user_dn_enc, delegation_id);
+    free(user_dn_enc);
+    user_dn_enc = NULL;
+
+    /* find the existing private key file */
+    prvkeyfile = GRSTx509CachedProxyKeyFind(proxydir, delegation_id, user_dn);
+    if (prvkeyfile == NULL)
+        goto end;
+
+    /* insert proxy private key into canl structure,
+       read from private key file */
+    ret = canl_cred_load_priv_key_file(c_ctx, proxy_bob, prvkeyfile, NULL, NULL);
+    if (ret)
+        goto end;
+    unlink(prvkeyfile);
+    free(prvkeyfile);
+    prvkeyfile = NULL;
+
+    ret = canl_cred_save_proxyfile(c_ctx, proxy_bob, proxyfile);
+    if (ret)
+        goto end;
+
+    retval = GRST_RET_OK;
+
+end:
+    if (proxyfile)
+        free(proxyfile);
+    if (proxy_bob)
+        canl_cred_free(c_ctx, proxy_bob);
+    if (certstack)
+        sk_X509_free(certstack);
+    if (prvkeyfile)
+        free(prvkeyfile);
+    if (user_dn_enc)
+        free(user_dn_enc);
+    if (c_ctx)
+        canl_free_ctx(c_ctx);
+
+    return retval;
+}
index cbd9eb0..6d60720 100644 (file)
@@ -895,7 +895,7 @@ int do_finds(char *sources[],
   struct sockaddr from;
   socklen_t fromlen;
 #define MAXBUF 8192  
-  char *request, response[MAXBUF], *p;
+  char *request, response[MAXBUF];
   GRSThtcpMessage msg;
   struct timeval start_timeval, wait_timeval;
   struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS];
@@ -1014,7 +1014,7 @@ int translate_sitecast_url(char **source_ptr,
   struct sockaddr from;
   socklen_t fromlen;
 #define MAXBUF 8192  
-  char *request, response[MAXBUF], *p;
+  char *request, response[MAXBUF];
   GRSThtcpMessage msg;
   struct timeval start_timeval, wait_timeval;
   struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS];
index 031a502..64a9bdb 100644 (file)
@@ -265,12 +265,25 @@ int main(int argc, char *argv[])
         
       fd = mkstemp(keycert);
       ofp = fdopen(fd, "w");
+      if (!ofp)
+      {
+          printf(stderr, "Cannot open tmp file for the key\n");
+          return 1;
+      }
           
       ifp = fopen(key, "r");          
+      {
+          printf(stderr, "Cannot open the file with the key\n");
+          return 1;
+      }
       while ((c = fgetc(ifp)) != EOF) fputc(c, ofp);          
       fclose(ifp);
           
       ifp = fopen(cert, "r");          
+      {
+          printf(stderr, "Cannot open the file with the cert\n");
+          return 1;
+      }
       while ((c = fgetc(ifp)) != EOF) fputc(c, ofp);          
       fclose(ifp);
           
index e2f13bf..61c3016 100644 (file)
@@ -34,9 +34,7 @@ echo "3.0 (quilt)" > source/format
 cat > gridsite.install <<EOF
 usr/lib/apache2/modules/mod_gridsite.so
 usr/sbin/real-gridsite-admin.cgi
-usr/sbin/gridsite-copy.cgi
 usr/sbin/gridsite-delegation.cgi
-usr/sbin/gridsite-storage.cgi
 EOF
 cat > gridsite.manpages <<EOF
 doc/gridsite-delegation.8
@@ -74,14 +72,6 @@ doc/urlencode.1
 doc/findproxyfile.1
 EOF
 
-# gsexec
-cat > gridsite-gsexec.install <<EOF
-usr/sbin/gsexec
-EOF
-cat > gridsite-gsexec.manpages <<EOF
-doc/gsexec.8
-EOF
-
 # service-clients
 cat > gridsite-service-clients.install <<EOF
 usr/bin/htproxyput
@@ -100,21 +90,8 @@ doc/htproxyrenew.1
 doc/htproxyinfo.1
 EOF
 
-# slashgrid
-cat > gridsite-slashgrid.dirs <<EOF
-etc/init.d
-EOF
-cat > gridsite-slashgrid.install <<EOF
-usr/sbin/slashgrid
-etc/rc.d/init.d/slashgrid etc/init.d
-var/spool/slashgrid
-EOF
-cat > gridsite-slashgrid.manpages <<EOF
-doc/slashgrid.8
-EOF
-
 # shared
-cat > libgridsite${MINOR_VERSION}.docs <<EOF
+cat > libgridsite2.docs <<EOF
 CHANGES
 INSTALL
 README
@@ -124,7 +101,7 @@ doc/fuse.spec
 doc/gsoap-devel.spec
 doc/build-apache2.sh
 EOF
-cat > libgridsite${MINOR_VERSION}.install <<EOF
+cat > libgridsite2.install <<EOF
 usr/lib/lib*.so.*
 EOF
 
index e02d1aa..2ba382d 100755 (executable)
@@ -1,15 +1,5 @@
 #!/bin/sh
 
-# test to see if fuse-devel (or fuse.h and libfuse) is installed
-#
-cat <<EOF >fuse-test.c
-#include <fuse.h>
-int main() { struct fuse_context ctx;
-return fuse_main(0, (char **) 0, (struct fuse_operations *) 0); }
-EOF
-make fuse-test
-if [ $? = 0 ] ; then have_fuse=1 ; fi
-
 if [ x"$project" = x"glite" ]; then
        globusdep=',vdt_globus_sdk'
 else
@@ -18,11 +8,11 @@ fi
 
 if [ x"$project" = x"emi1" ]; then
        shared_name="shared"
-       shared_provide_name="libs"
+       shared_provide="libs"
        shared_obsoletes="gridsite-libs < $PATCH_VERSION"
 else
        shared_name="libs"
-       shared_provide_name="shared"
+       shared_provide="shared"
        shared_obsoletes="gridsite-shared <= $PATCH_VERSION"
 fi
 
@@ -38,9 +28,6 @@ make gridsite-delegation.cgi
 if [ $? = 0 ] ; then have_gsoap=1 ; fi
 rm -f a.out
 
-if [ "$have_fuse" = "1" ]; then
-       fusedep=',fuse-devel'
-fi
 if [ "$have_gsoap" = "1" ]; then
        gsoapdep=',gsoap-devel'
 fi
@@ -62,8 +49,15 @@ Source: %{name}-%{version}.src.tar.gz
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
 URL: http://www.gridsite.org/
 Vendor: GridPP
-Buildrequires: libxml2-devel,curl-devel,httpd-devel,openssl-devel,pkgconfig
-Buildrequires: doxygen${gsoapdep}${fusedep}${globusdep}
+BuildRequires: libxml2-devel%{?_isa},httpd-devel%{?_isa},openssl-devel%{?_isa},pkgconfig
+BuildRequires: doxygen${gsoapdep}${globusdep}
+BuildRequires: canl-c-devel%{?_isa}
+%if %{?fedora}%{!?fedora:0} >= 10 || %{?rhel}%{!?rhel:0} >= 6
+BuildRequires:  libcurl-devel%{?_isa}
+%else
+BuildRequires:  curl-devel%{?_isa}
+%endif
+BuildRequires: libtool
 Packager: Andrew McNab <Andrew.McNab@manchester.ac.uk>
 Provides: gridsite-apache = %{version}-%{release}
 Obsoletes: gridsite-apache < %{version}-%{release}
@@ -84,7 +78,7 @@ See http://www.gridsite.org/ for details.
 Group: Development/Libraries
 Summary: GridSite shared library and core documentation
 Obsoletes: $shared_obsoletes
-Provides: gridsite-$shared_provide_name = %{version}-%{release}
+Provides: gridsite-$shared_provide = %{version}-%{release}
 
 %description $shared_name
 GridSite shared library and core documentation
@@ -114,37 +108,6 @@ HTTP/HTTPS rather than ssh as its transfer protocol.
 
 See http://www.gridsite.org/ for details.
 
-%package gsexec
-Group: Applications/Internet
-Summary: gsexec binary for the Apache HTTP server
-
-%description gsexec
-This package includes the /usr/sbin/gsexec binary which can be installed
-to allow the Apache HTTP server to run CGI programs (and any programs
-executed by SSI pages) as a user other than the 'apache' user. gsexec
-is a drop-in replacement for suexec, with extended functionality for use
-with GridSite and Grid Security credentials.
-
-See http://www.gridsite.org/ for details.
-
-EOF
-
-if [ x"$project" = x"emi1" ]; then
-cat <<EOF >>gridsite.spec
-%package apache
-Group: System Environment/Daemons
-Summary: GridSite mod_gridsite module for Apache httpd
-
-%description apache
-GridSite Apache module and CGI binaries. This is helper package to fix upgrade
-from upstream gridsite 1.7.19.
-
-See http://www.gridsite.org/ for details.
-
-EOF
-fi
-
-cat <<EOF >>gridsite.spec
 %prep
 
 %setup -q
@@ -155,13 +118,6 @@ make prefix=%{_prefix}
 
 EOF
 
-if [ $have_fuse ] ; then
-cat <<EOF >>gridsite.spec
-make prefix=%{_prefix} slashgrid
-
-EOF
-fi
-
 if [ $have_gsoap ] ; then
 cat <<EOF >>gridsite.spec
 make prefix=%{_prefix} gridsite-delegation.cgi htproxyput
@@ -176,23 +132,15 @@ rm -rf \$RPM_BUILD_ROOT
 
 cd src
 
-make install prefix=\$RPM_BUILD_ROOT/%{_prefix} libdir=%{_lib}
+make install prefix=%{_prefix} libdir=%{_lib} DESTDIR=\$RPM_BUILD_ROOT
+rm -f \$RPM_BUILD_ROOT/%{_libdir}/*.a
 EOF
 
-if [ $have_fuse ] ; then
-
-cat <<EOF >>gridsite.spec
-
-mkdir -p \$RPM_BUILD_ROOT/etc/rc.d/init.d
-make install-slashgrid prefix=\$RPM_BUILD_ROOT/%{_prefix}
-EOF
-fi
-
 if [ $have_gsoap ] ; then
 
 cat <<EOF >>gridsite.spec
 
-make install-ws prefix=\$RPM_BUILD_ROOT/%{_prefix}
+make install-ws prefix=%{_prefix} libdir=%{_lib} DESTDIR=\$RPM_BUILD_ROOT
 EOF
 fi
 
@@ -214,8 +162,6 @@ fi
 %attr(-, root, root) %{_prefix}/share/man/man8/mod_gridsite.8.gz
 %attr(-, root, root) %{_prefix}/%{_lib}/httpd/modules/mod_gridsite.so
 %attr(-, root, root) %{_prefix}/sbin/real-gridsite-admin.cgi
-%attr(-, root, root) %{_prefix}/sbin/gridsite-copy.cgi
-%attr(-, root, root) %{_prefix}/sbin/gridsite-storage.cgi
 EOF
 
 if [ $have_gsoap ] ; then
@@ -230,23 +176,17 @@ fi
 cat <<EOF >>gridsite.spec
 
 %files $shared_name
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.%{version}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.${MINOR_VERSION:-1.x}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.${MAJOR_VERSION:-1}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.%{version}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.${MINOR_VERSION:-1.x}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.${MAJOR_VERSION:-1}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.%{version}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.${MINOR_VERSION:-1.x}
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.${MAJOR_VERSION:-1}
 %attr(-, root, root) %{_prefix}/share/doc/gridsite-${MINOR_VERSION:-1.x}
+%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.2
+%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.2.*
+%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.2
+%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.2.*
+%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.2
+%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.2.*
 
 %files devel
 %attr(-, root, root) %{_prefix}/include/gridsite.h
 %attr(-, root, root) %{_prefix}/include/gridsite-gacl.h
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.a
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.a
-%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.a
 %attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so
 %attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so
 %attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so
@@ -273,39 +213,8 @@ cat <<EOF >>gridsite.spec
 %attr(-, root, root) %{_prefix}/share/man/man1/htfind.1.gz
 %attr(-, root, root) %{_prefix}/share/man/man1/urlencode.1.gz
 %attr(-, root, root) %{_prefix}/share/man/man1/findproxyfile.1.gz
-
-%files gsexec
-%attr(4510, root, apache) %{_prefix}/sbin/gsexec
-%attr(-, root, root) %{_prefix}/share/man/man8/gsexec.8.gz
 EOF
 
-if [ $have_fuse ] ; then
-
-cat <<EOF >>gridsite.spec
-%package slashgrid
-Group: Applications/Internet
-Summary: slashgrid daemon
-Requires: curl >= 7.12.1,fuse
-
-%description slashgrid
-SlashGrid daemon
-
-%post slashgrid
-mkdir -p /grid
-
-%preun slashgrid
-/sbin/service slashgrid stop ; :
-
-%files slashgrid
-%attr(0744, root, root) %{_prefix}/sbin/slashgrid
-%attr(0744, root, root) /etc/rc.d/init.d/slashgrid
-%attr(0700, root, root) /var/spool/slashgrid
-%attr(-, root, root) %{_prefix}/share/man/man8/slashgrid.8.gz
-
-EOF
-
-fi
-
 if [ $have_gsoap ] ; then
 
 cat <<EOF >>gridsite.spec
@@ -341,21 +250,9 @@ See http://www.gridsite.org/ for details.
 
 EOF
 
-if [ x"$project" = x"emi1" ]; then
-cat <<EOF >>gridsite.spec
-%files apache
-%attr(-, root, root) %{_prefix}/share/man/man8/mod_gridsite.8.gz
-%attr(-, root, root) %{_prefix}/%{_lib}/httpd/modules/mod_gridsite.so
-%attr(-, root, root) %{_prefix}/sbin/real-gridsite-admin.cgi
-%attr(-, root, root) %{_prefix}/sbin/gridsite-copy.cgi
-%attr(-, root, root) %{_prefix}/sbin/gridsite-storage.cgi
-
-EOF
-fi
-
 LC_TIME=C cat >> gridsite.spec <<EOF
 %changelog
-* `date '+%a %b %d %Y'` - autobuild@`hostname -f`
+* `date '+%a %b %d %Y'` autobuild@`hostname -f` - ${PATCH_VERSION:-1.x.x}-${RELEASE_VERSION:-1}
 - automatically generated package
 EOF