+* Mon Nov 15 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- Back out of (most of) redone VOMS support for committing
+  to JRA1 CVS.
+* Thu Oct 19 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- ==== GridSite version 1.1.3 ====
+* Thu Oct 19 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- Fix Bug #5203 from Martijn Steenbakkers <msteenba>
+  by fixing GACLparseEntry in gridsite-gacl.h
+- Change to C style comments (mostly) in gridsite.h and
+  gridsite-gacl.h (fixes part of Bug #4222 from 
+  <aleks@fys.uio.no>)
+- Fix Bug #4225 from <aleks@fys.uio.no> in
+  GRSTgaclCredsFree()
+- Add GRSTx509CachedProxyFind() and findproxyfile
+  command to allow proxies to be found in proxy cache
+- Change GRSTx509StoreProxy() to GRSTx509CacheProxy() for
+  consistency with this and GRSTx509CachedProxyKeyFind()
+* Wed Oct 18 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- ==== GridSite version 1.1.2 ====
+* Tue Oct 19 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- Copy code from delegation prototype into grst_x509.c
+  and include htproxyput.c and grst-delegation.c
+  optional targets (which depend on gSOAP.)
+* Wed Oct 13 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- Include per-file patch to GRSTgaclFileFindAclname: 
+  .gacl:FILENAME controls FILENAME if it exists.
+* Tue Jul 27 2004 Andrew McNab <Andrew.McNab@man.ac.uk>
+- ==== GridSite version 1.1.1 ====
+* Tue Jul 27 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- Include HTTP Downgrade support in htcp
+* Sat Jul 24 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- Include HTTP Downgrade support in mod_gridsite.
 * Thu Jul 22 2004 Andrew McNab <mcnab@hep.man.ac.uk>
-- ==== GridSite version 1.0.4 ====
+- Begin development version 1.1.x
+* Thu Jul 22 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 1.1.0 ====
 * Mon Jul 19 2004 Andrew McNab <mcnab@hep.man.ac.uk>
 - Changes in line with EGEE SCM - most importantly
   the top level directory becomes org.gridsite.core
 
 For more detailed instructions, see the install.html file, either
 in the ./doc subdirectory in the sources, in the directory
 gridsite-VERSION/html of the docs directory when GridSite is
-installed, or http://www.gridpp.ac.uk/gridsite/1.0.x/install.html
+installed, or http://www.gridsite.org/1.1.x/install.html
 
 GridSite is currently only supported on Linux, but should be
 trivially portable to other Unix platforms where the GNU build
 
 See INSTALL for build and installation instructions, and 
-http://www.gridpp.ac.uk/gridsite/ for configuration and
-usage guides.
+the Documentation section of http://www.gridsite.org/
+for configuration and usage guides.
 
 MAJOR_VERSION=1
-MINOR_VERSION=1.0
-PATCH_VERSION=1.0.4
+MINOR_VERSION=1.1
+PATCH_VERSION=1.1.3
 VERSION=$(PATCH_VERSION)
 
--- /dev/null
+Binaries (and links) are in ./bin; man pages are in ./man/man1
+
+Install by copying binaries/links onto your path, or by copying htcp
+and making symbolic links to htcp from htls, htll, htrm and htmkdir.
+
+All the .1 man pages should be copied to a suitable ./man/man1 
+directory on your man path.
+
+If you just want to install htcp in /usr/local, then unpacking this
+tgz file in /usr/local should do the trick. (Delete this README when
+you're finished!)
+
+For more about htcp see http://www.gridsite.org/
 
 #   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/
-#-----------------------------------------------------------------------------
+#---------------------------------------------------------------
+# For more information about GridSite: http://www.gridsite.org/
+#---------------------------------------------------------------
 #
 # This script takes an Apache .tar.gz as the single command line argument,
 # unpacks the file, modifies the httpd.spec it contains to work without
 
 
 <p> 
 This Guide is intended for webmasters setting up 
-<a href="http://www.gridpp.ac.uk/">GridSite</a> with an Apache 2.0
+<a href="http://www.gridsite.org/">GridSite</a> with an Apache 2.0
 webserver. We assume you have root access to the server machine to do this.
 There is a separate <a href="admin.html">Admin Guide</a> for
 people administrating areas of GridSite
 
--- /dev/null
+.TH findproxyfile 1 "October 2004" findproxyfile "FINDPROXYFILE Manual"
+.SH NAME
+.B findproxyfile
+\- returns full path to GSI Proxy file
+.SH SYNOPSIS
+.B findproxyfile 
+[--proxycache=PATH] [--delegation-id=ID] [--user-dn=DN] [--outsidecache]
+.SH DESCRIPTION
+.B findproxyfile
+returns full path to a GSI Proxy file, either in the proxy cache maintained
+by the GridSite G-HTTPS and delegation portType functions, or in other
+standard places.
+
+If a User DN is given
+.B findproxyfile 
+uses the value of the 
+.B --proxycache
+argument, the GRST_PROXY_PATH or the
+compile time default to detemine the location of the proxy cache directory.
+The directory is searched for a proxy having the given User DN and 
+Delegation ID. (If no Delegation ID is specificed, then the default value is
+used.)
+
+If
+.B findproxyfile
+does not find a proxy or if a User DN is not given, but
+.B --outsidecache
+was given, then the environment variable X509_USER_PROXY and the standard
+location /tmp/x509up_uUID are searched as well.
+
+.SH OPTIONS
+
+.IP "--proxycache=PATH"
+Give the path of the proxy cache directory explicitly, overriding the
+default and the GRST_PROXY_PATH environment variable if present.
+
+.IP "--delegation-id=ID"
+The optional delegation ID is search for in the proxy cache in addition to
+the User DN. If absent, the default Delegation ID value is searched for.
+
+.IP "--user-dn=DN"
+The DN of the full user certificate associated with the proxy to be searched
+for in the proxy cache. (This is not the DN of any proxy earlier in the
+chain: it is a the DN of a certificate issued by a recognised CA.)
+
+.IP "--outsidecache"
+If a User DN is not given, or a proxy not found in the cache, then search
+for a proxy using X509_USER_PROXY environment variable and file name of
+form /tmp/x509up_uUID as well.
+
+.SH RETURN VALUE
+If a proxy is found, its full path is output on standard out.
+
+.SH EXIT CODES
+0 is returned on succcess. Non-zero otherwise.
+
+.SH BUGS
+In this version, no attempt is made to verify or validate the proxies.
+
+.SH AUTHOR
+Andrew McNab <Andrew.McNab@man.ac.uk>
+
+findproxyfile is part of GridSite: http://www.gridsite.org/
 
 
 <p> 
 GACL is the authorization policy language used by 
-<a href="http://www.gridpp.ac.uk/gridsite/">GridSite</a> GACL allows
+<a href="http://www.gridsite.org/">GridSite</a> GACL allows
 policies to be written in terms of common Grid credentials: X.509
 identities, GSI proxies, VOMS attribute certificates and lists of X.509
 identities.
 
-.TH htcp 1 "December 2003" htcp "HTCP Manual"
+.TH htcp 1 "July 2004" htcp "HTCP Manual"
 .SH NAME
 .B htcp, htrm, htls, htll, htmkdir
 \- get, put, delete or list HTTP/HTTPS files or directories
 but leaves you vulnerable to "man in the middle" attacks by hostile servers
 masquerading as your target.
 
+.IP "--downgrade-size <minimum file size for downgrade>"
+Try to use HTTP-Downgrade for HTTPS URLs. Compatible servers will perform
+authentication and authorization on the HTTPS connection and then redirect
+to HTTP for the GET or PUT file transfer. htcp makes the HTTP request using
+the GRID_AUTH_ONETIME single-use passcode obtained via HTTPS. The downgrade
+option will be ignored for directory operations, HTTP URLs, or if the file
+size is less than the value given. If a downgraded transfer isn't possible,
+a normal HTTPS data transfer will be done.
+
 .SH FILES
 .IP /tmp/x509up_uID
 Default GSI Proxy file for Unix UID equal to ID.
 Not enough beta testing (hint hint...)
 
 .SH AUTHOR
-Andrew McNab <mcnab@hep.man.ac.uk>
+Andrew McNab <Andrew.McNab@man.ac.uk>
 
-htcp is part of GridSite: http://www.gridpp.ac.uk/gridsite/
+htcp is part of GridSite: http://www.gridsite.org/
 .SH "SEE ALSO"
 .BR scp(1),
 .BR curl(1),
 
 ##############################################################################
-## GridSite httpd-fileserver.conf - Andrew McNab <mcnab@hep.man.ac.uk>
+## GridSite httpd-fileserver.conf - Andrew McNab <Andrew.McNab@man.ac.uk>
 ##
 ## Example configuration file for GridSite as an HTTP(S) fileserver.
 ##
-## For GridSite documentation, see http://www.gridpp.ac.uk/gridsite/
+## For GridSite documentation, see http://www.gridsite.org/
 ##
 ## This file should be renamed /etc/httpd/conf/httpd.conf and Apache
 ## restarted to use Apache2/GridSite as a simple HTTP(S) fileserver. 
 SSLCertificateKeyFile   /etc/grid-security/hostkey.pem
 SSLCACertificatePath    /etc/grid-security/certificates
 #SSLCARevocationPath    YOUR CRL DIRECTORY WOULD GO HERE
+SSLSessionCache         dbm:/var/cache/mod_ssl/scache
+SSLSessionCacheTimeout  300
 SSLVerifyClient         optional
 SSLVerifyDepth          10
 SSLOptions              +ExportCertData +StdEnvVars
 
 ##############################################################################
-## GridSite httpd-webserver.conf - Andrew McNab <mcnab@hep.man.ac.uk>
+## GridSite httpd-webserver.conf - Andrew McNab <Andrew.McNab@man.ac.uk>
 ##
 ## Example configuration file for GridSite as a Web Server 
 ## (that is, primarily for interactive use with a browser.)
 ##
-## For GridSite documentation, see http://www.gridpp.ac.uk/gridsite/
+## For GridSite documentation, see http://www.gridsite.org/
 ##
 ## This file should be renamed /etc/httpd/conf/httpd.conf and Apache
 ## restarted to use Apache2/GridSite as a webserver. 
 SSLCertificateKeyFile   /etc/grid-security/hostkey.pem
 SSLCACertificatePath    /etc/grid-security/certificates
 #SSLCARevocationPath    YOUR CRL DIRECTORY WOULD GO HERE
+SSLSessionCache         dbm:/var/cache/mod_ssl/scache
+SSLSessionCacheTimeout  300
 SSLVerifyClient         optional
 SSLVerifyDepth          10
 SSLOptions              +ExportCertData +StdEnvVars
 
-<title>GridSite 1.0.x Documentation</title>
+<title>GridSite 1.1.x Documentation</title>
 <body>
-<h1 align=center>GridSite 1.0.x Documentation</h1>
+<h1 align=center>GridSite 1.1.x Documentation</h1>
 
 <p>
-<a href="http://www.gridpp.ac.uk/gridsite/">GridSite</a>
+<a href="http://www.gridsite.org/">GridSite</a>
 is a set of extensions to the Apache 2.0 webserver, which support 
 Grid security based on X.509 certificates. Since GridSite applies access
 control within Apache itself, via mod_gridsite, Grid authorization and
 
 <p>
 We currently distribute GridSite RPMs for RedHat Linux versions 9 and 7.3
 from our download area at 
-<a href="https://www.gridpp.ac.uk/gridsite/download/">
-https://www.gridpp.ac.uk/gridsite/download/</a>
+<a href="https://www.gridsite.org/download/">
+https://www.gridsite.org/download/</a>
 
 <p>
 <b>RedHat 9</b>:
 
 <p>
 Our download area at
-<a href="https://www.gridpp.ac.uk/gridsite/download/">
-https://www.gridpp.ac.uk/gridsite/download/</a> includes a tar-ball
+<a href="https://www.gridsite.org/download/">
+https://www.gridsite.org/download/</a> includes a tar-ball
 distribution of the sources, which can be unpacked and used to build
 GridSite from source. (Bleeding-edge developers can get the current snapshot
 of the same files from our CVS area.)
 starting point for building Apache by hand yourself. The complexities of
 this are outside of the scope of this Guide, but you are welcome to ask for
 assistance on the 
-<a href="http://www.gridpp.ac.uk/gridsite/discuss.html">GridSite
+<a href="http://www.gridsite.org/discuss.html">GridSite
 Discussion List</a>, although 
 <a href="http://www.apache.org/">www.apache.org</a> is a better starting
 point for purely Apache problems.
 
 and - are passed through unmodified, but all other characters are
 represented as %HH, where HH is their two-digit upper-case hexadecimal ASCII
 representation.
-For example, the URL http://www.gridpp.ac.uk/gridsite/ becomes
-http%3A%2F%2Fwww.gridpp.ac.uk%2Fgridsite%2F
+For example, the URL http://www.gridpp.ac.uk/ becomes
+http%3A%2F%2Fwww.gridpp.ac.uk%2F
 
 .B urlencode
 converts each character in all the strings given on the command line. If
 Not enough beta testing (hint hint...)
 
 .SH AUTHOR
-Andrew McNab <mcnab@hep.man.ac.uk>
+Andrew McNab <Andrew.McNab@man.ac.uk>
 
-urlencode is part of GridSite: http://www.gridpp.ac.uk/gridsite/
+urlencode is part of GridSite: http://www.gridsite.org/
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*------------------------------------------------------------------------*
- * For more about GridSite: http://www.gridpp.ac.uk/gridsite/             *
- *------------------------------------------------------------------------*/
+/*---------------------------------------------------------------*
+ * For more about GridSite: http://www.gridsite.org/             *
+ *---------------------------------------------------------------*/
 
 #ifndef HEADER_GACL_H
 #define HEADER_GACL_H
 #define GACLinit() GRSTgaclInit()
 
 #define GACLnewCred(x)         GRSTgaclCredNew((x))
-//GACLcred  *GACLnewCred(char *);
+/* GACLcred  *GACLnewCred(char *); */
 
 #define GACLaddToCred(x,y,z)   GRSTgaclCredAddValue((x),(y),(z))
-//int        GACLaddToCred(GACLcred *, char *, char *);
+/* int        GACLaddToCred(GACLcred *, char *, char *); */
 
 #define GACLfreeCred(x)                GRSTgaclCredFree((x))
-//int        GACLfreeCred(GACLcred *);
+/* int        GACLfreeCred(GACLcred *); */
 
 #define GACLaddCred(x,y)       GRSTgaclEntryAddCred((x),(y))
-//int        GACLaddCred(GACLentry *, GACLcred *);
+/* int        GACLaddCred(GACLentry *, GACLcred *); */
 
 #define GACLdelCred(x,y)       GRSTgaclEntryDelCred((x),(y))
-//int        GACLdelCred(GACLentry *, GACLcred *);
+/* int        GACLdelCred(GACLentry *, GACLcred *); */
 
 #define GACLprintCred(x,y)     GRSTgaclCredPrint((x),(y))
-// int        GACLprintCred(GACLcred *, FILE *);
+/*  int        GACLprintCred(GACLcred *, FILE *); */
 
 
 #define GACLnewEntry()         GRSTgaclEntryNew()
-// GACLentry *GACLnewEntry(void);
+/*  GACLentry *GACLnewEntry(void); */
 
 #define GACLfreeEntry(x)       GRSTgaclEntryFree((x))
-// int        GACLfreeEntry(GACLentry *);
+/*  int        GACLfreeEntry(GACLentry *); */
 
 #define GACLaddEntry(x,y)      GRSTgaclAclAddEntry((x),(y))
-// int        GACLaddEntry(GACLacl *, GACLentry *);
+/*  int        GACLaddEntry(GACLacl *, GACLentry *); */
 
 #define GACLprintEntry(x,y)    GRSTgaclEntryPrint((x),(y))
-// int        GACLprintEntry(GACLentry *, FILE *);
+/*  int        GACLprintEntry(GACLentry *, FILE *); */
 
 
 #define GACLprintPerm(x,y)     GRSTgaclPermPrint((x),(y))
-//int        GACLprintPerm(GACLperm, FILE *);
+/* int        GACLprintPerm(GACLperm, FILE *); */
 
 #define GACLallowPerm(x,y)     GRSTgaclEntryAllowPerm((x),(y))
-// int        GACLallowPerm(GACLentry *, GACLperm);
+/*  int        GACLallowPerm(GACLentry *, GACLperm); */
 
 #define GACLunallowPerm(x,y)   GRSTgaclEntryUnallowPerm((x),(y))
-//int        GACLunallowPerm(GACLentry *, GACLperm);
+/* int        GACLunallowPerm(GACLentry *, GACLperm); */
 
 #define GACLdenyPerm(x,y)      GRSTgaclEntryDenyPerm((x),(y))
-// int        GACLdenyPerm(GACLentry *, GACLperm);
+/*  int        GACLdenyPerm(GACLentry *, GACLperm); */
 
 #define GACLundenyPerm(x,y)    GRSTgaclEntryUndenyPerm((x),(y))
-// int        GACLundenyPerm(GACLentry *, GACLperm);
+/*  int        GACLundenyPerm(GACLentry *, GACLperm); */
 
 #define GACLpermToChar(x)      GRSTgaclPermToChar((x))
-// char      *GACLpermToChar(GACLperm);
+/*  char      *GACLpermToChar(GACLperm); */
 
 #define GACLcharToPerm(x)      GRSTgaclPermFromChar((x))
-// GACLperm   GACLcharToPerm(char *);
+/*  GACLperm   GACLcharToPerm(char *); */
 
 #define GACLnewAcl()           GRSTgaclAclNew()
-// GACLacl   *GACLnewAcl(void);
+/*  GACLacl   *GACLnewAcl(void); */
 
 #define GACLfreeAcl(x)         GRSTgaclAclFree((x))
-// int        GACLfreeAcl(GACLacl *);
+/*  int        GACLfreeAcl(GACLacl *); */
 
 #define GACLprintAcl(x,y)      GRSTgaclAclPrint((x),(y))
-// int        GACLprintAcl(GACLacl *, FILE *);
+/*  int        GACLprintAcl(GACLacl *, FILE *); */
 
 #define GACLsaveAcl(x,y)       GRSTgaclAclSave((y),(x))
-// int        GACLsaveAcl(char *, GACLacl *);
+/*  int        GACLsaveAcl(char *, GACLacl *); */
 
 #define GACLloadAcl(x)         GRSTgaclAclLoadFile((x))
-// GACLacl   *GACLloadAcl(char *);
+/*  GACLacl   *GACLloadAcl(char *); */
 
 #define GACLfindAclForFile(x)  GRSTgaclFileFindAclname((x))
-// char      *GACLfindAclForFile(char *);
+/*  char      *GACLfindAclForFile(char *); */
 
 #define GACLloadAclForFile(x)  GRSTgaclAclLoadforFile((x))
-// GACLacl   *GACLloadAclForFile(char *);
+/*  GACLacl   *GACLloadAclForFile(char *); */
 
 #define GACLisAclFile(x)       GRSTgaclFileIsAcl((x))
-// int        GACLisAclFile(char *);
+/*  int        GACLisAclFile(char *); */
 
 
 #define GACLnewUser(x)         GRSTgaclUserNew((x))
-// GACLuser *GACLnewUser(GACLcred *);
+/*  GACLuser *GACLnewUser(GACLcred *); */
 
 #define GACLfreeUser(x)                GRSTgaclUserFree((x))
-// int       GACLfreeUser(GACLuser *);
+/*  int       GACLfreeUser(GACLuser *); */
 
 #define GACLuserAddCred(x,y)   GRSTgaclUserAddCred((x),(y))
-// int       GACLuserAddCred(GACLuser *, GACLcred *);
+/*  int       GACLuserAddCred(GACLuser *, GACLcred *); */
 
 #define GACLuserHasCred(x,y)   GRSTgaclUserHasCred((x),(y))
-// int       GACLuserHasCred(GACLuser *, GACLcred *);
+/*  int       GACLuserHasCred(GACLuser *, GACLcred *); */
 
 #define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y))
-// GACLcred *GACLuserFindCredType(GACLuser *, char *);
+/*  GACLcred *GACLuserFindCredType(GACLuser *, char *); */
 
 #define GACLtestDnList(x,y)    GRSTgaclDNlistHasUser((x),(y))
-// int        GACLtestDnList(char *, GACLuser *);
+/*  int        GACLtestDnList(char *, GACLuser *); */
 
 #define GACLtestUserAcl(x,y)   GRSTgaclAclTestUser((x),(y))
-// GACLperm   GACLtestUserAcl(GACLacl *, GACLuser *);
+/*  GACLperm   GACLtestUserAcl(GACLacl *, GACLuser *); */
 
 #define GACLtestExclAcl(x,y)   GRSTgaclAclTestexclUser((x),(y))
-// GACLperm   GACLtestExclAcl(GACLacl *, GACLuser *);
+/*  GACLperm   GACLtestExclAcl(GACLacl *, GACLuser *); */
 
 
 #define GACLurlEncode(x)       GRSThttpUrlEncode((x))
-// char      *GACLurlEncode(char *);
+/*  char      *GACLurlEncode(char *); */
 
 #define GACLmildUrlEncode(x)   GRSThttpUrlMildencode((x))
-// char      *GACLmildUrlEncode(char *);
+/*  char      *GACLmildUrlEncode(char *); */
 
-GACLparseEntry *GRSTgaclEntryParse(xmlNodePtr cur);
-// special function for legacy EDG LB service
+GACLentry *GRSTgaclEntryParse(xmlNodePtr cur);
+/*  special function for legacy EDG LB service */
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*------------------------------------------------------------------------*
- * For more about GridSite: http://www.gridpp.ac.uk/gridsite/             *
- *------------------------------------------------------------------------*/
+/*---------------------------------------------------------------*
+ * For more about GridSite: http://www.gridsite.org/             *
+ *---------------------------------------------------------------*/
 
 #ifndef HEADER_SSL_H
 #include <ssl.h>
 #define GRST_VOMS_OID          "1.3.6.1.4.1.8005.100.100.1"
 #define GRST_VOMS_DIR          "/etc/grid-security/vomsdir"
 
+
+
 typedef struct { char                      *name;
                  char                      *value;
-                 struct _GRSTgaclNamevalue *next;  } GRSTgaclNamevalue;
-  
+                 void                      *next;  } GRSTgaclNamevalue;
+
 typedef struct { char                      *type;
                  int                        delegation;
                  GRSTgaclNamevalue         *firstname;
 
 int GRSTgaclInit(void);
 
-// #define GACLnewCred(x)              GRSTgaclCredNew((x))
+/* #define GACLnewCred(x)              GRSTgaclCredNew((x)) */
 GRSTgaclCred  *GRSTgaclCredNew(char *);
 
-// #define GACLaddToCred(x,y,z)        GRSTgaclCredAddValue((x),(y),(z))
+/* #define GACLaddToCred(x,y,z)        GRSTgaclCredAddValue((x),(y),(z)) */
 int        GRSTgaclCredAddValue(GRSTgaclCred *, char *, char *);
 
 #define GRSTgaclCredSetDelegation(cred, level) ((cred)->delegation = (level))
 #define GRSTgaclCredGetDelegation(cred) ((cred)->delegation)
 
-//#define GACLfreeCred(x)              GRSTgaclCredFree((x))
+/* #define GACLfreeCred(x)             GRSTgaclCredFree((x)) */
 int        GRSTgaclCredFree(GRSTgaclCred *);
 
-// #define GACLaddCred(x,y)    GRSTgaclEntryAddCred((x),(y))
+/*  #define GACLaddCred(x,y)   GRSTgaclEntryAddCred((x),(y)) */
 int        GRSTgaclEntryAddCred(GRSTgaclEntry *, GRSTgaclCred *);
 
-// #define GACLdelCred(x,y)    GRSTgaclEntryDelCred((x),(y))
+/*  #define GACLdelCred(x,y)   GRSTgaclEntryDelCred((x),(y)) */
 int        GRSTgaclEntryDelCred(GRSTgaclEntry *, GRSTgaclCred *);
 
-// #define GACLprintCred(x,y)  GRSTgaclCredPrint((x),(y))
+/*  #define GACLprintCred(x,y) GRSTgaclCredPrint((x),(y)) */
 int        GRSTgaclCredCredPrint(GRSTgaclCred *, FILE *);
 
 
-// #define GACLnewEntry(x)             GRSTgaclEntryNew((x))
+/*  #define GACLnewEntry(x)            GRSTgaclEntryNew((x)) */
 GRSTgaclEntry *GRSTgaclEntryNew(void);
 
-// #define GACLfreeEntry(x)    GRSTgaclEntryFree((x))
+/*  #define GACLfreeEntry(x)   GRSTgaclEntryFree((x)) */
 int        GRSTgaclEntryFree(GRSTgaclEntry *);
 
-// #define GACLaddEntry(x,y)   GRSTgaclAclAddEntry((x),(y))
+/*  #define GACLaddEntry(x,y)  GRSTgaclAclAddEntry((x),(y)) */
 int        GRSTgaclAclAddEntry(GRSTgaclAcl *, GRSTgaclEntry *);
 
-// #define GACLprintEntry(x,y) GRSTgaclEntryPrint((x),(y))
+/*  #define GACLprintEntry(x,y)        GRSTgaclEntryPrint((x),(y)) */
 int        GRSTgaclEntryPrint(GRSTgaclEntry *, FILE *);
 
 
-// #define GACLprintPerm(x,y)  GRSTgaclPermPrint((x),(y))
+/*  #define GACLprintPerm(x,y) GRSTgaclPermPrint((x),(y)) */
 int        GRSTgaclPermPrint(GRSTgaclPerm, FILE *);
 
-// #define GACLallowPerm(x,y)  GRSTgaclEntryAllowPerm((x),(y))
+/*  #define GACLallowPerm(x,y) GRSTgaclEntryAllowPerm((x),(y)) */
 int        GRSTgaclEntryAllowPerm(GRSTgaclEntry *, GRSTgaclPerm);
 
-// #define GACLunallowPerm(x,y)        GRSTgaclEntryUnallowPerm((x),(y))
+/*  #define GACLunallowPerm(x,y)       GRSTgaclEntryUnallowPerm((x),(y)) */
 int        GRSTgaclEntryUnallowPerm(GRSTgaclEntry *, GRSTgaclPerm);
 
-// #define GACLdenyPerm(x,y)   GRSTgaclEntryDenyPerm((x),(y))
+/*  #define GACLdenyPerm(x,y)  GRSTgaclEntryDenyPerm((x),(y)) */
 int        GRSTgaclEntryDenyPerm(GRSTgaclEntry *, GRSTgaclPerm);
 
-// #define GACLundenyPerm(x,y) GRSTgaclEntryUndenyPerm((x),(y))
+/*  #define GACLundenyPerm(x,y)        GRSTgaclEntryUndenyPerm((x),(y)) */
 int        GRSTgaclUndenyPerm(GRSTgaclEntry *, GRSTgaclPerm);
 
-// #define GACLpermToChar(x)   GRSTgaclPermToChar((x))
+/*  #define GACLpermToChar(x)  GRSTgaclPermToChar((x)) */
 char      *GRSTgaclPermToChar(GRSTgaclPerm);
 
-// #define GACLcharToPerm(x)   GRSTgaclPermFromChar((x))
+/*  #define GACLcharToPerm(x)  GRSTgaclPermFromChar((x)) */
 GRSTgaclPerm   GRSTgaclPermFromChar(char *);
 
-// #define GACLnewAcl(x)               GRSTgaclAclNew((x))
+/*  #define GACLnewAcl(x)              GRSTgaclAclNew((x)) */
 GRSTgaclAcl   *GRSTgaclAclNew(void);
 
-// #define GACLfreeAcl(x)              GRSTgaclAclFree((x))
+/*  #define GACLfreeAcl(x)             GRSTgaclAclFree((x)) */
 int        GRSTgaclAclFree(GRSTgaclAcl *);
 
-// #define GACLprintAcl(x,y)   GRSTgaclAclPrint((x),(y))
+/*  #define GACLprintAcl(x,y)  GRSTgaclAclPrint((x),(y)) */
 int        GRSTgaclAclPrint(GRSTgaclAcl *, FILE *);
 
-// #define GACLsaveAcl(x,y)    GRSTgaclAclSave((y),(x))
+/*  #define GACLsaveAcl(x,y)   GRSTgaclAclSave((y),(x)) */
 int        GRSTgaclAclSave(GRSTgaclAcl *, char *);
 
-// #define GACLloadAcl(x)              GRSTgaclFileLoadAcl((x))
+/*  #define GACLloadAcl(x)             GRSTgaclFileLoadAcl((x)) */
 GRSTgaclAcl   *GRSTgaclAclLoadFile(char *);
 
-// #define GACLfindAclForFile(x)       GRSTgaclFileFindAclname((x))
+/*  #define GACLfindAclForFile(x)      GRSTgaclFileFindAclname((x)) */
 char      *GRSTgaclFileFindAclname(char *);
 
-// #define GACLloadAclForFile(x)       GRSTgaclFileLoadAcl((x))
+/*  #define GACLloadAclForFile(x)      GRSTgaclFileLoadAcl((x)) */
 GRSTgaclAcl   *GRSTgaclAclLoadforFile(char *);
 
-// #define GACLisAclFile(x)    GRSTgaclFileIsAcl((x))
+/*  #define GACLisAclFile(x)   GRSTgaclFileIsAcl((x)) */
 int        GRSTgaclFileIsAcl(char *);
 
 
-// #define GACLnewUser(x)              GRSTgaclUserNew((x))
+/*  #define GACLnewUser(x)             GRSTgaclUserNew((x)) */
 GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *);
 
