diff --git a/net/xrootd/src/xrootd/config/ARCHS b/net/xrootd/src/xrootd/config/ARCHS
index 25ed6ac3ecfe0b321c64a9bd46b0c44f477bbe35..5a7889a470325d42df07f15f7dd18997aa76efd5 100644
--- a/net/xrootd/src/xrootd/config/ARCHS
+++ b/net/xrootd/src/xrootd/config/ARCHS
@@ -73,4 +73,5 @@ sun4x_5         sungcc    all    for Solaris 5.x with gcc and glibc
 sun4x_4         sungcc    all    for Solaris 4.x with gcc and glibc
 sunx86_510      sunCCi86pc  all  for Solaris 5.x on i86 platform
 sunx86_510      sunCCamd510 all  for Solaris 5.10 on x86-64 platform
+sunx86_510      sunCCamd    all  for Solaris 5.10 on x86-64 platform
 sunx86_511      sunCCi86pc  all  for Solaris 5.x > 5.10 on x86 platform
diff --git a/net/xrootd/src/xrootd/config/GNUmake.env.in b/net/xrootd/src/xrootd/config/GNUmake.env.in
index 3b27aa1226bb0b6d68194db71177232dc2f4d404..6345212533cee098c92821b2d15cd690edf0cc11 100644
--- a/net/xrootd/src/xrootd/config/GNUmake.env.in
+++ b/net/xrootd/src/xrootd/config/GNUmake.env.in
@@ -1,4 +1,4 @@
-#              $Id: GNUmake.env.in,v 1.31 2009/07/28 20:01:44 ganis Exp $
+#              $Id: GNUmake.env.in,v 1.32 2010/01/13 11:41:22 ganis Exp $
 
 #------------------------------------------------------------------------------#
 #                       C o m m o n   V a r i a b l e s                        #
@@ -71,6 +71,10 @@ PWDEXTRACFLAGS = @pwdextracflags@ @afsextracflags@
 @libssl@
 SSLEXTRACFLAGS = @sslextracflags@
 
+# XML relevant variables
+@incxml@
+@libxml@
+
 # perl relevant variables
 PERLBIN = @perlbin@
 PERLINC = @perlincdir@
diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664 b/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664
index 7fa184b03a7ef1f87794230aac0a38d6744d3da6..5835b02acba23edc16d082ae5069cf6280c3acc4 100644
--- a/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664
+++ b/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664
@@ -1,4 +1,4 @@
-#              $Id: GNUmake.rules.gccx8664,v 1.5 2006/10/19 14:49:29 ganis Exp $
+#              $Id: GNUmake.rules.gccx8664,v 1.6 2009/12/01 14:11:24 ganis Exp $
 
 #------------------------------------------------------------------------------#
 #                 R u l e s   f o r   g e n e r i c   g c c                    #
diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.icc b/net/xrootd/src/xrootd/config/GNUmake.rules.icc
index bcf65e861aadbbb070dc9d959b9f37176b6c7cef..4d8e24fe08a6be96741a1f9bfb56b44c0e80f1ea 100644
--- a/net/xrootd/src/xrootd/config/GNUmake.rules.icc
+++ b/net/xrootd/src/xrootd/config/GNUmake.rules.icc
@@ -1,4 +1,4 @@
-#              $Id: GNUmake.rules.icc,v 1.8 2009/06/11 08:31:30 ganis Exp $
+#              $Id: GNUmake.rules.icc,v 1.10 2009/12/01 14:11:01 ganis Exp $
 
 #------------------------------------------------------------------------------#
 #                 R u l e s   f o r   g e n e r i c   i c c                    #
diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664 b/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664
index 06fe3a7ebf2b8d0ac7c92b18c762a99cb9a75f32..50383c9f6ef8df55197b5ae3e10d41c840cbad75 100644
--- a/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664
+++ b/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664
@@ -1,4 +1,4 @@
-#              $Id: GNUmake.rules.iccx8664,v 1.5 2009/10/15 10:44:45 ganis Exp $
+#              $Id: GNUmake.rules.iccx8664,v 1.7 2009/12/01 14:11:15 ganis Exp $
 
 #------------------------------------------------------------------------------#
 #                 R u l e s   f o r   g e n e r i c   i c c                    #
diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd
index e68d4eb3ef41ced2a0785dd4ef89353ee9c505e5..497a27f977d9530d5a9905549976a34768790577 100644
--- a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd
+++ b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd
@@ -1,4 +1,4 @@
-#              $Id: GNUmake.rules.sunCCamd,v 1.7 2009/08/17 14:15:14 ganis Exp $
+#              $Id: GNUmake.rules.sunCCamd,v 1.8 2009/12/16 00:14:55 abh Exp $
 
 #------------------------------------------------------------------------------#
 #              R u l e s   f o r   g e n e r i c   s u n C C                   #
@@ -11,12 +11,12 @@ TYPEMISC   = -KPIC -DSUNCC -D__solaris__ -DSUNX86 $(CFTRACE) -library=stlport4
 SUNMT      = -D_REENTRANT -mt -D_POSIX_PTHREAD_SEMANTICS
 TYPECF32   =
 TYPECF64   = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
-TYPEOPT    = $(TYPEMISC) $(SUNMT) -O -m64
-TYPEDBG    = $(TYPEMISC) $(SUNMT) -g -xs -m64
-TYPELDSO   = -G -m64
+TYPEOPT    = $(TYPEMISC) $(SUNMT) -fast -xtarget=opteron -xarch=amd64
+TYPEDBG    = $(TYPEMISC) $(SUNMT) -g -xs -xtarget=opteron -xarch=amd64
+TYPELDSO   = -G -xtarget=opteron -xarch=amd64
 TYPESHLIB  = so
 
-TYPELIBS   = -L/usr/lib/amd64 -L/lib/amd64 \
+TYPELIBS   = -R/usr/lib/amd64 -L/usr/lib/amd64 -L/lib/amd64 \
              -lposix4 -lsocket -lnsl $(PTHREAD) -ldl
 TYPELIBMT  = -lmtmalloc
 TYPELIBSF  = -lsendfile
diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510 b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510
index 8365ce3283bed4256a9b2c2f19fa59fd7d3d4abf..9f8b6bdfc72e6e8f14207ebdf840549001cc9f9a 100644
--- a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510
+++ b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510
@@ -1,4 +1,4 @@
-#              $Id: GNUmake.rules.sunCCamd510,v 1.1 2009/09/16 14:22:42 ganis Exp $
+#              $Id: GNUmake.rules.sunCCamd510,v 1.2 2009/12/11 01:57:01 abh Exp $
 
 #------------------------------------------------------------------------------#
 #              R u l e s   f o r   g e n e r i c   s u n C C                   #
@@ -16,7 +16,7 @@ TYPEDBG    = $(TYPEMISC) $(SUNMT) -g -xs -xtarget=opteron -xarch=amd64
 TYPELDSO   = -G -xtarget=opteron -xarch=amd64
 TYPESHLIB  = so
 
-TYPELIBS   = -L/usr/lib/amd64 -L/lib/amd64 \
+TYPELIBS   = -R/usr/lib/amd64 -L/usr/lib/amd64 -L/lib/amd64 \
              -lposix4 -lsocket -lnsl $(PTHREAD) -ldl
 TYPELIBMT  = -lmtmalloc
 TYPELIBSF  = -lsendfile
diff --git a/net/xrootd/src/xrootd/config/GNUmakefile.in b/net/xrootd/src/xrootd/config/GNUmakefile.in
index 147a9935f13adbe3678b6abd9445c8439eac3c3c..a9c638632597dbb9bddec1f2468285d2b433601c 100644
--- a/net/xrootd/src/xrootd/config/GNUmakefile.in
+++ b/net/xrootd/src/xrootd/config/GNUmakefile.in
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile.in,v 1.36 2009/11/04 09:54:47 furano Exp $
+#              $Id: GNUmakefile.in,v 1.37 2010/01/13 11:41:22 ganis Exp $
 
 #------------------------------------------------------------------------------#
 #                             C o m p o n e n t s                              #
@@ -230,6 +230,11 @@ XrdSecpwd: FORCE
 	$(ECHO)cd src/XrdSecpwd;\
 	$(MAKE) $(MAKEXEQ) ARCH=$(ARCH) --no-print-directory
 
+XrdSecssl: FORCE
+	@echo $(XMSG) secssl $(XDBG) component...
+	$(ECHO)cd src/XrdSecssl;\
+	$(MAKE) $(MAKEXEQ) ARCH=$(ARCH) --no-print-directory
+
 XrdSecsss: FORCE
 	@echo $(XMSG) secsss $(XDBG) component...
 	$(ECHO)cd src/XrdSecsss;\
diff --git a/net/xrootd/src/xrootd/configure.classic b/net/xrootd/src/xrootd/configure.classic
index d6a8135548207ebc351b7ba49caa51e645ee26d9..b50a79a13c377cd9ba8be3be849627dc64288c0d 100755
--- a/net/xrootd/src/xrootd/configure.classic
+++ b/net/xrootd/src/xrootd/configure.classic
@@ -1,7 +1,7 @@
 #! /bin/sh
 ################################################################################
 #
-#          $Id: configure.classic,v 1.99 2009/10/15 10:46:58 ganis Exp $
+#          $Id: configure.classic,v 1.104 2010/01/13 11:41:21 ganis Exp $
 #                                                                              #
 #                     c o n f i g u r e . c l a s s i c                        #
 #                       (previously called configure)                          #
@@ -65,6 +65,7 @@ options="                 \
    enable_afs             \
    enable_echo            \
    enable_gsi             \
+   enable_secssl          \
    enable_krb4            \
    enable_krb5            \
    enable_mon             \
@@ -91,6 +92,7 @@ enable_mon=yes
 enable_perlint=no
 enable_shadowpw=no
 enable_shared=yes
+enable_secssl=
 enable_ssl=
 enable_thread=yes
 enable_javaint=no
@@ -684,6 +686,7 @@ enable/disable options, prefix with either --enable- or --disable-
    afs             AFS support, requires AFS libs and objects
    echo            Enable/disable echoing of commands during building
    gsi             GSI authentication support, requires OpenSSL
+   secssl          SSL authentication support, requires OpenSSL
    javaint         support for the Java Native Interface for XrdClientAdmin
    krb4            Kerberos4 support, requires Kerberos libs
    krb5            Kerberos5 support, requires Kerberos libs
@@ -718,6 +721,9 @@ with options, prefix with --with-, enables corresponding support
    ssl-libdir      OpenSSL support, location of libssl, libcrypto (has priority over ssl-dir)
    ssl-shared      OpenSSL support, controls usage of shared linkage SSL libraries {yes/no}
    thread-libdir   Path to libpthread
+   xml             SSL authentication support, location of XML2 
+   xml-incdir      SSL authentication support, location of libxml/tree.h, ... (has priority over xml-dir)
+   xml-libdir      SSL authentication support, location of libxml2 (has priority over xml-dir)
 
 with compiler options, prefix with --with-, overrides default value
 
@@ -729,7 +735,8 @@ Advanced options:
    --no-arch-subdirs   Do not use architecture dependent names for bin/lib paths
    --build-client-only Build only the client related stuff
    --build-sec-only    Build only security modules
-   --use-xrd-strlcpy   Force use of the xrd implementation of strlcpy 
+   --use-xrd-strlcpy   Force use of the xrd implementation of strlcpy
+   --use-libtcmalloc   Use the thread-caching malloc implementation if available
    --has-getpbynr      Do not check for getprotobyname_r
    --with-gethbynr=dir Do not check for gethostbyname_r, take prototype from 'dir'/netdb.h
    --with-nameinfo=dir Do not check for getnameinfo, take prototype from 'dir'/netdb.h
@@ -856,6 +863,7 @@ if test $# -gt 0 ; then
       --has-sigwaitinfo)       hassigwti="yes" ;;
       --has-fstatat)           hasfstatat="yes"      ;;
       --has-readline)          hasreadline="yes"     ;;
+      --use-libtcmalloc)       enable_libtcmalloc="yes"  ;;
       --with-afs=*)            afsdir=$optarg        ; enable_afs="yes"     ;;
       --with-afs-incdir=*)     afsincdir=$optarg     ; enable_afs="yes"    ;;
       --with-afs-libdir=*)     afslibdir=$optarg     ; enable_afs="yes"    ;;
@@ -873,6 +881,9 @@ if test $# -gt 0 ; then
       --with-ssl-libdir=*)     ssllibdir=$optarg     ; enable_ssl="yes"     ;;
       --with-ssl-shared=*)     sslshared=$optarg     ; enable_ssl="yes"     ;;
       --with-thread-libdir=*)  threadlibdir=$optarg  ; enable_thread="yes"  ;;
+      --with-xml=*)            xmldir=$optarg        ; enable_secssl="yes"  ;;
+      --with-xml-incdir=*)     xmlincdir=$optarg     ; enable_secssl="yes"  ;;
+      --with-xml-libdir=*)     xmllibdir=$optarg     ; enable_secssl="yes"  ;;
       --prefix=*)              inst_prefix=$optarg   ;;
       --libdir=*)              inst_libdir=$optarg   ;;
       --etcdir=*)              inst_etcdir=$optarg   ;;
@@ -892,6 +903,7 @@ if test $# -gt 0 ; then
 	f=`echo $1 | sed -e 's/--//' -e 's/-/_/'`
 if test "x$f" = "xenable_sec" ; then
    enable_gsi=yes
+   enable_secssl=no
    enable_krb4=yes
    enable_krb5=yes
    enable_pwd=yes
@@ -913,6 +925,7 @@ fi
 	f=`echo $1 | sed -e 's/--disable/enable/' -e 's/-/_/'`
 if test "x$f" = "xenable_sec" ; then
    enable_gsi=no
+   enable_secssl=no
    enable_krb4=no
    enable_krb5=no
    enable_pwd=no
@@ -1312,6 +1325,39 @@ if test "x$haslibz" != "x"; then
    fi
 fi
 
+######################################################################
+#
+### echo %%% Check for libtcmalloc
+#
+
+
+libtcmalloc=""
+if test "x$enable_libtcmalloc" = "xyes"; then
+  if test "x$syslibs" = "x" ; then
+     what2look4="/usr/lib /usr/local/lib /opt/lib $finkdir/lib"
+  else
+     what2look4="$syslibs"
+  fi
+
+  if test "x$haslibz" != "x"; then
+     check_library "libtcmalloc" "$enable_shared" "" $what2look4
+     if test "x$found_lib" = "x" ; then
+          libtcmalloc=""
+     else
+        libtcmalloc="$found_dir $found_lib"
+     fi
+
+     if test "x$libtcmalloc" = "x"; then
+       check_library "libtcmalloc_minimal" "$enable_shared" "" $what2look4
+       if test "x$found_lib" = "x" ; then
+            libtcmalloc=""
+       else
+          libtcmalloc="$found_dir $found_lib"
+       fi
+     fi
+  fi
+
+fi
 
 ######################################################################
 #
@@ -2277,6 +2323,96 @@ if test ! "x$enable_gsi" = "xno" ; then
     #
 fi
 
+######################################################################
+#
+### echo %%% Support for SSL authentication
+#
+#
+if test "x$enable_secssl" = "x" ; then
+   if test "x$platform" = "xlinux" || test "x$platform" = "xmacosx" ; then
+      # Default 'yes' for linux and macosx only, for the time being
+      enable_secssl="yes"
+   else
+      enable_secssl="no"
+   fi
+fi
+secsslextracflags=""
+havesecssl=""
+if test ! "x$enable_secssl" = "xno" ; then
+    #
+    # Requires XML2
+    if test ! "x$xmldir" = "x" ; then
+       if test "x$xmlincdir" = "x" ; then
+          xmlincdir=$xmldir/include
+       fi
+       if test "x$xmllibdir" = "x" ; then
+          xmllibdir=$xmldir/lib
+       fi
+    fi
+    # Check for xml include and library
+    check_header "libxml/tree.h" "$xmlincdir" $XMLDIR $XMLDIR/include \
+           /opt/libxml2/include /usr/local/include/libxml2 /usr/include/libxml2
+    if test ! "x$found_dir" = "x" ; then
+       xmlincdir=$found_dir
+       # Check the other headers
+       if test -f "$found_dir/libxml/xmlmemory.h" ; then
+          echo "Checking for libxml/xmlmemory.h ... $found_dir"
+          if test -f "$found_dir/libxml/parser.h" ; then
+             echo "Checking for libxml/parser.h ... $found_dir"
+          else
+             echo "Checking for libxml/parser.h ... no"
+             enable_secssl="no"
+          fi
+       else
+          echo "Checking for libxml/xmlmemory.h ... no"
+          enable_secssl="no"
+       fi
+    else
+       enable_secssl="no"
+    fi
+
+    if test "x$enable_secssl" = "xyes" ; then
+       check_library "libxml2" "$enable_shared" "$xmllibdir" $XMLDIR \
+           $XMLDIR/lib $XMLDIR/.libs /opt/libxml2/lib /usr/local/lib /usr/lib
+       if test ! "x$found_lib" = "x" ; then
+          xmllib=$found_lib
+          xmllibdir=$found_dir
+       else
+          enable_secssl="no"
+          xmlincdir=
+          xmllib=
+          xmllibdir=
+       fi
+    fi
+
+    #
+    message "Enabling SSL authentication module"
+    #
+    if test "x$enable_secssl" = "xyes" ; then
+       buildsec="yes"
+       # plug-in name
+       havesecssl="XrdSecssl"
+       # Require OpenSSL
+       if test ! "x$enable_ssl" = "xno" ; then
+          enable_ssl="yes"
+          incxml="INCXML = -I$xmlincdir"
+          libxml="LIBXML = $xmllibdir $xmllib"
+          echo "done"
+       else
+          # explicitely disabled: warn
+          incxml="INCXML = "
+          libxml="LIBXML = "
+          echo "no: SSL authentication requires OpenSSL"
+       fi
+    else
+       # explicitely disabled: warn
+       incxml="INCXML = "
+       libxml="LIBXML = "
+       echo "no: SSL authentication requires XML2 dev package"
+    fi
+    #
+fi
+
 ######################################################################
 #
 ### echo %%% OpenSSL support - Third party libraries
@@ -2467,6 +2603,9 @@ if test "x$enable_ssl" = "xyes" ; then
         # GSI needs OpenSSL
         echo "Disabling GSI ... (requires OpenSSL)"
         havegsi=""
+        # SSL needs OpenSSL
+        echo "Disabling SSL ... (requires OpenSSL)"
+        havesecssl=""
         if test "x$havepwd" = "x" ; then
            xrdsut=""
            xrdcrypto=""
@@ -2496,7 +2635,7 @@ fi
 xrdsec=
 hasxrdcrypto=
 if test "x$buildsec" = "xyes" ; then
-   xrdsec="$xrdsut $xrdcrypto $havekrb4 $havekrb5 $havepwd $havesss $havegsi XrdSecunix"
+   xrdsec="$xrdsut $xrdcrypto $havekrb4 $havekrb5 $havepwd $havesss $havesecssl $havegsi XrdSecunix"
    if test ! "x$xrdcrypto" = "x" ; then
       hasxrdcrypto="-DHAVE_XRDCRYPTO"
    fi
@@ -2707,10 +2846,13 @@ sed -e "s|@arch@|$arch|"                 \
     -e "s|@incssl@|$incssl|"           \
     -e "s|@libssl@|$libssl|"           \
     -e "s|@sslextracflags@|$sslextracflags|" \
+    -e "s|@incxml@|$incxml|"           \
+    -e "s|@libxml@|$libxml|"           \
     -e "s|@hasdevpoll@|$hasdevpoll|" \
     -e "s|@hasepoll@|$hasepoll|" \
     -e "s|@haslibz@|$haslibz|" \
     -e "s|@libz@|$libz|" \
+    -e "s|@libtcmalloc|$libtcmalloc@|" \
     -e "s|@hassendfile@|$hassendfile|" \
     -e "s|@hassigwti@|$hassigwti|" \
     -e "s|@hassetresuid@|$hassetresuid|" \
diff --git a/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc b/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc
index ac0723edb6a814d55c8accdda614b1c5dbb60cf2..1e6ca2fb2c330bc78cfda6d2d24986dadb9f013f 100644
--- a/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc
+++ b/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc
@@ -363,9 +363,12 @@ int XrdLink::Close(int defer)
 
 // At this point we can have no lock conflicts, so if someone is waiting for
 // us to terminate let them know about it. Note that we will get the condvar
-// mutex while we hold the opMutex. This is the required order!
+// mutex while we hold the opMutex. This is the required order! We will also
+// zero out the pointer to the condvar while holding the opmutex.
 //
-   if (KillcvP) {KillcvP->Lock(); KillcvP->Signal(); KillcvP->UnLock();}
+   if (KillcvP) {KillcvP->  Lock(); KillcvP->Signal();
+                 KillcvP->UnLock(); KillcvP = 0;
+                }
 
 // Remove ourselves from the poll table and then from the Link table. We may
 // not hold on to the opMutex when we acquire the LTMutex. However, the link
@@ -1203,6 +1206,16 @@ int XrdLink::Terminate(const XrdLink *owner, int fdnum, unsigned int inst)
    if (killDone.Wait(int(killWait))) {wTime += killWait; KillCnt |= KillXwt;}
       else wTime = -EPIPE;
    killDone.UnLock();
+
+// Reobtain the opmutex so that we can zero out the pointer the condvar pntr
+// This is really stupid code but because we don't have a way of associating
+// an arbitrary mutex with a condvar. But since this code is rarely executed
+// the ugliness is sort of tolerable.
+//
+   lp->opMutex.Lock(); KillcvP = 0; lp->opMutex.UnLock();
+
+// Do some tracing
+//
    TRACEI(DEBUG,"Terminate " << (wTime <= 0 ? "complete ":"timeout ") <<wTime);
    return wTime;
 }
diff --git a/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc b/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc
index f1c89f796096b58070d063c0cb745f243585f8dc..8b8b946fc00b05cb0f1109b6af83517dddccfa21 100644
--- a/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc
+++ b/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc
@@ -279,7 +279,7 @@ void XrdScheduler::Run()
               } else {
                num_JobsinQ = 0;
                if (num_Layoffs > 0)
-                  {num_Layoffs--; num_TDestroy--; num_Workers--;
+                  {num_Layoffs--; num_TDestroy++; num_Workers--; idl_Workers--;
                    TRACE(SCHED, "terminating thread; workers=" <<num_Workers);
                    SchedMutex.UnLock();
                    return;
diff --git a/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile b/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile
index 14cdbec5ea75e8cc50da6ffc3f08cc0163f083e9..991b2bdfc7ca72016a6b04b4ef88a0c4c87abc06 100644
--- a/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile
+++ b/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile,v 1.5 2009/08/26 22:22:28 abh Exp $
+#              $Id: GNUmakefile,v 1.6 2010/01/07 13:59:43 ganis Exp $
 
 #-----------------------------------------------------------------------------#
 #                            E n v i r o n m e n t                            #
diff --git a/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc b/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc
index 42b2da90d7532cb43cbb1727a6f509abc7c0039b..a796cdd7fc88b23d5ab1128b8dc67ec21d73728f 100644
--- a/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc
+++ b/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc
@@ -15,11 +15,16 @@ const char *Xrdadler32CVSID = "$Id$";
 #define _FILE_OFFSET_BITS 64
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <errno.h>
+#ifdef __linux__
+  #include <sys/xattr.h>
+#endif
 #include <zlib.h>
 
 #include "XrdPosix/XrdPosixExtern.hh"
@@ -31,6 +36,77 @@ const char *Xrdadler32CVSID = "$Id$";
 #include "XrdClient/XrdClientAdmin.hh"
 #include "XrdOuc/XrdOucString.hh"
 
+void fSetXattrAdler32(int fd, const char* attr, const char *value)
+{
+    struct stat st;
+    char mtime[12], attr_val[25];
+    int rc;
+    
+    rc = fstat(fd, &st);
+    if (rc < 0 || strlen(value) != 8) 
+        return; 
+    else
+        sprintf(mtime, "%ld", st.st_mtime);
+
+    strcpy(attr_val, value);
+    strcat(attr_val, ":"); 
+    strcat(attr_val, mtime);
+
+#if defined(__linux__)
+    rc = fsetxattr(fd, attr, attr_val, strlen(attr_val), 0x0);
+#elif defined(__solaris__)
+    int attrfd;
+    attrfd = openat(fd, attr, O_XATTR|O_CREAT|O_TRUNC|O_WRONLY); 
+    if (attrfd < 0) return;
+
+    rc = write(attrfd, attr_val, strlen(attr_val));
+/*
+   Solaris extended attributes are files in orthogonal namespace.
+   Their permission wont' change according to real files.
+ */
+    fchmod(attrfd, S_IRWXU|S_IRGRP|S_IROTH);
+    close(attrfd);
+#endif
+    return;
+}
+
+int fGetXattrAdler32(int fd, const char* attr, char *value)
+{
+    struct stat st;
+    char mtime[12], attr_val[25], *p;
+    int rc;
+
+    rc = fstat(fd, &st);
+    if (rc < 0)
+        return(0);
+    else
+        sprintf(mtime, "%ld", st.st_mtime);
+
+
+#if defined(__linux__)
+    rc = fgetxattr(fd, attr, attr_val, 25);
+#elif defined(__solaris__)
+    int attrfd;
+    attrfd = openat(fd, attr, O_XATTR|O_RDONLY);
+    if (attrfd < 0) return(0);
+
+    rc = read(attrfd, attr_val, 25);
+    close(attrfd);
+#else
+    return(0);
+#endif
+
+    if (rc == -1 || attr_val[8] != ':') return(0);
+    attr_val[8] = '\0';
+    attr_val[rc] = '\0';
+    p = attr_val + 9;
+     
+    if (strcmp(p, mtime)) return(0);
+
+    strcpy(value, attr_val);
+    return(strlen(value));
+}
+
 /* get the actual root url pointing to the data server */
 char get_current_url(const char *oldurl, char *newurl)
 {
@@ -90,7 +166,8 @@ char getchksum(const char *rooturl, char *chksum)
 
 int main(int argc, char *argv[])
 {
-    char path[2048], chksum[128], buf[N];
+    char path[2048], chksum[128], buf[N], adler_str[9];
+    const char attr[] = "user.checksum.adler32";
     struct stat stbuf;
     int fd, len, rc;
     uLong adler;
@@ -111,7 +188,6 @@ int main(int argc, char *argv[])
         else  
             XrdPosix_URL(argv[1], path, sizeof(path));
     }
-
     if (argc == 1 || path[0] == '\0')
     {                        /* this is a local file */
         if (argc > 1) 
@@ -124,6 +200,12 @@ int main(int argc, char *argv[])
                 printf("Error_accessing %s\n", path);
                 return 1;
             }
+            else  /* see if the adler32 is saved in attribute already */
+                if (fGetXattrAdler32(fd, attr, adler_str) == 8)
+                {
+                    printf("%s %s\n", adler_str, path);
+                    return 0;
+                }
         }
         else 
         {
@@ -133,7 +215,12 @@ int main(int argc, char *argv[])
         while ( (len = read(fd, buf, N)) > 0 )
             adler = adler32(adler, (const Bytef*)buf, len);
 
-        if (fd != STDIN_FILENO) close(fd);
+        if (fd != STDIN_FILENO) 
+        {   /* try saving adler32 to attribute before close() */
+            sprintf(adler_str, "%08lx", adler);
+            fSetXattrAdler32(fd, attr, adler_str);
+            close(fd);
+        }
         printf("%08lx %s\n", adler, path);
         return 0;
     }
diff --git a/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc b/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc
index 9156da40d48fbd9316949f9c85679922e434c309..8111e083ad5d351a0b720628b5bcdaeb6bdbba90 100644
--- a/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc
+++ b/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc
@@ -39,37 +39,40 @@ extern XrdSysError BwmEroute;
 /*                         L o c a l   C l a s s e s                          */
 /******************************************************************************/
   
-class XrdBwmHandleCB : public XrdOucErrInfo
+class XrdBwmHandleCB : public XrdOucEICB, public XrdOucErrInfo
 {
 public:
 
-void *operator new(size_t size)
-                  {void *mP;
+static
+XrdBwmHandleCB *Alloc()
+                  {XrdBwmHandleCB *mP;
                    xMutex.Lock();
-                   if (!(mP = Free)) mP = malloc(size);
-                      else memcpy(&Free, mP, sizeof(mP));
+                   if (!(mP = Free)) mP = new XrdBwmHandleCB;
+                      else Free = mP->Next;
                    xMutex.UnLock();
                    return mP;
                   }
 
-void  operator delete(void *p)
+void  Done(int &Results, XrdOucErrInfo *eInfo)
                   {xMutex.Lock();
-                   memcpy(p, &Free, sizeof(p));
-                   Free = p;
+                   Next = Free;
+                   Free = this;
                    xMutex.UnLock();
                   }
 
-      XrdBwmHandleCB() {}
+int   Same(unsigned long long arg1, unsigned long long arg2) {return 0;}
+
+      XrdBwmHandleCB() : Next(0) {}
      ~XrdBwmHandleCB() {}
 
 private:
-
-static XrdSysMutex xMutex;
-static void       *Free;
+       XrdBwmHandleCB *Next;
+static XrdSysMutex     xMutex;
+static XrdBwmHandleCB *Free;
 };
 
-XrdSysMutex XrdBwmHandleCB::xMutex;
-void       *XrdBwmHandleCB::Free = 0;
+XrdSysMutex     XrdBwmHandleCB::xMutex;
+XrdBwmHandleCB *XrdBwmHandleCB::Free = 0;
   
 /******************************************************************************/
 /*                     E x t e r n a l   L i n k a g e s                      */
@@ -206,7 +209,7 @@ XrdBwmHandle *XrdBwmHandle::Alloc(XrdBwmHandle *old_hP)
 void *XrdBwmHandle::Dispatch()
 {
    EPNAME("Dispatch");
-   XrdBwmHandleCB *erP = new XrdBwmHandleCB;
+   XrdBwmHandleCB *erP = XrdBwmHandleCB::Alloc();
    XrdBwmHandle   *hP;
    char *RespBuff;
    int   RespSize, readyH, Result, Err;
@@ -244,7 +247,7 @@ void *XrdBwmHandle::Dispatch()
        if (!Err) Policy->Done(readyH);
       } else {
        hP->myEICB.Wait(); hP->rTime = time(0);
-       erP->setErrCB(hP->ErrCB, hP->ErrCBarg);
+       erP->setErrCB((XrdOucEICB *)erP, hP->ErrCBarg);
        if (Err) {hP->Status = Idle; Result = SFS_ERROR;}
           else  {hP->Status = Dispatched;
                  erP->setErrCode(strlen(RespBuff));
@@ -254,7 +257,7 @@ void *XrdBwmHandle::Dispatch()
              <<(hP->Parms.Direction == XrdBwmPolicy::Incomming ? " <- ":" -> ")
              <<hP->Parms.RmtNode);
        hP->ErrCB->Done(Result, (XrdOucErrInfo *)erP);
-       erP = new XrdBwmHandleCB;
+       erP = XrdBwmHandleCB::Alloc();
       }
     hP->hMutex.UnLock();
    } while(1);
diff --git a/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile b/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile
index 096cec059ce8c2172f7515d75e9c206f0ad84ea9..624549a7a1c1fbba1dcf6cab37beef42220b54c6 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile
+++ b/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile,v 1.65 2009/09/18 14:46:48 furano Exp $
+#              $Id: GNUmakefile,v 1.67 2010/01/07 14:27:38 ganis Exp $
 
 #-----------------------------------------------------------------------------#
 #                            E n v i r o n m e n t                            #
@@ -204,7 +204,7 @@ $(XRDSTAGETOOL): $(OBJECTS) $(OBJECT_STAGETOOL) $(LIBDEPS)
 
 $(XRDCLI): $(OBJECTS) $(OBJECT_CLI) $(LIBDEPS)
 	@echo Creating executable $(XRDCLI) 
-	$(ECHO)$(LD) $(LDOP) $(OBJECT_CLI) $(BINLIBS) $(LIBM) $(LIBS) -o $(XRDCLI)
+	$(ECHO)$(LD) $(LDOP) $(OBJECT_CLI) $(BINLIBS) $(LIBM) $(LIBS) $(LIBREADLINE) -o $(XRDCLI)
 
 $(XRDADMIN): $(PERLMOD)
 	$(ECHO)if [ "$(PERLBIN)" != "" ]; then \
@@ -583,13 +583,13 @@ $(OBJDIR)/XrdCommandLine.o: XrdCommandLine.cc ../XrdClient/XrdClientUrlInfo.hh \
   ../XrdClient/XrdClientSock.hh ../XrdClient/XrdClientConn.hh \
   ../XrdClient/XrdClientConst.hh ../XrdClient/XrdClientReadCache.hh \
   ../XrdClient/XrdClientInputBuffer.hh ../XrdSys/XrdSysSemWait.hh \
-  ../XrdOuc/XrdOucHash.hh ../XrdOuc/XrdOucHash.icc  ../XrdOuc/XrdOucString.hh \
+  ../XrdOuc/XrdOucHash.hh ../XrdOuc/XrdOucHash.icc ../XrdOuc/XrdOucString.hh \
   ../XrdClient/XrdClientVector.hh ../XrdClient/XrdClientPSock.hh \
   ../XrdClient/XrdClientDebug.hh ../XrdClient/XrdClientEnv.hh \
   ../XrdOuc/XrdOucEnv.hh ../XrdSys/XrdSysHeaders.hh ../XrdSys/XrdSysLogger.hh \
   ../XrdClient/XrdClientAdmin.hh
 	@echo Compiling XrdCommandLine.cc
-	$(ECHO)$(CC) -g -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdCommandLine.o XrdCommandLine.cc
+	$(ECHO)$(CC) -g -c $(CFLAGS) $(CFREADLINE) $(INCLUDE) -o $(OBJDIR)/XrdCommandLine.o XrdCommandLine.cc
 
 $(OBJDIR)/XrdClientEnv.o: XrdClientEnv.cc ../XrdClient/XrdClientConst.hh \
   ../XrdClient/XrdClientEnv.hh ../XrdOuc/XrdOucEnv.hh \
diff --git a/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc b/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc
index 9cb132d434fbf823532f7ae8b3ec81830fa5984e..a8dbdcf93464dea8444e6f85abeb08418bf18b5d 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc
@@ -4,12 +4,28 @@
 #include "XrdClient/XrdClient.hh"
 #include "XrdClient/XrdClientEnv.hh"
 #include "XrdSys/XrdSysHeaders.hh"
+#include "XrdClient/XrdClientCallback.hh"
 #include <fstream>
 #include <vector>
 #include <string>
 #include <sys/time.h>
 #include <math.h>
 
+
+
+
+class MyXrdClientCallback: public XrdClientCallback {
+
+   virtual void OpenComplete(XrdClientAbs *clientP, void *cbArg, bool res) {
+      cout << "OpenComplete! res:" << res << endl;
+   }
+
+};
+
+
+
+
+
 kXR_unt16 open_mode = (kXR_ur | kXR_uw);
 kXR_unt16 open_opts = (0);
  
@@ -199,7 +215,8 @@ int main(int argc, char **argv) {
     kXR_int32 v_lens[20480];
 
     if (isrooturl) {
-	XrdClient *cli = new XrdClient(argv[1]);
+        MyXrdClientCallback mycb;
+	XrdClient *cli = new XrdClient(argv[1], &mycb, (void *)1234);
 
 	cli->Open(open_mode, open_opts | ( (vectored_style > 4) ? kXR_delete : 0 ) );
 	filezcount = 1;
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc
index 88887af80151cadfd00261f7cb425e28fa321ae6..b70c5c9905852d60776e0689239f221869ee4d9f 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc
@@ -26,6 +26,7 @@ const char *XrdClientCVSID = "$Id$";
 #include "XrdClient/XrdClientReadV.hh"
 #include "XrdOuc/XrdOucCRC.hh"
 #include "XrdClient/XrdClientReadAhead.hh"
+#include "XrdClient/XrdClientCallback.hh"
 
 #include <stdio.h>
 #ifndef WIN32
@@ -50,14 +51,19 @@ void *FileOpenerThread(void *arg, XrdClientThread *thr) {
    thr->SetCancelDeferred();
    thr->SetCancelOn();
 
-   thisObj->TryOpen(thisObj->fOpenPars.mode, thisObj->fOpenPars.options, false);
+
+   bool res = thisObj->TryOpen(thisObj->fOpenPars.mode, thisObj->fOpenPars.options, false);
+   if (thisObj->fXrdCcb) thisObj->fXrdCcb->OpenComplete(thisObj, thisObj->fXrdCcbArg, res);
 
    return 0;
 }
 
 
 //_____________________________________________________________________________
-XrdClient::XrdClient(const char *url) {
+XrdClient::XrdClient(const char *url,
+                     XrdClientCallback *XrdCcb,
+                     void *XrdCcbArg) : XrdClientAbs(XrdCcb, XrdCcbArg)  {
+
    fReadAheadMgr = 0;
    fReadTrimBlockSize = 0;
    fOpenerTh = 0;
@@ -74,7 +80,7 @@ XrdClient::XrdClient(const char *url) {
    if (!ConnectionManager)
       Info(XrdClientDebug::kUSERDEBUG,
            "Create",
-           "(C) 2004-2010 by the Xrootd group. XrdClient $Revision: 1.152 $ - Xrootd version: " << XrdVSTRING);
+           "(C) 2004-2010 by the Xrootd group. XrdClient $Revision: 1.154 $ - Xrootd version: " << XrdVSTRING);
    
 #ifndef WIN32
    signal(SIGPIPE, SIG_IGN);
@@ -361,6 +367,9 @@ bool XrdClient::Open(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) {
 		  fUrl.File << " on host " << fUrl.Host << ":" <<
 		  fUrl.Port);
 
+            if (fXrdCcb && !doitparallel) 
+               fXrdCcb->OpenComplete(this, fXrdCcbArg, false);
+
 	    return FALSE;
 
 	} else {
@@ -368,8 +377,11 @@ bool XrdClient::Open(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) {
 	    if (doitparallel) {
 		Info(XrdClientDebug::kUSERDEBUG, "Open", "File open in progress.");
 	    }
-	    else
+	    else {
 		Info(XrdClientDebug::kUSERDEBUG, "Open", "File opened succesfully.");
+                if (fXrdCcb) 
+                   fXrdCcb->OpenComplete(this, fXrdCcbArg, true);
+            }
 
 	}
 
@@ -986,6 +998,7 @@ bool XrdClient::TryOpen(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) {
     // otherwise return FALSE
     if (fConnModule->LastServerResp.status != kXR_NotFound) {
 	TerminateOpenAttempt();
+
 	return FALSE;
     }
 
@@ -1018,12 +1031,14 @@ bool XrdClient::TryOpen(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) {
 	    XrdClientMStream::EstablishParallelStreams(fConnModule);
 
 	    TerminateOpenAttempt();
+
 	    return TRUE;
 	}
 	else {
 
 	    Error("Open", "Error opening the file.");
 	    TerminateOpenAttempt();
+
 	    return FALSE;
 	}
 
@@ -1180,7 +1195,10 @@ bool XrdClient::Close() {
     }
 
     ClientRequest closeFileRequest;
-  
+
+    // Set the max transaction duration
+    fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
+
     memset(&closeFileRequest, 0, sizeof(closeFileRequest) );
 
     fConnModule->SetSID(closeFileRequest.header.streamid);
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh
index bac4dfb5cad3cc2f6ae90df4cf9ba3d39828b9c5..60354c18d78c9bd0ac4d21278f22b745db967b68 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh
@@ -177,7 +177,7 @@ protected:
 
 public:
 
-    XrdClient(const char *url);
+    XrdClient(const char *url, XrdClientCallback *XrdCcb = 0, void *XrdCcbArg = 0);
     virtual ~XrdClient();
 
     UnsolRespProcResult         ProcessUnsolicitedMsg(XrdClientUnsolMsgSender *sender,
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh
index b211b0d63504a6fe29a82c8efa353a454a9d16d5..74cd9f1220e8f33e2a53ec69f7f9880302a49e4f 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh
@@ -19,6 +19,8 @@
 #include "XrdClient/XrdClientUnsolMsg.hh"
 #include "XrdClient/XrdClientConn.hh"
 
+class XrdClientCallback;
+
 class XrdClientAbs: public XrdClientAbsUnsolMsgHandler {
 
    // Do NOT abuse of this
@@ -30,6 +32,11 @@ protected:
 
    char                        fHandle[4];  // The file handle returned by the server,
                                             // to be used for successive requests
+
+
+   XrdClientCallback*          fXrdCcb;
+   void *                      fXrdCcbArg;
+   
    // After a redirection the file must be reopened.
    virtual bool OpenFileWhenRedirected(char *newfhandle, 
 				       bool &wasopen) = 0;
@@ -40,8 +47,12 @@ protected:
 
 public:
 
-   XrdClientAbs() {
+   XrdClientAbs(XrdClientCallback *XrdCcb = 0, void *XrdCcbArg = 0) {
       memset( fHandle, 0, sizeof(fHandle) );
+
+      // Set the callback object, if any
+      fXrdCcb = XrdCcb;
+      fXrdCcbArg = XrdCcbArg;
    }
 
    virtual bool IsOpen_wait() {
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc
index ed54b3ed1993ec8255f16a0407f51cc60b5def07..15ef41e836c4789aeeeedc213887a5b710fae436 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc
@@ -871,7 +871,166 @@ bool XrdClientAdmin::Prepare(const char *buf, kXR_char option, kXR_char prty)
 }
 
 //_____________________________________________________________________________
-bool  XrdClientAdmin::DirList(const char *dir, vecString &entries) {
+bool  XrdClientAdmin::DirList(const char *dir, vecString &entries, bool askallservers) {
+   // Get an ls-like output with respect to the specified dir
+
+   // If this is a redirector, we will be given the list of the servers
+   //  which host this directory
+   // If askallservers is true then we will just ask for the whole list of servers.
+   //  the query will always be the same, and this will likely skip the 5s delay after the first shot
+   //  The danger is to be forced to contact a huge number of servers in very big clusters
+   //
+   bool ret = true;
+   XrdClientVector<XrdClientLocate_Info> hosts;
+   if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) {
+      char str[1024];
+      strcpy(str, "*");
+      strncat(str, dir, 1023);
+      if (!Locate((kXR_char *)str, hosts)) return false;
+   }
+   else {
+      XrdClientLocate_Info nfo;
+      memset(&nfo, 0, sizeof(nfo));
+      strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str());
+      hosts.Push_back(nfo);
+   }
+
+
+   // Then we cycle among them asking everyone
+   bool foundsomething = false;
+   for (int i = 0; i < hosts.GetSize(); i++) {
+
+      fConnModule->Disconnect(false);
+      XrdClientUrlInfo url((const char *)hosts[i].Location);
+
+      url.Proto = "root";
+
+      if (fConnModule->GoToAnotherServer(url) != kOK) {
+         ret = false;
+         break;
+      }
+
+      fConnModule->ClearLastServerError();
+      if (!DirList_low(dir, entries))  {
+         if (fConnModule->LastServerError.errnum != kXR_NotFound) {
+            ret = false;
+            break;
+         }
+      }
+      else foundsomething = true;
+
+
+   }
+
+   // At the end we want to rewind to the main redirector in any case
+   GoBackToRedirector();
+
+   if (!foundsomething) ret = false;
+   return ret;
+}
+
+//_____________________________________________________________________________
+bool  XrdClientAdmin::DirList(const char *dir,
+                              XrdClientVector<XrdClientAdmin::DirListInfo> &dirlistinfo,
+                              bool askallservers) {
+   // Get an ls-like output with respect to the specified dir
+   // Here we are also interested in the stat information for each file
+
+   // If this is a redirector, we will be given the list of the servers
+   //  which host this directory
+   // If askallservers is true then we will just ask for the whole list of servers.
+   //  the query will always be the same, and this will likely skip the 5s delay after the first shot
+   //  The danger is to be forced to contact a huge number of servers in very big clusters
+   //  If this is a concern, one should set askallservers to false
+   //
+   bool ret = true;
+   vecString entries;
+   XrdClientVector<XrdClientLocate_Info> hosts;
+   XrdOucString fullpath;
+
+   if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) {
+      char str[1024];
+      strcpy(str, "*");
+      strncat(str, dir, 1023);
+      if (!Locate((kXR_char *)str, hosts)) return false;
+   }
+   else {
+      XrdClientLocate_Info nfo;
+      memset(&nfo, 0, sizeof(nfo));
+      strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str());
+      hosts.Push_back(nfo);
+   }
+
+
+   // Then we cycle among them asking everyone
+   bool foundsomething = false;
+   for (int i = 0; i < hosts.GetSize(); i++) {
+
+      fConnModule->Disconnect(false);
+      XrdClientUrlInfo url((const char *)hosts[i].Location);
+      url.Proto = "root";
+
+      if (fConnModule->GoToAnotherServer(url) != kOK) {
+         ret = false;
+         break;
+      }
+
+      fConnModule->ClearLastServerError();
+
+      int precentries = entries.GetSize();
+      if (!DirList_low(dir, entries)) {
+         if ((fConnModule->LastServerError.errnum != kXR_NotFound) && (fConnModule->LastServerError.errnum != kXR_noErrorYet)) {
+            ret = false;
+            break;
+         }
+      }
+      else foundsomething = true;
+
+      int newentries = entries.GetSize();
+
+      DirListInfo info;
+      dirlistinfo.Resize(newentries);
+
+      // Here we have the entries. We want to accumulate the stat information for each of them
+      // We are still connected to the same server which gave the last dirlist response
+      info.host = GetCurrentUrl().HostWPort;
+      for (int k = precentries; k < newentries; k++) {
+         info.fullpath = dir;
+         if (info.fullpath[info.fullpath.length()-1] != '/') info.fullpath += "/";
+         info.fullpath += entries[k];
+         info.size = 0;
+         info.id = 0;
+         info.flags = 0;
+         info.modtime = 0;
+
+         if (!Stat(info.fullpath.c_str(),
+                   info.id,
+                   info.size,
+                   info.flags,
+                   info.modtime)) {
+            ret = false;
+            //break;
+         }
+
+         dirlistinfo[k] = info;
+
+      }
+
+   }
+
+   // At the end we want to rewind to the main redirector in any case
+   GoBackToRedirector();
+
+   if (!foundsomething) ret = false;
+   return ret;
+ }
+
+
+
+
+
+//_____________________________________________________________________________
+bool  XrdClientAdmin::DirList_low(const char *dir, vecString &entries) {
    bool ret;
    // asks the server for the content of a directory
    ClientRequest DirListFileRequest;
@@ -893,25 +1052,26 @@ bool  XrdClientAdmin::DirList(const char *dir, vecString &entries) {
    // Now parse the answer building the entries vector
    if (ret) {
 
-      kXR_char *entry, *startp = dl, *endp = dl;
+      kXR_char *startp = dl, *endp = dl;
+      char entry[1024];
+      XrdOucString e;
 
-      while (endp) {
+      while (startp) {
 
 	 if ( (endp = (kXR_char *)strchr((const char*)startp, '\n')) ) {
-            entry = (kXR_char *)malloc(endp-startp+1);
-            memset((char *)entry, 0, endp-startp+1);
-	    strncpy((char *)entry, (char *)startp, endp-startp);
+	    strncpy(entry, (char *)startp, endp-startp);
+            entry[endp-startp] = 0;
 	    endp++;
 	 }
 	 else
-	    entry = (kXR_char *)strdup((char *)startp);
+	    strcpy(entry, (char *)startp);
       
-	 if (entry && strlen((char *)entry)) {
-	    XrdOucString e((const char *)entry);
 
+         if (strlen(entry) && strcmp((char *)entry, ".") && strcmp((char *)entry, "..")) {
+	    e = entry;
 	    entries.Push_back(e);
-	    free(entry);
-	 }
+         }
+
 
 	 startp = endp;
       }
@@ -925,7 +1085,6 @@ bool  XrdClientAdmin::DirList(const char *dir, vecString &entries) {
 
 }
 
-
 //_____________________________________________________________________________
 long XrdClientAdmin::GetChecksum(kXR_char *path, kXR_char **chksum)
 {
@@ -1078,7 +1237,7 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientLocate_Info &resp, bool wri
        resp.CanWrite = 1;
        strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str());
      }
-     fConnModule->GoBackToRedirector();
+     GoBackToRedirector();
      return ok;
    }
 
@@ -1164,7 +1323,7 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientLocate_Info &resp, bool wri
    }
 
    // At the end we want to rewind to the main redirector in any case
-   fConnModule->GoBackToRedirector();
+   GoBackToRedirector();
 
    return found;
 }
@@ -1201,7 +1360,8 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientVector<XrdClientLocate_Info
        strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str());
        hosts.Push_back(resp);
      }
-     fConnModule->GoBackToRedirector();
+     GoBackToRedirector();
+
      return ok;
    }
 
@@ -1258,7 +1418,7 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientVector<XrdClientLocate_Info
    }
    
    // At the end we want to rewind to the main redirector in any case
-   fConnModule->GoBackToRedirector();
+   GoBackToRedirector();
 
    return (hosts.GetSize() > 0);
 }
@@ -1295,9 +1455,137 @@ bool XrdClientAdmin::Truncate(const char *path, long long newsize) {
 // Quickly jump to the former redirector. Useful after having been redirected.
 void XrdClientAdmin::GoBackToRedirector() {
 
-  if (fConnModule)
-    fConnModule->GoBackToRedirector();
+   if (fConnModule) {
+      fConnModule->GoBackToRedirector();
+
+      if (!fConnModule->IsConnected()) {
+         XrdClientUrlInfo u(fInitialUrl);
+         fConnModule->GoToAnotherServer(u);
+      }
+
+   }
+
+
+
+}
+
+
+
+// Compute an estimation of the available free space in the given cachefs partition
+// The estimation can be fooled if multiple servers mount the same network storage
+bool XrdClientAdmin::GetSpaceInfo(const char *logicalname,
+                                  long long &totspace,
+                                  long long &totfree,
+                                  long long &totused,
+                                  long long &largestchunk) {
+
+   bool ret = true;
+   XrdClientVector<XrdClientLocate_Info> hosts;
+
+   totspace = 0;
+   totfree = 0;
+   totused = 0;
+   largestchunk = 0;
+
+   if (fConnModule->GetServerProtocol() >= 0x291) {
+      if (!Locate((kXR_char *)"*", hosts)) return false;
+   }
+   else {
+      XrdClientLocate_Info nfo;
+      memset(&nfo, 0, sizeof(nfo));
+      strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str());
+      hosts.Push_back(nfo);
+   }
+
+
+   // Then we cycle among them asking everyone
+   for (int i = 0; i < hosts.GetSize(); i++) {
+
+      fConnModule->Disconnect(false);
+      XrdClientUrlInfo url((const char *)hosts[i].Location);
 
+      url.Proto = "root";
+
+      if (fConnModule->GoToAnotherServer(url) != kOK) {
+         ret = false;
+         break;
+      }
+
+
+
+      // Fire the query request and update the results
+      ClientRequest qspacereq;
+
+      // Set the max transaction duration
+      fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
+
+
+      memset( &qspacereq, 0, sizeof(qspacereq) );
+
+      fConnModule->SetSID(qspacereq.header.streamid);
+
+      qspacereq.query.requestid = kXR_query;
+      qspacereq.query.infotype = kXR_Qspace;
+      qspacereq.query.dlen = ( logicalname ? strlen(logicalname) : 0);
+
+      char *resp = 0;
+      if (fConnModule->SendGenCommand(&qspacereq, logicalname,
+                                      (void **)&resp, 0, TRUE,
+                                      (char *)"GetSpaceInfo")) {
+
+         XrdOucString rs(resp), s;
+         free(resp);
+
+         // Here we have the response relative to a server
+         // Now we are going to have fun in parsing it
+
+         int from = 0;
+         while ((from = rs.tokenize(s,from,'&')) != -1) {
+            if (s.length() < 4) continue;
+
+            int pos = s.find("=");
+            XrdOucString tk, val;
+            if (pos != STR_NPOS) {
+               tk.assign(s, 0, pos-1);
+               val.assign(s, pos+1);
+#ifndef WIN32
+               if ( (tk == "oss.space") && (val.length() > 1) ) {
+                  totspace += atoll(val.c_str());
+               } else
+                  if ( (tk == "oss.free") && (val.length() > 1) ) {
+                     totfree += atoll(val.c_str());
+                  } else
+                     if ( (tk == "oss.maxf") && (val.length() > 1) ) {
+                        largestchunk = xrdmax(largestchunk, atoll(val.c_str()));
+                     } else
+                        if ( (tk == "oss.used") && (val.length() > 1) ) {
+                           totused += atoll(val.c_str());
+                        }
+#else
+               if ( (tk == "oss.space") && (val.length() > 1) ) {
+                  totspace += _atoi64(val.c_str());
+               } else
+                  if ( (tk == "oss.free") && (val.length() > 1) ) {
+                     totfree += _atoi64(val.c_str());
+                  } else
+                     if ( (tk == "oss.maxf") && (val.length() > 1) ) {
+                        largestchunk = xrdmax(largestchunk, _atoi64(val.c_str()));
+                     } else
+                        if ( (tk == "oss.used") && (val.length() > 1) ) {
+                           totused += _atoi64(val.c_str());
+                        }
+#endif
+            }
+         }
+
+
+      }
+
+   }
+
+   // At the end we want to rewind to the main redirector in any case
+   GoBackToRedirector();
+   return ret;
 
 
 }
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh
index 1ac0813bd02db08c9b2d9213b274a85f0fb3bd90..3292a6fb939526bc3c1aea6afca366eae1b0a065 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh
@@ -45,7 +45,7 @@ struct XrdClientLocate_Info {
 class XrdClientAdmin : public XrdClientAbs {
 
    XrdOucString                    fInitialUrl;
-
+   bool                            DirList_low(const char *dir, vecString &entries);
    int                             LocalLocate(kXR_char *path,
 					       XrdClientVector<XrdClientLocate_Info> &res,
 					       bool writable, bool nowait, bool all = false);
@@ -85,7 +85,19 @@ class XrdClientAdmin : public XrdClientAbs {
 					    int &stagingutil);
 
    bool                            DirList(const char *dir,
-                                           vecString &);
+                                           vecString &entries, bool askallservers=false);
+
+   struct DirListInfo {
+      XrdOucString fullpath;
+      XrdOucString host;
+      long long size;
+      long id;
+      long flags;
+      long modtime;
+   };
+   bool                            DirList(const char *dir,
+                                           XrdClientVector<DirListInfo> &dirlistinfo,
+                                           bool askallservers=false);
 
    bool                            ExistFiles(vecString&,
                                               vecBool&);
@@ -93,6 +105,14 @@ class XrdClientAdmin : public XrdClientAbs {
    bool                            ExistDirs(vecString&,
                                              vecBool&);
 
+   // Compute an estimation of the available free space in the given cachefs partition
+   // The estimation can be fooled if multiple servers mount the same network storage
+   bool                            GetSpaceInfo(const char *logicalname,
+                                                long long &totspace,
+                                                long long &totfree,
+                                                long long &totused,
+                                                long long &largestchunk);
+   
    long                            GetChecksum(kXR_char *path,
                                                kXR_char **chksum);
 
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientCallback.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientCallback.hh
new file mode 100644
index 0000000000000000000000000000000000000000..01ac8fdc1b6c74171aa3bf5082f86ed1586033e4
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientCallback.hh
@@ -0,0 +1,32 @@
+//////////////////////////////////////////////////////////////////////////
+//                                                                      //
+// XrdClientCallback                                                    // 
+//                                                                      //
+// Author: Fabrizio Furano (CERN IT-DSS, 2009)                          //
+//                                                                      //
+// Base class for objects receiving events from XrdClient               //
+//                                                                      //
+//////////////////////////////////////////////////////////////////////////
+
+//       $Id$
+
+#ifndef XRD_CLIENTCALLBACK_H
+#define XRD_CLIENTCALLBACK_H
+
+class XrdClientAbs;
+
+class XrdClientCallback
+{
+
+public:
+
+   // Invoked when an Open request completes with some result.
+   virtual void OpenComplete(XrdClientAbs *clientP, void *cbArg, bool res) = 0;
+
+   XrdClientCallback() {}
+   virtual ~XrdClientCallback() {}
+};
+
+
+
+#endif
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc
index 6c0baaff81fb687d9b3f87b94333bc527527f496..26273cea3a25d3347a4f83598725c4fbfd193975 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc
@@ -904,7 +904,8 @@ bool XrdClientConn::CheckErrorStatus(XrdClientMessage *mex, short &Retry, char *
 
 	    fOpenError = (XErrorCode)ntohl(body_err->errnum);
  
-	    Info(XrdClientDebug::kNODEBUG, "CheckErrorStatus", "Server declared: " <<
+	    Info(XrdClientDebug::kNODEBUG, "CheckErrorStatus", "Server [" << GetCurrentUrl().HostWPort <<
+                 "] declared: " <<
 		 (const char*)body_err->errmsg << "(error code: " << fOpenError << ")");
 
 	    // Save the last error received
@@ -2050,6 +2051,10 @@ XReqErrorType XrdClientConn::GoToAnotherServer(XrdClientUrlInfo &newdest)
     // Re-directs to another server
    
     fGettingAccessToSrv = false; 
+
+    if (!newdest.Port) newdest.Port = 1094;
+    if (newdest.HostAddr == "") newdest.HostAddr = newdest.Host;
+
     if ( (fLogConnID = Connect( newdest, fUnsolMsgHandler)) == -1) {
 	  
 	// Note: if Connect is unable to work then we are in trouble.
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc
index adf5e43da8ac277012c6082285c1528d8da24434..d643dba3abfa701371576882f43690ad64a428b6 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc
@@ -38,6 +38,7 @@ kXR_int64 XrdClientReadV::ReqReadV(XrdClientConn *xrdc, char *handle, char *dest
 
 	    memcpy( &(buflis[i].fhandle), handle, 4 ); 
 
+
 	    if (!destbuf)
 		xrdc->SubmitPlaceholderToCache(reqvect[firstreq+i].offset,
 					       reqvect[firstreq+i].offset +
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh
index 17db0151d50b8bc51220ea084f318bdff17c9812..fada782b0b6c4053d8cc291bcd66aadfcfb22389 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh
@@ -160,7 +160,20 @@ public:
     }
 
     void Resize(int newsize) {
-	BufRealloc(newsize);
+        long oldsize = size;
+
+        if (newsize > oldsize) {
+           BufRealloc(newsize);
+           T *item = new T;
+           // Add new elements if needed
+           for (long i = oldsize; i < newsize; i++) {
+              put(*item, size++);
+           }
+        }
+        else {
+           for (long i = oldsize; i > newsize; i--)
+              Erase(i-1, false);
+        }
     }
 
     void Push_back(T& item) {
diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc
index 75a022375c9b9568640ac3c0bc60d1769cb166ea..8b16761fee4f347dd598e6f77889420ef699477f 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc
@@ -25,6 +25,7 @@ const char *XrdCommandLineCVSID = "$Id$";
 #include <unistd.h>
 #include <stdarg.h>
 #include <sstream>
+#include <signal.h>
 
 #ifdef HAVE_READLINE
 #include <readline/readline.h>
@@ -71,7 +72,7 @@ extern "C" {
 
 
 
-#define XRDCLI_VERSION            "(C) 2004-2010 by the Xrootd group. $Revision: 1.24 $ - Xrootd version: "XrdVSTRING
+#define XRDCLI_VERSION            "(C) 2004-2010 by the Xrootd group. $Revision: 1.33 $ - Xrootd version: "XrdVSTRING
 
 
 ///////////////////////////////////////////////////////////////////////
@@ -91,12 +92,21 @@ char *initialhost;
 
 XrdClient *genclient = 0;
 XrdClientAdmin *genadmin = 0;
-XrdOucString currentpath;
+XrdOucString currentpath = "/";
 
 XrdOucString  cmdline_cmd;
 
 ///////////////////////
 
+
+void
+CtrlCHandler(int sig) {
+   cerr << endl << "Please use 'exit' to terminate this program." << endl;
+   return;
+}
+
+
+
 void PrintUsage() {
    cerr << "usage: xrd [host]"
 
@@ -144,6 +154,8 @@ void PrintHelp() {
 
    cout << endl <<
       XRDCLI_VERSION << endl << endl <<
+      "Usage: xrd [-O<opaque_info>] [-DS<var_name> stringvalue] [-DI<var_name> integervalue] [host[:port]] [batchcommand]" << endl << endl <<
+
       "List of available commands:" << endl <<
       " cat <filename> [xrdcp parameters]" << endl <<
       "  outputs a file on standard output using xrdcp. <filename> can be a root:// URL." << endl <<
@@ -159,6 +171,8 @@ void PrintHelp() {
       "  current remote path. Also, they can be root:// URLs specifying any other host." << endl <<
       " dirlist [dirname]" << endl <<
       "  gets the requested directory listing." << endl <<
+      " dirlistrec [dirname]" << endl <<
+      "  gets the requested recursive directory listing." << endl <<
       " envputint <varname> <intval>" << endl <<
       "  puts an integer in the internal environment." << endl <<
       " envputstring <varname> <stringval>" << endl <<
@@ -198,6 +212,8 @@ void PrintHelp() {
       "  stages a file in." << endl <<
       " query <reqcode> <parms>" << endl <<
       "  obtain server information" << endl <<
+      " queryspace <logicalname>" << endl <<
+      "  obtain space information" << endl <<
       endl <<
       "For further information, please read the xrootd protocol documentation." << endl <<
       endl;
@@ -263,6 +279,7 @@ void PrintLocateInfo(XrdClientLocate_Info &loc) {
 int main(int argc, char**argv) {
 
    int retval = 0;
+   //signal(SIGINT, CtrlCHandler);
 
    DebugSetLevel(0);
 
@@ -285,7 +302,8 @@ int main(int argc, char**argv) {
 	 continue;
       }
 
-      if ( (strstr(argv[i], "-h") == argv[i])) {
+      if ( (strstr(argv[i], "-h") == argv[i]) || 
+           (strstr(argv[i], "--help") == argv[i]) ) {
 	 PrintUsage();
 	 exit(0);
 	 continue;
@@ -381,19 +399,33 @@ int main(int argc, char**argv) {
 
 	 // Quite trivial directory processing
 	 if (!strcmp(parmname, "..")) {
+            if (currentpath == "/") continue;
+
 	    int pos = currentpath.rfind('/');
 
 	    if (pos != STR_NPOS)
 	       currentpath.erase(pos);
 
-	    retval = 1;
-	 }
+            if (!currentpath.length()) {
+               currentpath = "/";
+            }
 
-	 if (!strcmp(parmname, "."))
 	    retval = 1;
+            continue;
+	 }
+         else
+            if (!strcmp(parmname, ".")) {
+               retval = 1;
+               continue;
+            }
 	    
-	 currentpath += "/";
-	 currentpath += parmname;
+         if (!currentpath.length() || (currentpath[currentpath.length()-1] != '/')) {
+            currentpath += "/";
+         }
+
+         if (parmname[0] == '/') currentpath = parmname;
+         else
+            currentpath += parmname;
 
       }
       else
@@ -471,6 +503,122 @@ int main(int argc, char**argv) {
 
       }
       else
+      // -------------------------- dirlistrec ---------------------------
+      if (!strcmp(cmd, "dirlistrec")) {
+         XrdClientVector<XrdOucString> pathq;
+
+	 if (!genadmin) {
+	    cout << "Not connected to any server." << endl;
+	    retval = 1;
+	 }
+
+	 char *dirname = tkzer.GetToken(0, 0);
+	 XrdOucString path;
+
+	 if (dirname) {
+	    if (dirname[0] == '/')
+	       path = dirname;
+	    else {
+               if ((currentpath.length() > 0) && (currentpath[currentpath.length()-1] != '/'))
+                  path = currentpath + "/" + dirname;
+               else
+                  path = currentpath + dirname;
+
+            }
+	 }
+	 else path = currentpath;
+
+	 if (!path.length()) {
+	    cout << "The current path is an empty string. Assuming '/'." << endl;
+	    path = '/';
+	 }
+
+
+         // Initialize the queue with this path
+         pathq.Push_back(path);
+
+
+         while (pathq.GetSize() > 0) {
+            XrdOucString pathtodo = pathq.Pop_back();
+
+            // Now try to issue the request
+            XrdClientVector<XrdClientAdmin::DirListInfo> nfo;
+            if (!genadmin->DirList(pathtodo.c_str(), nfo, true)) {
+               retval = 1;  
+               cout << "Error listing path '" << pathtodo << "' in server " <<
+                  genadmin->GetCurrentUrl().HostWPort <<
+                  " The path does not exist in some servers or there are malformed filenames." << endl;
+            }
+
+            // Now check the answer
+            if (!CheckAnswer(genadmin)) {
+               retval = 1;
+               cout << "Error '" << genadmin->LastServerError()->errmsg <<
+                  "' listing path '" << pathtodo <<
+                  "' in server" << genadmin->GetCurrentUrl().HostWPort <<
+                  " or in some of its child nodes." << endl;
+            }
+      
+            for (int i = 0; i < nfo.GetSize(); i++) {
+
+               if ((nfo[i].flags & kXR_isDir) &&
+                   (nfo[i].flags & kXR_readable) &&
+                   (nfo[i].flags & kXR_xset)) {
+                  
+
+                  // The path has not to be pushed if it's already present
+                  // This may happen if several servers have the same path
+                  bool foundpath = false;
+                  for (int ii = 0; ii < pathq.GetSize(); ii++) {
+                     if (nfo[i].fullpath == pathq[ii]) {
+                        foundpath = true;
+                        break;
+                     }
+                  }
+                  
+                  if (!foundpath)
+                     pathq.Push_back(nfo[i].fullpath);
+                  else 
+                     // If the path is already present in the queue then it was already printed as well.
+                     continue;
+
+               }
+
+               char ts[256];
+               strcpy(ts, "n/a");
+
+               struct tm *t = gmtime(&nfo[i].modtime);
+               strftime(ts, 255, "%F %T", t);
+
+               char cflgs[16];
+               memset(cflgs, 0, 16);
+
+               if (nfo[i].flags & kXR_isDir)
+                  strcat(cflgs, "d");
+               else strcat(cflgs, "-");
+
+               if (nfo[i].flags & kXR_readable)
+                  strcat(cflgs, "r");
+               else strcat(cflgs, "-");
+
+               if (nfo[i].flags & kXR_writable)
+                  strcat(cflgs, "w");
+               else strcat(cflgs, "-");
+
+               if (nfo[i].flags & kXR_xset)
+                  strcat(cflgs, "x");
+               else strcat(cflgs, "-");
+
+               printf("%s(%03ld) %12lld %s %s\n", cflgs, nfo[i].flags, nfo[i].size, ts, nfo[i].fullpath.c_str());
+
+            }
+            
+            if (nfo.GetSize()) cout << endl;
+         }
+
+         if (retval) cout << "Errors during processing. Please check them." << endl;
+      }
+      else
       // -------------------------- dirlist ---------------------------
       if (!strcmp(cmd, "dirlist")) {
 
@@ -485,28 +633,68 @@ int main(int argc, char**argv) {
 	 if (dirname) {
 	    if (dirname[0] == '/')
 	       path = dirname;
-	    else
-	       path = currentpath + "/" + dirname;
+	    else {
+               if ((currentpath.length() > 0) && (currentpath[currentpath.length()-1] != '/'))
+                  path = currentpath + "/" + dirname;
+               else
+                  path = currentpath + dirname;
+
+            }
 	 }
 	 else path = currentpath;
 
 	 if (!path.length()) {
-	    cout << "The current path is empty." << endl;
-	    retval = 1;
+	    cout << "The current path is an empty string. Assuming '/'." << endl;
+	    path = '/';
 	 }
 
 	 // Now try to issue the request
-	 vecString vs;
-	 genadmin->DirList(path.c_str(), vs);
+         XrdClientVector<XrdClientAdmin::DirListInfo> nfo;
+	 if (!genadmin->DirList(path.c_str(), nfo, true)) {
+            //nfo.Clear();
+            retval = 1;
+            
+         }
 
 	 // Now check the answer
-	 if (!CheckAnswer(genadmin))
+	 if (!CheckAnswer(genadmin)) {
 	    retval = 1;
+            //nfo.Clear();
+         }
       
-	 for (int i = 0; i < vs.GetSize(); i++)
-	    cout << vs[i] << endl;
 
-	 cout << endl;
+            for (int i = 0; i < nfo.GetSize(); i++) {
+               char ts[256];
+               strcpy(ts, "n/a");
+
+               struct tm *t = gmtime(&nfo[i].modtime);
+               strftime(ts, 255, "%F %T", t);
+
+               char cflgs[16];
+               memset(cflgs, 0, 16);
+
+               if (nfo[i].flags & kXR_isDir)
+                  strcat(cflgs, "d");
+               else strcat(cflgs, "-");
+
+               if (nfo[i].flags & kXR_readable)
+                  strcat(cflgs, "r");
+               else strcat(cflgs, "-");
+
+               if (nfo[i].flags & kXR_writable)
+                  strcat(cflgs, "w");
+               else strcat(cflgs, "-");
+
+               if (nfo[i].flags & kXR_xset)
+                  strcat(cflgs, "x");
+               else strcat(cflgs, "-");
+
+               printf("%s(%03ld) %12lld %s %s\n", cflgs, nfo[i].flags, nfo[i].size, ts, nfo[i].fullpath.c_str());
+
+            }
+
+            if (retval) cout << "Errors during processing. Please check." << endl;
+            cout << endl;
 
       }
       else
@@ -1276,6 +1464,36 @@ int main(int argc, char**argv) {
 
 	 cout << endl;
 	
+      }
+      else
+      // -------------------------- queryspace ---------------------------
+      if (!strcmp(cmd, "queryspace")) {
+
+	 if (!genadmin) {
+	    cout << "Not connected to any server." << endl;
+	    retval = 1;
+	 }
+
+	 char *ns = tkzer.GetToken(0, 0);
+         long long totspace;
+         long long totfree;
+         long long totused;
+         long long largestchunk;
+
+	 genadmin->GetSpaceInfo(ns, totspace, totfree, totused, largestchunk);
+
+	 // Now check the answer
+	 if (!CheckAnswer(genadmin))
+	   retval = 1;
+      
+	 cout << "Disk space approximations (MB):" << endl <<
+            "Total         : " << totspace/(1024*1024) << endl <<
+            "Free          : " << totfree/(1024*1024) << endl <<
+            "Used          : " << totused/(1024*1024) << endl <<
+            "Largest chunk : " << largestchunk/(1024*1024) << endl;
+
+	 cout << endl;
+
       }
       else {
 
@@ -1286,7 +1504,7 @@ int main(int argc, char**argv) {
       }
 
       
-      free(linebuf);
+      delete[] linebuf;
 
       // if it was a cmd from the commandline...
       if (cmdline_cmd.length() > 0) break;
diff --git a/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc b/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc
index bd8a2cccd105186bf87cf08cf061ed2a986771d5..0883937b41404894ac33a0f0c0df26ace9a88b67 100644
--- a/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc
+++ b/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc
@@ -88,7 +88,7 @@ struct XrdCpInfo {
 
 #define XRDCP_BLOCKSIZE          (4*1024*1024)
 #define XRDCP_XRDRASIZE          (20*XRDCP_BLOCKSIZE)
-#define XRDCP_VERSION            "(C) 2004-2010 by the Xrootd group. $Revision: 1.99 $ - Xrootd version: "XrdVSTRING
+#define XRDCP_VERSION            "(C) 2004-2010 by the Xrootd group. $Revision: 1.100 $ - Xrootd version: "XrdVSTRING
 
 ///////////////////////////////////////////////////////////////////////
 // Coming from parameters on the cmd line
@@ -662,12 +662,13 @@ int doCp_xrd2xrd(XrdClient **xrddest, const char *src, const char *dst) {
                cpnfo.mon->PutProgressInfo(bytesread, cpnfo.len, (float)bytesread / cpnfo.len * 100.0);
 
             free(buf);
-         }
 
-         if (!xrdxtrdfile && ((buf == 0) || (len == 0)) && (bytesread >= size)) {
-            if (buf) free(buf);
-            break;
          }
+         else
+            if (!xrdxtrdfile && ( ((buf == 0) && (len == 0)) || (bytesread >= size))) {
+               if (buf) free(buf);
+               break;
+            }
 
       }
       else {
@@ -933,12 +934,13 @@ int doCp_xrd2loc(const char *src, const char *dst) {
 	      cpnfo.mon->PutProgressInfo(bytesread, cpnfo.len, (float)bytesread / cpnfo.len * 100.0);
 
 	    free(buf);
-	 }
 
-         if (!xrdxtrdfile && ((buf == 0) || (len == 0)) && (bytesread >= size)) {
-            if (buf) free(buf);
-            break;
-         }
+	 }
+         else
+            if (!xrdxtrdfile && ( ((buf == 0) && (len == 0)) || (bytesread >= size)) ) {
+               if (buf) free(buf);
+               break;
+            }
 
 
       }
diff --git a/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile b/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile
index cc02e27878622d331fbdfd02c2deec8ae695070e..918fe62f48cb9567c49837ab59b42bb6f466efd0 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile
+++ b/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile,v 1.19 2009/11/03 02:52:19 abh Exp $
+#              $Id: GNUmakefile,v 1.20 2010/01/05 01:16:38 abh Exp $
 
 #------------------------------------------------------------------------------#
 #                       C o m m o n   V a r i a b l e s                        #
@@ -446,7 +446,7 @@ $(OBJDIR)/XrdCmsRRQ.o:       XrdCmsRRQ.cc        XrdCmsRRQ.hh \
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdCmsRRQ.o XrdCmsRRQ.cc
 
 $(OBJDIR)/XrdCmsRTable.o:    XrdCmsRTable.cc     XrdCmsRTable.hh \
-                             XrdCmsNode.hh       XrdCmsTrace.hh \
+                             XrdCmsNode.hh       XrdCmsTrace.hh  XrdCmsTypes.hh\
                              XrdSysPthread.hh
 	@echo Compiling XrdCmsRTable.cc 
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdCmsRTable.o XrdCmsRTable.cc
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc
index f977e4c90db4a23cef613bfadd299ab415c4770c..3d297557977533730f8ba7200b150a05e5a74338 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc
@@ -335,7 +335,7 @@ void XrdCmsCache::Bounce(SMask_t smask, int SNum)
   
 void XrdCmsCache::Drop(SMask_t smask, int SNum, int xHi)
 {
-   SMask_t nmask = ~smask;
+   SMask_t nmask(~smask);
 
 // Remove the node from the path list
 //
@@ -451,7 +451,7 @@ void XrdCmsCache::Dispatch(XrdCmsKeyItem *iP, short roQ, short rwQ)
 SMask_t XrdCmsCache::getBVec(unsigned int TODa, unsigned int &TODb)
 {
    EPNAME("getBVec");
-   SMask_t BVec = 0;
+   SMask_t BVec(0);
    long long i;
 
 // See if we can use a previously calculated bVec
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc
index cc97dd47bc88e88a6153b8517185dbff80859b35..11b8d5d689da10671e6cbb1bfd86d4d7e91a0056 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc
@@ -222,11 +222,10 @@ void *XrdCmsClientMan::Start()
        // must receive the respwait before the subsequent response.
        //
        while(Receive())
-            if (Response.modifier & CmsResponse::kYR_async) relayResp();
-               else if (Response.rrCode == kYR_status) setStatus();
-                       else {XrdCmsClientMsg::Reply(HPfx, Response, NetBuff);
-                             if (Response.rrCode == kYR_waitresp) syncResp.Wait();
-                            }
+                 if (Response.modifier & CmsResponse::kYR_async) relayResp();
+            else if (Response.rrCode == kYR_status) setStatus();
+            else if (XrdCmsClientMsg::Reply(HPfx, Response, NetBuff))
+                    {if (Response.rrCode == kYR_waitresp) syncResp.Wait();}
 
        // Tear down the connection
        //
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc
index 40454e5d7565018f4caae92b3427115cef90993a..423292184b0f0fb011843ea9606a82b578c75e62 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc
@@ -111,7 +111,7 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status,
    const char *act = "";
    unsigned int ipaddr;
    XrdCmsNode *nP = 0;
-   int Slot, Free = -1, Bump1 = -1, Bump2 = -1, Bump3 = -1;
+   int Slot, Free = -1, Bump1 = -1, Bump2 = -1, Bump3 = -1, aSet = 0;
    int tmp, Special = (Status & (CMS_isMan|CMS_isPeer));
    XrdSysMutexHelper STMHelper(STMutex);
 
@@ -124,15 +124,16 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status,
 // Slot  = Reconnecting node
 // Free  = Available slot           ( 1st in table)
 // Bump1 = Disconnected server      (last in table)
-// Bump2 = Connected    server      (last in table)
+// Bump2 = Connected    server      (last in table) if new one is managr/peer
 // Bump3 = Disconnected managr/peer ( 1st in table) if new one is managr/peer
 //
    for (Slot = 0; Slot < STMax; Slot++)
        if (NodeTab[Slot])
           {if (NodeTab[Slot]->isNode(ipaddr, theNID)) break;
-//Conn//   if (NodeTab[Slot]->isConn)
-              {if (!NodeTab[Slot]->isPerm)   Bump2 = Slot; // Last conn Server
-//Disc//      } else {
+/*Conn*/   if (NodeTab[Slot]->isConn)
+              {if (!NodeTab[Slot]->isPerm && Special)
+                                             Bump2 = Slot; // Last conn Server
+/*Disc*/      } else {
                if ( NodeTab[Slot]->isPerm)
                   {if (Bump3 < 0 && Special) Bump3 = Slot;}//  1st disc Man/Pr
                   else                       Bump1 = Slot; // Last disc Server
@@ -171,7 +172,9 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status,
                     return 0;
                    }
 
-                if (NodeTab[Slot] && !(Status & CMS_isPeer)) sendAList(lp);
+                if (Status & CMS_isMan) {setAltMan(Slot,ipaddr,sport); aSet=1;}
+                if (NodeTab[Slot] && !(Status & CMS_isPeer))
+                   sendAList(NodeTab[Slot]->Link);
 
                 DEBUG(lp->ID << " bumps " << NodeTab[Slot]->Ident <<" #" <<Slot);
                 NodeTab[Slot]->Lock();
@@ -187,7 +190,7 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status,
 
 // Assign new server
 //
-   if (Status & CMS_isMan) setAltMan(Slot, ipaddr, sport);
+   if (!aSet && (Status & CMS_isMan)) setAltMan(Slot, ipaddr, sport);
    if (Slot > STHi) STHi = Slot;
    nP->isBound   = 1;
    nP->isConn    = 1;
@@ -237,7 +240,7 @@ SMask_t XrdCmsCluster::Broadcast(SMask_t smask, const struct iovec *iod,
    EPNAME("Broadcast")
    int i;
    XrdCmsNode *nP;
-   SMask_t bmask, unQueried = 0;
+   SMask_t bmask, unQueried(0);
 
 // Obtain a lock on the table and screen out peer nodes
 //
@@ -304,7 +307,7 @@ SMask_t XrdCmsCluster::getMask(unsigned int IPv4adr)
 {
    int i;
    XrdCmsNode *nP;
-   SMask_t smask = 0;
+   SMask_t smask(0);
 
 // Obtain a lock on the table
 //
@@ -327,7 +330,7 @@ SMask_t XrdCmsCluster::getMask(unsigned int IPv4adr)
 SMask_t XrdCmsCluster::getMask(const char *Cid)
 {
    XrdCmsNode *nP;
-   SMask_t smask = 0;
+   SMask_t smask(0);
    XrdOucTList *cP;
    int i = 1, Cnum = -1;
 
@@ -424,16 +427,35 @@ int XrdCmsCluster::Locate(XrdCmsSelect &Sel)
 {
    EPNAME("Locate");
    XrdCmsPInfo   pinfo;
-   SMask_t       qfVec = 0;
+   SMask_t       qfVec(0);
+   char         *Path;
    int           retc = 0;
 
+// Check if this is a locate for all current servers
+//
+   if (*Sel.Path.Val != '*') Path = Sel.Path.Val;
+      else {if (*(Sel.Path.Val+1) == '\0')
+               {Sel.Vec.hf = ~0LL; Sel.Vec.pf = Sel.Vec.wf = 0;
+                return 0;
+               }
+            Path = Sel.Path.Val+1;
+           }
+
 // Find out who serves this path
 //
-   if (!Cache.Paths.Find(Sel.Path.Val, pinfo) || !pinfo.rovec)
+   if (!Cache.Paths.Find(Path, pinfo) || !pinfo.rovec)
       {Sel.Vec.hf = Sel.Vec.pf = Sel.Vec.wf = 0;
        return -1;
       } else Sel.Vec.wf = pinfo.rwvec;
 
+// Check if this was a non-lookup request
+//
+   if (*Sel.Path.Val == '*')
+      {Sel.Vec.hf = pinfo.rovec; Sel.Vec.pf = 0;
+       Sel.Vec.wf = pinfo.rwvec;
+       return 0;
+      }
+
 // Complete the request info object if we have one
 //
    if (Sel.InfoP)
@@ -481,7 +503,7 @@ void *XrdCmsCluster::MonPerf()
    struct iovec ioV[] = {{(char *)&Usage, sizeof(Usage)}};
    int ioVnum = sizeof(ioV)/sizeof(struct iovec);
    int ioVtot = sizeof(Usage);
-   SMask_t allNodes = ~static_cast<SMask_t>(0);
+   SMask_t allNodes(~0);
    int uInterval = Config.AskPing*Config.AskPerf;
 
 // Sleep for the indicated amount of time, then ask for load on each server
@@ -596,10 +618,6 @@ void XrdCmsCluster::Remove(const char *reason, XrdCmsNode *theNode, int immed)
 //
    theNode->isOffline = 1;
 
-// If the node is part of the cluster, do not count it anymore
-//
-   if (theNode->isBound) {theNode->isBound = 0; NodeCnt--;}
-
 // If the node is connected the simply close the connection. This will cause
 // the connection handler to re-initiate the node removal. The LockHandler
 // destructor will release the node table and node object locks as needed.
@@ -611,11 +629,16 @@ void XrdCmsCluster::Remove(const char *reason, XrdCmsNode *theNode, int immed)
        return;
       }
 
-// Indicate new state of this nodes if we are a reporting manager
+
+// If the node is part of the cluster, do not count it anymore and
+// indicate new state of this nodes if we are a reporting manager
 //
-   if (Config.asManager()) 
-      CmsState.Update(XrdCmsState::Counts, theNode->isSuspend ? 0 : -1,
-                                           theNode->isNoStage ? 0 : -1);
+   if (theNode->isBound)
+      {theNode->isBound = 0; NodeCnt--;
+       if (Config.asManager())
+          CmsState.Update(XrdCmsState::Counts, theNode->isSuspend ? 0 : -1,
+                                               theNode->isNoStage ? 0 : -1);
+      }
 
 // If this is an immediate drop request, do so now. Drop() will delete
 // the node object and remove the node lock. So, tell LockHandler that.
@@ -697,7 +720,8 @@ int XrdCmsCluster::Select(XrdCmsSelect &Sel)
 // which will force the client to wait. Otherwise, compute the primary and
 // secondary selections. If there are none, the client may have to wait if we
 // have servers that we can query regarding the file. Note that for files being
-// opened in write mode, only one writable copy may exist.
+// opened in write mode, only one writable copy may exist unless this is a
+// meta-operation (e.g., remove) in which case the file itself remain unmodified
 //
    if (!(Sel.Opts & XrdCmsSelect::Refresh)
    &&   (retc = Cache.GetFile(Sel, pinfo.rovec)))
@@ -705,7 +729,8 @@ int XrdCmsCluster::Select(XrdCmsSelect &Sel)
           {     if (Sel.Vec.bf) pmask = smask = 0;
            else if (Sel.Vec.hf)
                    {if (Sel.Opts & XrdCmsSelect::NewFile) return SelFail(Sel,eExists);
-                    if (Multiple(Sel.Vec.hf))             return SelFail(Sel,eDups);
+                    if (!(Sel.Opts & XrdCmsSelect::isMeta)
+                    &&  Multiple(Sel.Vec.hf))             return SelFail(Sel,eDups);
                     if (!(pmask = Sel.Vec.hf & amask))    return SelFail(Sel,eROfs);
                     smask = 0;
                    }
@@ -775,7 +800,7 @@ int XrdCmsCluster::Select(XrdCmsSelect &Sel)
 int XrdCmsCluster::Select(int isrw, SMask_t pmask,
                           int &port, char *hbuff, int &hlen)
 {
-   static const SMask_t smLow = 255;
+   static const SMask_t smLow(255);
    XrdCmsNode *nP = 0;
    SMask_t tmask;
    int Snum = 0;
@@ -1356,8 +1381,9 @@ XrdCmsNode *XrdCmsCluster::SelbyRef(SMask_t mask, int &nump, int &delay,
 void XrdCmsCluster::sendAList(XrdLink *lp)
 {
    static CmsTryRequest Req = {{0, kYR_try, 0, 0}, 0};
+   static int HdrSize = sizeof(Req.Hdr) + sizeof(Req.sLen);
    static char *AltNext = AltMans;
-   static struct iovec iov[4] = {{(caddr_t)&Req, sizeof(Req)},
+   static struct iovec iov[4] = {{(caddr_t)&Req, HdrSize},
                                  {0, 0},
                                  {AltMans, 0},
                                  {(caddr_t)"\0", 1}};
@@ -1377,14 +1403,15 @@ void XrdCmsCluster::sendAList(XrdLink *lp)
         dlen = iov[1].iov_len + iov[2].iov_len;
       }
 
-// Complete the request
+// Complete the request (account for trailing null character)
 //
+   dlen++;
    Req.Hdr.datalen = htons(static_cast<unsigned short>(dlen+sizeof(Req.sLen)));
    Req.sLen = htons(static_cast<unsigned short>(dlen));
 
 // Send the list of alternates (rotated once)
 //
-   lp->Send(iov, 4, dlen+sizeof(Req));
+   lp->Send(iov, 4, dlen+HdrSize);
 }
 
 /******************************************************************************/
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc
index 738f6a61f1b93d87cc561cae7e44133e269967b7..2d3e8f7e7db0659c4b4037be24fd71c48fb31c52 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc
@@ -314,7 +314,7 @@ int XrdCmsConfig::Configure2()
   Output:   0 upon success or !0 otherwise.
 */
    EPNAME("Configure2");
-   int NoGo = 0;
+   int Who, NoGo = 0;
    char *p, buff[512];
 
 // Print herald
@@ -377,7 +377,9 @@ int XrdCmsConfig::Configure2()
                if (isMeta) {SUPCount = 1; SUPLevel = 0;}
                if (!ManList) CmsState.Update(XrdCmsState::FrontEnd, 1);
               }
-    CmsState.Set(SUPCount, isManager && !isServer, AdminPath);
+    if (isManager) Who = (isServer ? -1 : 1);
+       else        Who = 0;
+    CmsState.Set(SUPCount, Who, AdminPath);
 
 // Create the pid file
 //
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc
index a6194f7b63c0ddf84936516419affe5f103a5433..9d96ce617e1f220bbb208f081fa21cc15c25793c 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc
@@ -483,7 +483,7 @@ void XrdCmsMeter::SpaceMsg(int why)
   
 void XrdCmsMeter::UpdtSpace()
 {
-   static const SMask_t allNodes = ~0ULL;
+   static const SMask_t allNodes(~0);
    SpaceData mySpace;
 
 // Get new space values for the cluser
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc
index 0793583b9962da743125be0953e8a1a502acf5ab..caf875530e6d0bead530a19d66c587baf87ad6cf 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc
@@ -73,7 +73,7 @@ XrdCmsNode::XrdCmsNode(XrdLink *lnkp, int port,
                        const char *nid,  int lvl, int id)
 {
     static XrdSysMutex   iMutex;
-    static const SMask_t smask_1 = 1;
+    static const SMask_t smask_1(1);
     static int           iNum = 1;
 
     Link     =  lnkp;
@@ -654,7 +654,7 @@ const char *XrdCmsNode::do_Mkpath(XrdCmsRRData &Arg)
 const char *XrdCmsNode::do_Mv(XrdCmsRRData &Arg)
 {
    EPNAME("do_Mv")
-   static const SMask_t allNodes = ~static_cast<SMask_t>(0);
+   static const SMask_t allNodes(~0);
    int rc;
 
 // Do some debugging
@@ -803,7 +803,7 @@ const char *XrdCmsNode::do_PrepDel(XrdCmsRRData &Arg)
 const char *XrdCmsNode::do_Rm(XrdCmsRRData &Arg)
 {
    EPNAME("do_Rm")
-   static const SMask_t allNodes = ~static_cast<SMask_t>(0);
+   static const SMask_t allNodes(~0);
    int rc;
 
 // Do some debugging
@@ -845,7 +845,7 @@ const char *XrdCmsNode::do_Rm(XrdCmsRRData &Arg)
 const char *XrdCmsNode::do_Rmdir(XrdCmsRRData &Arg)
 {
    EPNAME("do_Rmdir")
-   static const SMask_t allNodes = ~static_cast<SMask_t>(0);
+   static const SMask_t allNodes(~0);
    int rc;
 
 // Do some debugging
@@ -1405,13 +1405,14 @@ const char *XrdCmsNode::do_Try(XrdCmsRRData &Arg)
 
 // Add all the alternates to our alternate list
 //
+   tp = theList.GetLine();
    while((tp = theList.GetToken()))
          myMans.Add(IPAddr, tp, Config.PortTCP, myLevel);
 
 // Close the link and return an error
 //
-   Disc("redirected.");
-   return 0;
+// Disc("redirected.");
+   return ".redirected";
 }
   
 /******************************************************************************/
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc
index 47289a06c46972e20477af0076a2b68b58c9c168..337254ab4693a7912da1dbebee482c43ad525e70 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc
@@ -93,7 +93,7 @@ SMask_t XrdCmsPList_Anchor::Insert(const char *pname, XrdCmsPInfo *pinfo)
   
 void XrdCmsPList_Anchor::Remove(SMask_t mask)
 {
-    SMask_t zmask = ~mask;
+    SMask_t zmask(~mask);
     XrdCmsPList *pp = next, *prevp = 0;
 
 // Lock the list
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh
index 3540d79952c41b4e4cdb7918f7206365bbf3553c..f8c1452e135e2658d1e299f0378b220a65b91367 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh
@@ -54,25 +54,24 @@ class XrdCmsPList
 public:
 friend class XrdCmsPList_Anchor;
 
-inline XrdCmsPList    *Next() {return next;}
+inline XrdCmsPList   *Next() {return next;}
 inline char          *Path() {return pathname;}
 const  char          *PType();
 
        XrdCmsPList(const char *pname="", XrdCmsPInfo *pi=0)
-                 {next     = 0;
-                  pathlen  = strlen(pname);
-                  pathname = strdup(pname);
-                  if (pi) pathmask.Set(pi);
-                 }
+                  : next(0), pathname(strdup(pname)), pathlen(strlen(pname)),
+                    pathtype(0) {if (pi) pathmask.Set(pi);}
 
       ~XrdCmsPList() {if (pathname) free(pathname);}
 
 private:
 
+XrdCmsPInfo     pathmask;
 XrdCmsPList    *next;
-int             pathlen;
 char           *pathname;
-XrdCmsPInfo     pathmask;
+int             pathlen;
+char            pathtype;
+char            reserved[3];
 };
 
 class XrdCmsPList_Anchor
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc
index 560b639a623c1f7e3e84e970861a1a983831cb9d..0f69f5eda84c0bc5786da788fb7953188da0fead 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc
@@ -203,7 +203,8 @@ int XrdCmsProtocol::Execute(XrdCmsRRData &Arg)
               if (*etxt == '!')
                  {DEBUGR(etxt+1 <<" delayed " <<Arg.waitVal <<" seconds");
                   return -EINPROGRESS;
-                 } else Reply_Error(Arg, kYR_EINVAL, etxt);
+                 } else if (*etxt == '.') return -ECONNABORTED;
+                           else Reply_Error(Arg, kYR_EINVAL, etxt);
               else if (Arg.Routing & XrdCmsRouting::Forward && Cluster.NodeCnt
                    &&  !(Arg.Request.modifier & kYR_dnf)) Reissue(Arg);
    return 0;
@@ -354,7 +355,7 @@ void XrdCmsProtocol::Pander(const char *manager, int mport)
        // Remove manager from the config
        //
        Manager.Remove(myNode, (rc == kYR_redirect ? "redirected"
-                                  : (rc ? "lost connection" : Reason)));
+                                  : (Reason ? Reason : "lost connection")));
        ManTree.Disc(myNID);
        Link->Close();
        delete myNode; myNode = 0;
@@ -469,7 +470,7 @@ XrdCmsRouting *XrdCmsProtocol::Admit()
    XrdCmsLogin  Source(myBuff, sizeof(myBuff));
    CmsLoginData Data;
    const char  *Reason;
-   SMask_t      newmask, servset = 0;
+   SMask_t      newmask, servset(0);
    int addedp = 0, Status = 0, isMan = 0, isPeer = 0, isProxy = 0, isServ = 0;
    int wasSuspended = 0;
 
@@ -814,9 +815,11 @@ do{if ((rc = Link->RecvAll((char *)&Data->Request, ReqSize, maxWait)) < 0)
 //
    if (!(Data->Ident) || !(*Data->Ident)) Data->Ident = myNode->Ident;
 
-// Schedule this request
+// Schedule this request if async. Otherwise, do this inline. Note that
+// synchrnous requests are allowed to return status changes (e.g., redirect)
 //
-   if (Data->Routing & XrdCmsRouting::isSync) Execute(*Data);
+   if (Data->Routing & XrdCmsRouting::isSync)
+      {if ((rc = Execute(*Data)) && rc == -ECONNABORTED) return "redirected";}
       else if ((jp = XrdCmsJob::Alloc(this, Data)))
               {Sched->Schedule((XrdJob *)jp);
                Data = XrdCmsRRData::Objectify();
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc
index 925577f09e9df9b377a68bcecbbbd7d9fa48c047..43bcbe016b2a36fb1e1b69bdf240224aa7b289b2 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc
@@ -31,8 +31,10 @@ int XrdCmsRRData::getBuff(size_t bsz)
           size_t Alignment = PageSize;
 
    if (bsz < Alignment)
-      {do {Alignment = Alignment >> 1;} while(bsz < Alignment);
-       Alignment = Alignment << 1; bsz = Alignment;
+      {if (bsz <= 8) Alignment = bsz = 8;
+          else {do {Alignment = Alignment >> 1;} while(bsz < Alignment);
+                Alignment = Alignment << 1; bsz = Alignment;
+               }
       }
 
    if (Buff) free(Buff);
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh
index 387be5fab20d6e967722d28e957b40f7c9abae1a..e98a4bc7ec2ecfd21f31c234b6b7dafbb157b2aa 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh
@@ -15,6 +15,7 @@
 #include <string.h>
 
 #include "XrdCms/XrdCmsNode.hh"
+#include "XrdCms/XrdCmsTypes.hh"
 #include "XrdSys/XrdSysPthread.hh"
   
 class XrdCmsRTable
@@ -39,8 +40,6 @@ void          UnLock() {myMutex.UnLock();}
 
 private:
 
-static const int   maxRD = 65;  // slot 0 is never used.
-
 XrdSysMutex   myMutex;
 XrdCmsNode   *Rtable[maxRD];
 int           Hwm;
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc
index 5fc2647343456b606b872baddefbd5b55f704982..e086c65b45ce1947c282ff380b571dcc2ead6d7a 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc
@@ -89,18 +89,17 @@ XrdCmsResp *XrdCmsResp::Alloc(XrdOucErrInfo *erp, int msgid)
 /*                               R e c y c l e                                */
 /******************************************************************************/
   
-void XrdCmsResp::Recycle(void *p)
+void XrdCmsResp::Recycle()
 {
 
 // Recycle appendages
 //
    if (myBuff) {myBuff->Recycle(); myBuff = 0;}
 
-// If a free area pointer has been passed then this is from delete() and we
-// need to check if we have too many message objects at this point. Otherwise,
-// it's from a synchrnous recycle and it's always OK to requeue these.
+// We keep a stash of allocated response objects. If there are too many we
+// simply delete this object.
 //
-   if (p && XrdCmsResp::numFree >= XrdCmsResp::maxFree) free(p);
+   if (XrdCmsResp::numFree >= XrdCmsResp::maxFree) delete this;
       else {myMutex.Lock();
             next = nextFree;
             nextFree = this;
@@ -165,6 +164,7 @@ void XrdCmsResp::Reply()
 void XrdCmsResp::ReplyXeq()
 {
    EPNAME("Reply")
+   XrdOucEICB *theCB;
    int Result;
 
 // If there is no callback object, ignore this call. Eventually, we may wish
@@ -202,9 +202,15 @@ void XrdCmsResp::ReplyXeq()
 //
    SyncCB.Wait();
 
-// Invoke the callback; it must explicitly invoke delete on our upcast object.
+// We now must request a callback to recycle this object once the callback
+// response is actually sent by setting the callback object pointer to us.
 //
-   ErrCB->Done(Result, (XrdOucErrInfo *)this);
+   theCB = ErrCB;
+   ErrCB = (XrdOucEICB *)this;
+
+// Invoke the callback
+//
+   theCB->Done(Result, (XrdOucErrInfo *)this);
 }
 
 /******************************************************************************/
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh
index e7b845024f9060d815c748632e6af206906c62d0..80e4f5cb073a20bcbdd7715f2a76564a551b0c28 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh
@@ -49,13 +49,15 @@ XrdSysSemaphore     respSync;
 
 class XrdNetBuffer;
   
-class XrdCmsResp : public XrdOucErrInfo
+class XrdCmsResp : public XrdOucEICB, public XrdOucErrInfo
 {
 public:
 friend class XrdCmsRespQ;
 
 static XrdCmsResp *Alloc(XrdOucErrInfo *erp, int msgid);
 
+       void        Done(int &Result, XrdOucErrInfo *eInfo) {Recycle();}
+
 inline int         ID() {return myID;}
 
        void        Reply(const char   *Man, XrdCms::CmsRRHdr &rrhdr,
@@ -63,15 +65,16 @@ inline int         ID() {return myID;}
 
 static void        Reply();
 
+       int         Same(unsigned long long arg1, unsigned long long arg2)
+                       {return 0;}
+
 static void        setDelay(int repdly) {RepDelay = repdly;}
 
        XrdCmsResp() : XrdOucErrInfo(UserID) {next = 0; myBuff = 0;}
       ~XrdCmsResp() {}
 
-void   operator delete(void *p) {((XrdCmsResp *)p)->Recycle();}
-
 private:
-       void Recycle(void *p=0);
+       void Recycle();
        void ReplyXeq();
 
 static XrdSysSemaphore        isReady;
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc
index d4fbf2f5bff108354ad1170fa5432514a9ac9564..3000ad89c3440384d7b7191b218017291c701323 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc
@@ -113,9 +113,9 @@ void *XrdCmsState::Monitor()
           {if (myStatus.Hdr.modifier & CmsStatusRequest::kYR_Resume)
                     {myStatus.Hdr.streamid = htonl(myPort); RTsend = 1;}
                else {myStatus.Hdr.streamid = 0;
-                     RTsend = (theState & SRV_Suspend);
+                     RTsend = (isMan > 0 ? (theState & SRV_Suspend) : 0);
                     }
-           if (isMan && RTsend) 
+           if (isMan && RTsend)
               RTable.Send("status", (char *)&myStatus, sizeof(myStatus));
            Manager.Inform(myStatus.Hdr);
           }
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh
index ea430094c55099d044bee8f13147eb77b4528c87..e001d8fe0c255302b2e46ed558e620db6e5619a9 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh
@@ -41,7 +41,7 @@
 
 #define TRACEX(y) {Trace.Beg(0,epname); cerr <<y; Trace.End();}
 
-#define EPNAME(x) const char *epname = x;
+#define EPNAME(x) static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh
index 75c381b003255a967a5c3f994e86fabf16ee816f..b2f5030eb3e3d9b2566896dd4c8992f1be75b64f 100644
--- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh
+++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh
@@ -20,6 +20,11 @@ typedef unsigned long long SMask_t;
 //
 #define STMax 64
 
+// The following defines the maximum number of redirectors. It is one greater
+// than the actual maximum as the zeroth is never used.
+//
+#define maxRD 65
+
 #define XrdCmsMAX_PATH_LEN 1024
 
 #define XrdCmsVERSION "1.0.0"
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc
index fc3683e1aa06b8b5a831b2a356b35062cff0290d..86858ca3555070fda279c3f80e22b9245ed70f5e 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc
@@ -110,7 +110,7 @@ int XrdCnsConfig::Configure(int argc, char **argv, char *argt)
 
    const char *TraceID = "Config";
    XrdOucArgs Spec(&MLog,(argt ? "Cns_Config: ":"XrdCnsd: "),
-                          "a:b:dD:e:E:i:I:l:L:N:p:q:R:");
+                          "a:b:B:c:dD:e:E:i:I:l:L:N:p:q:R:");
    char buff[2048], *dP, *tP, *dnsEtxt = 0, *n2n = 0, *lroot = 0, *xpl = 0;
    char theOpt, *theArg;
    long long llval;
@@ -132,6 +132,8 @@ int XrdCnsConfig::Configure(int argc, char **argv, char *argt)
        case 'B': Opts |= optNoCns;
        case 'b': bPath = Spec.argval;
                  break;
+       case 'c': cPath = Spec.argval;
+                 break;
        case 'D': NoGo |= XrdOuca2x::a2i(MLog,"-D value",Spec.argval,&n,0,4);
                  if (!NoGo) EnvPutInt("DebugLevel", n);
                  break;
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc
index 0163e3d2f17531fc0528026d51038315ee6de44d..7de754bbd890b5e7ba75f260cabb52344d87f7eb 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc
@@ -71,7 +71,7 @@ void XrdCnsDaemon::getEvents(XrdOucStream &Events, const char *Who)
                ||  !evP->setType(eP))                  Miss = "eventid";
          else {switch(evP->Type())
                      {case XrdCnsLogRec::lrClosew:
-                           if (!(tp=Events.GetToken())) {Miss = "lfn";   break;}
+                           if (!(tp=getLFN(Events)))    {Miss = "lfn";   break;}
                            evP->setLfn1(tp);
                            if (!(tp=Events.GetToken())) {Miss = "size";  break;}
                            Size = strtoll(tp, &etp, 10);
@@ -85,17 +85,17 @@ void XrdCnsDaemon::getEvents(XrdOucStream &Events, const char *Who)
                            Mode = strtol(tp, &etp, 8);
                            if (*etp)                    {Miss = "mode";  break;}
                            evP->setMode(Mode);
-                           if (!(tp=Events.GetToken())) {Miss = "lfn";   break;}
+                           if (!(tp=getLFN(Events)))    {Miss = "lfn";   break;}
                            evP->setLfn1(tp);
                            break;
                       case XrdCnsLogRec::lrMv:
-                           if (!(tp=Events.GetToken())) {Miss = "lfn1";  break;}
+                           if (!(tp=getLFN(Events)))    {Miss = "lfn1";  break;}
                            evP->setLfn1(tp);
-                           if (!(tp=Events.GetToken())) {Miss = "lfn2";  break;}
+                           if (!(tp=getLFN(Events)))    {Miss = "lfn2";  break;}
                            evP->setLfn2(tp);
                            break;
                       default:     // rm | rmdir
-                           if (!(tp=Events.GetToken())) {Miss = "lfn";   break;}
+                           if (!(tp=getLFN(Events)))    {Miss = "lfn";   break;}
                            evP->setLfn1(tp);
                            break;
                      }
@@ -114,3 +114,18 @@ void XrdCnsDaemon::getEvents(XrdOucStream &Events, const char *Who)
 //
    MLog.Emsg("doEvents", "Lost event connection to", Who, "!");
 }
+
+/******************************************************************************/
+/*                                g e t L F N                                 */
+/******************************************************************************/
+
+char *XrdCnsDaemon::getLFN(XrdOucStream &Events)
+{
+   char *tP, *cgiP;
+
+// Obtain the lfn but discard any CGI information that has been mistakenly
+// passed. Some people recall the old documentation, sigh.
+//
+   if ((tP=Events.GetToken()) && (cgiP = index(tP, '?'))) *cgiP = '\0';
+   return tP;
+}
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh
index b2eb341242475490336a2a7d5da7de12720bca1a..2ebe5029518c23a49bb7f06b0ceb5fbd10cdce02 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh
@@ -26,5 +26,7 @@ void  getEvents(XrdOucStream &, const char *Who);
      ~XrdCnsDaemon() {}
 
 private:
+
+char *getLFN(XrdOucStream &Events);
 };
 #endif
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc
index 75bd73082bb6888f338a70f87f48459de74801a3..83e916150e581c713b8ad79d4e43c92f56b3a9a9 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc
@@ -227,8 +227,9 @@ int XrdCnsLogClient::Run(int Always)
 
 do{if (arkFN && time(0) >= mCheck)
       {if (!Manifest())
-          if (!Always) return 0;
-       MLog.Emsg("LogClient","Unable to create inventory at",arkURL);
+          {if (!Always) return 0;
+           MLog.Emsg("LogClient","Unable to create inventory at",arkURL);
+          }
        mCheck = time(0) + Config.mInt;
       }
 
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc
index ecfc2dba12e1e7982c3a36de4a03dc29a8e7f17d..b6d9c1d590e6221e814c763f5e15524bd62f1dd0 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc
@@ -122,7 +122,7 @@ int XrdCnsLogFile::Commit()
   
 int XrdCnsLogFile::Eol()
 {
-   XrdCnsLogFile *lfP = subNext;
+   XrdCnsLogFile *lfX, *lfP = subNext;
    XrdCnsLogRec   lRec(XrdCnsLogRec::lrEOL);
    int rc, bL = XrdCnsLogRec::MinSize + lRec.DLen();
    char *bP = (char *)&lRec;
@@ -141,8 +141,9 @@ int XrdCnsLogFile::Eol()
    while(lfP)
         {lfP->logSem.Post();
          lfP->logWait = 0;
-         lfP->synSem.Post();
+         lfX = lfP;
          lfP = lfP->subNext;
+         lfX->synSem.Post();
         }
 
 // All done
@@ -166,6 +167,10 @@ XrdCnsLogRec *XrdCnsLogFile::getRec()
        bL = XrdCnsLogRec::MinSize; recOffset = logOffset;
 
        if (!Read(bP, bL) || !((bL = Rec.DLen()))) return 0;
+       if (bL > XrdCnsLogRec::MinSize)
+          {MLog.Emsg("getRec", "Invalid record length detected in", logFN);
+           return 0;
+          }
 
        bP = bP + XrdCnsLogRec::MinSize;
        if (!Read(bP, bL)) return 0;
@@ -188,7 +193,7 @@ XrdCnsLogRec *XrdCnsLogFile::getRec()
 /*                                  O p e n                                   */
 /******************************************************************************/
   
-int XrdCnsLogFile::Open(int allocbuff)
+int XrdCnsLogFile::Open(int allocbuff, off_t thePos)
 {
    static const int AMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 
@@ -197,6 +202,13 @@ int XrdCnsLogFile::Open(int allocbuff)
    if ((logFD = open(logFN, O_CREAT|O_RDWR, AMode)) < 0)
       {MLog.Emsg("Open", errno, "open", logFN); return 0;}
 
+// Set starting position if need be
+//
+   if (thePos && (lseek(logFD, thePos, SEEK_SET) == (off_t)-1))
+      {MLog.Emsg("Open", errno, "seek into", logFN); close(logFD), logFD = -1;
+       return 0;
+      }
+
 // Allocate a memory buffer if so wanted
 //
    if (allocbuff)
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh
index 1a6bdf9487559a3b9ada5d1ba13e94254afe486b..362d3143668890edaf5dbf154aa744d4f9880ef8 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh
@@ -37,14 +37,14 @@ static void    maxRecs(int nRecs) {logRMax = nRecs;
                                    logBMax = nRecs * sizeof(XrdCnsLogRec);
                                   }
 
-int            Open(int aBuff=1);
+int            Open(int aBuff=1, off_t thePos=0);
 
 XrdCnsLogFile *Subscribe(const char *Path, int cNum);
 
 int            Unlink();
 
                XrdCnsLogFile(const char *Path, int cnum=0, int Wait=1)
-                            : logSem(0), subNext(0),
+                            : Next(0), logSem(0), subNext(0),
                               logBuff(0),logNext(0), logFN(strdup(Path)),
                               logFD(-1), logRdr(cnum), logWait(Wait),
                               logOffset(0), recOffset(0) {}
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc
index ae589c585e1e9d3022c276bf6cb13861b44d8f4b..69bd89d415db77f089eb30905ccc7fcbe5439ac9 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc
@@ -83,7 +83,7 @@ XrdCnsLogRec *XrdCnsLogRec::Get(char &lrType)
          qSem.Wait();
          qMutex.Lock();
         }
-   frstRec = lrP->Next;
+   if (!(frstRec = lrP->Next)) lastRec = 0;
    qMutex.UnLock();
 
 // Get the type and if its an eol marker recycle now
@@ -137,14 +137,14 @@ int XrdCnsLogRec::setData(const char *dP1, const char *dP2)
   int n1 = strlen(dP1), n2 = strlen(dP2);
   char *dP;
 
-// Make sure we have room
+// Make sure we have room "'lfn' + 'data1' + ' ' + data2"
 //
    if (n1+n2+2 > MAXPATHLEN) return 0;
 
 // Add the data in the fields
 //
    setSize(static_cast<long long>(Rec.Hdr.lfn1Len));
-   dP = Rec.Data.lfn + Rec.Hdr.lfn1Len + 1;
+   dP = Rec.Data.lfn + Rec.Hdr.lfn1Len+1;
    strcpy(dP, dP1);
    dP += n1; *dP++ = ' ';
    strcpy(dP, dP2);
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh
index 02f69a0e589031c36abf68e8cf27b618a0444a06..c3870a24248d6e0efe75d654ceaccc3d1a41685c 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh
@@ -55,7 +55,8 @@ struct LogRec
 
 static const int  OffDone  = offsetof(LogRec, Hdr.Done);
 static const int  FixDLen  = offsetof(Arg, lfn);
-static const int  MinSize  = sizeof(Ctl);
+static const int  MinSize  = sizeof(Ctl);  // Header
+static const int  MaxSize  = sizeof(Arg);  // Data
 static const long tBase    = 1248126834L;
 
 static const char lrClosew = 't';
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc
index 9c0a8474f898c06fa3449b4ab6df0e4988146416..ce4aed551a54d89e74056e8b99007691111d3d76 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc
@@ -112,7 +112,7 @@ int XrdCnsLogServer::Init(XrdOucTList *rList)
 //
    if (Stat.st_size != 0)
       {XrdCnsLogFile myLogFile(logDir);
-       if (!myLogFile.Open(0) || !myLogFile.Eol()) return 0;
+       if (!myLogFile.Open(0, Stat.st_size) || !myLogFile.Eol()) return 0;
       }
    unlink(logDir);
 
@@ -206,7 +206,7 @@ do{nRecs = Config.qLim; lfP = logFile;
 
    while(nRecs && (lrP = XrdCnsLogRec::Get(lrT)))
         {if (lrP->Type() == XrdCnsLogRec::lrCreate) Massage(lrP);
-         lfP->Add(lrP);
+         lfP->Add(lrP); lrP->Recycle();
          nRecs--;
         }
 
@@ -219,4 +219,8 @@ do{nRecs = Config.qLim; lfP = logFile;
        delete lfP;
       }
   } while(1);
+
+// At the moment we don't really have a recovery strategy
+//
+   MLog.Emsg("Run", "Fatal error occurred; terminating. . .");
 }
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc
index a08f5e874d166e996b83e3165bd95edcb96ebea4..e47c3346357443a01009421e59578abaa68edbd6 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc
@@ -146,6 +146,7 @@ int main(int argc, char *argv[])
    XrdSysLogger MLogger;
    XrdOucStream stdinEvents;    // STDIN fed events
    sigset_t myset;
+   char *xrdLogD = 0;
 
 // Establish message routing
 //
@@ -168,14 +169,15 @@ int main(int argc, char *argv[])
 //
    if (!Config.Configure(argc, argv)) exit(1);
 
-// Construct the logfile path and bind it (command line only)
+// Construct the logfile path and bind it
 //
-   if (!Config.logfn && (Config.logfn = getenv("XRDLOGDIR")))
+   if (Config.logfn || (xrdLogD = getenv("XRDLOGDIR")))
       {pthread_t tid;
        char buff[2048];
        int retc;
+       if (Config.logfn) strcpy(buff, Config.logfn);
+          else {strcpy(buff, xrdLogD); strcat(buff, "cnsdlog");}
        if (Config.logKeep) MLogger.setKeep(Config.logKeep);
-       strcpy(buff, Config.logfn); strcat(buff, "cnsdlog");
        MLogger.Bind(buff, 24*60*60);
        MLog.logger(&MLogger);
        if ((retc = XrdSysThread::Run(&tid, MLogWorker, (void *)0,
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc
index 37f669a3f8ceb481157fd382e62f580a29c8763e..c06cfa142e8fa58d92f8746d630ad6c248a38e26 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc
@@ -137,16 +137,17 @@ int XrdCnsSsiApplyM(const char *Mount, char *xP, void *Arg)
    static int doInit = 1;
    int n, iFD = *(int *)Arg;
 
-// Initialize the header (needs to be done once
+// Initialize the header (needs to be done once)
 //
    if (doInit)
       {memset(Hdr, ' ', sizeof(Hdr));
        aP->Type = XrdCnsLogRec::lrMount;
        doInit = 0;
-      } else aP->Mount = *xP;
+      }
 
 // Write out a directory record. Terminate processing upon error
 //
+   aP->Mount = *xP;
    iov[1].iov_base = (char *)Mount; n = strlen(Mount);
    iov[1].iov_len  = n;
 
@@ -167,10 +168,11 @@ int XrdCnsSsiApplyS(const char *Space, char *xP, void *Arg)
       {memset(Hdr, ' ', sizeof(Hdr));
        aP->Type = XrdCnsLogRec::lrSpace;
        doInit = 0;
-      } else aP->Mount = *xP;
+      }
 
 // Write out a directory record. Terminate processing upon error
 //
+   aP->Space = *xP;
    iov[1].iov_base = (char *)Space; n = strlen(Space);
    iov[1].iov_len  = n;
 
@@ -610,9 +612,9 @@ XrdCnsSsiFRec *XrdCnsSsi::AddFile(char *lfn, char *lP)
 
 // Extract out the directory, file name, space name and mount point, if any
 //
-   if ((sP = index(lfn, '?'))) *sP = '\0';
+   if ((sP = index(lfn, ' '))) *sP++ = '\0';
    if (!(fP = rindex(lfn+1, '/')) || !(*(fP+1)))
-      {if (sP) *sP = '?';
+      {if (sP) *(sP-1) = ' ';
        Say.V("Invalid log record ", lP); nErrs++;
        return 0;
       }
@@ -620,7 +622,7 @@ XrdCnsSsiFRec *XrdCnsSsi::AddFile(char *lfn, char *lP)
    if (sP)
       {if (!(mP = index(sP, ' '))) aP->Mount = mountP->Default();
           else {*mP++ = '\0';      aP->Mount = mountP->Add(mP);}
-       if (*(sP+1))                aP->Space = spaceP->Add(sP+1);
+       if (*sP)                    aP->Space = spaceP->Add(sP);
           else                     aP->Space = spaceP->Default();
       } else {                     aP->Mount = mountP->Default();
                                    aP->Space = spaceP->Default();
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc
index a642372d7a363bc68a534486221465b33c6821c3..694326e3e4d5d62a10e5fc00f99d3918e2e96bf7 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc
@@ -59,7 +59,7 @@ int XrdCnsSsiCfg::Configure(int argc, char **argv)
            Say.M("The diff function is not yet implemented.");
            return 0;
           }
-   else if (!strcmp("list", argv[1])) {Xeq = 'l'; Opts = "fhmnpsS";}
+   else if (!strcmp("list", argv[1])) {Xeq = 'l'; Opts = "fhlmnpsS";}
    else if (!strcmp("updt", argv[1])) {Xeq = 'u'; Opts = "v";}
    else Usage("Invalid function - ", argv[1]);
 
@@ -138,7 +138,7 @@ int XrdCnsSsiCfg::Configure(int argc, char **argv, const char *Opts)
 // Make sure we have some host entries
 //
    if (!(dirList = XrdCnsLog::Dirs(bPath, n)))
-      {Say.M("No server inventories found in'",bPath,"'."); return 0;}
+      {Say.M("No server inventories found in '",bPath,"'."); return 0;}
 
 // Issue warning if errors occurred
 //
diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc
index b5f20174d26997133d2b725962ff5f65d46145fb..be043b5a4df39edd978bc2aa5a09c4493d06ccef 100644
--- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc
+++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc
@@ -57,7 +57,9 @@ char XrdCnsXref::Add(const char *kval, char idx)
 
 // If a character was specified, try to use it.
 //
-   if ((i = (idx ? c2i(idx) : availI())) < 0) return 0;
+        if (idx) i = c2i(idx);
+   else if ((oldx = xTable.Find(xKey))) return *oldx;
+   else if ((i = availI()) < 0)         return 0;
 
 // Try to add the new entry
 //
@@ -67,7 +69,7 @@ char XrdCnsXref::Add(const char *kval, char idx)
                   {if (yTable[j])
                       {xTable.Del(yTable[j]); free(yTable[j]); yTable[j] = 0;}
                    xTable.Rep(xKey, xIndex+i, 0, Hash_keep);
-                   yTable[static_cast<int>(idx)] = xKey;
+                   yTable[j] = xKey;
                   }
               } else free(xKey);
 
diff --git a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh
index 616c3be42c2e1afa0951cd48a81725951706a492..58b1866ca38e6f9d1c444eca70e0a7264852f9be 100644
--- a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh
@@ -23,7 +23,7 @@
                                        cerr <<y; cryptoTrace->End();}}
 #define TRACE(act,x) if (QTRACE(act)) PRINT(x)
 #define DEBUG(y)     TRACE(Debug,y)
-#define EPNAME(x)    const char *epname = x;
+#define EPNAME(x)    static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc
index d864562dc5822052d4d4bfabfc247238be465eb2..019142c69259ee278d0c7f98ff8c32dc9f188c84 100644
--- a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc
+++ b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc
@@ -617,7 +617,7 @@ int XrdCryptosslASN1toUTC(ASN1_TIME *tsn1)
    if (!gmtime_r(&now, &gtn)) return etime;
    //
    // Calculate correction
-   int tzcor = (int)difftime(mktime(&ltn), mktime(&gtn));
+   int tzcor = (int) difftime(mktime(&ltn), mktime(&gtn));
    //
    // Apply correction
    etime += tzcor;
diff --git a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh
index 0a5147a526abf668b2a6a881b8b04c9fefad5503..d2f3936b8ad0f3b416166c986dcd583b4acfbccc 100644
--- a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh
@@ -23,7 +23,7 @@
                                        cerr <<y; sslTrace->End();}}
 #define TRACE(act,x) if (QTRACE(act)) PRINT(x)
 #define DEBUG(y)     TRACE(Debug,y)
-#define EPNAME(x)    const char *epname = x;
+#define EPNAME(x)    static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile b/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile
index ccc009153628f07553f66b79dee6b40148a089bb..85f0cb3a487b3bf6e30aab1d2c772094b05e1b11 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile
+++ b/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile,v 1.4 2009/06/11 15:31:49 ganis Exp $
+#              $Id: GNUmakefile,v 1.8 2010/01/05 22:55:24 abh Exp $
 
 #------------------------------------------------------------------------------#
 #                       C o m m o n   V a r i a b l e s                        #
@@ -26,11 +26,15 @@ SOURCES = \
         XrdFrmPstgMain.cc  \
         XrdFrmPstgReq.cc   \
         XrdFrmPstgXfr.cc   \
+        XrdFrmPurge.cc     \
+        XrdFrmPurgMain.cc  \
+        XrdFrmTSort.cc     \
         XrdFrmUtils.cc
 
 OBJLIB  = \
         $(OBJDIR)/XrdFrmConfig.o    \
         $(OBJDIR)/XrdFrmFiles.o     \
+        $(OBJDIR)/XrdFrmTSort.o     \
         $(OBJDIR)/XrdFrmUtils.o
 
 OBJADMIN= \
@@ -48,7 +52,11 @@ OBJPSTG = \
         $(OBJDIR)/XrdFrmPstgReq.o   \
         $(OBJDIR)/XrdFrmPstgXfr.o
 
-OBJECTS = $(OBJLIB) $(OBJADMIN) $(OBJPSTG)
+OBJPURG = \
+        $(OBJDIR)/XrdFrmPurgMain.o  \
+        $(OBJDIR)/XrdFrmPurge.o
+
+OBJECTS = $(OBJLIB) $(OBJADMIN) $(OBJPSTG) $(OBJPURG)
 
 OBJADDS = \
         $(OBJDIR)/XrdXrootdMonitor.o \
@@ -65,8 +73,9 @@ LIBRARY = $(LIBDIR)/libXrdFrm.a
 BINADMIN= $(BINDIR)/frm_admin
 BINPSTGA= $(BINDIR)/frm_pstga
 BINPSTGD= $(BINDIR)/frm_pstgd
+BINPURGD= $(BINDIR)/frm_purged
 
-TARGETS = $(LIBRARY) $(BINADMIN) $(BINPSTGA) $(BINPSTGD)
+TARGETS = $(LIBRARY) $(BINADMIN) $(BINPSTGA) $(BINPSTGD) $(BINPURGD)
 
 #------------------------------------------------------------------------------#
 #                           S e a r c h   P a t h s                            #
@@ -120,11 +129,16 @@ $(BINPSTGD): $(OBJLIB) $(OBJPSTG) $(OBJADDS) $(OBJDEPS)
 	@echo Creating executable $(BINPSTGD)
 	$(ECHO)$(LD) $(LDOP) $(OBJPSTG) $(OBJADDS) $(LIBS) $(BINLIBS) $(TYPELIBMT) -o $(BINPSTGD)
 
+$(BINPURGD): $(OBJLIB) $(OBJPURG) $(OBJADDS) $(OBJDEPS)
+	@echo Creating executable $(BINPURGD)
+	$(ECHO)$(LD) $(LDOP) $(OBJPURG) $(OBJADDS) $(LIBS) $(BINLIBS) $(TYPELIBMT) -o $(BINPURGD)
+
 $(OBJDIR)/XrdFrmAdmin.o:  XrdFrmAdmin.hh    XrdFrmAdmin.cc  XrdFrmTrace.hh \
                           XrdFrmConfig.hh   XrdFrmUtils.hh  \
                           XrdOss.hh         XrdOucExport.hh \
                           XrdOuca2x.hh      XrdOucArgs.hh   \
-                          XrdOucTList.hh    XrdOucTokenizer.hh
+                          XrdOucTList.hh    XrdOucTokenizer.hh \
+                          XrdSysError.hh
 	@echo Compiling XrdFrmAdmin.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdmin.o XrdFrmAdmin.cc
 
@@ -133,19 +147,19 @@ $(OBJDIR)/XrdFrmAdminAudit.o:  XrdFrmAdmin.hh    XrdFrmAdminAudit.cc \
                                XrdFrmTrace.hh    XrdFrmUtils.hh \
                                XrdOssPath.hh     XrdOssSpace.hh \
                                XrdOucNSWalk.hh   XrdOucTList.hh \
-                               XrdSysTimer.hh
+                               XrdSysError.hh    XrdSysTimer.hh
 	@echo Compiling XrdFrmAdminAudit.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdminAudit.o XrdFrmAdminAudit.cc
 
 $(OBJDIR)/XrdFrmAdminFiles.o: XrdFrmAdmin.hh    XrdFrmAdminFiles.cc \
                               XrdFrmConfig.hh   XrdFrmFiles.hh  \
-                              XrdFrmTrace.hh    XrdOssPath.hh
+                              XrdFrmTrace.hh    XrdSysError.hh
 	@echo Compiling XrdFrmAdminFiles.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdminFiles.o XrdFrmAdminFiles.cc
 
 $(OBJDIR)/XrdFrmAdminFind.o:  XrdFrmAdmin.hh    XrdFrmAdminFind.cc \
                               XrdFrmConfig.hh   XrdFrmFiles.hh  \
-                              XrdFrmTrace.hh    XrdOssPath.hh \
+                              XrdFrmTrace.hh    XrdSysError.hh \
                               XrdOucArgs.hh     XrdOucNSWalk.hh
 	@echo Compiling XrdFrmAdminFind.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdminFind.o XrdFrmAdminFind.cc
@@ -161,7 +175,7 @@ $(OBJDIR)/XrdFrmAdminMain.o:  XrdFrmAdmin.hh     XrdFrmAdminMain.cc \
 
 $(OBJDIR)/XrdFrmAdminQuery.o:  XrdFrmAdmin.hh    XrdFrmAdminQuery.cc \
                                XrdFrmConfig.hh   XrdFrmFiles.hh  \
-                               XrdFrmTrace.hh    \
+                               XrdFrmTrace.hh    XrdSysError.hh \
                                XrdOssPath.hh     XrdOssSpace.hh \
                                XrdOucArgs.hh     XrdOucNSWalk.hh \
                                XrdOucTList.hh
@@ -170,7 +184,7 @@ $(OBJDIR)/XrdFrmAdminQuery.o:  XrdFrmAdmin.hh    XrdFrmAdminQuery.cc \
 
 $(OBJDIR)/XrdFrmAdminUnlink.o: XrdFrmAdmin.hh    XrdFrmAdminUnlink.cc \
                                XrdFrmConfig.hh   XrdFrmTrace.hh \
-                               XrdFrmUtils.hh  \
+                               XrdFrmUtils.hh    XrdSysError.hh \
                                XrdOss.hh         XrdOssPath.hh \
                                XrdOucNSWalk.hh   XrdCmsNotify.hh
 	@echo Compiling XrdFrmAdminUnlink.cc
@@ -179,25 +193,29 @@ $(OBJDIR)/XrdFrmAdminUnlink.o: XrdFrmAdmin.hh    XrdFrmAdminUnlink.cc \
 $(OBJDIR)/XrdFrmConfig.o: XrdFrmConfig.hh XrdFrmConfig.cc    XrdFrmTrace.hh \
                           XrdCmsNotify.hh \
                           XrdInfo.hh      XrdNetDNS.hh       XrdOss.hh \
-                          XrdOuca2x.hh    XrdOucEnv.hh       XrdOucStream.hh \
+                          XrdOuca2x.hh    XrdOucEnv.hh       XrdOucExport.hh \
                           XrdOucUtils.hh  XrdOucName2Name.hh XrdOucMsubs.hh \
-                          XrdOucProg.hh   XrdOucTList.hh     XrdOssSpace.hh \
+                          XrdOucPList.hh  XrdOucProg.hh \
+                          XrdOucStream.hh XrdOucTList.hh     XrdOssSpace.hh \
                           XrdSysError.hh  XrdSysHeaders.hh   XrdSysLogger.hh \
                           XrdSysTimer.hh  XrdSysPlugin.hh    XrdSysPthread.hh \
-                          XrdSysPlatform.hh XrdXrootdMonitor.hh
+                          XrdSysPlatform.hh \
+                          XrdXrootdMonitor.hh
 	@echo Compiling XrdFrmConfig.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmConfig.o XrdFrmConfig.cc
 
 $(OBJDIR)/XrdFrmFiles.o:  XrdFrmFiles.hh    XrdFrmFiles.cc  XrdFrmTrace.hh \
                           XrdFrmConfig.hh   XrdFrmUtils.hh  XrdOssPath.hh \
-                          XrdOucHash.hh     XrdOucNSWalk.hh
+                          XrdOucHash.hh     XrdOucNSWalk.hh \
+                          XrdSysError.hh    XrdSysPlatform.hh
 	@echo Compiling XrdFrmFiles.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmFiles.o XrdFrmFiles.cc
 
 $(OBJDIR)/XrdFrmPstg.o:   XrdFrmPstg.hh    XrdFrmPstg.cc    XrdFrmTrace.hh \
                           XrdFrmConfig.hh  XrdFrmPstgReq.hh XrdFrmPstgXfr.hh \
                           XrdNetMsg.hh     XrdOucStream.hh \
-                          XrdSysHeaders.hh XrdSysPlatform.hh XrdSysPthread.hh
+                          XrdSysHeaders.hh XrdSysPlatform.hh XrdSysPthread.hh \
+                          XrdSysError.hh
 	@echo Compiling XrdFrmPstg.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPstg.o XrdFrmPstg.cc
 
@@ -232,6 +250,31 @@ $(OBJDIR)/XrdFrmPstgXfr.o: XrdFrmPstgXfr.cc      XrdFrmPstgXfr.hh \
 	@echo Compiling XrdFrmPstgXfr.cc
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPstgXfr.o XrdFrmPstgXfr.cc
 
+$(OBJDIR)/XrdFrmPurge.o:  XrdFrmPurge.hh   XrdFrmPurge.cc    XrdFrmTrace.hh \
+                          XrdFrmConfig.hh  XrdFrmFiles.hh    XrdFrmTSort.hh \
+                          XrdCmsNotify.hh \
+                          XrdOss.hh        XrdOssPath.hh     XrdOssSpace.hh \
+                          XrdOucHash.hh    XrdOucNSWalk.hh   XrdOucUtils.hh \
+                          XrdOucProg.hh    XrdOucStream.hh   XrdSysError.hh \
+                          XrdSysPlatform.hh
+	@echo Compiling XrdFrmPurge.cc
+	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPurge.o XrdFrmPurge.cc
+
+$(OBJDIR)/XrdFrmPurgMain.o: XrdFrmPurgMain.cc \
+                            XrdFrmConfig.hh      XrdFrmPurge.hh \
+                            XrdFrmTrace.hh \
+                            XrdNetOpts.hh        XrdNetSocket.hh \
+                            XrdOucUtils.cc \
+                            XrdSysError.hh       XrdSysHeaders.hh \
+                            XrdSysLogger.hh      XrdSysPthread.hh \
+                            XrdSysTimer.hh
+	@echo Compiling XrdFrmPurgMain.cc
+	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPurgMain.o XrdFrmPurgMain.cc
+
+$(OBJDIR)/XrdFrmTSort.o:  XrdFrmTSort.hh    XrdFrmTSort.cc  XrdFrmFiles.hh
+	@echo Compiling XrdFrmTSort.cc
+	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmTSort.o XrdFrmTSort.cc
+
 $(OBJDIR)/XrdFrmUtils.o:  XrdFrmUtils.hh    XrdFrmUtils.cc  XrdFrmTrace.hh \
                           XrdSysError.hh
 	@echo Compiling XrdFrmUtils.cc
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc
index 44fdb5f53eec22d7ff1b5d642d586dd342f6696d..6375ac4979b15fd45c4290ee9726af0c559f040c 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc
@@ -39,16 +39,16 @@ int XrdFrmAdmin::AuditNameNB(XrdFrmFileset *sP)
 
 // Report what is orphaned
 //
-   if (sP->File[XrdOssPath::isFail])
-      {num++; Msg("Orphaned fail file: ", sP->File[XrdOssPath::isFail]->Path);}
-   if (sP->File[XrdOssPath::isLock])
-      {num++; Msg("Orphaned lock file: ", sP->File[XrdOssPath::isLock]->Path);}
-   if (sP->File[XrdOssPath::isPfn ] )
-      {num++; Msg("Orphaned pfn  file: ", sP->File[XrdOssPath::isPfn ] ->Path);
-              Msg("PFN file refers to: ", sP->File[XrdOssPath::isPfn ] ->Link);
+   if (sP->failFile())
+      {num++; Msg("Orphaned fail file: ", sP->failPath());}
+   if (sP->lockFile())
+      {num++; Msg("Orphaned lock file: ", sP->lockPath());}
+   if (sP->pfnFile() )
+      {num++; Msg("Orphaned pfn  file: ", sP->pfnPath());
+              Msg("PFN file refers to: ", sP->pfnFile()->Link);
                  }
-   if (sP->File[XrdOssPath::isPin ] )
-      {num++; Msg("Orphaned pin  file: ", sP->File[XrdOssPath::isPin ] ->Path);}
+   if (sP->pinFile() )
+      {num++; Msg("Orphaned pin  file: ", sP->pinPath());}
 
 // Return if no fix is needed, otherwise check if we should ask before removal
 //
@@ -81,8 +81,8 @@ int XrdFrmAdmin::AuditNameNF(XrdFrmFileset *sP)
 
 // Indicate what is wrong
 //
-   Msg("Dangling link:  ", sP->File[XrdOssPath::isBase]->Path);
-   Msg("Missing target: ", sP->File[XrdOssPath::isBase]->Link);
+   Msg("Dangling link:  ", sP->basePath());
+   Msg("Missing target: ", sP->baseFile()->Link);
    numProb++;
 
 // Return if no fix is needed, otherwise check if we should ask before removal
@@ -95,8 +95,8 @@ int XrdFrmAdmin::AuditNameNF(XrdFrmFileset *sP)
 
 // Remove the symlink and associated files
 //
-   if (unlink(sP->File[XrdOssPath::isBase]->Path))
-      Emsg(errno,"remove symlink", sP->File[XrdOssPath::isBase]->Path);
+   if (unlink(sP->basePath()))
+      Emsg(errno,"remove symlink", sP->basePath());
       else if (AuditRemove(sP))
               {Msg("Symlink removed.");
                numFix++;
@@ -115,7 +115,7 @@ int XrdFrmAdmin::AuditNameNL(XrdFrmFileset *sP)
 
 // Indicate what is wrong
 //
-   Msg("Missing lock file: ", sP->File[XrdOssPath::isBase]->Path);
+   Msg("Missing lock file: ", sP->basePath());
    numProb++;
 
 // Return if no fix is needed, otherwise check if we should ask before removal
@@ -128,7 +128,7 @@ int XrdFrmAdmin::AuditNameNL(XrdFrmFileset *sP)
 
 // Create the lock file
 //
-   if (!mkFile(mkLF|isPFN, sP->File[XrdOssPath::isBase]->Path)) return 1;
+   if (!mkFile(mkLF|isPFN, sP->basePath())) return 1;
    Msg("Lock file created.");
    numFix++;
    return 1;
@@ -156,12 +156,12 @@ int XrdFrmAdmin::AuditNames()
    if (!Config.LocalPath(lDir, pDir, sizeof(pDir))) {finalRC = 4; return 1;}
    fP = new XrdFrmFiles(pDir, opts);
    while(Act && (sP = fP->Get(ec,1)))
-        {if (!(sP->File[XrdOssPath::isBase])) Act = AuditNameNB(sP);
-             else {if (sP->File[XrdOssPath::isBase]->Type == XrdOucNSWalk::NSEnt::isLink)
+        {if (!(sP->baseFile())) Act = AuditNameNB(sP);
+             else {if (sP->baseFile()->Type == XrdOucNSWalk::NSEnt::isLink)
                       Act = AuditNameNF(sP);
-                   if (Act && Opt.MPType && !(sP->File[XrdOssPath::isLock]))
+                   if (Act && Opt.MPType && !(sP->lockFile()))
                       Act = AuditNameNL(sP);
-                   if (Act && sP->File[XrdOssPath::isBase]->Link && isXA(sP->File[XrdOssPath::isBase]))
+                   if (Act && sP->baseFile()->Link && isXA(sP->baseFile()))
                       Act = AuditNameXA(sP);
                   }
          }
@@ -189,18 +189,18 @@ int XrdFrmAdmin::AuditNameXA(XrdFrmFileset *sP)
 
 // Make sure there is a PFN file here
 //
-   strcpy(Path, sP->File[XrdOssPath::isBase]->Link); strcat(Path, ".pfn");
+   strcpy(Path, sP->baseFile()->Link); strcat(Path, ".pfn");
    if (lstat(Path,&buf))
       {if (errno != ENOENT)
           {Emsg(errno, "stat ", Path); return AuditNameXL(sP,-1);}
-       Msg("Missing pfn link to ", sP->File[XrdOssPath::isBase]->Path);
+       Msg("Missing pfn link to ", sP->basePath());
        return AuditNameXL(sP,0);
       }
 
 // Make sure the PFN file is a link
 //
    if ((buf.st_mode & S_IFMT) != S_IFLNK)
-      {Msg("Invalid pfn file for ", sP->File[XrdOssPath::isBase]->Path);
+      {Msg("Invalid pfn file for ", sP->basePath());
        return AuditNameXL(sP,1);
       }
 
@@ -209,8 +209,8 @@ int XrdFrmAdmin::AuditNameXA(XrdFrmFileset *sP)
    if ((n = readlink(Path, lkbuff, sizeof(lkbuff)-1)) < 0)
       {Emsg(errno, "read link from ", Path); return AuditNameXL(sP,-1);}
    lkbuff[n] = '\0';
-   if (strcmp(sP->File[XrdOssPath::isBase]->Path, lkbuff))
-      {Msg("Incorrect pfn link to ", sP->File[XrdOssPath::isBase]->Path);
+   if (strcmp(sP->basePath(), lkbuff))
+      {Msg("Incorrect pfn link to ", sP->basePath());
        return AuditNameXL(sP,1);
       }
 
@@ -241,9 +241,9 @@ int XrdFrmAdmin::AuditNameXL(XrdFrmFileset *sP, int dorm)
 
 // Create the pfn symlink
 //
-   strcpy(Path, sP->File[XrdOssPath::isBase]->Link); strcat(Path, ".pfn");
+   strcpy(Path, sP->baseFile()->Link); strcat(Path, ".pfn");
    if (dorm) unlink(Path);
-   if (symlink(sP->File[XrdOssPath::isBase]->Path, Path)) 
+   if (symlink(sP->basePath(), Path))
       {Emsg(errno, "create symlink ", Path); return 1;}
    Msg("pfn symlink created.");
    numFix++;
@@ -260,20 +260,20 @@ int XrdFrmAdmin::AuditRemove(XrdFrmFileset *sP)
 
 // Remove the orphaned files
 //
-   if (sP->File[XrdOssPath::isFail])
-      {if (unlink(sP->File[XrdOssPath::isFail]->Path)) Emsg(errno,"remove fail file.");
+   if (sP->failFile())
+      {if (unlink(sP->failPath())) Emsg(errno,"remove fail file.");
           else rem++;
       }
-   if (sP->File[XrdOssPath::isLock])
-      {if (unlink(sP->File[XrdOssPath::isLock]->Path)) Emsg(errno,"remove lock file.");
+   if (sP->lockFile())
+      {if (unlink(sP->lockPath())) Emsg(errno,"remove lock file.");
           else rem++;
       }
-   if (sP->File[XrdOssPath::isPin ])
-      {if (unlink(sP->File[XrdOssPath::isPin ] ->Path)) Emsg(errno,"remove pin  file.");
+   if (sP-> pinFile())
+      {if (unlink(sP-> pinPath())) Emsg(errno,"remove pin  file.");
           else rem++;
       }
-   if (sP->File[XrdOssPath::isPfn ])
-      {if (unlink(sP->File[XrdOssPath::isPfn ] ->Path)) Emsg(errno,"remove pfn  file.");
+   if (sP-> pfnFile())
+      {if (unlink(sP-> pinPath())) Emsg(errno,"remove pfn  file.");
           else rem++;
       }
 
@@ -471,9 +471,9 @@ int XrdFrmAdmin::AuditSpaceXA(const char *Space, const char *Path)
 // Go and check out the files
 //
    while(Act && (sP = fP->Get(ec,1)))
-        {     if (!(sP->File[XrdOssPath::isBase])) Act = AuditNameNB(sP);
-         else if (!(sP->File[XrdOssPath::isPfn ] )) Act = AuditSpaceXANB(sP);
-         else {numFiles++; numBytes += sP->File[XrdOssPath::isBase]->Stat.st_size; continue;}
+        {     if (!(sP->baseFile())) Act = AuditNameNB(sP);
+         else if (!(sP-> pfnFile())) Act = AuditSpaceXANB(sP);
+         else {numFiles++; numBytes += sP->baseFile()->Stat.st_size; continue;}
         }
 
 // All done
@@ -494,11 +494,11 @@ int XrdFrmAdmin::AuditSpaceXANB(XrdFrmFileset *sP)
 
 // Update statistics which we may have to correct later
 //
-   numProb++; numFiles++; numBytes += sP->File[XrdOssPath::isBase]->Stat.st_size;
+   numProb++; numFiles++; numBytes += sP->baseFile()->Stat.st_size;
 
 // Report what is orphaned
 //
-   Msg("Missing pfn file for data file ", sP->File[XrdOssPath::isBase]->Path);
+   Msg("Missing pfn file for data file ", sP->basePath());
 
 // Return if no fix is needed, otherwise check if we should ask before removal
 //
@@ -510,8 +510,8 @@ int XrdFrmAdmin::AuditSpaceXANB(XrdFrmFileset *sP)
 
 // Remove the file
 //
-   if (unlink(sP->File[XrdOssPath::isBase]->Path)) Emsg(errno,"remove data file ", sP->File[XrdOssPath::isBase]->Path);
-      else {numFix++; numFiles--; numBytes -= sP->File[XrdOssPath::isBase]->Stat.st_size;}
+   if (unlink(sP->basePath())) Emsg(errno,"remove data file ", sP->basePath());
+      else {numFix++; numFiles--; numBytes -= sP->baseFile()->Stat.st_size;}
    return 1;
 }
   
@@ -674,8 +674,8 @@ int XrdFrmAdmin::AuditUsageXA(const char *Path, const char *Space)
 // Go and check out the files
 //
    while((sP = fP->Get(ec)))
-        {if ((sP->File[XrdOssPath::isBase]))
-            {numFiles++; numBytes += sP->File[XrdOssPath::isBase]->Stat.st_size;}
+        {if ((sP->baseFile()))
+            {numFiles++; numBytes += sP->baseFile()->Stat.st_size;}
         }
 
 // All done
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc
index 0e06379054b2b0ff859ea828e9d0a2b35b369dfa..f734e800737898eb1e04b48a33f48c835808fa07 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc
@@ -26,7 +26,6 @@ const char *XrdFrmAdminFilesCVSID = "$Id$";
 #include "XrdFrm/XrdFrmConfig.hh"
 #include "XrdFrm/XrdFrmFiles.hh"
 #include "XrdFrm/XrdFrmUtils.hh"
-#include "XrdOss/XrdOssPath.hh"
 
 using namespace XrdFrm;
 
@@ -58,8 +57,7 @@ int XrdFrmAdmin::mkLock(const char *Lfn)
 //
    fP = new XrdFrmFiles(Pfn, opts);
    while((sP = fP->Get(ec,1)))
-        {if (sP->File[XrdOssPath::isBase]
-         && mkFile(mkLF|isPFN, sP->File[XrdOssPath::isBase]->Path)) numFiles++;}
+        {if (sP->baseFile() && mkFile(mkLF|isPFN, sP->basePath())) numFiles++;}
 
 // All done
 //
@@ -96,8 +94,7 @@ int XrdFrmAdmin::mkPin(const char *Lfn, const char *Pdata, int Pdlen)
 //
    fP = new XrdFrmFiles(Pfn, opts);
    while((sP = fP->Get(ec,1)))
-        {if (sP->File[XrdOssPath::isBase]
-         &&  mkFile(mkPF|isPFN,sP->File[XrdOssPath::isBase]->Path,Pdata,Pdlen))
+        {if (sP->baseFile() && mkFile(mkPF|isPFN,sP->basePath(),Pdata,Pdlen))
              numFiles++;
         }
 
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc
index 2490332f914014e569b51314278bdb242dfa6618..0a63a84b0fbbbb99459e9334663964e543dc781b 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc
@@ -19,7 +19,6 @@
 #include "XrdFrm/XrdFrmConfig.hh"
 #include "XrdFrm/XrdFrmFiles.hh"
 #include "XrdFrm/XrdFrmTrace.hh"
-#include "XrdOss/XrdOssPath.hh"
 #include "XrdOuc/XrdOucArgs.hh"
 #include "XrdOuc/XrdOucNSWalk.hh"
 
@@ -78,8 +77,7 @@ int XrdFrmAdmin::FindNolk(XrdOucArgs &Spec)
    do {if (!Config.LocalPath(lDir, pDir, sizeof(pDir))) continue;
        fP = new XrdFrmFiles(pDir, opts);
        while((sP = fP->Get(ec)))
-            {if (!(sP->File[XrdOssPath::isLock]))
-                {Msg(sP->File[XrdOssPath::isBase]->Path); num++;}
+            {if (!(sP->lockFile())) {Msg(sP->basePath()); num++;}
             }
        if (ec) rc = 4;
        delete fP;
@@ -110,13 +108,13 @@ int XrdFrmAdmin::FindUnmi(XrdOucArgs &Spec)
    do {if (!Config.LocalPath(lDir, pDir, sizeof(pDir))) continue;
        fP = new XrdFrmFiles(pDir, opts);
        while((sP = fP->Get(ec)))
-            {     if (!(sP->File[XrdOssPath::isLock]))
+            {     if (!(sP->lockFile()))
                      Why = "Unmigrated; no lock file: ";
-             else if (sP->File[XrdOssPath::isBase]->Stat.st_mtime < 
-                      sP->File[XrdOssPath::isLock]->Stat.st_mtime)
+             else if (sP->baseFile()->Stat.st_mtime <
+                      sP->lockFile()->Stat.st_mtime)
                      Why="Unmigrated; modified: ";
              else continue;
-             Msg(Why, sP->File[XrdOssPath::isBase]->Path); num++;
+             Msg(Why, sP->basePath()); num++;
             }
        if (ec) rc = 4;
        delete fP;
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc
index 50ab2455b449b4ce9914ea45ce2b38d059dfcfac..6d3ad80e6a7348b1f4313677e239a5d9978c4d9a 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc
@@ -128,10 +128,10 @@ int XrdFrmAdmin::QuerySpace(XrdOucArgs &Spec)
                }
        else{fP = new XrdFrmFiles(pfn, opts);
             while((sP = fP->Get(ec,1)))
-                 {if (sP->File[XrdOssPath::isBase])
-                     QuerySpace(sP->File[XrdOssPath::isBase]->Path,
-                                sP->File[XrdOssPath::isBase]->Link,
-                                sP->File[XrdOssPath::isBase]->Lksz);
+                 {if (sP->baseFile())
+                     QuerySpace(sP->basePath(),
+                                sP->baseFile()->Link,
+                                sP->baseFile()->Lksz);
                  }
             if (ec) finalRC = 4;
             delete fP;
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc
index f6ec363ab874f4da92f770a7e0f5382adbed64b1..c0dc46f0a14eb8fc15b560097cfc31eba7337f62 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc
@@ -25,17 +25,21 @@ const char *XrdFrmConfigCVSID = "$Id$";
 #include "Xrd/XrdInfo.hh"
 #include "XrdCms/XrdCmsNotify.hh"
 #include "XrdFrm/XrdFrmConfig.hh"
-#include "XrdFrm/XrdFrmTrace.hh"
+#include "XrdFrm/XrdFrmTrace.hh"  // Add to GNUmake
+#include "XrdFrm/XrdFrmUtils.hh"
 #include "XrdNet/XrdNetDNS.hh"
 #include "XrdOss/XrdOss.hh"
 #include "XrdOss/XrdOssSpace.hh"
 #include "XrdOuc/XrdOuca2x.hh"
 #include "XrdOuc/XrdOucEnv.hh"
+#include "XrdOuc/XrdOucExport.hh"
 #include "XrdOuc/XrdOucMsubs.hh"
 #include "XrdOuc/XrdOucName2Name.hh"
 #include "XrdOuc/XrdOucProg.hh"
 #include "XrdOuc/XrdOucStream.hh"
+#include "XrdOuc/XrdOucPList.hh"
 #include "XrdOuc/XrdOucTList.hh"
+#include "XrdOuc/XrdOucTokenizer.hh" // Add to GNUmake
 #include "XrdOuc/XrdOucUtils.hh"
 #include "XrdSys/XrdSysError.hh"
 #include "XrdSys/XrdSysHeaders.hh"
@@ -100,7 +104,7 @@ void *XrdFrmConfigMum(void *parg)
 // All done
 //
    theSE->mySem.Post();
-   pthread_exit((void *)0);
+// pthread_exit((void *)0);
    return (void *)0;
 }
 
@@ -123,6 +127,7 @@ void *XrdLogWorker(void *parg)
 /******************************************************************************/
   
 XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo)
+             : dfltPolicy("*", -2, -3, 72000, 0)
 {
    char *sP, buff[128];
 
@@ -144,8 +149,18 @@ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo)
    cmsPath  = 0;
    monStage = 0;
    sSpec    = 0;
-   Solitary = 0;
+   isOTO    = 0;
+   Test     = 0;
+   Verbose  = 0;
+   pathList = 0;
+   spacList = 0;
    lockFN   = "DIR_LOCK";  // May be ".DIR_LOCK" if hidden
+   cmdHold  = -1;
+   cmdFree  = 0;
+   pVecNum  = 0;
+   pProg    = 0;
+   Fix      = 0;
+   dirHold  = 40*60*60;
 
    myUid    = geteuid();
    myGid    = getegid();
@@ -170,7 +185,7 @@ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo)
         if (ss == ssAdmin) {myFrmid = "admin"; myFrmID = "ADMIN";}
    else if (ss == ssMigr)  {myFrmid = "migr";  myFrmID = "MIGR";}
    else if (ss == ssPstg)  {myFrmid = "pstg";  myFrmID = "PSTG";}
-   else if (ss == ssPurg)  {myFrmid = "purg";  myFrmID = "PURG";}
+   else if (ss == ssPurg)  {myFrmid = "purge"; myFrmID = "PURG";}
    else                    {myFrmid = "frm";   myFrmID = "FRM";}
 
 // Set correct error prefix
@@ -181,8 +196,7 @@ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo)
 
 // Set correct oss type
 //
-   sprintf(buff, "XRDOSSTYPE=%s", myFrmid);
-   putenv(strdup(buff));
+   XrdOucEnv::Export("XRDOSSTYPE", myFrmid);
 
 // Set correct option prefix
 //
@@ -200,7 +214,7 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
 {
    extern XrdOss *XrdOssGetSS(XrdSysLogger *, const char *, const char *);
    XrdFrmConfigSE theSE;
-   int n, retc, isMum = 0, myXfrMax = -1, NoGo = 0, Verbose = 0;
+   int n, retc, isMum = 0, myXfrMax = -1, NoGo = 0;
    const char *temp;
    char c, buff[1024], *logfn = 0;
    long long logkeep = 0;
@@ -212,6 +226,7 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
     retc = strlen(argv[0]);
     while(retc--) if (argv[0][retc] == '/') break;
     myProg = &argv[0][retc+1];
+    vectArg = argv; numcArg = argc;
 
 // Process the options
 //
@@ -224,7 +239,9 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
                  ConfigFN = strdup(optarg);
                  break;
        case 'd': Trace.What |= TRACE_ALL;
-                 putenv((char *)"XRDDEBUG=1");
+                 XrdOucEnv::Export("XRDDEBUG","1");
+                 break;
+       case 'f': Fix = 1;
                  break;
        case 'h': Usage(0);
        case 'k': n = strlen(optarg)-1;
@@ -242,8 +259,13 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
                  break;
        case 'n': myInsName = optarg;
                  break;
+       case 'O': isOTO = 1;
+                 if (!ConfigOTO(optarg)) Usage(1);
+                 break;
        case 's': sSpec = 1;
                  break;
+       case 'T': Test  = 1;
+                 break;
        case 'v': Verbose = 1;
                  break;
        case 'w': if (XrdOuca2x::a2tm(Say,"wait time",optarg,&WaitTime))
@@ -289,14 +311,14 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
    sprintf(buff,"XRDINSTANCE=%s %s@%s",myProg,(myInst ? myInst:"anon"),myName);
    putenv(strdup(buff)); // XRDINSTANCE
    myInstance = strdup(index(buff,'=')+1);
-   sprintf(buff,"XRDHOST=%s", myName); putenv(strdup(buff));
-   sprintf(buff,"XRDPROG=%s", myProg); putenv(strdup(buff));
-   if (myInsName)
-      {sprintf(buff, "XRDNAME=%s", myInsName); putenv(strdup(buff));}
+   XrdOucEnv::Export("XRDHOST", myName);
+   XrdOucEnv::Export("XRDPROG", myProg);
+   if (myInsName) XrdOucEnv::Export("XRDNAME", myInsName);
 
 // We need to divert the output if we are in admin mode with no logfile
 //
-   if (!logfn && ssID == ssAdmin && !Verbose) isMum = ConfigMum(theSE);
+   if (!logfn && (ssID == ssAdmin || isOTO) && !Trace.What)
+      isMum = ConfigMum(theSE);
 
 // Put out the herald
 //
@@ -318,21 +340,27 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
 // Obtain and configure the oss (lightweight option only)
 //
    if (!isAgent)
-      {putenv(strdup("XRDREDIRECT=Q"));
-       Solitary = 1;
+      {XrdOucEnv::Export("XRDREDIRECT", "Q");
+       XrdOucEnv::Export("XRDOSSTYPE",  myFrmID);
+       if (ssID == ssMigr || ssID == ssPurg)
+          XrdOucEnv::Export("XRDOSSCSCAN", "off");
        if (!NoGo && !(ossFS=XrdOssGetSS(Say.logger(),ConfigFN,ossLib))) NoGo=1;
       }
 
-// Configure the admin component
-//
-   if (!NoGo && ssID == ssAdmin
-   && (ConfigN2N() || ConfigMss())) NoGo = 1;
-
-// Configure the pstg  component
-//
-   if (!NoGo && ssID == ssPstg && !isAgent
-   && (ConfigN2N() || !XrdXrootdMonitor::Init(0,&Say)
-      || !(xfrVec = ConfigCmd("xfrcmd", xfrCmd)))) NoGo = 1;
+// Configure each specific component
+//
+   if (!NoGo) switch(ssID)
+      {case ssAdmin: NoGo = (ConfigN2N() || ConfigMss());
+                     break;
+       case ssMigr:  NoGo = (ConfigN2N() || ConfigMP("migratable"));
+                     break;
+       case ssPstg:  if (!isAgent && (ConfigN2N() || ConfigMss()
+                     || !(xfrVec = ConfigCmd("xfrcmd", xfrCmd)))) NoGo = 1;
+                     break;
+       case ssPurg:  NoGo = (ConfigN2N() || ConfigMP("purgeable"));
+                     break;
+       default:      break;
+      }
 
 // If we have a post-processing routine, invoke it
 //
@@ -364,7 +392,7 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)())
 //
    return !NoGo;
 }
-
+  
 /******************************************************************************/
 /* Public:                     L o c a l P a t h                              */
 /******************************************************************************/
@@ -465,58 +493,106 @@ XrdOucMsubs *XrdFrmConfig::ConfigCmd(const char *cname, char *cdata)
 }
 
 /******************************************************************************/
-/* Private:                    C o n f i g M s s                              */
+/* Private:                     C o n f i g M P                               */
 /******************************************************************************/
-  
-int XrdFrmConfig::ConfigMss()
-{
-   if (MSSCmd)
-      {MSSProg = new XrdOucProg(&Say);
-       if (MSSProg->Setup(MSSCmd)) return 1;
-      }
-   return 0;
-}
 
-/******************************************************************************/
-/* Private:                    C o n f i g N 2 N                              */
-/******************************************************************************/
-
-int XrdFrmConfig::ConfigN2N()
+int XrdFrmConfig::ConfigMP(const char *pType)
 {
-   XrdSysPlugin    *myLib;
-   XrdOucName2Name *(*ep)(XrdOucgetName2NameArgs);
+   EPNAME("ConfigMP");
+   extern XrdOucPListAnchor *XrdOssRPList;
+   XrdOucTList *nP, *tP, *mypList = 0, *expList = 0;
+   char pDir[MAXPATHLEN+1];
+   long long pOpts, xOpt = (*pType == 'm' ? XRDEXP_MIG : XRDEXP_PURGE);
+   int i, NoGo = 0;
+
+// Verify that we have an RPList
+//
+   if (!XrdOssRPList)
+      {Say.Emsg("Config", "Cannot determine", pType, "paths."); return 1;}
+
+// Parse the arguments which consist of space names and paths
+//
+   for (i = nextArg; i < numcArg; i++)
+       {char *psVal = vectArg[i];
+        int   psLen = strlen(psVal);
+        if (*psVal == '/')
+           {pOpts = XrdOssRPList->Find(psVal);
+            if (pOpts & xOpt) mypList = InsertPL(mypList, psVal, psLen,
+                                                (pOpts & XRDEXP_NOTRW ? 0 : 1));
+               else {Say.Say("Config", psVal, "not marked", pType); NoGo = 1;}
+           } else {
+            VPInfo *vP = VPList;
+            while(vP && strcmp(psVal, vP->Name)) vP = vP->Next;
+            if (vP) spacList = new XrdOucTList(psVal, psLen, spacList);
+               else {Say.Emsg("Config", "Space", psVal, "not defined.");
+                     NoGo = 1;
+                    }
+           }
+       }
 
-// If we have no library path then use the default method (this will always
-// succeed).
+// Check if we should continue
 //
-   if (!N2N_Lib)
-      {the_N2N = XrdOucgetName2Name(&Say, ConfigFN, "", LocalRoot, RemoteRoot);
-       if (LocalRoot)  lcl_N2N = the_N2N;
-       if (RemoteRoot) rmt_N2N = the_N2N;
-       return 0;
-      }
+   if (NoGo) return 1;
 
-// Create a pluin object (we will throw this away without deletion because
-// the library must stay open but we never want to reference it again).
+// Get correct path list
 //
-   if (!(myLib = new XrdSysPlugin(&Say, N2N_Lib))) return 1;
+   if (!mypList)
+      {XrdOucPList *fP = XrdOssRPList->First();
+       short sval[2];
+       while(fP)
+            {sval[0] = (fP->Flag() & XRDEXP_NOTRW ? 0 : 1);
+             sval[1] = fP->Plen();
+             if (fP->Flag() & xOpt)
+                 mypList = new XrdOucTList(fP->Path(), sval, mypList);
+                 else
+                 expList = new XrdOucTList(fP->Path(), sval, expList);
+             fP = fP->Next();
+            }
+       if (!mypList)
+          {Say.Emsg("Config", "No", pType, "paths found."); return 1;}
+      }
 
-// Now get the entry point of the object creator
-//
-   ep = (XrdOucName2Name *(*)(XrdOucgetName2NameArgs))(myLib->getPlugin("XrdOucgetName2Name"));
-   if (!ep) return 1;
+// Now we need to construct a search list which may include excludes which
+// hapen when we get nested subtrees with different options
+//
+   while((tP = mypList))
+        {if (!LocalPath(tP->text, pDir, sizeof(pDir))) NoGo = 1;
+            else {pathList = new VPInfo(pDir, int(tP->sval[0]), pathList);
+                  DEBUG("Will scan " <<(tP->sval[0]?"r/w: ":"r/o: ") <<pDir);
+                  nP = expList;
+                  while(nP)
+                       {if (!strncmp(tP->text, nP->text, tP->sval[1]))
+                           InsertXD(nP->text);
+                        nP = nP->next;
+                       }
+                  mypList = tP->next; delete tP;
+                 }
+        }
 
+// Delete the explist
+//
+   while((tP = expList)) {expList = tP->next; delete tP;}
 
-// Get the Object now
+// All done now
 //
-   lcl_N2N = rmt_N2N = the_N2N = ep(&Say, ConfigFN, 
-                                   (N2N_Parms ? N2N_Parms : ""),
-                                   LocalRoot, RemoteRoot);
-   return lcl_N2N == 0;
+   return NoGo;
+}
+
+/******************************************************************************/
+/* Private:                    C o n f i g M s s                              */
+/******************************************************************************/
+  
+int XrdFrmConfig::ConfigMss()
+{
+   if (MSSCmd)
+      {MSSProg = new XrdOucProg(&Say);
+       if (MSSProg->Setup(MSSCmd)) return 1;
+      }
+   return 0;
 }
 
 /******************************************************************************/
-/*                             C o n f i g M u m                              */
+/* Private:                    C o n f i g M u m                              */
 /******************************************************************************/
 
 int XrdFrmConfig::ConfigMum(XrdFrmConfigSE &theSE)
@@ -568,25 +644,101 @@ int XrdFrmConfig::ConfigMum(XrdFrmConfigSE &theSE)
    FD.stdErr = -1;
    return 1;
 }
+
+/******************************************************************************/
+/* Private:                    C o n f i g N 2 N                              */
+/******************************************************************************/
+
+int XrdFrmConfig::ConfigN2N()
+{
+   XrdSysPlugin    *myLib;
+   XrdOucName2Name *(*ep)(XrdOucgetName2NameArgs);
+
+// If we have no library path then use the default method (this will always
+// succeed).
+//
+   if (!N2N_Lib)
+      {the_N2N = XrdOucgetName2Name(&Say, ConfigFN, "", LocalRoot, RemoteRoot);
+       if (LocalRoot)  lcl_N2N = the_N2N;
+       if (RemoteRoot) rmt_N2N = the_N2N;
+       return 0;
+      }
+
+// Create a pluin object (we will throw this away without deletion because
+// the library must stay open but we never want to reference it again).
+//
+   if (!(myLib = new XrdSysPlugin(&Say, N2N_Lib))) return 1;
+
+// Now get the entry point of the object creator
+//
+   ep = (XrdOucName2Name *(*)(XrdOucgetName2NameArgs))(myLib->getPlugin("XrdOucgetName2Name"));
+   if (!ep) return 1;
+
+
+// Get the Object now
+//
+   lcl_N2N = rmt_N2N = the_N2N = ep(&Say, ConfigFN, 
+                                   (N2N_Parms ? N2N_Parms : ""),
+                                   LocalRoot, RemoteRoot);
+   return lcl_N2N == 0;
+}
   
+/******************************************************************************/
+/*                             C o n f i g O T O                              */
+/******************************************************************************/
+  
+int XrdFrmConfig::ConfigOTO(char *Parms)
+{
+   char *Comma;
+
+// Pick up free argument
+//
+   if ((Comma = index(Parms, ','))) *Comma = '\0';
+   if (XrdOuca2x::a2sp(Say, "free value", Parms, &cmdFree, 1)) return 0;
+
+// Pick up hold argument
+//
+   if (!Comma || !(*(Comma+1))) return 1;
+   if (*(Comma+1) == ',') Comma++;
+      else {Parms = Comma+1;
+            if ((Comma = index(Parms, ','))) *Comma = '\0';
+            if (XrdOuca2x::a2i(Say,"hold value",Parms,&cmdHold,0)) return 0;
+           }
+
+// All done
+//
+   return 1;
+}
+
 /******************************************************************************/
 /*                           C o n f i g P a t h s                            */
 /******************************************************************************/
   
 int XrdFrmConfig::ConfigPaths()
 {
-   char *xPath, *yPath, buff[MAXPATHLEN];
+   char *xPath, *yPath, buff[MAXPATHLEN]; 
+   const char *insName;
    int retc;
 
 // Establish the cmsd notification path
 //
-   if (!(xPath = AdminPath) && !(xPath = getenv("XRDADMINPATH")))
-      xPath = (char *)"/tmp/";
-   cmsPath = new XrdCmsNotify(&Say, xPath, myInsName, XrdCmsNotify::isServ);
+   
 
 // Set the directory where the meta information is to go
-//
-   yPath = XrdOucUtils::genPath(xPath, myInsName, "frm");
+//  XRDADMINPATH already contains the instance name
+
+   if ( (!AdminPath) && (xPath = getenv("XRDADMINPATH"))) {
+       insName = 0;
+   }
+   else {
+       if (!(xPath = AdminPath))
+           xPath = (char *)"/tmp/";
+       insName = myInsName;
+   }
+   
+   cmsPath = new XrdCmsNotify(&Say, xPath, insName, XrdCmsNotify::isServ);
+
+   yPath = XrdOucUtils::genPath(xPath, insName, "frm");
    if (AdminPath) free(AdminPath); AdminPath = yPath;
 
 // Create the admin directory if it does not exists
@@ -668,29 +820,50 @@ int XrdFrmConfig::ConfigXeq(char *var, int mbok)
 
 // Process directives specific to each subsystem
 //
-// if (ssID == ssAdmin)
-//    {if (!strcmp(var, "oss.mssgwcmd"  )) return Grab(var, &MSSCmd,    0);
-//     if (!strcmp(var, "oss.msscmd"    )) return Grab(var, &MSSCmd,    0);
-//    }
-
-   if (ssID == ssPstg || ssID == ssAdmin)
-      {if (!strcmp(var, "ofs.osslib"    )) return Grab(var, &ossLib,    0);
+   if (ssID == ssAdmin)
+      {
+       if (!strcmp(var, "ofs.osslib"    )) return Grab(var, &ossLib,    0);
        if (!strcmp(var, "oss.cache"     )) return xcache();
        if (!strcmp(var, "oss.localroot" )) return Grab(var, &LocalRoot, 0);
        if (!strcmp(var, "oss.namelib"   )) return xnml();
        if (!strcmp(var, "oss.remoteroot")) return Grab(var, &LocalRoot, 0);
+//     if (!strcmp(var, "oss.mssgwcmd"  )) return Grab(var, &MSSCmd,    0);
+//     if (!strcmp(var, "oss.msscmd"    )) return Grab(var, &MSSCmd,    0);
+      }
+
+   if (ssID == ssMigr)
+      {
+       if (!strcmp(var, "stopfile"      )) return Grab(var, &StopFile,  0);
+       if (!strcmp(var, "waittime"      )) return xwtm();
       }
 
    if (ssID == ssPstg)
       {
-       if (!strcmp(var, "xrootd.monitor")) return xmon();
+       if (!strcmp(var, "ofs.osslib"    )) return Grab(var, &ossLib,    0);
+       if (!strcmp(var, "oss.cache"     )) return xcache();
+       if (!strcmp(var, "oss.localroot" )) return Grab(var, &LocalRoot, 0);
+       if (!strcmp(var, "oss.namelib"   )) return xnml();
+       if (!strcmp(var, "oss.remoteroot")) return Grab(var, &LocalRoot, 0);
+       if (!strcmp(var, "stopfile"      )) return Grab(var, &StopFile,  0);
        if (!strcmp(var, "waittime"      )) return xwtm();
+       if (!strcmp(var, "xrootd.monitor")) return xmon();
        if (!strcmp(var, "xfrmax"        )) return xmaxx();
        if (!strcmp(var, "xfrcmd"        )) return Grab(var, &xfrCmd,    1);
-       if (!strcmp(var, "stopfile"      )) return Grab(var, &StopFile,  0);
        if (!strcmp(var, "queuepath"     )) return Grab(var, &qPath,     0);
       }
 
+   if (ssID == ssPurg)
+      {
+       if (!strcmp(var, "dirhold"       )) return xdpol();
+       if (!strcmp(var, "oss.cache"     )) return xcache(1);
+       if (!strcmp(var, "ofs.osslib"    )) return Grab(var, &ossLib,    0);
+       if (!strcmp(var, "policy"        )) return xpol();
+       if (!strcmp(var, "polprog"       )) return xpolprog();
+       if (!strcmp(var, "queuepath"     )) return Grab(var, &qPath,     0);
+       if (!strcmp(var, "stopfile"      )) return Grab(var, &StopFile,  0);
+       if (!strcmp(var, "waittime"      )) return xwtm();
+      }
+
    // No match found, complain.
    //
    if (!mbok) cFile->noEcho();
@@ -700,6 +873,19 @@ int XrdFrmConfig::ConfigXeq(char *var, int mbok)
    return 0;
 }
 
+/******************************************************************************/
+/* Private:                      g e t T i m e                                */
+/******************************************************************************/
+
+int XrdFrmConfig::getTime(const char *emsg, const char *item, int *val,
+                          int minv, int maxv)
+{
+    if (strcmp(item, "forever"))
+       return  XrdOuca2x::a2tm(Say, emsg, item, val, minv, maxv);
+    *val = -1;
+    return 0;
+}
+  
 /******************************************************************************/
 /* Private:                         G r a b                                   */
 /******************************************************************************/
@@ -743,6 +929,52 @@ int XrdFrmConfig::Grab(const char *var, char **Dest, int nosubs)
    return 0;
 }
 
+/******************************************************************************/
+/* Private:                     I n s e r t P L                               */
+/******************************************************************************/
+  
+XrdOucTList *XrdFrmConfig::InsertPL(XrdOucTList *pL, const char *Path,
+                                    int Plen, int isRW)
+{
+   short sval[2] = {isRW, Plen};
+   XrdOucTList *pP = 0, *tP = pL;
+
+// Find insertion point
+//
+   while(tP && tP->sval[1] < Plen) {pP = tP; tP = tP->next;}
+
+// Insert new element
+//
+   if (pP) pP->next = new XrdOucTList(Path, sval, tP);
+      else       pL = new XrdOucTList(Path, sval, tP);
+
+// Return the new list
+//
+   return pL;
+}
+
+/******************************************************************************/
+/* Private:                     I n s e r t X D                               */
+/******************************************************************************/
+
+void XrdFrmConfig::InsertXD(const char *Path)
+{
+   EPNAME("InsertXD");
+   char pBuff[MAXPATHLEN], *pP;
+   int n = strlen(Path);
+
+// Make sure this does not end with a slash
+//
+   strcpy(pBuff, Path);
+   pP = pBuff + n - 1;
+   while(*pP == '/' && pP != pBuff) {*pP-- = '\0'; n--;}
+
+// Insert this directory into the exclude list for the current path
+//
+   pathList->Dir = new XrdOucTList(pBuff, n, pathList->Dir);
+   DEBUG("Excluding '" <<pBuff <<"'");
+}
+  
 /******************************************************************************/
 /* Private:                        U s a g e                                  */
 /******************************************************************************/
@@ -753,6 +985,53 @@ void XrdFrmConfig::Usage(int rc)
      _exit(rc);
 }
 
+/******************************************************************************/
+/* Private:                       x a p a t h                                 */
+/******************************************************************************/
+
+/* Function: xapath
+
+   Purpose:  To parse the directive: adminpath <path> [group]
+
+             <path>    the path of the FIFO to use for admin requests.
+
+             group     allows group access to the admin path
+
+   Output: 0 upon success or !0 upon failure.
+*/
+
+int XrdFrmConfig::xapath()
+{
+    char *pval, *val;
+    mode_t mode = S_IRWXU;
+
+// Get the path
+//
+   pval = cFile->GetWord();
+   if (!pval || !pval[0])
+      {Say.Emsg("Config", "adminpath not specified"); return 1;}
+
+// Make sure it's an absolute path
+//
+   if (*pval != '/')
+      {Say.Emsg("Config", "adminpath not absolute"); return 1;}
+
+// Record the path
+//
+   if (AdminPath) free(AdminPath);
+   AdminPath = strdup(pval);
+
+// Get the optional access rights
+//
+   if ((val = cFile->GetWord()) && val[0])
+      {if (!strcmp("group", val)) mode |= S_IRWXG;
+          else {Say.Emsg("Config", "invalid admin path modifier -", val);
+                return 1;
+               }
+      }
+   AdminMode = mode;
+   return 0;
+}
 
 /******************************************************************************/
 /*                                x c a c h e                                 */
@@ -769,7 +1048,7 @@ void XrdFrmConfig::Usage(int rc)
    Output: 0 upon success or !0 upon failure.
 */
 
-int XrdFrmConfig::xcache()
+int XrdFrmConfig::xcache(int isPrg)
 {
    char *val, *pfxdir, *sfxdir;
    char grp[XrdOssSpace::minSNbsz], fn[MAXPATHLEN], dn[MAXNAMLEN];
@@ -842,61 +1121,38 @@ void XrdFrmConfig::xcacheBuild(char *grp, char *fn, int isxa)
 
    while(nP && strcmp(nP->Name, grp)) nP = nP->Next;
 
-   if (!nP) VPList = nP = new VPInfo(grp, VPList);
+   if (!nP) VPList = nP = new VPInfo(grp, 0, VPList);
 
    tP = nP->Dir;
    while(tP && strcmp(tP->text, fn)) tP = tP->next;
    if (!tP) nP->Dir = new XrdOucTList(fn, isxa, nP->Dir);
 }
-
+  
 /******************************************************************************/
-/* Private:                       x a p a t h                                 */
+/* Private:                        x d p o l                                  */
 /******************************************************************************/
+  
 
-/* Function: xapath
-
-   Purpose:  To parse the directive: adminpath <path> [group]
+/* Function: xdpol
 
-             <path>    the path of the FIFO to use for admin requests.
+   Purpose:  To parse the directive: dirpolicy <sec>
 
-             group     allows group access to the admin path
+             <sec>     number of seconds to hold an empty directory or the
+                       word 'forever'.
 
    Output: 0 upon success or !0 upon failure.
 */
+int XrdFrmConfig::xdpol()
+{   int htm;
+    char *val;
 
-int XrdFrmConfig::xapath()
-{
-    char *pval, *val;
-    mode_t mode = S_IRWXU;
-
-// Get the path
-//
-   pval = cFile->GetWord();
-   if (!pval || !pval[0])
-      {Say.Emsg("Config", "adminpath not specified"); return 1;}
-
-// Make sure it's an absolute path
-//
-   if (*pval != '/')
-      {Say.Emsg("Config", "adminpath not absolute"); return 1;}
-
-// Record the path
-//
-   if (AdminPath) free(AdminPath);
-   AdminPath = strdup(pval);
-
-// Get the optional access rights
-//
-   if ((val = cFile->GetWord()) && val[0])
-      {if (!strcmp("group", val)) mode |= S_IRWXG;
-          else {Say.Emsg("Config", "invalid admin path modifier -", val);
-                return 1;
-               }
-      }
-   AdminMode = mode;
-   return 0;
+    if (!(val = cFile->GetWord()))
+       {Say.Emsg("Config",  "dirpolicy hold time not specified"); return 1;}
+    if (XrdOuca2x::a2tm(Say,"dirpolicy hold time", val, &htm, 0)) return 1;
+    dirHold = htm;
+    return 0;
 }
-  
+
 /******************************************************************************/
 /* Private:                        x m a x x                                  */
 /******************************************************************************/
@@ -915,7 +1171,7 @@ int XrdFrmConfig::xmaxx()
 
     if (!(val = cFile->GetWord()))
        {Say.Emsg("Config", "xfrmax value not specified"); return 1;}
-    if (XrdOuca2x::a2tm(Say, "xfrmax", val, &xmax, 1)) return 1;
+    if (XrdOuca2x::a2i(Say, "xfrmax", val, &xmax, 1)) return 1;
     xfrMax = xmax;
     return 0;
 }
@@ -1071,6 +1327,226 @@ int XrdFrmConfig::xnml()
    return 0;
 }
 
+/******************************************************************************/
+/* Private:                         x p o l                                   */
+/******************************************************************************/
+
+/* Function: xpol
+
+   Purpose:  To parse the directive: policy {*|sname} {nopurge|min [max]] [opts]
+
+             *         The default policy for all spaces.
+
+             sname     The policy to apply for this space. Defaults apply for
+                       unspecified values. To make sure the specified default
+                       is used, the '*' entry must appear first.
+
+             nopurge   Turns off purging.
+
+             min%      Minimum free space; purge starts when less available.
+                       Can be specified as a percentage (i.e., n%) or an
+                       absolute size value (with k, m, g, t suffix).
+                       Default: 5%
+
+             max%      Maximum free space; purge stops  when more available.
+                       Must be specified in the same units as min and must be
+                       greater than min.
+                       Default: min% + 2 or min * 1.2
+
+       opts: hold <tm> Time to hold a file before it can be purged. The <tm>
+                       can be a suffixed number or 'forever'.
+                       Default: 20h (20*3600)s
+
+             polprog   Invoke the policy program to do final determination.
+
+
+   Output: 0 upon success or !0 upon failure.
+*/
+int XrdFrmConfig::xpol()
+{
+   Policy *pP = &dfltPolicy;
+   char *val, sname[XrdOssSpace::minSNbsz];
+   long long minP = dfltPolicy.minFree, maxP = dfltPolicy.maxFree;
+   int       Hold = dfltPolicy.Hold, Ext = 0;
+   struct purgeopts {const char *opname; int isTime; int *oploc;} pgopts[] =
+      {
+       {"polprog", -1, &Ext},
+       {"hold",     1, &Hold}
+      };
+   int i, rc, numopts = sizeof(pgopts)/sizeof(struct purgeopts);
+
+// Get the space name
+//
+   if (!(val = cFile->GetWord()))
+      {Say.Emsg("Config", "space name not specified"); return 1;}
+   if (strlen(val) >= sizeof(sname))
+      {Say.Emsg("Config", "space name '", val, "' too long"); return 1;}
+
+// If we have an equal sign then an external policy is being defined
+//
+   if (!strcmp("=", val)) return xpolprog();
+   strcpy(sname, val);
+
+// The next item may be minimum percentage followed by a maximum percentage
+// Otherwise, it may be 'nopurge'.
+//
+   if (    (val = cFile->GetWord()) && isdigit(*val))
+      {if (    XrdOuca2x::a2sp(Say, "min free", val, &minP, 1)) return 1;
+       if ((val = cFile->GetWord()) && isdigit(*val))
+          {if (XrdOuca2x::a2sp(Say, "max free", val, &maxP, 1)) return 1;
+           if ((minP < 0 && maxP >= 0) || (minP >= 0 && maxP < 0))
+              {Say.Emsg("Config", "purge min/max may not differ in type.");
+               return 1;
+              }
+           if (XRDABS(minP) >= XRDABS(maxP))
+              {Say.Emsg("Config", "purge min must be < max value."); return 1;}
+           val = cFile->GetWord();
+          } else {
+           if (minP < 0) maxP = (minP < -99 ? -100 : minP - 1);
+              else       maxP = (minP * 120LL)/100LL;
+          }
+      } else if (val && !strcmp(val, "nopurge"))
+                {minP = maxP = 0;
+                 if ((val = cFile->GetWord()))
+                    {Say.Say("Config warning: ignoring extraneous policy option '",val,"'.");
+                     val = 0;
+                    }
+                }
+
+// Pick up the remining options
+//
+   while(val)
+        {for (i = 0; i < numopts; i++) if (!strcmp(val,pgopts[i].opname)) break;
+         if (i >= numopts)
+            {Say.Say("Config warning: ignoring invalid policy option '",val,"'.");
+             val = cFile->GetWord();
+             continue;
+            }
+         if (pgopts[i].isTime < 0) *(pgopts[i].oploc) = 1;
+            else {if (!(val = cFile->GetWord()))
+                     {Say.Emsg("Config", "policy", pgopts[i].opname,
+                                         "argument not specified.");
+                      return 1;
+                     }
+                  rc = (pgopts[i].isTime
+                     ?         getTime(    "purge value",val,pgopts[i].oploc,0)
+                     : XrdOuca2x::a2i (Say,"purge value",val,pgopts[i].oploc,0));
+                  if (rc) return 1;
+                 }
+         val = cFile->GetWord();
+        }
+
+// If an external policy applies, it must be present
+//
+   if (Ext && !pProg)
+      {Say.Emsg("Config", "External policy has not been pre-defined.");
+       return 1;
+      }
+
+// Add this policy definition
+//
+   while(pP && strcmp(pP->Sname, sname)) pP = pP->Next;
+   if (pP) {pP->minFree=minP; pP->maxFree=maxP; pP->Hold=Hold; pP->Ext=Ext;}
+      else {pP = new Policy(sname, minP, maxP, Hold, Ext);
+            pP->Next = dfltPolicy.Next; dfltPolicy.Next = pP;
+           }
+    return 0;
+}
+
+/******************************************************************************/
+/* Private:                     x p o l p r o g                               */
+/******************************************************************************/
+  
+/* Function: xpolprog
+
+   Purpose:  To parse the directive: policy = [vars] |<prog> [args]
+
+   Where:
+             =         Defines an external policy via a program, as follows:
+
+             vars      The information to ship to the program via stdin:
+                       atime   - access time
+                       ctime   - create time
+                       fname   - the filename itself
+                       fsize   - file size
+                       fspace  - free  space
+                       mtime   - modify time
+                       pfn     - physical file name
+                       sname   - space name
+                       tspace  - total space
+
+             |<prog>   The name of the policy program to receive the info.
+
+             args      Optional program arguments (substituted), up to 8.
+
+   Output: 0 upon success or !0 upon failure.
+*/
+int XrdFrmConfig::xpolprog()
+{
+   char *val, pBuff[4096], *pbP = pBuff;
+   struct polopts {const char *opname; int opval;} plopts[] =
+      {
+       {"atime",  PP_atime },
+       {"ctime",  PP_ctime },
+       {"fname",  PP_fname },
+       {"fsize",  PP_fsize },
+       {"fspace", PP_fspace},
+       {"mtime",  PP_mtime },
+       {"pfn",    PP_pfn   },
+       {"sname",  PP_sname },
+       {"tspace", PP_tspace},
+       {"usage",  PP_usage}
+      };
+   int i, n, numopts = sizeof(plopts)/sizeof(struct polopts);
+
+// Get the first token
+//
+   if (!(val = cFile->GetWord()))
+      {Say.Emsg("Config", "policy program not specified"); return 1;}
+   pVecNum = 0;
+
+// Pick up the remining options
+//
+   while(val && *val != '|')
+        {for (i = 0; i < numopts; i++) if (!strcmp(val,plopts[i].opname)) break;
+         if (i >= numopts)
+            {Say.Say("Config warning: ignoring invalid policy option '",val,"'.");
+             val = cFile->GetWord();
+             continue;
+            }
+         if (pVecNum >= pVecMax)
+            {Say.Emsg("Config", "To many policy program variables specified.");
+             return 1;
+            }
+         pVec[pVecNum++] = static_cast<char>(plopts[i].opval);
+         val = cFile->GetWord();
+        }
+
+// Pick up the program
+//
+   if (val) val++;
+   if (val && !(*val)) val = cFile->GetWord();
+   if (!val)
+      {Say.Emsg("Config", "policy program not specified."); return 1;}
+   i = strlen(val);
+   if (i >= (int)sizeof(pBuff)-8)
+      {Say.Emsg("Config", "policy program name is too long."); return 1;}
+   strcpy(pBuff, val); pbP = pBuff+i; *(pbP+1) = '\0';
+
+// Now get any optional arguments
+//
+   n = sizeof(pBuff) - i - 1;
+   if (!cFile->GetRest(pbP+1, n))
+      {Say.Emsg("Config", "policy program args are too long."); return 1;}
+   if (*(pbP+1)) *pbP = ' ';
+
+// Record the program
+//
+   if (pProg) free(pProg);
+   pProg = strdup(pBuff);
+   return 0;
+}
+
 /******************************************************************************/
 /* Private:                         x w t m                                   */
 /******************************************************************************/
@@ -1084,12 +1560,12 @@ int XrdFrmConfig::xnml()
    Output: 0 upon success or !0 upon failure.
 */
 int XrdFrmConfig::xwtm()
-{   int wscan = 0;
+{   int wscan = 0, wtime = (Test ? 1 : 30);
     char *val;
 
     if (!(val = cFile->GetWord()))
        {Say.Emsg("Config", "wait time not specified"); return 1;}
-    if (XrdOuca2x::a2tm(Say, "wait time", val, &wscan, 30)) return 1;
+    if (XrdOuca2x::a2tm(Say, "wait time", val, &wscan, wtime)) return 1;
     WaitTime = wscan;
     return 0;
 }
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh
index 28c059c3ad8df7c50659937618f8517106d25fd5..681a6c48b8509277f47ac0ea392ef3dde84d9196 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh
@@ -15,6 +15,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "XrdOss/XrdOssSpace.hh"
+
 class XrdCmsNotify;
 class XrdOss;
 class XrdOucMsubs;
@@ -50,23 +52,56 @@ XrdOss             *ossFS;
 XrdCmsNotify       *cmsPath;
 uid_t               myUid;
 gid_t               myGid;
+long long           cmdFree;
+int                 cmdHold;
 int                 AdminMode;
 int                 isAgent;
 int                 xfrMax;
 int                 WaitTime;
 int                 monStage;
 int                 sSpec;
+int                 isOTO;
+int                 Fix;
+int                 Test;
+int                 Verbose;
+char              **vectArg;
 int                 nextArg;
-int                 Solitary;
+int                 numcArg;
 
 struct VPInfo
       {VPInfo      *Next;
        char        *Name;
        XrdOucTList *Dir;
-                    VPInfo(char *n, struct VPInfo *p=0)
-                          : Next(p), Name(strdup(n)), Dir(0) {}
+       int          Val;
+                    VPInfo(char *n, int m=0, struct VPInfo *p=0)
+                          : Next(p), Name(strdup(n)), Dir(0), Val(m) {}
                    ~VPInfo() {} // Deletes are not important
       }            *VPList;
+VPInfo             *pathList;   // Migr/Purg list of paths
+XrdOucTList        *spacList;   // Migr/Purg list of spaces
+
+struct Policy
+      {long long minFree;
+       long long maxFree;
+       int       Hold;
+       int       Ext;
+       Policy   *Next;
+       char      Sname[XrdOssSpace::minSNbsz];
+                 Policy(const char *snv, long long minV, long long maxV,
+                        int hV, int xV) : minFree(minV), maxFree(maxV),
+                        Hold(hV), Ext(xV), Next(0) {strcpy(Sname, snv);}
+                ~Policy() {}
+      };
+Policy           dfltPolicy;
+
+int              dirHold;
+int              pVecNum;     // Number of policy variables
+static const int pVecMax=8;
+char             pVec[pVecMax];
+char            *pProg;
+
+enum  PPVar {PP_atime=0, PP_ctime, PP_fname, PP_fsize, PP_fspace,
+             PP_mtime,   PP_pfn,   PP_sname, PP_tspace, PP_usage};
 
 int   Configure(int argc, char **argv, int (*ppf)());
 
@@ -85,18 +120,26 @@ private:
 XrdOucMsubs *ConfigCmd(const char *cname, char *cdata);
 int          ConfigMum(XrdFrmConfigSE &theSE);
 int          ConfigN2N();
+int          ConfigMP(const char *);
 int          ConfigMss();
+int          ConfigOTO(char *Parms);
 int          ConfigPaths();
 int          ConfigProc();
 int          ConfigXeq(char *var, int mbok);
+int          getTime(const char *, const char *, int *, int mnv=-1, int mxv=-1);
 int          Grab(const char *var, char **Dest, int nosubs);
+XrdOucTList *InsertPL(XrdOucTList *pP, const char *Path, int Plen, int isRW);
+void         InsertXD(const char *Path);
 void         Usage(int rc);
 int          xapath();
-int          xcache();
+int          xcache(int isPrg=0);
 void         xcacheBuild(char *grp, char *fn, int isxa);
+int          xdpol();
 int          xmaxx();
 int          xnml();
 int          xmon();
+int          xpol();
+int          xpolprog();
 int          xwtm();
 
 char               *ConfigFN;
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc
index b79d120dff96802c60cf8e7668f6c32f7d6ed997..a987e85cea676456d6e1ced6b0745a959663cfbc 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc
@@ -10,28 +10,265 @@
   
 //          $Id$
 
+#include <errno.h>
 #include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include "XrdFrm/XrdFrmConfig.hh"
 #include "XrdFrm/XrdFrmFiles.hh"
 #include "XrdFrm/XrdFrmTrace.hh"
+#include "XrdOuc/XrdOucTList.hh"
+#include "XrdSys/XrdSysPlatform.hh"
 
 const char *XrdFrmFilesCVSID = "$Id$";
 
 using namespace XrdFrm;
 
+/******************************************************************************/
+/*                   C l a s s   X r d F r m F i l e s e t                    */
+/******************************************************************************/
+/******************************************************************************/
+/*                            D e s t r u c t o r                             */
+/******************************************************************************/
+  
+XrdFrmFileset::~XrdFrmFileset()
+{
+   int i;
+
+// Unlock any locked files
+//
+   UnLock();
+
+// Delete all the table entries
+//
+   for (i = 0; i < XrdOssPath::sfxNum; i++) if(File[i]) delete File[i];
+
+// If there is a shared directory buffer, decrease reference count, delete if 0
+//
+   if (dInfo)
+      {dInfo->ival[dRef]--;
+       if (!dInfo) delete dInfo;
+      }
+}
+
+/******************************************************************************/
+/*                               d i r P a t h                                */
+/******************************************************************************/
+  
+int XrdFrmFileset::dirPath(char *dBuff, int dBlen)
+{
+   char *dP = 0;
+   int   dN = 0, i;
+
+// If we have a shared directory pointer, use that as directory information
+// Otherwise, get it from one of the files in the fileset.
+//
+   if (dInfo) {dP = dInfo->text; dN = dInfo->ival[dLen];}
+      else {for (i = 0; i < XrdOssPath::sfxNum; i++)
+                if (File[i])
+                   {dP = File[i]->Path;
+                    dN = File[i]->File - File[i]->Path;
+                    break;
+                   }
+           }
+
+// Copy out the directory path
+//
+   if (dBlen > dN && dP) strncpy(dBuff, dP, dN);
+      else dN = 0;
+   *(dBuff+dN) = '\0';
+   return dN;
+}
+
+/******************************************************************************/
+/*                               R e f r e s h                                */
+/******************************************************************************/
+  
+int XrdFrmFileset::Refresh(int isMig, int doLock)
+{
+   XrdOucNSWalk::NSEnt *bP = baseFile(), *lP = lockFile();
+   char pBuff[MAXPATHLEN+1], *fnP, *pnP = pBuff;
+   int n;
+
+// Get the directory path for this entry
+//
+   if (!(n = dirPath(pBuff, sizeof(pBuff)-1))) return 0;
+   fnP = pBuff+n;
+
+// If we need to lock the entry, do so. We also check if file is in use
+//
+   if (doLock && bP)
+      {strcpy(fnP, baseFile()->File);
+       if (chkLock(pBuff)) return 0;
+       if (lP && dlkFD < 0)
+          {if ((dlkFD = getLock(pBuff)) < 0) return 0;
+           strcpy(fnP, lockFile()->File);
+           if ((flkFD = getLock(pBuff,0,1)) < 0)
+              {close(dlkFD); dlkFD = -1; return 0;}
+          }
+      }
+
+// Do a new stat call on each relevant file (pin file excluded for isMig)
+//
+   if (bP)
+      {if (bP->Link) pnP = bP->Link;
+         else strcpy(fnP, bP->File);
+       if (stat(pnP, &(bP->Stat)))
+          {Say.Emsg("Refresh", errno, "stat", pnP); UnLock(); return 0;}
+      }
+
+   if (lP)
+      {strcpy(fnP, lP->File);
+       if (stat(pBuff, &(lP->Stat)))
+          {Say.Emsg("Refresh", errno, "stat", pBuff); UnLock(); return 0;}
+      }
+
+   if (!isMig && (bP = pinFile()))
+      {strcpy(fnP, bP->File);
+       if (stat(pBuff, &(bP->Stat)) && errno == ENOENT)
+          {delete bP; File[XrdOssPath::isPin] = 0;}
+      }
+
+// All done
+//
+   return 1;
+}
+
+/******************************************************************************/
+/*                                U n L o c k                                 */
+/******************************************************************************/
+  
+void XrdFrmFileset::UnLock()
+{
+   if (flkFD >= 0) {close(flkFD); flkFD = -1;}
+   if (dlkFD >= 0) {close(dlkFD); dlkFD = -1;}
+}
+
+/******************************************************************************/
+/*                       P r i v a t e   M e t h o d s                        */
+/******************************************************************************/
+/******************************************************************************/
+/*                               c h k L o c k                                */
+/******************************************************************************/
+
+// Returns 0 if no lock exists o/w returns 1 or -1;
+  
+int XrdFrmFileset::chkLock(const char *Path)
+{
+   FLOCK_t lock_args;
+   int rc, lokFD;
+
+// Open the file appropriately
+//
+   if ((lokFD = open(Path, O_RDONLY)) < 0)
+      {Say.Emsg("chkLock", errno, "open", Path); return -1;}
+
+// Initialize the lock arguments
+//
+   bzero(&lock_args, sizeof(lock_args));
+   lock_args.l_type = F_WRLCK;
+
+// Now check if the lock can be obtained
+//
+   do {rc = fcntl(lokFD, F_GETLK, &lock_args);} while(rc < 0 && errno == EINTR);
+
+// Determine the result
+//
+   if (rc) Say.Emsg("chkLock", errno, "lock", Path);
+      else rc = (lock_args.l_type == F_UNLCK ? 0 : 1);
+
+// All done
+//
+   close(lokFD);
+   return rc;
+}
+
+/******************************************************************************/
+/*                               g e t L o c k                                */
+/******************************************************************************/
+  
+int XrdFrmFileset::getLock(char *Path, int Shared, int noWait)
+{
+   static const int AMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+   FLOCK_t lock_args;
+   int oFlags = (Shared ? O_RDONLY : O_RDWR|O_CREAT);
+   int rc, lokFD, Act;
+
+// Open the file appropriately
+//
+   if ((lokFD = open(Path, oFlags, AMode)) < 0)
+      {Say.Emsg("getLock", errno, "open", Path); return -1;}
+
+// Initialize the lock arguments
+//
+   bzero(&lock_args, sizeof(lock_args));
+   lock_args.l_type = (Shared ? F_RDLCK : F_WRLCK);
+   Act = (noWait ? F_SETLK : F_SETLKW);
+
+// Now obtain the lock
+//
+   do {rc = fcntl(lokFD, Act, &lock_args);} while(rc < 0 && errno == EINTR);
+
+// Determine the result
+//
+   if (rc)
+      {if (errno != EAGAIN) Say.Emsg("getLock", errno, "lock", Path);
+       close(lokFD); return -1;
+      }
+
+// All done
+//
+   return lokFD;
+}
+
+/******************************************************************************/
+/*                                  M k f n                                   */
+/******************************************************************************/
+  
+const char *XrdFrmFileset::Mkfn(XrdOucNSWalk::NSEnt *fP)
+{
+
+// If we have no file for this, return the null string
+//
+   if (!fP) return "";
+
+// If we have no shared directory pointer, return the full path
+//
+   if (!dInfo) return fP->Path;
+
+// Construct the name in a non-renterant way (this is documented)
+//
+   strcpy(dInfo->text+dInfo->ival[dLen], fP->File);
+   return dInfo->text;
+}
+  
+/******************************************************************************/
+/*                     C l a s s   X r d F r m F i l e s                      */
+/******************************************************************************/
 /******************************************************************************/
 /*                           C o n s t r u c t o r                            */
 /******************************************************************************/
   
-XrdFrmFiles::XrdFrmFiles(const char *dname, int opts)
+XrdFrmFiles::XrdFrmFiles(const char *dname, int opts,
+                        XrdOucTList *XList, XrdOucNSWalk::CallBack *cbP)
             : nsObj(&Say, dname, Config.lockFN,
                     XrdOucNSWalk::retFile | XrdOucNSWalk::retLink
                    |XrdOucNSWalk::retStat | XrdOucNSWalk::skpErrs
-                   | (opts & Recursive  ?   XrdOucNSWalk::Recurse : 0)),
-              fsList(0)
-{}
+                   | (opts & CompressD  ?   XrdOucNSWalk::noPath  : 0)
+                   | (opts & Recursive  ?   XrdOucNSWalk::Recurse : 0), XList),
+              fsList(0), manMem(opts & NoAutoDel ? Hash_keep : Hash_default),
+              shareD(opts & CompressD)
+{
+
+// Set Call Back method
+//
+   nsObj.setCallBack(cbP);
+}
 
 /******************************************************************************/
 /*                                   G e t                                    */
@@ -41,19 +278,21 @@ XrdFrmFileset *XrdFrmFiles::Get(int &rc, int noBase)
 {
    XrdOucNSWalk::NSEnt *nP;
    XrdFrmFileset *fsetP;
+   const char *dPath;
 
 // Check if we have something to return
 //
 do{while ((fsetP = fsList))
          {fsList = fsetP->Next; fsetP->Next = 0;
           if (fsetP->File[XrdOssPath::isBase] || noBase) {rc = 0; return fsetP;}
+             else if (manMem) delete fsetP;
          }
 
-// Start with next directory (we return when no directories left)
+// Start with next directory (we return when no directories left).
 //
-   do {if (!(nP = nsObj.Index(rc))) return 0;
+   do {if (!(nP = nsObj.Index(rc, &dPath))) return 0;
        fsTab.Purge(); fsList = 0;
-      } while(!Process(nP));
+      } while(!Process(nP, dPath));
 
   } while(1);
 
@@ -69,13 +308,28 @@ do{while ((fsetP = fsList))
 /*                               P r o c e s s                                */
 /******************************************************************************/
   
-int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP)
+int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP, const char *dPath)
 {
    XrdOucNSWalk::NSEnt *fP;
    XrdFrmFileset       *sP;
+   XrdOucTList         *dP = 0;
    char *dotP;
    int fType;
 
+// If compressed directories wanted, then setup a shared directory buffer
+// Warning! We use a hard-coded value for maximum filename length instead of
+//          constantly calling pathconf().
+//
+   if (shareD)
+      {int n = strlen(dPath);
+       char *dBuff = (char *)malloc(n+264);
+       strcpy(dBuff, dPath);
+       dP = new XrdOucTList;
+       dP->text = dBuff;
+       dP->ival[XrdFrmFileset::dLen] = n;
+       dP->ival[XrdFrmFileset::dRef] = 0;
+      }
+
 // Process the file list
 //
    while((fP = nP))
@@ -83,7 +337,9 @@ int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP)
          if (!strcmp(fP->File, Config.lockFN)) {delete fP; continue;}
          if ((dotP = rindex(fP->File, '.'))) *dotP = '\0';
          if (!(sP = fsTab.Find(fP->File)))
-            {sP = fsList = new XrdFrmFileset(fsList); fsTab.Add(fP->File, sP);}
+            {sP = fsList = new XrdFrmFileset(fsList, dP);
+             fsTab.Add(fP->File, sP, 0, manMem);
+            }
          if (dotP) *dotP = '.';
          fType = (int)XrdOssPath::pathType(fP->File);
          sP->File[fType] = fP;
@@ -91,5 +347,7 @@ int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP)
 
 // Indicate whether we have anything here
 //
-   return fsList != 0;
+   if (fsList) return 1;
+   if (dP) delete dP;
+   return 0;
 }
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh
index d13d24683f84645d40d4dbd59eb923a944e906e5..95ae68f35c7889ca1a6ef1c1f7d2f30ad61f6847 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh
@@ -19,6 +19,8 @@
 #include "XrdOuc/XrdOucHash.hh"
 #include "XrdOuc/XrdOucNSWalk.hh"
 
+class  XrdOucTList;
+
 /******************************************************************************/
 /*                   C l a s s   X r d F r m F i l e s e t                    */
 /******************************************************************************/
@@ -28,20 +30,52 @@ class  XrdFrmFileset
 public:
 friend class XrdFrmFiles;
 
-// These are the basic set of files related to the base file. Two other file
-// suffixes are ignore for fileset purposes (".anew" and ".stage"). To take
-// ownership of the entry, simply set the pointer to zero after saving it.
+// These are inline function to return most common file information
 //
-XrdOucNSWalk::NSEnt *File[XrdOssPath::sfxNum];
+inline XrdOucNSWalk::NSEnt *baseFile() {return File[XrdOssPath::isBase];}
+const  char                *basePath() {return Mkfn(baseFile());}
+inline XrdOucNSWalk::NSEnt *failFile() {return File[XrdOssPath::isFail];}
+const  char                *failPath() {return Mkfn(failFile());}
+inline XrdOucNSWalk::NSEnt *lockFile() {return File[XrdOssPath::isLock];}
+const  char                *lockPath() {return Mkfn(lockFile());}
+inline XrdOucNSWalk::NSEnt * pfnFile() {return File[XrdOssPath::isPfn ];}
+const  char                * pfnPath() {return Mkfn(pfnFile());}
+inline XrdOucNSWalk::NSEnt * pinFile() {return File[XrdOssPath::isPin ];}
+const  char                * pinPath() {return Mkfn(pinFile());}
+
+int                         dirPath(char *dBuff, int dBlen);
+
+int                         Refresh(int isMig=0, int doLock=1);
 
-                     XrdFrmFileset(XrdFrmFileset *sP=0) : Next(sP)
+void                        UnLock();
+
+                     XrdFrmFileset(XrdFrmFileset *sP=0, XrdOucTList *diP=0)
+                                  : Next(sP), dInfo(diP),
+                                    dlkFD(-1), flkFD(-1)
                                    {memset(File, 0, sizeof(File));}
-                    ~XrdFrmFileset() {int i;
-                                      for (i = 0; i < XrdOssPath::sfxNum; i++)
-                                          if(File[i]) delete File[i];
-                                     }
-private:
+                    ~XrdFrmFileset();
+
+// The following are public to ease management of this object
+//
 XrdFrmFileset *Next;
+int            Age;
+
+private:
+int         chkLock(const char *Path);
+int         getLock(char *Path, int Shared=0, int noWait=0);
+const char *Mkfn(XrdOucNSWalk::NSEnt *fP);
+
+// These are the basic set of files related to the base file. Two other file
+// suffixes are ignore for fileset purposes (".anew" and ".stage").
+//
+XrdOucNSWalk::NSEnt *File[XrdOssPath::sfxNum];
+
+XrdOucTList         *dInfo;     // Shared directory information
+static const int     dLen = 0;  // Index to directory path length in dInfo
+static const int     dRef = 1;  // Index to the reference counter in dInfo
+
+int                  dlkFD;     // Directory lock FD
+int                  flkFD;     // Lock file lock FD
 };
 
 /******************************************************************************/
@@ -54,18 +88,23 @@ public:
 
 XrdFrmFileset *Get(int &rc, int noBase=0);
 
-static const int Recursive = 0x0001;
+static const int Recursive = 0x0001;   // List filesets recursively
+static const int CompressD = 0x0002;   // Use shared directory object (not MT)
+static const int NoAutoDel = 0x0004;   // Do not automatically delete objects
 
-            XrdFrmFiles(const char *dname, int opts=Recursive);
+            XrdFrmFiles(const char *dname, int opts=Recursive,
+                        XrdOucTList *XList=0, XrdOucNSWalk::CallBack *cbP=0);
 
            ~XrdFrmFiles() {}
 
 private:
-int  Process(XrdOucNSWalk::NSEnt *nP);
+int  Process(XrdOucNSWalk::NSEnt *nP, const char *dPath);
 
 XrdOucHash<XrdFrmFileset>fsTab;
 
 XrdOucNSWalk             nsObj;
 XrdFrmFileset           *fsList;
+XrdOucHash_Options       manMem;
+int                      shareD;
 };
 #endif
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc
index d1a4cd8864db2218b0cf98415fcd3d493e5e1a60..4385cddd6fecc9f1c9d2741aebe6f5f9937d7664 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc
@@ -316,8 +316,7 @@ const char *XrdFrmPstgXfr::Stage(XrdFrmPstgXrq *xP, int &retcode)
       {rc = 0;
        while(!stat(Config.StopFile, &buf))
             {if (!rc--)
-                {DEBUG("Stop file " <<Config.StopFile
-                       <<" exists; staging suspended.");
+                {Say.Emsg("stage",Config.StopFile,"exists; staging suspended.");
                  rc = 12;
                 }
              XrdSysTimer::Snooze(5);
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurgMain.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurgMain.cc
new file mode 100644
index 0000000000000000000000000000000000000000..85f91a88b23e0fbede4a48f04f38ce23f8d98a5c
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurgMain.cc
@@ -0,0 +1,235 @@
+/******************************************************************************/
+/*                                                                            */
+/*                     X r d F r m P u r g M a i n . c c                      */
+/*                                                                            */
+/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+
+//           $Id$
+
+const char *XrdFrmPurgMainCVSID = "$Id$";
+
+/* This is the "main" part of the frm_purge command. Syntax is:
+*/
+static const char *XrdFrmOpts  = ":c:dfhk:l:n:O:Tv";
+static const char *XrdFrmUsage =
+
+  " [-c <cfgfile>] [-d] [-f] [-k {num | sz{k|m|g}] [-l <lfile>] [-n name]"
+  " [-O free[,hold]] [-T] [-v] [<spaces>] [<paths>]\n";
+/*
+Where:
+
+   -c     The configuration file. The default is '/opt/xrootd/etc/xrootd.cf'
+
+   -d     Turns on debugging mode.
+
+   -f     Fix orphaned files (i.e., lock and pin) by removing them.
+
+   -k     Keeps num log files or no more that sz log files.
+
+   -l     Specifies location of the log file. This may also come from the
+          XrdOucLOGFILE environmental variable.
+          By default, error messages go to standard error.
+
+   -n     The instance name.
+
+   -O     Run this one time only as a command. The parms are:
+          {free% | sz{k|m|g}[,hold]
+
+   -T     Runs in test mode (no actual purge will occur).
+
+   -v     Verbose mode, typically prints each file purged and other details.
+
+   o-t-a  The one-time-args run this as a command only once. The args direct
+          the purging process. These may only be specified when -O specified.
+
+          Syntax is: [space] path | space [path]
+*/
+
+/******************************************************************************/
+/*                         i n c l u d e   f i l e s                          */
+/******************************************************************************/
+  
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <sys/param.h>
+
+#include "XrdFrm/XrdFrmConfig.hh"
+#include "XrdFrm/XrdFrmPurge.hh"
+#include "XrdFrm/XrdFrmTrace.hh"
+#include "XrdNet/XrdNetOpts.hh"
+#include "XrdNet/XrdNetSocket.hh"
+#include "XrdOuc/XrdOucUtils.hh"
+#include "XrdSys/XrdSysError.hh"
+#include "XrdSys/XrdSysHeaders.hh"
+#include "XrdSys/XrdSysLogger.hh"
+#include "XrdSys/XrdSysPthread.hh"
+#include "XrdSys/XrdSysTimer.hh"
+
+using namespace XrdFrm;
+  
+/******************************************************************************/
+/*                      G l o b a l   V a r i a b l e s                       */
+/******************************************************************************/
+
+       XrdSysLogger       XrdFrm::Logger;
+
+       XrdSysError        XrdFrm::Say(&Logger, "");
+
+       XrdOucTrace        XrdFrm::Trace(&Say);
+
+       XrdFrmConfig       XrdFrm::Config(XrdFrmConfig::ssPurg,
+                                         XrdFrmOpts, XrdFrmUsage);
+
+// The following is needed to resolve symbols for objects included from xrootd
+//
+       XrdOucTrace       *XrdXrootdTrace;
+       XrdSysError        XrdLog(&Logger, "");
+       XrdOucTrace        XrdTrace(&Say);
+
+/******************************************************************************/
+/*                     T h r e a d   I n t e r f a c e s                      */
+/******************************************************************************/
+  
+void *mainServer(void *parg)
+{
+//  int udpFD = *static_cast<int *>(parg);
+//  XrdFrmPurge::Server(udpFD);
+    return (void *)0;
+}
+
+/******************************************************************************/
+/*                                  m a i n                                   */
+/******************************************************************************/
+  
+int main(int argc, char *argv[])
+{
+   extern int mainConfig();
+   sigset_t myset;
+
+// Turn off sigpipe and host a variety of others before we start any threads
+//
+   signal(SIGPIPE, SIG_IGN);  // Solaris optimization
+   sigemptyset(&myset);
+   sigaddset(&myset, SIGPIPE);
+   sigaddset(&myset, SIGCHLD);
+   pthread_sigmask(SIG_BLOCK, &myset, NULL);
+
+// Set the default stack size here
+//
+   if (sizeof(long) > 4) XrdSysThread::setStackSize((size_t)1048576);
+      else               XrdSysThread::setStackSize((size_t)786432);
+
+// Perform configuration
+//
+   if (!Config.Configure(argc, argv, &mainConfig)) exit(4);
+
+// Fill out the dummy symbol to avoid crashes
+//
+   XrdXrootdTrace = new XrdOucTrace(&Say);
+
+// Display configuration (defered because mum might have been in effect)
+//
+   if (!Config.isOTO || Config.Verbose) XrdFrmPurge::Display();
+
+// Now simply poke the server every so often
+//
+   if (Config.isOTO) XrdFrmPurge::Purge();
+      else do {if (Config.StopFile)
+                  {int n = 0;
+                   struct stat buf;
+                   while(!stat(Config.StopFile, &buf))
+                        {if (!n--)
+                            {Say.Emsg("PurgMain", Config.StopFile,
+                                      "exists; purging suspended."); n = 12;}
+                         XrdSysTimer::Snooze(5);
+                        }
+                  }
+               XrdFrmPurge::Purge();
+               XrdSysTimer::Snooze(Config.WaitTime);
+              } while(1);
+
+// All done
+//
+   exit(0);
+}
+
+/******************************************************************************/
+/*                            m a i n C o n f i g                             */
+/******************************************************************************/
+  
+int mainConfig()
+{
+   XrdFrmConfig::Policy *pP = Config.dfltPolicy.Next;
+   XrdFrmConfig::VPInfo *vP = Config.VPList;
+   XrdNetSocket *udpSock;
+   pthread_t tid;
+   int retc, udpFD;
+
+// If test is in effect, remove the fix flag
+//
+   if (Config.Test) Config.Fix = 0;
+
+// Go through the policy list and add each policy
+//
+   while((pP = Config.dfltPolicy.Next))
+        {if (!XrdFrmPurge::Policy(pP->Sname))
+            XrdFrmPurge::Policy(pP->Sname, pP->minFree, pP->maxFree,
+                                pP->Hold,  pP->Ext);
+         Config.dfltPolicy.Next = pP->Next;
+         delete pP;
+        }
+
+// Make sure we have a public policy
+//
+   if (!XrdFrmPurge::Policy("public"))
+       XrdFrmPurge::Policy("public", Config.dfltPolicy.minFree,
+                               Config.dfltPolicy.maxFree,
+                               Config.dfltPolicy.Hold,
+                               Config.dfltPolicy.Ext);
+
+// Now add any missing policies (we need one for every space)
+//
+   while(vP)
+      {if (!XrdFrmPurge::Policy(vP->Name))
+          XrdFrmPurge::Policy(vP->Name, Config.dfltPolicy.minFree,
+                                  Config.dfltPolicy.maxFree,
+                                  Config.dfltPolicy.Hold,
+                                  Config.dfltPolicy.Ext);
+       vP = vP->Next;
+      }
+
+// Enable the appropriate spaces and over-ride config value
+//
+   if (!XrdFrmPurge::Init(Config.spacList, Config.cmdFree, Config.cmdHold))
+      return 1;
+
+// We are done if this is a one-time-only call
+//
+   if (Config.isOTO) return 0;
+
+// Get a UDP socket for the server
+//
+   if (!(udpSock = XrdNetSocket::Create(&Say, Config.AdminPath,
+                   "purg.udp", Config.AdminMode, XRDNET_UDPSOCKET))) return 1;
+      else {udpFD = udpSock->Detach(); delete udpSock;}
+
+// Start the Server thread
+//
+   if ((retc = XrdSysThread::Run(&tid, mainServer, (void *)&udpFD,
+                                  XRDSYSTHREAD_BIND, "Server")))
+      {Say.Emsg("main", retc, "create server thread"); return 1;}
+
+// All done
+//
+   return 0;
+}
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3d4f13b090f937625987ddf2570484e369118856
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.cc
@@ -0,0 +1,927 @@
+/******************************************************************************/
+/*                                                                            */
+/*                        X r d F r m P u r g e . c c                         */
+/*                                                                            */
+/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+  
+//         $Id$
+
+const char *XrdFrmPurgeCVSID = "$Id$";
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <utime.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include "XrdCms/XrdCmsNotify.hh"
+#include "XrdOss/XrdOss.hh"
+#include "XrdOss/XrdOssPath.hh"
+#include "XrdOuc/XrdOucNSWalk.hh"
+#include "XrdOuc/XrdOucTList.hh"
+#include "XrdOuc/XrdOucProg.hh"
+#include "XrdOuc/XrdOucStream.hh"
+#include "XrdOuc/XrdOucUtils.hh"
+#include "XrdFrm/XrdFrmFiles.hh"
+#include "XrdFrm/XrdFrmConfig.hh"
+#include "XrdFrm/XrdFrmPurge.hh"
+#include "XrdFrm/XrdFrmTrace.hh"
+#include "XrdSys/XrdSysPlatform.hh"
+
+using namespace XrdFrm;
+
+/******************************************************************************/
+/*                         L o c a l   C l a s s e s                          */
+/******************************************************************************/
+/******************************************************************************/
+/*                  C l a s s   X r d F r m P u r g e D i r                   */
+/******************************************************************************/
+  
+class XrdFrmPurgeDir : XrdOucNSWalk::CallBack
+{
+public:
+
+void isEmpty(struct stat *dStat, const char *dPath, const char *lkFN);
+
+void Reset(time_t dExp)
+          {expDirTime = dExp; lowDirTime = 0; numRMD = numEMD = 0;}
+
+time_t expDirTime;
+time_t lowDirTime;
+int    numRMD;
+int    numEMD;
+
+     XrdFrmPurgeDir() {}
+    ~XrdFrmPurgeDir() {}
+};
+  
+/******************************************************************************/
+/*                               i s E m p t y                                */
+/******************************************************************************/
+
+void XrdFrmPurgeDir::isEmpty(struct stat *dStat, const char *dPath,
+                             const char *lkFN)
+{
+   static const int ossOpts = XRDOSS_isPFN | XRDOSS_resonly;
+   static const char *What = (Config.Test ? "Zorch  " : "Purged ");
+   struct stat pStat;
+   struct utimbuf times;
+   char Parent[MAXPATHLEN+1], *Slash;
+   int  n, rc;
+
+// Check if this directory is still considered active
+//
+   numEMD++;
+   if (dStat->st_mtime > expDirTime)
+      {if (!lowDirTime || lowDirTime > dStat->st_mtime)
+          lowDirTime = dStat->st_mtime;
+       return;
+      }
+
+// We can expire the directory. However, we need to get the parent mtime
+// because removing this directory should not change the parent's mtime.
+//
+   strcpy(Parent, dPath);
+   n = strlen(Parent);
+   if (Parent[n-1] == '/') Parent[--n] = '\0';
+   if ((Slash = rindex(Parent, '/')))
+      {*Slash = '\0';
+       if (stat(Parent, &pStat)) Slash = 0;
+      }
+
+// Delete the directory
+//
+   if (Config.Test) rc = 0;
+      else if (!(rc = Config.ossFS->Remdir(dPath, ossOpts)) && Slash)
+              {times.actime  = pStat.st_atime;
+               times.modtime = pStat.st_mtime;
+               utime(Parent, &times);
+              }
+
+// Report if successful
+//
+   if (!rc)
+      {numRMD++;
+       if (Config.Verbose)
+          {char sbuff[32];
+           struct tm tNow;
+           localtime_r(&(dStat->st_mtime), &tNow);
+           sprintf(sbuff, "%02d%02d%02d %02d:%02d:%02d ",
+                          tNow.tm_year-100, tNow.tm_mon+1, tNow.tm_mday,
+                          tNow.tm_hour,     tNow.tm_min,   tNow.tm_sec);
+           Say.Say(What, "empty dir ", sbuff, dPath);
+          }
+      }
+}
+
+/******************************************************************************/
+/*                     C l a s s   X r d F r m P u r g e                      */
+/******************************************************************************/
+/******************************************************************************/
+/*                        S t a t i c   M e m b e r s                         */
+/******************************************************************************/
+  
+XrdFrmPurge      *XrdFrmPurge::First     = 0;
+XrdFrmPurge      *XrdFrmPurge::Default   = 0;
+
+XrdOucProg       *XrdFrmPurge::PolProg   = 0;
+XrdOucStream     *XrdFrmPurge::PolStream = 0;
+
+int               XrdFrmPurge::Left2Do   = 0;
+
+time_t            XrdFrmPurge::lastReset = 0;
+time_t            XrdFrmPurge::nextReset = 0;
+
+XrdOucHash<char>  XrdFrmPurge::BadFiles;
+
+/******************************************************************************/
+/*                           C o n s t r u c t o r                            */
+/******************************************************************************/
+
+XrdFrmPurge::XrdFrmPurge(const char *snp, XrdFrmPurge *spp) : FSTab(1)
+{
+   strncpy(SName, snp, sizeof(SName)-1); SName[sizeof(SName)-1] = '\0';
+   Next = spp;
+   freeSpace = 0;
+   usedSpace =-1;
+   pmaxSpace = 0;
+   totlSpace = 0;
+   contSpace = 0;
+   minFSpace = 0;
+   maxFSpace = 0;
+   Enabled   = 0;
+   Stop      = 0;
+   SNlen     = strlen(SName);
+   Clear();
+}
+  
+/******************************************************************************/
+/* Private:                          A d d                                    */
+/******************************************************************************/
+  
+void XrdFrmPurge::Add(XrdFrmFileset *sP)
+{
+   EPNAME("Add");
+   XrdOucNSWalk::NSEnt *baseFile = sP->baseFile();
+   XrdFrmPurge *psP = Default;
+   const char *Why;
+   time_t xTime;
+
+// First, get the space name associated with the base file
+//
+   if ((baseFile->Link))
+      {char snBuff[XrdOssSpace::minSNbsz];
+       XrdOssPath::getCname(0, snBuff, baseFile->Link, baseFile->Lksz);
+       if (!(psP = Find(snBuff))) psP = Default;
+      }
+
+// Ignore the file is the space is not enabled for purging
+//
+   if (!(psP->Enabled)) {delete sP; return;}
+   psP->numFiles++;
+
+// Check to see if the file is really eligible for purging
+//
+   if ((Why = psP->Eligible(sP, xTime)))
+      {DEBUG(sP->basePath() <<"cannot be purged; " <<Why);
+       delete sP;
+       return;
+      }
+
+// Add the file to the purge table or the defer queue based on access time
+//
+   if (xTime >= psP->Hold) psP->FSTab.Add(sP);
+      else psP->Defer(sP, xTime);
+}
+  
+/******************************************************************************/
+/* Private:                      A d v a n c e                                */
+/******************************************************************************/
+
+XrdFrmFileset *XrdFrmPurge::Advance()
+{
+   XrdFrmFileset *fP, *xP;
+   int n;
+
+// Find a defer queue entry that meets the hold threshold
+//
+   for (n = DeferQsz-1; n >= 0 && !DeferQ[n]; n--) {}
+   if (n < 0) return 0;
+   if (time(0) - DeferT[n] > Hold) return 0;
+   fP = DeferQ[n]; DeferQ[n] = 0; DeferT[n] = 0;
+
+// Try to re-add everything in this queue
+//
+   while((xP = fP))
+        {fP = fP->Next;
+         if (xP->Refresh(0,0)) Add(xP);
+            else delete xP;
+        }
+
+// Return based on whether something now exists in the purge table
+//
+   return FSTab.Oldest();
+}
+  
+/******************************************************************************/
+/* Private:                        C l e a r                                  */
+/******************************************************************************/
+  
+void XrdFrmPurge::Clear()
+{
+   XrdFrmFileset *fP;
+   int n;
+
+// Zero out the defer queue
+//
+   for (n = 0; n < DeferQsz; n++)
+       while ((fP = DeferQ[n])) {DeferQ[n] = fP->Next; delete fP;}
+   memset(DeferT, 0, sizeof(DeferT));
+
+// Purge the eligible file table
+//
+   FSTab.Purge();
+
+// Clear counters
+//
+   numFiles = 0; prgFiles = 0; purgBytes = 0;
+}
+  
+/******************************************************************************/
+/* Private:                        D e f e r                                  */
+/******************************************************************************/
+
+void XrdFrmPurge::Defer(XrdFrmFileset *sP, time_t xTime)
+{
+   time_t aTime = sP->baseFile()->Stat.st_atime;
+   int n = xTime/DeferQsz;
+
+// Slot the entry into the defer queue vector
+//
+   if (!DeferQ[n] || aTime < DeferT[n]) DeferT[n] = aTime;
+   sP->Next = DeferQ[n];
+   DeferQ[n] = sP;
+}
+
+/******************************************************************************/
+/*                               D i s p l a y                                */
+/******************************************************************************/
+
+void XrdFrmPurge::Display()
+{
+   XrdFrmConfig::VPInfo *vP = Config.pathList;
+   XrdFrmPurge *spP = First;
+   XrdOucTList *tP;
+   const char *isExt;
+   char buff[1024], minfsp[32], maxfsp[32];
+
+// Type header
+//
+   Say.Say("=====> ", "Purge configuration:");
+
+// Display what we will scan
+//
+   while(vP)
+        {Say.Say("=====> ", "Scanning ", (vP->Val?"r/w: ":"r/o: "), vP->Name);
+         tP = vP->Dir;
+         while(tP) {Say.Say("=====> ", "Excluded ", tP->text); tP = tP->next;}
+         vP = vP->Next;
+        }
+
+// Display directory hold value
+//
+   if (Config.dirHold < 0) strcpy(buff, "forever");
+      else sprintf(buff, "%d", Config.dirHold);
+   Say.Say("=====> ", "Directory hold: ", buff);
+
+// Run through all of the policies, displaying each one
+//
+   spP = First;
+   while(spP)
+        {if (spP->Enabled)
+            {XrdOucUtils::fmtBytes(spP->minFSpace, minfsp, sizeof(minfsp));
+             XrdOucUtils::fmtBytes(spP->maxFSpace, maxfsp, sizeof(maxfsp));
+         isExt = spP->Ext ? " polprog" : "";
+             sprintf(buff, "policy %s min %s max %s free; hold: %d%s",
+                     spP->SName, minfsp, maxfsp, spP->Hold, isExt);
+            } else sprintf(buff, "policy %s nopurge", spP->SName);
+         Say.Say("=====> ", buff);
+         spP = spP->Next;
+        }
+}
+  
+/******************************************************************************/
+/* Private:                     E l i g i b l e                               */
+/******************************************************************************/
+  
+const char *XrdFrmPurge::Eligible(XrdFrmFileset *sP, time_t &xTime, int hTime)
+{
+   XrdOucNSWalk::NSEnt *baseFile = sP->baseFile();
+   XrdOucNSWalk::NSEnt *lockFile = sP->lockFile();
+   XrdOucNSWalk::NSEnt * pinFile;
+   time_t aTime, mTime, nowTime = time(0);
+   char pBuff[256];
+   int pFD, rLen, idleMin;
+
+// Get the acess time and modification time
+//
+   aTime = baseFile->Stat.st_atime;
+   mTime = baseFile->Stat.st_mtime;
+
+// File is not eligible if it's been accessed too recently
+//
+   xTime = static_cast<int>(nowTime - aTime);
+   if (hTime && xTime <= hTime) return "is in hold";
+
+// File is ineligible if ot has a fail file
+//
+   if (sP->failFile()) return "has fail file";
+
+// If there is a lock file and the file has not migrated, then ineligible
+// Note that entries were pre-screened for lock file requirements.
+//
+   if (lockFile && lockFile->Stat.st_mtime < mTime) return "not migrated";
+
+// If there is no pin file, then it is eligible subject to external policy
+//
+   if (!(pinFile = sP->pinFile())) return 0;
+
+// If the pin file sticky bit is on then the pin is permanent (ineligible)
+//
+   if (pinFile->Stat.st_mode & S_ISUID) return "is perm pinned";
+
+// If the mtime of the pin file is in the future this is when it gets unpinned
+//
+   if (pinFile->Stat.st_mtime > nowTime) return "is time pinned";
+
+// Check if there are more conditions
+//
+   if (!(pinFile->Stat.st_size)) return 0;
+   if (  pinFile->Stat.st_size >= static_cast<off_t>(sizeof(pBuff)))
+      return "is pinned incorrectly";
+
+// The contents of the file indicate how long the inactive period is, get it
+//
+   if ((pFD  = open(pinFile->Path, O_RDONLY)) < 0
+   ||  (rLen = read(pFD, pBuff, sizeof(pBuff)-1)) < 0)
+      {const char *Why = "open";
+       if (pFD >= 0) {close(pFD); Why = "read";}
+       Say.Emsg("Eligible", errno, Why, pinFile->Path);
+       return "is pinned";
+      }
+
+// Get the inactivity period
+//
+   close(pFD);
+   pBuff[rLen] = '\0';
+   if (sscanf(pBuff,"&inact_time=%d",&idleMin) != 1) return "pinfile error";
+   if (idleMin > xTime) return "is pin defered";
+   return 0;
+}
+
+/******************************************************************************/
+/* Private:                         F i n d                                   */
+/******************************************************************************/
+
+XrdFrmPurge *XrdFrmPurge::Find(const char *snp)
+{
+   XrdFrmPurge *spP = First;
+  
+// See if we already have this space defined
+//
+   while(spP && strcmp(snp, spP->SName)) spP = spP->Next;
+   return spP;
+}
+
+/******************************************************************************/
+/*                                  I n i t                                   */
+/******************************************************************************/
+
+int XrdFrmPurge::Init(XrdOucTList *sP, long long minV, int hVal)
+{
+   static char pVec[] = {char(XrdFrmConfig::PP_sname),
+                         char(XrdFrmConfig::PP_pfn),
+                         char(XrdFrmConfig::PP_fsize),
+                         char(XrdFrmConfig::PP_atime),
+                         char(XrdFrmConfig::PP_mtime)
+                        };
+
+   XrdFrmConfig::VPInfo *vP;
+   XrdOssVSInfo vsInfo;
+   XrdFrmPurge *xP, *ppP = 0, *spP = First;
+   XrdOucTList  *tP;
+   char xBuff[32];
+   int setIt, rc, haveExt = 0;
+
+// The first step is to remove any defined policies for which there is no space
+//
+   while(spP)
+        {vP = Config.VPList;
+         while(vP && strcmp(spP->SName, vP->Name)) vP = vP->Next;
+         if (!vP && strcmp("public", spP->SName))
+            {Say.Emsg("Init", "Purge policy", spP->SName,
+                              "deleted; space not defined.");
+             if (ppP) ppP->Next = spP->Next;
+                else  First =     spP->Next;
+             xP = spP; spP = spP->Next; delete xP;
+            } else {ppP = spP; spP = spP->Next;}
+        }
+
+// For each space enable it and optionally over-ride policy
+//
+   spP = First;
+   while(spP)
+        {setIt = 1;
+         if ((tP = sP))
+            {while(tP && strcmp(tP->text, spP->SName)) tP = tP->next;
+             if (!tP) setIt = 0;
+            }
+         if (setIt)
+            {if (minV) spP->minFSpace = spP->maxFSpace = minV;
+             if (hVal >= 0) {spP->Hold = hVal; spP->Hold2x = hVal*2;}
+             if (spP->minFSpace && spP->Hold >= 0)
+                {spP->Enabled = 1; haveExt |= spP->Ext;}
+            }
+         spP = spP->Next;
+        }
+
+// Go through each space policy getting the actual space and calculating
+// the targets based on the policy (we need to do this only once)
+//
+   spP = First; ppP = 0;
+   while(spP)
+        {if ((rc = Config.ossFS->StatVS(&vsInfo, spP->SName, 1)))
+            {Say.Emsg("Init", rc, "calculate space for", spP->SName);
+             if (ppP) ppP->Next = spP->Next;
+                else  First =     spP->Next;
+             xP = spP; spP = spP->Next; delete xP; continue;
+            }
+         spP->totlSpace = vsInfo.Total;
+         spP->spaceTLen = sprintf(xBuff, "%lld", vsInfo.Total);
+         spP->spaceTotl =  strdup(xBuff);
+         spP->pmaxSpace = vsInfo.Large;
+         spP->spaceTLep = sprintf(xBuff, "%lld", vsInfo.Large);
+         spP->spaceTotP =  strdup(xBuff);
+         if (spP->minFSpace < 0)
+            {spP->minFSpace = vsInfo.Total * XRDABS(spP->minFSpace) / 100LL;
+             spP->maxFSpace = vsInfo.Total * XRDABS(spP->maxFSpace) / 100LL;
+            } else if (vsInfo.Total < spP->minFSpace
+                   ||  vsInfo.Total < spP->maxFSpace)
+                      Say.Emsg("Init", "Warning: ", spP->SName, " min/max free "
+                               "space policy exceeds total available!");
+         ppP = spP; spP = spP->Next;
+        }
+
+// Make sure "public" still exists. While this should not happen, we check for
+// this possibility anyway.
+//
+   if (!(Default = Find("public")))
+      {Say.Emsg("Init", "Unable to start purge; no public policy found.");
+       return 0;
+      }
+
+// If a policy program is present, then we need to verify it
+//
+   if (Config.pProg && haveExt)
+      {PolProg = new XrdOucProg(&Say);
+       if (PolProg->Setup(Config.pProg) || PolProg->Start()) return 0;
+       PolStream = PolProg->getStream();
+       if (!Config.pVecNum)
+          {memcpy(Config.pVec, pVec, sizeof(pVec));
+           Config.pVecNum = sizeof(pVec);
+          }
+      }
+
+// All went well
+//
+   return 1;
+}
+  
+/******************************************************************************/
+/* Private:                   L o w O n S p a c e                             */
+/******************************************************************************/
+
+// This method *must* be called prior to Purge() and returns:
+// =0 -> Purge not needed.
+//!>0 -> Purge is  needed.
+  
+int XrdFrmPurge::LowOnSpace()
+{
+   XrdOssVSInfo VSInfo;
+   XrdFrmPurge *psP = First;
+   time_t eNow;
+
+// Recalculate free space and set initial status
+//
+   Left2Do = 0;
+   while(psP)
+        {if (psP->Enabled)
+            {if (Config.ossFS->StatVS(&VSInfo, psP->SName, 1)) psP->Stop = 1;
+                else {psP->freeSpace = VSInfo.Free;
+                      psP->contSpace = VSInfo.LFree;
+                      psP->usedSpace = VSInfo.Usage;
+                      if (psP->freeSpace >= psP->minFSpace) psP->Stop = 1;
+                         else {Left2Do++; psP->Stop = 0;}
+                     }
+            } else psP->Stop = 1;
+         psP = psP->Next;
+        }
+
+// If enough space then indicate no purge is needed
+//
+   if (!Left2Do) return 0;
+
+// Reset all policies to prepare for purging
+//
+   psP = First;
+   while(psP)
+        {psP->Clear();
+         psP = psP->Next;
+        }
+
+// We must check whether or not a full name space scan is required. This is
+// based on the last time we did one and whether or not a space needs one now.
+//
+   eNow = time(0);
+   if (eNow >= nextReset) {lastReset = eNow; nextReset = 0; Scan();}
+   return 1;
+}
+
+/******************************************************************************/
+/*                                P o l i c y                                 */
+/******************************************************************************/
+
+XrdFrmPurge *XrdFrmPurge::Policy(const char *sname, long long minV,
+                                 long long maxV, int hVal, int xVal)
+{
+   XrdFrmPurge *psP;
+  
+// Find or create a new policy
+//
+   if (!(psP = Find(sname))) First = psP = new XrdFrmPurge(sname, First);
+
+// Fill out the policy
+//
+   psP->minFSpace = minV;
+   psP->maxFSpace = maxV;
+   psP->Hold      = hVal;
+   psP->Hold2x    = hVal*2;
+   psP->Ext       = xVal;
+   return psP;
+}
+
+/******************************************************************************/
+/*                                 P u r g e                                  */
+/******************************************************************************/
+  
+void XrdFrmPurge::Purge()
+{
+   XrdFrmPurge *psP = First;
+
+// Check if are low on space, if not, ignore the call
+//
+   if (!LowOnSpace())
+      {Say.Emsg("Purge", "Purge cycle skipped; all policies met.");
+       return;
+      }
+
+// Report data at the start of the purge cycle
+//
+   Say.Emsg("Purge", "Purge cycle started.");
+   Stats(0);
+
+// Cycle through each space until we no longer can cycle
+//
+do{psP = First;
+   while(psP && Left2Do)
+        {if (!(psP->Stop) && (psP->Stop = psP->PurgeFile())) Left2Do--;
+         psP = psP->Next;
+        }
+  } while(Left2Do);
+
+// Report data at the end of the purge cycle
+//
+   Stats(1);
+   Say.Emsg("Purge", "Purge cycle ended.");
+}
+
+/******************************************************************************/
+/* Private:                    P u r g e F i l e                              */
+/******************************************************************************/
+  
+int XrdFrmPurge::PurgeFile()
+{
+   EPNAME("PurgeFile");
+   XrdFrmFileset *fP;
+   const char *fn, *Why = "file in use";
+   time_t xTime;
+   int rc, FilePurged = 0;
+
+// If we have don't have a file, see if we can grab some from the defer queue
+//
+do{if (!(fP = FSTab.Oldest()) && !(fP = Advance()))
+      {time_t nextScan = time(0)+Hold;
+       if (!nextReset || nextScan < nextReset) nextReset = nextScan;
+       return 1;
+      }
+   if (fP->Refresh() && !(Why = Eligible(fP, xTime, Hold))
+   && (!Ext || !(Why = XPolOK(fP))))
+      {fn = fP->basePath();
+       if (Config.Test) rc = 0;
+          else if (!(rc = Config.ossFS->Unlink(fn, XRDOSS_isPFN)))
+                  Config.cmsPath->Gone(fn);
+       if (!rc) {prgFiles++; FilePurged = 1;
+                 freeSpace += fP->baseFile()->Stat.st_size;
+                 purgBytes += fP->baseFile()->Stat.st_size;
+                 if (Config.Verbose) Track(fP);
+                }
+      } else {DEBUG("Purge " <<SName <<": keeping " <<fP->basePath() <<"; " <<Why);}
+   delete fP;
+  } while(!FilePurged && !Stop);
+
+// All done, indicate whether we should stop now
+//
+   return freeSpace >= maxFSpace || Stop;
+}
+
+/******************************************************************************/
+/* Private:                       R e m f i x                                 */
+/******************************************************************************/
+
+void XrdFrmPurge::Remfix(const char *Ftype, const char *Fname)
+{
+// Remove the offending file
+//
+   if (!Config.ossFS->Unlink(Fname,XRDOSS_isPFN))
+      Say.Emsg("Remfix", Ftype, "file orphan fixed; removed", Fname);
+}
+  
+/******************************************************************************/
+/* Private:                         S c a n                                   */
+/******************************************************************************/
+  
+  
+void XrdFrmPurge::Scan()
+{
+   static const int Opts = XrdFrmFiles::Recursive | XrdFrmFiles::CompressD
+                         | XrdFrmFiles::NoAutoDel;
+   static time_t lastHP = time(0), nextDP = 0, nowT = time(0);
+   static XrdFrmPurgeDir purgeDir;
+   static XrdOucNSWalk::CallBack *cbP;
+
+   XrdFrmConfig::VPInfo *vP = Config.pathList;
+   XrdFrmFileset *sP;
+   XrdFrmFiles   *fP;
+   const char *Extra;
+   char buff[128];
+   int isRW, ec = 0, Bad = 0, aFiles = 0, bFiles = 0;
+
+// Purge that bad file table evey 24 hours to keep complaints down
+//
+   if (nowT - lastHP >= 86400) {BadFiles.Purge(); lastHP = nowT;}
+
+// Determine if we need to do an empty directory trim
+//
+   if (Config.dirHold < 0 || nowT < nextDP) {cbP = 0; Extra = 0;}
+      else {nextDP = nowT + Config.dirHold;
+            purgeDir.Reset(nowT - Config.dirHold);
+            cbP = (XrdOucNSWalk::CallBack *)&purgeDir;
+            Extra = "and empty directory";
+           }
+
+// Indicate scan started
+//
+   VMSG("Scan", "Name space", Extra, "scan started. . .");
+
+// Process each directory
+//
+   do {fP = new XrdFrmFiles(vP->Name, Opts, vP->Dir, cbP);
+       isRW = vP->Val;
+       while((sP = fP->Get(ec,1)))
+            {aFiles++;
+             if (Screen(sP, isRW)) Add(sP);
+                else {delete sP; bFiles++;}
+            }
+       if (ec) Bad = 1;
+       delete fP;
+      } while((vP = vP->Next));
+
+// If we did a directory purge, schedule the next one and say what we did
+//
+   if (cbP)
+      {if ((purgeDir.numEMD - purgeDir.numRMD) > 0
+       &&   purgeDir.lowDirTime + Config.dirHold < nextDP)
+          nextDP = purgeDir.lowDirTime + Config.dirHold;
+       sprintf(buff, "%d of %d empty dir%s removed", purgeDir.numRMD,
+                     purgeDir.numEMD, (purgeDir.numEMD != 1 ? "s":""));
+       VMSG("Scan", "Empty directory space scan ended;", buff);
+      }
+
+// Indicate scan ended
+//
+   sprintf(buff, "%d file%s with %d error%s", aFiles, (aFiles != 1 ? "s":""),
+                                              bFiles, (bFiles != 1 ? "s":""));
+   VMSG("Scan", "Name space scan ended;", buff);
+
+// Issue warning if we encountered errors
+//
+   if (Bad) Say.Emsg("Scan", "Errors encountered while scanning for "
+                             "purgeable files.");
+}
+
+/******************************************************************************/
+/* Private:                       S c r e e n                                 */
+/******************************************************************************/
+
+int XrdFrmPurge::Screen(XrdFrmFileset *sP, int isRW)
+{
+   const char *What = 0, *badFN = 0;
+   char dPath[MAXPATHLEN+1];
+
+// Verify that we have all the relevant files
+//
+   if (!(sP->baseFile()))
+      {if (Config.Fix)
+          {if (sP->lockFile()) Remfix("Lock", sP->lockPath());
+           if (sP-> pinFile()) Remfix("Pin",  sP-> pinPath());
+           if (sP->failFile()) Remfix("Fail", sP->failPath());
+           return 0;
+          }
+       What = "No base file for";
+            if (sP->lockFile()) badFN = sP->lockPath();
+       else if (sP-> pinFile()) badFN = sP-> pinPath();
+       else if (sP->failFile()) badFN = sP->failPath();
+       else {What = "Orhpaned files in"; badFN = dPath;
+             sP->dirPath(dPath, sizeof(dPath));
+            }
+      } else if (isRW && !(sP->lockFile()))
+                {What = "No lock file for"; badFN = sP->basePath();}
+                else return 1;
+
+// Issue message if we haven't issued one before
+//
+   if (!BadFiles.Add(badFN, 0, 0, Hash_data_is_key))
+      Say.Emsg("Screen", What, badFN);
+   return 0;
+}
+  
+/******************************************************************************/
+/* Private:                        S t a t s                                  */
+/******************************************************************************/
+  
+void XrdFrmPurge::Stats(int Final)
+{
+   XrdFrmPurge *xsP, *psP = First;
+   long long pVal, xBytes, zBytes;
+   const char *xWhat, *nWhat, *zWhat;
+   char fBuff[64], uBuff[80], sBuff[512], xBuff[64], zBuff[64];
+   int nFiles;
+
+// Report data for each enabled space
+//
+   while((xsP = psP))
+        {psP = psP->Next;
+         if (!(xsP->Enabled)) continue;
+         if (xsP->usedSpace >= 0)
+            {if (Final) xsP->usedSpace -= xsP->purgBytes;
+             XrdOucUtils::fmtBytes(xsP->usedSpace, fBuff, sizeof(fBuff));
+             pVal = xsP->usedSpace*100/xsP->totlSpace;
+             sprintf(uBuff, "used %s (%lld%%) ", fBuff, pVal);
+            } else *uBuff = '\0';
+         XrdOucUtils::fmtBytes(xsP->freeSpace, fBuff, sizeof(fBuff));
+         pVal = xsP->freeSpace*100/xsP->totlSpace;
+         if (Final)
+            {xBytes = xsP->purgBytes; xWhat = "freed";
+             if ((zBytes = xsP->maxFSpace - xsP->freeSpace) > 0)
+                {XrdOucUtils::fmtBytes(zBytes, zBuff, sizeof(zBuff));
+                 zWhat = " deficit";
+                } else {*zBuff = '\0'; zWhat = "goal met";}
+             nFiles = xsP->prgFiles;  nWhat = "prgd";
+            } else {
+             xBytes = (xsP->freeSpace < xsP->minFSpace
+                    ?  xsP->maxFSpace - xsP->freeSpace : 0);
+             nFiles = xsP->FSTab.Count(); 
+             xWhat = "goal"; nWhat = "idle"; *zBuff = '\0'; zWhat = "";
+           }
+         XrdOucUtils::fmtBytes(xBytes, xBuff, sizeof(xBuff));
+         sprintf(sBuff, " %sfree %s (%lld%%) %d files %d %s; %s %s %s%s",
+                 uBuff,fBuff,pVal,xsP->numFiles,nFiles,nWhat,
+                 xBuff,xWhat,zBuff,zWhat);
+         Say.Say("++++++ ", xsP->SName, sBuff);
+        }
+}
+
+/******************************************************************************/
+/* Private:                        T r a c k                                  */
+/******************************************************************************/
+  
+void XrdFrmPurge::Track(XrdFrmFileset *sP)
+{
+   XrdOucNSWalk::NSEnt *fP = sP->baseFile();
+   const char *What = (Config.Test ? "Zorch  " : "Purged ");
+   char sbuff[128], fszbf[16];
+   struct tm tNow;
+
+// Format the size
+//
+   XrdOucUtils::fmtBytes(static_cast<long long>(fP->Stat.st_size),
+                         fszbf, sizeof(fszbf));
+
+// Format the information and display it
+//
+   localtime_r(&(fP->Stat.st_atime), &tNow);
+   sprintf(sbuff, " %8s %02d%02d%02d %02d:%02d:%02d ", fszbf,
+                  tNow.tm_year-100, tNow.tm_mon+1, tNow.tm_mday,
+                  tNow.tm_hour,     tNow.tm_min,   tNow.tm_sec);
+
+   Say.Say(What, SName, sbuff, sP->basePath());
+}
+
+/******************************************************************************/
+/* Private:                       X P o l O K                                 */
+/******************************************************************************/
+  
+const char *XrdFrmPurge::XPolOK(XrdFrmFileset *fsP)
+{
+   static char neg1[] = {'-','1','\0'};
+   XrdOucNSWalk::NSEnt *fP = fsP->baseFile();
+   char *Data[sizeof(Config.pVec)*2+2];
+   int   Dlen[sizeof(Config.pVec)*2+2];
+   char  atBuff[32], ctBuff[32], mtBuff[32], fsBuff[32], spBuff[32], usBuff[32];
+   char *Resp;
+   int i, k = 0;
+
+// Construct the data to be sent (not mt here)
+//
+   for (i = 0; i < Config.pVecNum; i++)
+       {switch(Config.pVec[i])
+              {case XrdFrmConfig::PP_atime:
+                    Data[k] = atBuff;
+                    Dlen[k] = sprintf(atBuff, "%lld",
+                              static_cast<long long>(fP->Stat.st_atime));
+                    break;
+               case XrdFrmConfig::PP_ctime:
+                    Data[k] = ctBuff;
+                    Dlen[k] = sprintf(ctBuff, "%lld",
+                              static_cast<long long>(fP->Stat.st_ctime));
+                    break;
+               case XrdFrmConfig::PP_fname:
+                    Data[k] = fP->File;  Dlen[k] = strlen(fP->File);
+                    break;
+               case XrdFrmConfig::PP_fsize:
+                    Data[k] = fsBuff;
+                    Dlen[k] = sprintf(fsBuff, "%lld",
+                              static_cast<long long>(fP->Stat.st_size));
+                    break;
+               case XrdFrmConfig::PP_fspace:
+                    Data[k] = spBuff;
+                    Dlen[k] = sprintf(spBuff, "%lld", freeSpace);
+                    break;
+               case XrdFrmConfig::PP_mtime:
+                    Data[k] = mtBuff;
+                    Dlen[k] = sprintf(mtBuff, "%lld",
+                              static_cast<long long>(fP->Stat.st_mtime));
+                    break;
+               case XrdFrmConfig::PP_pfn:
+                    Data[k] = (char *)fsP->basePath();
+                    Dlen[k] = strlen(Data[k]);
+                    break;
+               case XrdFrmConfig::PP_sname:
+                    Data[k] = SName;     Dlen[k] = SNlen;
+                    break;
+               case XrdFrmConfig::PP_tspace:
+                    Data[k] = spaceTotl; Dlen[k] = spaceTLen;
+                    break;
+               case XrdFrmConfig::PP_usage:
+                    if (usedSpace < 0) {Data[k] = neg1; Dlen[k]=2;}
+                       else {Dlen[k] = sprintf(usBuff, "%lld",
+                                               usedSpace - purgBytes);
+                             Data[k] = usBuff;
+                            }
+                    break;
+               default: break;
+              }
+        Data[++k] = (char *)" "; Dlen[k] = 1; k++;
+       }
+
+// Now finish up the vector
+//
+   Data[k-1] = (char *)"\n"; Data[k] = 0; Dlen[k] = 0;
+
+// Feed the program this information get the response
+//
+   if (PolProg->Feed((const char **)Data, Dlen) || !(Resp=PolStream->GetLine()))
+      {Stop = 1; return "external policy failed";}
+
+// Decode the response (single line with a charcater y|n|a)
+//
+   if (*Resp == 'y') return 0;
+   if (*Resp == 'n') return "external policy reject";
+   Stop = 1;
+   return "external policy stop";
+}
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.hh
new file mode 100644
index 0000000000000000000000000000000000000000..cad3bc87a94190962fa6b193af245f5d9f9418cb
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.hh
@@ -0,0 +1,108 @@
+#ifndef __FRMPURGE__
+#define __FRMPURGE__
+/******************************************************************************/
+/*                                                                            */
+/*                        X r d F r m P u r g e . h h                         */
+/*                                                                            */
+/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+  
+//         $Id$
+
+#include <time.h>
+#include <sys/types.h>
+
+#include "XrdFrm/XrdFrmTSort.hh"
+#include "XrdOss/XrdOssSpace.hh"
+#include "XrdOuc/XrdOucHash.hh"
+
+class XrdFrmFileset;
+class XrdOucPolProg;
+class XrdOucStream;
+class XrdOucTList;
+
+class XrdFrmPurge
+{
+public:
+
+static void          Display();
+
+static int           Init(XrdOucTList *sP=0, long long minV=-1, int hVal=-1);
+
+static XrdFrmPurge  *Policy(const char *sname) {return Find(sname);}
+static XrdFrmPurge  *Policy(const char *sname, long long minV, long long maxV,
+                                               int hVal, int xVal);
+
+static void          Purge();
+
+                     XrdFrmPurge(const char *snp, XrdFrmPurge *spp=0);
+                    ~XrdFrmPurge() {Clear();}
+
+private:
+
+// Methods
+//
+static void          Add(XrdFrmFileset *fsp);
+       XrdFrmFileset*Advance();
+       void          Clear();
+       void          Defer(XrdFrmFileset *sP, time_t xTime);
+const  char         *Eligible(XrdFrmFileset *sP, time_t &xTime, int hTime=0);
+static XrdFrmPurge  *Find(const char *snp);
+static int           LowOnSpace();
+       int           PurgeFile();
+static void          Remfix(const char *Ftype, const char *Fname);
+static void          Scan();
+static int           Screen(XrdFrmFileset *sP, int isRW);
+static void          Stats(int Final);
+       void          Track(XrdFrmFileset *sP);
+const  char         *XPolOK(XrdFrmFileset *sP);
+static XrdOucProg   *PolProg;
+static XrdOucStream *PolStream;
+
+// Static Variables
+
+static XrdOucHash<char> BadFiles;
+static time_t        lastReset;
+static time_t        nextReset;
+
+static XrdFrmPurge  *First;
+static XrdFrmPurge  *Default;
+
+static int           Left2Do;
+
+// Variables local to each object
+//
+long long            freeSpace;      // Current free space
+long long            fconMaxsp;      // Current free space contiguous
+long long            usedSpace;      // Curreny used space (if supported)
+long long            pmaxSpace;      // PMax  space (computed once)
+long long            totlSpace;      // Total space (computed once)
+long long            contSpace;      // Total contg (computed once)
+long long            purgBytes;      // Purged bytes on last purge cycle
+long long            minFSpace;      // Minimum free space
+long long            maxFSpace;      // Maximum free space (what we purge to)
+char                *spaceTotl;
+char                *spaceTotP;
+int                  spaceTLen;
+int                  spaceTLep;
+int                  Hold;           // Hold value
+int                  Hold2x;         // Hold x2 (what we actually use)
+int                  Ext;            // External policy applies
+int                  numFiles;       // Total number of files
+int                  prgFiles;       // Total number of purged
+int                  Enabled;
+int                  Stop;
+int                  SNlen;
+
+XrdFrmPurge         *Next;
+XrdFrmTSort          FSTab;
+char                 SName[XrdOssSpace::minSNbsz];
+
+static const int     DeferQsz = 16;
+XrdFrmFileset       *DeferQ[DeferQsz];
+time_t               DeferT[DeferQsz];
+};
+#endif
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f77e0a1282bba0f5f54c4019d05277b22b592e1a
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.cc
@@ -0,0 +1,154 @@
+/******************************************************************************/
+/*                                                                            */
+/*                        X r d F r m T S o r t . c c                         */
+/*                                                                            */
+/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+
+//         $Id$
+
+const char *XrdFrmTSortCVSID = "$Id$";
+
+#include "XrdFrm/XrdFrmFiles.hh"
+#include "XrdFrm/XrdFrmTSort.hh"
+//#include "iostream.h"
+
+/******************************************************************************/
+/*                                   A d d                                    */
+/******************************************************************************/
+  
+int XrdFrmTSort::Add(XrdFrmFileset *fsp)
+{
+   XrdOucNSWalk::NSEnt *nsp = fsp->baseFile();
+   int n;
+
+// Make sure we can actuall add this entry
+//
+   if (baseT < nsp->Stat.st_atime) return 0;
+
+// Get the relative time
+//
+   fsp->Age = static_cast<int>(baseT - nsp->Stat.st_atime);
+
+// Insert into the table
+//
+   n = fsp->Age/dVal;
+   if (n > 63) n = 63;
+   fsp->Next = FSTab[0][n];
+   FSTab[0][n] = fsp;
+   if (n > DYent) DYent = n;
+//cerr <<"Add " <<std::hex <<fsp->Age <<' ' <<std::dec <<0 <<',' <<n <<' ' <<fsp->basePath() <<endl;
+   numEnt++;
+   return 1;
+}
+
+/******************************************************************************/
+/* Private:                          B i n                                    */
+/******************************************************************************/
+
+int XrdFrmTSort::Bin(XrdFrmFileset *fsp, int j, int Shift)
+{
+   XrdFrmFileset *fsq;
+   int k, n = 0;
+
+   while((fsq = fsp))
+        {fsp = fsp->Next;
+         k = (fsq->Age >> Shift) & tMask;
+         if (k > n) n = k;
+         if (Shift || !sortSZ) fsq->Next = FSTab[j][k];
+            else fsq = Insert(fsq, FSTab[j][k]);
+         FSTab[j][k] = fsq;
+//cerr <<"Bin " <<std::hex <<fsq->Age <<' ' <<std::dec <<j <<',' <<k <<' ' <<fsq->basePath() <<endl;
+        }
+   return n;
+}
+
+/******************************************************************************/
+/* Private:                       I n s e r t                                 */
+/******************************************************************************/
+  
+XrdFrmFileset *XrdFrmTSort::Insert(XrdFrmFileset *newP, XrdFrmFileset *oldP)
+{
+   XrdFrmFileset *prvP = 0, *nowP = oldP;
+   off_t newSize = newP->baseFile()->Stat.st_size;
+
+// Find insertion point of new element (decreasing size order)
+//
+   while(nowP && newSize < nowP->baseFile()->Stat.st_size)
+        {prvP = nowP; nowP = nowP->Next;}
+
+// Perform insertion
+//
+   if (prvP) {prvP->Next = newP; newP->Next = nowP;}
+      else    newP->Next = nowP;
+
+// Return correct head of list
+//
+   return (prvP ? oldP : newP);
+}
+
+/******************************************************************************/
+/*                                O l d e s t                                 */
+/******************************************************************************/
+
+XrdFrmFileset *XrdFrmTSort::Oldest()
+{
+   XrdFrmFileset *fsp = 0;
+
+// Work backwards on the list, resorting as needed
+//
+   do {while(SCent >= 0)
+            {if ((fsp = FSTab[3][SCent]))
+                {if (!( FSTab[3][SCent] = fsp->Next)) SCent--;
+                 numEnt--;
+//cerr <<"Oldest " <<fsp->Age <<' ' <<fsp->basePath() <<endl;
+                 return fsp;
+                } else SCent--;
+            }
+//cerr <<"SC=" <<SCent <<" MN=" <<MNent <<" HR=" <<HRent <<" DY=" <<DYent <<endl;
+       fsp = 0;
+       while(MNent >= 0 && !fsp) fsp = FSTab[2][MNent--];
+       if (fsp) {FSTab[2][MNent+1]=0; SCent = Bin(fsp, 3, SCshift); continue;}
+       while(HRent >= 0 && !fsp) fsp = FSTab[1][HRent--];
+       if (fsp) {FSTab[1][HRent+1]=0; MNent = Bin(fsp, 2, MNshift); continue;}
+       while(DYent >= 0 && !fsp) fsp = FSTab[0][DYent--];
+       if (fsp) {FSTab[0][DYent+1]=0; HRent = Bin(fsp, 1, HRshift); continue;}
+      } while(numEnt);
+   return 0;
+}
+
+/******************************************************************************/
+/*                                 P u r g e                                  */
+/******************************************************************************/
+  
+void XrdFrmTSort::Purge()
+{
+   XrdFrmFileset *fsp, *csp;
+   int i, j, aBeg[4] = {DYent, HRent, MNent, SCent};
+
+   for (i = 0; i < 4; i++)
+       for (j = aBeg[i]; j >= 0; j--)
+           {if ((fsp = FSTab[i][j]))
+               while((csp = fsp)) {fsp = fsp->Next; delete csp;}
+           }
+   Reset();
+}
+
+/******************************************************************************/
+/* Private:                        R e s e t                                  */
+/*                                                                            */
+/******************************************************************************/
+
+void XrdFrmTSort::Reset()
+{
+
+// Clear the base table and set base time
+//
+   memset(FSTab, 0, sizeof(FSTab));
+   DYent = HRent = MNent = SCent = -1;
+   baseT = time(0);
+   numEnt = 0;
+}
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.hh
new file mode 100644
index 0000000000000000000000000000000000000000..710b04bcfe1e4dbc682f3a8642615aa4d3b580e9
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.hh
@@ -0,0 +1,53 @@
+#ifndef __FRMTSORT__
+#define __FRMTSORT__
+/******************************************************************************/
+/*                                                                            */
+/*                        X r d F r m T S o r t . h h                         */
+/*                                                                            */
+/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+  
+//         $Id$
+
+class XrdFrmFileset;
+
+class XrdFrmTSort
+{
+public:
+
+int               Add(XrdFrmFileset *fsp);
+
+int               Count() {return numEnt;}
+
+XrdFrmFileset    *Oldest();
+
+void              Purge();
+
+                  XrdFrmTSort(int szSort=0) : sortSZ(szSort) {Reset();}
+                 ~XrdFrmTSort() {Purge();}
+
+private:
+int               Bin(XrdFrmFileset *fsp, int j, int Shift);
+XrdFrmFileset    *Insert(XrdFrmFileset *newP, XrdFrmFileset *oldP);
+void              Reset();
+
+static const int  SCshift =  0;
+static const int  MNshift =  6;
+static const int  HRshift = 12;
+static const int  tMask = 0x3f;
+static const int  dVal  = 24*60*60;
+
+XrdFrmFileset    *FSTab[4][64];
+time_t            baseT;
+int               sortSZ;
+int               numEnt;
+
+int               DYent;   // [0,DYent]
+int               HRent;   // [1,HRent]
+int               MNent;   // [2,MNent]
+int               SCent;   // [3,SCent]
+};
+#endif
diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh
index 15f693a53ecda4efa5d668a3956572d78c674dcb..ab7e1b3960bed5d9dddfd0dc3372962ff65f38b4 100644
--- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh
@@ -35,8 +35,7 @@
                        {Trace.Beg(epname, Req.User); cerr <<y; Trace.End();}
 
 #define TRACEX(y) {Trace.Beg(0,epname); cerr <<y; Trace.End();}
-
-#define EPNAME(x) const char *epname = x;
+#define EPNAME(x) static const char *epname = x;
 
 #else
 
@@ -46,6 +45,10 @@
 
 #endif
 
+#define VMSG(a,...) if (Config.Verbose) Say.Emsg(a,__VA_ARGS__);
+
+#define VSAY(a,...) if (Config.Verbose) Say.Say(a,__VA_ARGS__);
+
 namespace XrdFrm
 {
 extern XrdSysLogger Logger;
diff --git a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc
index 6a6609a9b0f48de28999489c8f35c21a3d8320c8..e499907fa24b3ace62c1a22f6ea87651dd7deb22 100644
--- a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc
+++ b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc
@@ -83,11 +83,13 @@ void XrdOdcResp::Recycle()
 
 // Put this object on the free queue
 //
-   myMutex.Lock();
-   next = nextFree;
-   nextFree = this;
-   numFree++;
-   myMutex.UnLock();
+   if (numFree >= maxFree) delete this;
+      else {myMutex.Lock();
+            next = nextFree;
+            nextFree = this;
+            numFree++;
+            myMutex.UnLock();
+           }
 }
 
 /******************************************************************************/
@@ -97,6 +99,7 @@ void XrdOdcResp::Recycle()
 void XrdOdcResp::Reply(const char *Man, char *msg)
 {
    EPNAME("Reply")
+   XrdOucEICB *theCB;
    int Result, msgval;
    char *colon, *opaque;
 
@@ -174,9 +177,11 @@ void XrdOdcResp::Reply(const char *Man, char *msg)
 //
    SyncCB.Wait();
 
-// Invoke the callback; it must explicitly invoke delete on our upcast object.
+// Invoke the callback; telling it to call us back for recycling
 //
-   ErrCB->Done(Result, (XrdOucErrInfo *)this);
+   theCB = ErrCB;
+   ErrCB = (XrdOucEICB *)this;
+   theCB->Done(Result, (XrdOucErrInfo *)this);
 }
 
 /******************************************************************************/
diff --git a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh
index 32001222485824239836691cc8d0d87ce1e6a46f..de6972aec4df3e58267c47d7a63b9923411bffc5 100644
--- a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh
+++ b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh
@@ -43,27 +43,27 @@ XrdSysSemaphore     respSync;
 /*                            X r d O d c R e s p                             */
 /******************************************************************************/
   
-class XrdOdcResp : public XrdOucErrInfo
+class XrdOdcResp : public XrdOucErrInfo, public XrdOucEICB
 {
 public:
 friend class XrdOdcRespQ;
 
 static XrdOdcResp *Alloc(XrdOucErrInfo *erp, int msgid);
 
+       void        Done(int &Result, XrdOucErrInfo *eInfo) {Recycle();}
+
 inline int         ID() {return myID;}
 
        void        Reply(const char *Man, char *reply);
 
+       int         Same(unsigned long long arg1, unsigned long long arg2)
+                       {return 0;}
+
 static void        setDelay(int repdly) {RepDelay = repdly;}
 
        XrdOdcResp() : XrdOucErrInfo(UserID) {next = 0;}
       ~XrdOdcResp() {}
 
-void   operator delete(void *p)
-                      {if (XrdOdcResp::numFree >= XrdOdcResp::maxFree) free(p);
-                          else ((XrdOdcResp *)p)->Recycle();
-                      }
-
 private:
        void Recycle();
 
diff --git a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc
index 25e9977117af61e9744ced64f381f28646c5c7e0..3a8c3c1c2bedc85dcf554d3a3734ae36efe1e1e2 100644
--- a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc
+++ b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc
@@ -227,7 +227,7 @@ int XrdOfsDirectory::open(const char              *dir_path, // In
 */
 {
    EPNAME("opendir");
-   XrdOucEnv Open_Env(info);
+   XrdOucEnv Open_Env(info,0,client);
    int retc;
 
 // Trace entry
@@ -422,7 +422,7 @@ int XrdOfsFile::open(const char          *path,      // In
    mode_t theMode = Mode & S_IAMB;
    int retc, isPosc = 0, crOpts = 0, isRW = 0, open_flag = 0;
    int find_flag = open_mode & (SFS_O_NOWAIT | SFS_O_RESET);
-   XrdOucEnv Open_Env(info);
+   XrdOucEnv Open_Env(info,0,client);
 
 // Trace entry
 //
@@ -1194,7 +1194,7 @@ int XrdOfs::chmod(const char             *path,    // In
    EPNAME("chmod");
    mode_t acc_mode = Mode & S_IAMB;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv chmod_Env(info);
+   XrdOucEnv chmod_Env(info,0,client);
    int retc;
    XTRACE(chmod, path, "");
 
@@ -1261,7 +1261,7 @@ int XrdOfs::exists(const char                *path,        // In
    struct stat fstat;
    int retc;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv stat_Env(info);
+   XrdOucEnv stat_Env(info,0,client);
    XTRACE(exists, path, "");
 
 // Apply security, as needed
@@ -1339,13 +1339,17 @@ int XrdOfs::fsctl(const int               cmd,
 //
    if (opcode == SFS_FSCTL_LOCATE)
       {struct stat fstat;
+       const char *Path, *locArg;
        char rType[3], *Resp[] = {rType, locResp};
-       AUTHORIZE(client,0,AOP_Stat,"locate",args,einfo);
+            if (*args == '*')      {Path = args+1; locArg = args;}
+       else if (cmd & SFS_O_TRUNC) {Path = args;   locArg = (char *)"*";}
+       else                         Path = locArg = args;
+       AUTHORIZE(client,0,AOP_Stat,"locate",Path,einfo);
        if (Finder && Finder->isRemote()
-       &&  (retc = Finder->Locate(einfo, args, find_flag)))
+       &&  (retc = Finder->Locate(einfo, locArg, find_flag)))
           return fsError(einfo, retc);
-       if ((retc = XrdOfsOss->Stat(args, &fstat)))
-          return XrdOfsFS.Emsg(epname, einfo, retc, "locate", args);
+       if ((retc = XrdOfsOss->Stat(Path, &fstat)))
+          return XrdOfsFS.Emsg(epname, einfo, retc, "locate", Path);
        rType[0] = ((fstat.st_mode & S_IFBLK) == S_IFBLK ? 's' : 'S');
        rType[1] = (fstat.st_mode & S_IWUSR            ? 'w' : 'r');
        rType[2] = '\0';
@@ -1371,7 +1375,7 @@ int XrdOfs::fsctl(const int               cmd,
    if (opcode == SFS_FSCTL_STATLS)
       {const char *path;
 	char pbuff[1024], *opq = (char *) index(args, '?');
-       XrdOucEnv statls_Env(opq ? opq+1 : 0);
+       XrdOucEnv statls_Env(opq ? opq+1 : 0,0,client);
        if (!opq) path = args;
           else {int plen = opq-args;
                 if (plen >= (int)sizeof(pbuff)) plen = sizeof(pbuff)-1;
@@ -1474,7 +1478,7 @@ int XrdOfs::mkdir(const char             *path,    // In
    mode_t acc_mode = Mode & S_IAMB;
    int retc, mkpath = Mode & SFS_O_MKPTH;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv mkdir_Env(info);
+   XrdOucEnv mkdir_Env(info,0,client);
    XTRACE(mkdir, path, "");
 
 // Apply security, as needed
@@ -1560,7 +1564,7 @@ int XrdOfs::remove(const char              type,    // In
    EPNAME("remove");
    int retc, Opt;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv rem_Env(info);
+   XrdOucEnv rem_Env(info,0,client);
    XTRACE(remove, path, type);
 
 // Apply security, as needed
@@ -1628,8 +1632,8 @@ int XrdOfs::rename(const char             *old_name,  // In
    EPNAME("rename");
    int retc;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv old_Env(infoO);
-   XrdOucEnv new_Env(infoN);
+   XrdOucEnv old_Env(infoO,0,client);
+   XrdOucEnv new_Env(infoN,0,client);
    XTRACE(rename, new_name, "old fn=" <<old_name <<" new ");
 
 // Apply security, as needed
@@ -1692,7 +1696,7 @@ int XrdOfs::stat(const char             *path,        // In
    EPNAME("stat");
    int retc;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv stat_Env(info);
+   XrdOucEnv stat_Env(info,0,client);
    XTRACE(stat, path, "");
 
 // Apply security, as needed
@@ -1737,7 +1741,7 @@ int XrdOfs::stat(const char             *path,        // In
    struct stat buf;
    int retc;
    const char *tident = einfo.getErrUser();
-   XrdOucEnv stat_Env(info);
+   XrdOucEnv stat_Env(info,0,client);
    XTRACE(stat, path, "");
 
 // Apply security, as needed
@@ -1783,7 +1787,7 @@ int XrdOfs::truncate(const char             *path,    // In
 {
    EPNAME("truncate");
    const char *tident = einfo.getErrUser();
-   XrdOucEnv trunc_Env(info);
+   XrdOucEnv trunc_Env(info,0,client);
    int retc;
    XTRACE(truncate, path, "");
 
diff --git a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc
index af2038029b807e336da3d5b546034c6bf41a46a0..51d9aa9b93bf2065d9bd832b5443e1398d0288c1 100644
--- a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc
+++ b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc
@@ -25,7 +25,8 @@ const char *XrdOfsEvrCVSID = "$Id$";
 #include "XrdOuc/XrdOucTrace.hh"
 #include "XrdNet/XrdNetOpts.hh"
 #include "XrdNet/XrdNetSocket.hh"
-  
+#include "XrdSys/XrdSysHeaders.hh"
+
 /******************************************************************************/
 /*                     E x t e r n a l   L i n k a g e s                      */
 /******************************************************************************/
@@ -171,7 +172,7 @@ int XrdOfsEvr::Init(XrdSysError *eobj, XrdCmsClient *trgp)
   
 void XrdOfsEvr::recvEvents()
 {
-   static const char *epname = "recvEvent";
+   EPNAME("recvEvent");
    const char *tident = 0;
    char *lp,*tp;
 
@@ -321,7 +322,7 @@ void XrdOfsEvr::sendEvent(theEvent *ep)
 // to it just in case a client is in-transit
 //
    while((cp = ep->aClient))
-        {einfo = new XrdOucErrInfo(cp->User, cp->evtCB, cp->evtCBarg);
+        {einfo = new XrdOucErrInfo(cp->User, 0, cp->evtCBarg);
          einfo->setErrInfo(ep->finalRC, (ep->finalMsg ? ep->finalMsg : ""));
          cp->evtCB->Done(Result, einfo);
          ep->aClient = cp->Next;
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh
index a415c2ae5ee1c6dbb73a48e058724b6cc5ebd012..939165d64e53e77d1e0adfe53fbaf998110ecf66 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh
@@ -14,8 +14,10 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <strings.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <string.h>
 
 class XrdOucEnv;
 class XrdSysLogger;
@@ -137,6 +139,11 @@ virtual int     Stats(char *bp, int bl) {return 0;}
 virtual int     StatVS(XrdOssVSInfo *sP, const char *sname=0, int updt=0)
                       {return -ENOTSUP;}
 
+virtual int     Lfn2Pfn(const char *Path, char *buff, int blen)
+                       {if ((int)strlen(Path) >= blen) return -ENAMETOOLONG;
+                        strcpy(buff, Path); return 0;
+                       }
+
                 XrdOss() {}
 virtual        ~XrdOss() {}
 };
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc
index 38f78c37b53aee41623848a89dde55a95cc93f3a..d34e5201bf958dd78274c5571e90a02341df5185 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc
@@ -53,7 +53,6 @@ const char *XrdOssApiCVSID = "$Id$";
 #ifdef XRDOSSCX
 #include "oocx_CXFile.h"
 #endif
-// IOS_USING_DECLARATION_MARKER - BaBar iostreams migration, do not touch this line!
 
 /******************************************************************************/
 /*                  E r r o r   R o u t i n g   O b j e c t                   */
@@ -153,6 +152,18 @@ int XrdOssSys::Init(XrdSysLogger *lp, const char *configfn)
    return XrdOssOK;
 }
 
+/******************************************************************************/
+/*                               L f n 2 P f n                                */
+/******************************************************************************/
+  
+int XrdOssSys::Lfn2Pfn(const char *oldp, char *newp, int blen)
+{
+    if (lcl_N2N) return -(lcl_N2N->lfn2pfn(oldp, newp, blen));
+    if ((int)strlen(oldp) >= blen) return -ENAMETOOLONG;
+    strcpy(newp, oldp);
+    return 0;
+}
+
 /******************************************************************************/
 /*                          G e n L o c a l P a t h                           */
 /******************************************************************************/
@@ -406,9 +417,7 @@ int XrdOssSys::Truncate(const char *path, unsigned long long size)
 */
 int XrdOssDir::Opendir(const char *dir_path) 
 {
-#ifndef NODEBUG
-   const char *epname = "Opendir";
-#endif
+   EPNAME("Opendir");
    char actual_path[MAXPATHLEN+1], *local_path, *remote_path;
    unsigned long long isremote;
    int retc;
@@ -432,11 +441,10 @@ int XrdOssDir::Opendir(const char *dir_path)
 
 // If this is a local filesystem request, open locally.
 //
-   if (!isremote)
+   if (!isremote || (pflags & XRDEXP_NODREAD))
       {TRACE(Opendir, "lcl path " <<local_path <<" (" <<dir_path <<")");
-       if (!(lclfd = opendir((char *)local_path))) return -errno;
-       isopen = 1;
-       return XrdOssOK;
+       if ((lclfd = opendir((char *)local_path))) {isopen = 1; return XrdOssOK;}
+          else if (!isremote) return -errno;
       }
 
 // Generate remote path
@@ -447,27 +455,19 @@ int XrdOssDir::Opendir(const char *dir_path)
          else remote_path = actual_path;
       else remote_path = (char *)dir_path;
 
-// Trace this remote request
-//
-   TRACE(Opendir, "rmt path " <<remote_path <<" (" <<dir_path <<")");
+   TRACE(Opendir, "rmt path " << remote_path <<" (" << dir_path <<")");
 
-// If we need not read the actual directory, just check if it exists
+// If NOCHECK is in effect and we have an mss meta-cmd, just do a stat
 //
-   if (pflags & XRDEXP_NODREAD)
+   if (!(pflags & XRDEXP_NOCHECK) && XrdOssSS->MSSgwCmd)
       {struct stat fstat;
-       if (stat(local_path, &fstat))
-          {if (pflags & XRDEXP_NOCHECK) fstat.st_mode = S_IFDIR;
-              else {if (!XrdOssSS->MSSgwCmd) return -errno;
-                    if ((retc = XrdOssSS->MSS_Stat(remote_path, &fstat)))
-                       return retc;
-                   }
-          }
+       if ((retc = XrdOssSS->MSS_Stat(remote_path,&fstat))) return retc;
        if (!(S_ISDIR(fstat.st_mode))) return -ENOTDIR;
-       isopen = -1;
+       isopen = 1;
        return XrdOssOK;
       }
 
-// This is a remote directory and we must read it. Perform remote open
+// Open the directory at the remote location.
 //
    if (!(mssfd = XrdOssSS->MSS_Opendir(remote_path, retc))) return retc;
    isopen = 1;
@@ -936,6 +936,7 @@ int XrdOssFile::Open_ufs(const char *path, int Oflag, int Mode,
                          unsigned long long popts)
 {
     EPNAME("Open_ufs")
+    static const int isWritable = O_WRONLY|O_RDWR;
     int myfd, newfd, retc;
 #ifndef NODEBUG
     char *ftype = (char *)" path=";
@@ -955,6 +956,13 @@ int XrdOssFile::Open_ufs(const char *path, int Oflag, int Mode,
     do { myfd = open(path, Oflag|O_LARGEFILE, Mode);}
        while( myfd < 0 && errno == EINTR);
 
+// If the file is marked purgeable or migratable and we may modify this file,
+// then get a shared lock on the file to keep it from being migrated or purged
+// while it is open.
+//
+   if (popts & XRDEXP_PURGE || (popts & XRDEXP_MIG && Oflag & isWritable))
+      ufs_file.Serialize(myfd, XrdOssSHR);
+
 // Chck if file is compressed
 //
     if (myfd < 0) myfd = -errno;
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh
index ea691c2a95ddf13d4486abb866c108f408094754..a21d3041507756c4ac7bec2ca9232b9ddb769f08 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh
@@ -135,6 +135,7 @@ int       GenRemotePath(const char *, char *);
 int       Init(XrdSysLogger *, const char *);
 int       IsRemote(const char *path) 
                   {return (RPList.Find(path) & XRDEXP_REMOTE) != 0;}
+int       Lfn2Pfn(const char *Path, char *buff, int blen);
 int       Mkdir(const char *, mode_t mode, int mkpath=0);
 int       Mkpath(const char *, mode_t mode);
 unsigned long long PathOpts(const char *path) {return RPList.Find(path);}
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc
index 00fb862fa5e962c463d6f8374f82ddd4e829f693..b448bcbcc85cc3c41ae7a331654a6b80dff98105 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc
@@ -429,8 +429,8 @@ int XrdOssCache::Alloc(XrdOssCache::allocInfo &aInfo)
        else  if (!fuzAlloc) {if (curfree > maxfree)
                                 {fsp_sel = fsp; maxfree = curfree;}}
        else {diffree = (!(curfree + maxfree) ? 0.0
-                     : static_cast<double>(::llabs(maxfree - curfree)) /
-                       static_cast<double>(        maxfree + curfree));
+                     : static_cast<double>(XRDABS(maxfree - curfree)) /
+                       static_cast<double>(       maxfree + curfree));
              if (diffree > fuzAlloc) {fsp_sel = fsp; maxfree = curfree;}
             }
       } while((fsp = fsp->next) != fspend);
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc
index 453926692935f38d19031446dba22497ec6f35fc..4cb147785cc0afe7388d575147fa3d45ea9fbf68 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc
@@ -63,6 +63,8 @@ extern XrdOssSys   *XrdOssSS;
 
 extern XrdOucTrace  OssTrace;
 
+XrdOucPListAnchor  *XrdOssRPList;
+
 /******************************************************************************/
 /*                            E r r o r   T e x t                             */
 /******************************************************************************/
@@ -282,6 +284,10 @@ int XrdOssSys::Configure(const char *configfn, XrdSysError &Eroute)
 //
    if (!NoGo) Config_Display(Eroute);
 
+// Export the real path list (for frm et. al.)
+//
+   XrdOssRPList = &RPList;
+
 // All done, close the stream and return the return code.
 //
    val = (NoGo ? (char *)"failed." : (char *)"completed.");
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc
index ee33e0a996bcf68a39b33e577372cf77cccaa710..fac75964fb89c12b9259d9c312c44035108ebff1 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc
@@ -155,7 +155,7 @@ int XrdOssLock::Serialize(const char *fn, int lkwant)
 
 // Now lock the file and return the file descriptor.
 //
-    if ((rc = XLock(lkwant)))
+    if ((rc = XLock(lkfd, lkwant)))
        {char *mp;
         close(lkfd); lkfd = -1;
         if (rc == EWOULDBLOCK) return -EWOULDBLOCK;
@@ -270,7 +270,7 @@ int XrdOssLock::UnSerialize(int opts)
 
 // Release the lock if we need to.
 //
-   if (!(opts & XrdOssREGRADE)) XLock(0);
+   if (!(opts & XrdOssREGRADE)) XLock(lkfd, 0);
       else dosleep = 0;
 
 // Based on execution option, perform the required action.
@@ -279,7 +279,7 @@ int XrdOssLock::UnSerialize(int opts)
     switch(xopts)
          {case XrdOssLEAVE: break;
           case XrdOssRETRY: do {if (dosleep) nanosleep(&naptime, 0);
-                               if (! (rc = XLock(opts)) ) break;
+                               if (! (rc = XLock(lkfd, opts)) ) break;
                                dosleep = 1;
                               } while( rc == EWOULDBLOCK && 
                                       !(opts & XrdOssNOWAIT) && maxtry--);
@@ -324,13 +324,13 @@ int XrdOssLock::Build_LKFN(char *buff, int blen, const char *fn, int ftype)
 /*                                X L o c k                                   */
 /******************************************************************************/
 
-int XrdOssLock::XLock(int opts)
+int XrdOssLock::XLock(int lkFD, int opts)
 {
     FLOCK_t lock_args;
 
 // Make sure we have a lock outstanding
 //
-    if (lkfd < 0) return XrdOssOK;
+    if (lkFD < 0) return XrdOssOK;
 
 // Establish locking options
 //
@@ -341,7 +341,7 @@ int XrdOssLock::XLock(int opts)
 
 // Perform action.
 //
-    if (fcntl(lkfd, (opts & XrdOssNOWAIT ? F_SETLK : F_SETLKW), 
+    if (fcntl(lkFD, (opts & XrdOssNOWAIT ? F_SETLK : F_SETLKW),
                     &lock_args)) return errno;
     return XrdOssOK;
 }
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh
index 67f81ac19b94a6ab9bdb234d60b7f8a0692e61e4..790bb58921433a389efe282fdd0445cb429afba0 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh
@@ -17,6 +17,7 @@ class XrdOssLock
 public:
 
 int Serialize(const char *, int);
+int Serialize(int lkFD, int Opt) {return XLock(lkFD, Opt);}
 int NoSerialize(const char *, int);
 int ReSerialize(const char *, const char *);
 int UnSerialize(int opts=0);
@@ -28,7 +29,7 @@ private:
 
 int lkfd;      // Lock file handle
 
-int XLock(int);
+int XLock(int, int);
 int Build_LKFN(char *, int, const char *, int);
 };
 
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc
index 8326e368d41d991ac67d8fd1f2dd4f7ff2882ca2..f3aec8fe7862d53d307189070ed6ac18ab0c4e07 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc
@@ -83,8 +83,8 @@ int XrdOssSys::Stat(const char *path, struct stat *buff, int opts)
 
 // The file may be offline in a mass storage system, check if this is possible
 //
-   if (!IsRemote(path)) return -errno;
-   if (opts & XRDOSS_resonly || !MSSgwCmd) return -ENOMSG;
+   if (!IsRemote(path) || opts & XRDOSS_resonly) return -errno;
+   if (!MSSgwCmd) return -ENOMSG;
 
 // Generate remote path
 //
diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh
index db880e79cdd6f3c45e88e9f59d029fdbf68affea..2e254527c7a585bfeee3afcc24e69eec0a493a9a 100644
--- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh
@@ -38,7 +38,7 @@
 #define DEBUG(y) if (QTRACE(Debug)) \
                     {OssTrace.Beg(epname); cerr <<y; OssTrace.End();}
 
-#define EPNAME(x) const char *epname = x;
+#define EPNAME(x) static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc
index a946b017ba5a772ff4070c7d9887824ccbcf0351..45a18defd19159fed451a8a6ee068baa851f0222 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc
@@ -21,7 +21,9 @@ const char *XrdOucEnvCVSID = "$Id$";
 /*                           C o n s t r u c t o r                            */
 /******************************************************************************/
   
-XrdOucEnv::XrdOucEnv(const char *vardata, int varlen) : env_Hash(8,13)
+XrdOucEnv::XrdOucEnv(const char *vardata, int varlen, 
+                     const XrdSecEntity *secent)
+                    : env_Hash(8,13), secEntity(secent)
 {
    char *vdp, varsave, *varname, *varvalu;
 
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh
index fff132c79880705f2585c4760fada333b3371fa4..17f487261adffbe4c230d6b7c2718c2cf448619a 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh
@@ -18,6 +18,8 @@
 #endif
 #include "XrdOuc/XrdOucHash.hh"
 
+class XrdSecEntity;
+
 class XrdOucEnv
 {
 public:
@@ -61,16 +63,22 @@ static int   Export(const char *Var, int         Val);
 //
        char *Delimit(char *value);
 
+// secEnv() returns the security environment; which may be a null pointer.
+//
+inline const XrdSecEntity *secEnv() {return secEntity;}
+
 // Use the constructor to define the initial variable settings. The passed
 // string is duplicated and the copy can be retrieved using Env().
 //
-       XrdOucEnv(const char *vardata=0, int vardlen=0);
+       XrdOucEnv(const char *vardata=0, int vardlen=0, 
+                 const XrdSecEntity *secent=0);
 
       ~XrdOucEnv() {if (global_env) free((void *)global_env);}
 
 private:
 
 XrdOucHash<char> env_Hash;
+const XrdSecEntity *secEntity;
 char *global_env;
 int   global_len;
 };
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh
index 0a15e2fd3317927b23cfc9cd6a51e229ddd7844a..9f7c725a9691e3b581cf56a2299f55a1472dc4dd 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh
@@ -120,14 +120,19 @@ public:
 
 // Done() is invoked when the requested operation completes. Arguments are:
 //        Result - the original function's result (may be changed).
-//        eInfo  - Associated error information. The callback function must
-//                 manually delete this object when it is through! While icky
-//                 this allows callback functions to be asynchronous.
+//        eInfo  - Associated error information. The eInfo object may not be
+//                 modified until it's own callback Done() method is called, if
+//                 supplied. If the callback function in eInfo is zero, then the
+//                 eInfo object is deleted by the invoked callback. Otherwise,
+//                 that method must be invoked by this callback function after
+//                 the actual callback message is sent. This allows the callback
+//                 requestor to do post-processing and be asynchronous.
+//
 //
 virtual void        Done(int           &Result,   //I/O: Function result
                          XrdOucErrInfo *eInfo)=0; // In: Error Info
 
-// Same() is invoked to determine if two argtuments refer to the same user.
+// Same() is invoked to determine if two arguments refer to the same user.
 //        True is returned if so, false, otherwise.
 //
 virtual int         Same(unsigned long long arg1, unsigned long long arg2)=0;
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc
index f0c257e53baeaa866bccbea2a219a28b0e9fb749..79f0111e378627dc0f6fd721f7653d8f2d7c4b34 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc
@@ -27,9 +27,9 @@ const char *XrdOucNSWalkCVSID = "$Id$";
 /******************************************************************************/
   
 XrdOucNSWalk::XrdOucNSWalk(XrdSysError *erp, const char *dpath,
-                                             const char *lkfn, int opts)
+                                             const char *lkfn, int opts,
+                                             XrdOucTList *xlist)
 {
-
 // Set the required fields
 //
    eDest = erp;
@@ -40,6 +40,15 @@ XrdOucNSWalk::XrdOucNSWalk(XrdSysError *erp, const char *dpath,
    DPfd = LKfd = -1;
    errOK= opts & skpErrs;
    DEnts= 0;
+   edCB = 0;
+
+// Copy the exclude list if one exists
+//
+   if (!xlist) XList = 0;
+      else while(xlist)
+                {XList = new XrdOucTList(xlist->text,xlist->ival,XList);
+                 xlist = xlist->next;
+                }
 }
 
 /******************************************************************************/
@@ -53,6 +62,8 @@ XrdOucNSWalk::~XrdOucNSWalk()
    if (LKFn) free(LKFn);
 
    while((tP = DList)) {DList = tP->next; delete tP;}
+
+   while((tP = XList)) {XList = tP->next; delete tP;}
 }
 
 /******************************************************************************/
@@ -74,6 +85,7 @@ XrdOucNSWalk::NSEnt *XrdOucNSWalk::Index(int &rc, const char **dPath)
          rc = Build();
          if (LKfd >= 0) close(LKfd);
          if (DEnts || (rc && !errOK)) break;
+         if (edCB && isEmpty) edCB->isEmpty(&dStat, DPath, LKFn);
         }
 
 // Return the result
@@ -128,6 +140,11 @@ int XrdOucNSWalk::Build()
                  } theEnt;
    struct dirent  *dp;
    int             rc = 0, getLI = Opts & retLink;
+   int             nEnt = 0, xLKF = 0, chkED = (edCB != 0) && (LKFn != 0);
+
+// Initialize the empty flag prior to doing anything else
+//
+   isEmpty = 0;
 
 // If we can optimize with a directory file descriptor, get one
 //
@@ -149,17 +166,19 @@ int XrdOucNSWalk::Build()
    errno = 0;
    while((dp = readdir(theEnt.D)))
         {if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
-         strcpy(File, dp->d_name);
+         strcpy(File, dp->d_name); nEnt++;
          if (!theEnt.P) theEnt.P = new NSEnt();
          rc = getStat(theEnt.P, getLI);
          switch(theEnt.P->Type)
                {case NSEnt::isDir:
-                     if (Opts & Recurse && (!getLI || !isSymlink()))
+                     if (Opts & Recurse && (!getLI || !isSymlink())
+                     &&  (!XList || !inXList(File)))
                         DList = new XrdOucTList(DPath, 0, DList);
                      if (!(Opts & retDir)) continue;
                      break;
                 case NSEnt::isFile:
-                     if (!(Opts & retFile)) continue;
+                     if ((chkED && !xLKF && (xLKF = !strcmp(File, LKFn)))
+                     ||  !(Opts & retFile)) continue;
                      break;
                 case NSEnt::isLink:
                      if ((rc = getLink(theEnt.P)))
@@ -184,6 +203,13 @@ int XrdOucNSWalk::Build()
    *File = '\0';
    if ((rc = errno) && !errOK)
       {eDest->Emsg("Build", rc, "reading directory", DPath); return rc;}
+
+// Check if we need to do a callback for an empty directory
+//
+   if (edCB && xLKF == nEnt && !DEnts)
+      {if (!fstat(DPfd, &dStat)) isEmpty = 1;
+          else eDest->Emsg("Build", errno, "stating directory", DPath);
+      }
    return 0;
 }
 
@@ -247,6 +273,27 @@ do{rc = doLstat ? lstat(DPath, &(eP->Stat)) : stat(DPath, &(eP->Stat));
    return 0;
 }
   
+/******************************************************************************/
+/*                               i n X L i s t                                */
+/******************************************************************************/
+  
+int XrdOucNSWalk::inXList(const char *dName)
+{
+    XrdOucTList *xTP = XList, *pTP = 0;
+
+// Search for the directory entry
+//
+    while(xTP && strcmp(DPath, xTP->text)) {pTP = xTP; xTP = xTP->next;}
+
+// If not found return false. Otherwise, delete the entry and return true.
+//
+   if (!xTP) return 0;
+   if (pTP) pTP->next = xTP->next;
+      else      XList = xTP->next;
+   delete xTP;
+   return 1;
+}
+  
 /******************************************************************************/
 /*                             i s S y m l i n k                              */
 /******************************************************************************/
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh
index 33ca9370433f79f61bf7d60efc0e84ff78769c50..6d930ca7490a9e402fee3b1ee707dad58394a52e 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh
@@ -59,6 +59,23 @@ Etype         Type;   // One of the above. If isLink then Link is invalid!
 //
 NSEnt        *Index(int &rc, const char **dPath=0);
 
+// The CallBack class is used to intercept empty directories. When set by a
+// call to setCallBack(); should an empty directory (i.e., one with no entries
+// or only with a lock file) in encountered a call is made to to the isEmpty()
+// method. If lkFn is zero, the directory is empty; otherwise, lkFn is the name
+// of the singleton lock file. To unset the callback use setCallBack(0);
+//
+class CallBack
+{public:
+virtual
+void     isEmpty(struct stat *dStat, const char *dPath, const char *lkFn)=0;
+
+         CallBack() {}
+virtual ~CallBack() {}
+};
+
+void         setCallBack(CallBack *cbP=0) {edCB = cbP;}
+
 // The following are processing options passed to the constructor
 //
 static const int retDir =  0x0001; // Return directories (implies retStat)
@@ -76,13 +93,15 @@ static const int skpErrs=  0x8000; // Skip any entry causing an error
              XrdOucNSWalk(XrdSysError *erp,  // Error msg object. If 0->silent
                          const char *dname,  // Initial directory path
                          const char *LKfn=0, // Lock file name (see note below)
-                         int opts=retAll);   // Options        (see above)
+                         int opts=retAll,    // Options        (see above)
+                         XrdOucTList *xP=0); // 1st Level dir exclude list
             ~XrdOucNSWalk();
 
 // Note: When Lkfn is supplied and it exists in a directory about to be indexed
 //       then the file is opened in r/w mode and an exclusive lock is obtained.
 //       If either fails, the the directory is not indexed and Index() will
-//       return null pointer with rc != 0.
+//       return null pointer with rc != 0. Note that the lkfn is not returned
+//       as a directory entry if an empty directory call back has been set.
 
 private:
 void          addEnt(XrdOucNSWalk::NSEnt *eP);
@@ -90,13 +109,17 @@ int           Build();
 int           getLink(XrdOucNSWalk::NSEnt *eP);
 int           getStat(XrdOucNSWalk::NSEnt *eP, int doLstat=0);
 int           getStat();
+int           inXList(const char *dName);
 int           isSymlink();
 int           LockFile();
 void          setPath(char *newpath);
 
 XrdSysError  *eDest;
 XrdOucTList  *DList;
+XrdOucTList  *XList;
 struct NSEnt *DEnts;
+struct stat   dStat;
+CallBack     *edCB;
 char          DPath[1032];
 char         *File;
 char         *LKFn;
@@ -104,5 +127,6 @@ int           LKfd;
 int           DPfd;
 int           Opts;
 int           errOK;
+int           isEmpty;
 };
 #endif
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh
index c217c7952308459227bf4090e9c7415105e1f29f..351707ecced3b3b22dbf319baa2ccf11e3a05bf2 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh
@@ -22,18 +22,16 @@ public:
 inline unsigned long long  Flag() {return flags;}
 inline XrdOucPList        *Next() {return next;}
 inline char               *Path() {return path;}
+inline int                 Plen() {return pathlen;}
 
 inline int          PathOK(const char *pd, const int pl)
                           {return pl >= pathlen && !strncmp(pd, path, pathlen);}
 
 inline void         Set(unsigned long long fval) {flags = fval;}
 
-             XrdOucPList(const char *pathdata="", unsigned long long fvals=0)
-                  {next = 0; 
-                   pathlen = strlen(pathdata); 
-                   path    = strdup(pathdata);
-                   flags   = fvals;}
-
+             XrdOucPList(const char *pd="", unsigned long long fv=0)
+                        : flags(fv), next(0),  path(strdup(pd)),
+                          pathlen(strlen(pd)), reserved(0) {}
             ~XrdOucPList()
                   {if (path) free(path);}
 
@@ -41,10 +39,11 @@ friend class XrdOucPListAnchor;
 
 private:
 
-XrdOucPList       *next;
 unsigned long long flags;
+XrdOucPList       *next;
 char              *path;
 int                pathlen;
+int                reserved;
 };
 
 class XrdOucPListAnchor : public XrdOucPList
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc
index 5f76e6fd27fa2f468d61f3a6e463ffb9db406f6c..85fb71d9b19de3621d9e351e8d4b18d7cba165e3 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc
@@ -115,7 +115,7 @@ int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2,
 
 // Execute the command
 //
-   if (Sp->Exec(myArgs, 1))
+   if (Sp->Exec(myArgs, 1, theEFD))
       {rc = Sp->LastError();
        if (eDest) eDest->Emsg("Run", rc, "execute", Arg[0]);
        return -rc;
@@ -228,10 +228,11 @@ int XrdOucProg::Start()
 // Create a stream for this command (it is an eror if we are already started)
 //
    if (myStream) return EBUSY;
-   if (!(myStream = new XrdOucStream())) return ENOMEM;
+   if (!(myStream = new XrdOucStream(eDest))) return ENOMEM;
 
 // Execute the command and let it linger
 //
+   theEFD = 0;
    return Run(myStream);
 }
  
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh
index cd1640e950e4219c8ceae8796de373a2d88f5091..feb10765a165c27c53df1b14b8a2b4a1f72a73f9 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh
@@ -27,7 +27,7 @@ public:
 //
             XrdOucProg(XrdSysError *errobj=0)
                       {eDest = errobj; myStream = 0;
-                       ArgBuff = Arg[0] = 0; numArgs = 0;
+                       ArgBuff = Arg[0] = 0; numArgs = 0; theEFD = -1;
                       }
 
            ~XrdOucProg();
@@ -86,5 +86,6 @@ private:
   char         *Arg[64];
   int           numArgs;
   int           lenArgs;
+  int           theEFD;
 };
 #endif
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc
index b57e76065106edc07a1e474122966e4542c6df12..fa7c81d185b61cc7f8a26e27c080ac173685ee2f 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc
@@ -231,7 +231,7 @@ void XrdOucStream::Echo()
 /*                               E   x   e   c                                */
 /******************************************************************************/
   
-int XrdOucStream::Exec(const char *theCmd, int inrd)
+int XrdOucStream::Exec(const char *theCmd, int inrd, int efd)
 {
     int j;
     char *cmd, *origcmd, *parm[MaxARGC];
@@ -254,15 +254,14 @@ int XrdOucStream::Exec(const char *theCmd, int inrd)
 
     // Continue with normal processing
     //
-    j = Exec(parm, inrd);
+    j = Exec(parm, inrd, efd);
     free(origcmd);
     return j;
 }
 
-int XrdOucStream::Exec(char **parm, int inrd)
+int XrdOucStream::Exec(char **parm, int inrd, int efd)
 {
-    int fildes[2], Child_in = -1, Child_out = -1;
-    int Child_log = (Eroute ? Eroute->logger()->xlogFD() : -1);
+    int fildes[2], Child_in = -1, Child_out = -1, Child_log = -1;
 
     // Create a pipe. Minimize file descriptor leaks.
     //
@@ -284,12 +283,18 @@ int XrdOucStream::Exec(char **parm, int inrd)
            }
        } else {Child_out = FD; Child_in = FE;}
 
+    // Handle the standard error file descriptor
+    //
+    if (!efd) Child_log = (Eroute ? dup(Eroute->logger()->originalFD()) : -1);
+       else if (efd > 0) Child_log = efd;
+
     // Fork a process first so we can pick up the next request. We also
     // set the process group in case the chi;d hasn't been able to do so.
     //
     if ((child = fork()))
        {          close(Child_out);
         if (inrd) close(Child_in );
+        if (!efd && Child_log >= 0) close(Child_log);
         if (child < 0)
            return Err(Exec, errno, "fork request process for", parm[0]);
         setpgid(child, child);
@@ -580,7 +585,7 @@ int XrdOucStream::GetRest(char *theBuff, int Blen, int lowcase)
    while ((tp = GetWord(lowcase)))
          {tlen = strlen(tp);
           if (tlen+1 >= Blen) return 0;
-          if (myBuff != theBuff) *myBuff++ = ' ';
+          if (myBuff != theBuff) {*myBuff++ = ' '; Blen--;}
           strcpy(myBuff, tp);
           Blen -= tlen; myBuff += tlen;
          }
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh
index 65d6b7b3f8c20d96217e3075f9850d9edd345301..46545c0c3037d801dca0ba3eb41bbc405905aa19 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh
@@ -71,10 +71,14 @@ void         Echo();
 // will return the standard output of the executed command. If inrd=1 then
 // standardin is redirected so that subqseuent Put() calls write to the
 // process via standard in. When inrd=-1 then the current attached FD's are
-// used to redirect STDIN and STDOUT of the child process.
-//
-int          Exec(const char *,  int inrd=0);
-int          Exec(      char **, int inrd=0);
+// used to redirect STDIN and STDOUT of the child process. Standard error
+// is handled as determined by the efd argument:
+// efd < 0 -> The current stderr file decriptor is unchanged.
+// efd = 0 -> The stderr file descriptor is set to the original logging FD
+// efd > 0 -> The stderr file descriptor is set to the value of efd.
+//
+int          Exec(const char *,  int inrd=0, int efd=0);
+int          Exec(      char **, int inrd=0, int efd=0);
 
 // Get the file descriptor number associated with a stream
 //
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh
index 79631d361dba74207a8f76014ca7cb423ce69ed5..5a57713291433014424117919f7c83a028d33aa4 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh
@@ -364,7 +364,7 @@ ostream &operator<< (ostream &, const XrdOucString s);
 
 XrdOucString const operator+(const char *s1, const XrdOucString s2);
 XrdOucString const operator+(const char c, const XrdOucString s);
-XrdOucString const operator+(const int i, const XrdOucString s);     
+XrdOucString const operator+(const int i, const XrdOucString s);
 
 #endif
 
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh
index 5d412d505b2b77bbdc6d66a6d71ba2a9185634e2..a353ba3d4feadb743c2f05bd77d15ce5a24a3c94 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh
@@ -22,10 +22,32 @@ public:
 
 XrdOucTList *next;
 char        *text;
+union
+{
+long long    dval;
+int          ival[2];
+short        sval[4];
+char         cval[8];
 int          val;
+};
+
+             XrdOucTList(const char *tval, long long *dv,XrdOucTList *np=0)
+                        {next=np; text = (tval ? strdup(tval) : 0); dval=*dv;}
 
              XrdOucTList(const char *tval=0, int num=0, XrdOucTList *np=0)
-                        {text = (tval ? strdup(tval) : 0); val=num; next=np;}
+                        {next=np; text = (tval ? strdup(tval) : 0); val=num;}
+
+             XrdOucTList(const char *tval, int   iv[2], XrdOucTList *np=0)
+                        {next=np; text = (tval ? strdup(tval) : 0);
+                         memcpy(sval, iv, sizeof(ival));}
+
+             XrdOucTList(const char *tval, short sv[4], XrdOucTList *np=0)
+                        {next=np; text = (tval ? strdup(tval) : 0);
+                         memcpy(sval, sv, sizeof(sval));}
+
+             XrdOucTList(const char *tval, char  cv[8], XrdOucTList *np=0)
+                        {text = (tval ? strdup(tval) : 0); next=np;
+                         memcpy(cval, cv, sizeof(cval));}
 
             ~XrdOucTList() {if (text) free(text);}
 };
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc
index ed84253d1b39a4f3b41d95c743d223f097dcd40b..915557347e4f9e8de843a78b8694ae79016d6d34 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc
@@ -14,6 +14,7 @@ const char *XrdOucUtilsCVSID = "$Id$";
 
 #include <ctype.h>
 #include <errno.h>
+#include <stdio.h>
 
 #ifdef WIN32
 #include <direct.h>
@@ -143,6 +144,33 @@ int XrdOucUtils::doIf(XrdSysError *eDest, XrdOucStream &Config,
    return (val != 0);
 }
 
+/******************************************************************************/
+/*                              f m t B y t e s                               */
+/******************************************************************************/
+  
+int XrdOucUtils::fmtBytes(long long val, char *buff, int bsz)
+{
+   static const long long Kval = 1024LL;
+   static const long long Mval = 1024LL*1024LL;
+   static const long long Gval = 1024LL*1024LL*1024LL;
+   static const long long Tval = 1024LL*1024LL*1024LL*1024LL;
+   char sName = ' ';
+   int resid;
+
+// Get correct scaling
+//
+        if (val < 1024)  return snprintf(buff, bsz, "%lld", val);
+        if (val < Mval) {val = val*10/Kval; sName = 'K';}
+   else if (val < Gval) {val = val*10/Mval; sName = 'M';}
+   else if (val < Tval) {val = val*10/Gval; sName = 'G';}
+   else                 {val = val*10/Tval; sName = 'T';}
+   resid = val%10LL; val = val/10LL;
+
+// Format it
+//
+   return snprintf(buff, bsz, "%lld.%d%c", val, resid, sName);
+}
+
 /******************************************************************************/
 /*                               g e n P a t h                                */
 /******************************************************************************/
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh
index f72269ee47fb686cc6fbac967e2b781f9990b6d4..8cbaba4b3594a5d1d4c85445f952528f81bef481 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh
@@ -30,6 +30,8 @@ static int   doIf(XrdSysError *eDest, XrdOucStream &Config,
                   const char *what, const char *hname, 
                                     const char *nname, const char *pname);
  
+static int   fmtBytes(long long val, char *buff, int bsz);
+
 static char *genPath(const char *path, const char *inst, const char *psfx=0);
 
 static int   genPath(char *buff, int blen, const char *path, const char *psfx=0);
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc
index 641aac8fab9f85824176736b0e413d989456e03c..6e2d8488704184e517660a1aba411810a86f449a 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc
@@ -51,7 +51,7 @@ int XrdOuca2x::a2i(XrdSysError &Eroute, const char *emsg, const char *item,
 /*                                  a 2 l l                                   */
 /******************************************************************************/
 
-long long XrdOuca2x::a2ll(XrdSysError &Eroute, const char *emsg, const char *item,
+int XrdOuca2x::a2ll(XrdSysError &Eroute, const char *emsg, const char *item,
                                 long long *val, long long minv, long long maxv)
 {
     char *eP;
@@ -117,11 +117,55 @@ int XrdOuca2x::a2fm(XrdSysError &Eroute, const char *emsg, const char *item,
     return 0;
 }
  
+/******************************************************************************/
+/*                                  a 2 s p                                   */
+/******************************************************************************/
+
+int XrdOuca2x::a2sp(XrdSysError &Eroute, const char *emsg, const char *item,
+                                long long *val, long long minv, long long maxv)
+{
+    char *pp, buff[120];
+    int i;
+
+    if (!item || !*item)
+       {Eroute.Emsg("a2x", emsg, "value not specified"); return -1;}
+
+    i = strlen(item);
+    if (item[i-1] != '%') return a2sz(Eroute, emsg, item, val, minv, maxv);
+
+    errno = 0;
+    *val  = strtoll(item, &pp, 10);
+
+    if (errno || *pp != '%')
+       {Eroute.Emsg("a2x", emsg, item, "is not a number");
+        return -1;
+       }
+
+    if (maxv < 0) maxv = 100;
+
+    if (*val > maxv)
+       {sprintf(buff, "may not be greater than %lld%%", maxv);
+        Eroute.Emsg("a2x", emsg, item, buff);
+        return -1;
+       }
+
+    if (minv < 0) minv = 0;
+
+    if (*val > maxv)
+       {sprintf(buff, "may not be less than %lld%%", minv);
+        Eroute.Emsg("a2x", emsg, item, buff);
+        return -1;
+       }
+
+    *val = -*val;
+    return 0;
+}
+
 /******************************************************************************/
 /*                                  a 2 s z                                   */
 /******************************************************************************/
 
-long long XrdOuca2x::a2sz(XrdSysError &Eroute, const char *emsg, const char *item,
+int XrdOuca2x::a2sz(XrdSysError &Eroute, const char *emsg, const char *item,
                                 long long *val, long long minv, long long maxv)
 {   long long qmult;
     char *eP, *fP = (char *)item + strlen(item) - 1;
@@ -224,8 +268,8 @@ int XrdOuca2x::Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
  return -1;
 }
 
-long long XrdOuca2x::Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
-                                               const char *etxt2, long long val)
+int XrdOuca2x::Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
+                                         const char *etxt2, long long val)
 {char buff[256];
  sprintf(buff, etxt2, val);
  Eroute.Emsg("a2x", etxt1, item, buff);
diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh
index d8f9b581890433fcb64cf319870b1c95161f760b..cc545dd0383bae5e35ba1b512e88fc3ab4356ee8 100644
--- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh
+++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh
@@ -20,19 +20,20 @@
 class XrdOuca2x
 {
 public:
-static int         a2i( XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1);
-static long long   a2ll(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1);
-static int         a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv);
-static int         a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv, int maxv);
-static long long   a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1);
-static int         a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1);
-static int         a2vp(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1);
+static int a2i( XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1);
+static int a2ll(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1);
+static int a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv);
+static int a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv, int maxv);
+static int a2sp(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1);
+static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1);
+static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1);
+static int a2vp(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1);
 
 private:
-static int       Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
-                                           const char *etxt2, int       val);
-static long long Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
-                                           const char *etxt2, long long val);
+static int Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
+                                     const char *etxt2, int       val);
+static int Emsg(XrdSysError &Eroute, const char *etxt1, const char *item,
+                                     const char *etxt2, long long val);
 };
 
 #endif
diff --git a/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile b/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile
index 0d22f9dce02a4d61994e693b1d78832d5fd9cabf..7574982095db81dad8eb4fa0d1da80c5edc20d35 100644
--- a/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile
+++ b/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile,v 1.22 2009/03/17 20:54:29 abh Exp $
+#              $Id: GNUmakefile,v 1.25 2010/01/06 09:33:41 abh Exp $
 
 #------------------------------------------------------------------------------#
 #                       C o m m o n   V a r i a b l e s                        #
@@ -122,7 +122,7 @@ $(OBJDIR)/XrdPosixPreload32.o:  XrdPosixPreload32.cc   XrdPosixLinkage.hh \
 
 $(OBJDIR)/XrdPosixXrootd.o:  XrdPosixXrootd.cc   XrdPosixXrootd.hh \
                              XrdClient.hh        XrdPosixExtern.hh \
-                             XrdSysHeaders.hh \
+                             XrdSysHeaders.hh    XrdPosixCallBack.hh \
                              XrdPosixOsDep.hh
 	@echo Compiling XrdPosixXrootd.cc 
 	$(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdPosixXrootd.o XrdPosixXrootd.cc
diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc
index 16ce7539bd4ef2c0b7a2390cb9cf597d922244a6..68151fe4d4db88619940bd30d5b044a43e675c38 100644
--- a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc
+++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc
@@ -438,7 +438,8 @@ FILE *XrdPosix_Fopen(const char *path, const char *mode)
 
 // Now open the file
 //
-   if ((fd = Xroot.Open(myPath, omode, 0, 1)) < 0) return 0;
+   if ((fd = Xroot.Open(myPath, omode | XrdPosixXrootd::isStream , 0)) < 0)
+      return 0;
 
 // First obtain a free stream
 //
diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixCallBack.hh b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixCallBack.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c62be482f80f2a18affb11c8b68d44224748946c
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixCallBack.hh
@@ -0,0 +1,41 @@
+#ifndef __POSIX_CALLBACK_HH__
+#define __POSIX_CALLBACK_HH__
+/******************************************************************************/
+/*                                                                            */
+/*                   X r d P o s i x C a l l B a c k . h h                    */
+/*                                                                            */
+/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+
+//         $Id$
+
+// This abstract class defines the callback interface for file open() calls.
+// It is passed when using the XrdPosixXrootd::Open() call. When passed, the
+// open request will be done in the background. When a callback object is
+// supplied, Open() will *always* return -1. However, if started successfully,
+// Open() will return -1 with errno set to EINPROGRESS. Otherwise, errno will
+// contain the reason the Open() request immediately failed. Upon completion,
+// the callback's Compete() method is invoked.  The Result parameter will either
+// be a non-negative file descriptor or -errno indicating that the Open()
+// failed. Note that the caller is responsible for deleting the callback object
+// after it has been invoked. Note that callbacks will be executed in a
+// separate thread unless open() is called with O_SYNC or maxThreads is zero.
+// WARNING: If O_SYNC or maxThreads is zero, then the callback must *not*
+//          issue any filesystem calls using the supplied file descriptor.
+//          Ignoring this will produce undetermined results including possible
+//          deadlock. Synchrnous callbacks are only meant to support private
+//          thread management.
+
+class XrdPosixCallBack
+{
+public:
+
+virtual void Complete(int Result) = 0;
+
+             XrdPosixCallBack() {}
+virtual     ~XrdPosixCallBack() {}
+};
+#endif
diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc
index f0d131e0e576bc7353f4d010d8b6406c219fe6e5..5ef5cd5dd2de7911dcae209058ca49f32468f617 100644
--- a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc
+++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc
@@ -2,7 +2,7 @@
 /*                                                                            */
 /*                     X r d P o s i x X r o o t d . c c                      */
 /*                                                                            */
-/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University  */
 /*                            All Rights Reserved                             */
 /*   Produced by Andrew Hanushevsky for Stanford University under contract    */
 /*              DE-AC02-76-SFO0515 with the Department of Energy              */
@@ -20,13 +20,15 @@ const char *XrdPosixXrootdCVSID = "$Id$";
 #include <sys/uio.h>
 
 #include "XrdClient/XrdClient.hh"
-#include "XrdClient/XrdClientEnv.hh"
 #include "XrdClient/XrdClientAdmin.hh"
+#include "XrdClient/XrdClientCallback.hh"
+#include "XrdClient/XrdClientEnv.hh"
 #include "XrdClient/XrdClientUrlInfo.hh"
 #include "XrdClient/XrdClientVector.hh"
 #include "XrdSys/XrdSysHeaders.hh"
 #include "XrdSys/XrdSysPlatform.hh"
 #include "XrdOuc/XrdOucString.hh"
+#include "XrdPosix/XrdPosixCallBack.hh"
 #include "XrdPosix/XrdPosixLinkage.hh"
 #include "XrdPosix/XrdPosixXrootd.hh"
 
@@ -99,7 +101,7 @@ private:
 /*                    X r d P o s i x F i l e   C l a s s                     */
 /******************************************************************************/
   
-class XrdPosixFile
+class XrdPosixFile : public XrdClientCallback
 {
 public:
 
@@ -123,11 +125,20 @@ long long  setOffset(long long offs)
 void         Lock() {myMutex.Lock();}
 void       UnLock() {myMutex.UnLock();}
 
-int               FD;
+void       OpenComplete(XrdClientAbs *clientP, void *cbArg, bool res)
+                       {if (cbDone) return;
+                        if (XrdPosixXrootd::OpenCB(res, this, cbArg)) cbDone=1;
+                           else delete this;
+                       }
 
 XrdClientStatInfo stat;
+XrdPosixCallBack *theCB;
+XrdPosixFile     *Next;
+int               FD;
+int               cbResult;
 
-           XrdPosixFile(int fd, const char *path);
+           XrdPosixFile(int fd, const char *path,
+                        XrdPosixCallBack *cbP=0, int cbsync=0);
           ~XrdPosixFile();
 
 private:
@@ -135,6 +146,7 @@ private:
 XrdSysMutex myMutex;
 long long   currOffset;
 int         doClose;
+int         cbDone;
 };
 
 
@@ -161,10 +173,18 @@ int            XrdPosixXrootd::highDir  = -1;
 int            XrdPosixXrootd::lastDir  = -1;
 int            XrdPosixXrootd::devNull  = -1;
 int            XrdPosixXrootd::pllOpen  =  0;
+int            XrdPosixXrootd::maxThreads= 0;
 int            XrdPosixXrootd::Debug    = -2;
 
 XrdPosixXrootd XrdPosixXrootd;
   
+/******************************************************************************/
+/*                     T h r e a d   I n t e r f a c e s                      */
+/******************************************************************************/
+
+void *XrdPosixXrootdCB(void *carg)
+     {XrdPosixXrootd::OpenCB(); return (void *)0;}
+  
 /******************************************************************************/
 /*                X r d P o s i x A d m i n N e w   C l a s s                 */
 /******************************************************************************/
@@ -294,15 +314,23 @@ dirent64 *XrdPosixDir::nextEntry(dirent64 *dp)
 /*                           C o n s t r u c t o r                            */
 /******************************************************************************/
 
-XrdPosixFile::XrdPosixFile(int fd, const char *path)
-             : FD(fd),
+XrdPosixFile::XrdPosixFile(int fd, const char *path, XrdPosixCallBack *cbP,
+                           int isSync)
+             : theCB(cbP),
+               Next(0),
+               FD(fd),
+               cbResult(0),
                currOffset(0),
-               doClose(0)
+               doClose(0),
+               cbDone(0)
 {
+   static int OneVal = 1;
+   XrdClientCallback *myCB = (cbP ? (XrdClientCallback *)this : 0);
+   void *cbArg = (isSync ? (void *)&OneVal : 0);
 
 // Allocate a new client object
 //
-   if (!(XClient = new XrdClient(path))) stat.size = 0;
+   if (!(XClient = new XrdClient(path, myCB, cbArg))) stat.size = 0;
 }
   
 /******************************************************************************/
@@ -327,7 +355,7 @@ XrdPosixFile::~XrdPosixFile()
 /*                           C o n s t r u c t o r                            */
 /******************************************************************************/
 
-XrdPosixXrootd::XrdPosixXrootd(int fdnum, int dirnum)
+XrdPosixXrootd::XrdPosixXrootd(int fdnum, int dirnum, int thrnum)
 {
    extern XrdPosixLinkage Xunix;
    static int initDone = 0;
@@ -348,6 +376,7 @@ XrdPosixXrootd::XrdPosixXrootd(int fdnum, int dirnum)
 // before any XrdClient library routines are called.
 //
    initEnv();
+   maxThreads = thrnum;
 
 // Compute size of table
 //
@@ -477,6 +506,38 @@ int XrdPosixXrootd::Closedir(DIR *dirp)
    return 0;
 }
 
+/******************************************************************************/
+/*                              e n d P o i n t                               */
+/******************************************************************************/
+  
+int XrdPosixXrootd::endPoint(int FD, char *Buff, int Blen)
+{
+   XrdPosixFile    *fp;
+   XrdClientUrlInfo fURL;
+
+// Find the file object
+//
+   if (!(fp = findFP(FD))) return 0;
+
+// Obtain the current url from the file
+//
+   fURL = fp->XClient->GetCurrentUrl();
+   fp->UnLock();
+
+// Make sure url is valid
+//
+   if (!fURL.IsValid()) return -ENOTCONN;
+
+// Format host and port number, check if result is too long
+//
+   if (snprintf(Buff, Blen, "%s:%d", fURL.Host.c_str(), fURL.Port) >= Blen)
+      return -ENAMETOOLONG;
+
+// All done
+//
+   return fURL.Port;
+}
+
 /******************************************************************************/
 /*                                 F s t a t                                  */
 /******************************************************************************/
@@ -639,10 +700,11 @@ int XrdPosixXrootd::Mkdir(const char *path, mode_t mode)
 /*                                  O p e n                                   */
 /******************************************************************************/
   
-int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream)
+int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode,
+                         XrdPosixCallBack *cbP)
 {
    XrdPosixFile *fp;
-   int retc = 0, fd, XOflags, XMode;
+   int isSync, retc = 0, fd, XOflags, XMode;
 
 // Translate option bits to the appropraite values. Always 
 // make directory path for new file.
@@ -658,12 +720,13 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream)
 // Obtain a new filedscriptor from the system. Use the fd to track the file.
 //
    if ((fd = dup(devNull)) < 0) return -1;
-   if (Stream && fd > 255) {close(fd); errno = EMFILE; return -1;}
+   if (oflags & isStream && fd > 255) {close(fd); errno = EMFILE; return -1;}
+   isSync = (maxThreads == 0) || (oflags & O_SYNC);
 
 // Allocate the new file object
 //
    myMutex.Lock();
-   if (fd > lastFD || !(fp = new XrdPosixFile(fd, path)))
+   if (fd > lastFD || !(fp = new XrdPosixFile(fd, path, cbP, isSync)))
       {errno = EMFILE; myMutex.UnLock(); return -1;}
    myFiles[fd] = fp;
    if (fd > highFD) highFD = fd;
@@ -675,8 +738,8 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream)
 
 // Open the file
 //
-   if (!fp->XClient->Open(XMode, XOflags, pllOpen)
-   || (fp->XClient->LastServerResp()->status) != kXR_ok)
+   if (!fp->XClient->Open(XMode, XOflags, (cbP ? 1 : pllOpen))
+   || (!cbP && fp->XClient->LastServerResp()->status) != kXR_ok)
       {retc = Fault(fp, 0);
        myMutex.Lock();
        myFiles[fd] = 0;
@@ -686,6 +749,13 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream)
        return -1;
       }
 
+// If this is a callback open then just return EINPROGRESS
+//
+   if (cbP)
+      {errno = EINPROGRESS;
+       return -1;
+      }
+
 // Get the file size
 //
    fp->isOpen();
@@ -696,6 +766,95 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream)
    return fd;
 }
 
+/******************************************************************************/
+/*                                O p e n C B                                 */
+/******************************************************************************/
+  
+int XrdPosixXrootd::OpenCB(int res, XrdPosixFile *fp, void *cbArg)
+{
+   int retc, ec;
+
+// Diagnose any error
+//
+   if (!res || (fp->XClient->LastServerResp()->status) != kXR_ok)
+      {ec = Fault(fp, 0); retc = -1;
+       myMutex.Lock();
+       myFiles[fp->FD] = 0;
+       myMutex.UnLock();
+      } else {
+       fp->isOpen();
+       fp->XClient->Stat(&fp->stat);
+       retc = fp->FD;
+       ec = 0;
+      }
+
+// Check if this is a sync callback and, if so, do it now. Otherwise, try
+// via the callback manager.
+//
+   if (retc < 0 || cbArg)
+      {errno = ec;
+       fp->theCB->Complete(retc < 0 ? -ec : retc);
+      } else OpenCB(fp, retc, ec);
+   return retc != -1;
+}
+
+/******************************************************************************/
+
+int XrdPosixXrootd::OpenCB(XrdPosixFile *fp, int rCode, int eCode)
+{
+   static XrdSysMutex     cbMutex;
+   static XrdSysSemaphore cbReady(0);
+   static XrdPosixFile   *First = 0, *Last = 0;
+   static int Waiting = 0, numThreads = 0;
+          XrdPosixFile *cbFP;
+          int rc = 0;
+
+// If this is a feeder thread, look for some work
+//
+   if (!fp)
+   do {cbMutex.Lock();
+       if (!First && numThreads > 1)
+          {numThreads--; cbMutex.UnLock(); return 0;}
+       while(!(cbFP = First))
+            {Waiting = 1;
+             cbMutex.UnLock(); cbReady.Wait(); cbMutex.Lock();
+             Waiting = 0;
+            }
+       if (!(First = cbFP->Next)) Last = 0;
+       cbMutex.UnLock();
+       if (cbFP->cbResult < 0) errno = cbFP->cbResult;
+       cbFP->theCB->Complete(cbFP->cbResult);
+      } while(1);
+
+// Establish the final result
+//
+   fp->cbResult = (rCode < 0 ? -eCode : rCode);
+
+// Lock our data structure and queue this element
+//
+   cbMutex.Lock();
+   if (Last) fp->Next = Last;
+      else   First    = fp;
+   Last = fp;
+
+// See if we should start a thread
+//
+   if (!Waiting && numThreads < maxThreads)
+      {pthread_t tid;
+       if ((rc = XrdSysThread::Run(&tid, XrdPosixXrootdCB, (void *)0,
+                                  0, "Callback thread")))
+          cerr <<"XrdPosix: Unable to create callback thread; "
+               <<strerror(rc) <<endl;
+          else numThreads++;
+      }
+
+// All done
+//
+   cbReady.Post();
+   cbMutex.UnLock();
+   return rc;
+}
+
 /******************************************************************************/
 /*                               O p e n d i r                                */
 /******************************************************************************/
@@ -1249,6 +1408,7 @@ void XrdPosixXrootd::initEnv()
           {"XRDPOSIX_RCUP",        NAME_REMUSEDCACHEBLKS,     0},
           {"XRDPOSIX_RDTTL",       NAME_LBSERVERCONN_TTL,     0},
           {"XRDPOSIX_RTO",         NAME_REQUESTTIMEOUT,       0},
+          {"XRDPOSIX_TTO",         NAME_TRANSACTIONTIMEOUT,   0},
           {"XRDPSOIX_PSPC",        NAME_MULTISTREAMCNT,       0},
           {"XRDPSOIX_CTO",         NAME_CONNECTTIMEOUT,       0},
           {"XRDPSOIX_CRDELAY",     NAME_RECONNECTWAIT,        0},
@@ -1387,7 +1547,7 @@ int XrdPosixXrootd::Fault(XrdPosixFile *fp, int complete)
           cerr <<"XrdPosix: " <<etext <<endl;
       }
 
-   if (!complete) return rc;
+   if (!complete) return ecode;
    fp->UnLock();
    errno = ecode;
    return rc;
diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh
index 05991cfb5e2dfb03153699fd9c7afb95839a96ec..1cb14f97c60f65a1a15c216e49939ed180b2673f 100644
--- a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh
+++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh
@@ -4,7 +4,7 @@
 /*                                                                            */
 /*                        X r d P o s i x X r o o t d                         */
 /*                                                                            */
-/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University  */
 /*                            All Rights Reserved                             */
 /*   Produced by Andrew Hanushevsky for Stanford University under contract    */
 /*              DE-AC02-76-SFO0515 with the Department of Energy              */
@@ -29,6 +29,7 @@
 #include "XrdPosix/XrdPosixOsDep.hh"
 #include "XrdSys/XrdSysPthread.hh"
 
+class XrdPosixCallBack;
 class XrdPosixFile;
 class XrdPosixDir;
 
@@ -55,7 +56,10 @@ static off_t   Lseek(int fildes, off_t offset, int whence);
 
 static int     Mkdir(const char *path, mode_t mode);
 
-static int     Open(const char *path, int oflag, mode_t mode=0, int Stream=0);
+static const int isStream = 0x40000000; // Internal for Open oflag
+
+static int     Open(const char *path, int oflag, mode_t mode=0,
+                    XrdPosixCallBack *cbP=0);
 
 static DIR*    Opendir(const char *path);
   
@@ -103,6 +107,8 @@ static ssize_t Writev(int fildes, const struct iovec *iov, int iovcnt);
 //
 static int     Access(const char *path, int amode);
 
+static int     endPoint(int FD, char *Buff, int Blen);
+
 static bool    isXrootdDir(DIR *dirp);
 
 static int     mapError(int rc);
@@ -110,6 +116,9 @@ static int     mapError(int rc);
 static
 inline bool    myFD(int fd) {return fd <= highFD && myFiles && myFiles[fd];}
 
+static int     OpenCB(int res, XrdPosixFile *fp, void *cbArg);
+static int     OpenCB(XrdPosixFile *fp=0, int rC=0, int eC=0);
+
 static long long QueryOpaque(const char*, char*, int);
 
 static void    setDebug(int val);
@@ -120,7 +129,7 @@ static void    setEnv(const char *var, long val);
 
 static int     Debug;
 
-               XrdPosixXrootd(int maxfd=255, int maxdir=255);
+               XrdPosixXrootd(int maxfd=255, int maxdir=255, int maxthr=255);
               ~XrdPosixXrootd();
 
 private:
@@ -143,5 +152,6 @@ static int            lastDir;
 static int            highDir;
 static int            devNull;
 static int            pllOpen;
+static int            maxThreads;
 };
 #endif
diff --git a/net/xrootd/src/xrootd/src/XrdSec/Makefile.am b/net/xrootd/src/xrootd/src/XrdSec/Makefile.am
index 590593c26920f5eea78efff51d6fccbbe428ef9c..9d5ffafaeb6a450eb5d449fe6dd8b0f2b1a00323 100644
--- a/net/xrootd/src/xrootd/src/XrdSec/Makefile.am
+++ b/net/xrootd/src/xrootd/src/XrdSec/Makefile.am
@@ -4,12 +4,12 @@
 ##
 ## Initial version: 1.8.2005
 ##
-## Version info: $Id: Makefile.am,v 1.7 2008/12/11 02:48:46 abh Exp $
-## Checked in by $Author: abh $
+## Version info: $Id: Makefile.am,v 1.8 2010/01/12 15:04:06 ganis Exp $
+## Checked in by $Author: ganis $
 #######################################################################
 xrootdsecincdir = $(includedir)/xrootd/XrdSec
 
-xrootdsecinc_HEADERS = XrdSecInterface.hh XrdSecEntity.hh
+xrootdsecinc_HEADERS = XrdSecInterface.hh XrdSecEntity.hh XrdSecTLayer.hh
 
 lib_LTLIBRARIES = libXrdSec.la
 
diff --git a/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh b/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh
index 2e0e0f4157b9fc63ca2dfddd6d4236e2282b9d75..86e1adb68916f96e4eae7f7ae96e561268c35e22 100644
--- a/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh
@@ -26,7 +26,7 @@
 
 #define DEBUG(y) if (QTRACE(Debug)) \
                     {SecTrace->Beg(epname); cerr <<y; SecTrace->End();}
-#define EPNAME(x) const char *epname = x;
+#define EPNAME(x) static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh b/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh
index ad5cdf36f1ad0d31aba5d94756e11b4283d5a29e..a666e01f7c225beec553e258b4adf36a630d0120 100644
--- a/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh
@@ -20,7 +20,7 @@
                                        cerr <<y; gsiTrace->End();}}
 #define TRACE(act,x) if (QTRACE(act)) PRINT(x)
 #define DEBUG(y)     TRACE(Debug,y)
-#define EPNAME(x)    const char *epname = x;
+#define EPNAME(x)    static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile b/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile
index ff781e24e3c5e6c112f46911d28a6087a0db7aa3..0a10f3cf84a015c96aa6e7673fa8965023956675 100644
--- a/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile
+++ b/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile
@@ -1,4 +1,4 @@
-#              $Id: GNUmakefile,v 1.14 2008/09/24 17:01:15 ganis Exp $
+#              $Id: GNUmakefile,v 1.15 2009/12/02 13:04:28 ganis Exp $
 
 #------------------------------------------------------------------------------#
 #                       C o m m o n   V a r i a b l e s                        #
diff --git a/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc b/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc
index 1de1352723ab4ca076b1c8b5dd036ec683e6c681..65410e130419c984954445e3e4cf8f1fbad52c77 100644
--- a/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc
+++ b/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc
@@ -42,8 +42,6 @@ extern "C" {
 #include "XrdOuc/XrdOucTokenizer.hh"
 #include "XrdSec/XrdSecInterface.hh"
 #include "XrdSys/XrdSysPriv.hh"
-#include "XrdOuc/XrdOucString.hh"
-#include "XrdSut/XrdSutAux.hh"
   
 /******************************************************************************/
 /*                               D e f i n e s                                */
@@ -60,7 +58,7 @@ extern "C" {
 
 #define XrdSecMAXPATHLEN      4096
 
-#define CLDBG(x) if (options & XrdSecDEBUG) cerr <<"Seckrb5: " <<x <<endl;
+#define CLDBG(x) if (client_options & XrdSecDEBUG) cerr <<"Seckrb5: " <<x <<endl;
 #define CLPRT(x) cerr <<"Seckrb5: " <<x <<endl;
 
 typedef  krb5_error_code krb_rc;
@@ -86,6 +84,7 @@ static  char              *getPrincipal() {return Principal;}
 static  int                Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0);
 
 static  void               setOpts(int opts) {options = opts;}
+static  void               setClientOpts(int opts) {client_options = opts;}
 static  void               setParms(char *param) {Parms = param;}
 static  void               setExpFile(char *expfile)
                                      {if (expfile)
@@ -107,6 +106,7 @@ static  void               setExpFile(char *expfile)
                            Entity.name = CName;
                            Step = 0;
                            AuthContext = 0;
+                           AuthClientContext = 0;
                            Ticket = 0;
                            Creds = 0;
                           }
@@ -120,10 +120,14 @@ private:
 static int Fatal(XrdOucErrInfo *erp,int rc,const char *msg1,char *KP=0,int krc=0);
 static int get_krbCreds(char *KP, krb5_creds **krb_creds);
 
-static XrdSysMutex        krbContext;    // Client or server
-static int                options;       // Client or server
-static krb5_context       krb_context;   // Client or server
-static krb5_ccache        krb_ccache;    // Client or server
+static XrdSysMutex        krbContext;    // Server
+static XrdSysMutex        krbClientContext;// Client
+static int                options;       // Server
+static int                client_options;// Client
+static krb5_context       krb_context;   // Server
+static krb5_context       krb_client_context;   // Client 
+static krb5_ccache        krb_client_ccache;    // Client 
+static krb5_ccache        krb_ccache;    // Server
 static krb5_keytab        krb_keytab;    // Server
 static uid_t              krb_kt_uid;// Server
 static gid_t              krb_kt_gid;// Server
@@ -142,6 +146,7 @@ char                      CName[256];    // Kerberos limit
 char                     *Service;       // Target principal for client
 char                      Step;          // Indicates at which step we are
 krb5_auth_context         AuthContext;   // Authetication context
+krb5_auth_context         AuthClientContext;   // Authetication context
 krb5_ticket              *Ticket;        // Ticket associated to client authentication
 krb5_creds               *Creds;         // Client: credentials
 };
@@ -150,11 +155,15 @@ krb5_creds               *Creds;         // Client: credentials
 /*                           S t a t i c   D a t a                            */
 /******************************************************************************/
   
-XrdSysMutex         XrdSecProtocolkrb5::krbContext;        // Client or server
-
-int                 XrdSecProtocolkrb5::options = 0;       // Client or Server
-krb5_context        XrdSecProtocolkrb5::krb_context;       // Client or server
-krb5_ccache         XrdSecProtocolkrb5::krb_ccache;        // Client or server
+XrdSysMutex         XrdSecProtocolkrb5::krbContext;        // Server
+XrdSysMutex         XrdSecProtocolkrb5::krbClientContext;  // Client
+
+int                 XrdSecProtocolkrb5::client_options = 0;// Client
+int                 XrdSecProtocolkrb5::options = 0;       // Server
+krb5_context        XrdSecProtocolkrb5::krb_context;       // Server
+krb5_context        XrdSecProtocolkrb5::krb_client_context;       // Client 
+krb5_ccache         XrdSecProtocolkrb5::krb_client_ccache; // Client
+krb5_ccache         XrdSecProtocolkrb5::krb_ccache;        // Server
 krb5_keytab         XrdSecProtocolkrb5::krb_keytab = NULL; // Server
 uid_t               XrdSecProtocolkrb5::krb_kt_uid = 0;    // Server
 gid_t               XrdSecProtocolkrb5::krb_kt_gid = 0;    // Server
@@ -175,6 +184,7 @@ void XrdSecProtocolkrb5::Delete()
      if (Creds)       krb5_free_creds(krb_context, Creds);
      if (Ticket)      krb5_free_ticket(krb_context, Ticket);
      if (AuthContext) krb5_auth_con_free(krb_context, AuthContext);
+     if (AuthClientContext) krb5_auth_con_free(krb_client_context, AuthClientContext);
      if (Entity.host) free(Entity.host);
      if (Service)     free(Service);
      delete this;
@@ -191,7 +201,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
    int bsz;
    krb_rc rc;
    krb5_data         outbuf;
-
+   CLDBG("getCredentials");
 // Supply null credentials if so needed for this protocol
 //
    if (!Service)
@@ -206,48 +216,55 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
       sprintf(ccname, "KRB5CCNAME=FILE:/tmp/krb5cc_%d", geteuid());
       putenv(strdup(ccname));
    }
-
+   CLDBG(getenv("KRB5CCNAME"));
+	 
 // Initialize the context and get the cache default.
 //
-   if ((rc = krb5_init_context(&krb_context)))
+   if ((rc = krb5_init_context(&krb_client_context)))
       {Fatal(error, ENOPROTOOPT, "Kerberos initialization failed", Service, rc);
        return (XrdSecCredentials *)0;
       }
 
+   CLDBG("init context");
 // Obtain the default cache location
 //
-   if ((rc = krb5_cc_default(krb_context, &krb_ccache)))
+   if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache)))
       {Fatal(error, ENOPROTOOPT, "Unable to locate cred cache", Service, rc);
        return (XrdSecCredentials *)0;
       }
 
+   CLDBG("cc cache default");
 // Check if the server asked for a forwardable ticket
 //
    char *pfwd = 0;
    if ((pfwd = (char *) strstr(Service,",fwd")))
       {
-         options |= XrdSecEXPTKN;
+         client_options |= XrdSecEXPTKN;
          *pfwd = 0;
       }
 
 // Clear outgoing ticket and lock the kerberos context
 //
    outbuf.length = 0; outbuf.data = 0;
-   krbContext.Lock();
+
+   CLDBG("context lock");
+   krbClientContext.Lock();
+   CLDBG("context locked");
 
 // If this is not the first call, we are asked to send over a delegated ticket:
 // we must create it first
 // we save it into a file and return signalling the end of the hand-shake
 //
+
    if (Step > 0)
       {if ((rc = get_krbFwdCreds(Service, &outbuf)))
-          {krbContext.UnLock();
+          {krbClientContext.UnLock();
            Fatal(error, ESRCH, "Unable to get forwarded credentials", Service, rc);
            return (XrdSecCredentials *)0;
           } else
             {bsz = XrdSecPROTOIDLEN+outbuf.length;
              if (!(buff = (char *)malloc(bsz)))
-                {krbContext.UnLock();
+                {krbClientContext.UnLock();
                  Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service);
                  return (XrdSecCredentials *)0;
                 }
@@ -256,7 +273,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
                             (const void *)outbuf.data, (size_t)outbuf.length);
              CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service);
              if (outbuf.data)  free(outbuf.data);
-             krbContext.UnLock();
+             krbClientContext.UnLock();
              return new XrdSecCredentials(buff, bsz);
             }
       }
@@ -267,14 +284,14 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
 
 // Get a service ticket for this principal
 //
-   const char *reinitcmd = (options & XrdSecEXPTKN) ? "kinit -f" : "kinit";
+   const char *reinitcmd = (client_options & XrdSecEXPTKN) ? "kinit -f" : "kinit";
    bool notdone = 1;
    bool reinitdone = 0;
    while (notdone)
       {if ((rc = (krb_rc)get_krbCreds(Service, &Creds)))
-          { if (!(options & XrdSecINITTKN) || reinitdone)
-               {krbContext.UnLock();
-                const char *m = (!(options & XrdSecINITTKN)) ?
+          { if (!(client_options & XrdSecINITTKN) || reinitdone)
+               {krbClientContext.UnLock();
+                const char *m = (!(client_options & XrdSecINITTKN)) ?
                                 "No or invalid credentials" : "Unable to get credentials";
                 Fatal(error, ESRCH, m, Service, rc);
                 return (XrdSecCredentials *)0;
@@ -287,10 +304,10 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
                        continue;
                       }
           }
-       if (options & XrdSecEXPTKN)
+       if (client_options & XrdSecEXPTKN)
           {// Make sure the ticket is forwardable
            if (!(Creds->ticket_flags & TKT_FLG_FORWARDABLE))
-              { if (options & XrdSecINITTKN)
+              { if (client_options & XrdSecINITTKN)
                    { // Need to re-init
                     CLPRT("Existing ticket is not forwardable: re-init ");
                     rc = system(reinitcmd);
@@ -299,7 +316,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
                     reinitdone = 1;
                     continue;
                    } else {
-                    krbContext.UnLock();
+                    krbClientContext.UnLock();
                     Fatal(error, ESRCH, "Existing ticket is not forwardable: cannot continue",
                                         Service, rc);
                     return (XrdSecCredentials *)0;
@@ -312,15 +329,15 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
 
 // Set the RET_TIME flag in the authentication context 
 //
-   if ((rc = krb5_auth_con_init(krb_context, &AuthContext)))
-      {krbContext.UnLock();
+   if ((rc = krb5_auth_con_init(krb_client_context, &AuthClientContext)))
+      {krbClientContext.UnLock();
        Fatal(error, ESRCH, "Unable to init a new auth context", Service, rc);
        return (XrdSecCredentials *)0;
       }
 
 // Generate a kerberos-style authentication message
 //
-   rc = krb5_mk_req_extended(krb_context, &AuthContext,
+   rc = krb5_mk_req_extended(krb_client_context, &AuthClientContext,
              AP_OPTS_USE_SESSION_KEY,(krb5_data *)0, Creds,&outbuf);
 
 // Check if all succeeded. If so, copy the ticket into the buffer. We wish
@@ -330,7 +347,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
    if (!rc)
       {bsz = XrdSecPROTOIDLEN+outbuf.length;
        if (!(buff = (char *)malloc(bsz)))
-          {krbContext.UnLock();
+          {krbClientContext.UnLock();
            Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service);
            return (XrdSecCredentials *)0;
           }
@@ -339,14 +356,14 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
               (const void *)outbuf.data, (size_t)outbuf.length);
        CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service);
        if (outbuf.data)  free(outbuf.data);
-       krbContext.UnLock();
+       krbClientContext.UnLock();
        return new XrdSecCredentials(buff, bsz);
       }
 
 // Diagnose the failure
 //
    if (outbuf.data)  free(outbuf.data);
-   krbContext.UnLock();
+   krbClientContext.UnLock();
    Fatal(error, EACCES, "Unable to get credentials", Service, rc);
    return (XrdSecCredentials *)0;
 }
@@ -386,16 +403,28 @@ int XrdSecProtocolkrb5::Authenticate(XrdSecCredentials *cred,
        return -1;
       }
 
+   CLDBG("protocol check");
+
+   char printit[4096];
+   sprintf(printit,"Step is %d",Step);
+   CLDBG(printit);
 // If this is not the first call the buffer contains a forwarded token:
 // we save it into a file and return signalling the end of the hand-shake
 //
    if (Step > 0)
       {if ((rc = exp_krbTkn(cred, error)))
           iferror = (char *)"Unable to export the token to file";
-       if (rc && iferror)
+       if (rc && iferror) {
+          krbContext.UnLock();
           return Fatal(error, EINVAL, iferror, Principal, rc);
+       }
+       krbContext.UnLock();
+
        return 0;
       }
+   
+   krbContext.UnLock();
+   CLDBG("protocol check");
 
 // Increment the step
 //
@@ -407,13 +436,18 @@ int XrdSecProtocolkrb5::Authenticate(XrdSecCredentials *cred,
 
 // Create a kerberos style ticket and obtain the kerberos mutex
 //
+
+   CLDBG("Context Lock");
+
    inbuf.length = cred->size -XrdSecPROTOIDLEN;
    inbuf.data   = &cred->buffer[XrdSecPROTOIDLEN];
+
    krbContext.Lock();
 
 // Check if whether the IP address in the credentials must match that of
 // the incomming host.
 //
+   CLDBG("Context Locked");
    if (!(XrdSecProtocolkrb5::options & XrdSecNOIPCHK))
       {struct sockaddr_in *ip = (struct sockaddr_in *)&hostaddr;
       // The above is a hack but K5 does it this way
@@ -487,18 +521,28 @@ int XrdSecProtocolkrb5::Init(XrdOucErrInfo *erp, char *KP, char *kfn)
 
 // Create a kerberos context. There is one such context per protocol object.
 //
+
+// If we have no principal then this is a client-side call
+//
+   if (!KP) {
+     if ((rc = krb5_init_context(&krb_client_context)))
+       return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
+
+     // Obtain the default cache location
+     //
+     if ((rc = krb5_cc_default(krb_context, &krb_client_ccache)))
+        return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
+     return 0;
+   }
+
    if ((rc = krb5_init_context(&krb_context)))
       return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
 
 // Obtain the default cache location
 //
-   if ((rc = krb5_cc_default(krb_context, &krb_ccache)))
+   if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache)))
       return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
 
-// If we have no principal then this is a client-side call
-//
-   if (!KP) return 0;
-
 // Try to resolve the keyfile name
 //
    if (kfn && *kfn)
@@ -587,7 +631,7 @@ int XrdSecProtocolkrb5::Fatal(XrdOucErrInfo *erp, int rc, const char *msg,
 /*                          g e t _ k r b C r e d s                           */
 /******************************************************************************/
 
-// Warning! The krbContext lock must be held prior to calling this routine
+// Warning! The krbClientContext lock must be held prior to calling this routine
 
 int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds)
 {
@@ -601,30 +645,30 @@ int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds)
 
 // Setup the "principal/instance@realm"
 //
-   if ((rc = krb5_parse_name(krb_context,KP,&the_principal)))
+   if ((rc = krb5_parse_name(krb_client_context,KP,&the_principal)))
       {CLDBG("get_krbCreds: Cannot parse service name;" <<krb_etxt(rc));
        return rc;
       }
 
 // Copy the current target principal into the credentials
 //
-   if ((rc = krb5_copy_principal(krb_context, the_principal, &mycreds.server)))
+   if ((rc = krb5_copy_principal(krb_client_context, the_principal, &mycreds.server)))
       {CLDBG("get_krbCreds: err copying principal to creds; " <<krb_etxt(rc));
        return rc;
       }
 
 // Get our principal name
 //
-   if ((rc = krb5_cc_get_principal(krb_context, krb_ccache, &mycreds.client)))
-      {krb5_free_cred_contents(krb_context, &mycreds);
+   if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &mycreds.client)))
+      {krb5_free_cred_contents(krb_client_context, &mycreds);
        CLDBG("get_krbCreds: err copying client name to creds; " <<krb_etxt(rc));
        return rc;
       }
 
 // Now get the credentials (free our local info)
 //
-   rc = krb5_get_credentials(krb_context, 0, krb_ccache, &mycreds,  krb_creds);
-   krb5_free_cred_contents(krb_context, &mycreds);
+   rc = krb5_get_credentials(krb_client_context, 0, krb_client_ccache, &mycreds,  krb_creds);
+   krb5_free_cred_contents(krb_client_context, &mycreds);
 
 // Check if all went well
 //
@@ -643,21 +687,21 @@ int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata)
 
 // Fill-in our principal
 //
-   if ((rc = krb5_cc_get_principal(krb_context, krb_ccache, &client)))
+   if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &client)))
       {CLDBG("get_krbFwdCreds: err filling client principal; " <<krb_etxt(rc));
        return rc;
       }
 
 // Fill-in target (service) principal
 //
-   if ((rc = krb5_parse_name(krb_context, KP, &server)))
+   if ((rc = krb5_parse_name(krb_client_context, KP, &server)))
       {CLDBG("get_krbFwdCreds: Cannot parse service principal;" <<krb_etxt(rc));
        return rc;
       }
 
 // Set the timestamp in the authentication context
 //
-   if ((rc = krb5_auth_con_setflags(krb_context, AuthContext,
+   if ((rc = krb5_auth_con_setflags(krb_client_context, AuthClientContext,
                                    KRB5_AUTH_CONTEXT_RET_TIME)))
       {CLDBG("Unable to set KRB5_AUTH_CONTEXT_RET_TIME"
                            " in the authentication context" << krb_etxt(rc));
@@ -666,8 +710,8 @@ int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata)
 
 // Acquire a TGT for use at a remote host system
 //
-   if ((rc = krb5_fwd_tgt_creds(krb_context, AuthContext, 0 /*host*/,
-                                     client, server, krb_ccache, true,
+   if ((rc = krb5_fwd_tgt_creds(krb_client_context, AuthClientContext, 0 /*host*/,
+                                     client, server, krb_client_ccache, true,
                                      outdata)))
       {CLDBG("get_krbFwdCreds: err getting forwarded ticket;" <<krb_etxt(rc));
        return rc;
@@ -688,20 +732,43 @@ int XrdSecProtocolkrb5::exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp)
 
 // Create the cache filename, expanding the keywords, if needed
 //
-    XrdOucString ccfn(XrdSecProtocolkrb5::ExpFile);
-
-// Resolve place-holders, if any
-//
-    if (XrdSutResolve(ccfn, Entity.host, Entity.vorg, Entity.grps, Entity.name) != 0)
-       return rc;
+    char ccfile[XrdSecMAXPATHLEN];
+    strcpy(ccfile, XrdSecProtocolkrb5::ExpFile);
+    int nlen = strlen(ccfile);
+    char *pusr = (char *) strstr(&ccfile[0], "<user>");
+    if (pusr)
+       {int ln = strlen(CName);
+        if (ln != 6) {
+           // Adjust the space
+           int lm = strlen(ccfile) - (int)(pusr + 6 - &ccfile[0]); 
+           memmove(pusr+ln, pusr+6, lm);
+        }
+        // Copy the name
+        memcpy(pusr, CName, ln);
+        // Adjust the length
+        nlen += (ln - 6);
+        }
+    char *puid = (char *) strstr(&ccfile[0], "<uid>");
     struct passwd *pw = getpwnam(CName);
-    if (!pw)
-       return rc;
-    if (ccfn.find("<uid>") != STR_NPOS)
-       {XrdOucString suid; if (pw) suid += (int) pw->pw_uid;
-        ccfn.replace("<uid>", suid.c_str());
-       }
-    char *ccfile = (char *) ccfn.c_str();
+    if (puid)
+       {char cuid[20] = {0};
+        if (pw)
+           sprintf(cuid, "%d", pw->pw_uid);
+        int ln = strlen(cuid);
+        if (ln != 5) {
+           // Adjust the space
+           int lm = strlen(ccfile) - (int)(puid + 5 - &ccfile[0]); 
+           memmove(puid+ln, pusr+5, lm);
+        }
+        // Copy the name
+        memcpy(puid, cuid, ln);
+        // Adjust the length
+        nlen += (ln - 5);
+        }
+
+// Terminate to the new length
+//
+    ccfile[nlen] = 0;
 
 // Point the received creds
 //
@@ -784,18 +851,26 @@ char  *XrdSecProtocolkrb5Init(const char     mode,
    char parmbuff[1024];
    XrdOucTokenizer inParms(parmbuff);
    int options = XrdSecNOIPCHK;
+   static bool serverinitialized = false;
 
 // For client-side one-time initialization, we only need to set debug flag and
 // initialize the kerberos context and cache location.
 //
-   if (mode == 'c')
-      {int opts = 0;
+   if ((mode == 'c') || (serverinitialized))
+      {
+       CLPRT("Initializing the client");
+       int opts = 0;
        if (getenv("XrdSecDEBUG")) opts |= XrdSecDEBUG;
        if (getenv("XrdSecKRB5INITTKN")) opts |= XrdSecINITTKN;
-       XrdSecProtocolkrb5::setOpts(opts);
+       XrdSecProtocolkrb5::setClientOpts(opts);
        return (XrdSecProtocolkrb5::Init(erp) ? (char *)0 : (char *)"");
       }
 
+   if (!serverinitialized) {
+     serverinitialized = true;
+   }
+
+   CLPRT("Initializing the server");
 // Duplicate the parms
 //
    if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff));
@@ -866,6 +941,7 @@ char  *XrdSecProtocolkrb5Init(const char     mode,
 
 // Now initialize the server
 //
+   options |= XrdSecDEBUG;
    XrdSecProtocolkrb5::setExpFile(ExpFile);
    XrdSecProtocolkrb5::setOpts(options);
    if (!XrdSecProtocolkrb5::Init(erp, KPrincipal, Keytab))
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/GNUmakefile b/net/xrootd/src/xrootd/src/XrdSecssl/GNUmakefile
new file mode 100644
index 0000000000000000000000000000000000000000..093a235b9bb7e4a8cd9f2a3ca7880978fd0f396b
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/GNUmakefile
@@ -0,0 +1,150 @@
+#              $Id: GNUmakefile,v 1.3 2010/01/29 15:50:18 ganis Exp $
+
+#------------------------------------------------------------------------------#
+#                       C o m m o n   V a r i a b l e s                        #
+#------------------------------------------------------------------------------#
+  
+include ../GNUmake.env
+
+MORELIBS = -L$(LIBDIR) -lXrdSec $(LIBSSL) $(LIBDIR)/libsslGridSite.a \
+           $(LIBDIR)/libXrdOuc.a $(LIBDIR)/libXrdSys.a $(LIBDIR)/libXrdNet.a $(LIBXML)
+
+LIBDEPS = $(LIBDIR)/libsslGridSite.a $(LIBDIR)/libXrdOuc.a $(LIBDIR)/libXrdSys.a $(LIBDIR)/libXrdNet.a
+
+NCXXFLAGS = $(INCSSL) $(SSLEXTRACFLAGS) -DOPENSSL_NO_KRB5 $(subst deprecated,deprecated-declarations,$(CFLAGS)) 
+NCFLAGS = $(INCXML) $(INCSSL) $(SSLEXTRACFLAGS) -DOPENSSL_NO_KRB5 -U_FORTIFY_SOURCE $(subst deprecated,deprecated-declarations,$(CFLAGS)) 
+ 
+#------------------------------------------------------------------------------#
+#                             C o m p o n e n t s                              #
+#------------------------------------------------------------------------------#
+
+CCOMPILE = gcc
+
+SOURCES = \
+        XrdSecProtocolssl.cc \
+        XrdSecProtocolsslTest.cc \
+        libsslGridSite/grst_asn1.c \
+        libsslGridSite/grst_err.c \
+        libsslGridSite/grst_gacl.c \
+        libsslGridSite/grst_http.c \
+        libsslGridSite/grst_verifycallback.c \
+        libsslGridSite/grst_x509.c \
+        libsslGridSite/grst_xacml.c \
+
+OBJECTS = \
+        $(OBJDIR)/XrdSecProtocolssl.o
+
+OBJECTA = \
+        $(OBJDIR)/XrdSecProtocolsslTest.o
+
+OBJGRST = \
+        $(OBJDIR)/grst_asn1.o \
+        $(OBJDIR)/grst_err.o \
+        $(OBJDIR)/grst_gacl.o \
+        $(OBJDIR)/grst_http.o \
+        $(OBJDIR)/grst_verifycallback.o \
+        $(OBJDIR)/grst_x509.o \
+        $(OBJDIR)/grst_xacml.o\
+
+LIBGRST = $(LIBDIR)/libsslGridSite.a
+LIBRARY = $(LIBDIR)/libXrdSecssl.$(TYPESHLIB)
+
+SECSSLTEST = $(BINDIR)/xrdsecssltest
+
+TARGETS = $(LIBGRST) $(LIBRARY) $(SECSSLTEST)
+
+#------------------------------------------------------------------------------#
+#                           S e a r c h   P a t h s                            #
+#------------------------------------------------------------------------------#
+
+vpath XrdOuc% ../XrdOuc
+vpath XrdSec% ../XrdSec
+vpath XrdSys% ../XrdSys
+vpath XrdNet% ../XrdNet
+
+#------------------------------------------------------------------------------#
+#                          I n i t i a l   R u l e s                           #
+#------------------------------------------------------------------------------#
+ 
+include ../GNUmake.options
+
+anything: $(TARGETS)
+	@echo Make XrdSecssl done.
+
+#------------------------------------------------------------------------------#
+#                           D e p e n d e n c i e s                            #
+#------------------------------------------------------------------------------#
+
+$(LIBRARY): $(OBJECTS) $(LIBDEPS)
+	@echo Creating shared library $(LIBRARY) 
+	$(ECHO)$(CC) $(OBJECTS) $(LDSO) $(MORELIBS) $(LIBS) -o $(LIBRARY);
+
+$(LIBGRST): $(OBJGRST)
+	@echo Creating archive $(LIBGRST)
+	$(ECHO)rm -f $(LIBGRST)
+	$(ECHO)if [ "$(TYPE)" = "SunOS" -a "$(CC)" = "CC" ]; then \
+	          if [ "x$(SUNCACHE)" != "x" ]; then \
+		     $(CC) -xar -o $(LIBGRST) $(OBJDIR)$(SUNCACHE)/*/*.o; \
+		  else \
+		     $(CC) -xar -o $(LIBGRST) $(OBJGRST); \
+		  fi; \
+	       else \
+		  ar -rc $(LIBGRST) $(OBJGRST); \
+		  ranlib $(LIBGRST); \
+	       fi
+
+$(SECSSLTEST): $(OBJECTA) $(LIBDEPS)
+	@echo Creating executable $(SECSSLTEST)
+	$(ECHO)$(LD) $(LDOP) $(OBJECTA) -L$(LIBDIR) -lXrdSecssl $(MORELIBS) $(LIBS) -o $(SECSSLTEST)
+
+$(OBJDIR)/XrdSecProtocolssl.o:  XrdSecProtocolssl.cc 	XrdSecProtocolssl.hh 	XrdSecInterface.hh \
+                                XrdSysHeaders.hh 	XrdNetDNS.hh 		XrdOucErrInfo.hh XrdOucHash.hh \
+				XrdOucString.hh 	XrdOucTrace.hh 		XrdOucTokenizer.hh XrdSysPthread.hh \
+				XrdSysLogger.hh 	XrdSecInterface.hh 	XrdSecTLayer.hh \
+                                XrdSecEntity.hh       	XrdOucErrInfo.hh	XrdSecTLayer.hh \
+				libsslGridSite/grst_asn1.c	libsslGridSite/grst_err.c \
+				libsslGridSite/grst_gacl.c	libsslGridSite/grst_http.c \
+				libsslGridSite/grst_verifycallback.c libsslGridSite/grst_x509.c \
+				libsslGridSite/grst_xacml.c libsslGridSite/gridsite.h
+	@echo Compiling XrdSecProtocolssl.cc 
+	$(ECHO)$(CC) -c $(NCXXFLAGS)  $(INCLUDE) -o $(OBJDIR)/XrdSecProtocolssl.o XrdSecProtocolssl.cc
+
+$(OBJDIR)/XrdSecProtocolsslTest.o:  XrdSecProtocolsslTest.cc 	XrdSecProtocolssl.hh 	XrdSecInterface.hh \
+                                XrdSysHeaders.hh 	XrdNetDNS.hh 		XrdOucErrInfo.hh XrdOucHash.hh \
+				XrdOucString.hh 	XrdOucTrace.hh 		XrdOucTokenizer.hh XrdSysPthread.hh \
+				XrdSysLogger.hh 	XrdSecInterface.hh 	XrdSecTLayer.hh \
+                                XrdSecEntity.hh       	XrdOucErrInfo.hh	XrdSecTLayer.hh \
+				libsslGridSite/grst_asn1.c	libsslGridSite/grst_err.c \
+				libsslGridSite/grst_gacl.c	libsslGridSite/grst_http.c \
+				libsslGridSite/grst_verifycallback.c libsslGridSite/grst_x509.c \
+				libsslGridSite/grst_xacml.c libsslGridSite/gridsite.h
+	@echo Compiling XrdSecProtocolsslTest.cc
+	$(ECHO)$(CC) -c $(NCXXFLAGS)  $(INCLUDE) -o $(OBJDIR)/XrdSecProtocolsslTest.o XrdSecProtocolsslTest.cc
+
+$(OBJDIR)/grst_asn1.o:  	libsslGridSite/grst_asn1.c libsslGridSite/gridsite.h
+	@echo Compiling grst_asn1.c 
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_asn1.o libsslGridSite/grst_asn1.c
+
+$(OBJDIR)/grst_err.o:  		libsslGridSite/grst_err.c libsslGridSite/gridsite.h
+	@echo Compiling grst_err.c
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_err.o libsslGridSite/grst_err.c
+
+$(OBJDIR)/grst_gacl.o:  	libsslGridSite/grst_gacl.c libsslGridSite/gridsite.h
+	@echo Compiling grst_gacl.c
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_gacl.o libsslGridSite/grst_gacl.c
+
+$(OBJDIR)/grst_http.o:  	libsslGridSite/grst_http.c libsslGridSite/gridsite.h
+	@echo Compiling grst_http.c
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_http.o libsslGridSite/grst_http.c
+
+$(OBJDIR)/grst_verifycallback.o:  libsslGridSite/grst_verifycallback.c libsslGridSite/gridsite.h
+	@echo Compiling grst_verifycallback.c
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_verifycallback.o libsslGridSite/grst_verifycallback.c
+
+$(OBJDIR)/grst_x509.o:  	libsslGridSite/grst_x509.c libsslGridSite/gridsite.h
+	@echo Compiling grst_x509.c
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_x509.o libsslGridSite/grst_x509.c
+
+$(OBJDIR)/grst_xacml.o:  	libsslGridSite/grst_xacml.c libsslGridSite/gridsite.h
+	@echo Compiling grst_xacml.c
+	$(ECHO)$(CCOMPILE)  -c $(NCFLAGS)  $(INCLUDE) -o $(OBJDIR)/grst_xacml.o libsslGridSite/grst_xacml.c
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/Makefile.am b/net/xrootd/src/xrootd/src/XrdSecssl/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..4d67c0a009079fc6a9f48f59061e02b0e3a3039e
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/Makefile.am
@@ -0,0 +1,44 @@
+#######################################################################
+## Makefile.am for xrootd
+##
+##
+## Initial version: 1.8.2005
+##
+## Version info: $Id$
+## Checked in by $Author: ganis $
+#######################################################################
+
+SUBDIRS = libsslGridSite
+
+bin_PROGRAMS = xrdsecssltest
+
+lib_LTLIBRARIES = libXrdSecssl.la
+
+INCLUDES = -I$(top_srcdir)/src -I${XROOTD_INCDIR}/  -IlibsslGridSite ${OPENSSL_INCDIR}
+libXrdSecssl_la_SOURCES = XrdSecProtocolssl.cc XrdSecProtocolssl.hh XrdSecProtocolsslTrace.hh
+libXrdSecssl_la_LIBADD = 
+libXrdSecssl_la_LDFLAGS = -L${XROOTD_LIBDIR}/ -lXrdSec -lXrdSys -lXrdOuc -lXrdNet ${SSLLINKLIB} libsslGridSite/libsslGridSite.la ${OPENSSL_LIBDIR} -lcrypto -lssl ${XML2_LIBDIR} -lxml2 -module  
+libXrdSecssl_la_CXXFLAGS = -Wall -Werror -Wno-deprecated-declarations ${CXXFLAGS}
+libXrdSecssl_la_LIBTOOLFLAGS = --tag=disable-static
+
+xrdsecssltest_SOURCES = XrdSecProtocolsslTest.cc
+xrdsecssltest_LDADD = libXrdSecssl.la
+
+
+
+EXTRA_DIST = bootstrap.sh getAutotools.sh README acinclude.m4 configure.ac GNUmakefile.classic
+
+rpm:    dist
+	cp $(DIST_ARCHIVES) /usr/src/redhat/SOURCES/
+	rpmbuild -bb xrootd-secssl.spec
+
+srpm:	dist
+	rpmbuild -bs xrootd-secssl.spec
+
+test:  xrdsecssltest
+	if [ -d "/etc/grid-security/certificates" ] && [ -e "/etc/grid-security/hostcert.pem" ] && [ -e "/etc/grid-security/hostkey.pem" ] &&  [ -e "/tmp/x509up_u$$UID" ] ; then\
+		( echo "Starting Server ..." && ./xrdsecssltest server >& /tmp/xrdsecssltest-server.log & ) && echo "Starting Client ..." && sleep 1 && ./xrdsecssltest client 2>  /tmp/xrdsecssltest-client.log ; /usr/bin/killall -9 xrdsecssltest lt-xrdsecssltest >& /dev/null || echo Server stopped! ; \
+	else \
+		echo "Error: you need /etc/grid-security/certificates /etc/grid-security/hostcert.pem /etc/grid-security /tmp/x509up_u$$UID";\
+	fi
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/README b/net/xrootd/src/xrootd/src/XrdSecssl/README
new file mode 100644
index 0000000000000000000000000000000000000000..e6cae81aa68817b81f18658d09f9e94078250dde
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/README
@@ -0,0 +1,43 @@
+libXrdSecProtocolssl:
+=======================
+This library implements SSL authentication with Proxy/VOMS support for xrootd. The Verify/VOMS code is taken from Apache mod_ssl & mod_gridsite/libgridsite.
+The SSL handshake is done via the virtual socket Layer in xrootd. The implementation supports session reuse. Session information is stored on client side in files like /tmp/xssl_<uid>:<desthost>.<destdomain>
+
+Currently only the server verifies the client certificate/revocation lists.
+
+
+Configuratione Options:
+=======================
+
+server:
+--------------
+-d:<debug level>	: debug level (default 0)
+-cadir:<certdir>	: CA directory (default /etc/grid-services/certificates)
+-cert:<certfile>	: Service certificate (default /etc/grid-services/hostcert.pem for server)
+-key:<keyfile>		: Service key         (default /etc/grid-services/hostkey.pem  for server)
+-ca:<level>		: CA verification level (default 10)
+-t:<session lifetime>	: SSL Session lifetime in seconds (default 86400 sec = 24 hours)
+-export:<dir>           : Directory where forwared proxies are stored (default <unset> = don't store)
+-gridmapfile:<file>     : Grid Mapfile (default /etc/grid-security/grid-mapfile)
+-vomsmapfile:<file>     : VOMS Mapfile (default /etc/grid-security/voms-mapfile)
+-mapuser:1|0            : apply grid map file for user mapping (default 0)
+-mapgroup:1|0           : apply voms map file for group mapping (default 0)
+-mapcernuser:1|0        : apply automatic mapping of CERN users to AFS accounts
+-mapnobody:1|0          : if a user cannot be mapped this would map him to nobody if set, otherwise authentication failure
+
+client: (Env-Variables)
+
+XrdSecNoSSL             : disables usage of SSL protocol even if server offered
+XrdSecDEBUG		: debug level (default 0)
+XrdSecSSLCADIR		: CA directory (default /etc/grid-services/certificates)
+XrdSecSSLUSERCERT	: client certificate (default proxy /tmp/x509up_u$UID)
+XrdSecSSLUSERKEY	: client key         (default proxy /tmp/x509up_u$UID)
+XrdSecSSLPROXYFORWARD   : foward the proxy certificate to the remote service
+XrdSecSSLVERIFYDEPTH    : verification level depth (default 10)
+XrdSecSSLSESSION        : enable/disable ssl session reuse (default 1 [yes])
+
+# for Globus compatibility
+X509_CERT_DIR           : CA directory if XrdSecSSLCADIR is not defined
+X509_USER_KEY           : client key if XrdSecSSLUSERKEY is not defined
+X509_USER_CERT          : client certificate if XrdSecSSLUSERCERT is not defined
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.cc b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dad70e60e506bf18ccf27492ddc6c6810cc1f5fd
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.cc
@@ -0,0 +1,1430 @@
+/******************************************************************************/
+/*                                                                            */
+/*                 X r d S e c P r o t o c o l s s l . c c                    */
+/*                                                                            */
+/* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+
+//       $Id$
+
+
+const char *XrdSecProtocolsslCVSID = "$Id$";
+
+#include "XrdSecProtocolssl.hh"
+
+char*  XrdSecProtocolssl::sslcadir=0;
+char*  XrdSecProtocolssl::sslvomsdir=0;
+int    XrdSecProtocolssl::verifydepth=10;
+int    XrdSecProtocolssl::verifyindex=0;
+char*  XrdSecProtocolssl::sslkeyfile=0;
+char*  XrdSecProtocolssl::sslserverkeyfile=0;
+char   XrdSecProtocolssl::sslserverexportpassword[EXPORTKEYSTRENGTH+1]; 
+char*  XrdSecProtocolssl::sslcertfile=0;
+char*  XrdSecProtocolssl::sslproxyexportdir=(char*)0;
+bool   XrdSecProtocolssl::sslproxyexportplain=1;
+int    XrdSecProtocolssl::debug=0;
+time_t XrdSecProtocolssl::sslsessionlifetime=86400;
+bool   XrdSecProtocolssl::isServer=0;
+bool   XrdSecProtocolssl::forwardProxy=0;
+bool   XrdSecProtocolssl::allowSessions=1;
+char*  XrdSecProtocolssl::SessionIdContext = (char*)"xrootdssl"; 
+char*  XrdSecProtocolssl::gridmapfile = (char*) "/etc/grid-security/grid-mapfile";
+bool   XrdSecProtocolssl::mapuser  = false;
+bool   XrdSecProtocolssl::mapnobody  = false;
+char*  XrdSecProtocolssl::vomsmapfile = (char*) "/etc/grid-security/voms-mapfile";
+bool   XrdSecProtocolssl::mapgroup = false;
+bool   XrdSecProtocolssl::mapcerncertificates = false;
+
+X509_STORE*    XrdSecProtocolssl::store=0;
+X509_LOOKUP*   XrdSecProtocolssl::lookup=0;
+SSL_CTX*       XrdSecProtocolssl::ctx=0;
+XrdSysError    XrdSecProtocolssl::ssleDest(0, "secssl_");
+XrdSysLogger   XrdSecProtocolssl::Logger;
+time_t         XrdSecProtocolssl::storeLoadTime;
+
+XrdSysMutex XrdSecsslSessionLock::sessionmutex;
+XrdOucHash<XrdOucString> XrdSecProtocolssl::gridmapstore;
+XrdOucHash<XrdOucString> XrdSecProtocolssl::vomsmapstore;
+XrdOucHash<XrdOucString> XrdSecProtocolssl::stringstore;
+XrdSysMutex              XrdSecProtocolssl::StoreMutex;           
+XrdSysMutex              XrdSecProtocolssl::GridMapMutex;           
+XrdSysMutex              XrdSecProtocolssl::VomsMapMutex;           
+XrdSysMutex*             XrdSecProtocolssl::CryptoMutexPool[PROTOCOLSSL_MAX_CRYPTO_MUTEX];
+
+/******************************************************************************/
+/*             T h r e a d - S a f e n e s s   F u n c t i o n s              */
+/******************************************************************************/
+static unsigned long protocolssl_id_callback(void) {
+  return (unsigned long)XrdSysThread::ID();
+}
+
+void protocolssl_lock(int mode, int n, const char *file, int line)
+{
+  if (mode & CRYPTO_LOCK) {
+    if (XrdSecProtocolssl::CryptoMutexPool[n]) {
+      XrdSecProtocolssl::CryptoMutexPool[n]->Lock();
+    }
+  } else {
+    if (XrdSecProtocolssl::CryptoMutexPool[n]) {
+      XrdSecProtocolssl::CryptoMutexPool[n]->UnLock();
+    }
+  }
+}
+
+
+/******************************************************************************/
+/*             C l i e n t   O r i e n t e d   F u n c t i o n s              */
+/******************************************************************************/
+
+int secprotocolssl_pem_cb(char *buf, int size, int rwflag, void *password)
+{
+  memset(buf,0,size);
+  memcpy(buf,XrdSecProtocolssl::sslserverexportpassword,EXPORTKEYSTRENGTH+1);
+  return EXPORTKEYSTRENGTH;
+}
+
+void XrdSecProtocolssl::GetEnvironment() {
+  EPNAME("GetEnvironment");
+  // default the cert/key file to the standard proxy locations
+  char proxyfile[1024];
+  sprintf(proxyfile,"/tmp/x509up_u%d",(int)geteuid());
+
+  if (sslproxyexportdir) {
+    sprintf(proxyfile,"%s/x509up_u%d",sslproxyexportdir,(int)geteuid()); 
+  }
+
+  if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); }
+  if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); }
+
+  XrdSecProtocolssl::sslcertfile = strdup(proxyfile);
+  XrdSecProtocolssl::sslkeyfile  = strdup(proxyfile);
+
+  char *cenv = getenv("XrdSecDEBUG");
+  // debug
+  if (cenv)
+    if (cenv[0] >= 49 && cenv[0] <= 57) XrdSecProtocolssl::debug = atoi(cenv);
+
+  // directory with CA certificates
+  cenv = getenv("XrdSecSSLCADIR");
+  if (cenv) {
+    if (XrdSecProtocolssl::sslcadir) { free (XrdSecProtocolssl::sslcadir); }
+    XrdSecProtocolssl::sslcadir = strdup(cenv);
+  }
+  else {
+    // accept X509_CERT_DIR 
+    cenv = getenv("X509_CERT_DIR");
+    if (cenv) {
+      if (XrdSecProtocolssl::sslcadir) { free (XrdSecProtocolssl::sslcadir); }
+      XrdSecProtocolssl::sslcadir = strdup(cenv);
+    }
+  }
+  // directory with VOMS certificates
+  cenv = getenv("XrdSecSSLVOMSDIR");
+  if (cenv) {
+    if (XrdSecProtocolssl::sslvomsdir) { free (XrdSecProtocolssl::sslvomsdir); }
+    XrdSecProtocolssl::sslvomsdir = strdup(cenv);
+  }
+
+
+  // file with user cert
+  cenv = getenv("XrdSecSSLUSERCERT");
+  if (cenv) {
+    if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); }
+    XrdSecProtocolssl::sslcertfile = strdup(cenv);  
+  } else {
+    // accept X509_USER_CERT
+    cenv = getenv("X509_USER_CERT");
+    if (cenv) {
+      if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); }
+      XrdSecProtocolssl::sslcertfile = strdup(cenv);
+    } else {
+      // accept X509_USER_PROXY
+      cenv = getenv("X509_USER_PROXY");
+      if (cenv) {
+	if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); }
+	XrdSecProtocolssl::sslcertfile = strdup(cenv);
+      }
+    }
+  }
+    
+  // file with user key
+  cenv = getenv("XrdSecSSLUSERKEY");
+  if (cenv) {
+    if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); }    
+      XrdSecProtocolssl::sslkeyfile = strdup(cenv);
+  } else {
+    // accept X509_USER_KEY
+    cenv = getenv("X509_USER_KEY");
+    if (cenv) {
+      if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); }    
+      XrdSecProtocolssl::sslkeyfile = strdup(cenv);
+    } else {
+      // accept X509_USER_PROXY
+      cenv = getenv("X509_USER_PROXY");
+      if (cenv) {
+	if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); }    
+	XrdSecProtocolssl::sslkeyfile = strdup(cenv);
+      }
+    }
+  }
+  // verify depth
+  cenv = getenv("XrdSecSSLVERIFYDEPTH");
+  if (cenv)
+    XrdSecProtocolssl::verifydepth = atoi(cenv);
+    
+  // proxy forwarding
+  cenv = getenv("XrdSecSSLPROXYFORWARD");
+  if (cenv)
+    XrdSecProtocolssl::forwardProxy = atoi(cenv);
+
+  // ssl session reuse
+  cenv = getenv("XrdSecSSLSESSION");
+  if (cenv)
+    XrdSecProtocolssl::allowSessions = atoi(cenv);
+  
+  TRACE(Authen,"====> debug         = " << XrdSecProtocolssl::debug);
+  TRACE(Authen,"====> cadir         = " << XrdSecProtocolssl::sslcadir);
+  TRACE(Authen,"====> keyfile       = " << XrdSecProtocolssl::sslkeyfile);
+  TRACE(Authen,"====> certfile      = " << XrdSecProtocolssl::sslcertfile);
+  TRACE(Authen,"====> verify depth  = " << XrdSecProtocolssl::verifydepth);
+}
+
+int XrdSecProtocolssl::Fatal(XrdOucErrInfo *erp, const char *msg, int rc)
+{
+  const char *msgv[8];
+  int k, i = 0;
+  
+  msgv[i++] = "Secssl: ";    //0
+  msgv[i++] = msg;            //1
+
+  if (erp) erp->setErrInfo(rc, msgv, i);
+  else {for (k = 0; k < i; k++) cerr <<msgv[k];
+    cerr <<endl;
+  }
+  
+  return -1;
+}
+
+/******************************************************************************/
+/*                        g e t C r e d e n t i a l s                         */
+/******************************************************************************/
+
+
+void   
+XrdSecProtocolssl::secClient(int theFD, XrdOucErrInfo      *error) {
+  
+  EPNAME("secClient");
+
+  char *nossl = getenv("XrdSecNoSSL");
+  if (nossl) {
+    error->setErrInfo(ENOENT,"SSL is disabled by force");
+    return ;
+  }
+
+  XrdSecProtocolssl::GetEnvironment();
+
+  error->setErrInfo(0,"");
+  SSLMutex.Lock();
+
+  int err;
+  char*    str;
+  SSL_METHOD *meth;
+  SSL_SESSION *session=0;
+
+  SSL_load_error_strings();  
+  SSLeay_add_ssl_algorithms();
+  meth = (SSL_METHOD*) TLSv1_client_method();
+
+  ERR_load_crypto_strings();
+
+  XrdOucString sslsessionfile="";
+  XrdOucString sslsessionid="";
+  
+  sslsessionfile = "/tmp/xssl_";
+  sslsessionid += (int)geteuid();
+  sslsessionid += ":";
+  sslsessionid += host.c_str();
+  sslsessionfile += sslsessionid;
+
+  XrdSecsslSessionLock sessionlock;
+  sessionlock.SoftLock();
+
+  if (allowSessions) {
+    struct stat sessionstat;
+
+    if (!stat(sslsessionfile.c_str(),&sessionstat)) {
+      // session exists ... I try to read it
+      FILE* fp = fopen(sslsessionfile.c_str(), "r");
+      if (fp) {
+	if (sessionlock.HardLock(sslsessionfile.c_str())) {
+	  session = PEM_read_SSL_SESSION(fp, NULL, NULL, NULL);
+	  fclose(fp);
+	  sessionlock.HardUnLock();
+	}
+      }
+      
+      if (session) {
+	
+	DEBUG("info: ("<<__FUNCTION__<<") Session loaded from " << sslsessionfile.c_str());
+	char session_id[1024];
+	for (int i=0; i< (int)session->session_id_length; i++) {
+	  sprintf(session_id+(i*2),"%02x",session->session_id[i]);
+	}
+	
+	unsigned char buf[5],*p;
+	unsigned long l;
+	
+	p=buf;
+	l=session->cipher_id;
+	l2n(l,p);
+
+	DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")");
+      } else {
+	DEBUG("info: ("<<__FUNCTION__<<") Session load failed from " << sslsessionfile.c_str());
+	ERR_print_errors_fp(stderr);
+      }
+    }
+  }
+
+  clientctx = SSL_CTX_new (meth);
+
+
+  SSL_CTX_set_options(clientctx,  SSL_OP_ALL | SSL_OP_NO_SSLv2);
+
+  if (!clientctx) {
+    Fatal(error,"Cannot do SSL_CTX_new",-1);
+    exit(2);
+  }
+  
+  if (!XrdSecProtocolssl::sslproxyexportplain) {
+    // set a password callback here
+    SSL_CTX_set_default_passwd_cb(clientctx, secprotocolssl_pem_cb);
+    SSL_CTX_set_default_passwd_cb_userdata(clientctx, XrdSecProtocolssl::sslserverexportpassword);
+  }
+
+  if (SSL_CTX_use_certificate_chain_file(clientctx, sslcertfile) <= 0) {
+    ERR_print_errors_fp(stderr);
+    Fatal(error,"Cannot use certificate file",-1);
+    if (theFD>=0) {close(theFD); theFD=-1;}
+    if (clientctx) SSL_CTX_free (clientctx);
+    SSLMutex.UnLock();
+    return;
+  }
+
+  if (SSL_CTX_use_PrivateKey_file(clientctx, sslkeyfile, SSL_FILETYPE_PEM) <= 0) {
+    ERR_print_errors_fp(stderr);
+    Fatal(error,"Cannot use private key file",-1);
+    if (theFD>=0) {close(theFD); theFD=-1;}
+    if (clientctx) SSL_CTX_free (clientctx);
+    SSLMutex.UnLock();
+    return;
+  }
+
+
+  if (!SSL_CTX_check_private_key(clientctx)) {
+    fprintf(stderr,"Error: (%s) Private key does not match the certificate public key\n",__FUNCTION__);
+    Fatal(error,"Private key does not match the certificate public key",-1);
+    if (theFD>=0) {close(theFD); theFD=-1;}
+    if (clientctx) SSL_CTX_free (clientctx);
+    SSLMutex.UnLock();
+    return;
+  } else {
+    DEBUG("Private key check passed ...");
+  }
+  SSL_CTX_load_verify_locations(clientctx, NULL,sslcadir);
+  SSL_CTX_set_verify(clientctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,  GRST_callback_SSLVerify_wrapper);
+
+  SSL_CTX_set_cert_verify_callback(clientctx, GRST_verify_cert_wrapper, (void *) NULL);
+
+  grst_cadir   = sslcadir;
+  grst_vomsdir = sslvomsdir;
+
+  grst_depth=verifydepth;
+  SSL_CTX_set_verify_depth(clientctx, verifydepth);
+
+  if (session) {
+    SSL_CTX_add_session(clientctx,session);
+  }
+
+
+  ssl = SSL_new (clientctx);            
+  SSL_set_purpose(ssl,X509_PURPOSE_ANY);
+  if (session) {
+    SSL_set_session(ssl, session);
+  }
+
+  sessionlock.SoftUnLock();
+  sessionlock.HardUnLock();
+
+  if (!ssl) {
+    Fatal(error,"Cannot do SSL_new",-1);
+    exit(6);
+  }
+
+  SSL_set_fd (ssl, theFD);
+  err = SSL_connect (ssl);
+
+  if (err!=1) {
+    // we want to see the error message from the server side
+    ERR_print_errors_fp(stderr);
+    if (theFD>=0) {close(theFD);theFD=-1;}
+    if (clientctx) SSL_CTX_free (clientctx);
+    SSLMutex.UnLock();
+    return;
+  }
+
+  if (!session) 
+    session = SSL_get1_session(ssl);
+  
+  /* Get the cipher - opt */
+  
+  TRACE(Authen,"SSL connection uses cipher: "<<SSL_get_cipher (ssl));
+  
+  /* Get server's certificate (note: beware of dynamic allocation) - opt */
+  
+  server_cert = SSL_get_peer_certificate (ssl);       
+
+  if (!server_cert) {
+    TRACE(Authen,"Server didn't provide certificate");
+  }
+
+  XrdOucString rdn;
+  
+  str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
+  rdn = str;
+  TRACE(Authen,"Server certificate subject:\t" << str);
+  OPENSSL_free (str);
+  
+  str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);
+  TRACE(Authen,"Server certificate  issuer: \t" << str);
+  OPENSSL_free (str);
+  
+  X509_free (server_cert);
+  server_cert=0;
+
+
+  /******************************************/
+  /* this is called only to cleanup objects */
+  /******************************************/
+
+  /* get the grst_chain  */
+  GRSTx509Chain *grst_chain = (GRSTx509Chain*) SSL_get_app_data(ssl);
+  
+  if (grst_chain) {
+    GRST_print_ssl_creds((void*) grst_chain);
+    char* vr = GRST_get_voms_roles_and_free((void*) grst_chain);
+    SSL_set_app_data(ssl,0);
+    if (vr) {
+      free(vr);
+    }
+  }
+
+
+
+  if (forwardProxy) {
+    if (!strcmp(sslkeyfile,sslcertfile)) {
+      // this is a cert & key in one file atleast ... looks like proxy
+      int fd = open(sslkeyfile,O_RDONLY);
+      if (fd>=0) {
+	int nread = read(fd,proxyBuff, sizeof(proxyBuff));
+	if (nread>=0) {
+	  TRACE(Authen,"Uploading my Proxy ...\n");
+	  err = SSL_write(ssl, proxyBuff,nread);
+	  if (err!= nread) {
+	    Fatal(error,"Cannot forward proxy",-1);
+	    if (theFD>=0) {close(theFD); theFD=-1;}
+	    if (clientctx) SSL_CTX_free (clientctx);
+	    if (session) SSL_SESSION_free(session);
+	    SSLMutex.UnLock();
+	    return;
+	  }
+	  char ok[16];
+	  err = SSL_read(ssl,ok, 3);
+	  if (err != 3) {
+	    Fatal(error,"Didn't receive OK",-1);
+	    if (theFD>=0) {close(theFD); theFD=-1;}
+	    if (clientctx) SSL_CTX_free (clientctx);
+	    if (session) SSL_SESSION_free(session);
+	    SSLMutex.UnLock();
+	    return;
+	  } 
+	} else {
+	  close(fd);
+	  Fatal(error,"Cannot read proxy file to forward",-1);
+	  if (theFD>=0) {close(theFD);theFD=-1;}
+	  if (clientctx) SSL_CTX_free (clientctx);
+	  if (session) SSL_SESSION_free(session);
+	  SSLMutex.UnLock();
+	  return;
+	}
+      } else {
+	Fatal(error,"Cannot read proxy file to forward",-1);
+	if (theFD>=0) {close(theFD);theFD=-1;}
+	if (clientctx) SSL_CTX_free (clientctx);
+	if (session) SSL_SESSION_free(session);
+	SSLMutex.UnLock();
+	return;
+      }
+      close(fd);
+    }
+  }
+
+  if (allowSessions && session) {
+    char session_id[1024];
+    for (int i=0; i< (int)session->session_id_length; i++) {
+      sprintf(session_id+(i*2),"%02x",session->session_id[i]);
+    }
+    
+    if (session->cipher) {
+      DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Cipher: " << session->cipher->name  << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")");
+    } else {
+      DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")");
+    }
+    // write out the session
+    FILE* fp = fopen((const char*)(sslsessionfile.c_str()),"w+");
+    if (fp) {
+      if (sessionlock.HardLock(sslsessionfile.c_str())) {
+	PEM_write_SSL_SESSION(fp, session);
+      }
+      fclose(fp);
+      sessionlock.HardUnLock();
+      DEBUG("info: ("<<__FUNCTION__<<") Session stored to " << sslsessionfile.c_str());
+    }
+  }
+
+  while (!SSL_shutdown(ssl)) {
+    SSL_shutdown(ssl);
+  }
+
+  if (ssl) {
+    SSL_free(ssl);ssl = 0;
+  }
+
+  if (clientctx) SSL_CTX_free (clientctx);
+
+  if (theFD>=0) {close(theFD);theFD=-1;
+  }
+
+  if (session) SSL_SESSION_free(session);
+
+  SSLMutex.UnLock();
+  return;
+}
+
+/******************************************************************************/
+/*               S e r v e r   O r i e n t e d   M e t h o d s                */
+/******************************************************************************/
+
+
+
+/*----------------------------------------------------------------------------*/
+/* this helps to avoid memory leaks by strdup                                 */
+/* we maintain a string hash to keep all used user ids/group ids etc.         */
+
+char* 
+STRINGSTORE(const char* __charptr__) {
+  XrdOucString* yourstring;
+  if (!__charptr__ ) return (char*)"";
+
+  if ((yourstring = XrdSecProtocolssl::stringstore.Find(__charptr__))) {
+    return (char*)yourstring->c_str();
+  } else {
+    XrdOucString* newstring = new XrdOucString(__charptr__);
+    XrdSecProtocolssl::StoreMutex.Lock();
+    XrdSecProtocolssl::stringstore.Add(__charptr__,newstring);
+    XrdSecProtocolssl::StoreMutex.UnLock();
+    return (char*)newstring->c_str();
+  } 
+}
+
+/*----------------------------------------------------------------------------*/
+void MyGRSTerrorLogFunc (char *lfile, int lline, int llevel, char *fmt, ...) {
+  EPNAME("grst");
+  va_list args;
+  char fullmessage[4096];
+  fullmessage[0] = 0;
+
+  va_start(args, fmt);
+  vsprintf(fullmessage,fmt,args);
+  va_end(args);
+
+  // just remove linefeeds
+  XrdOucString sfullmessage = fullmessage;
+  sfullmessage.replace("\n","");
+
+  if (llevel <= GRST_LOG_WARNING) {
+    TRACE(Authen," ("<< lfile << ":" << lline <<"): " << sfullmessage);    
+  } else if (llevel <= GRST_LOG_INFO) {
+    TRACE(Authen, " ("<< lfile << ":" << lline <<"): " << sfullmessage);    
+  } else {
+    DEBUG(" ("<< lfile << ":" << lline <<"): " << sfullmessage);
+  }
+}
+
+/*----------------------------------------------------------------------------*/
+void   
+XrdSecProtocolssl::secServer(int theFD, XrdOucErrInfo      *error) {
+  int err;
+
+  char*    str;
+
+  EPNAME("secServer");
+
+  XrdSecsslSessionLock sessionlock; 
+
+  // check if we should reload the store
+  if ((time(NULL)-storeLoadTime) > 3600) {
+    if (store) {
+      TRACE(Authen,"Reloading X509 Store from " << sslcadir);
+      X509_STORE_free(store);
+      store = SSL_X509_STORE_create(NULL, sslcadir);
+      X509_STORE_set_flags(XrdSecProtocolssl::store,0);
+      storeLoadTime = time(NULL);
+    } 
+  }
+
+  SSLMutex.Lock();
+
+  //  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); // enable autoclear every 255 connections | SSL_SESS_CACHE_NO_AUTO_CLEAR );
+  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+
+  ssl = SSL_new (ctx);                           
+  SSL_set_purpose(ssl,X509_PURPOSE_ANY);
+
+
+  TRACE(Authen,"Info: ("<<__FUNCTION__<<") Session Cache has size: " <<SSL_CTX_sess_get_cache_size(ctx));
+
+  if (!ssl) {
+    fprintf(stderr,"Error: (%s) failed to create context\n",__FUNCTION__);
+    TRACE(Authen,"Error: ("<<__FUNCTION__<<") failed to create context");
+    exit(5);
+  }
+
+  SSL_set_fd (ssl, theFD);
+
+  // the accept run's without mutex
+  err = SSL_accept (ssl);                        
+
+  if (err!=1) {
+    long verifyresult = SSL_get_verify_result(ssl);
+    if (verifyresult != X509_V_OK) {
+      Fatal(error,X509_verify_cert_error_string(verifyresult),verifyresult);
+      TRACE(Authen,"Error: ("<<__FUNCTION__<<") failed SSL_accept ");
+    } else {
+      Fatal(error,"do SSL_accept",-1);
+      unsigned long lerr;
+      while ((lerr=ERR_get_error())) {TRACE(Authen,"SSL Queue error: err=" << lerr << " msg=" <<
+					  ERR_error_string(lerr, NULL));Fatal(error,ERR_error_string(lerr,NULL),-1);}
+    }
+    if (theFD>=0) {close(theFD); theFD=-1;}
+    SSLMutex.UnLock();
+    return;
+  }
+  
+  SSL_SESSION* session = SSL_get_session(ssl);
+
+  if (session) {
+    char session_id[1024];
+    for (int i=0; i< (int)session->session_id_length; i++) {
+      sprintf(session_id+(i*2),"%02x",session->session_id[i]);
+    }
+    
+    DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")");
+    DEBUG("Info: ("<<__FUNCTION__<<") cache items             : " << SSL_CTX_sess_number(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") client connects         : " << SSL_CTX_sess_connect(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") client renegotiates     : " << SSL_CTX_sess_connect_renegotiate(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") client connect finished : " << SSL_CTX_sess_connect_good(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") server accepts          : " << SSL_CTX_sess_accept(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") server renegotiates     : " << SSL_CTX_sess_accept_renegotiate(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") server accepts finished : " << SSL_CTX_sess_accept_good(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") session cache hits      : " << SSL_CTX_sess_hits(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") session cache misses    : " << SSL_CTX_sess_misses(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") session cache timeouts  : " << SSL_CTX_sess_timeouts(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") callback cache hits     : " << SSL_CTX_sess_cb_hits(ctx));
+    DEBUG("Info: ("<<__FUNCTION__<<") cache full overflows    : " << SSL_CTX_sess_cache_full(ctx) << " allowed: " << SSL_CTX_sess_get_cache_size(ctx));
+    
+  }
+  /* get the grst_chain  */
+  GRSTx509Chain *grst_chain = (GRSTx509Chain*) SSL_get_app_data(ssl);
+  
+  XrdOucString vomsroles="";
+  XrdOucString clientdn="";
+  
+  if (grst_chain) {
+    GRST_print_ssl_creds((void*) grst_chain);
+    char* vr = GRST_get_voms_roles_and_free((void*) grst_chain);
+    SSL_set_app_data(ssl,0);
+    if (vr) {
+      vomsroles = vr;
+      free(vr);
+    }
+  }
+  
+  TRACE(Authen,"Authenticated with VOMS roles: "<<vomsroles);
+  
+  long verifyresult = SSL_get_verify_result(ssl);
+  
+  TRACE(Authen,"Verify result is = "<<verifyresult);
+  
+  /* Get the cipher - opt */
+  
+  DEBUG("SSL connection uses cipher " << SSL_get_cipher(ssl));
+  
+  /* Get client's certificate (note: beware of dynamic allocation) - opt */
+  
+  client_cert = SSL_get_peer_certificate (ssl);
+
+
+  if (client_cert != NULL) {
+    str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
+    if (str) {
+      TRACE(Authen,"client certificate subject: "<< str);
+      clientdn = str;
+      OPENSSL_free (str);
+    } else {
+      TRACE(Authen,"client certificate subject: none");
+    }
+    
+    str = X509_NAME_oneline (X509_get_issuer_name  (client_cert), 0, 0);
+    
+    if (str) {
+      TRACE(Authen,"client certificate issuer : "<<str);
+      TRACE(Authen,"Setting dn="<<clientdn<<" roles="<<vomsroles);
+      OPENSSL_free (str);
+    } else {
+      TRACE(Authen,"client certificate issuer : none");
+      Fatal(error,"no client issuer",-1);
+      if (theFD>=0) {close(theFD);theFD=-1;}
+      SSLMutex.UnLock();
+      return;
+    }
+  } else {
+    TRACE(Authen,"Client does not have certificate.");
+    Fatal(error,"no client certificate",-1);
+    if (theFD>=0) {close(theFD);theFD=-1;}
+    SSLMutex.UnLock();
+    return;
+  }
+
+  // receive client proxy - if he send's one
+  err = SSL_read(ssl,proxyBuff, sizeof(proxyBuff));
+  if (err>0) {
+    TRACE(Authen,"Received proxy buffer with " << err << " bytes");
+    proxyBuff[err] = 0; //0 terminate the proxy buffer
+    Entity.endorsements = proxyBuff;
+    err = SSL_write(ssl,"OK\n",3);
+    if (err!=3) {
+      Fatal(error,"could not send end of handshake OK",-1);
+      if (theFD>=0) {close(theFD);theFD=-1;}
+      SSLMutex.UnLock();
+      return;
+    }
+    // pseudo read to let the client close the connection
+    char dummy[1];
+    err = SSL_read(ssl,dummy, sizeof(dummy));
+  } else {
+    TRACE(Authen,"Received no proxy");
+  }
+
+  //while (!SSL_shutdown(ssl)) {
+    SSL_shutdown(ssl);
+    //  }
+
+  strncpy(Entity.prot,"ssl", sizeof(Entity.prot));
+
+  /*----------------------------------------------------------------------------*/
+  /* mapping interface                                                          */
+  /*----------------------------------------------------------------------------*/
+     
+  if (!mapuser && !mapcerncertificates) { 
+    // no mapping put the DN
+    Entity.name = strdup(clientdn.c_str());
+  } else {
+    bool mapped=false;
+    // map user from grid map file
+    if (mapcerncertificates) {
+      // map from CERN DN
+      if ( (mapcerncertificates) && (clientdn.beginswith("/DC=ch/DC=cern/OU=Organic Units/OU=Users/CN="))) {
+	XrdOucString certsubject = clientdn;
+	certsubject.erasefromstart(strlen("/DC=ch/DC=cern/OU=Organic Units/OU=Users/CN="));
+	int pos=certsubject.find('/');                               
+	if (pos != STR_NPOS)                                         
+	  certsubject.erase(pos);			  	        
+	Entity.name = strdup(certsubject.c_str());
+	mapped=true;
+	TRACE(Authen,"Found CERN certificate - mapping to AFS account " << certsubject);
+      }
+    }
+    if (!mapped) {
+      if (mapuser) {
+	// treatment of old proxy
+	XrdOucString certsubject = clientdn;
+	certsubject.replace("/CN=proxy","");                           
+	// treatment of new proxy - leave only the first CN=, cut the rest
+	int pos = certsubject.find("CN=");
+	int pos2 = certsubject.find("/",pos);
+	if (pos2>0) certsubject.erase(pos2);
+	XrdOucString* gridmaprole;                                     
+	ReloadGridMapFile();
+	GridMapMutex.Lock();                             
+	
+	if ((gridmaprole = gridmapstore.Find(certsubject.c_str()))) { 
+	  Entity.name = strdup(gridmaprole->c_str());      
+	  Entity.role = 0;
+	}  else {
+	  Entity.name = strdup((char*)"nobody");
+	  Entity.role = 0;
+	  if (!XrdSecProtocolssl::mapnobody) {
+	    Fatal(error,"user cannot be mapped",-1);
+	  }
+	}
+	GridMapMutex.UnLock();
+      } else {
+	Entity.name = strdup((char*)"nobody");
+	Entity.role = 0;
+	if (!XrdSecProtocolssl::mapnobody) {
+	  Fatal(error,"user cannot be mapped",-1);
+	}
+      }
+    }
+  }
+  
+  
+  if (!mapgroup) {
+    if (vomsroles.length()) {
+      // no mapping put the VOMS groups and role
+      Entity.grps = strdup(vomsroles.c_str());
+      
+      XrdOucString vomsrole = vomsroles.c_str();
+      
+      if (vomsroles.length()) {
+	int dp = vomsrole.find(":");
+	if (dp != STR_NPOS) {
+	  vomsrole.assign(vomsroles,0,dp-1);
+	}
+	Entity.role = strdup(vomsrole.c_str());
+      } else {
+	Entity.role = strdup("");
+      }
+    } else {
+      // map the group from the passwd/group file
+      struct passwd* pwd;
+      struct group*  grp;
+      StoreMutex.Lock();
+      if ( (pwd = getpwnam(Entity.name)) && (grp = getgrgid(pwd->pw_gid))) {
+	Entity.grps   = strdup(grp->gr_name);
+	Entity.role   = strdup(grp->gr_name);
+      }
+      StoreMutex.UnLock();
+    }
+  } else {
+    // map groups & role from VOMS mapfile
+    XrdOucString defaultgroup="";                                     
+    XrdOucString allgroups="";  
+    if (VomsMapGroups(vomsroles.c_str(), allgroups,defaultgroup)) {
+      if (!strcmp(allgroups.c_str(),":")) {
+	// map the group from the passwd/group file
+	struct passwd* pwd;
+	struct group*  grp;
+	StoreMutex.Lock();
+	if ( (pwd = getpwnam(Entity.name)) && (grp = getgrgid(pwd->pw_gid))) {
+	  allgroups    = grp->gr_name;
+	  defaultgroup = grp->gr_name;
+	}
+	StoreMutex.UnLock();
+      }
+      Entity.grps   = strdup(allgroups.c_str());
+      Entity.role   = strdup(defaultgroup.c_str());
+    } else {
+      Fatal(error,"incomplete VOMS mapping",-1);
+    }
+  }
+
+  /*----------------------------------------------------------------------------*/
+  /* proxy forwarding                                                           */
+  /*----------------------------------------------------------------------------*/
+
+  if (sslproxyexportdir && Entity.endorsements) {
+    StoreMutex.Lock();
+    // get the UID of the entity name
+    struct passwd* pwd;
+    XrdOucString outputproxy = sslproxyexportdir; outputproxy+="/x509up_u"; 
+    if ( (pwd = getpwnam(Entity.name)) ) {
+      outputproxy += (int)pwd->pw_uid;
+    } else {
+      outputproxy += Entity.name;
+    }
+    XrdOucString outputproxytmp = outputproxy;
+    outputproxytmp += (int) rand();
+
+    if (XrdSecProtocolssl::sslproxyexportplain) {
+      int fd = open (outputproxytmp.c_str(),O_CREAT| O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+      if (fd>0) {
+	if ( ((int)write(fd,Entity.endorsements,strlen(Entity.endorsements))) != (int)strlen(Entity.endorsements)) {
+	  unlink(outputproxytmp.c_str());
+	  Fatal(error,"cannot export(write) user proxy",-1);
+	} else {
+	  TRACE(Identity,"Exported proxy buffer of " << Entity.name << " to file " << outputproxy.c_str());
+	}
+	if ( rename(outputproxytmp.c_str(),outputproxy.c_str()) ) {
+	  unlink(outputproxytmp.c_str());
+	  Fatal(error,"cannot rename temporary export proxy",-1);
+	}
+	close(fd);
+      } else {
+	Fatal(error,"cannot export(open) user proxy",-1);
+      }
+    } else {
+      EVP_PKEY* pkey=NULL;
+      X509* x509=NULL;
+      // we protect the private key with our session password
+      BIO* bp = BIO_new_mem_buf( (void *)Entity.endorsements, strlen(Entity.endorsements)+1);
+      FILE* fout = fopen (outputproxytmp.c_str(),"w+");
+      if (!fout) {
+	Fatal(error,"cannot export user proxy - unable to open proxy file",-1);
+      } else {
+	if (bp) {
+	  pkey = PEM_read_bio_PrivateKey(bp, &pkey,0,0);
+	  BIO_free(bp);
+	  if (!pkey) {
+	    Fatal(error,"cannot export user proxy - unable to read key/cert from BIO",-1);
+	  } else {
+	    int wk = PEM_write_PrivateKey(fout, pkey, EVP_des_ede3_cbc(),(unsigned char*)XrdSecProtocolssl::sslserverexportpassword,EXPORTKEYSTRENGTH,0,0);
+	    EVP_PKEY_free(pkey);
+	    
+	    if (!wk) {
+	      Fatal(error,"cannot export user proxy - unable to write private key",-1);
+	    } else {
+	      // deal with the certificates
+	      char* certificatebuffer = 0;
+	      certificatebuffer = Entity.endorsements;
+	      while ((certificatebuffer = strstr(certificatebuffer,"-----BEGIN CERTIFICATE-----"))) {
+		// we point to the next certificate to export in memory
+		BIO* bp = BIO_new_mem_buf( (void *)certificatebuffer, strlen(certificatebuffer)+1);
+		if (bp) {
+		  x509 = NULL;
+		  x509 = PEM_read_bio_X509(bp, &x509,0,0);
+		  BIO_free(bp);
+		  if (x509)  {
+		    int wc = PEM_write_X509(fout,x509);
+		    X509_free(x509);
+		    if (!wc) {
+		      Fatal(error,"cannto export user proxy - unable to write certificate",-1);
+		      break;
+		    }
+		  }
+		} else {
+		  Fatal(error,"cannot export user proxy - unable to allocate BIO to read private key",-1);
+		}
+		certificatebuffer++;
+	      }
+	    }
+	  }
+	} else {
+	  Fatal(error,"cannot export user proxy - unable to allocate BIO to read private key",-1);
+	}
+	
+	fclose(fout);
+	if ( rename(outputproxytmp.c_str(),outputproxy.c_str()) ) {
+	  unlink(outputproxytmp.c_str());
+	  Fatal(error,"cannot rename temporary export proxy",-1);
+	}
+      }
+    }
+    
+    StoreMutex.UnLock();
+  }
+
+
+  TRACE(Identity,"[usermapping] name=|" << Entity.name << "| role=|" << (Entity.role?Entity.role:"-") << "| grps=|"<< (Entity.grps?Entity.grps:"-") << "| DN=|" << clientdn.c_str() << "| VOMS=|" << vomsroles.c_str() << "|");
+
+  if (ssl) {
+    SSL_free(ssl);ssl = 0;
+  }
+
+  if (theFD>=0) {
+    close(theFD); 
+  }       
+
+  SSLMutex.UnLock();
+  return;
+}
+
+int 
+XrdSecProtocolssl::GenerateSession(const SSL* ssl, unsigned char *id, unsigned int *id_len) {
+  EPNAME("GenerateSession");
+  unsigned int count = 0;
+  do      {
+    RAND_pseudo_bytes(id, *id_len);
+    /* Prefix the session_id with the required prefix. NB: If our
+     * prefix is too long, clip it - but there will be worse effects
+     * anyway, eg. the server could only possibly create 1 session
+     * ID (ie. the prefix!) so all future session negotiations will
+     * fail due to conflicts. */
+    memcpy(id, "xrootdssl",
+	   (strlen("xrootdssl") < *id_len) ?
+	   strlen("xrootdssl") : *id_len);
+   TRACE(Authen,"Generated SSID **********************");
+  }
+  while(SSL_has_matching_session_id(ssl, id, *id_len) &&
+	(++count < MAX_SESSION_ID_ATTEMPTS));
+  if(count >= MAX_SESSION_ID_ATTEMPTS)
+    return 0;
+  return 1;
+}
+
+int 
+XrdSecProtocolssl::NewSession(SSL* ssl, SSL_SESSION *session) {
+  EPNAME("NewSession");
+  TRACE(Authen,"Creating new Session");
+  char session_id[1024];
+  for (int i=0; i< (int)session->session_id_length; i++) {
+    sprintf(session_id+(i*2),"%02x",session->session_id[i]);
+  }
+  DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")");
+  
+  SSL_set_timeout(session, sslsessionlifetime);
+  return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+void 
+XrdSecProtocolssl::ReloadGridMapFile()
+{ 
+  EPNAME("ReloadGridMapFile");
+
+  static time_t         GridMapMtime=0;
+  static time_t         GridMapCheckTime=0;
+  int now = time(NULL);
+
+  if ((!GridMapCheckTime) || ((now >GridMapCheckTime + 60)) ) {
+    // load it for the first time or again
+    struct stat buf;
+    if (!::stat(gridmapfile,&buf)) {
+      if (buf.st_mtime != GridMapMtime) {
+	GridMapMutex.Lock();
+	// store the last modification time
+	GridMapMtime = buf.st_mtime;
+	// store the current time of the check
+	GridMapCheckTime = now;
+	// dump the current table
+	gridmapstore.Purge();
+	// open the gridmap file
+	FILE* mapin = fopen(gridmapfile,"r");
+	if (!mapin) {
+	  // error no grid map possible
+	  TRACE(Authen,"Unable to open gridmapfile " << XrdOucString(gridmapfile) << " - no mapping!");
+	} else {
+	  char userdnin[4096];
+	  char usernameout[4096];
+	  int nitems;
+	  // parse it
+	  while ( (nitems = fscanf(mapin,"\"%[^\"]\" %s\n", userdnin,usernameout)) == 2) {
+	    XrdOucString dn = userdnin;
+	    dn.replace("\"","");
+	    // leave only the first CN=, cut the rest
+	    int pos = dn.find("CN=");
+	    int pos2 = dn.find("/",pos);
+	    if (pos2>0) dn.erase(pos2);
+
+	    if (!gridmapstore.Find(dn.c_str())) {
+	      gridmapstore.Add(dn.c_str(), new XrdOucString(usernameout));
+	      TRACE(Authen, "gridmapfile Mapping Added: " << dn.c_str() << " |=> " << usernameout);
+	    }
+	  }
+	  fclose(mapin);
+	}
+	GridMapMutex.UnLock();
+      } else {
+	// the file didn't change, we don't do anything
+      }
+    } else {
+      TRACE(Authen,"Unable to stat gridmapfile " << XrdOucString(gridmapfile) << " - no mapping!");
+    }
+  }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void 
+XrdSecProtocolssl::ReloadVomsMapFile()
+{
+  EPNAME("ReloadVomsMapFile");
+
+  static time_t         VomsMapMtime=0;
+  static time_t         VomsMapCheckTime=0;
+  int now = time(NULL);
+
+  if ((!VomsMapCheckTime) || ((now >VomsMapCheckTime + 60 )) ) {
+    // load it for the first time or again
+    struct stat buf;
+    if (!::stat(vomsmapfile,&buf)) {
+      if (buf.st_mtime != VomsMapMtime) {
+	VomsMapMutex.Lock();
+	// store the last modification time
+	VomsMapMtime = buf.st_mtime;
+	// store the current time of the check
+	VomsMapCheckTime = now;
+	// dump the current table
+	vomsmapstore.Purge();
+	// open the vomsmap file
+	FILE* mapin = fopen(vomsmapfile,"r");
+	if (!mapin) {
+	  // error no voms map possible
+	  TRACE(Authen,"Unable to open vomsmapfile " << XrdOucString(vomsmapfile) << " - no mapping!");
+	} else {
+	  char userdnin[4096];
+	  char usernameout[4096];
+	  int nitems;
+	  // parse it
+	  while ( (nitems = fscanf(mapin,"\"%[^\"]\" %s\n", userdnin,usernameout)) == 2) {
+	    XrdOucString dn = userdnin;
+	    dn.replace("\"","");
+	    if (!vomsmapstore.Find(dn.c_str())) {
+	      vomsmapstore.Add(dn.c_str(), new XrdOucString(usernameout));
+	      TRACE(Authen,"vomsmapfile Mapping Added: " << dn.c_str() << " |=> " << usernameout);
+	    }
+	  }
+	  fclose(mapin);
+	}
+	VomsMapMutex.UnLock();
+      } else {
+	// the file didn't change, we don't do anything
+      }
+    } else {
+      TRACE(Authen,"Unable to stat vomsmapfile " << XrdOucString(vomsmapfile) << " - no mapping!");
+    }
+  }
+}
+
+/*----------------------------------------------------------------------------*/
+
+bool
+XrdSecProtocolssl::VomsMapGroups(const char* groups, XrdOucString& allgroups, XrdOucString& defaultgroup) 
+{
+  EPNAME("VomsMapGroups");
+  // loop over all VOMS groups and replace them according to the mapping
+  XrdOucString vomsline = groups;
+  allgroups = ":";
+  defaultgroup = "";
+  vomsline.replace(":","\n");
+  XrdOucTokenizer vomsgroups((char*)vomsline.c_str());
+  const char* stoken;
+  int ntoken=0;
+  XrdOucString* vomsmaprole;                                     
+  while( (stoken = vomsgroups.GetLine())) {
+    if ((vomsmaprole = XrdSecProtocolssl::vomsmapstore.Find(stoken))) { 
+      allgroups += vomsmaprole->c_str();
+      allgroups += ":";
+      if (ntoken == 0) {
+	defaultgroup = vomsmaprole->c_str();
+      }
+      ntoken++;
+    } else {
+      TRACE(Authen,"No VOMS mapping found for " << XrdOucString(stoken));
+      return false;
+    }
+  }
+  return true;
+}
+
+/******************************************************************************/
+/*                X r d S e c p r o t o c o l u n i x I n i t                 */
+/******************************************************************************/
+  
+extern "C"
+{
+char  *XrdSecProtocolsslInit(const char     mode,
+                              const char    *parms,
+                              XrdOucErrInfo *erp)
+{
+  EPNAME("ProtocolsslInit");
+  // Initiate error logging and tracing
+  XrdSecProtocolssl::ssleDest.logger(&XrdSecProtocolssl::Logger);
+  
+  GRSTerrorLogFunc = &MyGRSTerrorLogFunc;
+  static bool serverinitialized = false;
+
+  // create the tracer
+  if (!SSLxTrace)
+    SSLxTrace = new XrdOucTrace(&XrdSecProtocolssl::ssleDest);
+
+  for (int i=0; i< PROTOCOLSSL_MAX_CRYPTO_MUTEX; i++) {
+    XrdSecProtocolssl::CryptoMutexPool[i] = new XrdSysMutex();
+  }
+
+
+  // read the configuration options
+  if ( (mode == 's') && (!serverinitialized) )  {
+    XrdSecProtocolssl::sslcertfile = strdup("/etc/grid-security/hostcert.pem");
+    XrdSecProtocolssl::sslkeyfile  = strdup("/etc/grid-security/hostkey.pem");
+    XrdSecProtocolssl::sslcadir    = strdup("/etc/grid-security/certificates");
+    XrdSecProtocolssl::sslvomsdir  = (char*)"/etc/grid-security/vomsdir";
+
+    XrdSecProtocolssl::isServer = 1;
+    serverinitialized = true; 
+    if (parms){
+      // Duplicate the parms
+      char parmbuff[1024];
+      strlcpy(parmbuff, parms, sizeof(parmbuff));
+      //
+      // The tokenizer
+      XrdOucTokenizer inParms(parmbuff);
+      char *op;
+
+      while (inParms.GetLine()) { 
+	while ((op = inParms.GetToken())) {
+	  if (!strncmp(op, "-d:",3)) {
+	    XrdSecProtocolssl::debug = atoi(op+3);
+	  } else if (!strncmp(op, "-cadir:",7)) {
+	    XrdSecProtocolssl::sslcadir = strdup(op+7);
+	  } else if (!strncmp(op, "-vomsdir:",6)) {
+	    XrdSecProtocolssl::sslvomsdir = strdup(op+6);
+	  } else if (!strncmp(op, "-cert:",6)) {
+	    XrdSecProtocolssl::sslcertfile = strdup(op+6);
+	  } else if (!strncmp(op, "-key:",5)) {
+	    XrdSecProtocolssl::sslkeyfile = strdup(op+5);
+	  } else if (!strncmp(op, "-ca:",4)) {
+	    XrdSecProtocolssl::verifydepth = atoi(op+4);
+	  } else if (!strncmp(op, "-t:",3)) {
+	    XrdSecProtocolssl::sslsessionlifetime = atoi(op+3);
+	  } else if (!strncmp(op, "-export:",8)) {
+	    XrdSecProtocolssl::sslproxyexportdir = strdup(op+8);
+	  } else if (!strncmp(op, "-gridmapfile:",13)) {
+	    XrdSecProtocolssl::gridmapfile = strdup(op+13);
+	  } else if (!strncmp(op, "-vomsmapfile:",13)) {
+	    XrdSecProtocolssl::vomsmapfile = strdup(op+13);
+	  } else if (!strncmp(op, "-mapuser:",9)) {
+	    XrdSecProtocolssl::mapuser = (bool) atoi(op+9);
+	  } else if (!strncmp(op, "-mapnobody:",11)) {
+	    XrdSecProtocolssl::mapnobody = (bool) atoi(op+11);
+	  } else if (!strncmp(op, "-mapgroup:",10)) {
+	    XrdSecProtocolssl::mapgroup = (bool) atoi(op+10);
+	  } else if (!strncmp(op, "-mapcernuser:",13)) {
+	    XrdSecProtocolssl::mapcerncertificates = (bool) atoi(op+13);
+	  }
+	}
+      }
+    }
+  } else {
+    if ( (mode == 'c') || (serverinitialized)) {
+      if (mode == 'c') {
+	for (int i=0; i< PROTOCOLSSL_MAX_CRYPTO_MUTEX; i++) {
+	  XrdSecProtocolssl::CryptoMutexPool[i] = 0;
+	}
+	XrdSecProtocolssl::sslcertfile = strdup("/etc/grid-security/hostcert.pem");
+	XrdSecProtocolssl::sslkeyfile  = strdup("/etc/grid-security/hostkey.pem");
+	XrdSecProtocolssl::sslcadir    = strdup("/etc/grid-security/certificates");
+	XrdSecProtocolssl::sslvomsdir  = (char*)"/etc/grid-security/vomsdir";
+      }
+      XrdSecProtocolssl::GetEnvironment();
+      XrdSecProtocolssl::isServer = 0;
+      if (serverinitialized) {
+	XrdSecProtocolssl::sslproxyexportplain = 0;
+      }
+    }
+  }
+
+  if (XrdSecProtocolssl::debug >= 4) {
+    SSLxTrace->What = TRACE_ALL | TRACE_Debug;
+  } else if (XrdSecProtocolssl::debug == 3 ) {
+    SSLxTrace->What |= TRACE_Authen;
+    SSLxTrace->What |= TRACE_Debug;
+    SSLxTrace->What |= TRACE_Identity;
+  } else if (XrdSecProtocolssl::debug == 2) {
+    SSLxTrace->What = TRACE_Debug;
+  } else if (XrdSecProtocolssl::debug == 1) {
+    SSLxTrace->What = TRACE_Identity;
+  } else SSLxTrace->What = 0;
+
+  // thread-saftey
+  if (PROTOCOLSSL_MAX_CRYPTO_MUTEX < CRYPTO_num_locks() ) {
+    fprintf(stderr,"Error: (%s) I don't have enough crypto mutexes as required by crypto_ssl [recompile increasing PROTOCOLSSL_MAX_CRYPTO_MUTEX to %d] \n",__FUNCTION__,CRYPTO_num_locks());
+    TRACE(Authen,"Error: I don't have enough crypto mutexes as required by crypto_ssl [recompile increasing PROTOCOLSSL_MAX_CRYPTO_MUTEX to " << (int)CRYPTO_num_locks() << "]");
+  } else {
+    TRACE(Authen,"====> SSL requires " << (int)CRYPTO_num_locks() << " mutexes for thread-safety");
+  }
+
+#if defined(OPENSSL_THREADS)
+  // thread support enabled
+  TRACE(Authen,"====> SSL with thread support!");
+#else
+  fprintf(stderr,"Error: (%s) SSL lacks thread support: Abort!");
+  TRACE(Authen,"Error: SSL lacks thread support: Abort!");
+#endif
+
+  // set callback functions
+  CRYPTO_set_locking_callback(protocolssl_lock);
+  CRYPTO_set_id_callback(protocolssl_id_callback);
+
+
+
+
+  if (XrdSecProtocolssl::isServer) {
+    TRACE(Authen,"====> debug         = " << XrdSecProtocolssl::debug);
+    TRACE(Authen,"====> cadir         = " << XrdSecProtocolssl::sslcadir);
+    TRACE(Authen,"====> keyfile       = " << XrdSecProtocolssl::sslkeyfile);
+    TRACE(Authen,"====> certfile      = " << XrdSecProtocolssl::sslcertfile);
+    TRACE(Authen,"====> verify depth  = " << XrdSecProtocolssl::verifydepth);
+    TRACE(Authen,"====> sess.lifetime = " << XrdSecProtocolssl::sslsessionlifetime);
+    TRACE(Authen,"====> gridmapfile   = " << XrdSecProtocolssl::gridmapfile);
+    TRACE(Authen,"====> vomsmapfile   = " << XrdSecProtocolssl::vomsmapfile);
+    TRACE(Authen,"====> mapuser       = " << XrdSecProtocolssl::mapuser);
+    TRACE(Authen,"====> mapnobody     = " << XrdSecProtocolssl::mapnobody);
+    TRACE(Authen,"====> mapgroup      = " << XrdSecProtocolssl::mapgroup);
+    TRACE(Authen,"====> mapcernuser   = " << XrdSecProtocolssl::mapcerncertificates);
+  } else {
+    if (XrdSecProtocolssl::debug) {
+      TRACE(Authen,"====> debug         = " << XrdSecProtocolssl::debug);
+      TRACE(Authen,"====> cadir         = " << XrdSecProtocolssl::sslcadir);
+      TRACE(Authen,"====> keyfile       = " << XrdSecProtocolssl::sslkeyfile);
+      TRACE(Authen,"====> certfile      = " << XrdSecProtocolssl::sslcertfile);
+      TRACE(Authen,"====> verify depth  = " << XrdSecProtocolssl::verifydepth);
+    }
+  }
+
+  if (XrdSecProtocolssl::isServer) {
+    XrdSecProtocolssl::sslproxyexportplain=0; // for security reasons a server should not export plain private keys
+    // check if we can map with a grid map file
+    if (XrdSecProtocolssl::mapuser && access(XrdSecProtocolssl::gridmapfile,R_OK)) {
+      fprintf(stderr,"Error: (%s) cannot access gridmapfile %s\n",__FUNCTION__,XrdSecProtocolssl::gridmapfile);
+      TRACE(Authen,"Error: cannot access gridmapfile "<< XrdOucString(XrdSecProtocolssl::gridmapfile));
+      return 0;
+    }
+    // check if we can map with a voms map file
+    if (XrdSecProtocolssl::mapgroup && access(XrdSecProtocolssl::vomsmapfile,R_OK)) {
+      fprintf(stderr,"Error: (%s) cannot access vomsmapfile %s\n",__FUNCTION__,XrdSecProtocolssl::vomsmapfile);
+      TRACE(Authen,"Error: cannot access vomsmapfile "<< XrdOucString(XrdSecProtocolssl::vomsmapfile));
+      return 0;
+    }
+    // check if we can export proxies
+    XrdOucString exportplain=XrdSecProtocolssl::sslproxyexportdir;
+    // if the export file starts with plain: we don't write the proxy with a passphrase
+    if (exportplain.beginswith("plain:")) {
+      XrdSecProtocolssl::sslproxyexportdir+=6;
+      XrdSecProtocolssl::sslproxyexportplain=true;
+      TRACE(Authen,"====> export plain proxy (warning: can be re-used out of daemon context) to dir: " << XrdSecProtocolssl::sslproxyexportdir);
+    }
+    if (XrdSecProtocolssl::sslproxyexportdir && access(XrdSecProtocolssl::sslproxyexportdir,R_OK | W_OK)) {
+      fprintf(stderr,"Error: (%s) cannot read/write proxy export directory %s\n",__FUNCTION__,XrdSecProtocolssl::sslproxyexportdir);
+      TRACE(Authen,"Error: cannot access proxyexportdir "<< XrdOucString(XrdSecProtocolssl::sslproxyexportdir));
+      return 0;
+    }
+  }
+
+  if (XrdSecProtocolssl::isServer) {
+    SSL_METHOD *meth;
+    // initialize openssl until the context is created 
+    SSL_load_error_strings();
+    SSLeay_add_ssl_algorithms();
+    
+    meth = (SSL_METHOD*)SSLv23_server_method();
+
+    XrdSecProtocolssl::ctx = SSL_CTX_new (meth);
+    if (!XrdSecProtocolssl::ctx) {
+      ERR_print_errors_fp(stderr);
+      return 0;
+    }
+      
+    if (SSL_CTX_use_certificate_file(XrdSecProtocolssl::ctx, XrdSecProtocolssl::sslcertfile, SSL_FILETYPE_PEM) <= 0) {
+      ERR_print_errors_fp(stderr);
+      return 0;
+    }
+    
+    if (SSL_CTX_use_PrivateKey_file(XrdSecProtocolssl::ctx,XrdSecProtocolssl::sslkeyfile, SSL_FILETYPE_PEM) <= 0) {
+      ERR_print_errors_fp(stderr);
+      return 0;
+    }
+      
+    if (!SSL_CTX_check_private_key(XrdSecProtocolssl::ctx)) {
+      fprintf(stderr,"Private key does not match the certificate public key\n");
+      return 0;
+    }
+
+    XrdSecProtocolssl::sslserverkeyfile=XrdSecProtocolssl::sslkeyfile;
+    // use the private server key as password for proxy private key export
+    memset(XrdSecProtocolssl::sslserverexportpassword,0,EXPORTKEYSTRENGTH); 
+
+    unsigned int seed = (unsigned int) (time(NULL) + (unsigned int) random());
+    srand(seed);
+    char rexportkey[16384];
+    rexportkey[0]=0;
+    for (int i=0; i < EXPORTKEYSTRENGTH; i++) {
+      XrdSecProtocolssl::sslserverexportpassword[i] = (unsigned char)(rand()%256);
+      if (!XrdSecProtocolssl::sslserverexportpassword[i]) XrdSecProtocolssl::sslserverexportpassword[i]++;
+      sprintf(rexportkey,"%s%x",rexportkey,XrdSecProtocolssl::sslserverexportpassword[i]);
+    }
+    XrdSecProtocolssl::sslserverexportpassword[EXPORTKEYSTRENGTH] = 0;
+    sprintf((char*)XrdSecProtocolssl::sslserverexportpassword,"1234567890");
+    // debug 
+    DEBUG("Created random export key: "<< rexportkey);
+    SSL_CTX_load_verify_locations(XrdSecProtocolssl::ctx, NULL,XrdSecProtocolssl::sslcadir);  
+    
+    // create the store
+    if (!XrdSecProtocolssl::store) {
+      DEBUG("Created SSL CRL store: " << XrdSecProtocolssl::store);
+      XrdSecProtocolssl::store = SSL_X509_STORE_create(NULL,XrdSecProtocolssl::sslcadir);
+      X509_STORE_set_flags(XrdSecProtocolssl::store,0);
+      XrdSecProtocolssl::storeLoadTime = time(NULL);
+    }
+    
+    XrdSecProtocolssl::ctx->verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+    grst_cadir   = XrdSecProtocolssl::sslcadir;
+    grst_vomsdir = XrdSecProtocolssl::sslvomsdir;
+    grst_depth   = XrdSecProtocolssl::verifydepth;
+    
+    SSL_CTX_set_cert_verify_callback(XrdSecProtocolssl::ctx,
+				     GRST_verify_cert_wrapper,
+				     (void *) NULL);
+    
+    SSL_CTX_set_verify(XrdSecProtocolssl::ctx, XrdSecProtocolssl::ctx->verify_mode,GRST_callback_SSLVerify_wrapper);
+    SSL_CTX_set_verify_depth(XrdSecProtocolssl::ctx, XrdSecProtocolssl::verifydepth + 1);
+
+    if(!SSL_CTX_set_generate_session_id(XrdSecProtocolssl::ctx, XrdSecProtocolssl::GenerateSession)) {
+      TRACE(Authen,"Cannot set session generator");
+      return 0;
+    }
+
+    //    SSL_CTX_set_quiet_shutdown(XrdSecProtocolssl::ctx,1);
+    SSL_CTX_set_options(XrdSecProtocolssl::ctx,  SSL_OP_ALL | SSL_OP_NO_SSLv2);
+    SSL_CTX_set_session_cache_mode(XrdSecProtocolssl::ctx, SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_AUTO_CLEAR );
+    SSL_CTX_set_session_id_context(XrdSecProtocolssl::ctx,(const unsigned char*) XrdSecProtocolssl::SessionIdContext,  strlen(XrdSecProtocolssl::SessionIdContext));
+    SSL_CTX_sess_set_new_cb(XrdSecProtocolssl::ctx, XrdSecProtocolssl::NewSession);
+  }
+  return (char *)"";
+}
+}
+
+/******************************************************************************/
+/*              X r d S e c P r o t o c o l u n i x O b j e c t               */
+/******************************************************************************/
+  
+extern "C"
+{
+XrdSecProtocol *XrdSecProtocolsslObject(const char              mode,
+                                         const char             *hostname,
+                                         const struct sockaddr  &netaddr,
+                                         const char             *parms,
+                                               XrdOucErrInfo    *erp)
+{
+   XrdSecProtocolssl *prot;
+
+// Return a new protocol object
+//
+   if (!(prot = new XrdSecProtocolssl(hostname, &netaddr)))
+      {const char *msg = "Secssl: Insufficient memory for protocol.";
+       if (erp) erp->setErrInfo(ENOMEM, msg);
+          else cerr <<msg <<endl;
+       return (XrdSecProtocol *)0;
+      }
+   
+// All done
+//
+   return prot;
+}
+}
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.hh b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f3d3273d7b084820d7f61db9bed994b290c08c05
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.hh
@@ -0,0 +1,246 @@
+/******************************************************************************/
+/*                                                                            */
+/*                 X r d S e c P r o t o c o l s s l . h h                    */
+/*                                                                            */
+/* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University  */
+/*                            All Rights Reserved                             */
+/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
+/*              DE-AC02-76-SFO0515 with the Department of Energy              */
+/******************************************************************************/
+
+//       $Id$
+
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <iostream>
+#include <stdlib.h>
+#include <strings.h>
+#include <grp.h>
+#include <pwd.h>
+
+#define OPENSSL_THREAD_DEFINES
+#include <openssl/opensslconf.h>
+
+#include <openssl/crypto.h>
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "XrdNet/XrdNetDNS.hh"
+#include "XrdOuc/XrdOucErrInfo.hh"
+#include "XrdOuc/XrdOucHash.hh"
+#include "XrdOuc/XrdOucString.hh"
+#include "XrdOuc/XrdOucTrace.hh"
+#include "XrdOuc/XrdOucTokenizer.hh"
+#include "XrdSys/XrdSysPthread.hh"
+#include "XrdSys/XrdSysLogger.hh"
+#include "XrdSec/XrdSecInterface.hh"
+#include "XrdSec/XrdSecTLayer.hh"
+#include "XrdSecssl/XrdSecProtocolsslTrace.hh"
+#include "libsslGridSite/grst_verifycallback.h"
+#include "libsslGridSite/gridsite.h"
+
+#define EXPORTKEYSTRENGTH 10
+
+#define PROTOCOLSSL_MAX_CRYPTO_MUTEX 256
+
+
+// fix for SSL 098 stuff and g++ 
+
+#ifdef R__SSL_GE_098
+#undef PEM_read_SSL_SESSION
+#undef PEM_write_SSL_SESSION
+
+#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( (void *(*)(void **, const unsigned char **, long int))d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(void **)x,cb,u)
+
+#define PEM_write_SSL_SESSION(fp,x) PEM_ASN1_write((int (*)(void*, unsigned char**))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,fp, (char *)x,  NULL,NULL,0,NULL,NULL)
+#endif
+
+#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+                         *((c)++)=(unsigned char)(((l)    )&0xff))
+
+#ifdef SUNCC
+#define __FUNCTION__ "-unknown-"
+#endif
+
+
+static XrdOucTrace        *SSLxTrace=0;
+
+class XrdSecProtocolssl;
+
+#define MAX_SESSION_ID_ATTEMPTS 10
+
+/******************************************************************************/
+/*              X r d S e c P r o t o c o l s s l C l a s s                   */
+/******************************************************************************/
+
+class XrdSecsslSessionLock {
+private:
+static  XrdSysMutex sessionmutex;
+  int sessionfd;
+  
+public:
+  XrdSecsslSessionLock() {sessionfd=0;}
+  bool SoftLock() { sessionmutex.Lock();return true;}
+  bool SoftUnLock() {sessionmutex.UnLock();return true;}
+#ifdef SUNCC
+  bool HardLock(const char* path) {return true;}
+  bool HardUnLock() {return true;}
+  ~XrdSecsslSessionLock() {sessionmutex.UnLock();}
+#else
+  bool HardLock(const char* path) {sessionfd = open(path,O_RDWR); if ( (sessionfd>0) && (!flock(sessionfd,LOCK_EX)))return true;return false;}
+  bool HardUnLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);sessionfd=0;}return true;}
+  ~XrdSecsslSessionLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);};sessionmutex.UnLock();}
+#endif
+
+};
+
+
+
+class XrdSecProtocolssl : public XrdSecTLayer
+{
+public:
+  friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor
+
+  XrdSecProtocolssl(const char* hostname, const struct sockaddr  *ipaddr) : XrdSecTLayer("ssl",XrdSecTLayer::isServer) {
+    credBuff    = 0;
+    ssl         = 0;
+    Entity.name = 0;
+    Entity.grps = 0;
+    Entity.endorsements = 0;
+    host        = hostname;
+    if (ipaddr)
+      Entity.host = (XrdNetDNS::getHostName((sockaddr&)*ipaddr));
+    else 
+      Entity.host = strdup("");
+    proxyBuff[0]=0;
+    client_cert=0;
+    server_cert=0;
+    ssl = 0 ;
+    clientctx = 0;
+  }
+  
+  
+  virtual void   secClient(int theFD, XrdOucErrInfo      *einfo);
+  virtual void   secServer(int theFD, XrdOucErrInfo      *einfo=0);
+
+  virtual void              Delete() {delete this;}
+
+
+  static int GenerateSession(const SSL* ssl, unsigned char *id, unsigned int *id_len);
+  static int NewSession(SSL* ssl, SSL_SESSION *pNew);
+  static int GetSession(SSL* ssl, SSL_SESSION *pNew);
+
+  static char*              SessionIdContext ;
+  static char*              sslcadir; 
+  static char*              sslvomsdir;
+  static char*              sslserverkeyfile; 
+  static char*              sslkeyfile;
+  static char*              sslcertfile;
+  static char*              sslproxyexportdir;
+  static bool               sslproxyexportplain;
+  static char               sslserverexportpassword[EXPORTKEYSTRENGTH+1];
+  
+  static char*              gridmapfile;
+  static char*              vomsmapfile;
+  static bool               mapuser;
+  static bool               mapnobody;
+  static bool               mapgroup;
+  static bool               mapcerncertificates;
+  static int                debug;
+  static time_t             sslsessionlifetime;
+  static bool               isServer;
+  static bool               forwardProxy;
+  static bool               allowSessions;
+  static X509_STORE*        store;  
+  static X509_LOOKUP*       lookup;
+  static int                verifydepth;
+  static int                verifyindex;
+  int                       sessionfd;
+  X509*    client_cert; 
+  X509*    server_cert;
+  XrdOucString              host;
+
+  // User/Group mapping
+  static void ReloadGridMapFile();
+  static void ReloadVomsMapFile();
+  static bool VomsMapGroups(const char* groups, XrdOucString& allgroups, XrdOucString& defaultgroup);
+
+  static void GetEnvironment();
+  static  XrdOucHash<XrdOucString>  gridmapstore;
+  static  XrdOucHash<XrdOucString>  vomsmapstore;
+  static  XrdOucHash<XrdOucString>  stringstore;
+  static  XrdSysMutex               StoreMutex;
+  static  XrdSysMutex               VomsMapMutex;
+  static  XrdSysMutex               GridMapMutex;
+  static  XrdSysMutex*              CryptoMutexPool[PROTOCOLSSL_MAX_CRYPTO_MUTEX];
+  // for error logging and tracing
+  static XrdSysLogger       Logger;
+  static XrdSysError        ssleDest;
+  static time_t             storeLoadTime;
+  
+  typedef struct {
+    int verbose_mode;
+    int verify_depth;
+    int always_continue;
+  } sslverify_t;
+  
+  char proxyBuff[16384];
+  static SSL_CTX* ctx;
+  SSL_CTX* clientctx;
+
+  XrdSysMutex SSLMutex;
+private:
+  
+  ~XrdSecProtocolssl() {
+    if (credBuff)    free(credBuff);
+    if (Entity.name) free(Entity.name);
+    if (Entity.grps) free(Entity.grps);
+    if (Entity.role) free(Entity.role);
+    if (Entity.host) free(Entity.host);
+    SSLMutex.Lock();
+    if (ssl) SSL_free(ssl);ssl=0;
+    if (client_cert) X509_free(client_cert);
+    if (server_cert) X509_free(server_cert);
+    SSLMutex.UnLock();    
+  }
+
+  static int Fatal(XrdOucErrInfo *erp, const char* msg, int rc);
+  
+  
+  struct sockaddr           hostaddr;      // Client-side only
+  char                     *credBuff;      // Credentials buffer (server)
+  int                       Step;          // Indicates step in authentication
+  
+  int sd;
+  int listen_sd;
+  struct sockaddr_in sa_serv;
+  struct sockaddr_in sa_cli;
+  SSL*     ssl;
+};
+
+extern "C"
+{
+  char  *XrdSecProtocolsslInit(const char     mode,
+			       const char    *parms,
+			       XrdOucErrInfo *erp);
+}
+
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTest.cc b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..abb94e49805c81c41c187646ef362824a36c76a1
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTest.cc
@@ -0,0 +1,135 @@
+
+#include <XrdSys/XrdSysLogger.hh>
+#include <XrdSec/XrdSecTLayer.hh>
+#include <XrdNet/XrdNetSocket.hh>
+#include <XrdNet/XrdNetOpts.hh>
+#include <XrdSecssl/XrdSecProtocolssl.hh>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#define TESTLOOP 100
+
+int main(int argc, char* argv[]) {
+  if (argc < 2) {
+    fprintf(stderr,"Error: you have to define if we are server or client!\n");
+    fprintf(stderr,"usage: xrdsslprotocoltest server|client [args]\n");
+    exit(-1);
+  }
+
+  // To silence a warning
+  EPNAME("main");
+  PRINT("dummy");
+
+  setenv("XrdSecDEBUG","10",1);
+  XrdSysLogger logger;
+  XrdSysError eDest(&logger,"ssltest");
+  
+  if (!strcmp("server",argv[1])) {
+    // server
+    const char* args="-d:10 -cadir:/etc/grid-security/certificates/";
+    if (argv[2]) {
+      args = argv[2];
+    }
+
+    struct sockaddr  netaddr;
+    XrdOucErrInfo error;
+    
+    XrdSecProtocolsslInit('s',args, &error);
+    XrdSecProtocolssl* protocol = new XrdSecProtocolssl("localhost",(const struct sockaddr*)&netaddr);
+    if (!protocol) {
+      fprintf(stderr,"Error: cannot create protocol object\n");
+      exit(-1);
+    }
+
+    XrdNetSocket* socket = new XrdNetSocket(&eDest);
+    socket->Open(0,12345,XRDNET_SERVER);
+
+    while(1) {
+      // do an infinite handshake loop
+      int theFd = socket->Accept();
+      if (theFd<=0) {
+	fprintf(stderr,"Accept failed on socket!\n");
+	exit(-1);
+      }
+      protocol->secServer(theFd, &error);
+      
+      fprintf(stderr,"Authentication done: [%d : %s]\n", error.getErrInfo(),error.getErrText());
+      close(theFd);
+    }
+
+    exit(0);
+  }  else {
+    if (!strcmp("client",argv[1])) {
+      // client
+      struct sockaddr  netaddr;
+      XrdOucErrInfo error;
+
+      XrdSecProtocolsslInit('c',"", &error);
+      XrdSecProtocolssl* protocol = new XrdSecProtocolssl("localhost",(const struct sockaddr*)&netaddr);
+      if (!protocol) {
+	fprintf(stderr,"Error: cannot create protocol object\n");
+	exit(-1);
+      }
+      XrdSecProtocolssl::allowSessions = false;
+      struct timeval tv1, tv2, tv3;
+      struct timezone tz;
+
+      gettimeofday(&tv1,&tz);
+      for (int i=0; i< TESTLOOP; i++) {
+	XrdNetSocket* socket = new XrdNetSocket(&eDest);
+	
+	socket->Open(0,12345);
+	
+	int theFd = socket->Detach();
+	if (theFd<=0) {
+	  fprintf(stderr,"unable to connect to socket\n");
+	  fprintf(stdout,"Client aborted: unable to connect to socket\n");
+	  exit(-1);
+	}
+	protocol->secClient(theFd, &error);
+	if (error.getErrInfo()) {
+	  fprintf(stderr,"Authentication done: [%d : %s]\n", error.getErrInfo(),error.getErrText());
+	  fprintf(stdout,"Client aborted: authentication failure: [%d : %s]\n", error.getErrInfo(),error.getErrText());
+	  exit(-1);
+	}
+
+	delete socket;
+      }
+      gettimeofday(&tv2,&tz);
+      XrdSecProtocolssl::allowSessions = true;
+      for (int i=0; i< TESTLOOP; i++) {
+	XrdNetSocket* socket = new XrdNetSocket(&eDest);
+	
+	socket->Open(0,12345);
+	
+	int theFd = socket->Detach();
+	
+	protocol->secClient(theFd, &error);
+	if (error.getErrInfo()) {
+	  fprintf(stderr,"Authentication done: [%d : %s]\n", error.getErrInfo(),error.getErrText());
+	  exit(-1);
+	}
+	delete socket;
+      }
+      gettimeofday(&tv3,&tz);
+
+      float inta = (((tv2.tv_sec-tv1.tv_sec) * 1000) + (tv2.tv_usec-tv1.tv_usec)/1000.0)/1000.0;
+      float intb = (((tv3.tv_sec-tv2.tv_sec) * 1000) + (tv3.tv_usec-tv2.tv_usec)/1000.0)/1000.0;
+      fprintf(stdout,"-----------------------------------------------------------------\n");
+      fprintf(stdout,"Tested %d iterations without and with sessions...\n",TESTLOOP);
+      fprintf(stdout,"-----------------------------------------------------------------\n");
+      fprintf(stdout,"Performance without Sessions: %.02f authentications/s\n",TESTLOOP/inta);
+      fprintf(stdout,"Performance with    Sessions: %.02f authentications/s\n",TESTLOOP/intb);
+      fprintf(stdout,"-----------------------------------------------------------------\n");
+      exit(0);
+    }
+  }
+  fprintf(stderr,"Error: you have to define if we are server or client!\n");
+  fprintf(stderr,"usage: xrdsslprotocoltest server|client\n");
+  exit(-1);
+}
+
+
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTrace.hh b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTrace.hh
new file mode 100644
index 0000000000000000000000000000000000000000..68f80913099e5d642f428b52d709cfd181c5ef1f
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTrace.hh
@@ -0,0 +1,41 @@
+// $Id$
+#ifndef ___SECSSL_TRACE_H___
+#define ___SECSSL_TRACE_H___
+/******************************************************************************/
+/*                                                                            */
+/*                    X r d S e c g s i T r a c e . h h                       */
+/*                                                                            */
+/* (C) 2008  A.J. Peters, CERN                                                */
+/*                                                                            */
+/******************************************************************************/
+
+#include <XrdOuc/XrdOucTrace.hh>
+
+#ifndef NODEBUG
+
+#include <iostream>
+
+#define QTRACE(act) (SSLxTrace && (SSLxTrace->What & TRACE_ ## act))
+#define PRINT(y)    {if (SSLxTrace) {SSLxTrace->Beg(epname); \
+                                       cerr <<y; SSLxTrace->End();}}
+#define TRACE(act,x) if (QTRACE(act)) PRINT(x)
+#define DEBUG(y)     TRACE(Debug,y)
+#define EPNAME(x)    const char *epname = x;
+
+#else
+
+#define QTRACE(x)
+#define  PRINT(x)
+#define  TRACE(x,y)
+#define  DEBUG(x)
+#define EPNAME(x)
+
+#endif
+
+#define TRACE_ALL      0x000f
+#define TRACE_Authenxx 0x0007
+#define TRACE_Authen   0x0004
+#define TRACE_Debug    0x0001
+#define TRACE_Identity 0x0002
+
+#endif
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/acinclude.m4 b/net/xrootd/src/xrootd/src/XrdSecssl/acinclude.m4
new file mode 100644
index 0000000000000000000000000000000000000000..d3fee5d2ff1537161c952b791a1cf451c9f65e2d
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/acinclude.m4
@@ -0,0 +1,274 @@
+dnl @synopsis ACX_LOCATEFILE(filename,path-list,[action if found],[action if not found])
+dnl
+dnl    Locates a file in a given search path
+dnl
+dnl    the directory containing the target file is available as acx_founddir
+dnl    the path-list is available as acx_searchpath
+dnl
+dnl    Author: Derek Feichtinger <derek.feichtinger@cern.ch>
+dnl    
+dnl    Version info: $Id: acinclude.m4,v 1.1 2010/01/13 11:41:24 ganis Exp $
+dnl    Checked in by $Author: ganis $
+dnl ========================================================================
+AC_DEFUN([ACX_LOCATEFILE],[
+  acx_searchpath="$2"
+  acx_founddir=""
+  AC_MSG_CHECKING([for $1 in $2])
+  for dir in $2 ; do
+     if test -f "$[]dir/$1" ; then
+        acx_founddir="$[]dir/"
+        break
+     fi
+  done
+  if test x"$[]acx_founddir" = x ; then
+    AC_MSG_RESULT([no])
+    ifelse([$4], ,: ,[$4])
+  else
+    AC_MSG_RESULT([found])
+    ifelse([$3], ,: ,[$3])
+  fi
+])
+
+dnl @synopsis ACX_MSG_ERROR(error-message)
+dnl
+dnl    like AC_MSG_ERROR, but also prints out some important
+dnl    environment settings
+AC_DEFUN([ACX_MSG_ERROR],[
+   AC_MSG_ERROR([$1
+  (CPPFLAGS="$[]CPPFLAGS)   (LDFLAGS="$[]LDFLAGS")
+  (CFLAGS="$[]CFLAGS")   (CXXFLAGS="$[]CXXFLAGS")])
+])
+
+dnl @synopsis ACX_WITH_BASEDIR
+dnl ############################################################
+dnl # give the user an easy way to specify a base installation
+dnl # directory dir, where headers and libraries are found in
+dnl # $dir/include and $dir/lib
+AC_DEFUN([ACX_WITH_BASEDIR],[
+AC_ARG_WITH(base-directory,
+            [  --with-base-directory=PATH   add PATH/include and PATH/lib to search paths],
+            [
+	     acx_base_incdir="$withval/include"
+	     acx_base_libdir="$withval/lib"
+	     acx_base_bindir="$withval/bin"
+             BASE_INCDIR="-I$withval/include"
+             BASE_LIBDIR="-L$withval/lib"
+            ]
+           )
+AC_SUBST(BASE_INCDIR)
+AC_SUBST(BASE_LIBDIR)
+])
+
+dnl @synopsis ACX_COLLECT_OPTION(optionname,[optvalue])
+dnl collects option names for an informative printout with
+dnl the ACX_PRINTOPTIONS macro. Relies on the existence of
+dnl an associated activate_FEATURE variable
+AC_DEFUN([ACX_COLLECT_OPTION],[
+   acx_optionvar="$[]acx_optionvar $1"
+   ifelse($2, , :,activate_$1=$2)
+#opt activate_$1=default_yes|default_no
+])
+
+dnl @synopsis ACX_LIBOPTION(optionname,enable-help-text,[yes|no])
+dnl
+dnl a default value for activate_FEATURE set before the evaluation of this
+dnl macro will be honored.
+dnl all options get collected in acx_optionvar for later printout with
+dnl the ACX_PRINTOPTIONS macro
+dnl specifying one of the --with-feature-*dir options will set the activate
+dnl state of the feature to yes, except if it has been deliberately turned off
+dnl with the --enable-feature=no or --disable-feature options
+AC_DEFUN([ACX_LIBOPTION],[
+   if test x"$[]activate_$1" = x; then 
+      ifelse($3, yes,activate_$1=default_yes,activate_$1=default_no)
+   fi
+   AC_ARG_ENABLE($1,[[  --enable-$1        $2 (default=$3)]],[[activate_$1=$enableval]],[[:]])
+   AC_ARG_WITH($1-libdir,[[  --with-$1-libdir=PATH    path containing $1 library]],
+                [acx_$1_libdir=$[]withval
+                if test $[]activate_$1 = default_no; then activate_$1=yes;fi
+                if test $[]activate_$1 = default_yes; then activate_$1=yes;fi
+   ])
+   AC_ARG_WITH($1-incdir,[[  --with-$1-incdir=PATH    path containing $1 headers]],
+                [acx_$1_incdir=$[]withval
+                if test $[]activate_$1 = default_no; then activate_$1=yes;fi
+                if test $[]activate_$1 = default_yes; then activate_$1=yes;fi
+   ])
+
+   if test x"$[]acx_$1_libdir" != x; then
+      translit($1,`a-z',`A-Z')_LIBDIR="-L$[]acx_$1_libdir"
+   fi
+   if test x"$[]acx_$1_incdir" != x; then
+      translit($1,`a-z',`A-Z')_INCDIR="-I$[]acx_$1_incdir"
+   fi
+
+   AC_SUBST(translit($1,`a-z',`A-Z')_LIBDIR)
+   AC_SUBST(translit($1,`a-z',`A-Z')_INCDIR)
+
+   ACX_COLLECT_OPTION($1)
+#opt acx_$1_libdir=PATH
+#opt acx_$1_incdir=PATH
+])
+
+dnl @synopsis ACX_PRINTOPTIONS
+dnl
+dnl prints a summary about option selections and default files
+dnl that have been read.
+dnl Relies on the oprions having been recorded by ACX_COLLECT_OPTION
+dnl or ACX_LIBOPTION and default files having been read with ACX_LOAD_DEFAULTS
+AC_DEFUN([ACX_PRINTOPTIONS],[
+  echo
+  if test x"$[]acx_defaultfiles" != x; then
+     echo "Defaults read from: $[]acx_defaultfiles"
+  fi
+
+  echo "SELECTED OPTIONS"
+  echo "----------------"
+  for opt in $[]acx_optionvar; do
+     optvar="activate_$[]opt"
+     optval="\$$[]optvar"
+     optval=`eval echo "$[]optval"`
+
+     optinc=acx_"$[]opt"_incdir
+     optincval="\$$[]optinc"
+     optincval=`eval echo "$[]optincval"`
+
+     optlib=acx_"$[]opt"_libdir
+     optlibval="\$$[]optlib"
+     optlibval=`eval echo "$[]optlibval"`
+
+     defaultval=`expr x"$[]optval" : 'xdefault_\(.*\)'`
+     if test x"$[]defaultval" != x; then
+        optval=$[]defaultval
+        bydefault="(by default)"
+     else
+        bydefault="            "
+     fi
+
+     if test x"$[]optval" != xyes; then
+        optincval="";optlibval=""
+     fi
+     echo "$[]opt:     $[]optval   $[]bydefault  $[]optincval  $[]optlibval"
+  done
+])
+
+dnl @synopsis ACX_LOAD_DEFAULTS(filename)
+dnl
+dnl Loads (sources) a file containing default settings.
+dnl The location of the file has to be given relative
+dnl to the srcdir
+AC_DEFUN([ACX_LOAD_DEFAULTS],[
+  acx_tmp="$[]srcdir/$1"
+  AC_MSG_CHECKING([for defaults file $[]acx_tmp])
+  if test -r "$[]acx_tmp"; then
+     AC_MSG_RESULT([found - sourcing...])
+     source $[]acx_tmp
+     acx_defaultfiles="$acx_defaultfiles $[]acx_tmp"
+  else
+     AC_MSG_RESULT([NOT FOUND])
+  fi
+])
+
+
+dnl AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl (c) 2008 Peter Simons <simons@cryp.to>
+dnl http://autoconf-archive.cryp.to/ax_have_epoll.html
+dnl
+dnl modified the acceptable Unix version for AX_HAVE_EPOLL
+dnl to be 2,6,12 (original macro had 2.5.45)
+AC_DEFUN([AX_HAVE_EPOLL], [dnl
+  ax_have_epoll_cppflags="${CPPFLAGS}"
+  AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
+  AC_MSG_CHECKING([for Linux epoll(7) interface])
+  AC_CACHE_VAL([ax_cv_have_epoll], [dnl
+    AC_LINK_IFELSE([dnl
+      AC_LANG_PROGRAM([dnl
+#include <sys/epoll.h>
+#ifdef HAVE_LINUX_VERSION_H
+#  include <linux/version.h>
+#  if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+#    error linux kernel version is too old to have epoll
+#  endif
+#endif
+], [dnl
+int fd, rc;
+struct epoll_event ev;
+fd = epoll_create(128);
+rc = epoll_wait(fd, &ev, 1, 0);])],
+      [ax_cv_have_epoll=yes],
+      [ax_cv_have_epoll=no])])
+  CPPFLAGS="${ax_have_epoll_cppflags}"
+  AS_IF([test "${ax_cv_have_epoll}" = "yes"],
+    [AC_MSG_RESULT([yes])
+$1],[AC_MSG_RESULT([no])
+$2])
+])dnl
+
+AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl
+  ax_have_epoll_cppflags="${CPPFLAGS}"
+  AC_CHECK_HEADER([linux/version.h],
+    [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"])
+  AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension])
+  AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl
+    AC_LINK_IFELSE([dnl
+      AC_LANG_PROGRAM([dnl
+#ifdef HAVE_LINUX_VERSION_H
+#  include <linux/version.h>
+#  if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#    error linux kernel version is too old to have epoll_pwait
+#  endif
+#endif
+#include <sys/epoll.h>
+#include <signal.h>
+], [dnl
+int fd, rc;
+struct epoll_event ev;
+fd = epoll_create(128);
+rc = epoll_wait(fd, &ev, 1, 0);
+rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])],
+      [ax_cv_have_epoll_pwait=yes],
+      [ax_cv_have_epoll_pwait=no])])
+  CPPFLAGS="${ax_have_epoll_cppflags}"
+  AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"],
+    [AC_MSG_RESULT([yes])
+$1],[AC_MSG_RESULT([no])
+$2])
+])dnl
+
+
+dnl AC_SYS_DEV_POLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl Dave Benson <daveb@ffem.org> 2008-04-12
+dnl from http://autoconf-archive.cryp.to/ac_sys_dev_poll.html
+dnl
+AC_DEFUN([AC_SYS_DEV_POLL], [AC_CACHE_CHECK(for /dev/poll support, ac_cv_dev_poll,
+    AC_TRY_COMPILE([#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/devpoll.h>],
+[
+  struct dvpoll p;
+  p.dp_timeout = 0;
+  p.dp_nfds = 0;
+  p.dp_fds = (struct pollfd *) 0;
+  return 0;
+],
+    ac_cv_dev_poll=yes
+    [$1],
+    ac_cv_dev_poll=no
+    [$2]
+    )
+  )
+])
+
+AC_DEFUN([ACX_PUT_PRIVATES],
+[
+        AH_BOTTOM([#ifdef __GNUC__
+#define UNUSED(z)  z __attribute__ ((unused))
+#define PRIVATE    __attribute__ ((visibility ("hidden")))
+#define PUBLIC     __attribute__ ((visibility ("default")))
+#else
+#define UNUSED
+#define PRIVATE
+#define PUBLIC
+#endif])])
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/bootstrap.sh b/net/xrootd/src/xrootd/src/XrdSecssl/bootstrap.sh
new file mode 100755
index 0000000000000000000000000000000000000000..65d7f9911b37bdddd940f7edb245afb3d71f7cec
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/bootstrap.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+libtoolize --copy --force
+aclocal
+automake -acf
+autoconf
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/configure.ac b/net/xrootd/src/xrootd/src/XrdSecssl/configure.ac
new file mode 100644
index 0000000000000000000000000000000000000000..fd5913becb5ff4cbeb45511a35616d7cef9851c4
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/configure.ac
@@ -0,0 +1,234 @@
+AC_PREREQ(2.57)
+AC_INIT(xrootd-secssl, 4.0.1,[andreas.joachim.peters@cern.ch])
+AC_CONFIG_SRCDIR([XrdSecProtocolsslTrace.hh])
+AC_CANONICAL_TARGET
+AM_INIT_AUTOMAKE(1.6 foreign)
+
+mkdir -p m4
+AC_CONFIG_MACRO_DIR([m4])
+
+case "$target_os" in
+   tiger*)
+     AC_DEFINE(__macos__)
+     ;;
+   darwin*)
+     AC_DEFINE(__macos__)
+     ;;
+   apple*)
+     AC_DEFINE(__macos__)
+     ;;
+   linux*)
+     # TODO: check why two different macros are used for this
+     AC_DEFINE(__linux__)
+     AC_DEFINE(__linux)
+     ;;
+   solaris*)
+     AC_DEFINE(__sun)
+     CC_PREFERENCE="cc gcc"
+     CXX_PREFERENCE="CC g++"
+     DFLT_OPT="-O2"
+     ;;
+   *)
+     AC_MSG_WARN([untested operating system])
+   ;;
+esac
+
+libversion=`libtoolize --version | head -1  | cut -d " " -f 4`
+case "$target_os" in
+   darwin*)
+   if test "x$libversion" = "x1.5.22"; then
+      AC_MSG_ERROR([broken libtool version $libversion found on this MAC - install newer one from http://www.gnu.org/software/libtool/news
+.html])
+   fi
+   ;;
+esac
+
+
+echo $libdir | grep lib64 >& /dev/null
+if test "$?" = "0" ; then 
+   MARK64=64;
+else
+   MARK64="";
+fi
+
+AC_SUBST(MARK64)
+
+AC_DEFINE(_LARGEFILE_SOURCE)
+AC_DEFINE(_FILE_OFFSET_BITS,64)
+AC_SYS_LARGEFILE
+
+# Do all testing using C++
+AC_LANG([C++])
+
+# Checks for programs.
+AC_PROG_CXX
+AC_PROG_LIBTOOL
+LT_INIT
+
+AC_ARG_WITH(xrootd-location,
+          [  --with-xrootd-location=installation of xrootd version to use],
+          [XROOTD_LOCATION=$withval],
+          [XROOTD_LOCATION=/opt/xrootd/])
+
+AC_ARG_WITH(xrootd-incdir,
+          [  --with-xrootd-incdir=installation of xrootd include files],
+          [XROOTD_INCDIR=$withval],
+          [XROOTD_INCDIR=$XROOTD_LOCATION/include/xrootd/])
+
+AC_ARG_WITH(xrootd-libdir,
+          [  --with-xrootd-libdir=installation of xrootd libraries],
+          [XROOTD_LIBDIR=$withval],
+          [XROOTD_LIBDIR=$XROOTD_LOCATION/lib])
+
+AC_SUBST(XROOTD_INCDIR)
+AC_SUBST(XROOTD_LIBDIR)
+
+if ! test -e "${XROOTD_INCDIR}/XrdOuc/XrdOucEnv.hh"
+then
+   AC_MSG_ERROR([xrootd header files could not be found in inc directory ${XROOTD_INCDIR}])
+fi
+
+# Check for xrootd library
+#if ! test -e "${XROOTD_LIBDIR}/libXrdOuc.so"
+#then
+#   AC_MSG_ERROR([xrootd library could not be found in lib directory ${XROOTD_LIBDIR}])
+#fi
+
+SSLLINKLIB="-L${XROOTD_LIBDIR}/ -lXrdOuc"
+
+####################################################################
+# OpenSSL support
+  ACX_LIBOPTION(openssl,[ssl library],yes)
+  ssltests=ok
+
+  if test x"$acx_openssl_incdir" != x; then
+     searchpath=$acx_openssl_incdir
+  else
+     searchpath="/usr/include"
+  fi
+
+  ACX_LOCATEFILE([openssl/opensslv.h],[$searchpath],
+                 [OPENSSL_INCDIR="-I$acx_founddir"
+                  OPENSSL_VERSION_TEXT=`grep OPENSSL_VERSION_TEXT "$dir"/openssl/opensslv.h | grep OpenSSL`
+                  AC_MSG_NOTICE([openssl header dir set to: $dir ]) ],
+                 [AC_MSG_ERROR([Could not locate openssl/opensslv.h])]
+                )
+
+  # test openssl version
+  verstr=`expr  x"$OPENSSL_VERSION_TEXT" : '.*\([[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*[[a-z]]*\)'`
+  AC_MSG_NOTICE([OpenSSL version : $verstr])
+
+  vermajor=`expr x"$verstr" : '.*\([[0-9]][[0-9]]*\).[[0-9]][[0-9]]*.[[0-9]][[0-9]]*'`
+  verminor=`expr x"$verstr" : '.*[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)\.[[0-9]][[0-9]]*'`
+  verrelease=`expr x"$verstr" : '.*[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)'`
+  vernum=`expr 10000 \* 0"$vermajor" + 100 \* 0"$verminor" + 0"$verrelease"`
+  if test 0"$vernum" -lt 906 ; then
+    AC_MSG_WARN([OpenSSL >= 0.9.6 required (found: $OPENSSL_VERSION_TEXT)
+                ssltests=failed])
+  fi
+
+  CPPFLAGS_BUP="$CPPFLAGS"
+  LDFLAGS_BUP=$LDFLAGS
+
+  CPPFLAGS="$OPENSSL_INCDIR $CPPFLAGS"
+  AC_CHECK_HEADER([openssl/ssl.h],
+                [],[ssltests=failed]
+                )
+
+
+  LDFLAGS="$OPENSSL_LIBDIR $LDFLAGS"
+  AC_CHECK_LIB([ssl],[SSL_library_init],
+               [:],[ssltests=failed],[-lcrypto]
+              )
+
+  if test x"$ssltests" != xok ; then
+    AC_MSG_ERROR([tests for openssl failed. Change configure options])
+  else
+    AC_DEFINE(R__SSL)
+    # note: -DPERL5 is needed for resolving a clash between unistd.h and
+    # openssl/des.h regarding the crypt function on some older systems (e.g. RH7.3)
+    if test 0"$vernum" -lt 907 ; then
+      AC_DEFINE(R__SSL_096)
+      AC_SUBST(SSLCXXFLAGS,[-DPERL5])
+    fi
+
+    # note: -DR__SSL_GE_098 allows to deal with some differences in ANS1 macros
+    # introduced in version 0.9.8
+    if test 0"$vernum" -ge 908 ; then
+      AC_DEFINE(R__SSL_GE_098)
+    fi
+  fi
+
+  CPPFLAGS="$CPPFLAGS_BUP"
+  LDFLAGS=$LDFLAGS_BUP
+
+####################################################################
+# libxml2 support
+  xmltests=ok
+  ACX_LIBOPTION(xml2,[xml library],yes)
+  if test x"$acx_xml2_incdir" != x; then
+     searchpath=$acx_xml2_incdir
+  else
+     searchpath="/usr/include/libxml2"
+  fi
+
+  ACX_LOCATEFILE([libxml/xmlmemory.h],[$searchpath],
+                 [XML2_INCDIR="-I$acx_founddir"
+                  AC_MSG_NOTICE([xml2 header dir set to: $dir ]) ],
+                 [AC_MSG_ERROR([Could not locate libxml/xmlmemory.h])]
+                )
+
+  CPPFLAGS_BUP="$CPPFLAGS"
+  LDFLAGS_BUP=$LDFLAGS
+
+  CPPFLAGS="$XML2_INCDIR $CPPFLAGS"
+
+  AC_CHECK_HEADER([libxml/xmlmemory.h],
+                [],[ssltests=failed]
+                )
+
+
+  LDFLAGS="$XML2_LIBDIR $LDFLAGS"
+  AC_CHECK_LIB([xml2],[xmlReaderForMemory],
+               [:],[xmltests=failed],[]
+              )
+
+  if test x"$xmltests" != xok ; then
+    AC_MSG_ERROR([tests for libxml2 failed. Change configure options])
+  else
+    AC_MSG_NOTICE([using xml settings: $XML2_INCDIR $XML2_LIBDIR ])
+  fi
+
+  CPPFLAGS="$CPPFLAGS_BUP"
+  LDFLAGS=$LDFLAGS_BUP
+
+AC_SUBST(XML2_INCDIR)
+AC_SUBST(XML2_LIBDIR)
+
+AC_SUBST(IS_XRDSECSSL)
+AC_SUBST(SSLLINKLIB)
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([sys/time.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_C_CONST
+AC_TYPE_MODE_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+
+# Checks for library functions.
+AC_FUNC_STAT
+AC_CHECK_FUNCS([gettimeofday mkdir strdup])
+
+AC_CONFIG_FILES([Makefile
+	libsslGridSite/Makefile 
+        xrootd-secssl.spec  
+] )
+
+test -L XrdSecssl || ln -s ./ XrdSecssl
+echo "=================================================="
+echo "Configuring for ../lib$MARK64 library directories"
+echo "=================================================="
+AC_OUTPUT
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/getAutotools.sh b/net/xrootd/src/xrootd/src/XrdSecssl/getAutotools.sh
new file mode 100755
index 0000000000000000000000000000000000000000..98a098fd938e214f53330fe1561d53e6a6e93353
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/getAutotools.sh
@@ -0,0 +1,214 @@
+#!/bin/bash
+
+############################################################################  
+# Little scriptlet to fetch and install an autotools development environment
+#
+# Author: Derek Feichtinger <derek.feichtinger@psi.ch>
+# Initial Version: 2007-06-02
+#
+# Version info: $Id$
+############################################################################  
+
+BUILDDIR=`pwd`/autotools-build
+INSTALLDIR=`pwd`/install
+
+VAUTOCONF=2.60
+VAUTOMAKE=1.10
+VLIBTOOL=1.5.22
+
+WGETSRC="http://ftp.gnu.org/gnu/"
+
+prompt=1
+
+unset WGET
+
+usage() {
+cat <<EOF
+Name: getAutotools.sh - fetch and install an autotools development environment
+
+Synopsis: getAutotools.sh [options]
+
+   Options:
+       -a autoconf version (default: $VAUTOCONF)
+       -m automake version (default: $VAUTOMAKE)
+       -l libtool version (default: $VLIBTOOL)
+       -b build directory (default: $BUILDDIR)
+       -i install directory (default: $INSTALLDIR)
+       -f do not prompt before installing
+
+Author: Derek Feichtinger <derek.feichtinger@psi.ch>
+EOF
+}
+
+testfor() {
+    p=`which $1`
+    status=$?
+    if test 0"$status" -ne 0; then
+	echo "Error: Could not find $1 in PATH" >&2
+        echo "notfound"
+	return
+    fi
+    # following test needed on macos/darwin since "which" is
+    # horribly broken (If command is not found prints Error
+    # to stdout and returns a status of 0)
+    if test ! -e "$p"; then
+        echo "Error: Could not find $1 in PATH" >&2
+        echo "notfound"
+	return
+    fi
+
+    echo "$p"
+    echo "################################# $p" >&2
+}
+
+fetch_archive() {
+    product=`expr $1 : '\([a-zA-Z]*\)-'`
+    if test x"$1" = x; then
+	echo "Error: fetch_archives() called without argument" >&2
+	exit 1
+    fi
+    if test ! -e $1.tar; then
+	if test ! -e $1.tar.gz; then
+	    test -z $WGET && WGET=`testfor wget`
+	    if test x"$WGET" = xnotfound; then
+		echo "you need to manually get $WGETSRC/$product/$1.tar.gz " \
+		    "and place it in $BUILDDIR" >&2
+		exit 1
+	    fi
+	    $WGET $WGETSRC/$product/$1.tar.gz
+	    if test ! -e $1.tar.gz; then
+		echo "Error: Failed to fetch tarball: $WGETSRC/$product/$1.tar.gz" >&2
+		exit 1
+	    fi
+	fi
+	gunzip $1.tar.gz
+	status=$?
+	if test 0"$status" -ne 0; then
+	    echo "Error: gunzip of $BUILDDIR/$1.tar.gz failed. Probably broken tarball." >&2
+	    echo "Remove the tarball and retry" >&2
+	    exit 1
+	fi	
+    fi
+}
+
+
+build_install() {
+    #product=`expr $1 : '\([^-]*\)'`
+    product=`expr $1 : '\([a-zA-Z]*\)-'`
+    if test x"$1" = x; then
+	echo "Error: build_install() called without argument" >&2
+	exit 1
+    fi
+
+    echo "######################## BUILDING AND INSTALLING $1"
+    cd $BUILDDIR
+    tar xvf $1.tar
+    status=$?
+    if test 0"$status" -ne 0; then
+	echo "Error: Failed to extract $1.tar" >&2
+	exit 1
+    fi	
+    cd $1
+    ./configure --prefix=$INSTALLDIR
+    make install
+    status=$?
+    if test 0"$status" -ne 0; then
+	echo "Error: make install of $1 failed" >&2
+	exit 1
+    fi	
+    if test ! -x $INSTALLDIR/bin/$product; then
+	echo "Error: Check for installed product failed: $INSTALLDIR/bin/$product" >&2
+	exit 1
+    fi
+}
+
+
+
+TEMP=`getopt -o a:b:m:l:hi:f --long help -n 'getAutotools' -- "$@"`
+if [ $? != 0 ] ; then usage ; echo "Terminating..." >&2 ; exit 1 ; fi
+#echo "TEMP: $TEMP"
+eval set -- "$TEMP"
+
+while true; do
+    case "$1" in
+        --help|-h)
+            usage
+            exit
+            ;;
+        -a)
+            VAUTOCONF="$2"
+            shift 2
+            ;;
+        -m)
+            VAUTOMAKE="$2"
+            shift 2
+            ;;
+        -l)
+            VLIBTOOL="$2"
+            shift 2
+            ;;
+        -b)
+            BUILDDIR="$2"
+            shift 2
+            ;;
+        -i)
+            INSTALLDIR="$2"
+            shift 2
+            ;;
+	-f)
+	    prompt=0
+	    shift
+	    ;;
+        --)
+            shift;
+            break;
+            ;;
+        *)
+            echo "Internal error!"
+            exit 1
+            ;;
+    esac
+done
+
+
+
+AUTOCONF=autoconf-$VAUTOCONF
+AUTOMAKE=automake-$VAUTOMAKE
+LIBTOOL=libtool-$VLIBTOOL
+
+if test 0"$prompt" -eq 1; then
+    cat <<EOF
+INSTALLATION SETTINGS:
+
+       autoconf version  : $VAUTOCONF
+       automake version  : $VAUTOMAKE
+       libtool version   : $VLIBTOOL
+       build directory   : $BUILDDIR
+       install directory : $INSTALLDIR
+
+Is this ok?   (Y/n)
+EOF
+    read a
+    if test x"$a" != xy -a x"$a" != xY; then
+	usage
+	exit 0
+    fi
+fi
+
+
+mkdir -p $BUILDDIR
+cd $BUILDDIR
+BUILDDIR=`pwd`
+
+export PATH=$INSTALLDIR/bin:$PATH
+for n in $AUTOCONF $AUTOMAKE $LIBTOOL; do
+    fetch_archive $n
+done
+for n in $AUTOCONF $AUTOMAKE $LIBTOOL; do
+    build_install $n
+done
+
+echo "############################################################"
+echo "In order to use this autotools environment you need to"
+echo "export PATH=$INSTALLDIR/bin:\$PATH"
+ 
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/Makefile.am b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..412cab3c7284877763e0617b6ed5a1dd2272596f
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/Makefile.am
@@ -0,0 +1,23 @@
+
+
+noinst_LTLIBRARIES = libsslGridSite.la
+INCLUDES = ${OPENSSL_INCDIR} -I/usr/kerberos/include -I. ${XML2_INCDIR}
+
+libsslGridSite_la_CFLAGS = -Wall -Wno-deprecated-declarations -Wno-deprecated-declarations -DOPENSSL_NO_KRB5 -U_FORTIFY_SOURCE ${CFLAGS}
+
+libsslGridSite_la_SOURCES = \
+	grst_err.c  \
+	grst_asn1.c \
+	grst_x509.c \
+	grst_gacl.c \
+	grst_xacml.c \
+	grst_http.c \
+	grst_verifycallback.c \
+	grst_verifycallback.h \
+	gridsite.h
+
+
+
+
+
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/gridsite.h b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/gridsite.h
new file mode 100644
index 0000000000000000000000000000000000000000..4606f93e1e2f189d15164789603b73e9b9471008
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/gridsite.h
@@ -0,0 +1,454 @@
+/*
+   Copyright (c) 2002-7, 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 about GridSite: http://www.gridsite.org/             *
+ *---------------------------------------------------------------*/
+
+#ifndef GRST_VERSION
+#define GRST_VERSION 010500
+#endif
+
+#ifndef GRST_NO_OPENSSL
+
+#ifndef HEADER_SSL_H
+#include <openssl/ssl.h>
+#endif
+
+#ifndef HEADER_CRYPTO_H
+#include <openssl/crypto.h>
+#endif
+#endif
+
+#ifndef _TIME_H
+#include <time.h>
+#endif
+
+#ifndef _STDIO_H
+#include <stdio.h>
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+// Everything ok (= OpenSSL X509_V_OK)
+#define GRST_RET_OK		0
+
+// Failed for unspecified reason
+#define GRST_RET_FAILED		1000
+
+// Failed to find certificate in some cert store / directory
+#define GRST_RET_CERT_NOT_FOUND	1001
+
+// Bad signature
+#define GRST_RET_BAD_SIGNATURE	1002
+
+// No such file or directory
+#define GRST_RET_NO_SUCH_FILE	1003
+
+
+// #define GRSTerrorLog(GRSTerrorLevel, GRSTerrorFmt, ...) if (GRSTerrorLogFunc != NULL) (GRSTerrorLogFunc)(__FILE__, __LINE__, GRSTerrorLevel, GRSTerrorFmt, __VA_ARGS__)
+
+#define GRSTerrorLog(GRSTerrorLevel, ...) if (GRSTerrorLogFunc != NULL) (GRSTerrorLogFunc)(__FILE__, __LINE__, GRSTerrorLevel, __VA_ARGS__)
+
+extern void (*GRSTerrorLogFunc)(char *, int, int, char *, ...);
+
+/* these levels are the same as Unix syslog() and Apache ap_log_error() */
+
+#define GRST_LOG_EMERG   0
+#define GRST_LOG_ALERT   1
+#define GRST_LOG_CRIT    2
+#define GRST_LOG_ERR     3
+#define GRST_LOG_WARNING 4
+#define GRST_LOG_NOTICE  5
+#define GRST_LOG_INFO    6
+#define GRST_LOG_DEBUG   7
+
+#define GRST_MAX_TIME_T	 INT32_MAX
+
+typedef struct { char                      *auri;
+                 int			    delegation;
+                 int			    nist_loa;
+                 time_t			    notbefore;
+                 time_t			    notafter;
+                 void                      *next;     } GRSTgaclCred;
+
+/* used by pre-AURI GRSTgaclCred structs */ 
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+typedef struct { char                      *name;
+                 char                      *value;
+                 void                      *next;  } GRSTgaclNamevalue;
+
+typedef int                GRSTgaclAction;
+typedef int                GRSTgaclPerm;
+ 
+typedef struct { GRSTgaclCred   *firstcred;
+                 GRSTgaclPerm    allowed;
+                 GRSTgaclPerm    denied;
+                 void           *next;    } GRSTgaclEntry;
+ 
+typedef struct { GRSTgaclEntry *firstentry; } GRSTgaclAcl;
+ 
+typedef struct { GRSTgaclCred *firstcred; char *dnlists; } GRSTgaclUser;
+
+#define GRST_PERM_NONE   0
+#define GRST_PERM_READ   1
+#define GRST_PERM_EXEC   2
+#define GRST_PERM_LIST   4
+#define GRST_PERM_WRITE  8
+#define GRST_PERM_ADMIN 16
+#define GRST_PERM_ALL   31
+
+/* DO NOT USE PermIsNone!! */
+#define GRSTgaclPermIsNone(perm)    ((perm) == 0)
+
+#define GRSTgaclPermHasNone(perm)    ((perm) == 0)
+#define GRSTgaclPermHasRead(perm)  (((perm) & GRST_PERM_READ ) != 0)
+#define GRSTgaclPermHasExec(perm)  (((perm) & GRST_PERM_EXEC ) != 0)
+#define GRSTgaclPermHasList(perm)  (((perm) & GRST_PERM_LIST ) != 0)
+#define GRSTgaclPermHasWrite(perm) (((perm) & GRST_PERM_WRITE) != 0)
+#define GRSTgaclPermHasAdmin(perm) (((perm) & GRST_PERM_ADMIN) != 0)
+
+#define GRST_ACTION_ALLOW 0
+#define GRST_ACTION_DENY  1
+
+#define GRST_HIST_PREFIX  ".grsthist"
+#define GRST_ACL_FILE     ".gacl"
+#define GRST_DN_LISTS     "/etc/grid-security/dn-lists"
+#define GRST_RECURS_LIMIT 9
+
+#define GRST_PROXYCERTINFO_OID	"1.3.6.1.4.1.3536.1.222"
+#define GRST_VOMS_OID		"1.3.6.1.4.1.8005.100.100.5"
+#define GRST_VOMS_DIR		"/etc/grid-security/vomsdir"
+
+#define GRST_ASN1_MAXCOORDLEN	50
+#define GRST_ASN1_MAXTAGS	500
+
+struct GRSTasn1TagList { char treecoords[GRST_ASN1_MAXCOORDLEN+1];
+                         int  start;
+                         int  headerlength;
+                         int  length;
+                         int  tag; } ;
+
+typedef struct { int    type;		/* CA, user, proxy, VOMS, ... */
+                 int    errors;		/* unchecked, bad sig, bad time */
+                 char   *issuer;	/* Cert CA DN, EEC of PC, or VOMS DN */
+                 char   *dn;		/* Cert DN, or VOMS AC holder DN */
+                 char   value[16384];	/* VOMS FQAN or NULL */
+                 time_t notbefore;
+                 time_t notafter;
+                 int    delegation;	/* relative to END of any chain */
+                 int    serial;
+                 char   *ocsp;		/* accessLocation field */
+                 void   *raw;		/* X509 or VOMS Extension object */
+                 void   *next; } GRSTx509Cert;
+
+#define GRST_CERT_BAD_FORMAT 1
+#define GRST_CERT_BAD_CHAIN  2
+#define GRST_CERT_BAD_SIG    4
+#define GRST_CERT_BAD_TIME   8
+#define GRST_CERT_BAD_OCSP  16
+
+#define GRST_CERT_TYPE_CA    1
+#define GRST_CERT_TYPE_EEC   2
+#define GRST_CERT_TYPE_PROXY 3
+#define GRST_CERT_TYPE_VOMS  4
+
+/* a chain of certs, starting from the first CA */
+typedef struct { GRSTx509Cert *firstcert; } GRSTx509Chain;
+
+#ifndef GRST_NO_OPENSSL
+int GRSTx509CertLoad(GRSTx509Cert *, X509 *);
+int GRSTx509ChainLoadCheck(GRSTx509Chain **, STACK_OF(X509) *, X509 *, char *, char *);
+#endif
+int GRSTx509ChainFree(GRSTx509Chain *);
+
+#define GRST_HTTP_PORT		777
+#define GRST_HTTPS_PORT		488
+#define GRST_HTCP_PORT		777
+#define GRST_GSIFTP_PORT	2811
+                         
+#define GRSThtcpNOPop 0
+#define GRSThtcpTSTop 1
+
+typedef struct { unsigned char length_msb;
+                 unsigned char length_lsb;
+                 char text[1]; } GRSThtcpCountstr;
+
+#define GRSThtcpCountstrLen(string) (256*((string)->length_msb) + (string)->length_lsb)
+
+typedef struct { unsigned char total_length_msb;
+                 unsigned char total_length_lsb;
+                 unsigned char version_msb;
+                 unsigned char version_lsb;
+                 unsigned char data_length_msb;
+                 unsigned char data_length_lsb;
+                 unsigned int  response : 4;
+                 unsigned int  opcode   : 4;
+                 unsigned int  rr       : 1;                 
+                 unsigned int  f1       : 1;
+                 unsigned int  reserved : 6;
+                 unsigned int  trans_id;	/* must be 4 bytes */
+                 GRSThtcpCountstr *method;
+                 GRSThtcpCountstr *uri;
+                 GRSThtcpCountstr *version;
+                 GRSThtcpCountstr *req_hdrs;
+                 GRSThtcpCountstr *resp_hdrs;
+                 GRSThtcpCountstr *entity_hdrs;
+                 GRSThtcpCountstr *cache_hdrs;   } GRSThtcpMessage;
+
+int GRSTgaclInit(void);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+GRSTgaclCred *GRSTgaclCredNew(char *);
+
+GRSTgaclCred *GRSTgaclCredCreate(char *, char *);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+int	GRSTgaclCredAddValue(GRSTgaclCred *, char *, char *);
+
+#define GRSTgaclCredGetAuri(cred) ((cred)->auri)
+
+#define GRSTgaclCredSetNotBefore(cred, time) ((cred)->notbefore = (time))
+#define GRSTgaclCredGetNotBefore(cred) ((cred)->notbefore)
+
+#define GRSTgaclCredSetNotAfter(cred, time) ((cred)->notafter = (time))
+#define GRSTgaclCredGetNotAfter(cred) ((cred)->notafter)
+
+#define GRSTgaclCredSetDelegation(cred, level) ((cred)->delegation = (level))
+#define GRSTgaclCredGetDelegation(cred) ((cred)->delegation)
+
+#define GRSTgaclCredSetNistLoa(cred, level) ((cred)->nist_loa = (level))
+#define GRSTgaclCredGetNistLoa(cred) ((cred)->nist_loa)
+
+/* #define GACLfreeCred(x)		GRSTgaclCredFree((x)) */
+int        GRSTgaclCredFree(GRSTgaclCred *);
+
+/*  #define GACLaddCred(x,y)	GRSTgaclEntryAddCred((x),(y)) */
+int        GRSTgaclEntryAddCred(GRSTgaclEntry *, GRSTgaclCred *);
+
+/*  #define GACLdelCred(x,y)	GRSTgaclEntryDelCred((x),(y)) */
+int        GRSTgaclEntryDelCred(GRSTgaclEntry *, GRSTgaclCred *);
+
+/*  #define GACLprintCred(x,y)	GRSTgaclCredPrint((x),(y)) */
+int        GRSTgaclCredCredPrint(GRSTgaclCred *, FILE *);
+
+int	   GRSTgaclCredCmpAuri(GRSTgaclCred *, GRSTgaclCred *);
+
+/*  #define GACLnewEntry(x)		GRSTgaclEntryNew((x)) */
+GRSTgaclEntry *GRSTgaclEntryNew(void);
+
+/*  #define GACLfreeEntry(x)	GRSTgaclEntryFree((x)) */
+int        GRSTgaclEntryFree(GRSTgaclEntry *);
+
+/*  #define GACLaddEntry(x,y)	GRSTgaclAclAddEntry((x),(y)) */
+int        GRSTgaclAclAddEntry(GRSTgaclAcl *, GRSTgaclEntry *);
+
+/*  #define GACLprintEntry(x,y)	GRSTgaclEntryPrint((x),(y)) */
+int        GRSTgaclEntryPrint(GRSTgaclEntry *, FILE *);
+
+
+/*  #define GACLprintPerm(x,y)	GRSTgaclPermPrint((x),(y)) */
+int        GRSTgaclPermPrint(GRSTgaclPerm, FILE *);
+
+/*  #define GACLallowPerm(x,y)	GRSTgaclEntryAllowPerm((x),(y)) */
+int        GRSTgaclEntryAllowPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+/*  #define GACLunallowPerm(x,y)	GRSTgaclEntryUnallowPerm((x),(y)) */
+int        GRSTgaclEntryUnallowPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+/*  #define GACLdenyPerm(x,y)	GRSTgaclEntryDenyPerm((x),(y)) */
+int        GRSTgaclEntryDenyPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+/*  #define GACLundenyPerm(x,y)	GRSTgaclEntryUndenyPerm((x),(y)) */
+int        GRSTgaclEntryUndenyPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+/*  #define GACLpermToChar(x)	GRSTgaclPermToChar((x)) */
+char      *GRSTgaclPermToChar(GRSTgaclPerm);
+
+/*  #define GACLcharToPerm(x)	GRSTgaclPermFromChar((x)) */
+GRSTgaclPerm   GRSTgaclPermFromChar(char *);
+
+/*  #define GACLnewAcl(x)		GRSTgaclAclNew((x)) */
+GRSTgaclAcl   *GRSTgaclAclNew(void);
+
+/*  #define GACLfreeAcl(x)		GRSTgaclAclFree((x)) */
+int        GRSTgaclAclFree(GRSTgaclAcl *);
+
+/*  #define GACLprintAcl(x,y)	GRSTgaclAclPrint((x),(y)) */
+int        GRSTgaclAclPrint(GRSTgaclAcl *, FILE *);
+
+/*  #define GACLsaveAcl(x,y)	GRSTgaclAclSave((y),(x)) */
+int        GRSTgaclAclSave(GRSTgaclAcl *, char *);
+
+/*  #define GACLloadAcl(x)		GRSTgaclFileLoadAcl((x)) */
+GRSTgaclAcl   *GRSTgaclAclLoadFile(char *);
+
+/*  #define GACLfindAclForFile(x)	GRSTgaclFileFindAclname((x)) */
+char      *GRSTgaclFileFindAclname(char *);
+
+/*  #define GACLloadAclForFile(x)	GRSTgaclFileLoadAcl((x)) */
+GRSTgaclAcl   *GRSTgaclAclLoadforFile(char *);
+
+/*  #define GACLisAclFile(x)	GRSTgaclFileIsAcl((x)) */
+int        GRSTgaclFileIsAcl(char *);
+
+
+/*  #define GACLnewUser(x)		GRSTgaclUserNew((x)) */
+GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *);
+
+/*  #define GACLfreeUser(x)		GRSTgaclUserFree((x)) */
+int       GRSTgaclUserFree(GRSTgaclUser *);
+
+/*  #define GACLuserAddCred(x,y)	GRSTgaclUserAddCred((x),(y)) */
+int       GRSTgaclUserAddCred(GRSTgaclUser *, GRSTgaclCred *);
+
+/*  #define GACLuserHasCred(x,y)	GRSTgaclUserHasCred((x),(y)) */
+int       GRSTgaclUserHasCred(GRSTgaclUser *, GRSTgaclCred *);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+int       GRSTgaclUserSetDNlists(GRSTgaclUser *, char *);
+
+int       GRSTgaclUserLoadDNlists(GRSTgaclUser *, char *);
+
+/*  #define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y)) */
+GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *, char *);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+int GRSTgaclDNlistHasUser(char *, GRSTgaclUser *);
+
+int GRSTgaclUserHasAURI(GRSTgaclUser *, char *);
+
+/*  #define GACLtestUserAcl(x,y)	GRSTgaclAclTestUser((x),(y)) */
+GRSTgaclPerm   GRSTgaclAclTestUser(GRSTgaclAcl *, GRSTgaclUser *);
+
+/*  #define GACLtestExclAcl(x,y)	GRSTgaclAclTestexclUser((x),(y)) */
+GRSTgaclPerm   GRSTgaclAclTestexclUser(GRSTgaclAcl *, GRSTgaclUser *);
+
+char      *GRSThttpUrlDecode(char *);
+
+/*  #define GACLurlEncode(x)	GRSThttpUrlEncode((x)) */
+char      *GRSThttpUrlEncode(char *);
+
+/*  #define GACLmildUrlEncode(x)	GRSThttpMildUrlEncode((x)) */
+char      *GRSThttpUrlMildencode(char *);
+
+int GRSTx509NameCmp(char *, char *);
+
+#ifndef GRST_NO_OPENSSL
+int GRSTx509KnownCriticalExts(X509 *);
+
+int GRSTx509IsCA(X509 *);
+int GRSTx509CheckChain(int *, X509_STORE_CTX *);
+int GRSTx509VerifyCallback(int, X509_STORE_CTX *);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+int GRSTx509GetVomsCreds(int *, int, size_t, char *, X509 *, STACK_OF(X509) *, char *);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+GRSTgaclCred *GRSTx509CompactToCred(char *);
+
+#ifndef SUNCC
+__attribute__ ((deprecated))
+#endif
+int GRSTx509CompactCreds(int *, int, size_t, char *, STACK_OF(X509) *, char *, X509 *);
+#endif 
+
+char *GRSTx509CachedProxyFind(char *, char *, char *);
+char *GRSTx509FindProxyFileName(void);
+int GRSTx509MakeProxyCert(char **, FILE *, char *, char *, char *, int);
+char *GRSTx509CachedProxyKeyFind(char *, char *, char *);
+int GRSTx509ProxyDestroy(char *, char *, char *);
+int GRSTx509ProxyGetTimes(char *, char *, char *, time_t *, time_t *);
+int GRSTx509CreateProxyRequest(char **, char **, char *);
+int GRSTx509MakeProxyRequest(char **, char *, char *, char *);
+
+char *GRSTx509MakeDelegationID(void);
+
+#ifndef GRST_NO_OPENSSL
+int GRSTx509StringToChain(STACK_OF(X509) **, char *);
+char *GRSTx509MakeProxyFileName(char *, STACK_OF(X509) *);
+#endif
+
+int GRSTx509CacheProxy(char *, char *, char *, char *);
+
+#define GRST_HEADFILE   "gridsitehead.txt"
+#define GRST_FOOTFILE   "gridsitefoot.txt"
+#define GRST_ADMIN_FILE "gridsite-admin.cgi"
+
+typedef struct { char *text;
+                 void *next; } GRSThttpCharsList;
+
+typedef struct { size_t             size;
+                 GRSThttpCharsList *first;
+                 GRSThttpCharsList *last;  } GRSThttpBody;
+
+void  GRSThttpBodyInit(GRSThttpBody *); 
+void  GRSThttpPrintf(GRSThttpBody *, char *, ...);
+int   GRSThttpCopy(GRSThttpBody *, char *);
+void  GRSThttpWriteOut(GRSThttpBody *);
+int   GRSThttpPrintHeaderFooter(GRSThttpBody *, char *, char *);
+int   GRSThttpPrintHeader(GRSThttpBody *, char *);
+int   GRSThttpPrintFooter(GRSThttpBody *, char *);
+char *GRSThttpGetCGI(char *);
+
+time_t GRSTasn1TimeToTimeT(unsigned char *, size_t);
+int    GRSTasn1SearchTaglist(struct GRSTasn1TagList taglist[], int, char *);
+#ifndef GRST_NO_OPENSSL
+int    GRSTasn1ParseDump(BIO *, unsigned char *, long,
+                         struct GRSTasn1TagList taglist[], int, int *);
+#endif
+int    GRSTasn1GetX509Name(char *, int, char *, char *,
+                           struct GRSTasn1TagList taglist[], int);
+
+int    GRSThtcpNOPrequestMake(char **, int *, unsigned int);
+int    GRSThtcpNOPresponseMake(char **, int *, unsigned int);
+int    GRSThtcpTSTrequestMake(char **, int *, unsigned int, char *, char *, char *);
+int    GRSThtcpTSTresponseMake(char **, int *, unsigned int, char *, char *, char *);
+int    GRSThtcpMessageParse(GRSThtcpMessage *, char *, int);
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_asn1.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_asn1.c
new file mode 100644
index 0000000000000000000000000000000000000000..459ad0e59f999161f933b5b5de5db3b155efff44
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_asn1.c
@@ -0,0 +1,535 @@
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifndef GRST_NO_OPENSSL
+#include <openssl/x509_vfy.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include <openssl/buffer.h>
+#include <openssl/objects.h>
+#include <openssl/asn1.h>
+#endif
+
+#ifdef SUNCC
+time_t timegm (struct tm *tm) {
+  time_t ret;
+  char *tz;
+
+  tz = getenv("TZ");
+  setenv("TZ", "", 1);
+  tzset();
+  ret = mktime(tm);
+  if (tz)
+    setenv("TZ", tz, 1);
+  else
+    unsetenv("TZ");
+  tzset();
+  return ret;
+}
+#endif
+
+#include "gridsite.h"
+
+#ifdef R__SSL_GE_098
+#define SSLARG const unsigned char**
+#else
+#define SSLARG unsigned char**
+#endif
+
+/// ASN1 time string (in a char *) to time_t
+/**
+ *  (Use ASN1_STRING_data() to convert ASN1_GENERALIZEDTIME to char * if
+ *   necessary)
+ */
+                                                                                
+time_t GRSTasn1TimeToTimeT(unsigned char *asn1time, size_t len)
+{
+   char   zone;
+   struct tm time_tm;
+   
+   if (len == 0) len = strlen((char*)asn1time);
+                                                                                
+   if ((len != 13) && (len != 15)) return 0; /* dont understand */
+                                                                                
+   if ((len == 13) &&
+       ((sscanf((char*)asn1time, "%02d%02d%02d%02d%02d%02d%c",
+         &(time_tm.tm_year),
+         &(time_tm.tm_mon),
+         &(time_tm.tm_mday),
+         &(time_tm.tm_hour),
+         &(time_tm.tm_min),
+         &(time_tm.tm_sec),
+         &zone) != 7) || (zone != 'Z'))) return 0; /* dont understand */
+                                                                                
+   if ((len == 15) &&
+       ((sscanf((char*)asn1time, "20%02d%02d%02d%02d%02d%02d%c",
+         &(time_tm.tm_year),
+         &(time_tm.tm_mon),
+         &(time_tm.tm_mday),
+         &(time_tm.tm_hour),
+         &(time_tm.tm_min),
+         &(time_tm.tm_sec),
+         &zone) != 7) || (zone != 'Z'))) return 0; /* dont understand */
+                                                                                
+   /* time format fixups */
+                                                                                
+   if (time_tm.tm_year < 90) time_tm.tm_year += 100;
+   --(time_tm.tm_mon);
+                                                                                
+   return timegm(&time_tm);
+}
+
+/* this function is taken from OpenSSL without modification */
+
+static int asn1_print_info(BIO *bp, int tag, int xclass, int constructed,
+	     int indent)
+	{
+	static const char fmt[]="%-18s";
+	static const char fmt2[]="%2d %-15s";
+	char str[128];
+	const char *p,*p2=NULL;
+
+	if (constructed & V_ASN1_CONSTRUCTED)
+		p="cons: ";
+	else
+		p="prim: ";
+	if (BIO_write(bp,p,6) < 6) goto err;
+#if OPENSSL_VERSION_NUMBER >= 0x0090701fL
+	BIO_indent(bp,indent,128);
+#endif
+
+	p=str;
+	if ((xclass & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
+			sprintf(str,"priv [ %d ] ",tag);
+	else if ((xclass & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
+		sprintf(str,"cont [ %d ]",tag);
+	else if ((xclass & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
+		sprintf(str,"appl [ %d ]",tag);
+	else p = ASN1_tag2str(tag);
+
+	if (p2 != NULL)
+		{
+		if (BIO_printf(bp,fmt2,tag,p2) <= 0) goto err;
+		}
+	else
+		{
+		if (BIO_printf(bp,fmt,p) <= 0) goto err;
+		}
+	return(1);
+err:
+	return(0);
+	}
+
+static void GRSTasn1AddToTaglist(struct GRSTasn1TagList taglist[], 
+                           int maxtag, int *lasttag,
+                           char *treecoords, int start, int headerlength,
+                           int length, int tag)
+{
+   if ((strlen(treecoords) > GRST_ASN1_MAXCOORDLEN) ||
+       (*lasttag + 1 > maxtag)) return;
+   
+   ++(*lasttag);
+   
+   strncpy(taglist[*lasttag].treecoords, treecoords, GRST_ASN1_MAXCOORDLEN+1);
+   taglist[*lasttag].start = start;
+   taglist[*lasttag].headerlength = headerlength;
+   taglist[*lasttag].length = length;
+   taglist[*lasttag].tag = tag;
+}
+
+int GRSTasn1SearchTaglist(struct GRSTasn1TagList taglist[], 
+                                 int lasttag, char *treecoords)
+{
+   int i;
+   
+   for (i=0; i <= lasttag; ++i)
+      {
+        if (strcmp(treecoords, taglist[i].treecoords) == 0) return i;
+      }
+      
+   return -1;
+}
+
+static int GRSTasn1PrintPrintable(BIO *bp, char *str, int length)
+{
+   int   ret = 0;
+   char *dup, *p;
+   
+   dup = (char*)malloc(length);
+   strncpy(dup,str,length);
+
+   for (p=dup; *p != '\0'; ++p) if ((*p < ' ') || (*p > '~')) *p = '.';
+
+   if (bp != NULL) ret = BIO_write(bp, dup, strlen(dup));
+
+   free(dup);
+   
+   return ret;
+}
+
+static int GRSTasn1Parse2(BIO *bp, unsigned char **pp, long length, int offset,
+	     int depth, int indent, int dump, char *treecoords,
+	     struct GRSTasn1TagList taglist[], int maxtag, int *lasttag)
+	{
+        int sibling = 0;
+        char sibtreecoords[512];
+
+	unsigned char *p,*ep,*tot,*op,*opp;
+	long len;
+	int tag,xclass,ret=0;
+	int nl,hl,j,r;
+	ASN1_OBJECT *o=NULL;
+	ASN1_OCTET_STRING *os=NULL;
+	int dump_indent;
+
+
+	dump_indent = 6;	/* Because we know BIO_dump_indent() */
+	p= *pp;
+	tot=p+length;
+	op=p-1;
+	while ((p < tot) && (op < p))
+		{
+		op=p;
+		j=ASN1_get_object((SSLARG)&p,&len,&tag,&xclass,length);
+
+		if (j & 0x80)
+			{
+			if ((bp != NULL) && 
+			    (BIO_write(bp,"Error in encoding\n",18) <= 0))
+				goto end;
+			ret=0;
+			goto end;
+			}
+		hl=(p-op);
+		length-=hl;
+
+		++sibling;
+		sprintf(sibtreecoords, "%s-%d", treecoords, sibling);
+
+                GRSTasn1AddToTaglist(taglist, maxtag, lasttag, sibtreecoords,
+                               (int)offset+(int)(op - *pp),
+                               (int) hl, len, tag);
+                               
+		if (bp != NULL)
+		  {
+		    BIO_printf(bp, "  %s %ld %d %ld %d ", sibtreecoords,
+		           (long)offset+(long)(op - *pp), hl, len, tag);
+
+		    GRSTasn1PrintPrintable(bp, (char*)p,
+//		                   &((*pp)[(long)offset+(long)(op - *pp)+hl]),
+		                           (len > 30) ? 30 : len);
+
+		    BIO_printf(bp, "\n");
+		 }
+
+
+		/* if j == 0x21 it is a constructed indefinite length object */
+		if ((bp != NULL) &&
+		    (BIO_printf(bp,"%5ld:",(long)offset+(long)(op- *pp))
+			<= 0)) goto end;
+
+		if (j != (V_ASN1_CONSTRUCTED | 1))
+			{
+			if ((bp != NULL) && 
+			    (BIO_printf(bp,"d=%-2d hl=%ld l=%4ld ",
+				depth,(long)hl,len) <= 0))
+				goto end;
+			}
+		else
+			{
+			if ((bp != NULL) &&
+			    (BIO_printf(bp,"d=%-2d hl=%ld l=inf  ",
+				depth,(long)hl) <= 0))
+				goto end;
+			}
+		if ((bp != NULL) && 
+		    !asn1_print_info(bp,tag,xclass,j,(indent)?depth:0))
+			goto end;
+		if (j & V_ASN1_CONSTRUCTED)
+			{
+			ep=p+len;
+			if ((bp != NULL) &&
+			    (BIO_write(bp,"\n",1) <= 0)) goto end;
+			if (len > length)
+				{
+				if (bp != NULL) BIO_printf(bp,
+					"length is greater than %ld\n",length);
+				ret=0;
+				goto end;
+				}
+			if ((j == 0x21) && (len == 0))
+				{
+				for (;;)
+					{
+					r=GRSTasn1Parse2(bp,&p,(long)(tot-p),
+						offset+(p - *pp),depth+1,
+						indent,dump,sibtreecoords,
+						taglist, maxtag, lasttag);
+					if (r == 0) { ret=0; goto end; }
+					if ((r == 2) || (p >= tot)) break;
+					}
+				}
+			else
+				while (p < ep)
+					{
+					r=GRSTasn1Parse2(bp,&p,(long)len,
+						offset+(p - *pp),depth+1,
+						indent,dump,sibtreecoords,
+						taglist, maxtag, lasttag);
+					if (r == 0) { ret=0; goto end; }
+					}
+			}
+		else if (xclass != 0)
+			{
+			p+=len;
+			if ((bp != NULL) && 
+			    (BIO_write(bp,"\n",1) <= 0)) goto end;
+			}
+		else
+			{
+			nl=0;
+			if (	(tag == V_ASN1_PRINTABLESTRING) ||
+				(tag == V_ASN1_T61STRING) ||
+				(tag == V_ASN1_IA5STRING) ||
+				(tag == V_ASN1_VISIBLESTRING) ||
+				(tag == V_ASN1_UTCTIME) ||
+				(tag == V_ASN1_GENERALIZEDTIME))
+				{
+				if ((bp != NULL) &&
+				    (BIO_write(bp,":",1) <= 0)) goto end;
+				if ((len > 0) && (bp != NULL) &&
+					BIO_write(bp,(char *)p,(int)len)
+					!= (int)len)
+					goto end;
+				}
+			else if (tag == V_ASN1_OBJECT)
+				{
+				opp=op;
+				if (d2i_ASN1_OBJECT(&o,(SSLARG)&opp,len+hl) != NULL)
+					{
+					if (bp != NULL)
+					  {
+					    if (BIO_write(bp,":",1) <= 0) goto end;
+					    i2a_ASN1_OBJECT(bp,o);
+					  }
+					}
+				else
+					{
+					if ((bp != NULL) && 
+					    (BIO_write(bp,":BAD OBJECT",11) <= 0))
+						goto end;
+					}
+				}
+			else if (tag == V_ASN1_BOOLEAN)
+				{
+				int ii;
+
+				opp=op;
+				ii=d2i_ASN1_BOOLEAN(NULL,(SSLARG)&opp,len+hl);
+				if (ii < 0)
+				{
+				  if ((bp != NULL) &&
+				      (BIO_write(bp,"Bad boolean\n",12)))
+						goto end;
+				}
+				if (bp != NULL) BIO_printf(bp,":%d",ii);
+				}
+			else if (tag == V_ASN1_BMPSTRING)
+				{
+				/* do the BMP thang */
+				}
+			else if (tag == V_ASN1_OCTET_STRING)
+				{
+				opp=op;
+				os=d2i_ASN1_OCTET_STRING(NULL,(SSLARG) &opp,len+hl);
+				if (os != NULL)
+					{
+					opp=os->data;
+
+					if (os->length > 0)
+					  {
+					    if ((bp != NULL) &&
+						    (BIO_write(bp,":",1) <= 0))
+							goto end;
+					    if ((bp != NULL) &&
+					        (GRSTasn1PrintPrintable(bp,
+					                (char*)opp,
+							os->length) <= 0))
+							goto end;
+					  }
+
+					M_ASN1_OCTET_STRING_free(os);
+					os=NULL;
+					}
+				}
+			else if (tag == V_ASN1_INTEGER)
+				{
+				ASN1_INTEGER *bs;
+				int i;
+
+				opp=op;
+				bs=d2i_ASN1_INTEGER(NULL,(SSLARG)&opp,len+hl);
+				if (bs != NULL)
+					{
+					if ((bp != NULL) &&
+					    (BIO_write(bp,":",1) <= 0)) goto end;
+					if (bs->type == V_ASN1_NEG_INTEGER)
+						if ((bp != NULL) &&
+						    (BIO_write(bp,"-",1) <= 0))
+							goto end;
+					for (i=0; i<bs->length; i++)
+						{
+						if ((bp != NULL) &&
+						    (BIO_printf(bp,"%02X",
+							bs->data[i]) <= 0))
+							goto end;
+						}
+					if (bs->length == 0)
+						{
+						if ((bp != NULL) && 
+						    (BIO_write(bp,"00",2) <= 0))
+							goto end;
+						}
+					}
+				else
+					{
+					if ((bp != NULL) && 
+					    (BIO_write(bp,"BAD INTEGER",11) <= 0))
+						goto end;
+					}
+				M_ASN1_INTEGER_free(bs);
+				}
+			else if (tag == V_ASN1_ENUMERATED)
+				{
+				ASN1_ENUMERATED *bs;
+				int i;
+
+				opp=op;
+				bs=d2i_ASN1_ENUMERATED(NULL,(SSLARG) &opp,len+hl);
+				if (bs != NULL)
+					{
+					if ((bp != NULL) &&
+					    (BIO_write(bp,":",1) <= 0)) goto end;
+					if (bs->type == V_ASN1_NEG_ENUMERATED)
+						if ((bp != NULL) &&
+						    (BIO_write(bp,"-",1) <= 0))
+							goto end;
+					for (i=0; i<bs->length; i++)
+						{
+						if ((bp != NULL) &&
+						    (BIO_printf(bp,"%02X",
+							bs->data[i]) <= 0))
+							goto end;
+						}
+					if (bs->length == 0)
+						{
+						if ((bp != NULL) &&
+						    (BIO_write(bp,"00",2) <= 0))
+							goto end;
+						}
+					}
+				else
+					{
+					if ((bp != NULL) &&
+					    (BIO_write(bp,"BAD ENUMERATED",11) <= 0))
+						goto end;
+					}
+				M_ASN1_ENUMERATED_free(bs);
+				}
+			else if (len > 0 && dump)
+				{
+				if (!nl) 
+					{
+					if ((bp != NULL) &&
+					    (BIO_write(bp,"\n",1) <= 0))
+						goto end;
+					}
+				if ((bp != NULL) &&
+				    (BIO_dump_indent(bp,(char *)p,
+					((dump == -1 || dump > len)?len:dump),
+					dump_indent) <= 0))
+					goto end;
+				nl=1;
+				}
+
+			if (!nl) 
+				{
+				if ((bp != NULL) &&
+				    (BIO_write(bp,"\n",1) <= 0)) goto end;
+				}
+			p+=len;
+			if ((tag == V_ASN1_EOC) && (xclass == 0))
+				{
+				ret=2; /* End of sequence */
+				goto end;
+				}
+			}
+
+		length-=len;
+		}
+	ret=1;
+end:
+	if (o != NULL) ASN1_OBJECT_free(o);
+	if (os != NULL) M_ASN1_OCTET_STRING_free(os);
+	*pp=p;
+	return(ret);
+	}
+
+int GRSTasn1ParseDump(BIO *bp, unsigned char *pp, long len,
+                      struct GRSTasn1TagList taglist[], 
+                      int maxtag, int *lasttag)
+        {
+           return(GRSTasn1Parse2(bp,&pp,len,0,0,0,0,"",
+                                 taglist, maxtag, lasttag));
+        }                        
+
+int GRSTasn1GetX509Name(char *x509name, int maxlength, char *coords,
+                        char *asn1string,
+                        struct GRSTasn1TagList taglist[], int lasttag)                        
+{
+   int i, iobj, istr, n, len = 0;
+   ASN1_OBJECT *obj = NULL;
+   unsigned char coordstmp[81], *q;
+   const unsigned char *shortname;
+
+   for (i=1; ; ++i)
+      {
+        snprintf((char*)coordstmp, sizeof(coordstmp), coords, i, 1);
+        iobj = GRSTasn1SearchTaglist(taglist, lasttag, (char*)coordstmp);
+        if (iobj < 0) break;
+        
+        snprintf((char*)coordstmp, sizeof(coordstmp), coords, i, 2);
+        istr = GRSTasn1SearchTaglist(taglist, lasttag, (char*)coordstmp);
+        if (istr < 0) break;
+        
+        q = (unsigned char*) &asn1string[taglist[iobj].start];
+        d2i_ASN1_OBJECT(&obj, (SSLARG) &q, taglist[iobj].length +
+                                  taglist[iobj].headerlength);
+
+        n = OBJ_obj2nid(obj);
+// free obj now?
+//	if (obj) free (obj);
+        shortname = (const unsigned char*) OBJ_nid2sn(n);
+        
+        if (len + 2 + strlen((char*)shortname) + taglist[istr].length >= maxlength)
+          {
+            x509name[0] = '\0';
+            return GRST_RET_FAILED;          
+          }
+        
+        sprintf(&x509name[len], "/%s=%.*s", shortname, 
+                                taglist[istr].length, 
+               &asn1string[taglist[istr].start+taglist[istr].headerlength]);
+        len += 2 + strlen((char*)shortname) + taglist[istr].length;
+      }
+      
+   x509name[len] = '\0';
+   
+   return (x509name[0] != '\0') ? GRST_RET_OK : GRST_RET_FAILED;
+}
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_err.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_err.c
new file mode 100644
index 0000000000000000000000000000000000000000..14adf13d0137edf15b31db946f700fe0cf53852a
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_err.c
@@ -0,0 +1,43 @@
+/*
+   Copyright (c) 2002-6, 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/
+   ---------------------------------------------------------------
+*/ 
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "gridsite.h"
+
+void (*GRSTerrorLogFunc)(char *, int, int, char *, ...) = NULL;
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_gacl.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_gacl.c
new file mode 100644
index 0000000000000000000000000000000000000000..2cf876c99ecc95d59c6a49e3d7ff8138e1284b4b
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_gacl.c
@@ -0,0 +1,1419 @@
+/*
+   Copyright (c) 2002-6, 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/ *
+ *---------------------------------------------------------------*/
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>              
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <strings.h>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+#include <fnmatch.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+#include "gridsite.h"
+
+#ifdef SUNCC
+/* SUN does not know strsep .... */
+
+static char* strsep(char** str, const char* delims)
+{
+  char* token;
+  if (*str==NULL) {
+    /* No more tokens */
+    return NULL;
+  }
+  
+  token=*str;
+  while (**str!='\0') {
+    if (strchr(delims,**str)!=NULL) {
+      **str='\0';
+      (*str)++;
+      return token;
+    }
+    (*str)++;
+  }
+  /* There is no other token */
+  *str=NULL;
+  return token;
+}
+
+#endif
+
+/*                                                                      *
+ * Global variables, shared by all GACL functions but private to libgacl *
+ *                                                                      */
+ 
+char     *grst_perm_syms[] =  { "none",
+                                "read",
+                                "exec",
+                                "list",
+                                "write",
+                                "admin",
+                                NULL              };
+
+GRSTgaclPerm grst_perm_vals[] =  {   GRST_PERM_NONE,
+                                     GRST_PERM_READ,
+                                     GRST_PERM_EXEC,
+                                     GRST_PERM_LIST,
+                                     GRST_PERM_WRITE,
+                                     GRST_PERM_ADMIN,
+                                     -1                };
+
+int GRSTgaclInit(void)
+{
+  xmlInitParser();
+
+  LIBXML_TEST_VERSION
+
+  xmlKeepBlanksDefault(0);
+
+  return 1;
+}                             
+
+/* declare these two private functions at the start */
+
+GRSTgaclAcl *GRSTgaclAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *);
+GRSTgaclAcl *GRSTxacmlAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *);
+
+/*                                             *
+ * Functions to manipulate GRSTgaclCred structures *
+ *                                             */
+
+GRSTgaclCred *GRSTgaclCredCreate(char *auri_prefix, char *auri_suffix)
+/*
+    GRSTgaclCredCreate - allocate a new GRSTgaclCred structure, and return
+                         it's pointer or NULL on (malloc) error.
+*/
+{
+  int           i;
+  char          auri[16384];
+  GRSTgaclCred *newcred; 
+
+  if      ((auri_prefix != NULL) && (auri_suffix == NULL))
+   sprintf(auri,"%s",auri_prefix);
+  else if ((auri_prefix == NULL) && (auri_suffix != NULL))
+   sprintf(auri,"%s",auri_suffix);
+  else if ((auri_prefix != NULL) && (auri_suffix != NULL))
+   sprintf(auri, "%s%s", auri_prefix, auri_suffix);
+  else return NULL;
+
+  for (i=0; (auri[i] != '\0') && isspace(auri[i]); ++i) ; /* leading space */
+
+  for (i=strlen(auri) - 1; (i >= 0) && isspace(auri[i]); --i)
+                                           auri[i]='\0'; /* trailing space */
+
+  newcred = malloc(sizeof(GRSTgaclCred));
+  if (newcred == NULL) 
+    {
+      return NULL;
+    }
+  
+  newcred->auri       = auri;
+  newcred->delegation = 0;
+  newcred->nist_loa   = 0;
+  newcred->notbefore  = 0;
+  newcred->notafter   = 0;
+  newcred->next       = NULL;
+
+  return newcred;
+}
+
+GRSTgaclCred *GRSTgaclCredNew(char *type)
+/*
+    GRSTgaclCredNew - allocate a new GRSTgaclCred structure, and return
+                      it's pointer or NULL on (malloc) error.
+*/
+{
+  if (type == NULL) return NULL;
+  
+  if ((strcmp(type, "person" ) == 0) ||
+      (strcmp(type, "voms"   ) == 0) ||
+      (strcmp(type, "dn-list") == 0) ||
+      (strcmp(type, "dns"    ) == 0) ||
+      (strcmp(type, "level"  ) == 0)) return GRSTgaclCredCreate("", NULL);
+      
+  if (strcmp(type, "any-user") == 0) 
+       return GRSTgaclCredCreate("gacl:", "any-user");
+                                  
+  if (strcmp(type, "auth-user") == 0) 
+       return GRSTgaclCredCreate("gacl:", "auth-user");
+
+  return NULL;
+}
+
+int GRSTgaclCredAddValue(GRSTgaclCred *cred, char *name, char *rawvalue)
+/*
+    GRSTgaclCredAddValue - add a name/value pair to a GRSTgaclCred
+*/
+{
+  int                i;
+  char              *value, *encoded_value;
+
+  if ((cred == NULL) || (cred->auri == NULL)) return 0;
+  free(cred->auri);
+  cred->auri = NULL;
+
+  /* no leading or trailing space in value */
+
+  value = rawvalue; 
+  while ((*value != '\0') && isspace(*value)) ++value;
+
+  value = strdup(value);
+  for (i=strlen(value) - 1; (i >= 0) && isspace(value[i]); --i) value[i]='\0';
+
+  encoded_value = GRSThttpUrlMildencode(value);
+
+  if (strcmp(name, "dn") == 0)
+    {
+      sprintf(cred->auri, "dn:%s", encoded_value);
+      free(value);
+      free(encoded_value);
+      return 1;
+    }
+  else if (strcmp(name, "fqan") == 0)
+    {
+      sprintf(cred->auri, "fqan:%s", encoded_value);
+      free(value);
+      free(encoded_value);
+      return 1;
+    }
+  else if (strcmp(name, "url") == 0)
+    {
+      sprintf(cred->auri, "%s", encoded_value);
+      free(value);
+      free(encoded_value);
+      return 1;
+    }
+  else if (strcmp(name, "hostname") == 0)
+    {
+      sprintf(cred->auri, "dns:%s", encoded_value);
+      free(value);
+      free(encoded_value);
+      return 1;
+    }
+  else if (strcmp(name, "nist-loa") == 0)
+    {
+      sprintf(cred->auri, "nist-loa:%s", encoded_value);
+      free(value);
+      free(encoded_value);
+      return 1;
+    }
+    
+  free(value);  
+  free(encoded_value);
+  return 0;
+}
+
+int GRSTgaclCredFree(GRSTgaclCred *cred)
+/*
+    GRSTgaclCredFree - free memory structures of a GRSTgaclCred, 
+    returning 1 always!
+*/
+{
+  if (cred == NULL) return 1;
+
+  if (cred->auri != NULL) free(cred->auri);
+  free(cred);
+  
+  return 1;
+}
+
+static int GRSTgaclCredsFree(GRSTgaclCred *firstcred)
+/*
+    GRSTgaclCredsFree - free a cred and all the creds in its *next chain
+*/
+{
+  if (firstcred == NULL) return 0;
+  
+  if (firstcred->next != NULL) GRSTgaclCredsFree(firstcred->next);
+  
+  return GRSTgaclCredFree(firstcred);
+}
+
+static int GRSTgaclCredInsert(GRSTgaclCred *firstcred, GRSTgaclCred *newcred)
+/* 
+    GRSTgaclCredInsert - insert a cred in the *next chain of firstcred
+
+    FOR THE MOMENT THIS JUST APPENDS!
+*/
+{
+  if (firstcred == NULL) return 0;
+  
+  if (firstcred->next == NULL)
+    {
+      firstcred->next = newcred;
+      return 1;
+    }
+
+  return GRSTgaclCredInsert(firstcred->next, newcred);     
+}
+
+int GRSTgaclEntryAddCred(GRSTgaclEntry *entry, GRSTgaclCred *cred)
+/*  
+    GRSTaddCred - add a new credential to an existing entry, returning 1
+    on success or 0 on error 
+*/ 
+{
+  if (entry == NULL) return 0;
+ 
+  if (entry->firstcred == NULL) 
+    {
+      entry->firstcred = cred;
+      return 1;
+    }
+  else return GRSTgaclCredInsert(entry->firstcred, cred);
+}
+
+static int GRSTgaclCredRemoveCred(GRSTgaclCred *firstcred, GRSTgaclCred *oldcred)
+/* 
+    (Private)
+
+    GRSTgaclCredRemoveCred - remove a cred in the *next chain of firstcred
+                     and relink the chain
+*/
+{
+  if (firstcred == NULL) return 0;
+
+  return 1;
+}
+
+int GRSTgaclEntryDelCred(GRSTgaclEntry *entry, GRSTgaclCred *cred)
+/*  
+    GRSTgaclEntryDelCred - remove a new cred from an entry, returning 1
+    on success (or absense) or 0 on error.
+*/ 
+{
+  if (entry == NULL) return 0;
+
+  return GRSTgaclCredRemoveCred(entry->firstcred, cred);
+}
+
+int GRSTgaclCredPrint(GRSTgaclCred *cred, FILE *fp)
+/* 
+   GRSTgaclCredPrint - print a credential and the AURI value it contains
+*/
+{
+  char *q;
+
+  if ((cred->auri != NULL) && (cred->auri[0] != '\0'))
+    {
+      fprintf(fp, "<cred>\n<auri>");
+
+      for (q=cred->auri; *q != '\0'; ++q)
+              if      (*q == '<')  fputs("&lt;",   fp);
+              else if (*q == '>')  fputs("&gt;",   fp);
+              else if (*q == '&')  fputs("&amp;" , fp);
+              else if (*q == '\'') fputs("&apos;", fp);
+              else if (*q == '"')  fputs("&quot;", fp);
+              else                 fputc(*q, fp);
+
+      fprintf(fp, "</auri>\n");
+      
+      if (cred->nist_loa > 0) 
+            fprintf(fp, "<nist-loa>%d</nist-loa>\n", cred->nist_loa);
+      
+      if (cred->delegation > 0) 
+            fprintf(fp, "<delegation>%d</delegation>\n", cred->delegation);
+      
+      fprintf(fp, "</cred>\n");
+
+      return 1;
+    }
+    
+  return 0;
+}
+
+int GRSTgaclCredCmpAuri(GRSTgaclCred *cred1, GRSTgaclCred *cred2)
+/*
+    GRSTgaclCredCmp - compare two credentials for exact match in AURI values
+                      (this means a string match, not just any-user=DN etc)
+*/
+{
+  if ((cred1 == NULL) && (cred2 == NULL)) return 0;
+  
+  if (cred1 == NULL) return -1;
+
+  if (cred2 == NULL) return 1;
+  
+  if ((cred1->auri == NULL) && (cred2->auri == NULL)) return 0;
+  
+  if (cred1->auri == NULL) return -1;
+
+  if (cred2->auri == NULL) return 1;
+  
+  return strcmp(cred1->auri, cred2->auri);
+}
+
+/*                                              *
+ * Functions to manipulate GRSTgaclEntry structures *
+ *                                              */
+
+GRSTgaclEntry *GRSTgaclEntryNew(void)
+/*
+    GRSTgaclEntryNew - allocate space for a new entry, returning its pointer
+                   or NULL on failure.
+*/
+{
+  GRSTgaclEntry *newentry;
+  
+  newentry = (GRSTgaclEntry *) malloc(sizeof(GRSTgaclEntry));
+  if (newentry == NULL) return NULL;
+
+  newentry->firstcred    = NULL;
+  newentry->allowed      = 0;
+  newentry->denied       = 0;
+  newentry->next         = NULL;
+
+  return newentry;
+}
+
+int GRSTgaclEntryFree(GRSTgaclEntry *entry)
+/* 
+    GRSTgaclEntryFree - free up space used by an entry (always returns 1)
+*/
+{
+  if (entry == NULL) return 1;
+
+  GRSTgaclCredsFree(entry->firstcred);  
+
+  free(entry);
+  
+  return 1;
+}
+
+static int GRSTgaclEntriesFree(GRSTgaclEntry *entry)
+/*
+    GRSTgaclEntriesFree - free up entry and all entries linked to in its *next 
+                      chain
+*/
+{
+  if (entry == NULL) return 0;
+  
+  if (entry->next != NULL) GRSTgaclEntriesFree(entry->next);
+  
+  return GRSTgaclEntryFree(entry);  
+}
+
+static int GRSTgaclEntryInsert(GRSTgaclEntry *firstentry, GRSTgaclEntry *newentry)
+/* 
+    GRSTgaclEntryInsert - insert an entry in the *next chain of firstentry
+
+    FOR THE MOMENT THIS JUST APPENDS
+*/
+{
+  if (firstentry == NULL) return 0;
+  
+  if (firstentry->next == NULL)
+    {
+      firstentry->next = newentry;
+      return 1;
+    }
+
+  return GRSTgaclEntryInsert(firstentry->next, newentry);     
+}
+
+int GRSTgaclAclAddEntry(GRSTgaclAcl *acl, GRSTgaclEntry *entry)
+/*  
+    GRSTgaclAclAddEntry - add a new entry to an existing acl, returning 1
+    on success or 0 on error 
+*/ 
+{
+  if (acl == NULL) return 0;
+
+  if (acl->firstentry == NULL) 
+    { 
+      acl->firstentry = entry;
+      return 1;
+    }
+  else return GRSTgaclEntryInsert(acl->firstentry, entry);
+}
+
+int GRSTgaclEntryPrint(GRSTgaclEntry *entry, FILE *fp)
+{
+  GRSTgaclCred  *cred;
+  GRSTgaclPerm  i;
+
+  fputs("<entry>\n", fp);
+  
+  for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+                                            GRSTgaclCredPrint(cred, fp);
+
+  if (entry->allowed)
+    {
+      fputs("<allow>", fp);
+
+      for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i)
+       if ((entry->allowed) & i) GRSTgaclPermPrint(i, fp);
+
+      fputs("</allow>\n", fp);
+    }
+    
+
+  if (entry->denied)
+    {
+      fputs("<deny>", fp);
+
+      for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i)
+       if (entry->denied & i) GRSTgaclPermPrint(i, fp);
+
+      fputs("</deny>\n", fp);
+    }
+    
+  fputs("</entry>\n", fp);
+
+  return 1;
+}
+
+/*                                         *
+ * Functions to manipulate GRSTgaclPerm items *
+ *                                         */
+
+int GRSTgaclPermPrint(GRSTgaclPerm perm, FILE *fp)
+{
+  GRSTgaclPerm i;
+  
+  for (i=GRST_PERM_READ; grst_perm_syms[i] != NULL; ++i)
+       if (perm == grst_perm_vals[i]) 
+         {
+           fprintf(fp, "<%s/>", grst_perm_syms[i]);
+           return 1;
+         }
+         
+  return 0;
+}
+
+int GRSTgaclEntryAllowPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+  entry->allowed = entry->allowed | perm;
+
+  return 1;
+}
+
+int GRSTgaclEntryUnallowPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+  entry->allowed = entry->allowed & ~perm;
+
+  return 1;
+}
+
+int GRSTgaclEntryDenyPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+  entry->denied = entry->denied | perm;
+
+  return 1;
+}
+
+int GRSTgaclEntryUndenyPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+  entry->denied = entry->denied & ~perm;
+
+  return 1;
+}
+
+char *GRSTgaclPermToChar(GRSTgaclPerm perm)
+/*
+   GRSTgaclPermToChar - return char * or NULL corresponding to most significant
+                     set bit of perm.
+*/
+{
+  char      *p = NULL;
+  GRSTgaclPerm  i;
+  
+  for (i=0; grst_perm_syms[i] != NULL; ++i)
+       if (perm & grst_perm_vals[i]) p = grst_perm_syms[i];
+
+  return p;
+}
+
+GRSTgaclPerm GRSTgaclPermFromChar(char *s)
+/*
+   GRSTgaclPermToChar - return access perm corresponding to symbol s[]
+*/
+{
+  GRSTgaclPerm i;
+
+  for (i=0; grst_perm_syms[i] != NULL; ++i)
+       if (strcasecmp(grst_perm_syms[i], s) == 0) return grst_perm_vals[i];
+
+  return -1; 
+}
+
+/*                                            *
+ * Functions to manipulate GRSTgaclAcl structures *
+ *                                            */
+
+GRSTgaclAcl *GRSTgaclAclNew(void)
+/*  
+    GRSTgaclAclNew - allocate a new acl and return its pointer (or NULL 
+                 on failure.)
+*/
+{
+  GRSTgaclAcl *newacl;
+  
+  newacl = (GRSTgaclAcl *) malloc(sizeof(GRSTgaclAcl));
+  if (newacl == NULL) return NULL;
+  
+  newacl->firstentry = NULL;
+
+  return newacl;
+}
+
+int GRSTgaclAclFree(GRSTgaclAcl *acl)
+/*
+    GRSTgaclAclFree - free up space used by *acl. Always returns 1.
+*/
+{
+  if (acl == NULL) return 1;
+
+  GRSTgaclEntriesFree(acl->firstentry);  
+
+  return 1;
+}
+
+int GRSTgaclAclPrint(GRSTgaclAcl *acl, FILE *fp)
+{
+  GRSTgaclEntry *entry;
+  
+  fputs("<gacl version=\"0.9.0\">\n", fp);
+  
+  for (entry = acl->firstentry; entry != NULL; entry = entry->next)
+                                            GRSTgaclEntryPrint(entry, fp);
+
+  fputs("</gacl>\n", fp);
+
+  return 1;
+}
+
+int GRSTgaclAclSave(GRSTgaclAcl *acl, char *filename)
+{
+  int   ret;
+  FILE *fp;
+  
+  fp = fopen(filename, "w");
+  if (fp == NULL) return 0;
+  
+  fputs("<?xml version=\"1.0\"?>\n", fp);
+  
+  ret = GRSTgaclAclPrint(acl, fp);
+  
+  fclose(fp);
+  
+  return ret;
+}
+
+/*                                                    *
+ * Functions for loading and parsing XML using libxml *
+ *                                                    */
+ 
+// need to check these for libxml memory leaks? - what needs to be freed?
+
+static GRSTgaclCred *GRSTgaclCredParse(xmlNodePtr cur)
+/*
+    GRSTgaclCredParse - parse a credential stored in the libxml structure cur, 
+                    returning it as a pointer or NULL on error.
+*/
+{
+  xmlNodePtr    cur2;
+  GRSTgaclCred *cred = NULL;
+ 
+  /* generic AURI creds first */
+
+  if (strcmp((char *) cur->name, "cred") == 0)
+    {
+      for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+         {
+           if (!xmlIsBlankNode(cur2) &&
+               (strcmp((char *) cur2->name, "auri") == 0))               
+             {
+               if (cred != NULL) /* multiple AURI */
+                 {
+                   GRSTgaclCredFree(cred);
+                   cred = NULL;
+                   return NULL;
+                 }
+
+               cred = GRSTgaclCredCreate((char *) xmlNodeGetContent(cur2),NULL);
+             }
+         }
+         
+      if (cred == NULL) return NULL;
+      
+      for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+         {
+           if (xmlIsBlankNode(cur2)) continue;
+           
+           if (strcmp((char *) cur2->name, "nist-loa") == 0)
+             {
+               cred->nist_loa = atoi((char *) xmlNodeGetContent(cur2));
+             }
+           else if (strcmp((char *) cur2->name, "delegation") == 0)
+             {
+               cred->delegation = atoi((char *) xmlNodeGetContent(cur2));
+             }
+         }
+    
+      return cred;
+    }
+  
+  /* backwards compatibility */
+
+  cred = GRSTgaclCredNew((char *) cur->name);
+  
+  for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+     {
+       if (!xmlIsBlankNode(cur2))
+        GRSTgaclCredAddValue(cred, (char *) cur2->name, 
+                             (char *) xmlNodeGetContent(cur2));
+     }
+
+  return cred;
+}
+
+static GRSTgaclEntry *GRSTgaclEntryParse(xmlNodePtr cur)
+/*
+    GRSTgaclEntryParse - parse an entry stored in the libxml structure cur,
+                     returning it as a pointer or NULL on error.
+*/
+{
+  int        i;
+  xmlNodePtr cur2;
+  GRSTgaclEntry *entry;
+  GRSTgaclCred  *cred;
+
+  if (xmlStrcmp(cur->name, (const xmlChar *) "entry") != 0) return NULL;
+  
+  cur = cur->xmlChildrenNode;
+
+  entry = GRSTgaclEntryNew();
+  
+  while (cur != NULL)
+       {
+         if (xmlIsBlankNode(cur))
+           {
+             cur=cur->next;
+             continue;
+           }
+         else if (xmlStrcmp(cur->name, (const xmlChar *) "allow") == 0)
+           {
+             for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+                if (!xmlIsBlankNode(cur2))
+                  {                
+                    for (i=0; grst_perm_syms[i] != NULL; ++i)
+                     if (xmlStrcmp(cur2->name, 
+                             (const xmlChar *) grst_perm_syms[i]) == 0)
+                       GRSTgaclEntryAllowPerm(entry, grst_perm_vals[i]);
+                  }
+           }
+         else if (xmlStrcmp(cur->name, (const xmlChar *) "deny") == 0)
+           {
+             for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+                if (!xmlIsBlankNode(cur2))
+                  {
+                    for (i=0; grst_perm_syms[i] != NULL; ++i)
+                     if (xmlStrcmp(cur2->name, 
+                             (const xmlChar *) grst_perm_syms[i]) == 0)
+                       GRSTgaclEntryDenyPerm(entry, grst_perm_vals[i]);
+                  }
+           }
+         else if ((cred = GRSTgaclCredParse(cur)) != NULL) 
+           {
+             if (!GRSTgaclEntryAddCred(entry, cred))
+               { 
+                 GRSTgaclCredFree(cred);                
+                 GRSTgaclEntryFree(entry);
+                 return NULL;
+               }
+           }
+         else /* I cannot parse this - give up rather than get it wrong */
+           {
+             GRSTgaclEntryFree(entry);
+             return NULL;
+           }
+           
+         cur=cur->next;
+       } 
+       
+  return entry;
+}
+
+GRSTgaclAcl *GRSTgaclAclLoadFile(char *filename)
+{
+  xmlDocPtr   doc;
+  xmlNodePtr  cur;
+  GRSTgaclAcl    *acl=NULL;
+
+  GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile() starting");
+
+  if (filename == NULL) 
+    {
+      GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile() cannot open a NULL filename");
+      return NULL;
+    }
+
+  doc = xmlParseFile(filename);
+  if (doc == NULL) 
+    {
+      GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile failed to open ACL file %s", filename);
+      return NULL;
+    }
+
+  cur = xmlDocGetRootElement(doc);
+  if (cur == NULL) 
+    {
+      xmlFreeDoc(doc);
+      GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile failed to parse root of ACL file %s", filename);
+      return NULL;
+    }
+
+  if (!xmlStrcmp(cur->name, (const xmlChar *) "Policy")) 
+    { 
+      GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile parsing XACML");
+      acl=GRSTxacmlAclParse(doc, cur, acl);
+    }
+  else if (!xmlStrcmp(cur->name, (const xmlChar *) "gacl")) 
+    {
+      GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile parsing GACL");
+      acl=GRSTgaclAclParse(doc, cur, acl);
+    }
+  else /* ACL format not recognised */
+    {
+      xmlFreeDoc(doc);
+      return NULL;
+    }
+    
+  xmlFreeDoc(doc);
+  return acl;
+}
+
+GRSTgaclAcl *GRSTgaclAclParse(xmlDocPtr doc, xmlNodePtr cur, GRSTgaclAcl *acl)
+{
+  GRSTgaclEntry  *entry;
+
+  cur = cur->xmlChildrenNode;
+
+  acl = GRSTgaclAclNew();
+
+  while (cur != NULL)
+       {
+         if (!xmlIsBlankNode(cur))
+           {
+             entry = GRSTgaclEntryParse(cur);
+             if (entry == NULL)
+               {
+                 GRSTgaclAclFree(acl);
+
+                 return NULL;
+               }
+
+             GRSTgaclAclAddEntry(acl, entry);
+           }
+
+         cur=cur->next;
+       }
+
+  return acl;
+}
+int GRSTgaclFileIsAcl(char *pathandfile)
+/* Return 1 if filename in *pathandfile starts GRST_ACL_FILE
+   Return 0 otherwise. */
+{
+  char *filename;
+
+  filename = rindex(pathandfile, '/');
+  if (filename == NULL) filename = pathandfile;
+  else                  filename++;
+
+  return (strncmp((const char*)filename, GRST_ACL_FILE, sizeof(GRST_ACL_FILE) - 1) == 0);
+}
+
+char *GRSTgaclFileFindAclname(char *pathandfile)
+/* Return malloc()ed ACL filename that governs the given file or directory 
+   (for directories, the ACL file is in the directory itself), or NULL if none
+   can be found. */
+{
+  int          len;
+  char        *path, *file, *p;
+  struct stat  statbuf;
+
+  len = strlen(pathandfile);
+  if (len == 0) return NULL;
+  
+  path = malloc(len + sizeof(GRST_ACL_FILE) + 2);
+  strcpy(path, pathandfile);
+
+  if ((stat(path, &statbuf) == 0)	&&
+       S_ISDIR(statbuf.st_mode)		&&
+      (path[len-1] != '/'))
+    {
+      strcat(path, "/");
+      ++len;
+    }
+    
+  if (path[len-1] != '/')
+    {
+      p = rindex(pathandfile, '/');
+      if (p != NULL)
+        {
+          file = &p[1];          
+          p = rindex(path, '/');          
+          sprintf(p, "/%s:%s", GRST_ACL_FILE, file);
+
+          if (stat(path, &statbuf) == 0) return path;
+
+          *p = '\0'; /* otherwise strip off any filename */
+        }
+    }
+
+  while (path[0] != '\0')
+       {
+         strcat(path, "/");
+         strcat(path, GRST_ACL_FILE);
+         
+         if (stat(path, &statbuf) == 0) return path;
+           
+         p = rindex(path, '/');
+         *p = '\0';     /* strip off the / we added for ACL */
+
+         p = rindex(path, '/');
+         if (p == NULL) break; /* must start without / and we there now ??? */
+
+         *p = '\0';     /* strip off another layer of / */                 
+       }
+       
+  free(path);
+  return NULL;
+}
+
+GRSTgaclAcl *GRSTgaclAclLoadforFile(char *pathandfile)
+/* Return ACL that governs the given file or directory (for directories,
+   the ACL file is in the directory itself.) */
+{
+  char        *path;
+  GRSTgaclAcl     *acl;
+
+  path = GRSTgaclFileFindAclname(pathandfile);
+  
+  if (path != NULL)
+    {
+      acl = GRSTgaclAclLoadFile(path);
+      free(path);
+      return acl;
+    }
+    
+  return NULL;
+}
+
+/*                                        *
+ * Functions to create and query GACLuser *
+ *                                        */
+
+GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *cred)
+{
+  GRSTgaclUser *user;
+  
+  if (cred == NULL) return NULL;
+  
+  user = malloc(sizeof(GRSTgaclUser));
+  
+  if (user != NULL)   
+    { 
+      user->firstcred = cred;
+      user->dnlists   = NULL;
+    }
+  
+  return user;
+}
+
+int GRSTgaclUserFree(GRSTgaclUser *user)
+{
+  if (user == NULL) return 1;
+  
+  if (user->firstcred != NULL) GRSTgaclCredsFree(user->firstcred);
+  
+  if (user->dnlists != NULL) free(user->dnlists);
+  
+  free(user);
+  
+  return 1;
+}
+
+int GRSTgaclUserAddCred(GRSTgaclUser *user, GRSTgaclCred *cred)
+{
+  GRSTgaclCred *crediter;
+
+  if ((user == NULL) || (cred == NULL)) return 0;
+
+  if (user->firstcred == NULL) 
+    {
+      user->firstcred = cred;
+      cred->next = NULL; /* so cannot be used to add whole lists */
+      return 1;
+    }
+  
+  crediter = user->firstcred;  
+
+  while (crediter->next != NULL) crediter = crediter->next;
+
+  crediter->next = cred;
+  cred->next = NULL; /* so cannot be used to add whole lists */
+       
+  return 1;
+}
+
+int GRSTgaclUserHasCred(GRSTgaclUser *user, GRSTgaclCred *cred)
+/* test if the user has the given credential */
+{
+  int                nist_loa = 999;
+  GRSTgaclCred      *crediter;
+
+  if ((cred == NULL) || (cred->auri == NULL)) return 0;
+
+  if (strcmp(cred->auri, "gacl:any-user") == 0) return 1;
+  
+  if ((user == NULL) || (user->firstcred == NULL)) return 0;
+  
+  if (strncmp((const char*)cred->auri, "dns:", 4) == 0)
+    {      
+      for (crediter=user->firstcred; 
+           crediter != NULL; 
+           crediter = crediter->next)
+        if ((crediter->auri != NULL) &&
+            (strncmp((const char*)crediter->auri, "dns:", 4) == 0))
+#ifdef SUNCC
+          return (fnmatch(cred->auri, crediter->auri, 0) == 0);
+#else
+          return (fnmatch(cred->auri, crediter->auri, FNM_CASEFOLD) == 0);
+#endif
+           
+      return 0;    
+    }
+    
+  if (strcmp(cred->auri, "gacl:auth-user") == 0)
+    {
+      for (crediter=user->firstcred; 
+           crediter != NULL; 
+           crediter = crediter->next)
+        if ((crediter->auri != NULL) &&
+            (strncmp((const char*)crediter->auri, "dn:", 3) == 0)) return 1;
+                
+      return 0;    
+    }
+  
+  if (sscanf(cred->auri, "nist-loa:%d", &nist_loa) == 1)
+    {
+      for (crediter=user->firstcred; 
+           crediter != NULL; 
+           crediter = crediter->next)
+        if ((crediter->auri != NULL) &&
+            (strncmp((const char*)crediter->auri, "dn:", 3) == 0) &&
+            (crediter->nist_loa >= nist_loa)) return 1;
+
+      return 0;    
+    }
+
+/*
+// can remove this once we preload DN Lists etc as AURIs?
+  if ((strncmp((const char*)cred->auri, "http:",  5) == 0) ||
+      (strncmp((const char*)cred->auri, "https:", 6) == 0))
+    {      
+      return GRSTgaclDNlistHasUser(cred->auri, user);
+    }
+*/
+  /* generic AURI = AURI test */
+
+  for (crediter=user->firstcred; crediter != NULL; crediter = crediter->next)
+     if ((crediter->auri != NULL) &&
+         (strcmp(crediter->auri, cred->auri) == 0)) return 1;
+           
+  return 0;    
+}
+
+GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *user, char *type)
+/* find the first credential of a given type for this user */
+{
+  GRSTgaclCred *cred;
+
+  if (user == NULL) return NULL;
+  
+  cred = user->firstcred;  
+
+  while (cred != NULL)
+       {
+         if ((strcmp(type, "person") == 0) &&       
+             (strncmp((const char*)cred->auri, "dn:", 3) == 0)) return cred;
+
+         if ((strcmp(type, "voms") == 0) &&       
+             (strncmp((const char*)cred->auri, "fqan:", 5) == 0)) return cred;
+
+         if ((strcmp(type, "dns") == 0) &&
+             (strncmp((const char*)cred->auri, "dns:", 4) == 0)) return cred;
+
+         if ((strcmp(type, "dn-list") == 0) &&
+             ((strncmp((const char*)cred->auri, "http:",  5) == 0) || 
+              (strncmp((const char*)cred->auri, "https:", 6) == 0))) return cred;
+         
+         cred = cred->next;       
+       }
+       
+  return NULL;
+}
+
+int GRSTgaclUserSetDNlists(GRSTgaclUser *user, char *dnlists)
+{
+  if (user->dnlists != NULL) free(user->dnlists);
+
+  if (dnlists == NULL) user->dnlists = NULL;
+  else                 user->dnlists = strdup(dnlists);
+
+  return GRSTgaclUserLoadDNlists(user, dnlists);
+}
+
+static void recurse4dnlists(GRSTgaclUser *user, char *dir,
+                            int recurse_level, GRSTgaclCred *dn_cred)
+/* try to find file[] in dir[]. try subdirs if not found. 
+   return full path to first found version or NULL on failure */
+{
+  int            fd, linestart, i;
+  char          *mapped, *s;
+  char           fullfilename[16384]; 
+  size_t         dn_len;
+  struct stat    statbuf;
+  DIR           *dirDIR;
+  struct dirent *file_ent;
+  GRSTgaclCred  *cred;
+
+  if (recurse_level >= GRST_RECURS_LIMIT) return;
+
+  dn_len = strlen(dn_cred->auri) - 3;
+
+  /* search this directory */
+  
+  dirDIR = opendir(dir);
+  
+  if (dirDIR == NULL) return;
+  
+  while ((file_ent = readdir(dirDIR)) != NULL)
+       {
+         if (file_ent->d_name[0] == '.') continue;
+       
+         sprintf(fullfilename, "%s/%s", dir, file_ent->d_name);
+
+         GRSTerrorLog(GRST_LOG_DEBUG, "recurse4dnlists opens %s", fullfilename);
+
+         if ((fd = open(fullfilename, O_RDONLY)) < 0)
+           ;
+         else if (fstat(fd, &statbuf) != 0)
+           ;
+         else if (S_ISDIR(statbuf.st_mode))
+           recurse4dnlists(user, fullfilename, recurse_level + 1, dn_cred);
+         else if ((S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) &&
+                  ((mapped = mmap(NULL, statbuf.st_size, 
+                               PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED))
+           {  
+             linestart = 0;
+             
+             while (linestart + dn_len <= statbuf.st_size)
+                  {
+                    GRSTerrorLog(GRST_LOG_DEBUG, "recurse4dnlists at %ld in %s", 
+                          linestart, fullfilename);
+
+                    for (i=0; 
+                         (linestart + i < statbuf.st_size) && (i < dn_len);
+                         ++i)
+                       if (mapped[linestart + i] != dn_cred->auri[3+i]) break;
+
+                    GRSTerrorLog(GRST_LOG_DEBUG, "recurse4dnlists at %d %d %d %d", 
+                          linestart, i, dn_len, statbuf.st_size);
+
+                    if ((i == dn_len) && 
+                        ((linestart+i == statbuf.st_size) ||
+                         (mapped[linestart+i] == '\n') ||
+                         (mapped[linestart+i] == '\r')))  /* matched */                    
+                      {                        
+                        s = GRSThttpUrlDecode(file_ent->d_name);                    
+                        cred = GRSTgaclCredCreate(s, NULL);
+                        GRSTerrorLog(GRST_LOG_DEBUG, 
+                                     "recurse4dnlists adds %s", s);
+                        free(s);
+                    
+                        GRSTgaclCredSetNotBefore(cred,  dn_cred->notbefore);
+                        GRSTgaclCredSetNotAfter(cred,   dn_cred->notafter);
+                        GRSTgaclCredSetDelegation(cred, dn_cred->delegation);
+                        GRSTgaclCredSetNistLoa(cred,    dn_cred->nist_loa);
+
+                        GRSTgaclUserAddCred(user, cred);
+                        break;
+                      }                    
+                      
+                    linestart += i;
+
+                    while ((linestart < statbuf.st_size) &&
+                           (mapped[linestart] != '\n') && 
+                           (mapped[linestart] != '\r')) ++linestart;
+
+                    while ((linestart < statbuf.st_size) &&
+                           ((mapped[linestart] == '\n') || 
+                            (mapped[linestart] == '\r'))) ++linestart;
+                  }
+             
+             munmap(mapped, statbuf.st_size);
+           }
+
+         if (fd < 0) close(fd);
+       }
+  
+  closedir(dirDIR);  
+}
+
+int GRSTgaclUserLoadDNlists(GRSTgaclUser *user, char *dnlists)
+/*
+    Examine DN Lists for attributes belonging to this user and
+    add them to *user as additional credentials.
+*/
+{
+  char *dn_lists_dirs, *dn_list_ptr, *dirname;
+  GRSTgaclCred *dn_cred;
+
+  /* check for DN Lists */
+
+  if (dnlists == NULL) dnlists = getenv("GRST_DN_LISTS");
+
+  if (dnlists == NULL) dnlists = GRST_DN_LISTS;
+  
+  if (*dnlists == '\0') return 1; /* Didn't ask for anything: that's ok */
+  
+  /* find this user's (first) DN */
+  
+  if (user == NULL) return 1; /* No user! */
+
+  for (dn_cred = user->firstcred; dn_cred != NULL; dn_cred = dn_cred->next)
+     { 
+       if (strncmp((const char*)dn_cred->auri, "dn:", 3) == 0) break;
+     }
+     
+  if (dn_cred == NULL) return 1; /* User has no DN! */
+
+  /* look through DN List files */
+  
+  dn_lists_dirs = strdup(dnlists); /* we need to keep this for free() later! */
+  dn_list_ptr   = dn_lists_dirs;   /* copy, for naughty function strsep()    */
+
+  while ((dirname = strsep(&dn_list_ptr, ":")) != NULL)
+       {
+         recurse4dnlists(user, dirname, 0, dn_cred);
+       }
+       
+  free(dn_lists_dirs);
+  return 1;
+}
+
+/*                                                     *
+ * Functions to test for access perm of an individual  *
+ *                                                     */
+
+#if 0
+static char *recurse4file(char *dir, char *file, 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[16384], fulldirname[16384];
+  struct stat    statbuf;
+  DIR           *dirDIR;
+  struct dirent *file_ent;
+
+  /* try to find in current directory */
+
+  sprintf(fullfilename, "%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;
+       
+         sprintf(fulldirname, "%s/%s", dir, file_ent->d_name);
+
+         if ((stat(fulldirname, &statbuf) == 0) &&
+             S_ISDIR(statbuf.st_mode) &&
+             ((fullfilename = recurse4file(fulldirname, file, 
+                                             recurse_level + 1)) != NULL))
+           {
+             closedir(dirDIR);             
+             return fullfilename;
+           }
+           
+       }
+  
+  closedir(dirDIR);  
+
+  return NULL;
+}
+#endif
+
+int GRSTgaclDNlistHasUser(char *listurl, GRSTgaclUser *user)
+{
+  return GRSTgaclUserHasAURI(user, listurl);
+}
+
+int GRSTgaclUserHasAURI(GRSTgaclUser *user, char *auri)
+{
+  GRSTgaclCred *cred;
+    
+  if ((auri == NULL) || (user == NULL)) return 0;
+  
+  for (cred = user->firstcred; cred != NULL; cred = cred->next)
+     {
+       if (strcmp(auri, cred->auri) == 0) return 1;
+     }
+
+  return 0;
+}
+
+GRSTgaclPerm GRSTgaclAclTestUser(GRSTgaclAcl *acl, GRSTgaclUser *user)
+/*
+    GACLgaclAclTestUser - return bit fields depending on access perms user has
+                      for given acl. All zero for no access. If *user is
+                      NULL, matching to "any-user" will still work.
+*/
+{
+  int        flag, onlyanyuser;
+  GRSTgaclPerm   allowperms = 0, denyperms = 0, allowed;
+  GRSTgaclEntry *entry;
+  GRSTgaclCred  *cred;
+  
+  if (acl == NULL) return 0;
+  
+  for (entry = acl->firstentry; entry != NULL; entry = entry->next)
+     {
+       flag = 1;        /* begin by assuming this entry applies to us */
+       onlyanyuser = 1; /* begin by assuming just <any-user/> */
+       
+       /* now go through creds, checking they all do apply to us */
+     
+       for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+             if (!GRSTgaclUserHasCred(user, cred)) flag = 0;
+             else if (strcmp(cred->auri, "gacl:any-user") != 0) onlyanyuser = 0;
+
+       if (!flag) continue; /* flag false if a subtest failed */
+
+       /* does apply to us, so we remember this entry's perms */
+       
+       /* we dont allow Write or Admin on the basis of any-user alone */
+
+       allowed = entry->allowed;
+
+       if (onlyanyuser)
+            allowed = entry->allowed & ~GRST_PERM_WRITE & ~GRST_PERM_ADMIN;
+       else allowed = entry->allowed;
+
+       allowperms = allowperms | allowed;
+       denyperms  = denyperms  | entry->denied;
+     }
+
+  return (allowperms & (~ denyperms)); 
+  /* for each perm type, any deny we saw kills any allow */
+}
+
+GRSTgaclPerm GRSTgaclAclTestexclUser(GRSTgaclAcl *acl, GRSTgaclUser *user)
+/*
+    GRSTgaclAclTestexclUser - 
+                      return bit fields depending on ALLOW perms OTHER users 
+                      have for given acl. All zero if they have no access.
+                      (used for testing if a user has exclusive access)
+*/
+{
+  int        flag;
+  GRSTgaclPerm  perm = 0;
+  GRSTgaclEntry *entry;
+  GRSTgaclCred  *cred;
+  
+  if (acl == NULL) return 0;
+  
+  for (entry = acl->firstentry; entry != NULL; entry = entry->next)
+     {
+       flag = 0; /* flag will be set if cred implies other users */
+     
+       for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+          {
+            if (strncmp((const char*)cred->auri, "dn:", 3) != 0)
+             /* if we ever add support for other person-specific credentials,
+                they must also be recognised here */
+              {
+                flag = 1;
+                break; 
+              }
+
+            if (!GRSTgaclUserHasCred(user, cred))
+                 /* if user doesnt have this person credential, assume
+                    it refers to a different individual */
+              {
+                flag = 1;
+                break;
+              }
+          }
+
+       if (flag) perm = perm | entry->allowed;
+     }
+
+  return perm;     
+}
+
+/* 
+    Wrapper functions for gridsite-gacl.h support of legacy API
+*/
+
+GRSTgaclEntry *GACLparseEntry(xmlNodePtr cur)
+{
+  return GRSTgaclEntryParse(cur);
+}
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_http.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_http.c
new file mode 100644
index 0000000000000000000000000000000000000000..34a9577003a506e7b1d59c9492b21f37e016980a
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_http.c
@@ -0,0 +1,477 @@
+/*
+   Copyright (c) 2002-3, 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.
+*/
+
+#ifndef VERSION
+#define VERSION "x.x.x"
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "gridsite.h"
+
+void GRSThttpBodyInit(GRSThttpBody *thisbody)
+{
+  thisbody->size = 0; /* simple, but we don't expose internals to callers */
+}
+
+void GRSThttpPrintf(GRSThttpBody *thisbody, char *fmt, ...)
+/* append printf() style format and arguments to *thisbody. */
+
+{
+  char    p[16384];
+  size_t   size;
+  va_list  args;
+
+  va_start(args, fmt);
+  size = vsprintf(p, fmt, args);  
+  va_end(args);
+
+  if (size >  0)
+    {
+      if (thisbody->size == 0) /* need to initialise */
+        {
+          thisbody->first = (GRSThttpCharsList *)malloc(sizeof(GRSThttpCharsList));
+          thisbody->first->text = p;
+          thisbody->first->next = NULL;
+      
+          thisbody->last = thisbody->first;          
+          thisbody->size = size;
+        }
+      else
+        {
+          thisbody->last->next = (GRSThttpCharsList *)
+                                               malloc(sizeof(GRSThttpCharsList));
+          ((GRSThttpCharsList *) thisbody->last->next)->text = p;
+          ((GRSThttpCharsList *) thisbody->last->next)->next = NULL;
+      
+          thisbody->last = thisbody->last->next;          
+          thisbody->size = thisbody->size + size;
+        }
+    }
+}
+
+int GRSThttpCopy(GRSThttpBody *thisbody, char *file)
+/* 
+   copy a whole file, named file[], into the body output buffer, returning
+   1 if file was found and copied ok, or 0 otherwise.
+*/
+{
+  int         fd, len;
+  char        *p;
+  struct stat statbuf;
+
+  fd = open(file, O_RDONLY);
+
+  if (fd == -1) return 0;
+
+  if (fstat(fd, &statbuf) != 0)
+    {
+      close(fd);
+      return 0;
+    }
+
+  p = malloc(statbuf.st_size + 1);
+
+  if (p == NULL)
+    {
+      close(fd);
+      return 0;
+    }
+
+  len = read(fd, p, statbuf.st_size);
+  p[len] = '\0';
+
+  close(fd);
+   
+  if (thisbody->size == 0) /* need to initialise */
+    {
+      thisbody->first = (GRSThttpCharsList *) malloc(sizeof(GRSThttpCharsList));
+      thisbody->first->text = p;
+      thisbody->first->next = NULL;
+      
+      thisbody->last = thisbody->first;
+      thisbody->size = len;
+    }
+  else
+    { 
+      thisbody->last->next=(GRSThttpCharsList *)malloc(sizeof(GRSThttpCharsList));
+      ((GRSThttpCharsList *) thisbody->last->next)->text = p;
+      ((GRSThttpCharsList *) thisbody->last->next)->next = NULL;
+      
+      thisbody->last = thisbody->last->next;
+      thisbody->size = thisbody->size + len;
+    }
+
+  return 1;      
+}
+
+void GRSThttpWriteOut(GRSThttpBody *thisbody)
+/* output Content-Length header, blank line then whole of the body to
+   standard output */
+{
+  GRSThttpCharsList *p;
+  
+  printf("Content-Length: %d\n\n", (int)thisbody->size);
+
+  p = thisbody->first;
+  
+  while (p != NULL)
+    {
+      fputs(p->text, stdout);
+    
+      p = p->next;      
+    }
+}
+
+int GRSThttpPrintHeaderFooter(GRSThttpBody *bp, char *file, char *headfootname)
+/* 
+    try to print Header or Footer appropriate for absolute path file[],
+    returning 1 rather than 0 if found.
+*/
+{
+  int          found = 0;
+  char        *pathfile, *p;
+  struct stat  statbuf;
+
+  pathfile = malloc(strlen(file) + strlen(headfootname) + 2);
+  strcpy(pathfile, file);
+
+  if ((pathfile[strlen(pathfile) - 1] != '/') &&
+      (stat(pathfile, &statbuf) == 0) && 
+       S_ISDIR(statbuf.st_mode)) strcat(pathfile, "/");
+  
+  for (;;)
+     {
+       p = rindex(pathfile, '/');
+       if (p == NULL) break;
+       p[1] = '\0';
+       strcat(p, headfootname);
+
+       if (stat(pathfile, &statbuf) == 0)
+         {
+           found = GRSThttpCopy(bp, pathfile);
+           break;
+         }
+
+       p[0] = '\0';
+     }
+
+  free(pathfile);
+  return found;
+}
+
+int GRSThttpPrintHeader(GRSThttpBody *bp, char *file)
+{
+  char *headname;
+  
+  headname = getenv("REDIRECT_GRST_HEAD_FILE");
+  if (headname == NULL) headname = getenv("GRST_HEAD_FILE");
+  if (headname == NULL) headname = GRST_HEADFILE;
+
+  if (headname[0] == '/') /* absolute location */
+    {
+      return GRSThttpCopy(bp, headname);
+    }
+    
+  return GRSThttpPrintHeaderFooter(bp, file, headname);
+}
+
+int GRSThttpPrintFooter(GRSThttpBody *bp, char *file)
+{
+  char *footname;
+  
+  footname = getenv("REDIRECT_GRST_FOOT_FILE");
+  if (footname == NULL) footname = getenv("GRST_FOOT_FILE");
+  if (footname == NULL) footname = GRST_FOOTFILE;
+
+  if (footname[0] == '/') /* absolute location */
+    {
+      return GRSThttpCopy(bp, footname);
+    }
+    
+  return GRSThttpPrintHeaderFooter(bp, file, footname);
+}
+
+char *GRSThttpGetCGI(char *name)
+/* 
+   Return a malloc()ed copy of CGI form parameter identified by name[],
+   either received by QUERY_STRING (via GET) or on stdin (via POST).
+   Caller must free() the returned string itself. If name[] is not found,
+   an empty NUL-terminated malloc()ed string is returned. name[] has any
+   URL-encoding reversed.
+*/
+{
+  char   *p, *namepattern, *valuestart, *returnvalue, *querystring;
+  int     c, i, j, n, contentlength = 0;
+  static char *cgiposted = NULL;
+
+  if (cgiposted == NULL) /* have to initialise cgiposted */
+    {
+      p = getenv("CONTENT_LENGTH");
+      if (p != NULL) sscanf(p, "%d", &contentlength);
+
+      querystring = getenv("REDIRECT_QUERY_STRING");
+      if (querystring == NULL) querystring = getenv("QUERY_STRING");
+      
+      if (querystring == NULL) cgiposted = malloc(contentlength + 3);
+      else cgiposted = malloc(contentlength + strlen(querystring) + 4);
+
+      cgiposted[0] = '&';
+
+      for (i = 1; i <= contentlength; ++i)
+         {
+           c = getchar();
+           if (c == EOF) break;
+           cgiposted[i] = c;           
+         }
+
+      cgiposted[i]   = '&';
+      cgiposted[i+1] = '\0';
+
+      if (querystring != NULL)
+        {
+          strcat(cgiposted, querystring);
+          strcat(cgiposted, "&");
+        }
+    }
+    
+  namepattern = malloc(strlen(name) + 3);
+  sprintf(namepattern, "&%s=", name);
+  
+  p = strstr(cgiposted, namepattern);
+  free(namepattern);
+  if (p == NULL) return strdup("");
+     
+  valuestart = &p[strlen(name) + 2];
+
+  for (n=0; valuestart[n] != '&'; ++n) ;
+  
+  returnvalue = malloc(n + 1);
+  
+  j=0;
+  
+  for (i=0; i < n; ++i) 
+     {
+       if ((i < n - 2) && (valuestart[i] == '%')) /* url encoded as %HH */
+         {
+           returnvalue[j] = 0;
+           
+           if (isdigit(valuestart[i+1])) 
+                 returnvalue[j] += 16 * (valuestart[i+1] - '0');
+           else if (isalpha(valuestart[i+1])) 
+                 returnvalue[j] += 16 * (10 + tolower(valuestart[i+1]) - 'a');
+                         
+           if (isdigit(valuestart[i+2])) 
+                 returnvalue[j] += valuestart[i+2] - '0';
+           else if (isalpha(valuestart[i+2])) 
+                 returnvalue[j] += 10 + tolower(valuestart[i+2]) - 'a';
+
+           i = i + 2;
+         }
+       else if (valuestart[i] == '+') returnvalue[j] = ' ';
+       else                           returnvalue[j] = valuestart[i];
+       
+       if (returnvalue[j] == '\r') continue; /* CR/LF -> LF */
+       ++j;
+     }
+
+  returnvalue[j] = '\0';
+
+  return returnvalue;
+}
+
+/*                   *
+ * Utility functions *
+ *                   */
+
+char *GRSThttpUrlDecode(char *in)
+{
+  int   i, j, n;
+  char *out;
+                                                                                
+  n = strlen(in);
+  out = malloc(n + 1);
+                                                                                
+  j=0;
+                                                                                
+  for (i=0; i < n; ++i)
+     {
+       if ((i < n - 2) && (in[i] == '%')) /* url encoded as %HH */
+         {
+           out[j] = 0;
+                                                                                
+           if (isdigit(in[i+1]))
+                 out[j] += 16 * (in[i+1] - '0');
+           else if (isalpha(in[i+1]))
+                 out[j] += 16 * (10 + tolower(in[i+1]) - 'a');
+                                                                                
+           if (isdigit(in[i+2]))
+                 out[j] += in[i+2] - '0';
+           else if (isalpha(in[i+2]))
+                 out[j] += 10 + tolower(in[i+2]) - 'a';
+                                                                                
+           i = i + 2;
+         }
+       else if (in[i] == '+') out[j] = ' ';
+       else                   out[j] = in[i];
+                                                                                
+       ++j;
+     }
+                                                                                
+  out[j] = '\0';
+                                                                                
+  return out;
+}
+
+char *GRSThttpUrlEncode(char *in)
+/* Return a pointer to a malloc'd string holding a URL-encoded (RFC 1738)
+   version of *in. Only A-Z a-z 0-9 . _ - are passed through unmodified.
+   (DN's processed by GRSThttpUrlEncode can be used as valid Unix filenames,
+   assuming they do not exceed restrictions on filename length.) */
+{
+  char *out, *p, *q;
+  
+  out = malloc(3*strlen(in) + 1);
+  
+  p = in;
+  q = out;
+  
+  while (*p != '\0')
+       {
+         if (isalnum(*p) || (*p == '.') || (*p == '_') || (*p == '-'))
+           {
+             *q = *p;
+             ++q;
+           }
+         else
+           {
+             sprintf(q, "%%%2X", *p);
+             q = &q[3];
+           }
+
+         ++p;
+       }
+  
+  *q = '\0';  
+  return out;
+}
+
+char *GRSThttpUrlMildencode(char *in)
+/* Return a pointer to a malloc'd string holding a partially URL-encoded
+   version of *in. "Partially" means that A-Z a-z 0-9 . = - _ @ and / 
+   are passed through unmodified. (DN's processed by GRSThttpUrlMildencode()
+   can be used as valid Unix paths+filenames if you are prepared to
+   create or simulate the resulting /X=xyz directories.) */
+{
+  char *out, *p, *q;
+  
+  out = malloc(3*strlen(in) + 1);
+  
+  p = in;
+  q = out;
+  
+  while (*p != '\0')
+       {
+         if (isalnum(*p) || (*p == '.') || (*p == '=') || (*p == '-') 
+                         || (*p == '/') || (*p == '@') || (*p == '_'))
+           {
+             *q = *p;
+             ++q;
+           }
+         else if (*p == ' ')
+           {
+             *q = '+';
+             ++q;
+           }
+         else
+           {
+             sprintf(q, "%%%2X", *p);
+             q = &q[3];
+           }
+
+         ++p;
+       }
+  
+  *q = '\0';  
+  return out;
+}
+
+/// Return a one-time passcode string, for use with GridHTTP
+/**
+ *  Returns
+ *
+ *  String is timestamp+SHA1_HASH(timestamp+":"+method+":"+URL)
+ *  Timestamps and hashes are in lowercase hexadecimal. Timestamps are
+ *  seconds since 00:00:00 on January 1, 1970 UTC.
+ */
+
+/*
+char *GRSThttpMakeOneTimePasscode(time_t timestamp, char *method, char *url)
+{
+  int    len, i;
+  char  stringtohash[16384], hashedstring[EVP_MAX_MD_SIZE], *returnstring;
+  const EVP_MD *m;
+  EVP_MD_CTX ctx;
+
+  m = EVP_sha1();
+  if (m == NULL) return NULL;
+
+  sprintf(stringtohash, "%08x:%s:%s", timestamp, method, url);
+ 
+  EVP_DigestInit(&ctx, m);
+  EVP_DigestUpdate(&ctx, stringtohash, strlen(stringtohash));
+  EVP_DigestFinal(&ctx, hashedstring, &len);
+
+  returnstring = malloc(9 + len * 2);
+
+  sprintf(returnstring, "%08x", timestamp);
+
+  for (i=0; 
+
+  return returnstring;
+}
+*/
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.c
new file mode 100644
index 0000000000000000000000000000000000000000..adc367a3f6d34bb7c2443c556881cae35a9386ad
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.c
@@ -0,0 +1,631 @@
+/* This code is extracted from mod_gridsite removing references to apache methods */
+
+/*
+   Copyright (c) 2003-7, 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.
+*/
+
+#include "grst_verifycallback.h"
+#include "gridsite.h"
+#include <openssl/x509v3.h>
+#include <string.h>
+
+X509_STORE* grst_store=NULL;
+int grst_verify=0;
+int grst_depth=0;
+char* grst_cadir   = "/etc/grid-certificates/certificates";
+char* grst_vomsdir = "/etc/grid-certificates/vomsdir";
+
+// APACHE mod_ssl functions
+int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx);
+int ssl_callback_SSLVerify_CRL(int ok, X509_STORE_CTX *ctx);
+
+// GRIDSITE functions
+int GRST_X509_check_issued_wrapper(X509_STORE_CTX *ctx, X509 *x, X509 *issuer)
+/* We change the default callback to use our wrapper and discard errors
+   due to GSI proxy chains (ie where users certs act as CAs) */
+{
+    int ret;
+    ret = X509_check_issued(issuer, x);
+    if (ret == X509_V_OK)
+                return 1;
+         
+    /* Non self-signed certs without signing are ok if they passed
+           the other checks inside X509_check_issued. Is this enough? */
+    if ((ret == X509_V_ERR_KEYUSAGE_NO_CERTSIGN) &&
+        (X509_NAME_cmp(X509_get_subject_name(issuer),
+                           X509_get_subject_name(x)) != 0)) return 1;
+ 
+    /* If we haven't asked for issuer errors don't set ctx */
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+    if (!(ctx->flags & X509_V_FLAG_CB_ISSUER_CHECK)) return 0;
+#else
+    if (!(ctx->param->flags & X509_V_FLAG_CB_ISSUER_CHECK)) return 0;
+#endif 
+  
+    ctx->error = ret;
+    ctx->current_cert = x;
+    ctx->current_issuer = issuer;
+    return ctx->verify_cb(0, ctx);
+}
+
+/* Later OpenSSL versions add a second pointer ... */
+int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx, void *p)
+
+/* Earlier ones have a single argument ... */
+// int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx)
+
+/* Before 0.9.7 we cannot change the check_issued callback directly in
+   the X509_STORE, so we must insert it in another callback that gets
+   called early enough */
+{
+   ctx->check_issued = GRST_X509_check_issued_wrapper;
+   return X509_verify_cert(ctx);
+}
+
+int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx)
+{
+   SSL *ssl            = (SSL *) X509_STORE_CTX_get_app_data(ctx);
+   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;
+   GRSTx509Chain *grst_old_chain;
+
+   /*
+    * GSI Proxy user-cert-as-CA handling:
+    * we skip Invalid CA errors at this stage, since we will check this
+    * again at errdepth=0 for the full chain using GRSTx509ChainLoadCheck
+    */
+   if (errnum == X509_V_ERR_INVALID_CA)
+     {
+       GRSTerrorLog(GRST_LOG_DEBUG,"Skip invalid CA error, since we will check again at errdepth=0");
+       ok = TRUE;
+       errnum = X509_V_OK;
+        X509_STORE_CTX_set_error(ctx, errnum);
+     }
+
+   /*
+    * New style GSI Proxy handling, with critical ProxyCertInfo
+    * extension: we use GRSTx509KnownCriticalExts() to check this
+    */
+#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)
+         {
+	   GRSTerrorLog(GRST_LOG_DEBUG,"GRSTx509KnownCriticalExts() accepts previously unhandled Critical Extension(GSI Proxy?)");
+	   ok = TRUE;
+	   errnum = X509_V_OK;
+	   X509_STORE_CTX_set_error(ctx, errnum);
+         }
+     }
+
+   if (errnum == X509_V_ERR_INVALID_PURPOSE ) {
+     GRSTerrorLog(GRST_LOG_DEBUG,"GRSTx509 invalid purpose error ignored ");
+     ok =  TRUE;
+     errnum = X509_V_OK;
+     X509_STORE_CTX_set_error(ctx, errnum);
+   }
+
+   returned_ok = ssl_callback_SSLVerify(ok, ctx);
+
+   /* 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 = GRSTx509ChainLoadCheck(&grst_chain, certstack, NULL,
+                                        grst_cadir, 
+                                        grst_vomsdir);
+
+        if (errnum != X509_V_OK)
+          {
+	    GRSTerrorLog(GRST_LOG_ERR,"Invalid certificate chain reported by GRSTx509CheckChain() %s\n", X509_verify_cert_error_string(errnum));
+            ok = FALSE;
+          }
+        else 
+          {
+	    GRSTerrorLog(GRST_LOG_DEBUG,"Valid certificate chain reported by GRSTx509ChainLoadCheck()\n");
+          }
+
+	// we don't free it but rather put it into the SSL context application data
+        //GRSTx509ChainFree(grst_chain);
+
+        //GRSTx509ChainFree(grst_chain);
+	
+	if ((grst_old_chain = SSL_get_app_data(ssl))) {
+	  GRSTx509ChainFree(grst_old_chain);
+	}
+	  
+	SSL_set_app_data(ssl,grst_chain);
+     }
+
+   return returned_ok;
+}
+
+/*
+    Save result validity info from chain into connection notes,
+    and write out in an SSL session creds file.
+*/
+
+void GRST_print_ssl_creds(void *in_chain)
+{
+   int                       i= 0;
+   int lowest_voms_delegation = 65535;
+   GRSTx509Chain *grst_chain = (GRSTx509Chain*) in_chain;
+   GRSTx509Cert  *grst_cert = NULL;
+
+   /* check if already done */
+
+   for (grst_cert = grst_chain->firstcert;
+        grst_cert != NULL; grst_cert = grst_cert->next)
+      {
+        if (grst_cert->type == GRST_CERT_TYPE_VOMS)
+          {
+            /* want to record the delegation level 
+               of the last proxy with VOMS attributes */
+	    GRSTerrorLog(GRST_LOG_DEBUG,"Recording VOMS delegation %d\n",grst_cert->delegation);
+            lowest_voms_delegation = grst_cert->delegation;
+          }
+        else if ((grst_cert->type == GRST_CERT_TYPE_EEC) ||
+                 (grst_cert->type == GRST_CERT_TYPE_PROXY))
+          {
+	    GRSTerrorLog(GRST_LOG_INFO,"(%d) dn: %s\n",i,grst_cert->dn);
+	    GRSTerrorLog(GRST_LOG_INFO,"notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",grst_cert->notbefore,grst_cert->notafter,grst_cert->delegation);
+	    ++i;
+          }
+      }
+   
+   for (grst_cert = grst_chain->firstcert; 
+        grst_cert != NULL; grst_cert = grst_cert->next)
+      {
+        if ((grst_cert->type == GRST_CERT_TYPE_VOMS) &&
+            (grst_cert->delegation == lowest_voms_delegation))
+          {
+            /* only export attributes from the last proxy to contain them */
+	    GRSTerrorLog(GRST_LOG_INFO,"fqan:%s\n",grst_cert->value);
+	    GRSTerrorLog(GRST_LOG_INFO,"notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",grst_cert->notbefore,grst_cert->notafter,grst_cert->delegation);
+            ++i;
+          }
+      }
+}
+
+
+char* GRST_get_voms_roles_and_free(void* in_chain)
+{
+  int          i, lowest_voms_delegation = 65535;
+  GRSTx509Chain *grst_chain = (GRSTx509Chain*) in_chain;
+  GRSTx509Cert  *grst_cert = NULL;
+
+  char* voms_roles = (char*) malloc(16384);
+  voms_roles[0] =0;
+
+  if (!voms_roles) {
+    return NULL;
+  }
+
+  /* check if already done */
+  
+  for (grst_cert = grst_chain->firstcert;
+       grst_cert != NULL; grst_cert = grst_cert->next)
+    {
+      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))
+	{
+	  ++i;
+	}
+    }
+  
+  for (grst_cert = grst_chain->firstcert; 
+       grst_cert != NULL; grst_cert = grst_cert->next)
+    {
+      if ((grst_cert->type == GRST_CERT_TYPE_VOMS) &&
+	  (grst_cert->delegation == lowest_voms_delegation))
+	{
+	  /* only export attributes from the last proxy to contain them */
+	  GRSTerrorLog(GRST_LOG_DEBUG,"fqan:%s\n",grst_cert->value);
+
+	  // filter the faulty roles out
+	  //	  if (((strstr(grst_cert->value,"Role=NULL")))) {
+	  //		//|| (strstr(grst_cert->value,"Capability=NULL"))))) {
+          // filter out - don't concatenate
+	  //	  } else {
+	  
+	  // don't filter, but leave it to the application to interpret the FQANs
+	  strcat(voms_roles,grst_cert->value);
+	  strcat(voms_roles,":");
+	  //	  }
+	  GRSTerrorLog(GRST_LOG_DEBUG,"notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",grst_cert->notbefore,grst_cert->notafter,grst_cert->delegation);
+	  ++i;
+	}
+    }
+  
+  if (strlen(voms_roles)) {
+    // remove last :
+    voms_roles[strlen(voms_roles)-1] = 0;
+  }
+
+
+  GRSTx509ChainFree(grst_chain);
+  return voms_roles;
+}
+
+
+
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_kernel.c
+**  The SSL engine kernel
+*/
+
+/*
+ * This OpenSSL callback function is called when OpenSSL
+ * does client authentication and verifies the certificate chain.
+ */
+int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+{
+  X509 *xs;
+  int errnum;
+  int errdepth;
+  char *cp;
+  char *cp2;
+  int depth;
+
+  /*
+   * Get context back through OpenSSL context
+   */
+  SSL *ssl            = (SSL *) X509_STORE_CTX_get_app_data(ctx);
+  
+  /*
+   * Get verify ingredients
+   */
+  xs       = X509_STORE_CTX_get_current_cert(ctx);
+  errnum   = X509_STORE_CTX_get_error(ctx);
+  errdepth = X509_STORE_CTX_get_error_depth(ctx);
+  
+  /*
+   * Log verification information
+   */
+  cp  = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+  cp2 = X509_NAME_oneline(X509_get_issuer_name(xs),  NULL, 0);
+  
+  GRSTerrorLog(GRST_LOG_DEBUG,
+	  "Certificate Verification: depth: %d, subject: %s, issuer: %s\n",
+	  errdepth, cp != NULL ? cp : "-unknown-",
+	  cp2 != NULL ? cp2 : "-unknown");
+  
+
+  if (cp)
+    OPENSSL_free(cp);
+  if (cp2)
+    OPENSSL_free(cp2);
+  
+  /*
+   * Check for optionally acceptable non-verifiable issuer situation
+   */
+  
+  if (   (   errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+	     || errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
+	     || errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
+#if SSL_LIBRARY_VERSION >= 0x00905000
+	     || errnum == X509_V_ERR_CERT_UNTRUSTED
+#endif
+	     || errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE  )
+	 && grst_verify == GRST_VERIFY_OPTIONAL_NO_CA ) {
+    GRSTerrorLog(GRST_LOG_ERR,
+		 "Certificate Verification: Verifiable Issuer is configured as "
+		 "optional, therefore we're accepting the certificate\n");
+    SSL_set_verify_result(ssl, X509_V_OK);
+    ok = TRUE;
+  }
+
+  /*
+   * Additionally perform CRL-based revocation checks
+   */
+  
+  if (ok) {
+    ok = ssl_callback_SSLVerify_CRL(ok, ctx);
+    if (!ok)
+      errnum = X509_STORE_CTX_get_error(ctx);
+  }
+  
+    /*
+     * If we already know it's not ok, log the real reason
+     */
+  if (!ok) {
+    GRSTerrorLog(GRST_LOG_ERR,"Certificate Verification: Error (%d): %s\n",
+		 errnum, X509_verify_cert_error_string(errnum));
+  }
+  
+  /*
+   * Finally check the depth of the certificate verification
+   */
+  
+  depth = grst_depth;
+  
+  if (errdepth > depth) {
+    GRSTerrorLog(GRST_LOG_ERR,
+		 "Certificate Verification: Certificate Chain too long "
+		 "(chain has %d certificates, but maximum allowed are only %d)\n",
+		 errdepth, depth);
+    ok = FALSE;
+  }
+  
+  /*
+   * And finally signal OpenSSL the (perhaps changed) state
+   */
+  return (ok);
+}
+
+int ssl_callback_SSLVerify_CRL(
+			       int ok, X509_STORE_CTX *ctx)
+{
+  X509_OBJECT obj;
+  X509_NAME *subject;
+  X509_NAME *issuer;
+  X509 *xs;
+  X509_CRL *crl;
+  X509_REVOKED *revoked;
+  EVP_PKEY *pubkey;
+  long serial;
+  int i, n, rc;
+  char *cp;
+  ASN1_TIME *t;
+  
+  /*
+   * Determine certificate ingredients in advance
+   */
+
+  GRSTerrorLog(GRST_LOG_DEBUG,"Checking certificate revocation lists\n");
+
+
+  xs      = X509_STORE_CTX_get_current_cert(ctx);
+  subject = X509_get_subject_name(xs);
+  issuer  = X509_get_issuer_name(xs);
+  
+
+  /*
+   * OpenSSL provides the general mechanism to deal with CRLs but does not
+   * use them automatically when verifying certificates, so we do it
+   * explicitly here. We will check the CRL for the currently checked
+   * certificate, if there is such a CRL in the store.
+   *
+   * We come through this procedure for each certificate in the certificate
+   * chain, starting with the root-CA's certificate. At each step we've to
+   * both verify the signature on the CRL (to make sure it's a valid CRL)
+   * and it's revocation list (to make sure the current certificate isn't
+   * revoked).  But because to check the signature on the CRL we need the
+   * public key of the issuing CA certificate (which was already processed
+   * one round before), we've a little problem. But we can both solve it and
+   * at the same time optimize the processing by using the following
+   * verification scheme (idea and code snippets borrowed from the GLOBUS
+   * project):
+   *
+   * 1. We'll check the signature of a CRL in each step when we find a CRL
+   *    through the _subject_ name of the current certificate. This CRL
+   *    itself will be needed the first time in the next round, of course.
+   *    But we do the signature processing one round before this where the
+   * public key of the issuing CA certificate (which was already processed
+   * one round before), we've a little problem. But we can both solve it and
+   * at the same time optimize the processing by using the following
+   * verification scheme (idea and code snippets borrowed from the GLOBUS
+   * project):
+   *
+   * 1. We'll check the signature of a CRL in each step when we find a CRL
+   *    through the _subject_ name of the current certificate. This CRL
+   *    itself will be needed the first time in the next round, of course.
+   *    But we do the signature processing one round before this where the
+   *    public key of the CA is available.
+   *
+   * 2. We'll check the revocation list of a CRL in each step when
+   *    we find a CRL through the _issuer_ name of the current certificate.
+   *    This CRLs signature was then already verified one round before.
+   *
+   * This verification scheme allows a CA to revoke its own certificate as
+   * well, of course.
+
+   */
+
+  if (!grst_store) {
+    return 1;
+  }
+
+
+  /*
+   * Try to retrieve a CRL corresponding to the _subject_ of
+   * the current certificate in order to verify it's integrity.
+   */
+  memset((char *)&obj, 0, sizeof(obj));
+  
+  rc = SSL_X509_STORE_lookup(grst_store,X509_LU_CRL, subject, &obj);
+
+  crl = obj.data.crl;
+  if (rc > 0 && crl != NULL) {
+    GRSTerrorLog(GRST_LOG_DEBUG,"CRL lookup ...");
+    /*
+     * Verify the signature on this CRL
+     */
+    pubkey = X509_get_pubkey(xs);
+    if (X509_CRL_verify(crl, pubkey) <= 0) {
+    GRSTerrorLog(GRST_LOG_ERR,"Invalid signature on CRL\n");
+    X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+    X509_OBJECT_free_contents(&obj);
+    if (pubkey != NULL)
+      EVP_PKEY_free(pubkey);
+    return 0;
+    }
+    
+    if (pubkey != NULL)
+      EVP_PKEY_free(pubkey);
+    
+    /*
+     * Check date of CRL to make sure it's not expired
+     */
+    if ((t = X509_CRL_get_nextUpdate(crl)) == NULL) {
+      GRSTerrorLog(GRST_LOG_ERR,"Found CRL has invalid enxtUpdate field\n");
+      X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+      X509_OBJECT_free_contents(&obj);
+      return 0;
+    }
+    if (X509_cmp_current_time(t) < 0) {
+      GRSTerrorLog(GRST_LOG_ERR,"Found CRL is expired - "
+		   "revoking all certificates until you get updated CRL\n");
+      X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+      X509_OBJECT_free_contents(&obj);
+      return 0;
+    }
+    X509_OBJECT_free_contents(&obj);
+  }
+  
+  /*
+   * Try to retrieve a CRL corresponding to the _issuer_ of
+   * the current certificate in order to check for revocation.
+   */
+  
+  memset((char *)&obj, 0, sizeof(obj));
+  rc = SSL_X509_STORE_lookup(grst_store,X509_LU_CRL, issuer, &obj);
+  crl = obj.data.crl;
+  if (rc > 0 && crl != NULL) {
+    /*
+     * Check if the current certificate is revoked by this CRL
+     */
+#if SSL_LIBRARY_VERSION < 0x00904000
+    n = sk_num(X509_CRL_get_REVOKED(crl));
+#else
+    n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+#endif
+    for (i = 0; i < n; i++) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+      revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+#else
+      revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+#endif
+      if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) {
+	serial = ASN1_INTEGER_get(revoked->serialNumber);
+	cp = X509_NAME_oneline(issuer, NULL, 0);
+	GRSTerrorLog(GRST_LOG_ERR,
+		     "Certificate with serial %ld (0x%lX) "
+		     "revoked per CRL from issuer %s\n",
+		     serial, serial, cp);
+	OPENSSL_free(cp);
+	
+	X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+	X509_OBJECT_free_contents(&obj);
+	return 0;
+      }
+    }
+    X509_OBJECT_free_contents(&obj);
+  }
+  return 1;
+}
+
+/*  _________________________________________________________________
+**
+**  Certificate Revocation List (CRL) Storage
+**  _________________________________________________________________
+*/
+
+X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath)
+{
+  X509_STORE *pStore;
+  X509_LOOKUP *pLookup;
+  
+  if (cpFile == NULL && cpPath == NULL)
+    return NULL;
+  if ((pStore = X509_STORE_new()) == NULL)
+    return NULL;
+  if (cpFile != NULL) {
+    if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_file())) == NULL) {
+      X509_STORE_free(pStore);
+      return NULL;
+    }
+    X509_LOOKUP_load_file(pLookup, cpFile, X509_FILETYPE_PEM);
+  }
+  if (cpPath != NULL) {
+    if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_hash_dir())) == NULL) {
+      X509_STORE_free(pStore);
+      return NULL;
+    }
+    X509_LOOKUP_add_dir(pLookup, cpPath, X509_FILETYPE_PEM);
+  }
+  return pStore;
+}
+
+int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType,
+                          X509_NAME *pName, X509_OBJECT *pObj)
+{
+  X509_STORE_CTX pStoreCtx;
+  int rc;
+  
+  X509_STORE_CTX_init(&pStoreCtx, pStore, NULL, NULL);
+  rc = X509_STORE_get_by_subject(&pStoreCtx, nType, pName, pObj);
+  X509_STORE_CTX_cleanup(&pStoreCtx);
+  return rc;
+}
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.h b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.h
new file mode 100644
index 0000000000000000000000000000000000000000..2be12e4ec90757b358df2fa02cba539da98700f6
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.h
@@ -0,0 +1,35 @@
+#ifndef GRST_VERIFYCALLBACK__H
+#define GRST_VERIFYCALLBACK__H
+
+#include <openssl/ssl.h>
+
+extern X509_STORE* grst_store;
+
+#define GRST_VERIFY_OPTIONAL_NO_CA 1
+
+extern int grst_verify; // relax the verify setting if you want to GRST_VERIFY_OPTIONAL_NO_CA
+extern int grst_depth;
+
+extern char* grst_vomsdir;
+extern char* grst_cadir;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+  int GRST_X509_set_log_fd(FILE* fd);
+  int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx, void *p);
+  int GRST_X509_check_issued_wrapper(X509_STORE_CTX *ctx, X509 *x, X509 *issuer);
+  int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx);
+  void GRST_print_ssl_creds(void *grst_chain);
+  char* GRST_get_voms_roles_and_free(void *grst_chain);
+
+  X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath);
+  int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType,
+			    X509_NAME *pName, X509_OBJECT *pObj);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_x509.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_x509.c
new file mode 100644
index 0000000000000000000000000000000000000000..c7d72cb120d025a866e63815b560bfeaf5658315
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_x509.c
@@ -0,0 +1,2149 @@
+/*
+   Copyright (c) 2002-7, 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/
+   ---------------------------------------------------------------
+*/ 
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#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 "gridsite.h"
+
+#define GRST_KEYSIZE		512
+#define GRST_PROXYCACHE		"/../proxycache/"
+#define GRST_MAX_CHAIN_LEN	9
+#define GRST_BACKDATE_SECONDS	300
+
+/// 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
+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) 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->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)
+///
+/// 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, EVP_md5(), NULL);
+#else
+   EVP_VerifyInit(&ctx, EVP_md5());
+#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_SIG  "-1-1-%d-3"
+   int            isig, iinfo;
+   char           certpath[16384], certpath2[16384], acvomsdn[200], dn_coords[200],
+                  info_coords[200], sig_coords[200];
+
+   DIR           *vomsDIR, *vomsDIR2;
+   struct dirent *vomsdirent, *vomsdirent2;
+   X509          *cert;
+
+   FILE          *fp;
+   struct stat    statbuf;
+
+   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,
+         (char*)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(sig_coords, sizeof(sig_coords), 
+            GRST_ASN1_COORDS_VOMS_SIG, acnumber);
+   isig  = GRSTasn1SearchTaglist(taglist, lasttag, sig_coords);
+
+   if ((iinfo < 0) || (isig < 0)) 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;
+        
+          sprintf(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);
+              
+              if (vomsDIR2 == NULL) continue;
+                
+
+              while ((vomsdirent2 = readdir(vomsDIR2)) != NULL)
+                {
+                  if (vomsdirent2->d_name[0] == '.') continue;
+                  
+                  sprintf(certpath2, "%s/%s/%s", 
+                           vomsdir, vomsdirent->d_name, vomsdirent2->d_name);
+                
+                  fp = fopen(certpath2, "r");
+                  GRSTerrorLog(GRST_LOG_DEBUG, 
+                               "Examine VOMS cert %s", certpath2);
+                  if (fp == NULL) continue;
+
+                  cert = PEM_read_X509(fp, NULL, NULL, NULL);
+                  fclose(fp);
+                  if (cert == NULL) continue;
+
+                  if (GRSTx509VerifySig(time1_time, time2_time,
+                            &asn1string[taglist[iinfo].start], 
+                            taglist[iinfo].length+taglist[iinfo].headerlength,
+                            &asn1string[taglist[isig].start+
+                                                taglist[isig].headerlength+1],
+                            taglist[isig].length - 1,
+                            cert) == GRST_RET_OK)
+                    {
+                      GRSTerrorLog(GRST_LOG_DEBUG, " VOMS cert signature match");
+                      X509_free(cert);
+                      closedir(vomsDIR2);
+                      closedir(vomsDIR);
+                      return GRST_RET_OK ; /* verified */              
+                    }
+            
+                  X509_free(cert);
+                }
+                
+              closedir(vomsDIR2);
+            }
+          else
+            {
+              fp = fopen(certpath, "r");
+              GRSTerrorLog(GRST_LOG_DEBUG, "Examine VOMS cert %s", certpath);
+              if (fp == NULL) continue;
+
+              cert = PEM_read_X509(fp, NULL, NULL, NULL);
+              fclose(fp);
+              if (cert == NULL) continue;
+
+              if (GRSTx509VerifySig(time1_time, time2_time,
+                            &asn1string[taglist[iinfo].start], 
+                            taglist[iinfo].length+taglist[iinfo].headerlength,
+                            &asn1string[taglist[isig].start+
+                                                taglist[isig].headerlength+1],
+                            taglist[isig].length - 1,
+                            cert) == GRST_RET_OK)
+                {
+                  X509_free(cert);
+                  closedir(vomsDIR);
+                  return GRST_RET_OK ; /* verified */              
+                }
+            
+              X509_free(cert);
+            }
+        }
+
+   closedir(vomsDIR);   
+   return GRST_RET_FAILED;
+}
+
+/// Get the VOMS attributes in the given extension
+static int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert, 
+                         time_t time1_time, time_t time2_time,
+                         X509_EXTENSION *ex, 
+                         char *ucuserdn, char *vomsdir)
+///
+/// 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_USER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d"
+#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"
+   ASN1_OCTET_STRING *asn1data;
+   char              *asn1string, acuserdn[200], acvomsdn[200],
+                      dn_coords[200], fqan_coords[200], time1_coords[200],
+                      time2_coords[200];
+   long               asn1length;
+   int                lasttag=-1, itag, i, acnumber = 1, chain_errors = 0;
+   struct GRSTasn1TagList taglist[MAXTAG+1];
+   time_t             actime1 = 0, actime2 = 0, time_now;
+
+   asn1data   = X509_EXTENSION_get_data(ex);
+   asn1string = (char*)ASN1_STRING_data(asn1data);
+   asn1length = ASN1_STRING_length(asn1data);
+
+   GRSTasn1ParseDump(NULL, (unsigned char*) 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_USER_DN, acnumber);
+        if (GRSTasn1GetX509Name(acuserdn, sizeof(acuserdn), 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(ucuserdn, acuserdn) != 0)
+                             chain_errors |= GRST_CERT_BAD_CHAIN;
+
+        if (GRSTx509VerifyVomsSig(&time1_time, &time2_time,
+                              (unsigned char*)asn1string, taglist, lasttag, vomsdir, acnumber)
+                              != GRST_RET_OK)
+                             chain_errors |= GRST_CERT_BAD_SIG; 
+
+        snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber);
+        itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords);
+        
+        if (itag > -1) actime1 = GRSTasn1TimeToTimeT((unsigned char*)
+                                   &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((unsigned char*)
+                                   &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;
+
+        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;
+                 sprintf(((*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(acuserdn);
+               }
+             else break;
+           }
+      }
+      
+   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; OpenSSL X509 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.)
+{
+   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 */
+   char *ucuserdn = NULL;	/* DN of 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];
+   char cacertpath[16384];
+   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;
+   grst_cert = NULL;
+   new_grst_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));
+   sprintf(cacertpath, "%s/%.8x.0", capath, (int)issuerhash);
+   
+   GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath);
+
+   fp = fopen(cacertpath, "r");
+
+   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 = (GRSTx509Chain*) malloc(sizeof(GRSTx509Chain));
+   *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 */
+              }
+
+            new_grst_cert->serial = (int) ASN1_INTEGER_get(
+                               X509_get_serialNumber(cert));
+            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;
+                    ucuserdn = new_grst_cert->dn;
+                    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((const char*)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((const char*)proxy_part_DN, "/CN=", 4) != 0)
+                  {
+                    new_grst_cert->errors  |= GRST_CERT_BAD_CHAIN;
+                    chain_errors |= GRST_CERT_BAD_CHAIN;
+                  }
+
+                if (strncmp((const char*)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,                                              
+                                              ex,
+                                              ucuserdn, 
+                                              vomsdir);
+                         grst_cert->delegation = (lastcert == NULL) ? i : i+1;
+                       }
+                   }
+                        
+              } 
+          }
+          
+
+      } /* end of for loop */
+
+   if (cacert != NULL) X509_free(cacert);
+ 
+   return GRST_RET_OK;
+}
+
+/// Check certificate chain for GSI proxy acceptability.
+int GRSTx509CheckChain(int *first_non_ca, X509_STORE_CTX *ctx)
+///
+/// Returns X509_V_OK/GRST_RET_OK if valid; OpenSSL X509 errors otherwise.
+///
+/// Inspired by GSIcheck written by Mike Jones, SVE, Manchester Computing,
+/// The University of Manchester.
+///
+/// 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)
+///
+/// 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
+///
+/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions
+///       (although via GRSTx509KnownCriticalExts() we can accept them.)
+{
+   STACK_OF(X509) *certstack;   /* Points to the client's cert chain */
+   X509 *cert;                  /* Points to the client's cert */
+   int depth;                   /* Depth of cert chain */
+   size_t len,len2;             /* Lengths of issuer and cert DN */
+   int IsCA;                    /* Holds whether cert is allowed to sign */
+   int prevIsCA=TRUE;           /* Holds whether previous cert in chain is 
+                                   allowed to sign */
+   int prevIsLimited;		/* previous cert was proxy and limited */
+   int i;                       /* Iteration variables */
+   char *cert_DN;               /* Pointer to current-certificate-in-chain's 
+                                   DN */
+   char *issuer_DN;             /* Pointer to 
+                                   issuer-of-current-cert-in-chain's DN */
+   char *proxy_part_DN;         /* Pointer to end part of current-cert-in-chain
+                                   maybe eg "/CN=proxy" */
+   time_t now;
+   
+   time(&now);
+
+   *first_non_ca = 0; /* set to something predictable if things fail */
+
+   /* Check for context */
+   if (!ctx) return X509_V_ERR_INVALID_CA; 
+     /* Can't GSI-verify if there is no context. Here and throughout this
+        function we report all errors as X509_V_ERR_INVALID_CA. */
+ 
+   /* Set necessary preliminary values */
+   IsCA          = TRUE;           /* =prevIsCA - start from a CA */
+   prevIsLimited = 0;
+ 
+   /* Get the client cert chain */
+   certstack = X509_STORE_CTX_get_chain(ctx);     /* Get the client's chain  */
+   depth     = sk_X509_num(certstack);            /* How deep is that chain? */
+ 
+   /* Check the client chain */
+   for (i=depth-1; i >= 0; --i) 
+      /* loop through client-presented chain starting at CA end */
+      {
+        prevIsCA=IsCA;
+
+        /* Check for X509 certificate and point to it with 'cert' */
+        if ( (cert = sk_X509_value(certstack, i)) )
+          {
+            /* we check times and reject immediately if invalid */
+          
+            if (now <
+           GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0))
+                  return X509_V_ERR_INVALID_CA;
+                
+            if (now > 
+           GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0))
+                  return X509_V_ERR_INVALID_CA;
+
+            /* If any forebear certificate is not allowed to sign we must 
+               assume all decendents are proxies and cannot sign either */
+            if (prevIsCA)
+              {
+                /* always treat the first cert (from the CA files) as a CA */
+                if (i == depth-1) IsCA = TRUE;
+                /* check if this cert is valid CA for signing certs */
+                else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+                
+                if (!IsCA) *first_non_ca = i;
+              } 
+            else 
+              {
+                IsCA = FALSE; 
+                /* Force proxy check next iteration. Important because I can
+                   sign any CA I create! */
+              }
+ 
+            cert_DN   = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
+            issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0);
+            len       = strlen(cert_DN);
+            len2      = strlen(issuer_DN);
+
+            /* issuer didn't have CA status, so this is (at best) a proxy:
+               check for bad proxy extension*/
+
+            if (!prevIsCA)
+              {
+                if (prevIsLimited) /* we reject proxies of limited proxies! */
+                                return X509_V_ERR_INVALID_CA;
+              
+                /* User not allowed to sign shortened DN */
+                if (len2 > len) return X509_V_ERR_INVALID_CA;                           
+                  
+                /* Proxy subject must begin with issuer. */
+                if (strncmp((const char*)cert_DN, issuer_DN, len2) != 0) 
+                              return X509_V_ERR_INVALID_CA;
+
+                /* Set pointer to end of base DN in cert_DN */
+                proxy_part_DN = &cert_DN[len2];
+
+                /* First attempt at support for Old and New style GSI
+                   proxies: /CN=anything is ok for now */
+                if (strncmp((const char*)proxy_part_DN, "/CN=", 4) != 0)
+                                         return X509_V_ERR_INVALID_CA;
+                                         
+                if ((strncmp((const char*)proxy_part_DN, "/CN=limited proxy", 17) == 0) &&
+                    (i > 0)) prevIsLimited = 1; /* ready for next cert ... */
+              } 
+          }
+      }
+
+   /* Check cert whose private key is being used by client. If previous in 
+      chain is not allowed to be a CA then need to check this final cert for 
+      valid proxy-icity too */
+   if (!prevIsCA) 
+     { 
+       if (prevIsLimited) return X509_V_ERR_INVALID_CA;
+        /* we do not accept proxies signed by limited proxies */
+     
+       if ( (cert = sk_X509_value(certstack, 0)) )
+         {
+           /* Load DN & length of DN and either its issuer or the
+              first-bad-issuer-in-chain */
+           cert_DN = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+           issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert),  NULL, 0);
+           len = strlen(cert_DN);
+           len2 = strlen(issuer_DN);
+ 
+           /* issuer didn't have CA status, check for bad proxy extension */
+
+           if (len2 > len) return X509_V_ERR_INVALID_CA;
+             /* User not allowed to sign shortened DN */
+
+           if (strncmp((const char*)cert_DN, issuer_DN, len2) != 0) 
+                           return X509_V_ERR_INVALID_CA;
+             /* Proxy subject must begin with issuer. */
+
+           proxy_part_DN = &cert_DN[len2];                         
+             /* Set pointer to end of DN base in cert_DN */
+             
+           /* Remander of subject must be either "/CN=proxy" or 
+              "/CN=limited proxy" (or /CN=XYZ for New style GSI) */
+              
+           /* First attempt at support for Old and New style GSI
+              proxies: /CN=anything is ok for now. */
+           if (strncmp((const char*)proxy_part_DN, "/CN=", 4) != 0)
+                                   return X509_V_ERR_INVALID_CA;
+         }
+     }
+ 
+   return X509_V_OK; /* 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 *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_USER_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;
+   unsigned char     *asn1string;
+   char               acuserdn[200],
+                      dn_coords[200], fqan_coords[200], time1_coords[200],
+                      time2_coords[200];
+   long               asn1length;
+   int                lasttag=-1, itag, i, acnumber = 1;
+   struct GRSTasn1TagList taglist[MAXTAG+1];
+   time_t             actime1, actime2, time_now;
+
+   asn1data   = X509_EXTENSION_get_data(ex);
+   asn1string = (unsigned char*) (ASN1_STRING_data((asn1data)));
+   asn1length = (long)ASN1_STRING_length(asn1data);
+
+   GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag);
+
+   for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */
+      {
+        snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_USER_DN, acnumber);
+        if (GRSTasn1GetX509Name(acuserdn, sizeof(acuserdn), dn_coords,
+                       (char*)asn1string, taglist, lasttag) != GRST_RET_OK) break;
+
+        if (GRSTx509NameCmp(ucuserdn, acuserdn) != 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];
+   unsigned char  *ucuser;
+   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 = (unsigned char*)
+        X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0);
+
+   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, (char*)ucuser, 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((const char*)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((const char*)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, *certchain;
+  int i, ncerts;
+  long serial = 1234, ptrlen;
+  EVP_PKEY *pkey, *CApkey;
+  const EVP_MD *digest;
+  X509 *certs[GRST_MAX_CHAIN_LEN];
+  X509_REQ *req;
+  X509_NAME *name, *CAsubject, *newsubject;
+  X509_NAME_ENTRY *ent;
+  FILE *fp;
+  BIO *reqmem, *certmem;
+  time_t notAfter;
+
+  /* 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)))
+    {
+      mpcerror(debugfp,
+              "GRSTx509MakeProxyCert(): error reading request from BIO memory\n");
+      BIO_free(reqmem);
+      return GRST_RET_FAILED;
+    }
+    
+  BIO_free(reqmem);
+
+  /* verify signature on the request */
+  if (!(pkey = X509_REQ_get_pubkey(req)))
+    {
+      mpcerror(debugfp,
+              "GRSTx509MakeProxyCert(): error getting public key from request\n");
+      
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }
+
+  if (X509_REQ_verify(req, pkey) != 1)
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error verifying signature on certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }
+    
+  /* read in the signing certificate */
+  if (!(fp = fopen(cert, "r")))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error opening signing certificate file\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  for (ncerts = 1; ncerts < GRST_MAX_CHAIN_LEN; ++ncerts)
+   if ((certs[ncerts] = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL) break;
+
+  if (ncerts == 1) /* zeroth cert with be new proxy cert */
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error reading signing certificate file\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  fclose(fp);
+  
+  CAsubject = X509_get_subject_name(certs[1]);
+
+  /* read in the CA private key */
+  if (!(fp = fopen(key, "r")))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error reading signing private key file\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  if (!(CApkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error reading signing private key in file\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  fclose(fp);
+  
+  /* get subject name */
+  if (!(name = X509_REQ_get_subject_name(req)))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error getting subject name from request\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  /* create new certificate */
+  if (!(certs[0] = X509_new()))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error creating X509 object\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  /* set version number for the certificate (X509v3) and the serial number   
+     need 3 = v4 for GSI proxy?? */
+  if (X509_set_version(certs[0], 3L) != 1)
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error setting certificate version\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  ASN1_INTEGER_set(X509_get_serialNumber(certs[0]), serial++);
+
+  if (!(name = X509_get_subject_name(certs[1])))
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error getting subject name from CA certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  if (X509_set_issuer_name(certs[0], name) != 1)
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting issuer name of certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  /* set issuer and subject name of the cert from the req and the CA */
+  ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("commonName"), 
+                                      MBSTRING_ASC, (unsigned char*)"proxy", -1);
+
+  newsubject = X509_NAME_dup(CAsubject);
+
+  X509_NAME_add_entry(newsubject, ent, -1, 0);
+
+  if (X509_set_subject_name(certs[0], newsubject) != 1)
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting subject name of certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+    
+  X509_NAME_free(newsubject);
+  X509_NAME_ENTRY_free(ent);
+
+  /* set public key in the certificate */
+  if (X509_set_pubkey(certs[0], pkey) != 1)
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting public key of the certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  /* set duration for the certificate */
+  if (!(X509_gmtime_adj(X509_get_notBefore(certs[0]), -GRST_BACKDATE_SECONDS)))
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting beginning time of the certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  if (!(X509_gmtime_adj(X509_get_notAfter(certs[0]), 60 * minutes)))
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting ending time of the certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }
+    
+  /* go through chain making sure this proxy is not longer lived */
+
+  notAfter = 
+     GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[0])), 0);
+
+  for (i=1; i < ncerts; ++i)
+       if (notAfter > 
+           GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[i])),
+                               0))
+         {
+           notAfter = 
+            GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[i])),
+                                0);
+            
+           ASN1_UTCTIME_set(X509_get_notAfter(certs[0]), notAfter);
+         }
+
+  /* sign the certificate with the signing private key */
+  if (EVP_PKEY_type(CApkey->type) == EVP_PKEY_RSA)
+    digest = EVP_md5();
+  else
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error checking signing private key for a valid digest\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  if (!(X509_sign(certs[0], CApkey, digest)))
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error signing certificate\n");
+
+      X509_REQ_free(req);
+      return GRST_RET_FAILED;
+    }    
+
+  /* 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");            
+
+           X509_REQ_free(req);
+           return GRST_RET_FAILED;
+         }
+
+       ptrlen = BIO_get_mem_data(certmem, &ptr);
+  
+       certchain = realloc(certchain, strlen(certchain) + ptrlen + 1);
+       
+       strncat(certchain, ptr, ptrlen);
+    
+       BIO_free(certmem);
+       X509_free(certs[i]);
+     }
+  
+  EVP_PKEY_free(pkey);
+  EVP_PKEY_free(CApkey);
+  X509_REQ_free(req);
+      
+  *proxychain = certchain;  
+  return GRST_RET_OK;
+}
+
+/// 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;
+  char* proxyfile = (char*)malloc(16384);
+  struct stat statbuf;
+
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+  sprintf(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;
+  char* prvkeyfile = (char*) malloc(16384);
+  struct stat statbuf;
+
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+  sprintf(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[16384];
+  va_list ap;
+  
+  va_start(ap, fmt);
+  vsprintf(path, fmt, ap);
+  va_end(ap);
+
+  ret = mkdir(path, mode);
+}
+
+/// 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;
+  size_t           ptrlen;
+  RSA             *keypair;
+  X509_NAME       *subject;
+  X509_NAME_ENTRY *ent;
+  EVP_PKEY        *pkey;
+  X509_REQ        *certreq;
+  BIO             *reqmem, *keymem;
+  const EVP_MD    *digest;
+
+  /* create key pair and put it in a PEM string */
+
+  if ((keypair = RSA_generate_key(GRST_KEYSIZE, 65537, NULL, NULL)) == NULL)
+                                                               return 1;
+
+  keymem = BIO_new(BIO_s_mem());
+  if (!PEM_write_bio_RSAPrivateKey(keymem, keypair, 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';
+
+  BIO_free(keymem);
+  
+  /* now create the certificate request */
+
+  certreq = X509_REQ_new();
+
+  OpenSSL_add_all_algorithms();
+
+  pkey = EVP_PKEY_new();
+  EVP_PKEY_assign_RSA(pkey, keypair);
+
+  X509_REQ_set_pubkey(certreq, pkey);
+  
+  subject = X509_NAME_new();
+  ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"), 
+                                      MBSTRING_ASC, (unsigned char*)"Dummy", -1);
+  X509_NAME_add_entry (subject, ent, -1, 0);
+  X509_REQ_set_subject_name (certreq, subject);
+  
+  digest = EVP_md5();
+  X509_REQ_sign(certreq, pkey, digest);
+
+  reqmem = BIO_new(BIO_s_mem());
+  PEM_write_bio_X509_REQ(reqmem, certreq);
+  ptrlen = BIO_get_mem_data(reqmem, &ptr);
+  
+  *reqtxt = malloc(ptrlen + 1);
+  memcpy(*reqtxt, ptr, ptrlen);
+  (*reqtxt)[ptrlen] = '\0';
+
+  BIO_free(reqmem);
+
+  X509_REQ_free(certreq);
+  
+  return 0;
+}
+
+/// 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[16384], *ptr, *user_dn_enc;
+  size_t           ptrlen;
+  FILE            *fp;
+  RSA             *keypair;
+  X509_NAME       *subject;
+  X509_NAME_ENTRY *ent;
+  EVP_PKEY        *pkey;
+  X509_REQ        *certreq;
+  BIO             *reqmem;
+  const EVP_MD    *digest;
+
+  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 */
+
+  sprintf(prvkeyfile, "%s/cache/%s/%s/userkey.pem",
+           proxydir, user_dn_enc, delegation_id);
+
+  if (prvkeyfile == NULL)  
+    {
+      free(user_dn_enc);
+      return GRST_RET_FAILED;
+    }
+        
+  if ((keypair = RSA_generate_key(GRST_KEYSIZE, 65537, NULL, NULL)) == NULL)
+                                                               return 1;
+          
+  if ((fp = fopen(prvkeyfile, "w")) == NULL) return 2;
+  
+  chmod(prvkeyfile, S_IRUSR | S_IWUSR);
+  free(user_dn_enc);
+
+  if (!PEM_write_RSAPrivateKey(fp, keypair, NULL, NULL, 0, NULL, NULL))
+                               return 3;
+  
+  if (fclose(fp) != 0) return 4;
+  
+  /* now create the certificate request */
+
+  certreq = X509_REQ_new();
+  if (certreq == NULL) return 5;
+
+  OpenSSL_add_all_algorithms();
+
+  pkey = EVP_PKEY_new();
+  EVP_PKEY_assign_RSA(pkey, keypair);
+
+  X509_REQ_set_pubkey(certreq, pkey);
+  
+  subject = X509_NAME_new();
+  ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"), 
+                                      MBSTRING_ASC, (unsigned char*)"Dummy", -1);
+  X509_NAME_add_entry (subject, ent, -1, 0);
+  X509_REQ_set_subject_name (certreq, subject);
+  
+  digest = EVP_md5();
+  X509_REQ_sign(certreq, pkey, digest);
+
+  reqmem = BIO_new(BIO_s_mem());
+  PEM_write_bio_X509_REQ(reqmem, certreq);
+  ptrlen = BIO_get_mem_data(reqmem, &ptr);
+  
+  *reqtxt = malloc(ptrlen + 1);
+  memcpy(*reqtxt, ptr, ptrlen);
+  (*reqtxt)[ptrlen] = '\0';
+
+  BIO_free(reqmem);
+
+  X509_REQ_free(certreq);
+  
+  return 0;
+}
+
+/// 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[16384], *user_dn_enc;
+
+  if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+    
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+  /* proxy file */
+  
+  sprintf(filename, "%s/%s/%s/userproxy.pem",
+           proxydir, user_dn_enc, delegation_id);
+
+  if (unlink(filename) != 0) ret = GRST_RET_NO_SUCH_FILE;  
+
+  /* voms file */
+  
+  sprintf(filename, "%s/%s/%s/voms.attributes",
+           proxydir, user_dn_enc, delegation_id);
+
+  unlink(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  filename[16384], *user_dn_enc;
+  FILE  *fp;
+  X509  *cert;
+
+  if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+    
+  user_dn_enc = GRSThttpUrlEncode(user_dn);
+  
+  sprintf(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");
+  
+  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;
+  unsigned int 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   c, i;
+  char *user_dn_enc, *ptr, *prvkeyfile, proxyfile[16384];
+  STACK_OF(X509) *certstack;
+  BIO  *certmem;
+  X509 *cert;
+  long  ptrlen;        
+  FILE *ifp, *ofp;
+
+  if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+    
+  /* find the existing private key file */
+
+  prvkeyfile = GRSTx509CachedProxyKeyFind(proxydir, delegation_id, user_dn);
+
+  if (prvkeyfile == NULL)
+    {
+      return GRST_RET_FAILED;
+    }
+
+  /* open it ready for later */
+
+  if ((ifp = fopen(prvkeyfile, "r")) == NULL)
+    {
+      free(prvkeyfile);
+      return GRST_RET_FAILED;
+    }
+
+  /* get the X509 stack */
+
+  if (GRSTx509StringToChain(&certstack, proxychain) != GRST_RET_OK)
+    {
+      fclose(ifp);
+      free(prvkeyfile);
+      return GRST_RET_FAILED;
+    }
+
+  /* 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);
+
+  sprintf(proxyfile, "%s/%s/%s/userproxy.pem",
+           proxydir, user_dn_enc, delegation_id);
+           
+  free(user_dn_enc);
+
+  /* set up to write proxy file */
+
+  ofp = fopen(proxyfile, "w");
+  chmod(proxyfile, S_IRUSR | S_IWUSR);
+
+  if (ofp == NULL)
+    {
+      fclose(ifp);
+      free(prvkeyfile);
+      return GRST_RET_FAILED;
+    }
+
+  /* write out the most recent proxy by itself */
+
+  if ( (cert = sk_X509_value(certstack, 0)) )
+    {
+      certmem = BIO_new(BIO_s_mem());
+      if (PEM_write_bio_X509(certmem, cert) == 1)
+        {
+          ptrlen = BIO_get_mem_data(certmem, &ptr);
+          fwrite(ptr, 1, ptrlen, ofp);
+        }
+
+      BIO_free(certmem);
+    }
+
+  /* insert proxy private key, read from private key file */
+
+  while ((c = fgetc(ifp)) != EOF) fputc(c, ofp);
+  unlink(prvkeyfile);
+  free(prvkeyfile);
+
+  for (i=1; i <= sk_X509_num(certstack) - 1; ++i)
+        /* loop through the proxy chain starting at 2nd most recent proxy */
+     {
+       if ( (cert = sk_X509_value(certstack, i)))
+         {
+           certmem = BIO_new(BIO_s_mem());
+           if (PEM_write_bio_X509(certmem, cert) == 1)
+             {
+               ptrlen = BIO_get_mem_data(certmem, &ptr);
+               fwrite(ptr, 1, ptrlen, ofp);
+             }
+
+           BIO_free(certmem);
+         }
+     }
+
+  sk_X509_free(certstack);
+
+  if (fclose(ifp) != 0) return GRST_RET_FAILED;
+  if (fclose(ofp) != 0) return GRST_RET_FAILED;
+
+  return GRST_RET_OK;
+}
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_xacml.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_xacml.c
new file mode 100644
index 0000000000000000000000000000000000000000..89fc50142c90363a1cb8573730f1d538c404e470
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_xacml.c
@@ -0,0 +1,565 @@
+/*
+   Andrew McNab and Shiv Kaushal, University of Manchester.
+   Copyright (c) 2002-3. 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.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <fnmatch.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+#include "gridsite.h"
+
+//#define XACML_DEBUG
+
+#ifdef XACML_DEBUG
+  #define XACML_DEBUG_FILE "/tmp/grstxacmldebug.out"
+#endif
+
+
+/*                                                                      *
+ * Global variables, shared by all GACL functions by private to libgacl *
+ *                                                                      */
+
+extern char     *grst_perm_syms[];
+extern GRSTgaclPerm grst_perm_vals[];
+
+
+FILE* debugfile;
+
+GRSTgaclAcl *GRSTgaclAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *);
+GRSTgaclAcl *GRSTxacmlAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *);
+int          GRSTxacmlPermPrint(GRSTgaclPerm perm, FILE *fp);
+
+/*                                                     *
+ * Functions to read in XACML 1.1 compliant format ACL *
+ * Functions based on method for opening GACL format   *
+ *                                                     */
+
+// need to check these for libxml memory leaks? - what needs to be freed?
+
+
+static GRSTgaclCred *GRSTxacmlCredParse(xmlNodePtr cur)
+/*
+    GRSTxacmlCredParse - parse a credential stored in the libxml structure cur,
+    returning it as a pointer or NULL on error.
+*/
+{
+  xmlNodePtr  attr_val;
+  xmlNodePtr  attr_des;
+  GRSTgaclCred   *cred;
+
+  // cur points to <Subject> or <AnySubjects/>, loop done outside this function.
+
+  if ( (xmlStrcmp(cur->name, (const xmlChar *) "AnySubject") == 0)) cred = GRSTgaclCredNew("any-user");
+
+  else{
+
+  attr_val=cur->xmlChildrenNode->xmlChildrenNode;
+  attr_des=attr_val->next;
+
+  cred = GRSTgaclCredNew((char *) xmlNodeGetContent(attr_des->properties->children));
+
+  cred->next      = NULL;
+
+  //Assumed that there is only one name/value pair per credential
+  GRSTgaclCredAddValue(cred, (char *) xmlNodeGetContent(attr_des->properties->next->children),
+                             (char *) xmlNodeGetContent(attr_val));
+  }
+
+  return cred;
+}
+
+static GRSTgaclEntry *GRSTxacmlEntryParse(xmlNodePtr cur)
+/*
+    GRSTxacmlEntryParse - parse an entry stored in the libxml structure cur,
+    returning it as a pointer or NULL on error. Also checks to see if the following
+    <Rule> tag refers to the same <Target> by checking the <RuleId> of both
+*/
+{
+  int        i, check=0;
+  xmlNodePtr cur2;
+  xmlNodePtr rule_root=cur;
+  GRSTgaclEntry *entry;
+  GRSTgaclCred  *cred;
+
+
+  // Next line not needed as function only called if <Rule> tag found
+  // if (xmlStrcmp(cur->name, (const xmlChar *) "Rule") != 0) return NULL;
+  // cur and rule_root point to the <Rule> tag
+
+  cur = cur->xmlChildrenNode->xmlChildrenNode;
+  // cur should now be pointing at <Subjects> tag
+#ifdef XACML_DEBUG
+  fprintf (debugfile, "Starting to Parse Entry\n");
+#endif
+  entry = GRSTgaclEntryNew();
+
+  while (cur!=NULL){
+
+    if (xmlStrcmp(cur->name, (const xmlChar *) "Subjects") == 0){
+#ifdef XACML_DEBUG
+      fprintf (debugfile, "Starting to Parse Credentials\n");
+#endif
+      if (check==0){
+        // cur still pointing at <Subjects> tag make cur2 point to <Subject> and loop over them.
+	cur2=cur->xmlChildrenNode;
+	while (cur2!=NULL){
+          if ( ((cred = GRSTxacmlCredParse(cur2)) != NULL) && (!GRSTgaclEntryAddCred(entry, cred))){
+            GRSTgaclCredFree(cred);
+            GRSTgaclEntryFree(entry);
+            return NULL;
+	  }
+	  cur2=cur2->next;
+        }
+      }
+    }
+
+    else if (xmlStrcmp(cur->name, (const xmlChar *) "Actions") == 0){
+#ifdef XACML_DEBUG
+      fprintf (debugfile, "Starting to Parse Permissions\n");
+#endif
+      if (xmlStrcmp(xmlNodeGetContent(rule_root->properties->next->children), (const xmlChar *) "Permit") == 0 ){
+#ifdef XACML_DEBUG
+	fprintf (debugfile, "\tPermit-ed actions: ");
+#endif
+        for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) //cur2-><Action>
+          for (i=0; grst_perm_syms[i] != NULL; ++i)
+            if (xmlStrcmp(xmlNodeGetContent(cur2->xmlChildrenNode->xmlChildrenNode), (const xmlChar *) grst_perm_syms[i]) == 0)
+            {
+#ifdef XACML_DEBUG
+              fprintf (debugfile, "%s ", grst_perm_syms[i]);
+#endif
+	      GRSTgaclEntryAllowPerm(entry, grst_perm_vals[i]);
+	    }
+      }
+
+      if (xmlStrcmp(xmlNodeGetContent(rule_root->properties->next->children), (const xmlChar *) "Deny") == 0 ) {
+#ifdef XACML_DEBUG
+	fprintf (debugfile, "\tDeny-ed actions: ");
+#endif
+        for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) //cur2-><Action>
+          for (i=0; grst_perm_syms[i] != NULL; ++i)
+            if (xmlStrcmp(xmlNodeGetContent(cur2->xmlChildrenNode->xmlChildrenNode), (const xmlChar *) grst_perm_syms[i]) == 0)
+            {
+              
+#ifdef XACML_DEBUG
+	      fprintf (debugfile, "%s ", grst_perm_syms[i]);
+#endif
+	      GRSTgaclEntryDenyPerm(entry, grst_perm_vals[i]);
+	    }
+      }
+
+    }
+    else{ // I cannot parse this - give up rather than get it wrong
+#ifdef XACML_DEBUG
+      fprintf (debugfile, "OOOPSIE\n");
+#endif
+      GRSTgaclEntryFree(entry);
+      return NULL;
+    }
+
+    cur=cur->next;
+
+    // Check if next Rule should be included when end of current rule reached
+    // If RuleId are from the same entry (eg Entry1A and Entry1D)
+    // make cur point to the next Rule's <Subjects> tag
+    if (cur==NULL)
+      if (check==0)
+        if (rule_root->next!=NULL)
+	  if ( strncmp((const char*)xmlNodeGetContent(rule_root->properties->children), // RuleId of this Rule
+		       (const char*)xmlNodeGetContent(rule_root->next->properties->children), // RuleId of next Rule
+		 6) == 0){
+#ifdef XACML_DEBUG
+	    fprintf (debugfile, "End of perms and creds, next is %s \n", xmlNodeGetContent(rule_root->next->properties->children));
+#endif
+	    rule_root=rule_root->next;
+	    cur=rule_root->xmlChildrenNode->xmlChildrenNode;
+#ifdef XACML_DEBUG
+	    fprintf (debugfile, "skipped to <%s> tag of next Rule\n", cur->name);
+#endif
+	    check++;
+	  }
+  }
+
+  return entry;
+}
+
+GRSTgaclAcl *GRSTxacmlAclLoadFile(char *filename)
+{
+  xmlDocPtr   doc;
+  xmlNodePtr  cur;
+  GRSTgaclAcl    *acl=NULL;
+
+  doc = xmlParseFile(filename);
+  if (doc == NULL) return NULL;
+
+  cur = xmlDocGetRootElement(doc);
+  if (cur == NULL) return NULL;
+
+  if (!xmlStrcmp(cur->name, (const xmlChar *) "Policy")) { acl=GRSTxacmlAclParse(doc, cur, acl);}
+  else if (!xmlStrcmp(cur->name, (const xmlChar *) "gacl")) {acl=GRSTgaclAclParse(doc, cur, acl);}
+  else /* ACL format not recognised */
+    {
+      xmlFreeDoc(doc);
+      free(cur);
+      return NULL;
+    }
+
+  xmlFreeDoc(doc);
+  return acl;
+}
+
+GRSTgaclAcl *GRSTxacmlAclParse(xmlDocPtr doc, xmlNodePtr cur, GRSTgaclAcl *acl)
+{
+  GRSTgaclEntry  *entry;
+
+  #ifdef XACML_DEBUG
+  debugfile=fopen(XACML_DEBUG_FILE, "w");
+  fprintf (debugfile, "ACL loaded..\n");
+  fprintf (debugfile, "Parsing XACML\n");
+  #endif
+
+  // Have an XACML policy file.
+  // Skip <Target> tag and set cur to first <Rule> tag
+  cur = cur->xmlChildrenNode->next;
+
+  acl = GRSTgaclAclNew();
+
+  while (cur != NULL){
+
+    if ( !xmlStrcmp(cur->name, (const xmlChar *)"Rule") )
+    { // IF statement not needed?
+      #ifdef XACML_DEBUG
+      fprintf (debugfile, "Rule %s found\n", xmlNodeGetContent(cur->properties->children) );
+      fprintf (debugfile, "Parsing Entry for this rule\n");
+      #endif
+      entry = GRSTxacmlEntryParse(cur);
+
+      if (entry == NULL)
+      {
+        GRSTgaclAclFree(acl);
+        return NULL;
+      }
+      else GRSTgaclAclAddEntry(acl, entry);
+
+      #ifdef XACML_DEBUG
+      fprintf (debugfile, "Entry read in\n\n");
+      #endif
+    }
+
+    // If the current and next Rules are part of the same entry then advance two Rules
+    // If not then advance 1
+    if (cur->next != NULL)
+    {
+      if ( strncmp((const char*)xmlNodeGetContent(cur->properties->children),       // RuleId of this Rule
+                   (const char*)xmlNodeGetContent(cur->next->properties->children), // RuleId of next Rule
+                   6) == 0)
+      {
+        #ifdef XACML_DEBUG
+	fprintf (debugfile, "skipping next rule %s, should have been caught previously\n\n", xmlNodeGetContent(cur->next->properties->children) );
+	#endif
+	cur=cur->next;
+      } // Check first 6 characters i.e. Entry1**/
+    }
+
+    cur=cur->next;
+
+  }
+
+  #ifdef XACML_DEBUG
+  fprintf (debugfile, "Finished loading ACL - Fanfare!\n");
+  fclose(debugfile);
+  #endif
+
+  return acl;
+}
+
+
+int GRSTxacmlFileIsAcl(char *pathandfile)
+/* Return 1 if filename in *pathandfile starts GRST_ACL_FILE
+   Return 0 otherwise. */
+{
+  char *filename;
+
+  filename = rindex(pathandfile, '/');
+  if (filename == NULL) filename = pathandfile;
+  else                  filename++;
+
+  return (strncmp((const char*)filename, GRST_ACL_FILE, sizeof(GRST_ACL_FILE) - 1) == 0);
+}
+
+char *GRSTxacmlFileFindAclname(char *pathandfile)
+/* Return malloc()ed ACL filename that governs the given file or directory
+   (for directories, the ACL file is in the directory itself), or NULL if none
+   can be found. */
+{
+  char        *path, *p;
+  struct stat  statbuf;
+
+  path = malloc(strlen(pathandfile) + sizeof(GRST_ACL_FILE) + 1);
+  strcpy(path, pathandfile);
+
+  if (stat(path, &statbuf) == 0)
+    {
+      if (!S_ISDIR(statbuf.st_mode)) /* can strip this / off straightaway */
+        {
+          p = rindex(path, '/');
+          if (p != NULL) *p = '\0';
+        }
+    }
+
+  while (path[0] != '\0')
+       {
+         strcat(path, "/");
+         strcat(path, GRST_ACL_FILE);
+
+         if (stat(path, &statbuf) == 0) return path;
+
+         p = rindex(path, '/');
+         *p = '\0';     /* strip off the / we added for ACL */
+
+         p = rindex(path, '/');
+         if (p == NULL) break; /* must start without / and we there now ??? */
+
+         *p = '\0';     /* strip off another layer of / */                 
+       }
+       
+  free(path);
+  return NULL;
+}
+
+GRSTgaclAcl *GRSTxacmlAclLoadforFile(char *pathandfile)
+/* Return ACL that governs the given file or directory (for directories,
+   the ACL file is in the directory itself.) */
+{
+  char        *path;
+  GRSTgaclAcl     *acl;
+
+  path = GRSTxacmlFileFindAclname(pathandfile);
+  
+  if (path != NULL)
+    {
+      acl = GRSTxacmlAclLoadFile(path);
+      free(path);
+      return acl;
+    }
+
+  return NULL;
+}
+
+
+
+/*                                                     *
+ * Functions to save ACL in XACML 1.1 compliant format *
+ * Functions based on method for saving to GACL format *
+ *                                                     */
+
+
+int GRSTxacmlCredPrint(GRSTgaclCred *cred, FILE *fp)
+/*
+   GRSTxacmlCredPrint - print a credential and any name-value pairs is contains in XACML form
+*/
+{
+  char *q;
+
+  if (cred->auri != NULL)
+    {
+	   fputs("\t\t\t\t<Subject>\n", fp);
+	   fputs("\t\t\t\t\t<SubjectMatch MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n", fp);
+	   fputs("\t\t\t\t\t\t<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">", fp);
+           for (q=cred->auri; *q != '\0'; ++q)
+              if      (*q == '<')  fputs("&lt;",   fp);
+              else if (*q == '>')  fputs("&gt;",   fp);
+              else if (*q == '&')  fputs("&amp;" , fp);
+              else if (*q == '\'') fputs("&apos;", fp);
+              else if (*q == '"')  fputs("&quot;", fp);
+              else                 fputc(*q, fp);
+
+
+	   fputs("</AttributeValue>\n", fp);
+
+	   fputs("\t\t\t\t\t\t<SubjectAttributeDesignator\n", fp);
+	   fputs("\t\t\t\t\t\t\tAttributeId=", fp);
+	   fprintf(fp, "\"cred\"\n");
+	   fputs("\t\t\t\t\t\t\tDataType=", fp);
+           fprintf(fp, "\"auri\"/>\n");
+	   fputs("\t\t\t\t\t</SubjectMatch>\n", fp);
+	   fputs("\t\t\t\t</Subject>\n", fp);
+    }
+    else fputs("\t\t\t\t<AnySubject/>\n", fp);
+
+  return 1;
+}
+
+
+int GRSTxacmlEntryPrint(GRSTgaclEntry *entry, FILE *fp, int rule_number)
+{
+  GRSTgaclCred  *cred;
+  GRSTgaclPerm  i;
+
+  if (entry->allowed){
+
+  fprintf(fp, "\t<Rule RuleId=\"Entry%dA\" Effect=\"Permit\">\n", rule_number);
+  fputs("\t\t<Target>\n", fp);
+  fputs("\t\t\t<Subjects>\n", fp);
+
+  for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+                                            GRSTxacmlCredPrint(cred, fp);
+
+  fputs("\t\t\t</Subjects>\n", fp);
+  fputs("\t\t\t<Actions>\n", fp);
+
+      for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i)
+       if ((entry->allowed) & i) GRSTxacmlPermPrint(i, fp);
+
+  fputs("\t\t\t</Actions>\n", fp);
+  fputs("\t\t</Target>\n", fp);
+  fputs("\t</Rule>\n", fp);
+  }
+
+  if (entry->denied){
+
+  fprintf(fp, "\t<Rule RuleId=\"Entry%dD\" Effect=\"Deny\">\n", rule_number);
+  fputs("\t\t<Target>\n", fp);
+  fputs("\t\t\t<Subjects>\n", fp);
+
+  for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+                                            GRSTxacmlCredPrint(cred, fp);
+
+  fputs("\t\t\t</Subjects>\n", fp);
+  fputs("\t\t\t<Actions>\n", fp);
+
+      for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i)
+       if (entry->denied & i) GRSTxacmlPermPrint(i, fp);
+
+  fputs("\t\t\t</Actions>\n", fp);
+  fputs("\t\t</Target>\n", fp);
+  fputs("\t</Rule>\n", fp);
+  }
+  return 1;
+}
+
+
+int GRSTxacmlPermPrint(GRSTgaclPerm perm, FILE *fp)
+{
+  GRSTgaclPerm i;
+
+  for (i=GRST_PERM_READ; grst_perm_syms[i] != NULL; ++i)
+       if (perm == grst_perm_vals[i])
+         {
+
+	   fputs("\t\t\t\t<Action>\n", fp);
+	   fputs("\t\t\t\t\t<ActionMatch MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n", fp);
+	   fputs("\t\t\t\t\t\t<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">", fp);
+	   fprintf(fp, "%s", grst_perm_syms[i]);
+	   fputs("</AttributeValue>\n", fp);
+	   fputs("\t\t\t\t\t\t<ActionAttributeDesignator\n", fp);
+	   fputs("\t\t\t\t\t\t\tAttributeId=\"urn:oasis:names:tc:xacml:1.0:action:action-id\"\n", fp);
+	   fputs("\t\t\t\t\t\t\tDataType=\"http://www.w3.org/2001/XMLSchema#string\"/>\n", fp);
+	   fputs("\t\t\t\t\t</ActionMatch>\n", fp);
+	   fputs("\t\t\t\t</Action>\n",fp);
+
+           return 1;
+         }
+
+  return 0;
+}
+
+int GRSTxacmlAclPrint(GRSTgaclAcl *acl, FILE *fp, char* dir_uri)
+{
+  GRSTgaclEntry *entry;
+  int rule_number=1;
+
+  fputs("<Policy", fp);
+  fputs("\txmlns=\"urn:oasis:names:tc:xacml:1.0:policy\"\n", fp);
+  fputs("\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n", fp);
+  fputs("\txsi:schemaLocation=\"urn:oasis:names:tc:xacml:1.0:policy cs-xacml-schema-policy-01.xsd\"\n", fp);
+  fputs("\tPolicyId=\"GridSitePolicy\"\n", fp);
+  fputs("\tRuleCombiningAlgId=\"urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides\">\n\n", fp);
+
+  fputs("\t<Target>\n\t\t<Resources>\n\t\t\t<Resource>\n", fp);
+  fputs("\t\t\t\t<ResourceMatch MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n", fp);
+  fputs("\t\t\t\t\t<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">", fp);
+  fprintf(fp, "%s", dir_uri);
+  fputs("</AttributeValue>\n", fp);
+  fputs("\t\t\t\t\t<ResourceAttributeDesignator\n", fp);
+  fputs("\t\t\t\t\t\tAttributeId=\"urn:oasis:names:tc:xacml:1.0:resource:resource-id\"\n", fp);
+  fputs("\t\t\t\t\t\tDataType=\"http://www.w3.org/2001/XMLSchema#string\"/>\n", fp);
+
+  fputs("\t\t\t\t</ResourceMatch>\n\t\t\t</Resource>\n\t\t</Resources>\n\t\t<Subjects>\n\t\t\t<AnySubject/>\n\t\t</Subjects>", fp);
+  fputs("\n\t\t<Actions>\n\t\t\t<AnyAction/>\n\t\t</Actions>\n\t</Target>\n\n", fp);
+
+  for (entry = acl->firstentry; entry != NULL; entry = entry->next){
+
+	GRSTxacmlEntryPrint(entry, fp, rule_number);
+	rule_number++;
+  }
+
+  fputs("</Policy>\n", fp);
+
+  return 1;
+}
+
+int GRSTxacmlAclSave(GRSTgaclAcl *acl, char *filename, char* dir_uri)
+{
+  int   ret;
+  FILE *fp;
+
+  fp = fopen(filename, "w");
+  if (fp == NULL) return 0;
+
+  fprintf(fp,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+
+  ret = GRSTxacmlAclPrint(acl, fp, dir_uri);
+
+  fclose(fp);
+
+  return ret;
+}
+
+
+
+
diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/xrootd-secssl.spec.in b/net/xrootd/src/xrootd/src/XrdSecssl/xrootd-secssl.spec.in
new file mode 100644
index 0000000000000000000000000000000000000000..00561bacd71ac1402e2c369961688b5200f63590
--- /dev/null
+++ b/net/xrootd/src/xrootd/src/XrdSecssl/xrootd-secssl.spec.in
@@ -0,0 +1,38 @@
+%define _unpackaged_files_terminate_build 0
+%define __os_install_post       /bin/true
+
+Summary: ssl plugin with VOMS support
+Name: xrootd-secssl
+Version: @VERSION@
+Release: 1
+Prefix: /opt/xrootd
+License: none
+Group: Applications/File
+Source: @PACKAGE_TARNAME@-@PACKAGE_VERSION@.tar.gz
+BuildRoot: %{_tmppath}/%{name}-root
+AutoReqProv: no
+
+%description
+SSL Plugin for xrootd with VOMS/GSI support and session implementation.
+
+%prep
+
+# TODO: change this explicit path
+%setup -n @PACKAGE_TARNAME@-@PACKAGE_VERSION@
+
+%build
+./bootstrap.sh
+./configure --prefix=/opt/xrootd --with-xrootd-location=/opt/xrootd 
+make -j 4
+
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT
+
+
+%files
+/opt/xrootd/*
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
diff --git a/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh b/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh
index 5395304923411abe43c6002982348d4968d66705..b88fc607e1708874bd3b355a0d7430910526345e 100644
--- a/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh
+++ b/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh
@@ -26,7 +26,7 @@
                                     cerr <<y; sutTrace->End();}}
 #define TRACE(act,x) if (QTRACE(act)) PRINT(x)
 #define DEBUG(y)     TRACE(Debug,y)
-#define EPNAME(x)    const char *epname = x;
+#define EPNAME(x)    static const char *epname = x;
 
 #else
 
diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc
index e96bc8b76ee6a66fe0ee0532324e63c2ea2b38ff..090e33cdc36028c94e2ae51c95f1d285384b76e7 100644
--- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc
+++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc
@@ -158,6 +158,32 @@ int XrdSysLogger::xlogFD() {return -1;}
 /******************************************************************************/
 /*                       P r i v a t e   M e t h o d s                        */
 /******************************************************************************/
+/******************************************************************************/
+/*                               p u t E m s g                                */
+/******************************************************************************/
+  
+// This internal logging method is used when the caller already has the mutex!
+
+void XrdSysLogger::putEmsg(char *msg, int msz)
+{
+    struct iovec eVec[2];
+    int retc;
+    char tbuff[24];
+
+// Prefix message with time
+//
+   eVec[0].iov_base = tbuff;
+   eVec[0].iov_len  = (int)Time(tbuff);
+   eVec[1].iov_base = msg;
+   eVec[1].iov_len  = msz;
+
+// In theory, writev may write out a partial list. This rarely happens in
+// practice and so we ignore that possibility (recovery is pretty tough).
+//
+   do { retc = writev(eFD, (const struct iovec *)eVec, 2);}
+               while (retc < 0 && errno == EINTR);
+}
+
 /******************************************************************************/
 /*                                R e B i n d                                 */
 /******************************************************************************/
@@ -219,9 +245,6 @@ int XrdSysLogger::ReBind(int dorename)
 /*                                  T r i m                                   */
 /******************************************************************************/
 
-#define putEmsg(msg,msz) eVec[1].iov_base = msg; eVec[1].iov_len = msz; \
-                         eVec[0].iov_base = 0; Put(2, eVec)
-  
 #ifndef WIN32
 void XrdSysLogger::Trim()
 {
@@ -240,7 +263,6 @@ void XrdSysLogger::Trim()
           } logList(0,0,0);
 
    struct LogFile *logEnt, *logPrev, *logNow;
-   struct iovec eVec[2];
    char eBuff[2048], logFN[256], logDir[1024], *logSfx;
    struct dirent *dp;
    struct stat buff;
diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh
index bf7c302aeba433c6ef819f42ada6ec89cafdd0ba..36204fb32558f429c24ce02775c60ed1cddffd88 100644
--- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh
+++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh
@@ -88,6 +88,7 @@ int        eInt;
 time_t     eNow;
 int        doLFR;
 
+void   putEmsg(char *msg, int msz);
 int    ReBind(int dorename=1);
 void   Trim();
 };
diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh
index d7722850b6124e6a63ef5cec5a31d93b049ab4af..06ebb34e771dd18ceec66d9c81073b3d87e7353c 100644
--- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh
+++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh
@@ -229,4 +229,8 @@ extern "C"
 #define net_errno errno
 #endif
 
+// The following gets arround a relative new gcc compiler bug
+//
+#define XRDABS(x) (x < 0 ? -x : x)
+
 #endif  // __XRDSYS_PLATFORM_H__
diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh
index b924a3af2e2cd8d1380f9b0f76c82d420292c3f6..f60e1a6439651bacb41ccd14d71cb6a93e0dbc48 100644
--- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh
+++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh
@@ -23,11 +23,6 @@
 #else
 #include <semaphore.h>
 #endif
-#if defined(__solaris__)
-#define SEM_IS_BLOCKED EBUSY
-#else
-#define SEM_IS_BLOCKED EAGAIN
-#endif
 
 #include "XrdSys/XrdSysError.hh"
 
@@ -237,10 +232,10 @@ class XrdSysSemaphore
 public:
 
 inline int  CondWait()
-       {if (sem_trywait( &h_semaphore ))
-           {if (errno == SEM_IS_BLOCKED) return 0;
-                else { throw "sem_CondWait() failed";}
-           }
+       {while(sem_trywait( &h_semaphore ))
+             {if (errno == EAGAIN) return 0;
+              if (errno != EINTR) { throw "sem_CondWait() failed";}
+             }
         return 1;
        }
 
diff --git a/net/xrootd/src/xrootd/src/XrdVersion.hh b/net/xrootd/src/xrootd/src/XrdVersion.hh
index f51487090da3bb18539e8951cc1a8192950b49aa..dce338e752596394092bf1cee3a53a0f93d54ec5 100644
--- a/net/xrootd/src/xrootd/src/XrdVersion.hh
+++ b/net/xrootd/src/xrootd/src/XrdVersion.hh
@@ -1,7 +1,7 @@
 // $Id$
 #ifndef __XRD_VERSION_H__
 #define __XRD_VERSION_H__
-#define XrdVERSION  "20091202-0509-root-1"
+#define XrdVERSION  "v20100205-0000-root-1"
 #if XrdDEBUG
 #define XrdVSTRING XrdVERSION "_dbg"
 #else
diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc
index abefc8b8eda89cb73b83b72ee6416499d75ef3e2..16d9fb7b7327d6db5556822686a8ac5d7aa1ab76 100644
--- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc
+++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc
@@ -41,7 +41,7 @@ static XrdXrootdCBJob *Alloc(XrdXrootdCallBack *cbF, XrdOucErrInfo *erp, int rva
 
        void            DoIt();
 
-inline void            Recycle(){myMutex.Lock();  // eInfo is deleted elsewhere
+inline void            Recycle(){myMutex.Lock();
                                  Next = FreeJob;
                                  FreeJob = this;
                                  myMutex.UnLock();
@@ -125,9 +125,10 @@ void XrdXrootdCBJob::DoIt()
                }
       } else cbFunc->sendError(Result, eInfo);
 
-// Recycle ourselves before returning
+// Tell the requestor that the callback has completed
 //
-   delete eInfo;
+   if (eInfo->getErrCB()) eInfo->getErrCB()->Done(Result, eInfo);
+      else delete eInfo;
    eInfo = 0;
    Recycle();
 }
@@ -182,7 +183,8 @@ void XrdXrootdCallBack::Done(int           &Result,   //I/O: Function result
 //
    if (!(cbj = XrdXrootdCBJob::Alloc(this, eInfo, Result)))
       {eDest->Emsg("Done",ENOMEM,"get call back job; user",eInfo->getErrUser());
-       delete eInfo;
+       if (eInfo->getErrCB()) eInfo->getErrCB()->Done(Result, eInfo);
+          else delete eInfo;
       } else Sched->Schedule((XrdJob *)cbj);
 }
 
@@ -277,7 +279,6 @@ void XrdXrootdCallBack::sendResp(XrdOucErrInfo  *eInfo,
                                  int             ovhd)
 {
    const char *TraceID = "sendResp";
-   unsigned long long Dest;
    struct iovec       rspVec[4];
    XrdXrootdReqID     ReqID;
    int                dlen = 0, n = 1;
@@ -292,10 +293,10 @@ void XrdXrootdCallBack::sendResp(XrdOucErrInfo  *eInfo,
        {        rspVec[n].iov_base = (caddr_t)Msg;
         dlen += rspVec[n].iov_len  = strlen(Msg)+ovhd; n++;      // 2
        }
-// Decode the destination
+
+// Set the destination
 //
-   eInfo->getErrCB(Dest);
-   ReqID.setID(Dest);
+   ReqID.setID(eInfo->getErrArg());
 
 // Send the async response
 //
diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc
index 3ae9705d9a99ea9452557e8965afa702830b949f..f113b1dd6b5d7119b0c404b459018cd5e96542ad 100644
--- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc
+++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc
@@ -178,10 +178,11 @@ int XrdXrootdProtocol::Configure(char *parms, XrdProtocol_Config *pi)
 //
    for ( ; optind < pi->argc; optind++) xexpdo(pi->argv[optind]);
 
-// Pre-initialize some i/o values
+// Pre-initialize some i/o values. Note that we now set maximum readv element
+// transfer size to the buffer size (before it was a reasonable 256K).
 //
    if (!(as_miniosz = as_segsize/2)) as_miniosz = as_segsize;
-   maxBuffsz = BPool->MaxSize();
+   maxTransz = maxBuffsz = BPool->MaxSize();
 
 // Now process and configuration parameters
 //
diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh
index b4aee676f8876990d8e991cc65486f428c73e8cc..b9d8243f6ca32b303fbe46a5a274394b0d8d1c6f 100644
--- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh
+++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh
@@ -29,9 +29,9 @@
 /*                               D e f i n e s                                */
 /******************************************************************************/
   
-#define XROOTD_VERSBIN 0x00000290
+#define XROOTD_VERSBIN 0x00000291
 
-#define XROOTD_VERSION "2.9.0"
+#define XROOTD_VERSION "2.9.1"
 
 #define ROOTD_PQ 2012
 
diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc
index 168418841795e3e4cfd1890699973e6b81de23f0..2202abf9f9f815f237c720daeb0b632fbe46c556 100644
--- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc
+++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc
@@ -512,7 +512,7 @@ int XrdXrootdProtocol::do_Locate()
    static XrdXrootdCallBack locCB("locate");
    int rc, opts, fsctl_cmd = SFS_FSCTL_LOCATE;
    const char *opaque;
-   char *fn = argp->buff, opt[8], *op=opt;
+   char *Path, *fn = argp->buff, opt[8], *op=opt;
    XrdOucErrInfo myError(Link->ID, &locCB, ReqID.getID());
 
 // Unmarshall the data
@@ -532,10 +532,21 @@ int XrdXrootdProtocol::do_Locate()
       return Response.Send(kXR_redirect, Route[RD_locate].Port,
                                          Route[RD_locate].Host);
 
+// Check if this is a non-specific locate
+//
+        if (*fn != '*') Path = fn;
+   else if (*(fn+1))    Path = fn+1;
+   else                {Path = 0; 
+                        fn = XPList.Next()->Path();
+                        fsctl_cmd |= SFS_O_TRUNC;
+                       }
+
 // Prescreen the path
 //
-   if (rpCheck(fn, &opaque)) return rpEmsg("Locating", fn);
-   if (!Squash(fn))          return vpEmsg("Locating", fn);
+   if (Path)
+      {if (rpCheck(Path, &opaque)) return rpEmsg("Locating", Path);
+       if (!Squash(Path))          return vpEmsg("Locating", Path);
+      }
 
 // Preform the actual function
 //
@@ -2226,14 +2237,16 @@ int XrdXrootdProtocol::fsError(int rc, XrdOucErrInfo &myError)
       }
 
 // Process the deferal. We also synchronize sending the deferal response with
-// sending the actual defered response as these can violate time causality.
+// sending the actual defered response by calling Done() in the callback object.
+// This allows the requestor of he callback know that we actually send the
+// kXR_waitresp to the end client and avoid violating time causality.
 //
    if (rc == SFS_STARTED)
       {SI->stallCnt++;
        if (ecode <= 0) ecode = 1800;
        TRACEI(STALL, Response.ID() <<"delaying client up to " <<ecode <<" sec");
        rc = Response.Send(kXR_waitresp, ecode, eMsg);
-       myError.getErrCB()->Done(ecode, &myError);
+       if (myError.getErrCB()) myError.getErrCB()->Done(ecode, &myError);
        return (rc ? rc : 1);
       }
 
diff --git a/net/xrootd/src/xrootd/utils/getCRLcert b/net/xrootd/src/xrootd/utils/getCRLcert
index df9cfbd743747c8c084c1d67e21a11f7cb115727..a0d51ee23f59bf1db08420e38f1a4f8b19b43907 100755
--- a/net/xrootd/src/xrootd/utils/getCRLcert
+++ b/net/xrootd/src/xrootd/utils/getCRLcert
@@ -1,4 +1,3 @@
-<<<<<<< .working
 #! /bin/sh
 
 # $Id: getCRLcert,v 1.1 2009/05/06 15:31:45 ganis Exp $
diff --git a/net/xrootd/src/xrootd/utils/testXrdSecssl b/net/xrootd/src/xrootd/utils/testXrdSecssl
new file mode 100755
index 0000000000000000000000000000000000000000..16fc5972d753d4b6910e17c0a8c9184c3099d39b
--- /dev/null
+++ b/net/xrootd/src/xrootd/utils/testXrdSecssl
@@ -0,0 +1,41 @@
+#
+# Test script for the secssl built with the classic build.
+# Run it like this in a bash shell:
+#
+#        source utils/testXrdSecssl
+#
+# NB: 1) If not bash, start a bash shell first: $ bash
+#     2) You need to include <xrddir>/bin in PATH and <xrddir>/lib
+#        in LD_LIBRARY_PATH first
+#
+# For the autotools build, the test can be run in the following way:
+#
+#        cd src/XrdSecssl
+#        make test
+#
+
+BINEXE="xrdsecssltest"
+
+if test -d "/etc/grid-security/certificates" ; then
+   if test -e "/etc/grid-security/hostcert.pem" && test -e "/etc/grid-security/hostkey.pem" ; then
+      if test -e "/tmp/x509up_u$UID" ; then
+         # Start the test
+         echo "Starting Server ..."
+         $BINEXE server >& /tmp/xrdsecssltest-server.log &
+         echo "Starting Client ..."
+         sleep 1
+         $BINEXE client 2>  /tmp/xrdsecssltest-client.log
+         # Cleanup processes
+         /usr/bin/killall -9 `basename $BINEXE` >& /dev/null
+         sleep 1
+         echo "Client and Server stopped!"
+      else
+         echo "Error: you need /tmp/x509up_u$UID";\
+      fi
+   else
+      echo "Error: you need /etc/grid-security/hostcert.pem and /etc/grid-security/hostkey.pem";\
+   fi
+else
+   echo "Error: you need /etc/grid-security/certificates"
+fi
+