-// #define GACLfreeUser(x)             GRSTgaclUserFree((x))
+/*  #define GACLfreeUser(x)            GRSTgaclUserFree((x)) */
 int       GRSTgaclUserFree(GRSTgaclUser *);
 
-// #define GACLuserAddCred(x,y)        GRSTgaclUserAddCred((x),(y))
+/*  #define GACLuserAddCred(x,y)       GRSTgaclUserAddCred((x),(y)) */
 int       GRSTgaclUserAddCred(GRSTgaclUser *, GRSTgaclCred *);
 
-// #define GACLuserHasCred(x,y)        GRSTgaclUserHasCred((x),(y))
+/*  #define GACLuserHasCred(x,y)       GRSTgaclUserHasCred((x),(y)) */
 int       GRSTgaclUserHasCred(GRSTgaclUser *, GRSTgaclCred *);
 
 int       GRSTgaclUserSetDNlists(GRSTgaclUser *, char *);
 
-// #define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y))
+/*  #define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y)) */
 GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *, char *);
 
-// #define GACLtestDnList(x,y) GRSTgaclDNlistHasUser((x),(y))
+/*  #define GACLtestDnList(x,y)        GRSTgaclDNlistHasUser((x),(y)) */
 int GRSTgaclDNlistHasUser(char *, GRSTgaclUser *);
 
-// #define GACLtestUserAcl(x,y)        GRSTgaclAclTestUser((x),(y))
+/*  #define GACLtestUserAcl(x,y)       GRSTgaclAclTestUser((x),(y)) */
 GRSTgaclPerm   GRSTgaclAclTestUser(GRSTgaclAcl *, GRSTgaclUser *);
 
-// #define GACLtestExclAcl(x,y)        GRSTgaclAclTestexclUser((x),(y))
+/*  #define GACLtestExclAcl(x,y)       GRSTgaclAclTestexclUser((x),(y)) */
 GRSTgaclPerm   GRSTgaclAclTestexclUser(GRSTgaclAcl *, GRSTgaclUser *);
 
 
 char      *GRSThttpUrlDecode(char *);
 
-// #define GACLurlEncode(x)    GRSThttpUrlEncode((x))
+/*  #define GACLurlEncode(x)   GRSThttpUrlEncode((x)) */
 char      *GRSThttpUrlEncode(char *);
 
-// #define GACLmildUrlEncode(x)        GRSThttpMildUrlEncode((x))
+/*  #define GACLmildUrlEncode(x)       GRSThttpMildUrlEncode((x)) */
 char      *GRSThttpUrlMildencode(char *);
 
 int GRSTx509NameCmp(char *, char *);
 int GRSTx509CheckChain(int *, X509_STORE_CTX *);
 int GRSTx509VerifyCallback(int, X509_STORE_CTX *);
 
-int GRSTx509CheckVomsSig(unsigned char *, unsigned int,
-                         unsigned char *, unsigned int,
-                         char *, char *, char *);
-int GRSTx509GetVomsCreds(int *, int, size_t, char *, X509 *, X509 *, char *);
+int GRSTx509GetVomsCreds(int *, int, size_t, char *, X509 *, STACK_OF(X509) *, char *);
 GRSTgaclCred *GRSTx509CompactToCred(char *);
 int GRSTx509CompactCreds(int *, int, size_t, char *, STACK_OF(X509) *, char *);
-
+char *GRSTx509CachedProxyFind(char *, char *, char *);
+char *GRSTx509FindProxyFileName(void);
+int GRSTx509MakeProxyCert(char **, FILE *, char *, char *, char *, int);
+char *GRSTx509CachedProxyKeyFind(char *, char *, char *);
+int GRSTx509MakeProxyRequest(char **, char *, char *, char *);
+int GRSTx509CacheProxy(char *, char *, char *, char *);
 
 #define GRST_HEADFILE   "gridsitehead.txt"
 #define GRST_FOOTFILE   "gridsitefoot.txt"
 
 #   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/ 
-#-----------------------------------------------------------------------------
+#---------------------------------------------------------------
+# For more information about GridSite: http://www.gridsite.org/ 
+#---------------------------------------------------------------
 
 include ../VERSION
 
 endif
 
 ifndef prefix
-export prefix=/home/dimeglio/gridsite
+export prefix=/usr/local
 endif
 
 ifndef MYCFLAGS
-export MYCFLAGS=-I. -I../interface -I/usr/include/httpd -I/usr/include/apr-0
+export MYCFLAGS=-I. -I../interface -I/usr/include/httpd -I/usr/include/apr-0 -I/opt/glite/include
 endif
 
 ifndef MYLDFLAGS
 export MYLDFLAGS=-L.
+# export MYLDFLAGS=-L. -L/opt/glite/lib -lvomsc
 endif
 
 #
 #
 
 build: libgridsite.so.$(VERSION) libgridsite.a htcp mod_gridsite.so \
-       urlencode real-gridsite-admin.cgi apidoc
+       urlencode findproxyfile real-gridsite-admin.cgi
 
 libgridsite.so.$(VERSION): grst_x509.o grst_gacl.o grst_http.o
        gcc -shared -Wl,-soname,libgridsite.so.$(MINOR_VERSION) \
        gcc -DVERSION=\"$(PATCH_VERSION)\" -I. -o htcp htcp.c \
           `curl-config --cflags` `curl-config --libs` 
 
-mod_gridsite.so: mod_gridsite.c mod_ssl-private.h
+mod_gridsite.so: mod_gridsite.c mod_ssl-private.h libgridsite.a
        gcc $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \
            -I/usr/include/openssl -I/usr/kerberos/include \
+           -I/usr/include/libxml2 \
            -DVERSION=\"$(VERSION)\" -o mod_gridsite.so \
            mod_gridsite.c $(MYLDFLAGS) -lxml2 -lm -lz -lgridsite
 
             -I/usr/include/openssl -I/usr/kerberos/include \
             -DVERSION=\"$(VERSION)\" -lgridsite -lssl -lcrypto -lxml2 -lz -lm
 
-apidoc: ../interface/gridsite.h grst_x509.c grst_gacl.c grst_http.c \
-         Doxyfile doxygen.css
+findproxyfile: findproxyfile.c libgridsite.a
+       gcc -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \
+            -o findproxyfile findproxyfile.c -L. \
+            -I/usr/include/openssl -I/usr/kerberos/include -lgridsite \
+            -lssl -lcrypto -lxml2 -lz -lm
+
+apidoc:
        doxygen Doxyfile
 
 gaclexample: gaclexample.c libgridsite.a
        gcc -o gaclexample gaclexample.c -I. -L. \
             -I/usr/include/openssl -I/usr/kerberos/include -lgridsite \
-            -lxml2 -lz -lm 
+            -lssl -lcrypto -lxml2 -lz -lm 
+
+#
+# Delegation machinery, including SOAP delegation portType. To build this
+# you need to install gSOAP and set GSOAPDIR to the directory containing
+# soapcpp2 and stdsoap2.h 
+#
+
+GSOAPDIR=/usr/local/lib/gsoap
+
+delegation.wsdl: delegation.h
+       $(GSOAPDIR)/soapcpp2 -c delegation.h
+
+libstdsoap2.a: $(GSOAPDIR)/stdsoap2.c
+       gcc -c -DWITH_OPENSSL $(GSOAPDIR)/stdsoap2.c
+       ar src libstdsoap2.a stdsoap2.o
+
+gridsite-delegation.cgi: grst-delegation.c delegation.h delegation.wsdl \
+                         libstdsoap2.a soapC.c soapServer.c
+       gcc $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-delegation.cgi \
+            grst-delegation.c \
+            -I/usr/include/openssl -I/usr/kerberos/include -I$(GSOAPDIR) \
+            -DVERSION=\"$(VERSION)\" \
+            soapC.c soapServer.c libstdsoap2.a \
+            -lgridsite -lcurl -lz -lssl -lcrypto -lxml2 -lm
+
+htproxyput: htproxyput.c delegation.h delegation.wsdl \
+            soapC.c soapServer.c libstdsoap2.a
+       gcc $(MYCFLAGS) $(MYLDFLAGS) -o htproxyput \
+            htproxyput.c \
+            -I/usr/include/openssl -I/usr/kerberos/include \
+            -g -DVERSION=\"$(VERSION)\" \
+            -I$(GSOAPDIR) -DWITH_OPENSSL \
+            soapC.c soapClient.c libstdsoap2.a \
+            -lgridsite -lcurl -lz -lssl -lcrypto -lxml2 -lm
+
+proxyput-example: proxyput-example.c delegation.h delegation.wsdl \
+            soapC.c soapServer.c libstdsoap2.a
+       gcc $(MYCFLAGS) $(MYLDFLAGS) -o proxyput-example \
+            proxyput-example.c \
+            -I/usr/include/openssl -I/usr/kerberos/include \
+            -g -DVERSION=\"$(VERSION)\" \
+            -I$(GSOAPDIR) -DWITH_OPENSSL \
+            soapC.c soapClient.c libstdsoap2.a \
+            -lgridsite -lcurl -lz -lssl -lcrypto -lxml2 -lm
 
 #
 # Install
 #
 
-install:
+install: apidoc
        mkdir -p $(prefix)/include \
                  $(prefix)/lib \
                  $(prefix)/bin \
        cp -f ../interface/gridsite.h $(prefix)/include
        cp -f ../interface/gridsite-gacl.h $(prefix)/include
        cp -f urlencode $(prefix)/bin
+       cp -f findproxyfile $(prefix)/bin
        cp -f libgridsite.a $(prefix)/lib
        cp -f real-gridsite-admin.cgi $(prefix)/sbin
        cp -f  libgridsite.so.$(PATCH_VERSION) $(prefix)/lib
        cp -f ../doc/*.html ../doc/*.conf ../doc/*.1 ../doc/*.sh \
                $(prefix)/share/doc/gridsite-$(VERSION)
        cp -f ../doc/*.1 $(prefix)/share/man/man1
-       gzip -f $(prefix)/share/man/man1/*.1
+       gzip $(prefix)/share/man/man1/*.1
        cd ../doc ; for i in *.1 ; do ../src/roffit < $$i \
           > $(prefix)/share/doc/gridsite-$(VERSION)/$$i.html ; done
        cp -f htcp $(prefix)/bin
                  ../gridsite-$(PATCH_VERSION)/interface
        cp -f ../VERSION ../README ../LICENSE ../CHANGES ../INSTALL \
                  ../gridsite-$(PATCH_VERSION)
-       cp -f Makefile *.c *.h roffit gridsite.spec \
+       cp -f Makefile grst*.c htproxyput.c proxyput-example.c htcp.c \
+                 urlencode.c findproxyfile.c gaclexample.c mod_gridsite.c \
+                 delegation.h grst_admin.h mod_ssl-private.h \
+                 roffit gridsite.spec \
                  Doxyfile doxygen.css doxyheader.html \
                  ../gridsite-$(PATCH_VERSION)/src
        cp -f ../doc/*.html ../doc/*.1 ../doc/*.conf ../doc/*.sh \
 
--- /dev/null
+//gsoap ns service name:       delegation
+//gsoap ns service style:      rpc
+//gsoap ns service encoding:   encoded
+//gsoap ns service namespace:  http://www.gridsite.org/ns/delegation.wsdl
+//gsoap ns service location:   http://localhost/delegserver.cgi
+
+struct ns__putProxyResponse { } ;
+
+//gsoap ns schema namespace: urn:delegation
+int ns__getProxyReq(char *delegationID, char **request);
+int ns__putProxy(char *delegationID, char *proxy, 
+                 struct ns__putProxyResponse *unused);
 
-<p><a href=http://www.gridpp.ac.uk/authz/>GridSite</a> Version 0.9.1
+<p><a href=http://www.gridsite.org/>GridSite</a> Version 1.1.x
 
--- /dev/null
+/*
+   Copyright (c) 2002-4, 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 "0.0.0"
+#endif
+
+#define _GNU_SOURCE
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "gridsite.h"
+  
+void printsyntax(char *argv0)
+{
+  char *p;
+
+  p = rindex(argv0, '/');
+  if (p != NULL) ++p;
+  else           p = argv0;
+
+  fprintf(stderr, "%s [--outsidecache] [--proxycache=PATH] "
+                  "[--delegation-id=DELEGATION-ID] [--user-dn=USER-DN]\n"
+                  "(Version: %s)\n", p, VERSION);
+}
+  
+#define GRST_PROXY_CACHE "/var/www/proxycache"
+  
+int main(int argc, char *argv[])
+{
+  char  *delegation_id = "_", *proxycache = "", *user_dn = "",
+        *proxyfile = NULL;        
+  int    c, outsidecache = 0, verbose = 0, option_index;
+  struct option long_options[] = {      {"verbose",            0, 0, 'v'},
+                                        {"outsidecache",       0, 0, 0},
+                                        {"proxycache",         1, 0, 0},
+                                        {"delegation-id",      1, 0, 0},
+                                        {"user-dn",            1, 0, 0},
+                                        {0, 0, 0, 0}  };
+
+  if (argc == 1)
+    {
+      printsyntax(argv[0]);
+      return 0;
+    }
+
+  while (1)
+       {
+         option_index = 0;
+                                                                                
+         c = getopt_long(argc, argv, "v", long_options, &option_index);
+
+         if      (c == -1) break;
+         else if (c == 0)
+           {
+             if      (option_index == 1) outsidecache  = 1;
+             else if (option_index == 2) proxycache    = optarg;
+             else if (option_index == 3) delegation_id = optarg;
+             else if (option_index == 4) user_dn       = optarg;
+           }
+         else if (c == 'v') ++verbose;
+       }
+       
+  if (*user_dn != '\0') /* try to find in proxy cache */
+    {
+      if ((proxycache == NULL) || (*proxycache == '\0'))
+        proxycache = getenv("GRST_PROXY_CACHE");
+
+      if ((proxycache == NULL) || (*proxycache == '\0'))
+        proxycache = GRST_PROXY_CACHE;
+
+      proxyfile = GRSTx509CachedProxyFind(proxycache, delegation_id, user_dn);
+    }
+    
+  if (((proxyfile == NULL) || (*proxyfile == '\0')) && outsidecache)
+    {
+      proxyfile = GRSTx509FindProxyFileName();
+    }
+
+  if ((proxyfile != NULL) && (*proxyfile != '\0'))
+    {
+      puts(proxyfile);
+      return 0;   
+    }
+    
+  fputs("No proxy file found\n", stderr);
+    
+  return 1;
+}
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*------------------------------------------------------------------------*
- * For more about GridSite: http://www.gridpp.ac.uk/gridsite/             *
- *------------------------------------------------------------------------*/
+/*---------------------------------------------------------------*
+ * For more about GridSite: http://www.gridsite.org/             *
+ *---------------------------------------------------------------*/
 
 /*
    Example program using GACL 
 
 Group: System Environment/Daemons
 Source: %{name}-%{version}.src.tar.gz
 Prefix: %(echo ${MYPREFIX:-/usr})
-URL: http://www.gridpp.ac.uk/gridsite/
+URL: http://www.gridsite.org/
 Vendor: GridPP
 #Requires: libxml2,curl-ssl,mod_ssl
 #Buildrequires: libxml2-devel,curl-ssl-devel,httpd-devel
-Packager: Andrew McNab <mcnab@hep.man.ac.uk>
+Packager: Andrew McNab <Andrew.McNab@man.ac.uk>
 
 %description
 GridSite adds GSI, VOMS and GACL support to Apache 2.0 (mod_gridsite),
 programs for interactive management of HTTP(S) servers (gridsite-admin.cgi)
 
 See %(echo ${MYPREFIX:-/usr})/share/doc/gridsite-%{version} and
-http://www.gridpp.ac.uk/gridsite/ for details.
+http://www.gridsite.org/ for details.
 
 %package -n htcp
 Group: Applications/Internet
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/lib/libgridsite.so.%{version}
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/lib/libgridsite.so
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/urlencode
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/findproxyfile
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/doc/gridsite-%{version}
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/urlencode.1.gz
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/include/gridsite.h
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htls.1.gz
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htll.1.gz
 %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htmkdir.1.gz
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/findproxyfile.1.gz
 
--- /dev/null
+/*
+   Copyright (c) 2002-4, 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.
+*/
+
+/*---------------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridpp.ac.uk/authz/gridsite/ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef VERSION
+#define VERSION "0.0.1"
+#endif
+
+#define _GNU_SOURCE
+#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 <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>
+
+#include <curl/curl.h>
+/* #include <gacl.h> */
+
+#include "gridsite.h"
+
+#include "soapH.h"
+#include "delegation.nsmap"
+
+#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 <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>
+
+#define GRST_KEYSIZE       512
+#define GRST_PROXYCACHE    "/../proxycache/"
+#define GRST_SUPPORT_G_HTTPS
+
+#ifdef GRST_SUPPORT_G_HTTPS
+void GRSThttpError(char *status)
+{
+  printf("Status: %s\n", status);
+  printf("Server-CGI: GridSite %s\n", VERSION);
+  printf("Content-Length: %d\n", 2 * strlen(status) + 58);
+  puts("Content-Type: text/html\n");
+   
+  printf("<head><title>%s</title></head>\n", status);
+  printf("<body><h1   >%s</h1   ></body>\n", status);
+   
+  exit(0);
+}
+
+int GRSTmethodPutProxy(char *delegation_id, char *user_dn)
+/* return 0 on success; non-zero on error */
+{
+  int   c, len = 0, i;
+  char *docroot, *contentlen, *contenttype, *proxychain, *proxydir;
+  FILE *fp;
+
+  if (((contenttype = getenv("CONTENT_TYPE")) == NULL) ||
+       (strcmp(contenttype, "application/x-x509-user-cert-chain") != 0))
+                               return 2;
+  
+  contentlen = getenv("CONTENT_LENGTH");
+  if (contentlen == NULL) return 2;
+  len = atoi(contentlen);
+  
+  if ((delegation_id == NULL) || (*delegation_id == '\0')) 
+                                                    delegation_id = "_";
+  
+  docroot = getenv("DOCUMENT_ROOT");
+  asprintf(&proxydir, "%s/%s", docroot, GRST_PROXYCACHE);
+
+  if ((user_dn == NULL) || (user_dn[0] == '\0') ||
+      (GRSTx509CacheProxy(proxydir, delegation_id, user_dn, proxychain) 
+                                                      != GRST_RET_OK))
+    {
+      return GRST_RET_FAILED;
+    }
+    
+  free(proxydir);
+      
+  return GRST_RET_OK;
+}
+#endif
+
+int main(int argn, char *argv[])
+{
+  char      *docroot, *method, *request, *p, *client_dn, *user_dn,
+            *delegation_id, *reqtxt, *proxydir;
+  struct soap soap;
+  
+  method  = getenv("REQUEST_METHOD");
+  if (strcmp(method, "POST") == 0)
+    {
+      soap_init(&soap);
+      soap_serve(&soap); /* CGI application */
+      return 0;
+    }
+    
+#ifdef GRST_SUPPORT_G_HTTPS
+  docroot = getenv("DOCUMENT_ROOT");
+
+  request = strdup(getenv("REQUEST_URI"));
+  p = index(request, '?');
+  if (p != NULL) *p = '\0';
+
+      
+  /* non HTTP POST methods - ie special G-HTTPS methods */
+
+  delegation_id = getenv("HTTP_DELEGATION_ID");
+  if ((delegation_id == NULL) || (*delegation_id == '\0')) delegation_id = "_";
+
+  user_dn = NULL;
+  client_dn = getenv("SSL_CLIENT_S_DN"); 
+  if (client_dn != NULL) 
+    {
+      user_dn = strdup(client_dn);
+
+      /* we assume here that mod_ssl has verified proxy chain already ... */
+
+      p = strstr(user_dn, "/CN=proxy");
+      if (p != NULL) *p = '\0';      
+
+      p = strstr(user_dn, "/CN=limited proxy");
+      if (p != NULL) *p = '\0';      
+    }
+  
+  if (user_dn == NULL) /* all methods require client auth */
+    {
+      GRSThttpError("403 Forbidden");
+    }  
+  else if (strcmp(method, "GET-PROXY-REQ") == 0)
+    {
+      docroot = getenv("DOCUMENT_ROOT");
+      asprintf(&proxydir, "%s/%s", docroot, GRST_PROXYCACHE);
+    
+      if (GRSTx509MakeProxyRequest(&reqtxt, proxydir,
+                                   delegation_id, user_dn) == 0)
+        {
+          puts("Status: 200 OK");
+          puts("Content-Type: application/x-x509-cert-request");
+          printf("Content-Length: %d\n\n", strlen(reqtxt));
+          fputs(reqtxt, stdout);
+          free(proxydir);
+          return 0;
+        }
+      
+      puts("Status: 500 Internal Server Error\n");
+      free(proxydir);
+      return 0;
+    }  
+  else if (strcmp(method, "PUT-PROXY-CERT") == 0)
+    {
+      if (GRSTmethodPutProxy(delegation_id, user_dn) == 0)
+        {
+          puts("Status: 200 OK\n");
+          return 0;
+        }
+        
+      puts("Status: 500 Internal Server Error\n");
+      return 0;
+    }  
+  else 
+    {
+      GRSThttpError("501 Method Not Implemented");
+    }
+#endif
+}
+
+int ns__getProxyReq(struct soap *soap, char *delegation_id,                                        
+                                       char **request)
+{ 
+  char *p, *client_dn, *user_dn, *docroot, *proxydir;
+  
+  user_dn = NULL;
+  client_dn = getenv("SSL_CLIENT_S_DN"); 
+  if (client_dn != NULL) 
+    {
+      user_dn = strdup(client_dn);
+
+      /* we assume here that mod_ssl has verified proxy chain already ... */
+
+      p = strstr(user_dn, "/CN=proxy");
+      if (p != NULL) *p = '\0';      
+
+      p = strstr(user_dn, "/CN=limited proxy");
+      if (p != NULL) *p = '\0';      
+    }
+
+  if ((delegation_id == NULL) || (*delegation_id == '\0')) delegation_id = "_";
+  
+  docroot = getenv("DOCUMENT_ROOT");
+  asprintf(&proxydir, "%s/%s", docroot, GRST_PROXYCACHE);
+
+  if ((user_dn != NULL) && (user_dn[0] != '\0') && 
+      (GRSTx509MakeProxyRequest(request, proxydir,  
+                                delegation_id, user_dn) == 0))
+    {
+      return SOAP_OK;
+    }
+      
+  return SOAP_ERR;
+} 
+
+int ns__putProxy(struct soap *soap, char *delegation_id, 
+                                    char *proxy,
+                                    struct ns__putProxyResponse *unused)
+{ 
+  int   fd, c, len = 0, i;
+  char *docroot, *proxydir, *p, *client_dn, *user_dn;
+  
+  user_dn = NULL;
+  client_dn = getenv("SSL_CLIENT_S_DN"); 
+  if (client_dn != NULL) 
+    {
+      user_dn = strdup(client_dn);
+
+      /* we assume here that mod_ssl has verified proxy chain already ... */
+
+      p = strstr(user_dn, "/CN=proxy");
+      if (p != NULL) *p = '\0';      
+
+      p = strstr(user_dn, "/CN=limited proxy");
+      if (p != NULL) *p = '\0';      
+    }
+  
+  if ((delegation_id == NULL) || (*delegation_id == '\0')) 
+                                                    delegation_id = "_";
+  
+  docroot = getenv("DOCUMENT_ROOT");
+  asprintf(&proxydir, "%s/%s", docroot, GRST_PROXYCACHE);
+
+  if ((user_dn == NULL) || (user_dn[0] == '\0') ||
+      (GRSTx509CacheProxy(proxydir, delegation_id, user_dn, proxy) 
+                                                      != GRST_RET_OK))
+    {
+      return SOAP_ERR;
+    }
+      
+  return SOAP_OK;
+} 
+
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*---------------------------------------------------------------------------*
- * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/       *
- *---------------------------------------------------------------------------*/
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
 
 void  GRSThttpError(char *);
 void  adminfooter(GRSThttpBody *, char *, char *, char *, char *);
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*---------------------------------------------------------------------------*
- * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/       *
- *---------------------------------------------------------------------------*/
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
 
 #ifndef VERSION
 #define VERSION "x.x.x"
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*---------------------------------------------------------------------------*
-* This program is part of GridSite: http://www.gridpp.ac.uk/authz/gridsite/ *
-*---------------------------------------------------------------------------*/
+/*-----------------------------------------------------------*
+* This program is part of GridSite: http://www.gridsite.org/ *
+*------------------------------------------------------------*/
 
 #include <stdio.h>
 #include <stdlib.h>
 /*****************************************/
 
 void show_acl(int admin, GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
-  /* Shows the contents of the ACL. Gives edit 'buttons' if (int admin) == 1*/
+  // Shows the contents of the ACL. Gives edit 'buttons' if (int admin) == 1
   GRSTgaclAcl *acl;
   GRSTgaclEntry *entry;
   GRSTgaclCred *cred;
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*---------------------------------------------------------------------------*
- * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/       *
- *---------------------------------------------------------------------------*/
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
 
 #ifndef VERSION
 #define VERSION "x.x.x"
   if ((getenv("GRST_NO_LINK") == NULL) &&
       (getenv("REDIRECT_GRST_NO_LINK") == NULL))
     GRSThttpPrintf(bp, "Built with "
-     "<a href=\"http://www.gridpp.ac.uk/gridsite/\">GridSite</a> %s\n",
+     "<a href=\"http://www.gridsite.org/\">GridSite</a> %s\n",
      VERSION);
    
   GRSThttpPrintf(bp, "</small>\n");
 
    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/ *
- *------------------------------------------------------------------------*/
+/*---------------------------------------------------------------*
+ * For more information about GridSite: http://www.gridsite.org/ *
+ *---------------------------------------------------------------*/
 
 #include <stdio.h>
 #include <stdlib.h>
   
   if (firstcred->next != NULL) GRSTgaclCredsFree(firstcred->next);
   
-  return GRSTgaclCredsFree(firstcred);
+  return GRSTgaclCredFree(firstcred);
 }
 
 static int GRSTgaclCredInsert(GRSTgaclCred *firstcred, GRSTgaclCred *newcred)
    (for directories, the ACL file is in the directory itself), or NULL if none
    can be found. */
 {
-  char        *path, *p;
+  int          len;
+  char        *path, *file, *p;
   struct stat  statbuf;
+   
+  len = strlen(pathandfile);
+  if (len == 0) return NULL;
   
-  path = malloc(strlen(pathandfile) + sizeof(GRST_ACL_FILE) + 1);
+  path = malloc(len + sizeof(GRST_ACL_FILE) + 2);
   strcpy(path, pathandfile);
 
-  if (stat(path, &statbuf) == 0)
+  if ((stat(path, &statbuf) == 0)      && 
+       S_ISDIR(statbuf.st_mode)                &&
+      (path[len-1] != '/'))
     {
-      if (!S_ISDIR(statbuf.st_mode)) /* can strip this / off straightaway */
+      strcat(path, "/");
+      ++len;
+    }
+    
+  if (path[len-1] != '/')
+    {
+      p = rindex(pathandfile, '/');
+      if (p != NULL)
         {
-          p = rindex(path, '/');
-          if (p != NULL) *p = '\0';
+          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 */
         }
     }
 
 
 /*
-   Copyright (c) 2002-3, Andrew McNab, University of Manchester
+   Copyright (c) 2002-4, Andrew McNab, University of Manchester
    All rights reserved.
 
    Redistribution and use in source and binary forms, with or
    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/
-   ------------------------------------------------------------------------
+   ---------------------------------------------------------------
+    For more information about GridSite: http://www.gridsite.org/
+   ---------------------------------------------------------------
 */ 
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
-#include <sys/stat.h>
 #include <unistd.h>       
-#include <sys/types.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
 #include <dirent.h>
 #include <string.h>
-#include <openssl/evp.h>
+#include <pwd.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
 #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>
+
+#ifdef GRST_VOMS_SUPPORT
+#include <glite/security/voms/voms_apic.h>
+#endif
 
 #include "gridsite.h"
 
+#define GRST_KEYSIZE   512
+#define GRST_PROXYCACHE        "/../proxycache/"
+#define GRST_MAX_CHAIN_LEN 9
+
 /// Compare X509 Distinguished Name strings
 int GRSTx509NameCmp(char *a, char *b)
 /**
 //   else    return GRST_RET_FAILED;
 }
 
-/// Check VOMS signature
-/** 
- *  Return GRST_RET_OK if signature starting at *sig matches *data and is 
- *  from VOMS *vo; return GRST_RET_FAILED otherwise.
- */
- 
-int GRSTx509CheckVomsSig(unsigned char *sig, unsigned int siglen, 
-                         unsigned char *data, unsigned int datalen,
-                         char *vomsdir, char *vo, char *vomsdn)
-{
-   int            ret = 0;
-   char          *certfilename, *certdn;
-   FILE          *fp;
-   DIR           *vomsDIR;
-   struct dirent *certdirent;
-   X509          *cert;
-   EVP_PKEY      *pubkey;
-   EVP_MD_CTX     ctx;   
-   struct stat    statbuf;
-   time_t         now;
-   
-   time(&now);
-
-   vomsDIR = opendir(vomsdir);
-   if (vomsDIR == NULL) 
-     {
-       return GRST_RET_NO_SUCH_FILE;
-     }
-
-   while ((certdirent = readdir(vomsDIR)) != NULL)
-        {
-          certfilename = malloc(strlen(vomsdir) + 
-                                strlen(certdirent->d_name) + 2);
-                                
-          strcpy(certfilename, vomsdir);
-          strcat(certfilename, "/");
-          strcat(certfilename, certdirent->d_name);
-          
-          if ((stat(certfilename, &statbuf) != 0) || S_ISDIR(statbuf.st_mode))
-            {
-              free(certfilename);
-              continue;
-            }
-          
-          fp = fopen(certfilename, "r");
-          free(certfilename);
-          
-          if (fp == NULL) continue;
-                                  
-          cert = PEM_read_X509(fp, NULL, NULL, NULL);
-          fclose(fp);
-          
-          if (cert == NULL) continue;
-
-          certdn = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
-
-          if ((now <
-     GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert))) ) ||
-              (now >
-     GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter( cert))) ) ||          
-              (GRSTx509NameCmp(certdn, vomsdn) != 0))
-            {
-              X509_free(cert);
-              continue;
-            }
-                    
-          pubkey = X509_extract_key(cert);
-
-          if (pubkey != NULL)
-            {   
-//              EVP_MD_CTX_init(&ctx);
-              EVP_VerifyInit(&ctx, EVP_sha1());
-              EVP_VerifyUpdate(&ctx, (unsigned char *) data, datalen);
-     
-              ret=EVP_VerifyFinal(&ctx, (unsigned char *) sig, siglen, pubkey);
-                 
-//              EVP_MD_CTX_cleanup(&ctx);
-            }
-
-          closedir(vomsDIR); /* we're finished now, one way or the other */
-
-          X509_free(cert);
-
-          /* since we matched cert names and times, we always return */
-
-          if (ret == 1) return GRST_RET_OK;
-          else          return GRST_RET_BAD_SIGNATURE;
-        }
-
-   closedir(vomsDIR);
-
-   return GRST_RET_CERT_NOT_FOUND; /* didnt find a matching VOMS cert */
-}
-
 /// Get the VOMS attributes in the extensions to the given cert
 /*
  *  Puts any VOMS credentials found into the Compact Creds string array
  */
 
 int GRSTx509GetVomsCreds(int *lastcred, int maxcreds, size_t credlen, 
-                         char *creds, X509 *cert, X509 *usercert, 
+                         char *creds, X509 *cert, STACK_OF(X509) *certstack,
                          char *vomsdir)
 {
-   int  i, j;   
+#ifndef GRST_VOMS_SUPPORT
+   return GRST_RET_OK;
+}
+#else
+
+/*
+   int  j;   
    unsigned int siglen=-1, datalength=-1, dataoffset = -1;
    char s[80];
    unsigned char *charstr, *p, *time1 = NULL, *time2 = NULL, *vo = NULL,
    ASN1_STRING    *asn1str;
    time_t          now, time1_time = 0, time2_time = 0, 
                    uctime1_time, uctime2_time;
+*/
+
+
+   struct vomsdata *vd;
+   int    i, j, vomserror;
+
+   vd = VOMS_Init(NULL, NULL);
+
+   if (VOMS_Retrieve(cert, certstack, RECURSE_CHAIN, vd, &vomserror) &&
+       (vd->data != NULL))
+     {     
+       for (i = 0; vd->data[i] != NULL; ++i)
+          {
+            if (vd->data[i]->fqan != NULL)
+                for (j = 0; vd->data[i]->fqan[j] != NULL; ++j)
+                   {
+                     if (*lastcred >= maxcreds - 1)
+                       {
+                         VOMS_Destroy(vd);
+                         return GRST_RET_OK;
+                       }
+
+                     ++(*lastcred);
+            
+                     snprintf(&creds[*lastcred * (credlen + 1)], 
+                           credlen+1,
+                           "VOMS %010lu %010lu 0 %s",
+                           GRSTasn1TimeToTimeT(vd->data[i]->date1), 
+                           GRSTasn1TimeToTimeT(vd->data[i]->date2),
+                           vd->data[i]->fqan[j]);
+                   }
+          }
+     }
+   else
+     {
+       FILE *fp = fopen("/tmp/getvoms.log", "w");
+       fprintf(fp, "%d\n", vomserror);
+       fclose(fp);
+     }
+   
+   VOMS_Destroy(vd);   
+   return GRST_RET_OK;
+}
+
+#if 0
 
    time(&now);
 
                    ++p;
                  }
              }
-             
-            if (
-                (now >= time1_time) &&
+/*             
+            if ((now >= time1_time) &&
                 (now <= time2_time) &&
                 (signature != NULL) &&
                 (data != NULL) &&
                 (GRSTx509CheckVomsSig(signature, siglen, 
                                     &((ASN1_STRING_data(asn1str))[dataoffset]),
                                     datalength, vomsdir, vo, 
-                                    server) == GRST_RET_OK)) while (1)
+                                    server) == GRST_RET_OK)) 
+                                    while (1)
+*/
              {
                if (strncmp(p, "GROUP:", sizeof("GROUP:") - 1) == 0)
                  {
 
    return GRST_RET_OK;
 }
+#endif
+
+#endif
 
 /// Turn a Compact Cred line into a GRSTgaclCred object
 /**
        strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
        
        GRSTx509GetVomsCreds(lastcred, maxcreds, credlen, creds, 
-                            gsiproxycert, usercert, vomsdir);
+                            gsiproxycert, certstack, vomsdir);
      }
          
    return GRST_RET_OK;
 }
+
+/// Find proxy file name of the current user
+/**
+ *  Return a string with the proxy file name or NULL if not present.
+ *  This function does not check if the proxy has expired.
+ */
+ 
+char *GRSTx509FindProxyFileName(void)
+{
+  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
+/**
+ *  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.
+ */
+
+int GRSTx509MakeProxyCert(char **proxychain, FILE *debugfp, 
+                           char *reqtxt, char *cert, char *key, int minutes)
+{
+  char *ptr, *certchain;
+  int i, subjAltName_pos, ncerts;
+  long serial = 1, 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;
+  X509V3_CTX ctx;
+  X509_EXTENSION *subjAltName;
+  STACK_OF (X509_EXTENSION) * req_exts;
+  FILE *fp;
+  BIO *reqmem, *certmem;
+
+  /* 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");
+      return GRST_RET_FAILED;
+    }
+
+  if (X509_REQ_verify(req, pkey) != 1)
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error verifying signature on certificate\n");
+      return GRST_RET_FAILED;
+    }
+    
+  /* read in the signing certificate */
+  if (!(fp = fopen(cert, "r")))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error opening signing certificate file\n");
+      return GRST_RET_FAILED;
+    }    
+
+  for (ncerts = 1; ncerts < GRST_MAX_CHAIN_LEN; ++ncerts)
+   if (!(certs[ncerts] = PEM_read_X509(fp, NULL, NULL, NULL))) break;
+
+  if (ncerts == 1) /* zeroth cert with be new proxy cert */
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error reading signing certificate file\n");
+      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");
+      return GRST_RET_FAILED;
+    }    
+
+  if (!(CApkey = PEM_read_PrivateKey (fp, NULL, NULL, NULL)))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error reading signing private key in file\n");
+      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");
+      return GRST_RET_FAILED;
+    }    
+
+  /* create new certificate */
+  if (!(certs[0] = X509_new ()))
+    {
+      mpcerror(debugfp,
+            "GRSTx509MakeProxyCert(): error creating X509 object\n");
+      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");
+      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");
+      return GRST_RET_FAILED;
+    }    
+
+  if (X509_set_issuer_name (certs[0], name) != 1)
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting issuer name of certificate\n");
+      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, "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");
+      return GRST_RET_FAILED;
+    }    
+
+  /* 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");
+      return GRST_RET_FAILED;
+    }    
+
+// need to set validity within limits of earlier certificates in the chain
+
+  /* set duration for the certificate */
+  if (!(X509_gmtime_adj (X509_get_notBefore(certs[0]), 0)))
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error setting beginning time of the certificate\n");
+      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");
+      return GRST_RET_FAILED;
+    }    
+
+  /* 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");
+      return GRST_RET_FAILED;
+    }    
+
+  if (!(X509_sign (certs[0], CApkey, digest)))
+    {
+      mpcerror(debugfp,
+      "GRSTx509MakeProxyCert(): error signing certificate\n");
+      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");            
+           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);
+     }
+    
+  *proxychain = certchain;
+    
+  return GRST_RET_OK;
+}
+
+/// Find a proxy file in the proxy cache
+/**
+ *  Returns the full path and file name of proxy file associated
+ *  with given delegation ID and user DN.
+ */
+
+char *GRSTx509CachedProxyFind(char *proxydir, char *delegation_id, 
+                              char *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.
+*/
+{
+  int   ret, len;
+  char *filename = NULL, *line, *p, *proxyfile = NULL;
+  DIR *proxyDIR;
+  FILE *fp;  
+  struct dirent *ent;
+  struct stat entstat;
+     
+  if ((proxyDIR = opendir(proxydir)) == NULL) return NULL;
+ 
+  len = strlen(delegation_id);
+  if (strlen(user_dn) > len) len = strlen(user_dn);
+ 
+  if ((line = malloc(len + 2)) == NULL) return NULL;
+ 
+  while ((ent = readdir(proxyDIR)) != NULL)
+       {
+         if (ent->d_name[0] != '.') /* private keys begin with . */
+           {       
+             if (asprintf(&filename, "%s/%s", proxydir, ent->d_name) == -1)
+                                                                      break;
+             if ((stat(filename, &entstat) != 0) 
+                 || !S_ISREG(entstat.st_mode))
+               {
+                 free(filename);
+                 continue;
+               }
+               
+             fp = fopen(filename, "r");
+             if (fp != NULL)
+               {
+                 if (fgets(line, len + 2, fp) != NULL)
+                   {
+                     p = index(line, '\n');
+                     
+                     if (p != NULL)
+                       {
+                         *p = '\0';
+                         if (strcmp(line, delegation_id) == 0)
+                           {
+                             if (fgets(line, len + 2, fp) != NULL)
+                               {
+                                 p = index(line, '\n');
+
+                                 if (p != NULL)
+                                   {
+                                     *p = '\0';
+                           
+                                     if (strcmp(line, user_dn) == 0)
+                                       {                           
+                                         proxyfile = filename;
+                                         fclose(fp);
+                                         break;
+                                       }
+                                   }
+                               }
+                           }
+                       }
+                   }
+               
+                 fclose(fp);
+               }
+               
+             free(filename);
+           }         
+       }
+  
+  closedir(proxyDIR);
+  free(line);
+ 
+  return proxyfile;
+}
+
+/// Find a temporary proxy private key file in the proxy cache
+/**
+ *  Returns the full path and file name of the private key file associated
+ *  with given delegation ID and user DN.
+ */
+
+char *GRSTx509CachedProxyKeyFind(char *proxydir, char *delegation_id, 
+                                 char *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.
+*/
+{
+  int   ret, len;
+  char *filename = NULL, *line, *p, *keyfile = NULL;
+  DIR *proxyDIR;
+  FILE *fp;  
+  struct dirent *ent;
+  struct stat entstat;
+     
+  if ((proxyDIR = opendir(proxydir)) == NULL) return NULL;
+ 
+  len = strlen(delegation_id);
+  if (strlen(user_dn) > len) len = strlen(user_dn);
+ 
+  if ((line = malloc(len + 2)) == NULL) return NULL;
+ 
+  while ((ent = readdir(proxyDIR)) != NULL)
+       {
+         if (ent->d_name[0] == '.') /* private keys begin with . */
+           {       
+             if (asprintf(&filename, "%s/%s", proxydir, ent->d_name) == -1)
+                                                                      break;             
+             if ((stat(filename, &entstat) != 0) 
+                 || !S_ISREG(entstat.st_mode))
+               {
+                 free(filename);
+                 continue;
+               }
+               
+             fp = fopen(filename, "r");
+             if (fp != NULL)
+               {
+                 if (fgets(line, len + 2, fp) != NULL)
+                   {
+                     p = index(line, '\n');
+                     
+                     if (p != NULL)
+                       {
+                         *p = '\0';
+                         if (strcmp(line, delegation_id) == 0)
+                           {
+                             if (fgets(line, len + 2, fp) != NULL)
+                               {
+                                 p = index(line, '\n');
+
+                                 if (p != NULL)
+                                   {
+                                     *p = '\0';
+                           
+                                     if (strcmp(line, user_dn) == 0)
+                                       {                           
+                                         keyfile = filename;
+                                         fclose(fp);
+                                         break;
+                                       }
+                                   }
+                               }
+                           }
+                       }
+                   }
+               
+                 fclose(fp);
+               }
+               
+             free(filename);
+           }         
+       }
+  
+  closedir(proxyDIR);
+  free(line);
+ 
+  return keyfile;
+}
+
+/// Make and store a X.509 request for a GSI proxy
+/**
+ *  Returns GRST_RET_OK on success, non-zero otherwise. Request string
+ *  is PEM encoded, and the key is stored in proxydir as temporary file
+ *  with a filename like .XXXXXX
+ */ 
+
+int GRSTx509MakeProxyRequest(char **reqtxt, char *proxydir, 
+                             char *delegation_id, char *user_dn)
+{
+  int              i, fd;
+  char            *docroot, *reqfile, *prvkeyfile, *ptr;
+  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;
+  struct stat      statbuf;
+
+  if ((keypair = RSA_generate_key(GRST_KEYSIZE, 3, NULL, NULL)) == NULL)
+                                                               return 1;
+  asprintf(&prvkeyfile, "%s/.XXXXXX", proxydir);
+          
+  fd = mkstemp(prvkeyfile);
+    
+  if ((fp = fdopen(fd, "w")) == NULL) return 1;
+                               
+  fprintf(fp, "%s\n%s\n", delegation_id, user_dn);
+    
+  if (!PEM_write_RSAPrivateKey(fp, keypair, NULL, NULL, 0, NULL, NULL))
+                               return 1;
+  
+  if (fclose(fp) != 0) return 1;
+  
+  /* now create the certificate request */
+
+  certreq = X509_REQ_new();
+  if (certreq == NULL) return 1;
+
+  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, "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;
+}
+
+/// Store a GSI proxy chain in the proxy cache, along with the private key
+/**
+ *  Returns GRST_RET_OK on success, non-zero otherwise. The existing
+ *  private key with the same delegation ID and user DN is appended to
+ *  make a valid proxy file, and the temporary private key file deleted.
+ */
+
+int GRSTx509CacheProxy(char *proxydir, char *delegation_id, 
+                                       char *user_dn, char *proxychain)
+{
+  int   fd, c, len = 0, i;
+  char *cert, *upcertfile, *prvkeyfile, *p;
+  FILE *ifp, *ofp;
+    
+  prvkeyfile = GRSTx509CachedProxyKeyFind(proxydir, delegation_id, user_dn);
+
+  if (prvkeyfile == NULL)  
+    {
+      free(proxydir);
+      return GRST_RET_FAILED;
+    }
+        
+  if ((ifp = fopen(prvkeyfile, "r")) == NULL) 
+    {
+      free(prvkeyfile);
+      free(proxydir);
+      return GRST_RET_FAILED;
+    }
+
+  if (asprintf(&upcertfile, "%s/XXXXXX", proxydir) == -1) 
+                                                    return GRST_RET_FAILED;
+
+  if ((fd = mkstemp(upcertfile)) == -1)
+    {
+      fclose(ifp);
+      free(prvkeyfile);
+      free(upcertfile);
+      return GRST_RET_FAILED;
+    }
+    
+  if ((ofp = fdopen(fd, "w")) == NULL)
+    {
+      close(fd);
+      fclose(ifp);
+      free(prvkeyfile);
+      free(upcertfile);
+      return GRST_RET_FAILED;
+    }
+
+  fprintf(ofp, "%s\n%s\n", delegation_id, user_dn);
+ 
+  fputs(proxychain, ofp); /* write out certificates */
+  
+  while ((c = fgetc(ifp)) != EOF) fputc(c, ofp); /* append proxy private key */
+      
+  if (fclose(ifp) != 0) return GRST_RET_FAILED;
+  if (fclose(ofp) != 0) return GRST_RET_FAILED;
+  
+  unlink(prvkeyfile);
+  
+  free(prvkeyfile);
+  free(upcertfile);
+  
+/* should also check validity of proxy cert to avoid suprises? */
+      
+  return GRST_RET_OK;
+} 
 
 /*
-   Copyright (c) 2002-3, Andrew McNab, University of Manchester
+   Copyright (c) 2002-4, Andrew McNab, University of Manchester
    All rights reserved.
 
    Redistribution and use in source and binary forms, with or
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*------------------------------------------------------------------------*
- * For more about GridSite: http://www.gridpp.ac.uk/gridsite/             *
- *------------------------------------------------------------------------*/
+/*---------------------------------------------------------------*
+ * For more about GridSite: http://www.gridsite.org/             *
+ *---------------------------------------------------------------*/
 
 #ifndef VERSION
 #define VERSION "0.0.0"
                           char *errorbuf;
                           int   noverify;
                           int   anonymous;
+                          long long downgrade;
                           int   verbose;       } ;
                           
 struct grst_index_blob { char   *text;
                        time_t  modified;
                        int     modified_set; } ; 
 
-struct grst_header_data { int    retcode;
+struct grst_header_data { int    retcode;                         
                           char  *location;
+                          char  *gridauthonetime;
                           size_t length;
                           int    length_set;
                           time_t modified;                           
-                          int    modified_set; } ;
+                          int    modified_set;
+                          struct grst_stream_data *common_data; } ;
 
 size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p)
 /* Find the values of the return code, Content-Length, Last-Modified
    and Location headers */
 {
   float f;
-  char  *s;
+  char  *s, *q;
   size_t realsize;
   struct tm modified_tm;
   struct grst_header_data *header_data;
             header_data->length_set = 1;
   else if (sscanf(s, "HTTP/%f %d ", &f, &(header_data->retcode)) == 2) ;
   else if (strncmp(s, "Location: ", 10) == 0) 
-                                 header_data->location = strdup(&s[10]);
+      {
+        header_data->location = strdup(&s[10]);
+         
+        if (header_data->common_data->verbose > 0)
+             fprintf(stderr, "Received Location: %s\n", header_data->location);
+      }
+  else if (strncmp(s, "Set-Cookie: GRID_AUTH_ONETIME=", 30) == 0) 
+      {
+        header_data->gridauthonetime = strdup(&s[12]);
+        q = index(header_data->gridauthonetime, ';');
+        if (q != NULL) *q = '\0';       
+
+        if (header_data->common_data->verbose > 0)
+             fprintf(stderr, "Received Grid Auth Cookie: %s\n", 
+                             header_data->gridauthonetime);
+      }
   else if (strncmp(s, "Last-Modified: ", 15) == 0)
       {
         /* follow RFC 2616: first try RFC 822 (kosher), then RFC 850 and 
   CURL        *easyhandle;
   struct stat  statbuf;
   struct       grst_header_data header_data;
+  struct curl_slist *dgheader_slist = NULL, *nodgheader_slist = NULL;
   
   easyhandle = curl_easy_init();
   
+  if (common_data->downgrade >= (long long) 0)
+    {               
+      asprintf(&p, "HTTP-Downgrade-Size: %lld", common_data->downgrade);      
+      dgheader_slist = curl_slist_append(dgheader_slist, p);
+      free(p);
+      
+      nodgheader_slist = curl_slist_append(nodgheader_slist,
+                                           "HTTP-Downgrade-Size:");
+    }
+  
   curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent);
   if (common_data->verbose > 1)
                    curl_easy_setopt(easyhandle, CURLOPT_VERBOSE, 1);
 
            curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, common_data->fp);
            curl_easy_setopt(easyhandle, CURLOPT_URL,       sources[isrc]);
+           
+           if ((common_data->downgrade >= (long long) 0) &&
+               (strncmp(sources[isrc], "https://", 8) == 0))
+             {
+               if (common_data->verbose > 0)
+                 fprintf(stderr, "Add  HTTP-Downgrade-Size: %lld  header\n",
+                         common_data->downgrade);
+                 
+               curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,dgheader_slist);
+             }
+           else 
+             curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,nodgheader_slist);
          }
        else if (common_data->method == HTCP_PUT)
          {
            curl_easy_setopt(easyhandle, CURLOPT_URL,        thisdestination);
            curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE, statbuf.st_size);
            curl_easy_setopt(easyhandle, CURLOPT_UPLOAD,   1);
+
+           if (((long long) statbuf.st_size >= common_data->downgrade) &&
+               (strncmp(thisdestination, "https://", 8) == 0))
+               curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,dgheader_slist);
+           else 
+             curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,nodgheader_slist);
          }
 
-       header_data.retcode = 0;
+       header_data.retcode  = 0;
+       header_data.location = NULL;
+       header_data.gridauthonetime = NULL;
+       header_data.common_data = common_data;
        thiserror = curl_easy_perform(easyhandle);
        
+       fclose(common_data->fp);
+
+       if ((common_data->downgrade >= (long long) 0) &&
+           (thiserror == 0) &&
+           (header_data.retcode == 302) &&
+           (header_data.location != NULL) &&
+           (strncmp(header_data.location, "http://", 7) == 0) &&
+           (header_data.gridauthonetime != NULL))
+         {
+           if (common_data->verbose > 0)
+             fprintf(stderr, "... Found (%d)\nHTTP-Downgrade to %s\n", 
+                     header_data.retcode, header_data.location);
+
+           /* try again with new URL and all the previous CURL options */
+
+           if (common_data->method == HTCP_GET)
+             {
+               common_data->fp = fopen(thisdestination, "w");
+               if (common_data->fp == NULL)
+                 {
+                   fprintf(stderr, "... failed to open destination source "
+                                   "file %s\n", thisdestination);
+                   anyerror = 99;
+                   if (isdirdest) free(thisdestination);
+                   continue;
+                 }
+             }
+           else if (common_data->method == HTCP_PUT)
+             {
+               common_data->fp = fopen(sources[isrc], "r");
+               if (common_data->fp == NULL)
+                 {
+                   fprintf(stderr, "... failed to open source file %s\n",
+                               sources[isrc]);
+                   anyerror = 99;
+                   if (isdirdest) free(thisdestination);
+                   continue;
+                 }
+             }
+
+           header_data.retcode  = 0;           
+           curl_easy_setopt(easyhandle, CURLOPT_URL, header_data.location);
+           curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, nodgheader_slist);
+           curl_easy_setopt(easyhandle, CURLOPT_COOKIE, 
+                                                  header_data.gridauthonetime);
+           thiserror = curl_easy_perform(easyhandle);
+
+           fclose(common_data->fp);
+         }
+
        if ((thiserror != 0) ||
            (header_data.retcode <  200) ||
            (header_data.retcode >= 300))
          {
            fprintf(stderr, "... curl error: %s (%d), HTTP error: %d\n",
                    common_data->errorbuf, thiserror, header_data.retcode);
-           
+                   
            if (thiserror != 0) anyerror = thiserror;
            else                anyerror = header_data.retcode;
          }
        else if (common_data->verbose > 0) 
                   fprintf(stderr, "... OK (%d)\n", header_data.retcode);
-
-       fclose(common_data->fp);
         
        if (isdirdest) free(thisdestination);
      }
   CURL  *easyhandle;
   struct grst_header_data header_data;
   
+  header_data.common_data = common_data;
+
   easyhandle = curl_easy_init();
   
   curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent);
   CURL  *easyhandle;
   struct grst_header_data header_data;
   
+  header_data.common_data = common_data;
+
   easyhandle = curl_easy_init();
   
   curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent);
                  {
                    allocated += 256;
                    list = (struct grst_dir_list *)
-                             malloc(allocated * sizeof(struct grst_dir_list));
+                           realloc((void *) list,
+                                   allocated * sizeof(struct grst_dir_list));
                  }
                  
                list[used].filename     = NULL;
 
   time(&now);
 
+  header_data.common_data = common_data;
+
   easyhandle = curl_easy_init();
   
   curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent);
            curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1);
          }
 
+       header_data.gridauthonetime = NULL;
        header_data.length_set   = 0;
        header_data.modified_set = 0;
        header_data.retcode      = 0;
                         asprintf(&s, "%s%s", sources[isrc], list[i].filename);                        
                         curl_easy_setopt(easyhandle, CURLOPT_URL, s);
 
+                        header_data.gridauthonetime = NULL;
                         header_data.length_set   = 0;
                         header_data.modified_set = 0;
                         header_data.retcode = 0;
 "from remote servers using HTTP or HTTPS, or to put or delete files or\n"
 "directories onto remote servers using HTTPS. htcp is similar to scp(1)\n"
 "but uses HTTP/HTTPS rather than ssh as its transfer protocol.\n"
-"See the htcp(1) or http://www.gridpp.ac.uk/gridsite/ for details.\n"
+"See the htcp(1) or http://www.gridsite.org/ for details.\n"
 "(Version: %s)\n", p, p, VERSION);
 }
 
   struct stat statbuf;
   struct grst_stream_data common_data;
   struct passwd *userpasswd;
-  struct option long_options[] = {     {"verbose",     0, 0, 'v'},
-                                       {"cert",        1, 0, 0},
-                                       {"key",         1, 0, 0},
-                                       {"capath",      1, 0, 0},
-                                       {"delete",      0, 0, 0},
-                                       {"list",        0, 0, 0},
-                                       {"long-list",   0, 0, 0},
-                                       {"mkdir",       0, 0, 0},
-                                       {"no-verify",   0, 0, 0},
-                                       {"anon",        0, 0, 0},
-//                                     {"streams",     1, 0, 0},
-//                                     {"blocksize",   1, 0, 0},
-//                                     {"recursive",   0, 0, 0},
+  struct option long_options[] = {     {"verbose",             0, 0, 'v'},
+                                       {"cert",                1, 0, 0},
+                                       {"key",                 1, 0, 0},
+                                       {"capath",              1, 0, 0},
+                                       {"delete",              0, 0, 0},
+                                       {"list",                0, 0, 0},
+                                       {"long-list",           0, 0, 0},
+                                       {"mkdir",               0, 0, 0},
+                                       {"no-verify",           0, 0, 0},
+                                       {"anon",                0, 0, 0},
+                                       {"downgrade-size",      1, 0, 0},
+//                                     {"streams",             1, 0, 0},
+//                                     {"blocksize",           1, 0, 0},
+//                                     {"recursive",           0, 0, 0},
                                        {0, 0, 0, 0}  };
 
 #if (LIBCURL_VERSION_NUM < 0x070908)
   common_data.method    = 0;
   common_data.errorbuf  = malloc(CURL_ERROR_SIZE);
   asprintf(&(common_data.useragent),
-           "htcp/%s (http://www.gridpp.ac.uk/gridsite/)", VERSION);
+                          "htcp/%s (http://www.gridsite.org/)", VERSION);
   common_data.verbose   = 0;
   common_data.noverify  = 0;
   common_data.anonymous = 0;
+  common_data.downgrade = (long long) -1;
   
   while (1)
        {
              else if (option_index == 7) common_data.method    = HTCP_MKDIR;
              else if (option_index == 8) common_data.noverify  = 1;
              else if (option_index == 9) common_data.anonymous = 1;
+             else if (option_index ==10) common_data.downgrade = atoll(optarg);
            }
          else if (c == 'v') ++(common_data.verbose);
        }
 
--- /dev/null
+/*
+   Copyright (c) 2002-4, 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.
+*/
+
+/*
+
+Build with:
+
+gcc -lcurl -lssl -lcrypto -o grst-proxy-put grst-proxy-put.c libgridsite.a
+
+http://www.gridpp.ac.uk/authz/gridsite/
+
+*/
+
+#ifndef VERSION
+#define VERSION "0.0.0"
+#endif
+
+#define _GNU_SOURCE
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#include <getopt.h>
+
+#include "gridsite.h"
+
+#include "soapH.h"
+#include "delegation.nsmap"
+
+#define USE_SOAP               0
+#define USE_G_HTTPS            1
+#define HTPROXY_PUT            0
+
+int debugfunction(CURL *curl, curl_infotype type, char *s, size_t n, void *p)
+{
+  fwrite(s, sizeof(char), n, (FILE *) p);
+
+  return 0;
+}
+
+size_t parsegprheaders(void *ptr, size_t size, size_t nmemb, void *p)
+{
+  int   i;
+  
+  if ((size * nmemb > 15) && 
+      (strncmp((char *) ptr, "Delegation-ID: ", 15) == 0))
+    {
+      *((char **) p) = malloc( size * nmemb - 14 );
+      
+      memcpy(*((char **) p), &(((char *) ptr)[15]), size * nmemb - 15);
+  
+      for (i=0; i < size * nmemb - 15; ++i) 
+        if (((*((char **) p))[i] == '\n') || ((*((char **) p))[i] == '\r'))
+          {
+            (*((char **) p))[i] = '\0'; /* drop trailing newline */
+            break;
+          }
+          
+      (*((char **) p))[size * nmemb - 15] = '\0';
+    }
+    
+  return size * nmemb;
+}
+
+struct gprparams { char *req; size_t len; } ;
+
+size_t storegprbody(void *ptr, size_t size, size_t nmemb, void *p)
+{
+  ((struct gprparams *) p)->req = realloc( ((struct gprparams *) p)->req,
+                          ((struct gprparams *) p)->len + size * nmemb + 1);
+
+  memcpy( &((((struct gprparams *) p)->req)[((struct gprparams *) p)->len]),
+           ptr, size * nmemb);
+         
+  ((struct gprparams *) p)->len += size * nmemb;
+
+  return size * nmemb;
+}
+
+int GRSTgetProxyReq(CURL *curl, FILE *debugfp, char *delegid, char **reqtxt, 
+                    char *requrl, char *cert, char *key)
+{
+  char     *delheader;
+  struct    curl_slist *headerlist = NULL;
+  CURLcode  res;
+  struct    gprparams params;
+
+  params.req = NULL;
+  params.len = 0;
+
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) ¶ms);
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, storegprbody);
+
+  curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,  "PEM");
+  curl_easy_setopt(curl, CURLOPT_SSLCERT,      cert);
+
+  curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE,   "PEM");
+  curl_easy_setopt(curl, CURLOPT_SSLKEY,       key);    
+  curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, NULL);
+
+//  curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parsegprheaders);
+//  curl_easy_setopt(curl, CURLOPT_WRITEHEADER,    (void *) delegid);
+  
+  curl_easy_setopt(curl, CURLOPT_CAPATH, "/etc/grid-security/certificates/");
+
+  curl_easy_setopt(curl, CURLOPT_URL,           requrl);
+  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET-PROXY-REQ");
+
+  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,0);
+  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,0);
+
+  asprintf(&delheader, "Delegation-ID: %s", delegid);
+  headerlist = curl_slist_append(headerlist, delheader);                                                                           
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
+
+  if (debugfp != NULL)
+    {
+      curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);       
+      curl_easy_setopt(curl, CURLOPT_DEBUGDATA,     debugfp);
+      curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debugfunction);
+    }
+       
+  res = curl_easy_perform(curl);
+  
+  if (params.req != NULL)
+    {
+      params.req[params.len] = '\0';
+      *reqtxt = params.req;
+    }
+  else *reqtxt = NULL;
+  
+  return (int) res;
+}
+
+struct ppcparams{ char *cert; size_t len; };
+
+size_t getppcbody(void *ptr, size_t size, size_t nmemb, void *p)
+{
+  size_t i;
+  
+  if (((struct ppcparams *) p)->len == 0) return 0;
+  
+  if (size * nmemb < ((struct ppcparams *) p)->len) i = size * nmemb;
+  else                             i = ((struct ppcparams *) p)->len;
+  
+  memcpy(ptr, ((struct ppcparams *) p)->cert, i);
+  
+  ((struct ppcparams *) p)->len -= i;   
+  ((struct ppcparams *) p)->cert = &((((struct ppcparams *) p)->cert)[i+1]);
+     
+  return i;
+}
+
+int GRSTputProxyCerts(CURL *curl, FILE *debugfp, char *delegid, char *certtxt,
+                      char *requrl, char *cert, char *key)
+{
+  CURLcode    res;
+  char       *delheader;
+  long        httpcode;
+  struct curl_slist *headerlist = NULL;
+  struct ppcparams params;
+
+  params.cert = certtxt;
+  params.len  = strlen(certtxt);
+
+  curl_easy_setopt(curl, CURLOPT_READDATA,     ¶ms);
+  curl_easy_setopt(curl, CURLOPT_READFUNCTION, getppcbody);
+  curl_easy_setopt(curl, CURLOPT_INFILESIZE,   strlen(certtxt));
+  curl_easy_setopt(curl, CURLOPT_UPLOAD,       1);
+
+  curl_easy_setopt(curl, CURLOPT_NOBODY,       1);  
+
+  curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,  "PEM");
+  curl_easy_setopt(curl, CURLOPT_SSLCERT,      cert);
+
+  curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE,   "PEM");
+  curl_easy_setopt(curl, CURLOPT_SSLKEY,       key);    
+//  curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, NULL);
+
+  curl_easy_setopt(curl, CURLOPT_CAPATH, "/etc/grid-security/certificates/");
+
+  curl_easy_setopt(curl, CURLOPT_URL,           requrl);
+  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT-PROXY-CERT");
+
+  headerlist = curl_slist_append(headerlist, 
+                 "Content-Type: application/x-x509-user-cert-chain");
+                                   
+  asprintf(&delheader, "Delegation-ID: %s", delegid);
+  headerlist = curl_slist_append(headerlist, delheader);
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
+  
+curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
+
+  if (debugfp != NULL)
+    {
+      curl_easy_setopt(curl, CURLOPT_VERBOSE,       1);       
+      curl_easy_setopt(curl, CURLOPT_DEBUGDATA,     debugfp);
+      curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debugfunction);
+    } 
+     
+  res = curl_easy_perform(curl);
+
+  curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &httpcode);
+  
+  curl_slist_free_all(headerlist);
+
+  free(delheader);
+  
+  return (int) res;
+}
+
+
+#if (LIBCURL_VERSION_NUM < 0x070908)
+char *make_tmp_ca_roots(char *dir)
+/* libcurl before 7.9.8 doesnt support CURLOPT_CAPATH and the directory,
+   so we make a temporary file with the concatenated CA root certs: that
+   is, all the files in that directory which end in .0 */
+{
+  int    ofd, ifd, c;
+  size_t size;
+  char   tmp_ca_roots[] = "/tmp/.ca-roots-XXXXXX", buffer[4096], *s;
+  DIR   *rootsDIR;
+  struct dirent *root_ent;
+
+  if ((rootsDIR = opendir(dir)) == NULL) return NULL;
+
+  if ((ofd = mkstemp(tmp_ca_roots)) == -1)
+    {
+      closedir(rootsDIR);
+      return NULL;
+    }
+
+  while ((root_ent = readdir(rootsDIR)) != NULL)
+       {
+         if ((root_ent->d_name[0] != '.') &&
+             (strlen(root_ent->d_name) > 2) &&
+             (strncmp(&(root_ent->d_name[strlen(root_ent->d_name)-2]),
+                                                        ".0", 2) == 0))
+           {
+             asprintf(&s, "%s/%s", dir, root_ent->d_name);
+             ifd = open(s, O_RDONLY);
+             free(s);
+
+             if (ifd != -1)
+               {
+                 while ((size = read(ifd, buffer, sizeof(buffer))) > 0)
+                                                 write(ofd, buffer, size);
+
+                 close(ifd);
+               }
+           }
+       }
+
+  closedir(rootsDIR);
+
+  if (close(ofd) == 0) return strdup(tmp_ca_roots);
+
+  unlink(tmp_ca_roots); /* try to clean up if errors */
+
+  return NULL;
+}
+#endif
+  
+void printsyntax(char *argv0)
+{
+  char *p;
+
+  p = rindex(argv0, '/');
+  if (p != NULL) ++p;
+  else           p = argv0;
+
+  fprintf(stderr, "%s [options] URL\n"
+          "(Version: %s)\n", p, VERSION);
+}
+  
+int main(int argc, char *argv[])
+{
+  char  *delegation_id = "", *reqtxt, *certtxt, *valid = NULL, 
+        *cert = NULL, *key = NULL, *capath = NULL, *keycert;
+  struct ns__putProxyResponse *unused;
+  int    option_index, c, protocol = USE_SOAP, noverify = 0, 
+         method = HTPROXY_PUT, verbose = 0, fd, minutes;
+  struct soap soap_get, soap_put;
+  FILE   *ifp, *ofp;
+  struct stat statbuf;
+  struct passwd *userpasswd; 
+  struct option long_options[] = {      {"verbose",     0, 0, 'v'},
+                                        {"cert",        1, 0, 0},
+                                        {"key",         1, 0, 0},
+                                        {"capath",      1, 0, 0},
+                                        {"soap",        0, 0, 0},
+                                        {"g-https",     0, 0, 0},
+                                        {"no-verify",   0, 0, 0},
+                                        {"valid",       1, 0, 0},
+                                        {"delegation-id",1, 0, 0},
+                                        {"put",         0, 0, 0},
+                                        {0, 0, 0, 0}  };
+  CURL *curl;
+
+  if (argc == 1)
+    {
+      printsyntax(argv[0]);
+      return 0;
+    }
+
+  while (1)
+       {
+         option_index = 0;
+                                                                                
+         c = getopt_long(argc, argv, "v", long_options, &option_index);
+
+         if      (c == -1) break;
+         else if (c == 0)
+           {
+             if      (option_index == 1) cert      = optarg;
+             else if (option_index == 2) key       = optarg;
+             else if (option_index == 3) capath    = optarg;
+             else if (option_index == 4) protocol  = USE_SOAP;
+             else if (option_index == 5) protocol  = USE_G_HTTPS;
+             else if (option_index == 6) noverify  = 1;
+             else if (option_index == 7) valid     = optarg;
+             else if (option_index == 8) delegation_id = optarg;
+             else if (option_index == 9) method    = HTPROXY_PUT;
+           }
+         else if (c == 'v') ++verbose;
+       }
+
+  if (optind + 1 != argc)
+    {
+      fprintf(stderr, "Must specify a target URL!\n");
+      return 1;
+    }
+    
+  if (valid == NULL) minutes = 60 * 12;
+  else minutes = atoi(valid);
+  
+  if (verbose) fprintf(stderr, "Proxy valid for %d minutes\n", minutes);
+ 
+  ERR_load_crypto_strings ();
+  OpenSSL_add_all_algorithms();
+
+  if      ((cert == NULL) && (key != NULL)) cert = key;
+  else if ((cert != NULL) && (key == NULL)) key = cert;
+  else if ((cert == NULL) && (key == NULL))
+    {
+      cert = getenv("X509_USER_PROXY");
+      if (cert != NULL) key = cert;
+      else
+        {
+          asprintf(&(cert), "/tmp/x509up_u%d", geteuid());
+                                                                                
+          /* one fine day, we will check the proxy file for 
+             expiry too to avoid suprises when we try to use it ... */
+
+          if (stat(cert, &statbuf) == 0) key = cert;
+          else
+            {
+              cert = getenv("X509_USER_CERT");
+              key  = getenv("X509_USER_KEY");
+                                                                                
+              userpasswd = getpwuid(geteuid());
+                                                                                
+              if ((cert == NULL) &&
+                  (userpasswd != NULL) &&
+                  (userpasswd->pw_dir != NULL))
+                asprintf(&(cert), "%s/.globus/usercert.pem",
+                                                    userpasswd->pw_dir);
+                                                                                
+              if ((key == NULL) &&
+                  (userpasswd != NULL) &&
+                  (userpasswd->pw_dir != NULL))
+                asprintf(&(key), "%s/.globus/userkey.pem",
+                                                    userpasswd->pw_dir);
+                                                                                
+            }
+        }
+    }
+                                                                                
+  if (capath == NULL) capath = getenv("X509_CERT_DIR");
+  if (capath == NULL) capath = "/etc/grid-security/certificates";
+
+  if (verbose) fprintf(stderr, "key=%s\ncert=%s\ncapath=%s\n",
+                       key, cert, capath);
+
+#if (LIBCURL_VERSION_NUM < 0x070908)
+  /* libcurl before 7.9.8 doesnt support CURLOPT_CAPATH and the directory */
+
+  if ((capath != NULL) &&
+      (stat(capath, &statbuf) == 0) && S_ISDIR(statbuf.st_mode))
+    {
+      tmp_ca_roots = make_tmp_ca_roots(capath);
+      capath = tmp_ca_roots;
+    }
+#endif
+
+  if (protocol == USE_G_HTTPS)
+    {
+      if (verbose) fprintf(stderr, "Using G-HTTPS delegation protocol\n");
+
+      if (verbose) fprintf(stderr, "Delegation-ID: %s\n", delegation_id);
+
+      curl_global_init(CURL_GLOBAL_DEFAULT);   
+      curl = curl_easy_init();
+      
+//  curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, NULL);
+
+      GRSTgetProxyReq(curl, stderr, delegation_id, &reqtxt, 
+                      argv[optind], cert, key);
+  
+      if (GRSTx509MakeProxyCert(&certtxt, stderr, reqtxt, cert, key, minutes)
+          != GRST_RET_OK)     
+        {
+          return 1;
+        }
+
+      GRSTputProxyCerts(curl, stderr, delegation_id, certtxt, 
+                        argv[optind], cert, key);
+
+      curl_easy_cleanup(curl);  
+      curl_global_cleanup();
+  
+      return 0;
+    }
+  else if (protocol == USE_SOAP)
+    {
+      if (strcmp(key, cert) != 0) /* we have to concatenate for gSOAP */
+        {
+          keycert = strdup("/tmp/XXXXXX");
+        
+          fd = mkstemp(keycert);          
+          ofp = fdopen(fd, "w");
+          
+          ifp = fopen(key, "r");          
+          while ((c = fgetc(ifp)) != EOF) fputc(c, ofp);          
+          fclose(ifp);
+          
+          ifp = fopen(cert, "r");          
+          while ((c = fgetc(ifp)) != EOF) fputc(c, ofp);          
+          fclose(ifp);
+          
+          fclose(ofp);       
+          
+          if (verbose) fprintf(stderr, "Created %s key/cert file\n", keycert);            
+        }
+      else keycert = key;
+
+      if (verbose) 
+        {
+          fprintf(stderr, "Using SOAP delegation protocol\n");
+          fprintf(stderr, "Delegation-ID: %s\n", delegation_id);
+          fprintf(stderr, "Send getProxyReq to service\n");
+        }
+
+      soap_init(&soap_get);
+  
+      if (soap_ssl_client_context(&soap_get,
+                                  SOAP_SSL_DEFAULT,
+                                  keycert, 
+                                  "",
+                                  NULL,
+                                  capath,
+                                  NULL))
+        {
+          soap_print_fault(&soap_get, stderr);
+          return 1;
+        } 
+
+      soap_call_ns__getProxyReq(&soap_get, 
+                                argv[optind],  /* HTTPS url of service */
+                                "",            /* no password on proxy */
+                                delegation_id, 
+                                &reqtxt);
+      
+      if (soap_get.error)
+        {
+          soap_print_fault(&soap_get, stderr);
+          return 1;        
+        }
+        
+      if (verbose) fprintf(stderr, "reqtxt:\n%s", reqtxt);
+      
+      if (GRSTx509MakeProxyCert(&certtxt, stderr, reqtxt, cert, key, minutes) 
+          != GRST_RET_OK)
+        {
+          return 1;
+        }
+
+      soap_init(&soap_put);
+  
+      if (verbose) fprintf(stderr, "Send putProxy to service:\n%s\n", certtxt);
+
+      if (soap_ssl_client_context(&soap_put,
+                                  SOAP_SSL_DEFAULT,
+                                  keycert, 
+                                  "",
+                                  NULL,
+                                  capath,
+                                  NULL))
+        {
+          soap_print_fault(&soap_put, stderr);
+          return 1;
+        } 
+
+      soap_call_ns__putProxy(&soap_put, argv[optind], "", delegation_id, 
+                             certtxt, unused);      
+      if (soap_put.error)
+        {
+          soap_print_fault(&soap_put, stderr);
+          return 1;        
+        }
+
+      return 0;
+    }  
+
+  /* weirdness */
+}
+
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*---------------------------------------------------------------------------*
- * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/       *
- *---------------------------------------------------------------------------*/
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
 
 #ifndef VERSION
 #define VERSION "x.x.x"
 #include <sys/stat.h>
 #include <fcntl.h>              
 #include <malloc.h>
+#include <stdlib.h>
 #include <string.h>
 #include <dirent.h>
-
+#include <ctype.h>
 #include <time.h>
 
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+
 #include "mod_ssl-private.h"
 
 #include "gridsite.h"
    char *editable;
    char *headfile;
    char *footfile;
+   int   downgrade;
+   char *authcookiesdir;
+   int   soap2cgi;
 }  mod_gridsite_cfg; /* per-directory config choices */
 
+
+typedef struct
+{
+  xmlDocPtr doc;
+//  char *outbuffer;
+} soap2cgi_ctx; /* store per-request context for Soap2cgi in/out filters */
+
+static const char Soap2cgiFilterName[]="Soap2cgiFilter";
+
+static void mod_gridsite_soap2cgi_insert(request_rec *r)
+{
+    mod_gridsite_cfg *conf;
+    soap2cgi_ctx     *ctx;
+    
+    conf = (mod_gridsite_cfg *) ap_get_module_config(r->per_dir_config,
+                                                      &gridsite_module);
+                                                      
+    if (conf->soap2cgi) 
+      {
+        ctx = (soap2cgi_ctx *) malloc(sizeof(soap2cgi_ctx));        
+        ctx->doc = NULL;
+        
+        ap_add_output_filter(Soap2cgiFilterName, ctx, r, r->connection);
+
+        ap_add_input_filter(Soap2cgiFilterName, NULL, r, r->connection);
+      }
+}
+
+xmlNodePtr find_one_child(xmlNodePtr parent_node, char *name)
+{
+    xmlNodePtr cur;
+
+    for (cur = parent_node->children; cur != NULL; cur = cur->next)
+       {
+         if ((cur->type == XML_ELEMENT_NODE) &&
+             (strcmp(cur->name, name) == 0)) return cur;
+       }
+
+    return NULL;
+}
+
+int add_one_node(xmlDocPtr doc, char *line)
+{
+    char *p, *name, *aftername, *attrname = NULL, *value = NULL;
+    xmlNodePtr cur, cur_child;
+
+    cur = xmlDocGetRootElement(doc);
+
+    p = index(line, '=');
+    if (p == NULL) return 1;
+
+    *p = '\0';
+    value = &p[1];
+
+    name = line;
+
+    while (1) /* go through each .-deliminated segment of line[] */
+         {
+           if ((p = index(name, '.')) != NULL)
+             {
+               *p = '\0';
+               aftername = &p[1];
+             }
+           else aftername = &name[strlen(name)];
+
+           if ((p = index(name, '_')) != NULL)
+             {
+               *p = '\0';
+               attrname = &p[1];
+             }
+
+           cur_child = find_one_child(cur, name);
+
+           if (cur_child == NULL)
+                    cur_child = xmlNewChild(cur, NULL, name, NULL);
+
+           cur = cur_child;
+
+           name = aftername;
+
+           if (attrname != NULL)
+             {
+               xmlSetProp(cur, attrname, value);
+               return 0;
+             }
+
+           if (*name == '\0')
+             {
+               xmlNodeSetContent(cur, value);
+               return 0;
+             }             
+         }
+}
+
+static apr_status_t mod_gridsite_soap2cgi_out(ap_filter_t *f,
+                                              apr_bucket_brigade *bbIn)
+{
+    char        *p, *name, *outbuffer;
+    request_rec *r = f->r;
+    conn_rec    *c = r->connection;
+    apr_bucket         *bucketIn, *pbktEOS;
+    apr_bucket_brigade *bbOut;
+
+    const char *data;
+    apr_size_t len;
+    char *buf;
+    apr_size_t n;
+    apr_bucket *pbktOut;
+
+    soap2cgi_ctx *ctx;
+    xmlNodePtr   root_node = NULL;
+    xmlBufferPtr buff;
+
+    ctx = (soap2cgi_ctx *) f->ctx;
+
+// LIBXML_TEST_VERSION;
+
+    bbOut = apr_brigade_create(r->pool, c->bucket_alloc);
+
+    if (ctx->doc == NULL)
+      {
+        ctx->doc = xmlNewDoc("1.0");
+             
+        root_node = xmlNewNode(NULL, "Envelope");
+        xmlDocSetRootElement(ctx->doc, root_node);
+                                                                                
+        xmlNewChild(root_node, NULL, "Header", NULL);
+        xmlNewChild(root_node, NULL, "Body",   NULL);
+      }
+    
+    apr_brigade_pflatten(bbIn, &outbuffer, &len, r->pool);
+       
+    /* split up buffer and feed each line to add_one_node() */
+    
+    name = outbuffer;
+    
+    while (*name != '\0')
+         {
+           p = index(name, '\n');
+           if (p != NULL) 
+             {
+               *p = '\0';
+               ++p;             
+             }
+           else p = &name[strlen(name)]; /* point to final NUL */
+           
+           add_one_node(ctx->doc, name);
+           
+           name = p;
+         }
+
+    APR_BRIGADE_FOREACH(bucketIn, bbIn)
+       {
+         if (APR_BUCKET_IS_EOS(bucketIn))
+           {
+             /* write out XML tree we have built */
+
+             buff = xmlBufferCreate();
+             xmlNodeDump(buff, ctx->doc, root_node, 0, 0);
+
+// TODO: simplify/reduce number of copies or libxml vs APR buffers?
+
+             buf = (char *) xmlBufferContent(buff);
+
+             pbktOut = apr_bucket_heap_create(buf, strlen(buf), NULL, 
+                                              c->bucket_alloc);
+
+             APR_BRIGADE_INSERT_TAIL(bbOut, pbktOut);
+       
+             xmlBufferFree(buff);
+
+             pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
+             APR_BRIGADE_INSERT_TAIL(bbOut, pbktEOS);
+
+             continue;
+           }
+       }
+       
+    return ap_pass_brigade(f->next, bbOut);
+}
+
+static apr_status_t mod_gridsite_soap2cgi_in(ap_filter_t *f,
+                                             apr_bucket_brigade *pbbOut,
+                                             ap_input_mode_t eMode,
+                                             apr_read_type_e eBlock,
+                                             apr_off_t nBytes)
+{
+    request_rec *r = f->r;
+    conn_rec *c = r->connection;
+//    CaseFilterInContext *pCtx;
+    apr_status_t ret;
+
+#ifdef NEVERDEFINED
+
+    ret = ap_get_brigade(f->next, pCtx->pbbTmp, eMode, eBlock, nBytes);    
+ 
+    if (!(pCtx = f->ctx)) {
+        f->ctx = pCtx = apr_palloc(r->pool, sizeof *pCtx);
+        pCtx->pbbTmp = apr_brigade_create(r->pool, c->bucket_alloc);
+    }
+ 
+    if (APR_BRIGADE_EMPTY(pCtx->pbbTmp)) {
+        ret = ap_get_brigade(f->next, pCtx->pbbTmp, eMode, eBlock, nBytes);
+ 
+        if (eMode == AP_MODE_EATCRLF || ret != APR_SUCCESS)
+            return ret;
+    }
+ 
+    while(!APR_BRIGADE_EMPTY(pCtx->pbbTmp)) {
+        apr_bucket *pbktIn = APR_BRIGADE_FIRST(pCtx->pbbTmp);
+        apr_bucket *pbktOut;
+        const char *data;
+        apr_size_t len;
+        char *buf;
+        int n;
+ 
+        /* It is tempting to do this...
+         * APR_BUCKET_REMOVE(pB);
+         * APR_BRIGADE_INSERT_TAIL(pbbOut,pB);
+         * and change the case of the bucket data, but that would be wrong
+         * for a file or socket buffer, for example...
+         */
+                                                                                
+        if(APR_BUCKET_IS_EOS(pbktIn)) {
+            APR_BUCKET_REMOVE(pbktIn);
+            APR_BRIGADE_INSERT_TAIL(pbbOut, pbktIn);
+            break;
+        }
+                                                                                
+        ret=apr_bucket_read(pbktIn, &data, &len, eBlock);
+        if(ret != APR_SUCCESS)
+            return ret;
+                                                                                
+        buf = malloc(len);
+        for(n=0 ; n < len ; ++n)
+            buf[n] = apr_toupper(data[n]);
+                                                                                
+        pbktOut = apr_bucket_heap_create(buf, len, 0, c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
+        apr_bucket_delete(pbktIn);
+    }
+#endif
+                                                                                
+    return APR_SUCCESS;
+}
+
 char *make_admin_footer(request_rec *r, mod_gridsite_cfg *conf,
                         int isdirectory)
 /*
     if (conf->gridsitelink)
       {
         temp = apr_psprintf(r->pool,
-           ". Built with <a href=\"http://www.gridpp.ac.uk/gridsite/\">"
+           ". Built with <a href=\"http://www.gridsite.org/\">"
            "GridSite</a> %s\n", VERSION);
         out = apr_pstrcat(r->pool, out, temp, NULL);
       }
                               
                if (S_ISDIR(statbuf.st_mode))
                     temp = apr_psprintf(r->pool, 
-                      "<tr><td><a href=\"%s/\" "
-                      "last-modified=\"%ld\">%s/</a></td>"
+                      "<tr><td><a href=\"%s/\" content-length=\"%ld\" "
+                      "last-modified=\"%ld\">"
+                      "%s/</a></td>"
                       "<td align=right>%ld</td>%s</tr>\n", 
-                      namelist[n]->d_name, statbuf.st_mtime,
+                      namelist[n]->d_name, statbuf.st_size, statbuf.st_mtime,
                       namelist[n]->d_name, 
                       statbuf.st_size, modified);
                else temp = apr_psprintf(r->pool, 
     return OK;
 }
 
+int http_downgrade(request_rec *r, mod_gridsite_cfg *conf)
+{ 
+    int          i;
+    char        *httpurl, *filetemplate, *cookievalue, *envname_i, 
+                *grst_cred_i, expires_str[APR_RFC822_DATE_LEN];
+    apr_uint64_t gridauthcookie;
+    apr_table_t *env;
+    apr_time_t   expires_time;
+    apr_file_t  *fp;
+
+    /* create random cookie and gridauthcookie file */
+
+    if (apr_generate_random_bytes((char *) &gridauthcookie, 
+                                  sizeof(gridauthcookie))
+         != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR;
+    
+    filetemplate = apr_psprintf(r->pool, "%s/%016llxXXXXXX", 
+                                ap_server_root_relative(r->pool,
+                                                        conf->authcookiesdir),
+                                gridauthcookie);
+                                    
+    if (apr_file_mktemp(&fp, 
+                        filetemplate, 
+                        APR_CREATE | APR_WRITE | APR_EXCL,
+                        r->pool)
+                      != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR;
+
+    expires_time = apr_time_now() + apr_time_from_sec(300); 
+    /* onetime cookies are valid for only 5 mins! */
+
+    apr_file_printf(fp, "expires=%lu\ndomain=%s\npath=%s\nonetime=yes\n", 
+                   (time_t) apr_time_sec(expires_time), r->hostname, r->uri);
+
+    for (i=0; ; ++i)
+       {
+         envname_i = apr_psprintf(r->pool, "GRST_CRED_%d", i);
+         if (grst_cred_i = (char *)
+                           apr_table_get(r->connection->notes, envname_i))
+           {
+             apr_file_printf(fp, "%s=%s\n", envname_i, grst_cred_i);
+           }
+         else break; /* GRST_CRED_i are numbered consecutively */
+       }
+
+    if (apr_file_close(fp) != APR_SUCCESS) 
+      {
+        apr_file_remove(filetemplate, r->pool); /* try to clean up */
+        return HTTP_INTERNAL_SERVER_ERROR;
+      }
+    
+    /* send redirection header back to client */
+       
+    cookievalue = rindex(filetemplate, '/');
+    if (cookievalue != NULL) ++cookievalue;
+    else cookievalue = filetemplate;
+       
+    apr_rfc822_date(expires_str, expires_time);
+
+    apr_table_add(r->headers_out, 
+                  apr_pstrdup(r->pool, "Set-Cookie"), 
+                  apr_psprintf(r->pool, 
+                  "GRID_AUTH_ONETIME=%s; "
+                  "expires=%s; "
+                  "domain=%s; "
+                  "path=%s",
+                  cookievalue, expires_str, r->hostname, r->uri));
+    
+    httpurl = apr_pstrcat(r->pool, "http://", r->hostname,
+                        ap_escape_uri(r->pool, r->uri), NULL);
+    apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), httpurl);
+
+    r->status = HTTP_MOVED_TEMPORARILY;  
+    return OK;
+}
+
 int http_put_method(request_rec *r, mod_gridsite_cfg *conf)
 {
   char        buf[2048];
   if (apr_file_open(&fp, r->filename, APR_WRITE | APR_CREATE | APR_BUFFERED,
       APR_UREAD | APR_UWRITE, r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR;
    
-// need to add Range: support at some point too
+// TODO: need to add Range: support at some point too
 
   retcode = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
   if (retcode == OK)
    and GET inside ghost directories.
 */
 {
-    /* *** is this a write method? only possible if  GridSiteAuth on *** */
+    char      *downgradesize;
+    apr_off_t  numericsize;
+
+    /* *** is this a write method or HTTP downgrade? 
+           only possible if  GridSiteAuth on *** */
     
     if (conf->auth)
       {
+        if ((conf->downgrade) &&
+            ((downgradesize = (char *) apr_table_get(r->headers_in, 
+                                       "HTTP-Downgrade-Size")) != NULL) &&
+            ((numericsize = (apr_off_t) atoll(downgradesize)) >= 0) &&
+
+// TODO: what if we're pointing at a CGI or some dynamic content???
+            (((r->method_number == M_GET) && (r->finfo.size >= numericsize))
+             || (r->method_number == M_PUT)) &&
+
+            (strcasecmp(apr_table_get(r->subprocess_env, "HTTPS"), "on") == 0))
+            return http_downgrade(r, conf);
+      
         if ((r->method_number == M_PUT) && 
             (conf->methods != NULL) &&
             (strstr(conf->methods, " PUT "   ) != NULL))
         conf->headfile = apr_pstrdup(p, GRST_HEADFILE);
         conf->footfile = apr_pstrdup(p, GRST_FOOTFILE); 
                /* GridSiteHeadFile and GridSiteFootFile  file name */
+
+        conf->downgrade     = 0;     /* GridSiteDowngrade     on/off       */
+        conf->authcookiesdir = apr_pstrdup(p, "gridauthcookies");
+                                     /* GridSiteAuthCookiesDir dir-path     */
+        conf->soap2cgi      = 0;     /* GridSiteSoap2cgi      on/off       */
       }
     else
       {
         conf->unzip         = NULL;  /* GridSiteUnzip         file-path    */
         conf->methods       = NULL;  /* GridSiteMethods       methods      */
         conf->editable      = NULL;  /* GridSiteEditable      types        */
-        conf->headfile      = NULL;  /* GridSiteHeadFile      file name */
-        conf->footfile      = NULL;  /* GridSiteFootFile      file name */
+        conf->headfile      = NULL;  /* GridSiteHeadFile      file name    */
+        conf->footfile      = NULL;  /* GridSiteFootFile      file name    */
+        conf->downgrade     = UNSET; /* GridSiteDowngrade     on/off       */
+        conf->authcookiesdir= NULL;  /* GridSiteAuthCookiesDir dir-path    */
+        conf->soap2cgi      = UNSET; /* GridSiteSoap2cgi      on/off       */
       }
       
     return conf;
     if (direct->footfile != NULL) conf->footfile = direct->footfile;
     else                          conf->footfile = server->footfile;
         
+    if (direct->downgrade != UNSET) conf->downgrade = direct->downgrade;
+    else                            conf->downgrade = server->downgrade;
+        
+    if (direct->authcookiesdir != NULL)
+                                conf->authcookiesdir = direct->authcookiesdir;
+    else                        conf->authcookiesdir = server->authcookiesdir;
+        
+    if (direct->soap2cgi != UNSET) conf->soap2cgi = direct->soap2cgi;
+    else                           conf->soap2cgi = server->soap2cgi;
+        
     return conf;
 }
 
       ((mod_gridsite_cfg *) cfg)->indexheader =
         apr_pstrdup(a->pool, parm);
     }
+    else if (strcasecmp(a->cmd->name, "GridSiteAuthCookiesDir") == 0)
+    {
+      if (index(parm, '/') != NULL) 
+           return "/ not permitted in GridSiteAuthCookiesDir";
+
+      ((mod_gridsite_cfg *) cfg)->authcookiesdir =
+        apr_pstrdup(a->pool, parm);
+    }
         
     return NULL;
 }
     {
       ((mod_gridsite_cfg *) cfg)->gridsitelink = flag;
     }
+    else if (strcasecmp(a->cmd->name, "GridSiteDowngrade") == 0)
+    {
+// TODO: return error if try this on non-HTTPS virtual server
+
+      ((mod_gridsite_cfg *) cfg)->downgrade = flag;
+    }
+    else if (strcasecmp(a->cmd->name, "GridSiteSoap2cgi") == 0)
+    {
+      ((mod_gridsite_cfg *) cfg)->soap2cgi = flag;
+    }
 
     return NULL;
 }
 
 static const command_rec mod_gridsite_cmds[] =
 {
+// TODO: need to check and document valid contexts for each command!
+
     AP_INIT_FLAG("GridSiteAuth", mod_gridsite_flag_cmds, 
                  NULL, OR_FILEINFO, "on or off"),
     AP_INIT_FLAG("GridSiteEnvs", mod_gridsite_flag_cmds, 
     AP_INIT_TAKE1("GridSiteIndexHeader", mod_gridsite_take1_cmds,
                    NULL, OR_FILEINFO, "filename of directory header"),
     
+    AP_INIT_FLAG("GridSiteDowngrade", mod_gridsite_flag_cmds, 
+                 NULL, OR_FILEINFO, "on or off"),
+    AP_INIT_TAKE1("GridSiteAuthCookiesDir", mod_gridsite_take1_cmds,
+                 NULL, OR_FILEINFO, "directory with Grid Auth Cookies"),
+
+    AP_INIT_FLAG("GridSiteSoap2cgi", mod_gridsite_flag_cmds,
+                 NULL, OR_FILEINFO, "on or off"),
     {NULL}
 };
 
     conf = (mod_gridsite_cfg *)
                     ap_get_module_config(r->per_dir_config, &gridsite_module);
 
+    /* we handle DN Lists as regular files, even if they also match
+       directory names  */
+
     if ((conf != NULL) &&
         (conf->dnlistsuri != NULL) &&
         (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0) &&
 {
     int          retcode = DECLINED, i, n;
     char        *dn, *p, envname[14], *grst_cred_0 = NULL, *dir_path, 
-                *remotehost, s[99], *grst_cred_i, *file;
+                *remotehost, s[99], *grst_cred_i, *file, *cookies,
+                *gridauthonetime, *cookiefile, oneline[1025], *key_i;
     const char  *content_type;
     time_t       now, notbefore, notafter;
     apr_table_t *env;
+    apr_finfo_t  cookiefile_info;
+    apr_file_t  *fp;
     GRSTgaclCred    *cred = NULL, *cred_0 = NULL;
     GRSTgaclUser    *user = NULL;
     GRSTgaclPerm     perm = GRST_PERM_NONE;
     if ((cfg->auth == 0) &&
         (cfg->envs == 0))
                return DECLINED; /* if not turned on, look invisible */
-    
+
     env = r->subprocess_env;
 
-    if (r->connection->notes != NULL)   
-     grst_cred_0 = (char *) apr_table_get(r->connection->notes, "GRST_CRED_0");
+    if ((p = (char *) apr_table_get(r->headers_in, "Cookie")) != NULL)
+      {
+        cookies = apr_pstrcat(r->pool, " ", p, NULL);
+        gridauthonetime = strstr(cookies, " GRID_AUTH_ONETIME=");
+                
+        if (gridauthonetime != NULL)
+          {
+            for (p = &gridauthonetime[19]; (*p != '\0') && (*p != ';'); ++p)
+                                                if (!isalnum(*p)) *p = '_';
+        
+            cookiefile = apr_psprintf(r->pool, "%s/%s",
+                                      ap_server_root_relative(r->pool, 
+                                                   cfg->authcookiesdir),
+                                      &gridauthonetime[19]);
+                                      
+            if ((apr_stat(&cookiefile_info , cookiefile, 
+                          APR_FINFO_TYPE, r->pool) == APR_SUCCESS) &&
+                (cookiefile_info.filetype == APR_REG) &&
+                (apr_file_open(&fp, cookiefile, APR_READ, 0, r->pool)
+                                                         == APR_SUCCESS))
+              {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "Open Grid Auth Cookie file %s", cookiefile);
+              
+                while (apr_file_gets(oneline, 
+                                     sizeof(oneline), fp) == APR_SUCCESS)
+                     {
+                       p = index(oneline, '\n');
+                       if (p != NULL) *p = '\0';
+                       
+                       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                                    "%s: %s", cookiefile, oneline);
+
+                       if ((strncmp(oneline, "expires=", 8) == 0) &&
+                           (apr_time_from_sec(atoll(&oneline[8])) < 
+                                                       apr_time_now()))
+                                  break;                
+                       else if ((strncmp(oneline, "domain=", 7) == 0) &&
+                                (strcmp(&oneline[7], r->hostname) != 0))
+                                  break; /* exact needed in the version */
+                       else if ((strncmp(oneline, "path=", 5) == 0) &&
+                                (strcmp(&oneline[5], r->uri) != 0))
+                                  break;
+                       else if  (strncmp(oneline, "onetime=yes", 11) == 0)
+                                  apr_file_remove(cookiefile, r->pool);
+                       else if  (strncmp(oneline, "GRST_CRED_", 10) == 0)
+                           {
+                             grst_cred_i = index(oneline, '=');
+                             if (grst_cred_i == NULL) continue;       
+                             *grst_cred_i = '\0';
+                             ++grst_cred_i;
+
+                             i = atoi(&oneline[10]);
+                             cred = GRSTx509CompactToCred(grst_cred_i);
+                             
+                             if (cred == NULL) continue;
+
+                             if ((i == 0) && (user == NULL))
+                               {           
+                                 if (GRSTgaclCredGetDelegation(cred) 
+                                   <= ((mod_gridsite_cfg *) cfg)->gsiproxylimit)
+                                   {
+                                     user = GRSTgaclUserNew(cred);
+                                   
+                                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 
+                                                  0, r->server,
+                                                  "Using identity %s from "
+                                                  "GRID_AUTH_ONETIME", 
+                                                  grst_cred_i);
+
+                                     if (((mod_gridsite_cfg *) cfg)->envs)
+                                      apr_table_setn(env, oneline, grst_cred_i);
+                                   }
+                               }
+                             else if ((i > 0) && (user != NULL))
+                               {
+                                 GRSTgaclUserAddCred(user, cred);
+                                     
+                                 if (((mod_gridsite_cfg *) cfg)->envs)
+                                       apr_table_set(env,oneline,grst_cred_i);
+                               }                             
+                           }
+                     }
+
+                apr_file_close(fp);
+              }            
+          }
+      }
+    
+    /* do we need/have per-connection (SSL) cred variable(s)? */
 
-    if (grst_cred_0 != NULL) /* do we have per-connection cred variable(s)? */
+    if ((user == NULL) && 
+        (r->connection->notes != NULL) &&
+        ((grst_cred_0 = (char *) 
+            apr_table_get(r->connection->notes, "GRST_CRED_0")) != NULL))
       {
         if (((mod_gridsite_cfg *) cfg)->envs)
                             apr_table_setn(env, "GRST_CRED_0", grst_cred_0);
             (GRSTgaclCredGetDelegation(cred_0) 
                          <= ((mod_gridsite_cfg *) cfg)->gsiproxylimit))
           {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "Using identity %s from SSL/TLS", grst_cred_0);
+
             user = GRSTgaclUserNew(cred_0);
 
             /* check for VOMS GRST_CRED_i too */
 
 static void register_hooks(apr_pool_t *p)
 {
+    /* set up the Soap2cgi input and output filters */
+
+    ap_hook_insert_filter(mod_gridsite_soap2cgi_insert, NULL, NULL,
+                          APR_HOOK_MIDDLE);
+
+    ap_register_output_filter(Soap2cgiFilterName, mod_gridsite_soap2cgi_out,
+                              NULL, AP_FTYPE_RESOURCE);
+
+//    ap_register_input_filter(Soap2cgiFilterName, mod_gridsite_soap2cgi_in,
+//                              NULL, AP_FTYPE_RESOURCE);
+
+    /* config and handler stuff */
+
     ap_hook_post_config(mod_gridsite_server_post_config, NULL, NULL, 
                                                               APR_HOOK_LAST);
     ap_hook_child_init(mod_gridsite_child_init, NULL, NULL, APR_HOOK_MIDDLE);
     
     ap_hook_fixups(mod_gridsite_perm_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
     
-    ap_hook_handler(mod_gridsite_handler, NULL, NULL, APR_HOOK_FIRST);
+    ap_hook_handler(mod_gridsite_handler, NULL, NULL, APR_HOOK_FIRST);    
 }
 
 module AP_MODULE_DECLARE_DATA gridsite_module =
 
  * limitations under the License.
  */
 
-/*---------------------------------------------------------------------------*
- * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/       *
- *---------------------------------------------------------------------------*/
+/*------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridsite.org/       *
+ *------------------------------------------------------------------*/
 
 
 /*
 
--- /dev/null
+/*
+   Copyright (c) 2002-4, 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.
+*/
+
+/* 
+    Change the hard-coded defaults below to your set up. 
+*/
+
+#define LOCALPROXY     "/tmp/x509up"
+#define DELEGATIONURL  "https://testing.hep.man.ac.uk/gridsite-delegation.cgi"
+#define CAPATH         "/etc/grid-security/certificates"
+#define DELEGATIONID    "1234567890"
+#define EXPIREMINUTES  60 
+  
+#ifndef VERSION
+#define VERSION "0.0.0"
+#endif
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include "gridsite.h"
+
+#include "soapH.h"
+#include "delegation.nsmap"
+
+int main(int argc, char *argv[])
+{
+  char *reqtxt, *certtxt;
+  struct ns__putProxyResponse *unused;
+  struct soap soap_get, soap_put;
+ 
+  ERR_load_crypto_strings ();
+  OpenSSL_add_all_algorithms();
+
+  soap_init(&soap_get);
+  
+  if (soap_ssl_client_context(&soap_get,
+                                  SOAP_SSL_DEFAULT,
+                                  LOCALPROXY, 
+                                  "",
+                                  NULL,
+                                  CAPATH,
+                                  NULL))
+        {
+          soap_print_fault(&soap_get, stderr);
+          return 1;
+        } 
+
+  soap_call_ns__getProxyReq(&soap_get, 
+                                DELEGATIONURL, /* HTTPS url of service */
+                                "",            /* no password on proxy */
+                                DELEGATIONID, 
+                                &reqtxt);
+      
+  if (soap_get.error)
+    {
+          soap_print_fault(&soap_get, stderr);
+          return 1;        
+    }
+        
+  if (GRSTx509MakeProxyCert(&certtxt, stderr, reqtxt, 
+                            LOCALPROXY, LOCALPROXY, EXPIREMINUTES) 
+          != GRST_RET_OK)
+    {
+          return 1;
+    }
+
+  soap_init(&soap_put);
+  
+  if (soap_ssl_client_context(&soap_put,
+                                  SOAP_SSL_DEFAULT,
+                                  LOCALPROXY, 
+                                  "",
+                                  NULL,
+                                  CAPATH,
+                                  NULL))
+        {
+          soap_print_fault(&soap_put, stderr);
+          return 1;
+        } 
+
+  soap_call_ns__putProxy(&soap_put, DELEGATIONURL, "", DELEGATIONID, 
+                             certtxt, unused);      
+  if (soap_put.error)
+    {
+          soap_print_fault(&soap_put, stderr);
+          return 1;        
+    }
+
+  return 0;
+}
+
 
    POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*------------------------------------------------------------------------*
- * For more about GridSite: http://www.gridpp.ac.uk/gridsite/             *
- *------------------------------------------------------------------------*/
+/*---------------------------------------------------------------*
+ * For more about GridSite: http://www.gridsite.org/             *
+ *---------------------------------------------------------------*/
 
 #include <stdio.h>
 #include <string.h>