--- /dev/null
+* Thu Jul 22 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 1.0.4 ====
+* 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
+* Mon Jul 19 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 1.0.3 ====
+* Mon Jun 28 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- In GRSTx509CheckChain() and GRSTx509CompactCreds()
+ we now accept the first cert in a chain as a CA
+ even if it is X509v3 but without the CA bits set.
+ (On the basis that the first chain is from the
+ administrator-installed CA files store.)
+* Sun Jun 27 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 1.0.2 ====
+* Sun Jun 27 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- Fix for Bug #2860 (so can now read DN Lists over
+ HTTPS when have no user certificate if relevant
+ .gacl gives <read> permission but not <list>)
+- Include gridsite-gacl.h mods from Daniel Kouril
+ <kouril@ics.muni.cz> to fix faulty definitions
+ of GACLnewEntry() and GACLnewAcl() and to make
+ a legacy non-static GACLparseEntry() wrapper.
+* Thu Jun 17 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- Changes to mod_gridsite.h for Fedora Core 2 /
+ Apache 2.0.49+ mod_ssl changes (mod_ssl-private.h)
+* Wed Jun 9 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- Incorporate EGEE CVS layout changes in production
+ branch.
+* Wed Jun 9 2004 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 1.0.1 ====
+* Sun Dec 14 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- 1.0.0 is first full production release
+ (development now in 1.1.x branch)
+* Sun Dec 14 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 1.0.0 ====
+* Sat Dec 13 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Remove need for modified mod_ssl-gridsite: now
+ mod_gridsite intercepts callbacks with wrappers.
+- Add GRSTx509NameCmp() which compares string reps of
+ DNs across OpenSSL version changes (ie Email=)
+* Fri Dec 12 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.11 ====
+* Thu Dec 11 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Simplify checking of cert/proxy chain in
+ mod_ssl-gridsite: rely on mod_ssl/OpenSSL more.
+* Wed Dec 2 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.10 ====
+* Tue Dec 1 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- GACL ignores leading/trailing spaces in values.
+* Sat Nov 29 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Better directory listing in htcp.
+- htcp now built as separate binary RPM.
+- gridsite-admin.cgi upload now redirects to same
+ directory after upload (Bug #1939); allows
+ optional new name for file (Request / Bug #1940);
+ and has better checking of ../dir/file attacks.
+* Sat Nov 29 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.8 ====
+* Thu Nov 27 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Shiv's updated GACL editor, with redirects.
+* Wed Nov 26 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Include Daniel Stenberg's roffit script to make
+ HTML man pages for htcp and urlencode.
+- Various fixes found when installing GridPP WWW.
+* Wed Nov 26 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.7 ====
+* Thu Nov 20 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Major updates to htcp (htrm/htls/htll)
+- GACL now recurses subdirectories when examining
+ the DN List directories path.
+* Sat Nov 15 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.6 ====
+* Fri Nov 14 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Function call fixes in grst-admin.cgi
+* Thu Nov 13 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Add htcp (curl-url-get reborn)
+* Thu Nov 13 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.5 ====
+* Thu Nov 13 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- More grst-admin.cgi GACL updates from Shiv.
+- .gacl security improvements to grst-admin.cgi from
+ Shiv Kaushal and Peter Moore.
+* Tue Nov 11 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- One RPM instead of three, with version from VERSION
+- Textarea for HTML/Text editing now 80 columns
+* Mon Nov 10 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Add delegation level and GridSiteGSIProxyLimit
+ support.
+- Add GridSiteAdminList handling to mod_gridsite
+ and real-gridsite-admin.cgi
+* Sun Nov 9 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Add directory create/delete, and file/dir rename.
+- Add ZIP listing/unzipping via external unzip
+ utility from http://www.info-zip.org/pub/infozip/
+* Mon Nov 3 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Include next version of Shiv's GACL editor.
+- Add rpm-usr target to Makefile, to make RPMs
+ out-of-the-box compatible with RH9 and its Apache2
+- Use REMOTE_DOUBLE_REV for GACL hostname creds in
+ mod_gridsite.c/mod_gridsite_perm_handler()
+* Sun Oct 26 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Include GACL editor in real-gridsite-admin.cgi
+ from Shiv Kaushal <shiv@hep.man.ac.uk>
+* Sun Oct 26 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Reorganise into a single build tree, including
+ Apache 2.0 .h files to remove circular dependency.
+* Sun Oct 26 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.4 ====
+* Sun Oct 19 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- Include many pieces of GridSite code from 0.3.x (CGI)
+ fileGridSite and mod_gridsite 0.9.0
+* Sun Oct 19 2003 Andrew McNab <mcnab@hep.man.ac.uk>
+- ==== GridSite version 0.9.3 ====
--- /dev/null
+BUILDING/INSTALLING GRIDSITE
+============================
+
+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
+
+GridSite is currently only supported on Linux, but should be
+trivially portable to other Unix platforms where the GNU build
+tools are available.
+
+When building from source, two routes are available: building
+with Make or with RPM.
+
+BUILDING WITH MAKE
+==================
+
+make
+make install
+
+will build all components and install them all under the default
+locations of /usr/local/[lib|bin|include|sbin] The default prefix
+/usr/local is set by the prefix variable in the top level Makefile
+
+BUILDING WITH RPM
+=================
+
+For RedHat Linux and derivatives, building with RPM is recommended.
+The command
+
+make rpm
+
+will build the gridsite and htcp binary RPMs in the directory
+../RPMTMP/RPMS/i386 relative to the working directory. A SRPM is
+put into ../RPMTMP/SRPMS
+
+Building with RPM uses the default prefix /usr, although the
+resulting RPMs are relocatable to other hierarchies.
--- /dev/null
+Copyright (c) 2002-4, Andrew McNab and Shiv Kaushal,
+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.
+
+
+Clearly marked portions of the published GridSite source code
+are derived from Apache httpd or its modules, and are covered
+by the Apache Software License:
+
+Copyright 2001-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
--- /dev/null
+See INSTALL for build and installation instructions, and
+http://www.gridpp.ac.uk/gridsite/ for configuration and
+usage guides.
--- /dev/null
+MAJOR_VERSION=1
+MINOR_VERSION=1.0
+PATCH_VERSION=1.0.4
+VERSION=$(PATCH_VERSION)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Copyright (c) 2004 on behalf of the EU EGEE Project:
+ The European Organization for Nuclear Research (CERN),
+ Istituto Nazionale di Fisica Nucleare (INFN), Italy
+ Datamat Spa, Italy
+ Centre National de la Recherche Scientifique (CNRS), France
+ CS Systeme d'Information (CSSI), France
+ Royal Institute of Technology, Center for Parallel Computers (KTH-PDC), Sweden
+ Universiteit van Amsterdam (UvA), Netherlands
+ University of Helsinki (UH.HIP), Finland
+ University of Bergen (UiB), Norway
+ Council for the Central Laboratory of the Research Councils (CCLRC), United Kingdom
+
+ Build file for the Gridsite Core Subsystem
+
+ Authors: Alberto Di Meglio <alberto.di.meglio@cern.ch>
+ Version info: $Id$
+ Release: $Name$
+
+ Revision history:
+ $Log$
+-->
+
+<project name="gridsite-core" default="dist">
+
+ <description>
+ Ant build file to build the Gridsite Core Component
+ </description>
+
+ <!-- =========================================
+ Import properties (order is important)
+ ========================================= -->
+
+ <!-- Import baseline & user properties -->
+ <import file="../org.glite/project/baseline.properties.xml" />
+
+ <!-- Import subsystem build properties,
+ subsystem properties &
+ subsystem common properties -->
+ <import file="./project/properties.xml" />
+
+ <!-- Import global build properties and global properties -->
+ <import file="${global.properties.file}" />
+
+ <!-- =========================================
+ Load dependencies properties files (order is important)
+ ========================================= -->
+ <property file="${user.dependencies.file}"/>
+ <property file="${subsystem.dependencies.file}"/>
+ <property file="${global.dependencies.file}"/>
+
+ <!-- =========================================
+ Load configure options
+ ========================================= -->
+ <import file="${global.configure.options.file}"/>
+ <import file="${component.configure.options.file}"/>
+
+ <!-- =========================================
+ Import global task definitions
+ ========================================= -->
+ <import file="${global.taskdefs.file}" />
+
+ <!-- =========================================
+ Import global compiler definitions
+ ========================================= -->
+ <import file="${global.compilerdefs.file}" />
+
+ <!-- =========================================
+ Import targets
+ ========================================= -->
+ <import file="${global.targets-common.file}" />
+
+ <!-- =========================================
+ Load version file
+ ========================================= -->
+ <property file="${module.version.file}"/>
+
+ <!-- ===============================================
+ Public common targets
+ =============================================== -->
+
+ <target name="localinit" depends="envcheck">
+ <mkdir dir="${stage.dir}" />
+ <mkdir dir="${dist.dir}" />
+ </target>
+
+ <target name="init" depends="localinit">
+ </target>
+
+ <target name="checkstyle" depends="init">
+ </target>
+
+ <target name="compile" depends="checkstyle">
+ <if>
+ <isset property="build.make.arguments"/>
+ <then>
+ <!-- Call make default compile target -->
+ <make target="build" dir="${module.src.dir}" failonerror="${failonerror}" args="${build.make.arguments}"/>
+ </then>
+ <else>
+ <!-- Call make default compile target -->
+ <make target="build" dir="${module.src.dir}" failonerror="${failonerror}"/>
+ </else>
+ </if>
+ </target>
+
+ <target name="compiletest" depends="compile">
+ </target>
+
+ <target name="unittest" depends="compiletest">
+ </target>
+
+ <target name="unitcoverage" depends="unittest">
+ </target>
+
+ <target name="doc" depends="unitcoverage">
+ </target>
+
+ <target name="stage" depends="doc">
+ <if>
+ <isset property="build.make.arguments"/>
+ <then>
+ <!-- Call make default compile target -->
+ <make target="install" dir="${module.src.dir}" failonerror="${failonerror}" args="${build.make.arguments}"/>
+ </then>
+ <else>
+ <!-- Call make default compile target -->
+ <make target="install" dir="${module.src.dir}" failonerror="${failonerror}"/>
+ </else>
+ </if>
+ </target>
+
+ <target name="dist" depends="stage">
+ <make target="rpm" dir="${module.src.dir}" failonerror="${failonerror}"/>
+ <exec dir="${module.dir}/RPMTMP/BUILDROOT/usr" executable="tar">
+ <arg line="-czf ${module.dir}/gridsite-${module.version}_bin.tar.gz ." />
+ </exec>
+ <copy file="gridsite-${module.version}_bin.tar.gz" todir="${dist.dir}"/>
+ <copy file="gridsite-${module.version}.src.tar.gz" tofile="${dist.dir}/gridsite-${module.version}_src.tar.gz"/>
+ <copy todir="${dist.dir}/rhel30/1386/RPMS">
+ <fileset dir="${module.dir}/RPMTMP/RPMS/i386">
+ <include name="*.rpm"/>
+ </fileset>
+ </copy>
+ <delete>
+ <fileset dir="${module.dir}">
+ <include name="*.tar.gz"/>
+ </fileset>
+ </delete>
+ <delete dir="RPMTMP"/>
+ </target>
+
+ <target name="install" depends="localinit">
+ <make target="install" dir="${module.src.dir}" failonerror="${failonerror}"/>
+ </target>
+
+ <target name="all" depends="dist">
+ </target>
+
+ <target name="clean" depends="envcheck">
+ <property name="offline.repository" value="true" />
+ <make target="clean" dir="${module.src.dir}" failonerror="false"/>
+ <delete dir="${module.dir}/src/doxygen"/>
+ <delete>
+ <fileset dir="${module.dir}/src">
+ <include name="*.o"/>
+ <include name="*.so"/>
+ <include name="*.so.*"/>
+ <include name="*.a"/>
+ </fileset>
+ </delete>
+ </target>
+
+ <target name="cleanAll" depends="clean"/>
+
+ <!-- ===============================================
+ Private targets
+ =============================================== -->
+
+ <!-- ===============================================
+ Modules proxy targets
+ =============================================== -->
+
+ <!-- component targets definitions tag = do not remove = -->
+
+
+ <!-- Main proxy -->
+ <target name="buildmodules" depends="">
+ <echo append="true" file="${global.project.dir}/cruisecontrol-stub.xml">
+ <project name="${subsystem.name}" type="post-subsystem" packageName="gridsite-${subsystem.prefix}"/>
+ </echo>
+ </target>
+
+</project>
--- /dev/null
+<title>GridSite Admin Guide</title>
+<body>
+<h1 align=center>GridSite Admin Guide</h1>
+
+<p>
+This Guide is intended for people administrating areas of GridSite
+websites or fileservers, or managing GridSite's DN List groups - that is,
+how to use GridSite to manage other people's access to parts of the site -
+for example, people's write access to areas devoted to specific subprojects.
+
+<p>
+ There is a separate
+<a href="user.html">User Guide</a>
+ which explains how to authenticate to the server with X.509 certificates,
+and how to manage files via a standard web browser or with command-line
+HTTPS clients. You should be familiar with the User Guide to fully
+understand this Admin Guide.
+
+<p>
+ You may also find the
+<a href="config.html">Config Guide</a>
+ useful to understand how the Apache webserver is configured with GridSite
+extensions. If you are also the Apache webmaster for your site, you will
+definitely need to read the Config Guide to create the httpd.conf file.
+However, if you only need to manage webpages and files, then this Admin
+Guide and the User Guide should be sufficient.
+
+<h2>Groups and DN Lists</h2>
+
+<p>
+GridSite defines groups of people using plain text DN Lists - that is, lists
+of people's certificate DNs. Each DN List has a URL which uniquely
+identifies the list (and may also allow other sites to obtain the list and
+use it themselves.) For example, the list of all GridPP members is
+https://www.gridpp.ac.uk/dn-lists/gridpp (note that it's https:// not
+http:// - this means that other sites that download the list can check the
+certificate of www.gridpp.ac.uk and know they're talking to the
+authoritative source of the lists.)
+
+<p>
+The system can also have a number of other DN Lists which are associated with
+specific groups of people and perhaps with specific areas of responsibility
+of the website. If the DN List directory URI is /dn-lists/ then
+there is a full list of the DN Lists exported by the server at that URI
+(for example, https://www.gridpp.ac.uk/dn-lists/ )
+
+<p>
+If you have permission to modify a DN List, you can start changing it by
+going to /dn-lists/ (via HTTPS), using the "Manage directory"
+button and finding the URL of your DN List in the listings. You may
+need to go down into a subdirectory to find your list. For
+example, https://www.gridpp.ac.uk/dn-lists/atlas is in the atlas
+subdirectory of /dn-lists/ (You may wish to bookmark the listing of such
+a directory if you frequently work with one.)
+
+<p>
+DN List directories are managed by the ACLs described in the next section,
+and if you have write permission, you can edit the lists already there, and
+add new lists with the same prefix (this means you can readily create your
+own subgroups.)
+
+<h2>Access Control Lists</h2>
+
+<p>
+DN Lists appear in the Grid Access Control Lists (GACL) used by GridSite.
+These are stored as .gacl files in directories: if the .gacl file is
+present, it governs access to the directory; if it is absent, then the
+parent directories are searched upwards until a .gacl is found.
+
+<p>
+The GridSite <a href="gacl.html">GACL Reference</a> explains the XML format
+of these files, but they
+can be edited using the ACL editor built into the GridSite system by people
+who have the Admin permission within the ACL.
+
+<p>
+If you have this permission in a given directory, when you view directory
+listings or files in that directory you will see the option "Manage
+Directory" in the page footer. This allows you to get a listing of the
+directory and the .gacl file will appear at the top if it's present. If not,
+then there will be a button to create a new .gacl file with the same
+permissions as have been inherited by that directory from its parent.
+
+<p>
+GACL allows quite complex conditions to be imposed on access, but normally
+you can think of an ACL as being composed of a number of entries, each of
+which contains one condition (the required credential) and a set of allowed
+and denied permissions.
+
+<p>
+Credentials can be individual user's certificate names or whole groups of
+certificate names if a DN List is given. (You can also specifiy hostname
+patterns using Unix shell wildcards (eg *.ac.uk) or EDG VOMS attribute
+certificates - see the GACL Reference for details.)
+
+<p>
+Permissions can be Admin (edit the ACL), Write (create, modify or delete
+files), List (browse the directory) or Read (read files.) Permissions can be
+allowed or denied. If denied by any entry, the permission is not available
+to that user or DN List (depending on what credential type was associated
+with the Deny.)
+
+</body>
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2002-3, Andrew McNab, University of Manchester
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# o Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+# o Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+#-----------------------------------------------------------------------------
+# For more information about GridSite: http://www.gridpp.ac.uk/gridsite/
+#-----------------------------------------------------------------------------
+#
+# 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
+# the "-C" option to configure (which RedHat 7.3 doesnt like) and
+# outputs source and binary RPMs in SRPMS and RPMS/i386
+
+if [ "$1" = "" ] ; then
+ echo Must give a tar.gz file name
+ exit
+fi
+
+export MYTOPDIR=`pwd`
+
+if [ -x /usr/bin/rpmbuild ] ; then
+ export RPMCMD=rpmbuild
+else
+ export RPMCMD=rpm
+fi
+
+echo "$1" | grep '\.tar\.gz$' >/dev/null 2>&1
+if [ $? = 0 ] ; then # a gzipped source tar ball
+
+ rm -Rf $MYTOPDIR/BUILD $MYTOPDIR/BUILDROOT $MYTOPDIR/SOURCES
+ mkdir -p $MYTOPDIR/SOURCES $MYTOPDIR/SPECS $MYTOPDIR/BUILD \
+ $MYTOPDIR/SRPMS $MYTOPDIR/RPMS/i386 $MYTOPDIR/BUILDROOT
+
+ shortname=`echo $1 | sed 's:^.*/::' | sed 's:\.tar\.gz$::'`
+
+ cp -f $1 SOURCES
+
+ tar zxvf SOURCES/$shortname.tar.gz $shortname/httpd.spec
+ cp -f $shortname/httpd.spec SPECS
+
+ sed -e 's/configure -C /configure /' \
+ SPECS/httpd.spec >SPECS/httpd-2.spec
+
+ $RPMCMD --define "_topdir $MYTOPDIR" \
+ -ba --buildroot $MYTOPDIR/BUILDROOT SPECS/httpd-2.spec
+
+ exit
+fi
+
+echo I dont recognise the file type (must be .tar.gz)
+
+exit
--- /dev/null
+<title>GridSite Config Guide</title>
+<body>
+<h1 align=center>GridSite Config Guide</h1>
+
+<p>
+This Guide is intended for webmasters setting up
+<a href="http://www.gridpp.ac.uk/">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
+websites or fileservers, or managing GridSite's DN List groups. That is, for
+people managing files on the server rather than the server itself.
+
+<h2>Installation</h2>
+
+<p>
+We assume you have installed Apache 2.0 and GridSite, using the
+<a href="install.html">Building and Installation Guide</a> where necessary.
+This Config Guide assumes installation has been done under /usr. For an
+alternative tree like /usr/local, the relative paths should be the same.
+
+<p>
+Installation should have given you an Apache 2.0 httpd binary at
+/usr/sbin/httpd and a set of standard Apache 2.0 modules in
+/usr/lib/httpd/modules/ including the standard mod_ssl
+and our mod_gridsite.so module.
+
+<p>
+GridSite also includes some commands and man pages in /usr/bin and
+/usr/share/man/man1: <a href="urlencode.1.html">urlencode</a> and
+<a href="htcp.1.html">htcp</a>.
+
+<h2>Certificates</h2>
+
+<p>
+You must also install the CA root certificates of the CA's
+used by the users you wish to talk to. These should be installed in
+/etc/grid-security/certificates as files like 01621954.0, and RPMs and tar
+files for many common European and North American CAs are available from
+<a href="https://datagrid.in2p3.fr/distribution/datagrid/security/">
+https://datagrid.in2p3.fr/distribution/datagrid/security/</a>
+
+<p>
+This location also has VOMS server certificate RPMs which install into
+the /etc/grid-security/vomsdir directory. You may also manually install VOMS
+server certificates into that directory with any filename. (GridSite
+currently parses the certificate itself when looking for a match, rather
+than checking the filename.)
+
+<p>
+The server itself needs a certificate to supply to clients that use HTTPS
+connections. You should apply for this from your Certification Authority
+(for example, the <a href="http://ca.grid-support.ac.uk/">UK e-Science
+CA</a>) and your request must use the advertised hostname of your server
+(the one that appears in URLs and not, for instance, the canonical name of
+the host itself.) This advertised hostname should appear in the
+Distinguished Name of your request. (For example
+/C=UK/O=eScience/OU=Manchester/L=HEP/CN=www.gridpp.ac.uk) For compatability
+with standard browsers, the /CN= component should not include any
+Globus-style service name (so <b>not</b> /CN=host/www.gridpp.ac.uk) If
+possible, you should also include the advertised hostname as a DNS Subject
+Alternative Name. Consult your CA first if you're in any doubt about how to
+compose your certificate request.
+
+<p>
+Once you've got your certificate,
+Apache uses the certificate and private key in PEM format. If you obtained
+your certificate and key in PKCS#12 or .p12 format (eg by exporting from a web
+browser), you can convert the .p12 file to .pem with the following commands:
+<pre>
+openssl pkcs12 -in ck.p12 -clcerts -nokeys -out hostcert.pem
+openssl pkcs12 -in ck.p12 -nodes -nocerts -out hostkey.pem
+</pre>
+
+<p>
+Copy the PEM files to /etc/grid-security/ as hostcert.pem (which
+should be world readable) and hostkey.pem (which should only be readable by
+root):
+
+<pre>
+chown root.root hostkey.pem hostcert.pem
+chmod 400 hostkey.pem
+chmod 444 hostcert.pem
+</pre>
+
+<h2>httpd.conf</h2>
+
+<p>
+/etc/httpd/conf/httpd.conf is the key to configuring the Apache 2.0
+webserver. The directives in this file determine which files the server will
+publish, how they are handled, which areas are writeable and who can access
+them. Through mod_gridsite.so, the GridSite system itself is configured by
+directives in this file.
+
+<p>
+The easiest way to get started is to examine the example httpd.conf files we
+provide.
+
+<!--
+virtual servers
+directory sections
+order of loadable modules
+-->
+
+<h2>httpd-fileserver.conf</h2>
+
+<p>
+<a href="httpd-fileserver.conf">httpd-fileserver.conf</a> is an example
+configuration file to use Apache/GridSite as a read/write HTTP(S)
+fileserver, including comments on how to get the server up and running.
+
+<h2>httpd-webserver.conf</h2>
+
+<p>
+<a href="httpd-webserver.conf">httpd-webserver.conf</a> is an example
+configuration file to use Apache/GridSite as a Web Server
+(that is, primarily for interactive use with a browser)
+including comments on how to get the server up and running.
+
+<h2>GridSite Directives</h2>
+
+<p>
+The <a href="module.html">mod_gridsite reference</a> lists all the GridSite
+httpd.conf directives.
+
+<p>
+To start serving files, make a directory /var/www/htdocs owned by
+nobody.nobody, including the .gacl access control file described below,
+and add the following directive to the HTTPS <Directory> section:
+
+<p>
+GridSiteMethods GET PUT DELETE
+
+<p>
+If you wish to accept Globus GSI Proxies as well as full X.509 user
+certificates, set GridSiteGSIProxyLimit to the depth of proxy you
+wish to accept. (As a _rough_ guide: 0=No Proxies; 1=Proxy on user's
+machine; 2=Proxy owned by running Globus job; 3=Proxy delegated by a
+Globus job.)
+
+<h2>GACL access control</h2>
+
+<p>
+The <a href="gacl.html">GACL reference</a> explains the XML access
+control files used by GridSite. These allow flexible policies to be written,
+in terms of X.509 user certificates, GSI proxies, VOMS attribute
+certificates, DN List groups and DNS hostnames.
+
+<p>
+For example, to give all clients read and list permission:
+<p>
+<pre>
+<gacl>
+<entry>
+ <any-user/>
+ <allow><read/><list/></allow>
+</entry>
+</gacl>
+</pre>
+
+<p>
+To enable writing, add DN List, Person or VOMS entries to the file.
+For example:
+
+<p>
+<pre>
+<gacl>
+<entry>
+ <any-user/>
+ <allow><read/><list/></allow>
+</entry>
+<entry>
+ <person>
+ <dn>/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab</dn>
+ </person>
+ <allow><write/></allow>
+</entry>
+</gacl>
+</pre>
+
+<p>
+The GACL file that governs a directory is stored as .gacl in that directory.
+If no .gacl is present, then GridSite will search the parent directories in
+ascending order until one is found.
+
+<!--
+<h2>DN Lists</h2>
+<h2>gridsite-admin.cgi</h2>
+<h2>Other CGI</h2>
+-->
+
+</body>
--- /dev/null
+<title>GridSite: Grid Access Control Language</title>
+<body>
+<h1 align=center>GridSite: Grid Access Control Language</h1>
+
+<p>
+GACL is the authorization policy language used by
+<a href="http://www.gridpp.ac.uk/gridsite/">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.
+
+<p>
+GridSite both uses GACL policies and provides a GACL manipulation API for
+C/C++ in the GridSite library.
+
+<h2>Credentials</h2>
+
+<p>
+In GridSite 1.0.x, four credential types are supported:
+
+<p>
+<person>
+<dn>/O=Grid/CN=Name</dn>
+</person>
+
+<p>
+<voms>
+<fqan>/vo.dom.ain/group</fqan>
+</voms>
+
+<p>
+<dn-list>
+<url>https://www.vo.dom.ain/dn-lists/group</url>
+</dn-list>
+
+<p>
+<dns>
+<hostname>host*.dom.ain</hostname>
+</dns>
+
+<h2>Permissions</h2>
+
+<p>
+Five permissions are supported: Admin, Write, List, Exec and Read. Admin is
+permission to modify the authorization policy itself, but applications can
+map the other permissions to local methods as appropriate to their
+environment. For filesystems and fileservers, Write, List and Read have
+their usual meanings: creating or modifying files or directories; browsing
+directories; reading files. Exec is not used by GridSite itself, and
+applications are free to give it a meaning within their own contexts.
+
+<p>
+In 1.0.x, only per-directory GACL files are supported, and the file is stored
+in the directory in question, or in one of its parent directories. (GridSite
+searches upwards until it finds one.)
+
+<p>
+In GACL files, the permissions are represented by single tags:
+<admin/>, <write/>, <list/>, <exec/>, <read/>.
+Permission
+tags are contained within Allow or Deny blocks. For example:
+<allow><read/><list/></allow> or
+<deny><admin/></deny>.
+
+<h2>Entries</h2>
+
+<p>
+Entries associate credentials with permission statements. Entries consist of
+one or more credential blocks, and either an Allow or a Deny block, or both.
+If multiple credentials are present in one entry, they must all be held by a
+user to receive the association permissions. (So Entries provide logical AND
+of credentials.)
+
+<h2>Access Control Lists</h2>
+
+<p>
+ACLs consist of a list of one or more Entry blocks. When a user's credentials
+are compared to the ACL, the permissions given to the user by Allow blocks
+are recorded, along with those forbidden by Deny blocks. When all entries
+have been evaluated, any forbidden permissions are removed from those
+granted. (So Deny always wins over Allow, even between different Entries,
+but otherwise ACLs provide logical OR of credentials.)
+
+</body>
--- /dev/null
+.TH htcp 1 "December 2003" htcp "HTCP Manual"
+.SH NAME
+.B htcp, htrm, htls, htll, htmkdir
+\- get, put, delete or list HTTP/HTTPS files or directories
+.SH SYNOPSIS
+.B htcp [options]
+.I Source-URL[s] [Destination URL]
+.SH DESCRIPTION
+.B htcp
+is a client to fetch files or directory listings from remote servers using
+HTTP or HTTPS, or to put or delete files or directories onto remote servers
+using HTTPS. htcp is similar to scp(1), but uses HTTP/HTTPS rather than ssh
+as its transfer protocol.
+
+When talking to an HTTPS server, htcp can run "anonymously", with a
+standard X.509 user certificate and key, or with a GSI Proxy. This makes
+htcp very useful in Grid environments where many users have certificates
+and where jobs and users have access to GSI proxies.
+
+.SH URLs
+htcp supports the file:, http: and https: URL schemes as sources and
+destinations. If no scheme is given, the URL scheme is assumed to be file:
+and relative to the current directory if not an absolute path.
+
+If multiple sources are given, they will be used in turn and the destination
+must be a directory (directories are indicated by a trailing /) However,
+source and destination cannot both refer to remote servers.
+
+.SH OPTIONS
+.IP "-v/--verbose"
+Turn on debugging information. Used once, this option will enable htcp's
+messages to stderr. Used twice, will also enable the underlying libcurl
+messages.
+
+.IP "--delete"
+Instead of copying files, delete all the URLs given on the command line.
+Calling the program as htrm has the same effect.
+
+.IP "--list"
+.br
+Instead of copying files, output lists of files located in the URL-directories
+given on the command line. Calling the program as htls has the same effect.
+
+.IP "--long-list"
+Instead of copying files, output long listings of files located in the
+URL-directories given on the command line. If available, the size in bytes
+and modification time of each file is given. Calling the program as
+htll has the same effect.
+
+.IP "--mkdir"
+Instead of copying files, attempt to create a directory on a remote server
+with HTTP PUT. The server must support the convention that PUT to a URL with
+a trailing slash means create a directory. No file body is sent. Calling the
+program as htmkdir has the same effect.
+
+.IP "--anon"
+.br
+Do not attempt to use X.509 user certificates or GSI proxies to authenticate
+to the remote HTTPS server. This means you are "anonymous", but the server's
+identity may still be verified and the connection is still encrypted.
+
+.IP "--cert <X.509 cert path> and --key <X.509 key path>"
+Path to the PEM-encoded
+X.509 or GSI Proxy user certificate and key to use for HTTPS
+connections, intead of "anonymous mode." If only one of --key or --cert
+is given, then that will be tried for both. If neither is given, then the
+following order of precedence is used:
+the file name held by the variable X509_USER_PROXY; the file
+/tmp/x509up_uID (with Unix UID equal to ID); the file names held by
+X509_USER_CERT / X509_USER_KEY; the files ~/.globus/usercert.pem and
+~/.globus/userkey.pem (where ~/ is the home directory of the user.)
+
+.IP "--capath <X.509 CA root certs directory or file>"
+Path to the PEM-encoded CA root certificates to use when
+verifying remote servers' host certificates in HTTPS connections. Ideally
+this should be a directory of hash.0 files as described in the OpenSSL
+verify(1) man page, but a file may be used instead. If --capath is not
+given, the value of the environment variable X509_CERT_DIR will be tried.
+If this is not valid, then /etc/grid-security/certificates will be used.
+
+.IP "--no-verify"
+Do not use CA root certificates to verify remote servers' host certificates.
+This is useful for testing sites before their certificate is set up properly,
+but leaves you vulnerable to "man in the middle" attacks by hostile servers
+masquerading as your target.
+
+.SH FILES
+.IP /tmp/x509up_uID
+Default GSI Proxy file for Unix UID equal to ID.
+
+.IP /etc/grid-security/certificates
+Default location for trusted Certification Authority root certificates to use
+when checking server certificates.
+
+.IP /tmp/.ca-roots-XXXXXX
+Prior to 7.9.8, the underlying curl library did not support the CA root
+certificates directory.
+If built with an old version of libcurl, htcp will concatenate the
+certificates in the CA roots directory into a unique temporary file and use
+that.
+
+.SH ENVIRONMENT
+
+.IP X509_CERT_DIR
+Holds directory to search for Certification Authority root certificates when
+verifying server certificates. (Tried if --capath is not given on the
+command line.)
+
+.IP X509_USER_PROXY
+Holds file name of a GSI Proxy to use as user certificate. (Tried if --cert or
+--key are not given on the command line.)
+
+.IP "X509_USER_CERT and X509_USER_KEY"
+Holds file name of X.509 user certificate and key. (Tried if X509_USER_PROXY
+is not valid.)
+
+.SH EXIT CODES
+0 is returned on complete success. Curl error codes are returned when
+reported by the underlying curl library, and CURLE_HTTP_RETURNED_ERROR (22)
+is returned when the HTTP(S) server returns a code outside the range 200-299.
+The manpage libcurl-errors(3) lists all the curl error codes.
+
+.SH TO DO
+Recursive copying. Server-side wildcards. Parallel streams. Error recovery.
+
+.SH BUGS
+Not enough beta testing (hint hint...)
+
+.SH AUTHOR
+Andrew McNab <mcnab@hep.man.ac.uk>
+
+htcp is part of GridSite: http://www.gridpp.ac.uk/gridsite/
+.SH "SEE ALSO"
+.BR scp(1),
+.BR curl(1),
+.BR wget(1),
+.BR verify(1),
+.BR libcurl-errors(3)
--- /dev/null
+.so man1/htcp.1
--- /dev/null
+.so man1/htcp.1
--- /dev/null
+.so man1/htcp.1
--- /dev/null
+.so man1/htcp.1
--- /dev/null
+##############################################################################
+## GridSite httpd-fileserver.conf - Andrew McNab <mcnab@hep.man.ac.uk>
+##
+## Example configuration file for GridSite as an HTTP(S) fileserver.
+##
+## For GridSite documentation, see http://www.gridpp.ac.uk/gridsite/
+##
+## This file should be renamed /etc/httpd/conf/httpd.conf and Apache
+## restarted to use Apache2/GridSite as a simple HTTP(S) fileserver.
+##
+## You do not need to install the GridSite mod_ssl.so module if you
+## do not wish to use Globus Proxies or VOMS attributes, but you must
+## have the mod_gridsite.so in /usr/lib/httpd/modules
+##
+## We're assuming you have (a) the host's hostcert.pem and hostkey.pem
+## in /etc/grid-security/ and (b) the Certification Authorities' you
+## trust have their root certs in /etc/grid-security/certificates
+##
+## (You can get RPMs for many European and North American Grid CAs
+## from https://datagrid.in2p3.fr/distribution/datagrid/security/ )
+##
+## If you want to use DN Lists in ACLs, they should be placed/downloaded
+## in /etc/grid-security/dn-lists/
+##
+## To start serving files, make a directory /var/www/htdocs owned by
+## nobody.nobody, including the file .gacl containing:
+##
+## <gacl>
+## <entry>
+## <any-user/>
+## <allow><read/><list/></allow>
+## </entry>
+## </gacl>
+##
+## To enable writing, add DN List, Person or VOMS entries to the GACL
+## (see the GridSite GACL document for the syntax.) For example:
+##
+## <gacl>
+## <entry>
+## <any-user/>
+## <allow><read/><list/></allow>
+## </entry>
+## <entry>
+## <person>
+## <dn>/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab</dn>
+## </person>
+## <allow><write/></allow>
+## </entry>
+## </gacl>
+##
+## and add the following directive to the HTTPS <Directory> section:
+##
+## GridSiteMethods GET PUT DELETE
+##
+## If you wish to accept Globus GSI Proxies as well as full X.509 user
+## certificates, set GridSiteGSIProxyLimit to the depth of proxy you
+## wish to accept.
+##
+## (As a _rough_ guide: 0=No Proxies; 1=Proxy on user's machine; 2=Proxy
+## owned by running Globus job; 3=Proxy delegated by a Globus job.)
+##
+## With this done and Apache restarted, you can upload a file with:
+##
+## curl -v --cert ~/.globus/usercert.pem --key ~/.globus/userkey.pem \
+## --capath /etc/grid-security/certificates --upload-file /tmp/tmp.txt \
+## https://INSERT.HOSTNAME.HERE/tmp.txt
+##
+## (or with --cert /tmp/x509up_u`id -u` --key /tmp/x509up_u`id -u` to use
+## a Globus GSI Proxy created with grid-proxy-init.)
+##############################################################################
+
+ServerRoot "/etc/httpd"
+
+PidFile logs/httpd.pid
+
+Timeout 300
+KeepAlive On
+MaxKeepAliveRequests 100
+KeepAliveTimeout 15
+
+LoadModule log_config_module /usr/lib/httpd/modules/mod_log_config.so
+LoadModule ssl_module /usr/lib/httpd/modules/mod_ssl.so
+LoadModule gridsite_module /usr/lib/httpd/modules/mod_gridsite.so
+LoadModule mime_module /usr/lib/httpd/modules/mod_mime.so
+LoadModule dir_module /usr/lib/httpd/modules/mod_dir.so
+
+TypesConfig /etc/mime.types
+
+# User and group who will own files created by Apache
+User nobody
+Group nobody
+
+DocumentRoot "/var/www/htdocs"
+
+<Directory />
+ AllowOverride None
+</Directory>
+
+LogLevel debug
+LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+
+CustomLog logs/httpd-gridsite-access combined
+ErrorLog logs/httpd-gridsite-errors
+
+HostnameLookups On
+
+######################################################################
+# Plain unauthenticated HTTP on port 80
+######################################################################
+
+Listen 80
+<VirtualHost *:80>
+
+<Directory "/var/www/htdocs">
+ GridSiteIndexes on
+ GridSiteAuth on
+ GridSiteDNlists /etc/grid-security/dn-lists/
+</Directory>
+
+</VirtualHost>
+
+######################################################################
+# Secured and possibly authenticated HTTPS on port 443
+######################################################################
+Listen 443
+<VirtualHost *:443>
+
+SSLEngine on
+SSLCertificateFile /etc/grid-security/hostcert.pem
+SSLCertificateKeyFile /etc/grid-security/hostkey.pem
+SSLCACertificatePath /etc/grid-security/certificates
+#SSLCARevocationPath YOUR CRL DIRECTORY WOULD GO HERE
+SSLVerifyClient optional
+SSLVerifyDepth 10
+SSLOptions +ExportCertData +StdEnvVars
+
+<Directory "/var/www/htdocs">
+ GridSiteIndexes on
+ GridSiteAuth on
+ GridSiteDNlists /etc/grid-security/dn-lists/
+ GridSiteGSIProxyLimit 0
+# GridSiteMethods GET PUT DELETE
+</Directory>
+
+</VirtualHost>
--- /dev/null
+##############################################################################
+## GridSite httpd-webserver.conf - Andrew McNab <mcnab@hep.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/
+##
+## This file should be renamed /etc/httpd/conf/httpd.conf and Apache
+## restarted to use Apache2/GridSite as a webserver.
+##
+## You do not need to install the GridSite mod_ssl.so module if you
+## do not wish to use Globus Proxies or VOMS attributes, but you must
+## have the mod_gridsite.so in /usr/lib/httpd/modules
+##
+## We're assuming you have (a) the host's hostcert.pem and hostkey.pem
+## in /etc/grid-security/ and (b) the Certification Authorities' you
+## trust have their root certs in /etc/grid-security/certificates
+##
+## (You can get RPMs for many European and North American Grid CAs
+## from https://datagrid.in2p3.fr/distribution/datagrid/security/ )
+##
+## If you want to use DN Lists in ACLs, they should be placed/downloaded
+## in /etc/grid-security/dn-lists/ or /var/www/htdocs/dn-lists/
+## (Lists in /etc/grid-security/dn-lists/ override lists elsewhere.)
+##
+## To start serving files, make a directory /var/www/htdocs owned by
+## nobody.nobody, including the file .gacl containing:
+##
+## <gacl>
+## <entry>
+## <any-user/>
+## <allow><read/><list/></allow>
+## </entry>
+## </gacl>
+##
+## To enable writing, add DN List, Person or VOMS entries to the GACL
+## (see the GridSite GACL document for the syntax.) For example:
+##
+## <gacl>
+## <entry>
+## <any-user/>
+## <allow><read/><list/></allow>
+## </entry>
+## <entry>
+## <person>
+## <dn>/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab</dn>
+## </person>
+## <allow><write/></allow>
+## </entry>
+## </gacl>
+##
+## and add the following directive to the HTTPS <Directory> section:
+##
+## GridSiteMethods GET PUT DELETE
+##
+## If you wish to accept Globus GSI Proxies as well as full X.509 user
+## certificates, set GridSiteGSIProxyLimit to the depth of proxy you
+## wish to accept.
+##
+## (As a _rough_ guide: 0=No Proxies; 1=Proxy on user's machine; 2=Proxy
+## owned by running Globus job; 3=Proxy delegated by a Globus job.)
+##
+## With this done and Apache restarted, you can upload a file with:
+##
+## curl -v --cert ~/.globus/usercert.pem --key ~/.globus/userkey.pem \
+## --capath /etc/grid-security/certificates --upload-file /tmp/tmp.txt \
+## https://INSERT.HOSTNAME.HERE/tmp.txt
+##
+## (or with --cert /tmp/x509up_u`id -u` --key /tmp/x509up_u`id -u` to use
+## a Globus GSI Proxy created with grid-proxy-init.)
+##############################################################################
+
+ServerRoot "/etc/httpd"
+
+## You MUST put your server's fully qualified domain name here
+## This, the DOMAIN part of the https://DOMAIN/... URLs you want
+ServerName FULL.SERVER.NAME
+
+PidFile logs/httpd.pid
+
+Timeout 300
+KeepAlive On
+MaxKeepAliveRequests 100
+KeepAliveTimeout 15
+
+LoadModule log_config_module /usr/lib/httpd/modules/mod_log_config.so
+LoadModule ssl_module /usr/lib/httpd/modules/mod_ssl.so
+LoadModule gridsite_module /usr/lib/httpd/modules/mod_gridsite.so
+LoadModule mime_module /usr/lib/httpd/modules/mod_mime.so
+LoadModule dir_module /usr/lib/httpd/modules/mod_dir.so
+LoadModule alias_module /usr/lib/httpd/modules/mod_alias.so
+LoadModule cgi_module /usr/lib/httpd/modules/mod_cgi.so
+
+TypesConfig /etc/mime.types
+
+# User and group who will own files created by Apache
+User nobody
+Group nobody
+
+DocumentRoot "/var/www/htdocs"
+
+<Directory />
+ AllowOverride None
+</Directory>
+
+LogLevel debug
+LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+
+CustomLog logs/httpd-gridsite-access combined
+ErrorLog logs/httpd-gridsite-errors
+
+HostnameLookups On
+
+######################################################################
+# Plain unauthenticated HTTP on port 80
+######################################################################
+
+Listen 80
+<VirtualHost *:80>
+
+## This is used to serve the Manage Directory links in footers,
+## and to allow you to edit files and ACLs via your browser.
+ScriptAlias /real-gridsite-admin.cgi /usr/sbin/real-gridsite-admin.cgi
+
+<Directory "/var/www/htdocs">
+ ## This sets up GACL authorization for this server.
+ GridSiteAuth on
+
+ ## This exports various bits of info into the CGI environment
+ ## variables (and is needed for gridsite-admin.cgi to work.)
+ GridSiteEnvs on
+
+ ## Nice GridSite directory listings (without truncating file names!)
+ GridSiteIndexes on
+
+ ## If this is on, GridSite will look for gridsitehead.txt and
+ ## gridsitefoot.txt in the current directory or its parents, and
+ ## use them to replace the <body> and </body> tags in .html files.
+ GridSiteHtmlFormat on
+
+ ## These directives (and the ScriptAlias above) allow authorized
+ ## people to manage files, ACLs and DN Lists through their web
+ ## browsers. Via HTTP, this just means extended directory listings
+ ## and History pages.
+ GridSiteAdminURI /real-gridsite-admin.cgi
+ GridSiteAdminFile gridsite-admin.cgi
+</Directory>
+
+</VirtualHost>
+
+######################################################################
+# Secured and possibly authenticated HTTPS on port 443
+######################################################################
+Listen 443
+<VirtualHost *:443>
+
+SSLEngine on
+SSLCertificateFile /etc/grid-security/hostcert.pem
+SSLCertificateKeyFile /etc/grid-security/hostkey.pem
+SSLCACertificatePath /etc/grid-security/certificates
+#SSLCARevocationPath YOUR CRL DIRECTORY WOULD GO HERE
+SSLVerifyClient optional
+SSLVerifyDepth 10
+SSLOptions +ExportCertData +StdEnvVars
+
+## This is used to serve the Manage Directory links in footers,
+## and to allow you to edit files and ACLs via your browser.
+ScriptAlias /real-gridsite-admin.cgi /usr/sbin/real-gridsite-admin.cgi
+
+<Directory "/var/www/htdocs">
+ ## This sets up GACL authorization for this server.
+ GridSiteAuth on
+
+ ## This exports various bits of info into the CGI environment
+ ## variables (and is needed for gridsite-admin.cgi to work.)
+ GridSiteEnvs on
+
+ ## Nice GridSite directory listings (without truncating file names!)
+ GridSiteIndexes on
+
+ ## If this is on, GridSite will look for gridsitehead.txt and
+ ## gridsitefoot.txt in the current directory or its parents, and
+ ## use them to replace the <body> and </body> tags in .html files.
+ GridSiteHtmlFormat on
+
+ ## This is the path of directories (and all their subdirectories) for
+ ## GACL to search when it encounters a dn-list credential. The DN List
+ ## files are plain text, one DN per line, and must have the full url
+ ## as the file name, but URL Encoded - eg with urlencode(1)
+ GridSiteDNlists /etc/grid-security/dn-lists/:/var/www/htdocs/dn-lists/
+
+ ## This is used to form the URL at which DN Lists "owned" by this
+ ## server are exported. https://FULL.SERVER.NAME/dn-lists/file
+ ## ALL FILES WITH URLs ON THIS SERVER WILL BE EXPORTED IRRESPECTIVE
+ ## OF WHERE THEY ARE FOUND ON THE DN-LISTS PATH!!
+ GridSiteDNlistsURI /dn-lists/
+
+ ## If this is greater than zero, we will accept GSI Proxies for clients
+ ## (full client certificates - eg inside web browsers - are always ok)
+ GridSiteGSIProxyLimit 0
+
+ ## This directive allows authorized people to write/delete files
+ ## from non-browser clients - eg with htcp(1)
+ GridSiteMethods GET PUT DELETE
+
+ ## These directives (and the ScriptAlias above) allow authorized
+ ## people to manage files, ACLs and DN Lists through their web
+ ## browsers via HTTPS. The value of GridSiteAdminFile appears to
+ ## exist in every directory, but is internally redirected by
+ ## mod_gridsite to the value of GridSiteAdminURI (the ScriptAlias
+ ## then maps that onto the real-gridsite-admin.cgi executable.)
+ GridSiteAdminURI /real-gridsite-admin.cgi
+ GridSiteAdminFile gridsite-admin.cgi
+</Directory>
+
+</VirtualHost>
--- /dev/null
+<title>GridSite 1.0.x Documentation</title>
+<body>
+<h1 align=center>GridSite 1.0.x Documentation</h1>
+
+<p>
+<a href="http://www.gridpp.ac.uk/gridsite/">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
+the associated verified credentials are available to all technologies
+supported by Apache, including static file serving, SSI, CGI, PHP, JSP and
+mod_perl.
+
+<h2>Guides</h2>
+
+<p>
+<dl>
+<dt><b><a href="user.html">User Guide</a></b>
+<dd>End-user documentation for people managing webpages and files on
+ GridSite servers, either through the web interface or with command
+ line clients like htcp.
+<p>
+
+<dt><b><a href="admin.html">Admin Guide</a></b>
+<dd>For people administering areas of GridSite websites or fileservers, or
+ managing GridSite's support for DN List groups.
+<p>
+
+<dt><b><a href="install.html">Building and Installation</a></b>
+<dd>Instructions for building GridSite from source, and installing from
+ binaries or RPMs.
+<p>
+
+<dt><b><a href="config.html">Config Guide</a></b>
+<dd>For webmasters setting up Apache 2.0 and GridSite, and writing the
+ Apache httpd.conf file.
+<p>
+
+<dt><b><a href="httpd-fileserver.conf">httpd-fileserver.conf</a></b> and
+ <b><a href="httpd-webserver.conf">httpd-webserver.conf</a></b>
+<dd>Example configuration files for simple HTTP(S) fileservers and
+ webservers, with explanatory comments.
+<p>
+
+</dl>
+
+<h2>Reference</h2>
+
+<p>
+<dl>
+<dt><b><a href="gacl.html">Grid Access Control Lists</a></b>
+<dd>Syntax and usage of the XML Grid Access Control Lists used by GridSite.
+<p>
+
+<dt><b><a href="htcp.1.html">htcp</a></b> and
+ <b><a href="urlencode.1.html">urlencode</a></b> man pages
+<dd>Command line tools for copying files to or from HTTP(S) servers, and
+ for URL-encoding strings.
+<p>
+
+<!--
+<dt><b><a href="gridsite-admin.html">gridsite-admin.cgi</a></b>
+<dd>A CGI program providing site administration functions for users with
+ standard web browsers, via HTTPS. gridsite-admin.cgi includes a file
+ manager, support for file uploading, and editors for HTML, text and
+ Grid Access Control List files.
+<p>
+-->
+
+<dt><b><a href="module.html">mod_gridsite</a></b>
+<dd>An Apache 2.0 module which enforces access control via Grid Access
+ Control Lists, and X.509, GSI or VOMS credentials. mod_gridsite also
+ gives Apache built-in support for the HTTP PUT and DELETE methods, and
+ formatting of HTML pages with standard headers and footers.
+<p>
+
+<!--
+<dt><b><a href="library.html">libgridsite</a></b>
+<dd>The GridSite library provides common functions for other components of
+ the GridSite system, and utilities for programs using CGI, X.509, GSI,
+ VOMS and HTTP.
+<p>
+-->
+
+<dt><b><a href="gridsite_8h.html">gridsite.h API reference</a></b>
+<dd>A detailed description of the C API provided by libgridsite, generated
+ from the sources by doxygen.
+<p>
+
+</dl>
+
+</body>
--- /dev/null
+<title>GridSite: Building and Installation Guide</title>
+<body>
+<h1 align=center>GridSite: Building and Installation Guide</h1>
+
+<p>
+This Guide explains how to build GridSite from source, and how to install
+the server components alongside an Apache 2.0 webserver. There is a
+separate <a href="config.html">Config Guide</a> which explains how to modify
+the httpd.conf file, and how to set up other files and directories used by
+the system. You should look through all of this Building and Installation
+Guide to decide which is the easiest route for your system.
+
+<h2>Installing with RPM</h2>
+
+<p>
+If you are installing on Linux with the binary RPM release, you can skip
+most of this Guide, install the binary rpm(s) and go straight
+to the Config Guide.
+
+<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>
+
+<p>
+<b>RedHat 9</b>:
+This is the simpler case, since the standard release includes a suitable
+version of Apache 2.0: just install the gridsite-...-1.i386.rpm to get the
+various GridSite components.
+
+<p>
+<b>RedHat 7.3</b>:
+This is more complicated because you must also install a back-ported Apache
+2.0 RPM. We distribute RPMs built on 7.3 aimed at RedHat 7.3
+machines with updates, from our download area. These are built from the
+tar.gz and .spec files distributed by the
+Apache Foundation itself, using the
+<a href="build-apache2.sh">build-apache2.sh</a> script in the GridSite
+/usr/share/doc/gridsite directory. The Apache RPMs install in /usr, and you
+should at least install the httpd and mod_ssl RPMs.
+You must also install the gridsite-...-1.i386.rpm as above.
+
+<p>
+GridSite also depends on shared libraries from libcurl and libxml2, and the
+RPMs distributed as part of the standard RedHat 7.3 and 9 releases are
+sufficient.
+
+<p>
+With the RPMs installed, you can proceed to the
+<a href="config.html">Config Guide</a>.
+
+<h2>Requirements for building GridSite from source</h2>
+
+<p>
+GridSite is currently only supported on Linux, but should be
+straightforwardly
+portable to other Unix platforms where the GNU build tools are available.
+
+<p>
+GridSite consists of a core library (libgridsite[.so|.a]), an Apache module
+(mod_gridsite.so), a CGI utility (gridsite-admin.cgi) and some command line
+tools (htcp, urlencode.)
+
+<p>
+All of the components use the GridSite library, and this in turn depends on
+libcurl and libxml2. You will need the development versions of these
+packages installed before you can proceed. (They are available as part of
+RedHat Linux releases 7.x onwards, for instance.)
+
+<h2>Building GridSite with Make</h2>
+
+<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
+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.)
+
+<p>
+GridSite needs a copy of the Apache 2.0 include files to build, and the
+location of this is set by the MYCFLAGS variable in the top-level Makefile.
+For manual builds, the default
+<b>MYCFLAGS=-I/usr/local/include/httpd</b> is used.
+If you wish to use the GridSite module with Apache
+2.0 installed elsewhere, you should change the MYCFLAGS variable to point to
+the includes directory installed by the development part of that Apache 2.0
+distribution.
+
+<p>
+<pre>
+make
+make install
+</pre>
+
+<p>
+will build all components and install them all under the default
+locations of /usr/local/[lib|bin|include|sbin] The default prefix for manual
+builds is
+/usr/local, as set by the prefix variable in the top level Makefile
+(/usr is the default for RPMs.)
+
+<h2>Building GridSite with RPM</h2>
+
+<p>
+For RedHat Linux and derivatives, building with RPM is recommended.
+The command <b>make rpm</b> in the top level of the source tree
+will build the GridSite and htcp binary RPMs in the
+directory ../RPMTMP/RPMS/i386 relative to the working directory. An SRPM is
+put into ../RPMTMP/SRPMS
+This build assumes the Apache 2.0 includes are in /usr/include/httpd.
+
+<p>
+<b>If you make RPMs on a RedHat 9 system (or a 7.3 system with our httpd RPM
+installed), you can install the resulting GridSite
+RPM alongside the standard Apache 2.0 RPM without having to
+modify shared library or Apache module paths.</b>
+
+<p>
+For other configurations,
+you can modify the assumed location of the Apache 2.0 includes
+by changing the MYCFLAGS variable in the rpm target near the
+foot of the top level Makefile.
+
+<h2>Building Apache 2.0</h2>
+
+<p>
+If it is not possible to use binary RPMs of Apache 2.0,
+then it can be built from source using the build-apache2.sh script
+found in the GridSite docs directory.
+The script includes instructions on how to build from the tarballs
+distributed by the Apache Foundation.
+(it removes the -C option from "configure -C" in the .spec file
+and builds the RPMs under the current directory.)
+
+<p>
+If these targets do not work on your build platform,
+the Makefile and the scriptlets in the included SPEC files are a good
+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
+Discussion List</a>, although
+<a href="http://www.apache.org/">www.apache.org</a> is a better starting
+point for purely Apache problems.
+
+</body>
--- /dev/null
+library docs
--- /dev/null
+<title>GridSite Apache module: mod_gridsite</title>
+<body>
+<h1 align=center>GridSite Apache module: mod_gridsite</h1>
+
+<p>
+mod_gridsite is an Apache 2.0 module which enforces access control via Grid
+Access Control Lists, and X.509, GSI or VOMS credentials. mod_gridsite also
+gives Apache built-in support for the HTTP PUT and DELETE methods, and
+formatting of HTML pages with standard headers and footers.
+
+<p>
+Since mod_gridsite access
+control within Apache itself, Grid authorization and
+the associated verified credentials are available to all technologies
+supported by Apache, including static file serving, SSI, CGI, PHP, mod_perl
+and Java servlets via a connector to Tomcat.
+
+<p>
+Operation of mod_gridsite can be configured using runtime directives
+in Apache's standard httpd.conf configuration file. The module must first be
+loaded with a LoadModule directive:
+
+<p>
+<b>LoadModule gridsite_module /PATH/TO/MODULES/mod_gridsite.so</b>
+
+<p>
+The module's behaviour is then controlled by GridSite... directives within
+Apache <Directory ...> sections, allowing different directories to use
+GridSite features in different ways.
+
+<h2>GridSite directives</h2>
+
+<dl>
+<dt><b>GridSiteIndexes on|off</b>
+<dd>Determines whether GridSite generates HTML directory listings. These
+ have some advantages over standard Apache directory listings (eg the
+ displayed filenames are never truncated) and will include standard
+ headers and footers if GridSiteHtmlFormat is on.
+ <br>
+ (Default: GridSiteIndexes off)
+<p>
+
+<dt><b>GridSiteIndexHeader file</b>
+<dd>If the named file is found in the directory being listed, the file
+ is included verbatim at the top of the listing and excluded from
+ the file-by-file listing. The file can either be HTML or plain text (in
+ which case browsers will be treat it as one HTML paragraph.)
+ <br>
+ (Default: none)
+<p>
+
+<dt><b>GridSiteHtmlFormat on|off</b>
+<dd>Determines where HTML pages receive additional formatting before being
+ sent to the client. This includes the "Last modified",
+ "View page history", "Switch to HTTP(S)",
+ "Print View" and "Built with GridSite" footer
+ elements. If header and footer files are found, they will be used too.
+ <br>
+ (Default: GridSiteHtmlFormat off)
+<p>
+
+<dt><b>GridSiteHeadFile file</b><br>
+ <b>GridSiteFootFile file</b>
+<dd>Set the filenames to be searched for as standard headers and footers
+ for HTML pages. For each HTML page, the directory of that page is tried
+ first, and then parent directories in ascending order until a header /
+ footer file is found. Header files are inserted in place of HTML
+ <body[ ...]> tags; footer files in place of </body>. (These
+ standard files should each include the appropriate body tag as a
+ replacement.)
+ <br>
+ (Defaults: GridSiteHeadFile gridsitehead.txt,
+ GridSiteFootFile gridsitefoot.txt)
+<p>
+
+<dt><b>GridSiteAuth on|off</b>
+<dd>Enables GridSite access control features, using
+ <a href="gacl.html">GACL</a> files. The files are named .gacl and are
+ per-directory. The current directory is tried and then parent
+ directories in ascending order until a .gacl file is found.
+ <br>
+ (Default: GridSiteAuth off)
+<p>
+
+<dt><b>GridSiteAdminList uri</b>
+<dd>All members of the DN List with name "uri" receive the full set
+ of permissions, irrespective of per-directory .gacl files. People in
+ this group have full control over the whole site.
+ <br>
+ (Default: none)
+<p>
+
+<dt><b>GridSiteGSIProxyLimit limit</b>
+<dd>When using GSI Proxy credentials,
+ proxies with delegation depth greater than "limit" will
+ be ignored by mod_gridsite authorization decisions. A limit of zero
+ implies only full X.509
+ certificates (and no proxies) will be accepted. A limit of 1 implies
+ that only the initial proxy, usually created on the user's own machine,
+ is acceptable. Higher levels lead to proxies on remote machines, eg
+ used by running jobs, being accepted.
+ <br>
+ (Default: GridSiteGSIProxyLimit 1)
+<p>
+
+<dt><b>GridSiteMethods [GET] [PUT] [DELETE]</b>
+<dd>Specifies which HTTP methods are supported by GridSite. GET (and HEAD)
+ are always supported. PUT and DELETE support is turned on by this
+ directive, subject to a positive statement that write permission is
+ allowed for the directory in question, by a GACL file.
+ <br>
+ (Default: GridSite GET)
+<p>
+
+<dt><b>GridSiteDNlists directory1[:directory2[:directory3]...]</b>
+<dd>Sets up the DN List path used by <a href="gacl.html">GACL</a> for
+ evaluating <dn-list> credentials. If this directive is not used,
+ then GACL will use the GRST_DN_LISTS variable from Apache's own
+ environment. If that is not set either, then /etc/grid-security/dn-lists
+ is searched.
+ <br>
+ (Default: none)
+<p>
+
+<dt><b>GridSiteDNlistsURI uri</b>
+<dd>If GridSiteDNlistsURI is used, then the URI given appears to be
+ populated with all the DN lists on the current DN lists path which
+ match the current server. That is, for server https://example.org/
+ with DN lists URI /dn-lists/, all DN lists with URLs starting
+ https://example.org/dn-lists/ will appear to be present in /dn-lists/,
+ irrespective of where in the path they are stored.
+ <br>
+ (Default: none)
+<p>
+
+<dt><b>GridSiteAdminURI uri</b>
+<dd>GridSiteAdminURI gives the absolute URI on the server of the GridSite
+ Admin CGI program, which is used for file management, HTML and GACL
+ editing. This should be used in conjunction with the standard Apache
+ directive ScriptAlias to map that URI to the real-gridsite-admin.cgi
+ executable. For example:
+ <br>
+ <b>ScriptAlias /real-gridsite-admin.cgi
+ /PATH/TO/real-gridsite-admin.cgi</b>
+ <br>
+ This URI is always reached by an internal redirection from the value
+ set by GridSiteAdminFile, and is never visible to users.
+ <br>
+ (Default: none)
+<p>
+
+<dt><b>GridSiteAdminFile cgifilename</b>
+<dd>If GridSiteAdminURI is set, then the cgifilename of GridSiteAdminFile
+ appears to be present in all directories when explicitly
+ requested (it does not appear in directory listings.) Requests for these
+ ghost CGI URIs are internally redirected to the value set by
+ GridSiteAdminURI.
+ <br>
+ (Default: GridSiteAdminFile gridsite-admin.cgi)
+<p>
+
+<dt><b>GridSiteEnvs on|off</b>
+<dd>This makes mod_gridsite export several variables into the environment
+ of CGI programs and other dynamic content systems. The variable names
+ are listed below. For gridsite-admin.cgi mechanism to work, this switch
+ must be left in its default state of on.
+ <br>
+ (Default: GridSiteEnvs on)
+<p>
+
+<dt><b>GridSiteEditable [ext1 [ext2 [ext3] ...]]]</b>
+<dd>A space-separated list of file extensions which can safely be edited
+ by the GridSite Text/HTML editor. The extensions are given without the
+ initial dot.
+ <br>
+ (Default: GridSiteEditable txt shtml html htm css js php jsp)
+<p>
+
+<dt><b>GridSiteHelpURI uri</b>
+<dd>If set, gives the URI to use for "Website Help" links in HTML
+ page footers.
+ <br>
+ (Default: none)
+<p>
+
+<dt><b>GridSiteLink on|off</b>
+<dd>Turns off the link in the HTML page footers which gives credit to
+ GridSite.
+ <br>
+ (Default: GridSiteLink on)
+<p>
+
+<dt><b>GridSiteUnzip path</b>
+<dd>If "path" is set by this directive, then real-gridsite-admin.cgi
+ will offer to list the contents of .zip archives on the server.
+ Users with write access are able to unpack the contents into the same
+ directory as the .zip file. The value of "path" must point
+ to the location of the
+ <a href="http://www.info-zip.org/UnZip.html">unzip</a> binary.
+ <br>
+ (Default: none)
+<p>
+
+</dl>
+
+<h2>Environment variables</h2>
+
+<p>
+The following variables are present in the environment of CGI programs and
+other dynamic content systems if the <b>GridSiteEnvs on</b> directive is
+in effect.
+
+<p>
+<dl>
+<dt><b>GRST_PERM</b>
+<dd>Numerical value of the permission bit-map obtained by comparing the
+ user with the GACL in force. (These should be tested using the
+ GRSTgaclPermHasXXXX functions from GACL.)
+<p>
+
+<dt><b>GRST_ADMIN_LIST</b>
+<dd>URI of the DN List, listing people with full admin and write access
+ to the whole site.
+<p>
+
+<dt><b>GRST_GSIPROXY_LIMIT</b>
+<dd>Maximum valid delegation level for GSI Proxies.
+<p>
+
+<dt><b>GRST_DIR_PATH</b>
+<dd>Absolute path in the local filesystem to the directory holding the
+ file being requested.
+<p>
+
+<dt><b>GRST_HELP_URI</b>
+<dd>URI of website help pages set by GridSiteHelpURI directive.
+<p>
+
+<dt><b>GRST_ADMIN_FILE</b>
+<dd>Filename of per-directory ghost gridsite-admin.cgi program. (This is
+ used by real-gridsite-admin.cgi to construct links in its pages.)
+<p>
+
+<dt><b>GRST_EDITABLE</b>
+<dd>Space-separated list of extensions which can safely be edited with a
+ Text/HTML editor.
+<p>
+
+<dt><b>GRST_HEAD_FILE</b> and <b>GRST_FOOT_FILE</b>
+<dd>Filenames of standard header and footer files.
+<p>
+
+<dt><b>GRST_DN_LISTS</b>
+<dd>DN lists search path.
+<p>
+
+<dt><b>GRST_DN_LISTS_URI</b>
+<dd>Directory of virtual URIs used to publish this site's DN Lists.
+<p>
+
+<dt><b>GRST_UNZIP</b>
+<dd>Full path to the unzip binary, used to list and unpack .zip files.
+<p>
+
+<dt><b>GRST_NO_LINK</b>
+<dd>If set, do not include credit links to GridSite in page footers.
+<p>
+
+</dl>
+
+</body>
--- /dev/null
+.TH urlencode 1 "November 2003" urlencode "URLENCODE Manual"
+.SH NAME
+.B urlencode
+\- convert strings to or from URL-encoded form
+.SH SYNOPSIS
+.B urlencode
+[-m|-d]
+.I string [string ...]
+.SH DESCRIPTION
+.B urlencode
+encodes strings according to RFC 1738.
+
+That is, characters A-Z a-z 0-9 . _
+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
+
+.B urlencode
+converts each character in all the strings given on the command line. If
+multiple strings are given, they are concatenated with separating spaces
+before conversion.
+
+.SH OPTIONS
+.IP "-m"
+Instead of full conversion, do GridSite "mild URL encoding" in which A-Z a-z
+0-9 . = - _ @ and / are passed through unmodified. This results in slightly
+more human-readable strings but the application must be prepared to create
+or simulate the directories implied by any slashes.
+
+.IP "-d"
+Do URL-decoding rather than encoding, according to RFC 1738. %HH and %hh
+strings are converted and other characters are passed through unmodified,
+with the exception that + is converted to space.
+
+.SH EXIT CODES
+0 is always returned.
+
+.SH BUGS
+Not enough beta testing (hint hint...)
+
+.SH AUTHOR
+Andrew McNab <mcnab@hep.man.ac.uk>
+
+urlencode is part of GridSite: http://www.gridpp.ac.uk/gridsite/
--- /dev/null
+<title>GridSite User Guide</title>
+<body>
+<h1 align=center>GridSite User Guide</h1>
+
+<p><i>If you are setting up a GridSite-based website you may wish to use this
+file as the basis of your end-user documentation. If so, copy all of the
+files from the GridSite doc directory (probably
+<small>/usr/share/doc/gridsite-VERSION/</small>)
+to somewhere on your website like
+<b>/gridsite-doc/</b> and add <b>GridSiteHelpURI /gridsite-doc/user.html</b>
+to the virtual server configuration in
+httpd.conf - you should also look through the rest of the HTML source since
+there are some comments you may find helpful.</i>
+
+<p>
+This Guide is intended for people using GridSite websites with conventional
+web browsers, especially people with write access to areas of the site.
+ There is a separate
+<a href="admin.html">Administration Guide</a>
+ with additional information for people managing access control and group
+membership. This Guide assumes you are familiar with basic Web and HTML
+concepts. Towards the end we discuss how to access servers with command
+line tools like curl and htcp.
+
+<h2>Reading from HTTP and HTTPS servers</h2>
+
+<p>
+GridSite servers are usually accessible both via HTTP and via HTTPS. You can
+always tell which version you are using by looking at whether the URL in your
+browser's location window starts with "http://" or
+"https://" HTTPS means that the connection to the server is
+encrypted, that you can verify you're talking to the real server and not an
+imposter, and gives you the option to authenticate to the site and perhaps
+gain write access.
+
+<p>
+ Simple browsing of the website via HTTP or HTTPS is reasonably
+ self-explanatory. If configured, additional links may appear in the footer
+ of each webpage with links to this help,
+<!-- if GridSiteHelpURI uri is set -->
+ and to switch between HTTP and HTTPS versions of the page. Pages may also
+ have a link to the page History,
+<!-- GridSiteAdminURI uri must be set and gridsite-admin.cgi working for
+ the history-viewing mechanism to work -->
+ showing the dates of changes to that page and names of its authors.
+
+<p>
+ When looking at HTTPS pages, you may find your browser reports it cannot
+ verify the server's certificate since it does not recognise the
+ Certification Authority (CA) it uses. You should attempt to load the CA's
+ root certificate into your browser to stop these warnings. (This means your
+ browser will be able to identify any servers using fake certificates which
+ you shouldn't trust.) How you obtain the CA Root Certificate from a
+ trust-worthy source depends on the CA. For example, the UK e-Science CA
+ lets you download it <a href="http://ca.grid-support.ac.uk/">from their
+ website</a>.
+<!-- if most of your users use one or two CAs, you could add a link to their
+CA root cert loading instructions here -->
+
+<h2>Authenticating</h2>
+
+<p>
+ To go beyond reading pages you need to obtain a user certificate and load it
+ into your web browser. How you do this again depends on the Certification
+ Authority you have access to (for most Grid projects, CAs are organised
+ on a national basis.) To use the UK e-Science CA example again,
+ <a href="http://ca.grid-support.ac.uk/">from their website</a> has links to
+ the procedure for applying for a certificate from within a web browser.
+<!-- again, a link to your CA would be good here -->
+
+<p>
+A user certificate usually has a version of your name and affiliation as its
+Distinguished Name (DN) - for example,
+"/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab"
+
+<p>
+Once you've obtained a user certificate in your name from your CA, you need
+to make sure it is loaded into the browser you normally use to browse the
+web. How you do this is different for different browsers and to some extent
+for different CAs (but if you applied
+for the CA through your browser, you may already have it there.)
+
+<p>
+Browsers want the certificate and private key in the PKCS#12 format, which
+is normally a single file with the extension ".p12".
+Many programs which are based on OpenSSL, such as Globus and curl, prefer
+the PEM (".pem") format for certificates, with separate
+certificate and key files ("usercert.pem" and
+"userkey.pem", for example.) If you only have the files in .pem
+format and have access to openssl, you
+can use its command line tools to convert PEM to PKCS#12:
+<pre>
+openssl pkcs12 -in usercert.pem -inkey userkey.pem -export -out certkey.p12
+</pre>
+
+<p>
+<b>Be very careful not to accidentally overwrite .pem or .p12 files when
+doing this kind of thing! In particular, if you lose your private key, you
+cannot retrieve it from your CA.</b>
+
+<p>
+ Once your user certificate is loaded, you should be able to see your
+ certificate name appear when you look at an HTTPS GridSite page which has
+ the page footers enabled - for example, the "Switch to HTTP" link
+ present. If GridSite understands your user certificate, it displays a
+ "You are ..." line in the footer. (However, the Apache webserver
+ must also be set up with your CAs root certificate for this to work. The
+ <a href="https://www.gridpp.ac.uk/">GridPP HTTPS home page</a> is set up
+ to recognise a good range of European and North American Grid CAs.)
+<!-- the CA root certificates normally go somewhere like
+/etc/grid-security/certificates and httpd.conf should reflect this with the
+directive SSLCACertificatePath /etc/grid-security/certificates -->
+
+<h2>Authorization</h2>
+
+<p>
+ Once users can prove their identity to the web server, it then becomes
+ possible to give them appropriate rights depending on that identity.
+ GridSite allows site administrators to specify these rights for individuals
+ and groups using
+<a href="gacl.html">GACL</a>
+ access control files. (The
+<a href="admin.html">Administration Guide</a>
+ explains how to manage these files.) GACL defines who can
+ read files, who can list directories,
+ who can write or create files and who can modify the GACL policy files. To
+ get increased access to an area of a site, you need to contact the
+ administrator for that area and give the DN of your certificate (it's not
+ necessary to send any certificate files.)
+
+<h2>Managing Directories and Files</h2>
+
+<p>
+If you have list permission for the directory containing a page, you should
+see an extra link "Manage Directory" in the page's set of footer
+links, which allows you to browse the directory even if the normal
+index.html is present. If page histories are available, this listing view
+also has links to them.
+
+<p>
+The real power of GridSite becomes available if you have write access to a
+directory. In that case, the "Manage Directory" page has
+additional links to Delete or Rename pages and other files, and to Edit HTML
+and plain text files. An Edit link also appears in the footer links of HTML
+pages.
+
+<p>
+If you use the Edit function, you are presented with an HTML form containing
+the current filename and the full HTML or plain text of the page for you to
+edit. This allows you to maintain the content of the site "in
+place" and to see the result of your changes immediately, in context.
+
+<p>
+If you modify the filename in the form before saving, GridSite will make a
+new file with that name, and the old file will still be present, unmodified.
+(However, you cannot use this feature for creating a file in a different
+directory.)
+As you make changes, the history of the changes and your certificate DN are
+recorded, and available in the history page for that file.
+
+<p>
+ For people with write access, the "Manage Directory" page also has
+ options to upload a file from the computer your browser is running on, and to
+ create files and directories. If it's enabled, you can also view the
+ contents of WinZIP / PKZIP / .zip files, and unpack their contents into the
+ current directory. (This feature is very useful if you have several files
+ to upload at one time.)
+<!-- This needs the GridSiteUnzip path directive in httpd.conf -->
+
+<h2>HTML Formatting in GridSite</h2>
+
+<p>
+As well as providing access control and file management, GridSite provides
+some simple formatting of HTML pages by adding standard headers and footers.
+(If this isn't sufficient, GridSite will happily coexist with HTML
+preprocessor languages like SSI, PHP and JSP.)
+
+<p>
+ If HTML formatting is enabled
+ for the current directory, GridSite looks for the files gridsitehead.txt and
+ gridsitefoot.txt in that directory, or goes up through the parent
+ directories until they are found.
+<!-- GridSiteHtmlFormat on turns this on and GridSiteHeadFile file and
+GridSiteFootFile file can change the names of the header and footer. If
+you change from the defaults, you need to change this paragraph. -->
+
+<p>
+The <body> and </body> tags from the HTML file are replaced with
+the contents of the gridsitehead.txt and gridsitefoot.txt files, which
+should normally be chunks of HTML including a replacement <body>
+or </body> tag. If either tag is absent from the original page, then
+the header or footer is just added rather than being inserted in place of
+the tag. (One consequence of this absence is that HTML header tags like
+<title> can end up after a <body> tag, and can get ignored by
+browsers - so always include <body> ... </body> in your pages.)
+
+<p>
+This simple system is suprisingly flexible, and allows a variety of top and
+bottom, or sidebar navigation layouts of pages. Since the <body ...>
+tag is under full control of the author of the gridsitehead.txt file,
+backgrounds, colour schemes and style sheets can easily be specified.
+
+<p>
+For example:
+
+<p>
+<table border=1 cellpadding=3>
+<tr><th>Source</th><th>HTML</th></tr>
+<tr><td>page.html</td><td><title>PAGE TITLE</title></td></tr>
+<tr><td>page.html<br>(replaced)</td><td><body></td></tr>
+<tr><td>gridsitehead.txt</td>
+ <td><body text=blue><br>
+ Heading text<br>
+ <table border=1><br><tr><br><td>Standard<br><br>
+ sidebar</td><br><td></td></tr>
+<tr><td>page.html</td><td><p><br>Page content...</td></tr>
+<tr><td>page.html<br>(replaced)</td><td></body></td></tr>
+<tr><td>gridsitefoot.txt</td><td></td><br></tr><br>
+ </table><br>Footer text<br></body></td></tr>
+</table>
+
+<p>
+produces pages with a layout like:
+
+<p>
+<table border=1 cellpadding=3>
+<tr><td colspan=2>Heading text</td></tr>
+<tr><td>Standard<br>sidebar</td><td>Page content...</td><tr>
+<tr><td colspan=2>Footer text</td></tr>
+</table>
+
+<h2>Command line use</h2>
+
+<p>
+GridSite adds support for the HTTP PUT and DELETE methods, and this makes it
+easy to create or delete files from within programs and commands without
+using a web browser and HTML forms. It is straightforward, although slightly
+awkward, to use a standard HTTPS-aware client like
+<a href="http://curl.haxx.se/">curl</a> to upload files, but GridSite
+provides htcp as a more convenient client program, which is easier to use
+with GSI Proxies and X.509 user certificates, and has a syntax closer to the
+familiar scp command.
+
+<p>
+The following examples assume the GridSite server has GSI support and use a
+GSI proxy as the client certificate. For non-GSI use, just skip the
+grid-proxy-init stage, and replace the proxy
+filename with $HOME/.globus/usercert.pem and $HOME/.globus/userkey.pem (or
+wherever your PEM format certificate and key are stored.)
+
+<p>
+First generate a GSI proxy with grid-proxy-init. This will create a proxy file
+in /tmp/x509up_uXXXXX where XXXXX is your Unix UID (also given by <b>id
+-u</b>.) The GSI proxy contains a
+temporary private key and certificate signed by your long-term user
+certificate.
+
+<p>
+You should make sure you have a copy of the CA root certificates of the CA's
+used by the servers you wish to talk to. These are usually installed in
+/etc/grid-security/certificates as files like 01621954.0, and RPMs and tar
+files for many common European and North American CAs are available from
+<a href="https://datagrid.in2p3.fr/distribution/datagrid/security/">
+https://datagrid.in2p3.fr/distribution/datagrid/security/</a>
+
+<p>
+To upload a file with curl:
+<pre>
+curl --cert /tmp/x509up_u`id -n` --key /tmp/x509up_u`id -n` \
+ --capath /etc/grid-security/certificates \
+ --upload-file /tmp/new.file.txt https://server/new.file.txt
+</pre>
+
+<p>
+The equivalent htcp command is:
+<pre>
+htcp /tmp/new.file.txt https://server/new.file.txt
+</pre>
+since htcp looks for the GSI proxy and CA certificates automatically. htcp
+can also be used to copy remote files to the local machine by reversing the
+arguments. For more details, see the
+<a href="htcp.1.html">htcp(1)</a> man page.
+
+<p>
+htcp also has options for deleting files, and doing short or long listings,
+and these can also be accessed using the htrm, htls and htll commands (which
+are normally symbolic links to htcp.)
+
+<p>
+Directory indexes are based on parsing the index returned by the web server
+and by using the HTTP HEAD method to obtain the file size and modification
+times.
+
+<p>
+All of the ht** commands can accept multiple source file arguments, and this
+allows you to copy multiple files to or from the server. Shell wildcard
+expansion on the local machine is especially useful:
+<pre>
+htcp /tmp/new.*.txt https://server/
+</pre>
+
+</body>
--- /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.
+*/
+
+/*------------------------------------------------------------------------*
+ * For more about GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+#ifndef HEADER_GACL_H
+#define HEADER_GACL_H
+#endif
+
+#ifndef GACL_LIB_VERSION
+#define GACL_LIB_VERSION "x.x.x"
+#endif
+
+typedef GRSTgaclCred GACLcred;
+
+typedef int GACLaction;
+typedef unsigned int GACLperm;
+
+typedef GRSTgaclEntry GACLentry;
+
+typedef GRSTgaclAcl GACLacl;
+
+typedef GRSTgaclUser GACLuser;
+
+extern char *gacl_perm_syms[];
+extern GACLperm gacl_perm_vals[];
+
+#define GACL_PERM_NONE GRST_PERM_NONE
+#define GACL_PERM_READ GRST_PERM_READ
+#define GACL_PERM_LIST GRST_PERM_LIST
+#define GACL_PERM_WRITE GRST_PERM_WRITE
+#define GACL_PERM_ADMIN GRST_PERM_ADMIN
+
+#define GACLhasNone(perm) (perm == 0)
+#define GACLhasRead(perm) ((perm & GRST_PERM_READ) != 0)
+#define GACLhasList(perm) ((perm & GRST_PERM_LIST) != 0)
+#define GACLhasWrite(perm) ((perm & GRST_PERM_WRITE) != 0)
+#define GACLhasAdmin(perm) ((perm & GRST_PERM_ADMIN) != 0)
+
+#define GACL_ACTION_ALLOW GRST_ACTION_ALLOW
+#define GACL_ACTION_DENY GRST_ACTION_DENY
+
+#define GACL_ACL_FILE GRST_ACL_FILE
+#define GACL_DN_LISTS GRST_DN_LISTS
+
+#define GACLinit() GRSTgaclInit()
+
+#define GACLnewCred(x) GRSTgaclCredNew((x))
+//GACLcred *GACLnewCred(char *);
+
+#define GACLaddToCred(x,y,z) GRSTgaclCredAddValue((x),(y),(z))
+//int GACLaddToCred(GACLcred *, char *, char *);
+
+#define GACLfreeCred(x) GRSTgaclCredFree((x))
+//int GACLfreeCred(GACLcred *);
+
+#define GACLaddCred(x,y) GRSTgaclEntryAddCred((x),(y))
+//int GACLaddCred(GACLentry *, GACLcred *);
+
+#define GACLdelCred(x,y) GRSTgaclEntryDelCred((x),(y))
+//int GACLdelCred(GACLentry *, GACLcred *);
+
+#define GACLprintCred(x,y) GRSTgaclCredPrint((x),(y))
+// int GACLprintCred(GACLcred *, FILE *);
+
+
+#define GACLnewEntry() GRSTgaclEntryNew()
+// GACLentry *GACLnewEntry(void);
+
+#define GACLfreeEntry(x) GRSTgaclEntryFree((x))
+// int GACLfreeEntry(GACLentry *);
+
+#define GACLaddEntry(x,y) GRSTgaclAclAddEntry((x),(y))
+// int GACLaddEntry(GACLacl *, GACLentry *);
+
+#define GACLprintEntry(x,y) GRSTgaclEntryPrint((x),(y))
+// int GACLprintEntry(GACLentry *, FILE *);
+
+
+#define GACLprintPerm(x,y) GRSTgaclPermPrint((x),(y))
+//int GACLprintPerm(GACLperm, FILE *);
+
+#define GACLallowPerm(x,y) GRSTgaclEntryAllowPerm((x),(y))
+// int GACLallowPerm(GACLentry *, GACLperm);
+
+#define GACLunallowPerm(x,y) GRSTgaclEntryUnallowPerm((x),(y))
+//int GACLunallowPerm(GACLentry *, GACLperm);
+
+#define GACLdenyPerm(x,y) GRSTgaclEntryDenyPerm((x),(y))
+// int GACLdenyPerm(GACLentry *, GACLperm);
+
+#define GACLundenyPerm(x,y) GRSTgaclEntryUndenyPerm((x),(y))
+// int GACLundenyPerm(GACLentry *, GACLperm);
+
+#define GACLpermToChar(x) GRSTgaclPermToChar((x))
+// char *GACLpermToChar(GACLperm);
+
+#define GACLcharToPerm(x) GRSTgaclPermFromChar((x))
+// GACLperm GACLcharToPerm(char *);
+
+#define GACLnewAcl() GRSTgaclAclNew()
+// GACLacl *GACLnewAcl(void);
+
+#define GACLfreeAcl(x) GRSTgaclAclFree((x))
+// int GACLfreeAcl(GACLacl *);
+
+#define GACLprintAcl(x,y) GRSTgaclAclPrint((x),(y))
+// int GACLprintAcl(GACLacl *, FILE *);
+
+#define GACLsaveAcl(x,y) GRSTgaclAclSave((y),(x))
+// int GACLsaveAcl(char *, GACLacl *);
+
+#define GACLloadAcl(x) GRSTgaclAclLoadFile((x))
+// GACLacl *GACLloadAcl(char *);
+
+#define GACLfindAclForFile(x) GRSTgaclFileFindAclname((x))
+// char *GACLfindAclForFile(char *);
+
+#define GACLloadAclForFile(x) GRSTgaclAclLoadforFile((x))
+// GACLacl *GACLloadAclForFile(char *);
+
+#define GACLisAclFile(x) GRSTgaclFileIsAcl((x))
+// int GACLisAclFile(char *);
+
+
+#define GACLnewUser(x) GRSTgaclUserNew((x))
+// GACLuser *GACLnewUser(GACLcred *);
+
+#define GACLfreeUser(x) GRSTgaclUserFree((x))
+// int GACLfreeUser(GACLuser *);
+
+#define GACLuserAddCred(x,y) GRSTgaclUserAddCred((x),(y))
+// int GACLuserAddCred(GACLuser *, GACLcred *);
+
+#define GACLuserHasCred(x,y) GRSTgaclUserHasCred((x),(y))
+// int GACLuserHasCred(GACLuser *, GACLcred *);
+
+#define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y))
+// GACLcred *GACLuserFindCredType(GACLuser *, char *);
+
+#define GACLtestDnList(x,y) GRSTgaclDNlistHasUser((x),(y))
+// int GACLtestDnList(char *, GACLuser *);
+
+#define GACLtestUserAcl(x,y) GRSTgaclAclTestUser((x),(y))
+// GACLperm GACLtestUserAcl(GACLacl *, GACLuser *);
+
+#define GACLtestExclAcl(x,y) GRSTgaclAclTestexclUser((x),(y))
+// GACLperm GACLtestExclAcl(GACLacl *, GACLuser *);
+
+
+#define GACLurlEncode(x) GRSThttpUrlEncode((x))
+// char *GACLurlEncode(char *);
+
+#define GACLmildUrlEncode(x) GRSThttpUrlMildencode((x))
+// char *GACLmildUrlEncode(char *);
+
+GACLparseEntry *GRSTgaclEntryParse(xmlNodePtr cur);
+// special function for legacy EDG LB service
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*------------------------------------------------------------------------*
+ * For more about GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+#ifndef HEADER_SSL_H
+#include <ssl.h>
+#endif
+
+#ifndef HEADER_CRYPTO_H
+#include <crypto.h>
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+/// Everything ok (= OpenSSL X509_V_OK)
+#define GRST_RET_OK 0
+
+/// Failed for unspecified reason
+#define GRST_RET_FAILED 1000
+
+/// Failed to find certificate in some cert store / directory
+#define GRST_RET_CERT_NOT_FOUND 1001
+
+/// Bad signature
+#define GRST_RET_BAD_SIGNATURE 1002
+
+/// No such file or directory
+#define GRST_RET_NO_SUCH_FILE 1003
+
+#define GRST_PROXYCERTINFO_OID "1.3.6.1.4.1.3536.1.222"
+#define GRST_VOMS_OID "1.3.6.1.4.1.8005.100.100.1"
+#define GRST_VOMS_DIR "/etc/grid-security/vomsdir"
+
+typedef struct { char *name;
+ char *value;
+ struct _GRSTgaclNamevalue *next; } GRSTgaclNamevalue;
+
+typedef struct { char *type;
+ int delegation;
+ GRSTgaclNamevalue *firstname;
+ void *next; } GRSTgaclCred;
+
+typedef int GRSTgaclAction;
+typedef unsigned int GRSTgaclPerm;
+
+typedef struct { GRSTgaclCred *firstcred;
+ GRSTgaclPerm allowed;
+ GRSTgaclPerm denied;
+ void *next; } GRSTgaclEntry;
+
+typedef struct { GRSTgaclEntry *firstentry; } GRSTgaclAcl;
+
+typedef struct { GRSTgaclCred *firstcred;
+ char *dnlists; } GRSTgaclUser;
+
+#define GRST_PERM_NONE 0
+#define GRST_PERM_READ 1
+#define GRST_PERM_EXEC 2
+#define GRST_PERM_LIST 4
+#define GRST_PERM_WRITE 8
+#define GRST_PERM_ADMIN 16
+#define GRST_PERM_ALL 31
+
+/* DO NOT USE PermIsNone!! */
+#define GRSTgaclPermIsNone(perm) (perm == 0)
+
+#define GRSTgaclPermHasNone(perm) (perm == 0)
+#define GRSTgaclPermHasRead(perm) ((perm & GRST_PERM_READ ) != 0)
+#define GRSTgaclPermHasExec(perm) ((perm & GRST_PERM_EXEC ) != 0)
+#define GRSTgaclPermHasList(perm) ((perm & GRST_PERM_LIST ) != 0)
+#define GRSTgaclPermHasWrite(perm) ((perm & GRST_PERM_WRITE) != 0)
+#define GRSTgaclPermHasAdmin(perm) ((perm & GRST_PERM_ADMIN) != 0)
+
+#define GRST_ACTION_ALLOW 0
+#define GRST_ACTION_DENY 1
+
+#define GRST_HIST_PREFIX ".grsthist"
+#define GRST_ACL_FILE ".gacl"
+#define GRST_DN_LISTS "/etc/grid-security/dn-lists"
+#define GRST_RECURS_LIMIT 9
+
+int GRSTgaclInit(void);
+
+// #define GACLnewCred(x) GRSTgaclCredNew((x))
+GRSTgaclCred *GRSTgaclCredNew(char *);
+
+// #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))
+int GRSTgaclCredFree(GRSTgaclCred *);
+
+// #define GACLaddCred(x,y) GRSTgaclEntryAddCred((x),(y))
+int GRSTgaclEntryAddCred(GRSTgaclEntry *, GRSTgaclCred *);
+
+// #define GACLdelCred(x,y) GRSTgaclEntryDelCred((x),(y))
+int GRSTgaclEntryDelCred(GRSTgaclEntry *, GRSTgaclCred *);
+
+// #define GACLprintCred(x,y) GRSTgaclCredPrint((x),(y))
+int GRSTgaclCredCredPrint(GRSTgaclCred *, FILE *);
+
+
+// #define GACLnewEntry(x) GRSTgaclEntryNew((x))
+GRSTgaclEntry *GRSTgaclEntryNew(void);
+
+// #define GACLfreeEntry(x) GRSTgaclEntryFree((x))
+int GRSTgaclEntryFree(GRSTgaclEntry *);
+
+// #define GACLaddEntry(x,y) GRSTgaclAclAddEntry((x),(y))
+int GRSTgaclAclAddEntry(GRSTgaclAcl *, GRSTgaclEntry *);
+
+// #define GACLprintEntry(x,y) GRSTgaclEntryPrint((x),(y))
+int GRSTgaclEntryPrint(GRSTgaclEntry *, FILE *);
+
+
+// #define GACLprintPerm(x,y) GRSTgaclPermPrint((x),(y))
+int GRSTgaclPermPrint(GRSTgaclPerm, FILE *);
+
+// #define GACLallowPerm(x,y) GRSTgaclEntryAllowPerm((x),(y))
+int GRSTgaclEntryAllowPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+// #define GACLunallowPerm(x,y) GRSTgaclEntryUnallowPerm((x),(y))
+int GRSTgaclEntryUnallowPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+// #define GACLdenyPerm(x,y) GRSTgaclEntryDenyPerm((x),(y))
+int GRSTgaclEntryDenyPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+// #define GACLundenyPerm(x,y) GRSTgaclEntryUndenyPerm((x),(y))
+int GRSTgaclUndenyPerm(GRSTgaclEntry *, GRSTgaclPerm);
+
+// #define GACLpermToChar(x) GRSTgaclPermToChar((x))
+char *GRSTgaclPermToChar(GRSTgaclPerm);
+
+// #define GACLcharToPerm(x) GRSTgaclPermFromChar((x))
+GRSTgaclPerm GRSTgaclPermFromChar(char *);
+
+// #define GACLnewAcl(x) GRSTgaclAclNew((x))
+GRSTgaclAcl *GRSTgaclAclNew(void);
+
+// #define GACLfreeAcl(x) GRSTgaclAclFree((x))
+int GRSTgaclAclFree(GRSTgaclAcl *);
+
+// #define GACLprintAcl(x,y) GRSTgaclAclPrint((x),(y))
+int GRSTgaclAclPrint(GRSTgaclAcl *, FILE *);
+
+// #define GACLsaveAcl(x,y) GRSTgaclAclSave((y),(x))
+int GRSTgaclAclSave(GRSTgaclAcl *, char *);
+
+// #define GACLloadAcl(x) GRSTgaclFileLoadAcl((x))
+GRSTgaclAcl *GRSTgaclAclLoadFile(char *);
+
+// #define GACLfindAclForFile(x) GRSTgaclFileFindAclname((x))
+char *GRSTgaclFileFindAclname(char *);
+
+// #define GACLloadAclForFile(x) GRSTgaclFileLoadAcl((x))
+GRSTgaclAcl *GRSTgaclAclLoadforFile(char *);
+
+// #define GACLisAclFile(x) GRSTgaclFileIsAcl((x))
+int GRSTgaclFileIsAcl(char *);
+
+
+// #define GACLnewUser(x) GRSTgaclUserNew((x))
+GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *);
+
+// #define GACLfreeUser(x) GRSTgaclUserFree((x))
+int GRSTgaclUserFree(GRSTgaclUser *);
+
+// #define GACLuserAddCred(x,y) GRSTgaclUserAddCred((x),(y))
+int GRSTgaclUserAddCred(GRSTgaclUser *, GRSTgaclCred *);
+
+// #define GACLuserHasCred(x,y) GRSTgaclUserHasCred((x),(y))
+int GRSTgaclUserHasCred(GRSTgaclUser *, GRSTgaclCred *);
+
+int GRSTgaclUserSetDNlists(GRSTgaclUser *, char *);
+
+// #define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y))
+GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *, char *);
+
+// #define GACLtestDnList(x,y) GRSTgaclDNlistHasUser((x),(y))
+int GRSTgaclDNlistHasUser(char *, GRSTgaclUser *);
+
+// #define GACLtestUserAcl(x,y) GRSTgaclAclTestUser((x),(y))
+GRSTgaclPerm GRSTgaclAclTestUser(GRSTgaclAcl *, GRSTgaclUser *);
+
+// #define GACLtestExclAcl(x,y) GRSTgaclAclTestexclUser((x),(y))
+GRSTgaclPerm GRSTgaclAclTestexclUser(GRSTgaclAcl *, GRSTgaclUser *);
+
+
+char *GRSThttpUrlDecode(char *);
+
+// #define GACLurlEncode(x) GRSThttpUrlEncode((x))
+char *GRSThttpUrlEncode(char *);
+
+// #define GACLmildUrlEncode(x) GRSThttpMildUrlEncode((x))
+char *GRSThttpUrlMildencode(char *);
+
+int GRSTx509NameCmp(char *, char *);
+
+int GRSTx509KnownCriticalExts(X509 *);
+
+time_t GRSTasn1TimeToTimeT(char *);
+int GRSTx509IsCA(X509 *);
+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 *);
+GRSTgaclCred *GRSTx509CompactToCred(char *);
+int GRSTx509CompactCreds(int *, int, size_t, char *, STACK_OF(X509) *, char *);
+
+
+#define GRST_HEADFILE "gridsitehead.txt"
+#define GRST_FOOTFILE "gridsitefoot.txt"
+#define GRST_ADMIN_FILE "gridsite-admin.cgi"
+
+typedef struct { char *text;
+ void *next; } GRSThttpCharsList;
+
+typedef struct { size_t size;
+ GRSThttpCharsList *first;
+ GRSThttpCharsList *last; } GRSThttpBody;
+
+void GRSThttpBodyInit(GRSThttpBody *);
+void GRSThttpPrintf(GRSThttpBody *, char *, ...);
+int GRSThttpCopy(GRSThttpBody *, char *);
+void GRSThttpWriteOut(GRSThttpBody *);
+int GRSThttpPrintHeaderFooter(GRSThttpBody *, char *, char *);
+char *GRSThttpGetCGI(char *);
--- /dev/null
+ <!-- ======================================================
+ Define extra properties here ...
+ ====================================================== -->
+
+ <project name="configure options">
+ <property name="build.make.arguments"
+ value="prefix=${stage.abs.dir}"/>
+ </project>
--- /dev/null
+###################################################################
+# System dependencies
+###################################################################
+
+org.glite.version = HEAD
+org.glite.core.version = HEAD
+
+# Component dependencies tag = do not remove this line =
+
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ Copyright (c) 2004 on behalf of the EU EGEE Project:
+ The European Organization for Nuclear Research (CERN),
+ Istituto Nazionale di Fisica Nucleare (INFN), Italy
+ Datamat Spa, Italy
+ Centre National de la Recherche Scientifique (CNRS), France
+ CS Systeme d'Information (CSSI), France
+ Royal Institute of Technology, Center for Parallel Computers (KTH-PDC), Sweden
+ Universiteit van Amsterdam (UvA), Netherlands
+ University of Helsinki (UH.HIP), Finland
+ University of Bergen (UiB), Norway
+ Council for the Central Laboratory of the Research Councils (CCLRC), United Kingdom
+
+ GLite Middleware WMS Configuration Specification File
+
+ Authors: Alberto Di Meglio <alberto.di.meglio@cern.ch>
+ Joachim Flammer <Joachim.Flammer@cern.ch>
+ Version info: $Id$
+ Release: $Name$
+
+ Revision history:
+ $Log$
+ Revision 1.7 2004/10/18 23:01:18 dimeglio
+ Added oscheck to various targets
+
+ Revision 1.6 2004/10/12 14:21:21 eronchie
+ Removed ssl_utils dependency
+
+ Revision 1.5 2004/08/20 09:51:39 eronchie
+ Updated buildmodules orders
+
+ Revision 1.4 2004/08/04 07:30:29 eronchie
+ Added cppunit
+
+ Revision 1.3 2004/07/23 14:50:08 eronchie
+ Added exception
+
+ Revision 1.2 2004/07/23 08:27:03 eronchie
+ Updated
+
+
+-->
+
+<project name="GLite Middleware WMS Utils CSF" default="all">
+
+ <!-- overwrite default workspace directory -->
+ <property name="workspace.dir" value="../.." />
+
+ <!-- ===============================================
+ Load properties
+ =============================================== -->
+
+ <!-- load baseline and user properties -->
+ <import file="${workspace.dir}/org.glite/project/baseline.properties.xml" />
+
+ <!-- define build properties file location since we are already in project dir -->
+ <property name="subsystem.build.properties.file" value="./build.properties" />
+
+ <!-- Load subsytem-specific property files -->
+ <import file="./properties.xml"/>
+
+ <!-- load global properties -->
+ <import file="${global.properties.file}" />
+
+ <!-- ===============================================
+ Load dependencies
+ =============================================== -->
+
+ <!-- Load user dependencies file -->
+ <property file="${user.dependencies.file}" />
+
+ <!-- Load subsystem dependencies file -->
+ <property file="./dependencies.properties" />
+
+ <!-- Load global dependencies file -->
+ <property file="${global.dependencies.file}" />
+
+ <!-- ===============================================
+ Load targets
+ =============================================== -->
+ <import file="${global.targets-envchecks.file}" />
+ <import file="${global.targets-external-dependencies.file}" />
+
+ <!-- ===============================================
+ Evaluate CVS tags
+ =============================================== -->
+
+ <target name="evaluate.cvs.tags" description="Figure out if we need tagged CVS checkout">
+ <condition property="glite.head">
+ <and>
+ <equals arg1="${org.glite.version}" arg2="HEAD" />
+ <or>
+ <istrue value="${update}" />
+ <not>
+ <available file="${global.dependencies.file}" type="file" />
+ </not>
+ </or>
+ </and>
+ </condition>
+ <condition property="glite.tag">
+ <and>
+ <not>
+ <equals arg1="${org.glite.version}" arg2="HEAD" />
+ </not>
+ <or>
+ <istrue value="${update}" />
+ <not>
+ <available file="${global.dependencies.file}" type="file" />
+ </not>
+ </or>
+ </and>
+ </condition>
+ <condition property="glite-wms-utils.head">
+ <and>
+ <equals arg1="${org.glite.wms-utils.version}" arg2="HEAD" />
+ <istrue value="${update}" />
+ </and>
+ </condition>
+ <condition property="glite-wms-utils.tag">
+ <and>
+ <not>
+ <equals arg1="${org.glite.wms-utils.version}" arg2="HEAD" />
+ </not>
+ <istrue value="${update}" />
+ </and>
+ </condition>
+
+ <!-- condition property tag = do not remove = -->
+
+ <condition property="tls.head">
+ <equals arg1="${org.glite.wms-utils.tls.version}" arg2="HEAD" />
+ </condition>
+
+ <condition property="jobid.head">
+ <equals arg1="${org.glite.wms-utils.jobid.version}" arg2="HEAD" />
+ </condition>
+
+ <condition property="exception.head">
+ <equals arg1="${org.glite.wms-utils.exception.version}" arg2="HEAD" />
+ </condition>
+ </target>
+
+ <presetdef name="cvs-co">
+ <cvs command="checkout" dest="${workspace.dir}" />
+ </presetdef>
+
+ <!-- =====================================================
+ Self-update if required
+ ===================================================== -->
+
+ <!-- Update main GLite module -->
+ <target name="org.glite" depends="get.glite.head, get.glite.tag"/>
+ <target name="get.glite.head" if="glite.head">
+ <cvs-co package="org.glite" />
+ </target>
+ <target name="get.glite.tag" if="glite.tag">
+ <cvs-co package="org.glite"
+ tag="${org.glite.version}" />
+ </target>
+
+ <!-- Update the current module -->
+ <target name="org.glite.wms-utils" depends="get.glite-wms-utils.head, get.glite-wms-utils.tag"/>
+ <target name="get.glite-wms-utils.head" if="glite-wms-utils.head">
+ <cvs-co package="org.glite.wms-utils" />
+ <fail>The org.glite and org.glite.wms-utils modules have been updated, please rerun the configuration file</fail>
+ </target>
+ <target name="get.glite-wms-utils.tag" if="glite-wms-utils.tag">
+ <cvs-co package="org.glite.wms-utils"
+ tag="${org.glite.wms-utils.version}" />
+ <fail>The org.glite and org.glite.wms-utils modules have been updated, please rerun the configuration file</fail>
+ </target>
+
+ <!-- *****************************************************-->
+ <!-- Development tools -->
+ <!-- *****************************************************-->
+
+ <!-- All development tools -->
+ <target name="devtools" depends="oscheck,
+ junitcheck,
+ junit,
+ chkstyle,
+ jalopy,
+ ant-contrib,
+ cpptasks,
+ egee-ant-ext"/>
+
+ <!-- =====================================================
+ External libraries
+ ===================================================== -->
+
+ <!-- All external libraries -->
+ <target name="external" depends="oscheck,
+ log4j"/>
+
+ <!-- =====================================================
+ GLite WMS modules
+ ===================================================== -->
+
+ <!-- component targets tag = do not remove = -->
+
+ <!-- Tls -->
+ <target name="tls" depends="boost,classads,globus,cppunit,exception,get.tls.head, get.tls.tag"/>
+ <target name="get.tls.head" if="tls.head">
+ <cvs-co package="org.glite.wms-utils.tls" />
+ </target>
+ <target name="get.tls.tag" unless="tls.head">
+ <cvs-co package="org.glite.wms-utils.tls"
+ tag="${org.glite.wms-utils.tls.version}" />
+ </target>
+
+ <!-- Jobid -->
+ <target name="jobid" depends="boost,classads,globus,cppunit,exception,get.jobid.head, get.jobid.tag"/>
+ <target name="get.jobid.head" if="jobid.head">
+ <cvs-co package="org.glite.wms-utils.jobid" />
+ </target>
+ <target name="get.jobid.tag" unless="jobid.head">
+ <cvs-co package="org.glite.wms-utils.jobid"
+ tag="${org.glite.wms-utils.jobid.version}" />
+ </target>
+
+ <!-- exception -->
+ <target name="exception" depends="cppunit,get.exception.head, get.exception.tag"/>
+ <target name="get.exception.head" if="exception.head">
+ <cvs-co package="org.glite.wms-utils.exception" />
+ </target>
+ <target name="get.exception.tag" unless="exception.head">
+ <cvs-co package="org.glite.wms-utils.exception"
+ tag="${org.glite.wms-utils.exception.version}" />
+ </target>
+
+ <!-- All project modules -->
+ <target name="project" depends="exception,
+ tls,
+ jobid"/>
+
+
+ <!-- ====================================================
+ Checkout all
+ ==================================================== -->
+
+ <!-- All libraries -->
+ <target name="all" depends="oscheck,evaluate.cvs.tags,defaultenvchecks,org.glite,org.glite.wms-utils,devtools,external,project" />
+
+ <!-- ====================================================
+ Print dependecies to console
+ ==================================================== -->
+
+ <target name="dependencies">
+ <concat>
+ <fileset dir="." includes="dependencies.properties" />
+ </concat>
+ </target>
+
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2004 on behalf of the EU EGEE Project:
+ The European Organization for Nuclear Research (CERN),
+ Istituto Nazionale di Fisica Nucleare (INFN), Italy
+ Datamat Spa, Italy
+ Centre National de la Recherche Scientifique (CNRS), France
+ CS Systeme d'Information (CSSI), France
+ Royal Institute of Technology, Center for Parallel Computers (KTH-PDC), Sweden
+ Universiteit van Amsterdam (UvA), Netherlands
+ University of Helsinki (UH.HIP), Finland
+ University of Bergen (UiB), Norway
+ Council for the Central Laboratory of the Research Councils (CCLRC), United Kingdom
+
+ Common build properties file for the Gridsite Core modules
+
+ Authors: Alberto Di Meglio <alberto.di.meglio@cern.ch>
+ Version info: $Id$
+ Release: $Name$
+
+ Revision history:
+ $Log$
+-->
+
+<project name="Gridsite Core common properties">
+
+ <!-- Include build properties to allow overwriting
+ of properties for subsystem -->
+ <property name="subsystem.build.properties.file" value="./project/build.properties" />
+ <property file="${subsystem.build.properties.file}" />
+
+ <!-- ======================================================
+ Define subsystem properties
+ ====================================================== -->
+
+ <!-- Subsystem name -->
+ <property name="subsystem.name" value="${gridsite-core.subsystem.name}"/>
+
+ <!-- Subsystem prefix -->
+ <property name="subsystem.prefix" value="${gridsite-core.subsystem.prefix}"/>
+
+ <!-- ======================================================
+ Define general subsystem properties
+ ====================================================== -->
+
+ <!-- Include common subsystem properties -->
+ <import file="${subsystem.general.properties.file}" />
+
+ <!-- ======================================================
+ Define extra properties here ...
+ ====================================================== -->
+
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2004 on behalf of the EU EGEE Project:
+ The European Organization for Nuclear Research (CERN),
+ Istituto Nazionale di Fisica Nucleare (INFN), Italy
+ Datamat Spa, Italy
+ Centre National de la Recherche Scientifique (CNRS), France
+ CS Systeme d'Information (CSSI), France
+ Royal Institute of Technology, Center for Parallel Computers (KTH-PDC), Sweden
+ Universiteit van Amsterdam (UvA), Netherlands
+ University of Helsinki (UH.HIP), Finland
+ University of Bergen (UiB), Norway
+ Council for the Central Laboratory of the Research Councils (CCLRC), United Kingdom
+
+ Common Ant task definition file for the Gridsite Core modules
+
+ Authors: Alberto Di Meglio <alberto.di.meglio@cern.ch>
+ Version info: $Id$
+ Release: $Name$
+
+ Revision history:
+ $Log$
+-->
+
+<project name="Gridsite Core common tasks and types definitions">
+
+<!-- ======================================================
+ Subsystem task definitions
+ ====================================================== -->
+
+</project>
\ No newline at end of file
--- /dev/null
+module.version=1.0.4
+module.build=1
+module.age=5
--- /dev/null
+# Doxyfile 1.2.18
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME =
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = doxygen
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET = doxygen.css
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output dir.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = YES
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
+# or Internet explorer 4.0+). Note that for large projects the tree generation
+# can take a very long time. In such cases it is better to disable this feature.
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse the
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
--- /dev/null
+#
+# Andrew McNab and Shiv Kaushal, University of Manchester.
+# Copyright (c) 2002-4. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# o Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+# o Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+#-----------------------------------------------------------------------------
+# For more information about GridSite: http://www.gridpp.ac.uk/gridsite/
+#-----------------------------------------------------------------------------
+
+include ../VERSION
+
+RPMCMD=$(shell if [ -x /usr/bin/rpmbuild ] ; then echo /usr/bin/rpmbuild; else echo rpm; fi)
+
+ifndef MYRPMDIR
+export MYRPMDIR=$(shell pwd)/../RPMTMP
+endif
+
+ifndef prefix
+export prefix=/home/dimeglio/gridsite
+endif
+
+ifndef MYCFLAGS
+export MYCFLAGS=-I. -I../interface -I/usr/include/httpd -I/usr/include/apr-0
+endif
+
+ifndef MYLDFLAGS
+export MYLDFLAGS=-L.
+endif
+
+#
+# Build
+#
+
+build: libgridsite.so.$(VERSION) libgridsite.a htcp mod_gridsite.so \
+ urlencode real-gridsite-admin.cgi apidoc
+
+libgridsite.so.$(VERSION): grst_x509.o grst_gacl.o grst_http.o
+ gcc -shared -Wl,-soname,libgridsite.so.$(MINOR_VERSION) \
+ -o libgridsite.so.$(PATCH_VERSION) grst_x509.o grst_gacl.o grst_http.o
+
+libgridsite.a: grst_x509.o grst_gacl.o grst_http.o
+ ar src libgridsite.a grst_x509.o grst_gacl.o grst_http.o
+
+grst_x509.o: grst_x509.c ../interface/gridsite.h
+ gcc $(MYCFLAGS) -I/usr/include/openssl \
+ -I/usr/kerberos/include -c grst_x509.c
+
+grst_gacl.o: grst_gacl.c ../interface/gridsite.h
+ gcc $(MYCFLAGS) -I/usr/include/openssl \
+ -I/usr/kerberos/include `xml2-config --cflags` -c grst_gacl.c
+
+grst_http.o: grst_http.c ../interface/gridsite.h
+ gcc $(MYCFLAGS) -I/usr/include/openssl \
+ -I/usr/kerberos/include -c grst_http.c
+
+urlencode: urlencode.c libgridsite.a
+ gcc -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \
+ -o urlencode urlencode.c -L. \
+ -I/usr/include/openssl -I/usr/kerberos/include -lgridsite
+
+htcp: htcp.c
+ 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
+ gcc $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \
+ -I/usr/include/openssl -I/usr/kerberos/include \
+ -DVERSION=\"$(VERSION)\" -o mod_gridsite.so \
+ mod_gridsite.c $(MYLDFLAGS) -lxml2 -lm -lz -lgridsite
+
+real-gridsite-admin.cgi: grst_admin_main.c grst_admin_gacl.c \
+ grst_admin_file.c grst_admin.h
+ gcc $(MYCFLAGS) $(MYLDFLAGS) -o real-gridsite-admin.cgi \
+ grst_admin_main.c \
+ grst_admin_gacl.c \
+ grst_admin_file.c \
+ -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
+ 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
+
+#
+# Install
+#
+
+install:
+ mkdir -p $(prefix)/include \
+ $(prefix)/lib \
+ $(prefix)/bin \
+ $(prefix)/sbin \
+ $(prefix)/share/man/man1 \
+ $(prefix)/lib/httpd/modules \
+ $(prefix)/share/doc/gridsite-$(PATCH_VERSION)
+ cp -f ../interface/gridsite.h $(prefix)/include
+ cp -f ../interface/gridsite-gacl.h $(prefix)/include
+ cp -f urlencode $(prefix)/bin
+ cp -f libgridsite.a $(prefix)/lib
+ cp -f real-gridsite-admin.cgi $(prefix)/sbin
+ cp -f libgridsite.so.$(PATCH_VERSION) $(prefix)/lib
+ ln -sf libgridsite.so.$(PATCH_VERSION) \
+ $(prefix)/lib/libgridsite.so
+ ln -sf libgridsite.so.$(PATCH_VERSION) \
+ $(prefix)/lib/libgridsite.so.$(MAJOR_VERSION)
+ ln -sf libgridsite.so.$(PATCH_VERSION) \
+ $(prefix)/lib/libgridsite.so.$(MINOR_VERSION)
+ cp -f doxygen/index.html \
+ $(prefix)/share/doc/gridsite-$(PATCH_VERSION)/doxygen-index.html
+ cp -f doxygen/* $(prefix)/share/doc/gridsite-$(PATCH_VERSION)
+ cp -f ../CHANGES ../README ../INSTALL ../LICENSE ../VERSION \
+ $(prefix)/share/doc/gridsite-$(PATCH_VERSION)
+ 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
+ cd ../doc ; for i in *.1 ; do ../src/roffit < $$i \
+ > $(prefix)/share/doc/gridsite-$(VERSION)/$$i.html ; done
+ cp -f htcp $(prefix)/bin
+ ln -sf htcp $(prefix)/bin/htls
+ ln -sf htcp $(prefix)/bin/htll
+ ln -sf htcp $(prefix)/bin/htrm
+ ln -sf htcp $(prefix)/bin/htmkdir
+ cp -f mod_gridsite.so $(prefix)/lib/httpd/modules
+
+#
+# Distributions
+#
+
+# source files tarball
+dist:
+ mkdir -p ../gridsite-$(PATCH_VERSION)/src \
+ ../gridsite-$(PATCH_VERSION)/doc \
+ ../gridsite-$(PATCH_VERSION)/interface
+ cp -f ../VERSION ../README ../LICENSE ../CHANGES ../INSTALL \
+ ../gridsite-$(PATCH_VERSION)
+ cp -f Makefile *.c *.h roffit gridsite.spec \
+ Doxyfile doxygen.css doxyheader.html \
+ ../gridsite-$(PATCH_VERSION)/src
+ cp -f ../doc/*.html ../doc/*.1 ../doc/*.conf ../doc/*.sh \
+ ../gridsite-$(PATCH_VERSION)/doc
+ cp -f ../interface/*.h \
+ ../gridsite-$(PATCH_VERSION)/interface
+ cd .. ; tar zcvf gridsite-$(PATCH_VERSION).src.tar.gz \
+ gridsite-$(PATCH_VERSION)
+ rm -Rf ../gridsite-$(PATCH_VERSION)
+
+
+# binary tarball distribution for htcp users
+htcp-bin: htcp
+ mkdir -p ../htcp-bin-$(PATCH_VERSION)/bin \
+ ../htcp-bin-$(PATCH_VERSION)/man/man1
+ cp -f ../doc/README.htcp-bin ../htcp-bin-$(PATCH_VERSION)
+ cp -f htcp ../htcp-bin-$(PATCH_VERSION)/bin
+ cp -f ../doc/htcp.1 ../doc/htrm.1 ../doc/htls.1 ../doc/htll.1 \
+ ../doc/htmkdir.1 ../htcp-bin-$(PATCH_VERSION)/man/man1
+ ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htls
+ ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htll
+ ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htrm
+ ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htmkdir
+ cd ../htcp-bin-$(VERSION) ; tar zcvf ../htcp-$(VERSION).bin.tar.gz .
+ rm -Rf ../htcp-bin-$(PATCH_VERSION)
+
+# RPM targets: build and RPMs go into subdirectories of ../RPMTMP/
+rpm: dist gridsite.spec
+ rm -Rf $(MYRPMDIR)/BUILDROOT $(MYRPMDIR)/BUILD
+ mkdir -p $(MYRPMDIR)/SOURCES $(MYRPMDIR)/SPECS $(MYRPMDIR)/BUILD \
+ $(MYRPMDIR)/SRPMS $(MYRPMDIR)/RPMS/i386 $(MYRPMDIR)/BUILDROOT
+ cp -f ../gridsite-$(PATCH_VERSION).src.tar.gz $(MYRPMDIR)/SOURCES
+ cp -f gridsite.spec $(MYRPMDIR)/SPECS
+ export MYPREFIX=/usr ; export MYVERSION=$(PATCH_VERSION) ; \
+ $(RPMCMD) --define "_topdir $(MYRPMDIR)" \
+ -ba --buildroot $(MYRPMDIR)/BUILDROOT gridsite.spec
+
--- /dev/null
+H1 { text-align: center; }
+CAPTION { font-weight: bold }
+A.qindex {}
+A.qindexRef {}
+A.el { text-decoration: none; font-weight: bold }
+A.elRef { font-weight: bold }
+A.code { text-decoration: none; font-weight: normal; color: #4444ee }
+A.codeRef { font-weight: normal; color: #4444ee }
+A:hover { text-decoration: none; background-color: #f2f2ff }
+DL.el { margin-left: -1cm }
+DIV.fragment { width: 100%; border: none; background-color: #eeeeee }
+DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px }
+TD.md { background-color: #f2f2ff; font-weight: bold; }
+TD.mdname1 { background-color: #f2f2ff; font-weight: bold; color: #602020; }
+TD.mdname { background-color: #f2f2ff; font-weight: bold; color: #602020; width: 600px; }
+DIV.groupHeader { margin-left: 16px; margin-top: 12px; margin-bottom: 6px; font-weight: bold }
+DIV.groupText { margin-left: 16px; font-style: italic; font-size: smaller }
+XXBODY { background: white }
+TD.indexkey {
+ background-color: #eeeeff;
+ font-weight: bold;
+ padding-right : 10px;
+ padding-top : 2px;
+ padding-left : 10px;
+ padding-bottom : 2px;
+ margin-left : 0px;
+ margin-right : 0px;
+ margin-top : 2px;
+ margin-bottom : 2px
+}
+TD.indexvalue {
+ background-color: #eeeeff;
+ font-style: italic;
+ padding-right : 10px;
+ padding-top : 2px;
+ padding-left : 10px;
+ padding-bottom : 2px;
+ margin-left : 0px;
+ margin-right : 0px;
+ margin-top : 2px;
+ margin-bottom : 2px
+}
+span.keyword { color: #008000 }
+span.keywordtype { color: #604020 }
+span.keywordflow { color: #e08000 }
+span.comment { color: #800000 }
+span.preprocessor { color: #806020 }
+span.stringliteral { color: #002080 }
+span.charliteral { color: #008080 }
--- /dev/null
+<p><a href=http://www.gridpp.ac.uk/authz/>GridSite</a> Version 0.9.1
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*------------------------------------------------------------------------*
+ * For more about GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+/*
+ Example program using GACL
+
+ Build with:
+
+ gcc -o gaclexample gaclexample.c -L. -I. -lgridsite -lxml2 -lz -lm
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gridsite.h>
+
+int main()
+{
+ GRSTgaclCred *cred, *usercred;
+ GRSTgaclEntry *entry;
+ GRSTgaclAcl *acl1, *acl2;
+ GRSTgaclUser *user;
+ GRSTgaclPerm perm0, perm1, perm2;
+ FILE *fp;
+
+ /* must initialise GACL before using it */
+
+ GRSTgaclInit();
+
+ /* build up an ACL, starting with a credential */
+
+ cred = GRSTgaclCredNew("person");
+
+ GRSTgaclCredAddValue(cred, "dn", "/O=Grid/CN=Mr Grid Person");
+
+ /* create an entry to put it in */
+
+ entry = GRSTgaclEntryNew();
+
+ /* add the credential to it */
+
+ GRSTgaclEntryAddCred(entry, cred);
+
+ /* add another credential */
+
+ cred = GRSTgaclCredNew("dn-list");
+ GRSTgaclCredAddValue(cred, "url", "example-dn-list");
+ GRSTgaclEntryAddCred(entry, cred);
+
+ fp = fopen("example-dn-list", "w");
+ fputs("/O=Grid/CN=Mr Grid Person\n", fp);
+ fclose(fp);
+
+ /* associate some permissions and denials to the credential */
+
+ GRSTgaclEntryAllowPerm( entry, GRST_PERM_READ);
+ GRSTgaclEntryAllowPerm( entry, GRST_PERM_WRITE);
+ GRSTgaclEntryAllowPerm( entry, GRST_PERM_ADMIN);
+ GRSTgaclEntryDenyPerm( entry, GRST_PERM_ADMIN);
+ GRSTgaclEntryDenyPerm( entry, GRST_PERM_LIST);
+
+ perm0 = GRST_PERM_READ | GRST_PERM_WRITE;
+
+ printf("test perm should be %d\n", perm0);
+
+ /* create a new ACL and add the entry to it */
+
+ acl1 = GRSTgaclAclNew();
+
+ GRSTgaclAclAddEntry(acl1, entry);
+
+ /* create a GRSTgaclUser to compare with the ACL */
+
+ usercred = GRSTgaclCredNew("person");
+
+ GRSTgaclCredAddValue(usercred, "dn", "/O=Grid/CN=Mr Grid Person");
+
+ user = GRSTgaclUserNew(usercred);
+
+ GRSTgaclUserSetDNlists(user, getcwd(NULL, 0));
+ printf("DN Lists dir %s\n", getcwd(NULL, 0));
+
+// putenv("GRST_DN_LISTS=.");
+
+ perm1 = GRSTgaclAclTestUser(acl1, user);
+
+ printf("test /O=Grid/CN=Mr Grid Person in acl = %d\n", perm1);
+
+ /* print and save the whole ACL */
+
+ GRSTgaclAclPrint(acl1, stdout);
+
+ GRSTgaclAclSave(acl1, "example.gacl");
+
+ puts("gridacl.out saved");
+
+ puts("");
+
+ /* load the ACL back off the disk, print and test it */
+
+ acl2 = GRSTgaclAclLoadFile("example.gacl");
+
+ puts("gridacl.out loaded");
+
+ if (acl2 != NULL) GRSTgaclAclPrint(acl2, stdout); else puts("acl2 is NULL");
+
+ perm2 = GRSTgaclAclTestUser(acl2, user);
+
+ printf("test /O=Grid/CN=Mr Grid Person in acl = %d\n", perm2);
+
+ if (perm1 != perm0) return 1;
+ if (perm2 != perm0) return 2;
+
+ return 0;
+}
--- /dev/null
+Name: gridsite
+Version: %(echo ${MYVERSION:-1.0.x})
+Release: 1
+Summary: GridSite
+Copyright: Modified BSD
+Group: System Environment/Daemons
+Source: %{name}-%{version}.src.tar.gz
+Prefix: %(echo ${MYPREFIX:-/usr})
+URL: http://www.gridpp.ac.uk/gridsite/
+Vendor: GridPP
+#Requires: libxml2,curl-ssl,mod_ssl
+#Buildrequires: libxml2-devel,curl-ssl-devel,httpd-devel
+Packager: Andrew McNab <mcnab@hep.man.ac.uk>
+
+%description
+GridSite adds GSI, VOMS and GACL support to Apache 2.0 (mod_gridsite),
+a library for manipulating these technologies (libgridsite), and CGI
+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.
+
+%package -n htcp
+Group: Applications/Internet
+Summary: HTTP(S) read/write client
+#Requires: curl-ssl
+
+%description -n htcp
+htcp is a client to fetch files or directory listings from remote
+servers using HTTP or HTTPS, or to put or delete files or directories
+onto remote servers using HTTPS. htcp is similar to scp(1), but uses
+HTTP/HTTPS rather than ssh as its transfer protocol.
+
+%prep
+
+%setup
+
+%build
+cd src
+make prefix=$RPM_BUILD_ROOT/%(echo ${MYPREFIX:-/usr})
+
+%install
+cd src
+make install prefix=$RPM_BUILD_ROOT/%(echo ${MYPREFIX:-/usr})
+
+%post
+/sbin/ldconfig
+ln -sf %(echo ${MYPREFIX:-/usr})/share/doc/gridsite-%{version} \
+ %(echo ${MYPREFIX:-/usr})/share/doc/gridsite
+
+%postun
+rm -f %(echo ${MYPREFIX:-/usr})/share/doc/gridsite
+
+%files
+%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})/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})/include/gridsite-gacl.h
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/lib/libgridsite.a
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/lib/httpd/modules/mod_gridsite.so
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/sbin/real-gridsite-admin.cgi
+
+%files -n htcp
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htcp
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htls
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htll
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htrm
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmkdir
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htcp.1.gz
+%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htrm.1.gz
+%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
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab and Shiv Kaushal,
+ 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/gridsite/ *
+ *---------------------------------------------------------------------------*/
+
+void GRSThttpError(char *);
+void adminfooter(GRSThttpBody *, char *, char *, char *, char *);
+int GRSTstrCmpShort(char *, char *);
+char *makevfilename(char *, size_t, char *);
+
+/*CGI GACL - Edit interface functions*/
+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);
+void new_entry_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void new_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void edit_entry_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void edit_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void add_cred_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void add_cred(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_cred(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_entry_sure(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_cred_sure(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+
+/*Functions producing messages*/
+//void error(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void admin_continue(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file, GRSThttpBody *bp);
+
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*---------------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef VERSION
+#define VERSION "x.x.x"
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+// when porting: remember that sendfile() is very OS-specific!
+#include <sys/sendfile.h>
+
+#include <gridsite.h>
+
+#include "grst_admin.h"
+
+char *storeuploadfile(char *boundary, int *bufferused)
+{
+// rewrite this to copy whole POSTed stdin HTTP body to disk then
+// mmap() and pick apart? How to deal with 100MB uploaded files, say?
+
+ char *filebuffer = NULL;
+ int bufferlen = 0, c, boundarylen;
+
+ *bufferused = 0;
+ boundarylen = strlen(boundary);
+
+ while ((c = getchar()) != EOF)
+ {
+ if (*bufferused > 1024*1024*100) return NULL;
+
+ ++(*bufferused);
+
+ if (*bufferused > bufferlen)
+ {
+ bufferlen = bufferlen + 1000;
+ filebuffer = realloc(filebuffer, (size_t) bufferlen);
+ }
+
+ filebuffer[*bufferused - 1] = c;
+
+ if ( (*bufferused >= boundarylen + 4) &&
+ (boundary[boundarylen-1] == c) &&
+ (boundary[boundarylen-2] == filebuffer[*bufferused - 2]) &&
+ (strncmp(boundary, &filebuffer[*bufferused - boundarylen],
+ boundarylen) == 0))
+ {
+ *bufferused = *bufferused - boundarylen - 4;
+
+ if (filebuffer == NULL) return strdup("");
+ else return filebuffer;
+ }
+ }
+
+ return NULL;
+}
+
+void uploadfile(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *dir_uri, char *admin_file)
+{
+ char *boundary, *p, oneline[200], *filename = NULL,
+ tmpfilename[256], *filebuffer = NULL, *filepath,
+ *vfile, *dir_path_vfile;
+ int mimestate, bufferused = 0, itworked = 0;
+ FILE *fp;
+ GRSThttpBody bp;
+
+#define MIMESTUNKNOWN 1
+#define MIMESTUPLOAD 2
+#define MIMESTFILENM 3
+
+ if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
+
+ p = getenv("CONTENT_TYPE");
+ boundary = &p[30];
+
+ mimestate = MIMESTUNKNOWN;
+
+ while (fgets(oneline, sizeof(oneline), stdin) != NULL)
+ {
+ if (*oneline == 13) // MIME has CR/LF line breaks, CR=13
+ {
+ if (mimestate == MIMESTUPLOAD)
+ {
+ filebuffer = storeuploadfile(boundary, &bufferused);
+ mimestate = MIMESTUNKNOWN;
+ }
+ else if (mimestate == MIMESTFILENM)
+ {
+ fgets(tmpfilename, sizeof(tmpfilename), stdin);
+ if (*tmpfilename != 13)
+ {
+ p = index(tmpfilename, 13);
+ *p = '\0';
+ filename = strdup(tmpfilename);
+ }
+ mimestate = MIMESTUNKNOWN;
+ }
+ }
+ else if (GRSTstrCmpShort(oneline,
+ "Content-Disposition: form-data; name=\"uploadfile\"; filename=\"")
+ == 0)
+ {
+ mimestate = MIMESTUPLOAD;
+ if (filename == NULL)
+ {
+ filename = strdup(&oneline[61]);
+
+ p = rindex(&oneline[61], '\\');
+ if (p != NULL) { ++p ; filename = p; }
+
+ p = rindex(&oneline[61], '/');
+ if (p != NULL) { ++p ; filename = p; }
+
+ p = index(filename, '"');
+ if (p != NULL) *p = '\0';
+ }
+ }
+ else if (GRSTstrCmpShort(oneline,
+ "Content-Disposition: form-data; name=\"file\"") == 0)
+ {
+ mimestate = MIMESTFILENM;
+ }
+ }
+
+ if ((filebuffer != NULL) && (bufferused >= 0))
+ {
+ if (filename == NULL) GRSThttpError("403 Forbidden");
+ else if ((index(filename, '/') != NULL) ||
+ (strcmp(filename, GRST_ACL_FILE) == 0))
+ {
+ puts("Status: 403 Forbidden filename\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Forbidden filename %s</title>\n", filename);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Forbidden filename %s</h1>\n",
+ filename);
+
+ GRSThttpPrintf(&bp,
+ "<p align=center>New file names cannot include slashes "
+ "or use the reserved ACL name, %s\n", GRST_ACL_FILE);
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+ return;
+ }
+ else
+ {
+ vfile = makevfilename(filename, bufferused, dn);
+ asprintf(&dir_path_vfile, "%s/%s", dir_path, vfile);
+
+ fp = fopen(dir_path_vfile, "w");
+ if (fp != NULL)
+ {
+ if ((fwrite(filebuffer,
+ sizeof(char), bufferused, fp) == bufferused) &&
+ (fclose(fp) == 0))
+ {
+ asprintf(&filepath, "%s/%s", dir_path, filename);
+
+ unlink(filepath); /* this can fail ok */
+
+ itworked = (link(dir_path_vfile, filepath) == 0);
+ }
+ }
+ }
+
+ free((void *) filebuffer);
+ }
+
+ if (itworked)
+ {
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+ return;
+ }
+
+ puts("Status: 500 Failed trying to upload\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp, "<title>Failed to upload</title>\n");
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Failed to upload</h1>\n");
+
+ GRSThttpPrintf(&bp, "<p align=center>GridSite considers you are authorized "
+ "to upload the file, but the upload failed. This is "
+ "probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void deletefileaction(char *dn, GRSTgaclPerm perm, char *help_uri,
+ char *dir_path, char *file, char *dir_uri,
+ char *admin_file)
+{
+ int fd, numfiles;
+ char *dir_path_file, *dir_path_vfile, *p, *vfile, *dnlistsuri,
+ *fulluri, *server_name, *realfile;
+ struct stat statbuf;
+ GRSThttpBody bp;
+ struct dirent *subdirfile_ent;
+ DIR *subDIR;
+
+ if (((strcmp(file, GRST_ACL_FILE) != 0) && !GRSTgaclPermHasWrite(perm)) ||
+ ((strcmp(file, GRST_ACL_FILE) == 0) && !GRSTgaclPermHasAdmin(perm)))
+ GRSThttpError("403 Forbidden");
+
+ dnlistsuri = getenv("GRST_DN_LISTS_URI");
+ if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
+
+ if ((dnlistsuri != NULL) &&
+ (strncmp(dnlistsuri, dir_uri, strlen(dnlistsuri)) == 0))
+ realfile = GRSThttpUrlEncode(file);
+ else if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+ else realfile = file;
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(realfile) + 2);
+
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, realfile);
+
+ if ((stat(dir_path_file, &statbuf) == 0) && S_ISDIR(statbuf.st_mode))
+ {
+ subDIR = opendir(dir_path_file);
+ if (subDIR == NULL) numfiles = 99; /* stop deletion */
+ else
+ {
+ numfiles = 0;
+ while ((subdirfile_ent = readdir(subDIR)) != NULL)
+ if (subdirfile_ent->d_name[0] != '.') ++numfiles;
+ else if (strncmp(subdirfile_ent->d_name,
+ GRST_ACL_FILE,
+ sizeof(GRST_ACL_FILE)) == 0) ++numfiles;
+ closedir(subDIR);
+ }
+
+ if (numfiles == 0)
+ {
+ vfile = makevfilename(file, 0, dn);
+ dir_path_vfile = malloc(strlen(dir_path) + strlen(vfile) + 2);
+ strcpy(dir_path_vfile, dir_path);
+ strcat(dir_path_vfile, "/");
+ strcat(dir_path_vfile, vfile);
+
+ if (rename(dir_path_file, dir_path_vfile) == 0)
+ {
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+ return;
+ }
+ }
+ }
+ else if (unlink(dir_path_file) == 0)
+ {
+ if (strcmp(file, GRST_ACL_FILE) != 0)
+ {
+ vfile = makevfilename(file, 0, dn);
+ dir_path_file = malloc(strlen(dir_path) + strlen(vfile) + 2);
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, vfile);
+
+ fd = open(dir_path_file, O_WRONLY | O_CREAT);
+ if (fd != -1) close(fd);
+ }
+
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+
+ return;
+ }
+
+ puts("Status: 500 Failed trying to delete\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp, "<title>Error deleting %s%s</title>\n", dir_uri, file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error deleting %s%s</h1>\n",
+ dir_uri, file);
+
+ GRSThttpPrintf(&bp, "<p align=center>GridSite considers you are authorized "
+ "to delete %s, but the delete failed. This is "
+ "probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.",
+ file);
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void deletefileform(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp, "<title>Delete %s</title>\n", file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Delete %s</h1>\n", file);
+
+ GRSThttpPrintf(&bp,"<form action=\"%s%s\" method=post>\n",dir_uri,admin_file);
+ GRSThttpPrintf(&bp,"<h2 align=center>Do you really want to delete %s?", file);
+ GRSThttpPrintf(&bp,"<p align=center><input type=submit value=\"Yes, delete %s\"></h2>\n", file);
+ GRSThttpPrintf(&bp,"<input type=hidden name=file value=\"%s\">\n", file);
+ GRSThttpPrintf(&bp,"<input type=hidden name=cmd value=deleteaction>\n");
+ GRSThttpPrintf(&bp,"</form>\n");
+
+ GRSThttpPrintf(&bp,"<p align=center>Or "
+ "<a href=\"%s%s?cmd=managedir\">return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void renameform(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp, "<title>Rename %s</title>\n", file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Rename %s%s</h1>\n", dir_uri, file);
+
+ GRSThttpPrintf(&bp,"<form action=\"%s%s\" method=post>\n",dir_uri,admin_file);
+ GRSThttpPrintf(&bp,"<h2 align=center>What do you want to rename %s to?</h2>", file);
+ GRSThttpPrintf(&bp,"<input type=hidden name=file value=\"%s\">\n", file);
+ GRSThttpPrintf(&bp,"<p align=center>New name: <input type=text name=newfile value=\"%s\">\n", file);
+ GRSThttpPrintf(&bp,"<input type=submit value=\"Rename\">\n");
+ GRSThttpPrintf(&bp,"<input type=hidden name=cmd value=renameaction>\n");
+ GRSThttpPrintf(&bp,"</form>\n");
+
+ GRSThttpPrintf(&bp,"<p align=center>Or "
+ "<a href=\"%s%s?cmd=managedir&diruri=%s\">return to "
+ "directory listing</a>\n", dir_uri, admin_file, dir_uri);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void editfileaction(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ char *pagetext, *dir_path_file, *vfile, *dir_path_vfile,
+ *dnlistsuri, *server_name, *fulluri, *realfile;
+ FILE *fp;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm) || (strcmp(file, GRST_ACL_FILE) == 0))
+ GRSThttpError("403 Forbidden");
+
+ dnlistsuri = getenv("GRST_DN_LISTS_URI");
+ if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
+
+ if ((dnlistsuri != NULL) &&
+ (strncmp(dnlistsuri, dir_uri, strlen(dnlistsuri)) == 0))
+ {
+ realfile = GRSThttpUrlEncode(file);
+
+ if (realfile[0] == '.') GRSThttpError("403 Forbidden");
+ }
+ else if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+ else realfile = file;
+
+ asprintf(&dir_path_file, "%s/%s", dir_path, realfile);
+
+ pagetext = GRSThttpGetCGI("pagetext");
+ vfile = makevfilename(file, strlen(pagetext), dn);
+ asprintf(&dir_path_vfile, "%s/%s", dir_path, vfile);
+
+ fp = fopen(dir_path_vfile, "w");
+ if (fp == NULL)
+ {
+ puts("Status: 500 Failed trying to write\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Error writing %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error writing %s%s</h1>\n",
+ dir_uri, file);
+
+ GRSThttpPrintf(&bp,
+ "<p align=center>GridSite considers you are authorized "
+ "to write the file, but the write failed. This is "
+ "probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+ return;
+ }
+
+ fwrite(pagetext, strlen(pagetext), sizeof(char), fp);
+
+ fclose(fp);
+
+ unlink(dir_path_file);
+
+ if (link(dir_path_vfile,dir_path_file) != 0) GRSThttpError("403 Forbidden");
+
+ if ((strlen(file) > 7) && (strcmp(&file[strlen(file) - 5], ".html") == 0))
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s\n\n", dir_uri, file);
+ else printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+}
+
+void create_acl(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int fd;
+ char *tmpgacl, *newgacl;
+ GRSTgaclAcl *acl;
+ FILE *fp;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError("403 Forbidden");
+
+ asprintf(&tmpgacl, "%s/.tmp.XXXXXX", dir_path);
+ asprintf(&newgacl, "%s/%s", dir_path, GRST_ACL_FILE);
+
+ if (((acl = GRSTgaclAclLoadforFile(dir_path)) != NULL) &&
+ ((fd = mkstemp(tmpgacl)) != -1) &&
+ ((fp = fdopen(fd, "w+")) != NULL) &&
+ GRSTgaclAclPrint(acl, fp) &&
+ (fclose(fp) == 0) &&
+ (rename(tmpgacl, newgacl) == 0))
+ {
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+
+ free(tmpgacl);
+ free(newgacl);
+ return;
+ }
+
+ puts("Status: 500 Failed trying to create\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Error creating %s%s</title>\n", dir_uri,
+ GRST_ACL_FILE);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error creating %s%s</h1>\n",
+ dir_uri, GRST_ACL_FILE);
+
+ GRSThttpPrintf(&bp, "<p align=center>GridSite considers you are authorized "
+ "to create it, but the create failed. This is "
+ "probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+
+ free(tmpgacl);
+ free(newgacl);
+}
+
+void renameaction(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int len;
+ char *dir_path_file, *vfile, *dir_path_vfile,
+ *dnlistsuri, *newfile, *dir_path_newfile;
+ struct stat statbuf;
+ FILE *fp;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm) || (strcmp(file, GRST_ACL_FILE) == 0))
+ GRSThttpError("403 Forbidden");
+
+ if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, file);
+
+ if (stat(dir_path_file, &statbuf) != 0) GRSThttpError("404 Not Found");
+
+ newfile = GRSThttpGetCGI("newfile");
+
+ if ((strcmp(newfile, GRST_ACL_FILE) == 0) ||
+ (strcmp(newfile, file) == 0)) GRSThttpError("403 Forbidden");
+
+ dir_path_newfile = malloc(strlen(dir_path) + strlen(newfile) + 2);
+ strcpy(dir_path_newfile, dir_path);
+ strcat(dir_path_newfile, "/");
+ strcat(dir_path_newfile, newfile);
+
+ vfile = makevfilename(newfile, statbuf.st_size, dn);
+ dir_path_vfile = malloc(strlen(dir_path) + strlen(vfile) + 2);
+ strcpy(dir_path_vfile, dir_path);
+ strcat(dir_path_vfile, "/");
+ strcat(dir_path_vfile, vfile);
+
+ unlink(dir_path_newfile); /* just in case */
+
+ if ((link(dir_path_file, dir_path_vfile ) == 0) &&
+ (link(dir_path_file, dir_path_newfile) == 0) &&
+ (unlink(dir_path_file) == 0))
+ {
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s\n\n", dir_uri);
+ return;
+ }
+
+ puts("Status: 500 Failed trying to rename\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Error renaming %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error renaming %s%s</h1>\n",
+ dir_uri, file);
+
+ GRSThttpPrintf(&bp, "<p align=center>GridSite considers you are authorized "
+ "to rename it, but the rename failed. This is "
+ "probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void newdirectory(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int len;
+ char *dir_path_file, *vfile, *dir_path_vfile, *filedup;
+ FILE *fp;
+ GRSThttpBody bp;
+
+ if ((file[0] == '\0') ||
+ !GRSTgaclPermHasWrite(perm) || (strcmp(file, GRST_ACL_FILE) == 0))
+ GRSThttpError("403 Forbidden");
+
+ filedup = strdup(file);
+ if (filedup[strlen(filedup)-1] == '/') filedup[strlen(filedup)-1] = '\0';
+ if (index(filedup, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, file);
+
+ if (mkdir(dir_path_file, 0751) == 0)
+ {
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+ return;
+ }
+
+ puts("Status: 500 Failed trying to create\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Error create %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error creating directory %s%s</h1>\n",
+ dir_uri, file);
+
+ GRSThttpPrintf(&bp,
+ "<p align=center>GridSite considers you are authorized "
+ "to create the directory, but the creation failed. This "
+ "is probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "parent directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void editdnlistaction(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int numdn = 0, ifd, ofd, numdnlines = 0, i, found;
+ char *dir_path_file, *dir_path_tmpfile, *realfile,
+ *dnlistsuri, *server_name, *fulldiruri, *p, oneline[513],
+ **dnlines, name[81], *add;
+ FILE *ofp;
+ struct stat statbuf;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
+
+ dnlistsuri = getenv("GRST_DN_LISTS_URI");
+ if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
+
+ server_name = getenv("SERVER_NAME");
+
+ if ((server_name == NULL) ||
+ (dnlistsuri == NULL) ||
+ (strncmp(dnlistsuri, dir_uri, strlen(dnlistsuri)) != 0))
+ GRSThttpError("403 Forbidden");
+
+ asprintf(&fulldiruri, "https://%s%s", server_name, dir_uri);
+
+ if ((strncmp(fulldiruri, file, strlen(fulldiruri)) != 0) &&
+ ((strncmp(fulldiruri, file, strlen(fulldiruri) - 1) != 0) ||
+ (strlen(fulldiruri) - 1 != strlen(file))))
+ {
+ puts("Status: 403 Forbidden\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Error writing %s</title>\n", file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error writing %s to %s</h1>\n",
+ file, dir_uri);
+
+ GRSThttpPrintf(&bp, "<p align=center>You cannot create a DN List "
+ "with that prefix in this directory. Please see the "
+ "the GridSite User's Guide for an explanation.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+ return;
+ }
+
+ p = GRSThttpGetCGI("numdn");
+ if ((p == NULL) || (sscanf(p, "%d", &numdn) != 1))
+ GRSThttpError("500 No number of DNs");
+
+ if (numdn > 0)
+ {
+ dnlines = malloc(sizeof(char *) * numdn);
+
+ for (i=1; i <= numdn; ++i)
+ {
+ sprintf(name, "dn%d", i);
+ p = GRSThttpGetCGI(name);
+
+ if (*p != '\0')
+ {
+ dnlines[numdnlines] = p;
+ ++numdnlines;
+ }
+ }
+ }
+
+ add = GRSThttpGetCGI("add");
+
+ realfile = GRSThttpUrlEncode(file);
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(realfile) + 2);
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, realfile);
+
+ dir_path_tmpfile = malloc(strlen(dir_path) + 13);
+ strcpy(dir_path_tmpfile, dir_path);
+ strcat(dir_path_tmpfile, "/.tmp.XXXXXX");
+
+ if (((ofd = mkstemp(dir_path_tmpfile)) != -1) &&
+ ((ofp = fdopen(ofd, "w")) != NULL))
+ {
+ if (*add != '\0')
+ {
+ fputs(add, ofp);
+ fputc('\n', ofp);
+ }
+
+ for (i=0; i < numdnlines; ++i)
+ {
+ fputs(dnlines[i], ofp);
+ fputc('\n', ofp);
+ }
+
+ if ((fclose(ofp) == 0) &&
+ ((stat(dir_path_file, &statbuf) != 0) ||
+ (unlink(dir_path_file) == 0)) &&
+ (rename(dir_path_tmpfile, dir_path_file) == 0))
+ {
+ printf("Status: 302 Moved Temporarily\nContent-Length: 0\n"
+ "Location: %s%s?cmd=managedir\n\n", dir_uri, admin_file);
+ return;
+ }
+ }
+
+ puts("Status: 500 Failed trying to write\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Error writing %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1 align=center>Error writing %s%s</h1>\n",
+ dir_uri, file);
+
+ GRSThttpPrintf(&bp, "<p align=center>GridSite considers you are authorized "
+ "to write the file, but the write failed. This is "
+ "probably a web server or operating system level "
+ "misconfiguration. Consult the site administrator.");
+
+ GRSThttpPrintf(&bp,"<p align=center>"
+ "<a href=\"%s%s?cmd=managedir\">Return to "
+ "directory listing</a>\n", dir_uri, admin_file);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+
+ /* try to clean up */
+ if (stat(dir_path_tmpfile, &statbuf) == 0) unlink(dir_path_tmpfile);
+}
+
+void printfile(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int fd;
+ char *dir_path_file;
+ struct stat statbuf;
+
+ if (!GRSTgaclPermHasRead(perm)) GRSThttpError("403 Forbidden");
+
+ if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);
+
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, file);
+
+ fd = open(dir_path_file, O_RDONLY);
+ if (fd == -1) GRSThttpError("500 Internal server error");
+
+ if ((fstat(fd, &statbuf) != 0) ||
+ !S_ISREG(statbuf.st_mode)) GRSThttpError("403 Forbidden");
+
+ printf("Status: 200 OK\nContent-Type: text/html\nContent-Length: %d\n\n",
+ statbuf.st_size);
+
+ fflush(stdout);
+
+ sendfile(1, fd, 0, statbuf.st_size);
+}
+
+void filehistory(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int fd, n, i, j, enclen, num = 0;
+ char *encodedfile, *p, *dndecoded, modified[99], *vfile, *q,
+ *encdn;
+ time_t file_time;
+ size_t file_size;
+ struct stat statbuf;
+ struct dirent **namelist;
+ struct tm file_tm;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasRead(perm)) GRSThttpError("403 Forbidden");
+
+ if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+ GRSThttpPrintf(&bp, "<title>History of %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+ GRSThttpPrintf(&bp,
+ "<h1 align=center>History of <a href=\"%s%s\">%s%s</a></h1>\n",
+ dir_uri, file, dir_uri, file);
+
+ asprintf(&vfile, "%s/%s", dir_path, file);
+ if (stat(vfile, &statbuf) == 0)
+ {
+ localtime_r((const time_t *) &(statbuf.st_mtime), &file_tm);
+ strftime(modified, sizeof(modified),
+ "%a %e %b %Y %k:%M", &file_tm);
+
+ GRSThttpPrintf(&bp, "<p align=center>Last modified: %s\n", modified);
+ }
+ free(vfile);
+
+ encodedfile = GRSThttpUrlEncode(file);
+ for (p=encodedfile; *p != '\0'; ++p) if (*p == '%') *p = '=';
+ enclen = strlen(encodedfile);
+
+ n = scandir(dir_path, &namelist, 0, alphasort);
+
+ if (n > 0)
+ {
+ for (i = n - 1; i >= 0; --i)
+ {
+ if ((strncmp(namelist[i]->d_name, GRST_HIST_PREFIX,
+ sizeof(GRST_HIST_PREFIX) - 1) == 0) &&
+ ((namelist[i]->d_name)[sizeof(GRST_HIST_PREFIX) - 1] == ':') &&
+ (strncmp(&((namelist[i]->d_name)[sizeof(GRST_HIST_PREFIX)]),
+ encodedfile, enclen) == 0) &&
+ ((namelist[i]->d_name)[sizeof(GRST_HIST_PREFIX)+enclen] == ':'))
+ {
+ if (num == 0) GRSThttpPrintf(&bp,
+ "<p align=center><table border=1 cellpadding=5>\n"
+ "<tr><td>Date</td><td>Size after</td>"
+ "<td colspan=2>Changed by</td></tr>\n");
+
+ ++num;
+
+ p = index(namelist[i]->d_name, ':');
+ p = index(&p[1], ':');
+ sscanf(&p[1], "%X:", &file_time);
+ p = index(&p[1], ':'); /* skip over microseconds time */
+ p = index(&p[1], ':');
+ sscanf(&p[1], "%X:", &file_size);
+ p = index(&p[1], ':');
+
+ encdn = strdup(&p[1]);
+ q = index(encdn, ':');
+ if (q != NULL) *q = '\0';
+
+ for (q=encdn; *q != '\0'; ++q) if (*q == '=') *q = '%';
+ dndecoded = GRSThttpUrlDecode(encdn);
+
+ localtime_r((const time_t *) &file_time, &file_tm);
+ strftime(modified, sizeof(modified),
+ "%a %e %b %Y %k:%M", &file_tm);
+
+ GRSThttpPrintf(&bp,
+ "<tr><td>%s</td><td align=right>%d</td><td>%s</td>\n",
+ modified, file_size, dndecoded);
+
+ free(dndecoded);
+
+ asprintf(&vfile, "%s/%s", dir_path, namelist[i]->d_name);
+ if ((stat(vfile, &statbuf) == 0) && (statbuf.st_size > 0))
+ {
+ GRSThttpPrintf(&bp, "<td><a href=\"");
+ if (strcmp (file, GRST_ACL_FILE)==0)
+ GRSThttpPrintf(&bp, "%s%s?cmd=acl_history&dir_uri=%s&file=%s\">View</a></td></tr>\n",
+ dir_uri, admin_file, dir_uri, namelist[i]->d_name);
+ else GRSThttpPrintf(&bp, "%s%s\">View</a></td></tr>\n",
+ dir_uri, namelist[i]->d_name);
+ }
+ else GRSThttpPrintf(&bp, "<td> </td></tr>");
+
+ free(vfile);
+ }
+ }
+ }
+
+ if (num > 0) GRSThttpPrintf(&bp, "</table>\n");
+ else GRSThttpPrintf(&bp, "<p align=center>No history for this file\n");
+
+ if (GRSTgaclPermHasList(perm))
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ else adminfooter(&bp, dn, help_uri, dir_uri, NULL);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+}
+
+void ziplist(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ char *shellcmd, *unzip, oneline[129];
+ FILE *fp;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasRead(perm)) GRSThttpError("403 Forbidden");
+
+ if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+ GRSThttpPrintf(&bp, "<title>Contents of %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+ GRSThttpPrintf(&bp,
+ "<h1 align=center>Contents of ZIP file <a href=\"%s%s\">%s%s</a></h1>\n",
+ dir_uri, file, dir_uri, file);
+
+ unzip = getenv("GRST_UNZIP");
+ if (unzip == NULL) unzip = getenv("REDIRECT_GRST_UNZIP");
+
+ if (unzip != NULL)
+ {
+ GRSThttpPrintf(&bp, "<center><table><tr><td><pre>\n");
+ asprintf(&shellcmd, "cd %s ; %s -Z %s", dir_path, unzip, file);
+ fp = popen(shellcmd, "r");
+
+ while (fgets(oneline, sizeof(oneline), fp) != NULL)
+ GRSThttpPrintf(&bp, "%s", oneline);
+ pclose(fp);
+ GRSThttpPrintf(&bp, "</pre></td></tr></table></center>\n");
+
+ if (GRSTgaclPermHasWrite(perm))
+ GRSThttpPrintf(&bp,
+ "<p><center><form action=\"%s%s\" method=post>"
+ "<input type=submit value=\"Unzip this file\"> in %s"
+ "<input type=hidden name=cmd value=unzipfile>"
+ "<input type=hidden name=file value=\"%s\"></form>"
+ "<p>(All files are placed in the same directory and files "
+ "beginning with "." are ignored.)</center>\n",
+ dir_uri, admin_file, dir_uri, file);
+ }
+ else GRSThttpPrintf(&bp, "<p align=center>unzip path not defined!\n");
+
+ if (GRSTgaclPermHasList(perm))
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ else adminfooter(&bp, dn, help_uri, dir_uri, NULL);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+}
+
+void unzipfile(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ char *shellcmd, *unzip, oneline[129];
+ FILE *fp;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
+
+ if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+ GRSThttpPrintf(&bp, "<title>Unzipping %s%s</title>\n", dir_uri, file);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+ GRSThttpPrintf(&bp,
+ "<h1 align=center>Unzipping <a href=\"%s%s\">%s%s</a></h1>\n",
+ dir_uri, file, dir_uri, file);
+
+ unzip = getenv("GRST_UNZIP");
+ if (unzip == NULL) unzip = getenv("REDIRECT_GRST_UNZIP");
+
+ if (unzip != NULL)
+ {
+ GRSThttpPrintf(&bp, "<center><table><tr><td><pre>\n");
+ asprintf(&shellcmd, "cd %s ; %s -jo %s -x '.*'", dir_path, unzip, file);
+ fp = popen(shellcmd, "r");
+
+ while (fgets(oneline, sizeof(oneline), fp) != NULL)
+ GRSThttpPrintf(&bp, "%s", oneline);
+ pclose(fp);
+ GRSThttpPrintf(&bp, "</pre></td></tr></table></center>\n");
+
+ if (GRSTgaclPermHasList(perm))
+ GRSThttpPrintf(&bp, "<p align=center>"
+ "<b><a href=\"%s%s?cmd=managedir\">Back to "
+ "directory</a></b>", dir_uri, admin_file);
+ }
+ else GRSThttpPrintf(&bp, "<p align=center>unzip path not defined!\n");
+
+ if (GRSTgaclPermHasList(perm))
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+ else adminfooter(&bp, dn, help_uri, dir_uri, NULL);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+}
+
+void editfileform(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int fd, rawpagesize, i, c;
+ char *dir_path_file, *rawpage, *p;
+ FILE *fp = NULL;
+ struct stat statbuf;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
+
+ if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);
+
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, file);
+
+ fd = open(dir_path_file, O_RDONLY);
+ if (fd != -1)
+ {
+ fp = fdopen(fd, "r");
+ if (fp == NULL) GRSThttpError("500 File open failed!");
+
+ if ((fstat(fd, &statbuf) != 0) ||
+ !S_ISREG(statbuf.st_mode)) GRSThttpError("500 Not a regular file!");
+ }
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp, "<title>Edit file %s</title>\n", file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1>Edit file %s</h1>\n", file);
+
+ GRSThttpPrintf(&bp,"<form action=\"%s%s\" method=post>\n",dir_uri,admin_file);
+ GRSThttpPrintf(&bp,"<p><input type=submit value=\"Save changes\">\n");
+ GRSThttpPrintf(&bp,"<p>File name: <input type=text name=file value=\"%s\">\n", file);
+ GRSThttpPrintf(&bp,"<input type=hidden name=cmd value=editaction>\n");
+ GRSThttpPrintf(&bp,"<p><textarea name=pagetext cols=80 rows=22>");
+
+ if (fp != NULL)
+ {
+ rawpagesize = statbuf.st_size + 1000;
+ rawpage = malloc(rawpagesize);
+
+ i = 0;
+
+ while ((c = fgetc(fp)) != EOF)
+ {
+ if (c == '<') { strcpy(&rawpage[i], "<");
+ i += 4; }
+ else if (c == '>') { strcpy(&rawpage[i], ">");
+ i += 4; }
+ else if (c == '&') { strcpy(&rawpage[i], "&");
+ i += 5; }
+ else if (c == '"') { strcpy(&rawpage[i], """);
+ i += 6; }
+ else { rawpage[i] = c;
+ i += 1; }
+
+ if (i >= rawpagesize - 7)
+ {
+ rawpagesize += 1000;
+ rawpage = realloc(rawpage, rawpagesize);
+ }
+ }
+
+ rawpage[i] = '\0';
+
+ GRSThttpPrintf(&bp, "%s", rawpage);
+ }
+
+ GRSThttpPrintf(&bp, "</textarea>\n");
+ GRSThttpPrintf(&bp, "<p><input type=submit value=\"Save changes\">\n");
+ GRSThttpPrintf(&bp, "</form>\n");
+
+ if (fp != NULL) fclose(fp);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+}
+
+void editdnlistform(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *file, char *dir_uri, char *admin_file)
+{
+ int fd, i, c, numdn = 0;
+ char *dir_path_file, *rawpage, *p, *dnlistsuri, *server_name, *fulluri,
+ *realfile, oneline[513];
+ FILE *fp = NULL;
+ struct stat statbuf;
+ GRSThttpBody bp;
+
+ dnlistsuri = getenv("GRST_DN_LISTS_URI");
+ if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
+
+ if (!GRSTgaclPermHasWrite(perm) ||
+ (dnlistsuri == NULL) ||
+ (strncmp(dnlistsuri, dir_uri, strlen(dnlistsuri)) != 0))
+ GRSThttpError("403 Forbidden");
+
+ realfile = GRSThttpUrlEncode(file);
+
+ dir_path_file = malloc(strlen(dir_path) + strlen(realfile) + 2);
+
+ strcpy(dir_path_file, dir_path);
+ strcat(dir_path_file, "/");
+ strcat(dir_path_file, realfile);
+
+ fd = open(dir_path_file, O_RDONLY);
+ if (fd != -1) /* we dont mind open failing, but it must work if it doesnt */
+ {
+ fp = fdopen(fd, "r");
+ if (fp == NULL) GRSThttpError("500 File open failed!");
+
+ if ((fstat(fd, &statbuf) != 0) ||
+ !S_ISREG(statbuf.st_mode)) GRSThttpError("500 Not a regular file!");
+ }
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp, "<title>Edit DN List %s</title>\n", file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1>Edit DN List</h1>\n");
+
+ GRSThttpPrintf(&bp,"<form action=\"%s%s\" method=post>\n",dir_uri,admin_file);
+ GRSThttpPrintf(&bp,"<p><input type=submit value=\"Update\">\n");
+ GRSThttpPrintf(&bp,"<p>List URL: <input type=text name=file value=\"%s\" "
+ "size=%d>\n", file, strlen(file));
+ GRSThttpPrintf(&bp,"<input type=hidden name=cmd value=editdnlistaction>\n");
+
+ if (fp != NULL)
+ {
+ GRSThttpPrintf(&bp, "<p><table>\n<tr><th>Keep?</th>"
+ "<th>Name</th></tr>\n");
+
+ while (fgets(oneline, sizeof(oneline), fp) != NULL)
+ {
+ ++numdn;
+
+ p = rindex(oneline, '\n');
+ if (p != NULL) *p = '\0';
+
+ GRSThttpPrintf(&bp, "<tr><td align=center><input type=checkbox "
+ "name=\"dn%d\" value=\"%s\" checked></td>"
+ "<td>%s</td></tr>\n", numdn, oneline, oneline);
+ }
+
+ GRSThttpPrintf(&bp,"</table>\n");
+ }
+
+ GRSThttpPrintf(&bp,"<input type=hidden name=numdn value=\"%d\">\n", numdn);
+
+ GRSThttpPrintf(&bp, "<p>Add new DN: <input type=text name=add "
+ "size=60 maxlength=512>\n");
+
+ GRSThttpPrintf(&bp,"<p><input type=submit value=\"Update\">\n");
+ GRSThttpPrintf(&bp, "</form>\n");
+
+ if (fp != NULL) fclose(fp);
+
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+}
+
+void managedir(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *dir_uri, char *admin_file)
+{
+ int n, is_dnlists_dir = 0, enclen, numfiles, encprefixlen;
+ char *d_namepath, modified[99], *absaclpath, *editable, *p, *unzip,
+ *dnlistsuri, *d_name, *server_name, *fulluri, *encfulluri,
+ *encprefix, *dnlistsprefix;
+ GRSThttpBody bp;
+ struct tm mtime_tm;
+ struct stat statbuf;
+ struct dirent **namelist, *subdirfile_ent;
+ DIR *subDIR;
+
+ if (((!GRSTgaclPermHasWrite(perm)) &&
+ (!GRSTgaclPermHasList(perm))) ||
+ (stat(dir_path, &statbuf) != 0) || !S_ISDIR(statbuf.st_mode))
+ GRSThttpError("403 Forbidden");
+
+ editable = getenv("GRST_EDITABLE");
+ if (editable == NULL) editable = getenv("REDIRECT_GRST_EDITABLE");
+
+ unzip = getenv("GRST_UNZIP");
+ if (unzip == NULL) unzip = getenv("REDIRECT_GRST_UNZIP");
+
+ dnlistsuri = getenv("GRST_DN_LISTS_URI");
+ if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
+
+ if (dnlistsuri && (strncmp(dnlistsuri, dir_uri, strlen(dnlistsuri)) == 0))
+ {
+ is_dnlists_dir = 1;
+ server_name = getenv("SERVER_NAME");
+
+ asprintf(&fulluri, "https://%s%s", server_name, dir_uri);
+ encfulluri = GRSThttpUrlEncode(fulluri);
+ enclen = strlen(encfulluri);
+
+ asprintf(&dnlistsprefix, "https://%s%s", server_name, dnlistsuri);
+ encprefix = GRSThttpUrlEncode(dnlistsprefix);
+ encprefixlen = strlen(encprefix);
+ }
+
+ printf("Status: 200 OK\nContent-Type: text/html\n");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintf(&bp,"<title>Manage directory %s</title>\n", dir_uri);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpPrintf(&bp, "<h1>Manage directory %s</h1>\n<table>\n", dir_uri);
+
+ if (dir_uri[1] != '\0')
+ GRSThttpPrintf(&bp,
+ "<tr><td colspan=3>[<a href=\"../%s?cmd=managedir\">Parent "
+ "directory</a>]</td></tr>\n", admin_file);
+
+ if (GRSTgaclPermHasList(perm) || GRSTgaclPermHasAdmin(perm))
+ {
+ absaclpath = malloc(strlen(dir_path) + sizeof(GRST_ACL_FILE) + 1);
+ strcpy(absaclpath, dir_path);
+ strcat(absaclpath, "/");
+ strcat(absaclpath, GRST_ACL_FILE);
+
+ if (stat(absaclpath, &statbuf) == 0) /* ACL exists in THIS directory */
+ {
+ localtime_r(&(statbuf.st_mtime), &mtime_tm);
+ strftime(modified, sizeof(modified),
+ "<td align=right>%R</td><td align=right>%e %b %y</td>",
+ &mtime_tm);
+
+ if (!is_dnlists_dir)
+ {
+ GRSThttpPrintf(&bp,
+ "<tr><td><a href=\"%s\">%s</a></td>"
+ "<td align=right>%ld</td>%s\n",
+ GRST_ACL_FILE,
+ GRST_ACL_FILE,
+ statbuf.st_size, modified);
+
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=history&file=%s\">"
+ "History</a></td>",
+ dir_uri, admin_file, GRST_ACL_FILE);
+ }
+ else GRSThttpPrintf(&bp,
+ "<tr><td>%s</td>"
+ "<td align=right>%ld</td>%s\n",
+ GRST_ACL_FILE,
+ statbuf.st_size, modified);
+
+ if (GRSTgaclPermHasAdmin(perm))
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=admin_acl\">Edit</a></td>"
+ "<td><a href=\"%s%s?cmd=delete&file=%s\">Delete</a></td>",
+ dir_uri, admin_file,
+ dir_uri, admin_file, GRST_ACL_FILE);
+ else if (GRSTgaclPermHasRead(perm))
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=show_acl\">View</a></td>"
+ "<td> </td>", dir_uri, admin_file);
+ else GRSThttpPrintf(&bp, "<td> </td><td> </td>\n");
+
+ GRSThttpPrintf(&bp, "<td> </td></tr>\n");
+ }
+ else if (GRSTgaclPermHasAdmin(perm))
+ GRSThttpPrintf(&bp, "<form method=post action=\"%s%s\">\n"
+ "<tr><td colspan=8><input type=submit value=\"Create .gacl\"></td>\n"
+ "<input type=hidden name=cmd value=\"create_acl\"></tr></form>\n",
+ dir_uri, admin_file);
+ }
+
+ if (GRSTgaclPermHasList(perm))
+ {
+ n = scandir(dir_path, &namelist, 0, alphasort);
+ while (n--)
+ {
+ if (namelist[n]->d_name[0] != '.')
+ {
+ d_namepath = malloc(strlen(dir_path) +
+ strlen(namelist[n]->d_name) + 2);
+ strcpy(d_namepath, dir_path);
+ strcat(d_namepath, "/");
+ strcat(d_namepath, namelist[n]->d_name);
+ stat(d_namepath, &statbuf);
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ subDIR = opendir(d_namepath);
+
+ if (subDIR == NULL) numfiles = 99; /* stop deletion */
+ else
+ {
+ numfiles = 0;
+ while ((subdirfile_ent = readdir(subDIR)) != NULL)
+ if (subdirfile_ent->d_name[0] != '.') ++numfiles;
+ else if (strncmp(subdirfile_ent->d_name,
+ GRST_ACL_FILE,
+ sizeof(GRST_ACL_FILE)) == 0) ++numfiles;
+
+ closedir(subDIR);
+ }
+ }
+
+ free(d_namepath);
+
+ localtime_r(&(statbuf.st_mtime), &mtime_tm);
+ strftime(modified, sizeof(modified),
+ "<td align=right>%R</td><td align=right>%e %b %y</td>",
+ &mtime_tm);
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ GRSThttpPrintf(&bp,
+ "<tr><td><a href=\"%s%s/%s?cmd=managedir\">"
+ "%s/</a></td>"
+ "<td align=right>%ld</td>%s\n<td colspan=2> </td>",
+ dir_uri, namelist[n]->d_name, admin_file,
+ namelist[n]->d_name,
+ statbuf.st_size, modified);
+
+ if (numfiles == 0)
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=delete&file=%s\">"
+ "Delete</a></td>\n",
+ dir_uri, admin_file, namelist[n]->d_name);
+ else GRSThttpPrintf(&bp, "<td> </td>\n");
+
+ GRSThttpPrintf(&bp, "<td> </td></tr>\n");
+ }
+ else if (is_dnlists_dir)
+ {
+ if ((strlen(namelist[n]->d_name) <= encprefixlen) ||
+ (strncmp(namelist[n]->d_name, encprefix,
+ encprefixlen) != 0)) continue;
+
+ d_name = GRSThttpUrlDecode(namelist[n]->d_name);
+
+ GRSThttpPrintf(&bp, "<tr><td><a href=\"%s\">%s</a></td>"
+ "<td align=right>%ld</td>%s"
+ "<td> </td>",
+ d_name, d_name,
+ statbuf.st_size, modified);
+
+ if (GRSTgaclPermHasWrite(perm))
+ GRSThttpPrintf(&bp, "<form action=\"%s%s\" method=post>"
+ "<td><input type=submit value=Edit></td>"
+ "<input type=hidden name=cmd value=editdnlist>"
+ "<input type=hidden name=file value=\"%s\">"
+ "</form>\n",
+ dir_uri, admin_file, d_name);
+ else GRSThttpPrintf(&bp, "<td> </td>\n");
+
+ if (GRSTgaclPermHasWrite(perm))
+ GRSThttpPrintf(&bp, "<form action=\"%s%s\" method=post>"
+ "<td><input type=submit value=Delete></td>"
+ "<input type=hidden name=cmd value=delete>"
+ "<input type=hidden name=file value=\"%s\">"
+ "</form>\n",
+ dir_uri, admin_file, d_name);
+ else GRSThttpPrintf(&bp, "<td> </td>\n");
+
+ GRSThttpPrintf(&bp, "<td> </td></tr>");
+ }
+ else /* regular directory, not DN Lists */
+ {
+ d_name = namelist[n]->d_name;
+
+ GRSThttpPrintf(&bp,
+ "<tr><td><a href=\"%s%s\">%s</a></td>"
+ "<td align=right>%ld</td>%s",
+ dir_uri, d_name,
+ d_name,
+ statbuf.st_size, modified);
+
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=history&file=%s\">"
+ "History</a></td>",
+ dir_uri, admin_file, d_name);
+
+ p = rindex(namelist[n]->d_name, '.');
+
+ if ((unzip != NULL) &&
+ (p != NULL) &&
+ (strcasecmp(&p[1], "zip") == 0) &&
+ GRSTgaclPermHasRead(perm))
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=ziplist&file=%s\">"
+ "List</a></td>\n",
+ dir_uri, admin_file, d_name);
+ else if ((p != NULL) &&
+ (strstr(editable, &p[1]) != NULL) &&
+ GRSTgaclPermHasWrite(perm))
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=edit&file=%s\">"
+ "Edit</a></td>\n",
+ dir_uri, admin_file, d_name);
+ else GRSThttpPrintf(&bp, "<td> </td>");
+
+ if (GRSTgaclPermHasWrite(perm))
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=delete&file=%s\">"
+ "Delete</a></td>\n", dir_uri, admin_file, d_name);
+ else
+ GRSThttpPrintf(&bp, "<td> </td>\n");
+
+ if (GRSTgaclPermHasWrite(perm))
+ GRSThttpPrintf(&bp,
+ "<td><a href=\"%s%s?cmd=rename&file=%s\">"
+ "Rename</a></td></tr>\n", dir_uri, admin_file, d_name);
+ else
+ GRSThttpPrintf(&bp, "<td> </td></tr>");
+ }
+ }
+
+ free(namelist[n]);
+ }
+
+ free(namelist);
+ }
+
+ if (GRSTgaclPermHasWrite(perm))
+ {
+ if (is_dnlists_dir)
+ {
+ GRSThttpPrintf(&bp, "<form method=post action=\"%s%s\">\n"
+ "<tr><td colspan=4>New list name: "
+ "<input type=text name=file value=\"%sNEW_LIST\" size=%d>\n"
+ "<input type=hidden name=cmd value=editdnlist></td>"
+ "<td colspan=2 align=center><input type=submit value=Create></td>\n"
+ "</tr></form>\n",
+ dir_uri, admin_file, fulluri, strlen(fulluri)+8);
+
+ GRSThttpPrintf(&bp, "<form method=post action=\"%s%s\">\n"
+ "<tr><td colspan=4>New directory: "
+ "<input type=text name=file>\n"
+ "<td colspan=2 align=center><input type=submit name=button value=\"Create\"></td>\n"
+ "<input type=hidden name=cmd value=edit></td></tr></form>\n",
+ dir_uri, admin_file);
+ }
+ else
+ {
+ GRSThttpPrintf(&bp, "<form method=post action=\"%s%s\">\n"
+ "<tr><td colspan=8><hr width=\"75%\"></td></tr>\n"
+ "<tr><td>New name:</td>"
+ "<td colspan=3><input type=text name=file size=25>\n"
+ "<td colspan=2 align=center><input type=submit name=button value=\"New file\"></td>\n"
+ "<td colspan=2 align=center><input type=submit name=button value=\"New directory\"></td>\n"
+ "<input type=hidden name=cmd value=edit></td></tr></form>\n",
+ dir_uri, admin_file);
+
+ GRSThttpPrintf(&bp,
+ "<form method=post action=\"%s%s\" enctype=\"multipart/form-data\">\n"
+ "<tr><td colspan=8><hr width=\"75%\"></td></tr>\n"
+ "<tr><td rowspan=2>Upload file:</td>"
+ "<td colspan=2>New name:</td>"
+ "<td colspan=6><input type=text name=file size=25> "
+ "<input type=submit value=Upload></td></tr>\n"
+ "<tr><td colspan=2>Local name:</td>"
+ "<td colspan=6><input type=file name=uploadfile size=25></td></tr>\n"
+ "</form>\n", dir_uri, admin_file);
+ }
+ }
+
+ GRSThttpPrintf(&bp, "</table>\n");
+
+ if (!is_dnlists_dir) adminfooter(&bp, dn, help_uri, dir_uri, NULL);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+}
+
--- /dev/null
+/*
+ Copyright (c) 2003, Shiv Kaushal, 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/ *
+*---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <gridsite.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+extern char *grst_perm_syms[];
+extern int grst_perm_vals[];
+
+#include "grst_admin.h"
+
+// CGI GACL Editor interface functions
+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);
+void new_entry_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void new_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void edit_entry_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void edit_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void add_cred_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void add_cred(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_cred(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_entry_sure(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void del_cred_sure(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void admin_continue(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file, GRSThttpBody *bp);
+
+// Functions for producing HTML output
+void StartHTML(GRSThttpBody *bp, char *dir_uri, char* dir_path);
+void StartForm(GRSThttpBody *bp, char* dir_uri, char* dir_path, char* admin_file, int timestamp, char* target_function);
+void EndForm(GRSThttpBody *bp);
+void GRSTgaclCredTableStart(GRSThttpBody *bp);
+void GRSTgaclCredTableAdd(GRSTgaclUser *user, GRSTgaclEntry *entry, GRSTgaclCred *cred, GRSTgaclNamevalue *namevalue, int cred_no, int entry_no, int admin, int timestamp, GRSThttpBody *bp, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+void GRSTgaclCredTableEnd(GRSTgaclEntry* entry, int entry_no, int admin, int timestamp, GRSThttpBody *bp, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+
+// ACL Manipulation functions
+int GACLentriesInAcl(GRSTgaclAcl *acl);
+int GRSTgaclCredsInEntry(GRSTgaclEntry *entry);
+void check_acl_save(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file, GRSTgaclUser* user, GRSTgaclAcl *acl, GRSThttpBody *bp);
+void GACLeditGetPerms(GRSTgaclEntry *entry);
+GRSTgaclEntry *GACLreturnEntry(GRSTgaclAcl *acl, int entry_no);
+GRSTgaclCred *GACLreturnCred(GRSTgaclEntry *entry, int cred_no);
+
+void StringHTMLEncode (char* string, GRSThttpBody *bp);
+
+void revert_acl(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file);
+
+/*****************************************/
+/********** FUNCTIONS FOLLOW *************/
+/*****************************************/
+
+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
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSTgaclNamevalue *namevalue;
+ int entry_no, cred_no, allow, deny,timestamp;
+ GRSThttpBody bp;
+ char* AclFilename;
+ struct stat file_info;
+ int history_mode=0;
+
+ if (admin==2){
+ history_mode=1;
+ admin=0;
+ }
+
+ /*double-check access permision*/
+ if (!GRSTgaclPermHasAdmin(perm)) admin=0;
+
+ StartHTML(&bp, dir_uri, dir_path);
+
+ /* Load ACL from file and get timestamp*/
+ if (history_mode==1) {
+ AclFilename=malloc(strlen(dir_path)+strlen(file)+2);
+ strcpy(AclFilename, dir_path);
+ strcat(AclFilename, "/");
+ strcat(AclFilename, file);
+ }
+ else AclFilename=GRSTgaclFileFindAclname(dir_path);
+
+ if (AclFilename==NULL){
+ GRSThttpPrintf ( &bp,"The ACL was not found !!!<br>\n");
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+ }
+
+ stat(GRSTgaclFileFindAclname(dir_path), &file_info);
+ timestamp=file_info.st_mtime;
+ acl = GRSTgaclAclLoadFile(AclFilename);
+
+ if (acl==NULL){
+ GRSThttpPrintf ( &bp,"The ACL was found but could not be loaded - it could be incorrectly formatted<br>\n");
+ adminfooter(&bp, dn, help_uri, dir_uri, NULL);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(&bp);
+ return;
+ }
+
+ if (admin) GRSThttpPrintf (&bp,"<a href=\"%s%s?cmd=new_entry_form&diruri=%s×tamp=%d\">New Entry</a><br>\n", dir_uri, admin_file, dir_uri, timestamp );
+
+ // Start with the first entry in the list and work through
+ entry=acl->firstentry;
+ entry_no=1;
+ while (entry!=NULL){
+
+ GRSThttpPrintf (&bp,"<br>Entry %d:\n", entry_no);
+ if (admin){
+ GRSThttpPrintf (&bp,"<a href=\"%s%s?cmd=edit_entry_form&entry_no=%d&diruri=%s×tamp=%d\">Edit Entry</a> ", dir_uri, admin_file, entry_no, dir_uri, timestamp );
+ GRSThttpPrintf (&bp,"<a href=\"%s%s?cmd=del_entry_sure&entry_no=%d&diruri=%s×tamp=%d\">Delete Entry</a> ",dir_uri, admin_file, entry_no, dir_uri, timestamp );
+ GRSThttpPrintf (&bp,"<p>\n");
+ }
+
+ GRSTgaclCredTableStart(&bp);
+
+ // Start with the first credential in the entry and work through
+ cred=entry->firstcred;
+ cred_no=1;
+ while (cred!=NULL){
+ namevalue=cred->firstname;
+ GRSTgaclCredTableAdd(user, entry, cred, namevalue, cred_no, entry_no, admin, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ // Change to next credential
+ cred=cred->next;
+ cred_no++;
+ }
+
+ GRSTgaclCredTableEnd (entry, entry_no, admin, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ // Change to next entry
+ entry=entry->next;
+ entry_no++;
+ }
+
+ if (!admin && GRSTgaclPermHasAdmin(perm) && !history_mode) //Print a link for admin mode, if not in admin mode but the user has admin permissions
+ GRSThttpPrintf (&bp,"<a href=\"%s%s?cmd=admin_acl&diruri=%s×tamp=%d\">Admin Mode</a>", dir_uri, admin_file, dir_uri, timestamp );
+ if (history_mode==1 && GRSTgaclDNlistHasUser(getenv("REDIRECT_GRST_ADMIN_LIST"), user)){
+ StartForm(&bp, dir_uri, dir_path, admin_file, timestamp, "revert_acl");
+//GRSThttpPrintf (&bp,"<a href=\"%s%s?cmd=revert_acl&diruri=%s×tamp=%d&file=%s\">Revert to this Version</a>", dir_uri, admin_file, dir_uri, timestamp, file );
+ GRSThttpPrintf (&bp, "<input type=\"hidden\" name=\"file\" value=\"%s\">\n", file);
+ // Revert Button
+ GRSThttpPrintf (&bp, "<p align=center><input type=\"submit\" value=\"Revert to this ACL\" name=\"B1\"></p>\n</form>\n");
+ }
+
+ adminfooter(&bp, dn, help_uri, dir_uri, NULL);
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE); GRSThttpWriteOut(&bp); return;
+}
+
+
+void new_entry_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm,char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Presents the user with a form asking for details required to create a new entry
+ GRSThttpBody bp;
+ int timestamp=atol(GRSThttpGetCGI("timestamp"));
+ GRSTgaclCred* cred;
+ GRSTgaclEntry *entry;
+ GRSTgaclNamevalue* namevalue;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ StartHTML(&bp, dir_uri, dir_path);
+ StartForm(&bp, dir_uri, dir_path, admin_file, timestamp, "new_entry");
+ GRSThttpPrintf (&bp, "<font size=\"4\"><b>NEW ENTRY IN ACL FOR %s </b></font></p>\n", dir_uri);
+
+ GRSTgaclCredTableStart(&bp);
+ GRSTgaclCredTableAdd(user, entry,cred, namevalue, 0, 0, 0, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ GRSTgaclCredTableEnd (entry, 0, 0, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+
+ /*Submit and reset buttons - submit button sends the data in the form back to the script & new_entry() to be called*/
+ EndForm(&bp);
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+}
+
+void new_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Processes the information entered into the form from new_entry_form() and adds a new entry to the ACL
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ char *type, *value;
+ GRSThttpBody bp;
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ // Get new credential info and perform checks
+ type=GRSThttpGetCGI("type");
+ value=GRSThttpGetCGI("cred0_value");
+
+ if (strcmp(type, "not_chosen")==0){
+ GRSThttpError ("500 Invalid input - credential type not chosen");
+ return;
+ }
+
+ // Create the credential
+ cred=GRSTgaclCredNew(type);
+ if (strcmp(type, "person")==0) GRSTgaclCredAddValue(cred,"dn", value);
+ else if (strcmp(type, "dn-list")==0) GRSTgaclCredAddValue(cred, "url", value);
+ else if (strcmp(type, "voms")==0) GRSTgaclCredAddValue(cred, "fqan", value);
+ else if (strcmp(type, "dns")==0) GRSTgaclCredAddValue(cred, "hostname", value);
+ else if (strcmp(type, "any-user")==0) {} // namevalue not entered for any-user credential
+ else{
+ GRSThttpError ("500 Invalid input - credential type not valid");
+ return;
+ }
+
+ // Create and empty entry, add the credential and get permissions
+ entry = GRSTgaclEntryNew();
+ GRSTgaclEntryAddCred(entry, cred);
+ GACLeditGetPerms(entry);
+
+ // Load the ACL, add the entry and save
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));
+ GRSTgaclAclAddEntry(acl, entry);
+ check_acl_save(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, user, acl, &bp);
+ return;
+}
+
+void del_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Deletes the entry denoted by the GCI variable "entry_no"*/
+ int entry_no;
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *previous, *entry;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ // Load the ACL
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));
+
+ // Get the number of the entry to be deleted and check okay to delete
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ if(GACLentriesInAcl(acl)<=1){
+ StartHTML(&bp, dir_uri, dir_path);
+ GRSThttpPrintf (&bp, "ERROR: Cannot delete all entries from the ACL<br>\n");
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+ }
+
+ // Get pointer to entry and previous entry
+ entry = GACLreturnEntry(acl, entry_no);
+ if (entry_no!=1) previous = GACLreturnEntry(acl, entry_no-1);
+
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read entry from ACL file");
+ return;
+ }
+
+ // Perform deletion from the list by changing pointers
+ if (entry_no==1) acl->firstentry=entry->next;
+ else if (entry_no==GACLentriesInAcl(acl)) previous->next=NULL;
+ else previous->next=entry->next;
+
+ // Save ACL and exit
+ check_acl_save(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, user, acl, &bp);
+
+ return;
+}
+
+
+void edit_entry_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Presents the user with an editable form containing details of entry denoted by CGI variable entry_no*/
+ int entry_no, cred_no, i, admin=0, timestamp=atol(GRSThttpGetCGI("timestamp"));
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSTgaclNamevalue *namevalue;
+ // struct _GACLnamevalue *namevalue;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ // Load ACL from file
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));
+
+ // Get pointer to the entry and check okay
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ StartHTML(&bp, dir_uri, dir_path);
+ GRSThttpPrintf (&bp, "<b><font size=\"4\">EDITING ENTRY %d IN ACL FOR %s </font></b></p>\n", entry_no, dir_uri);
+
+ // Start with first credential in the entry and display them in order*/
+ cred=entry->firstcred;
+ cred_no=1;
+ StartForm(&bp, dir_uri, dir_path, admin_file, timestamp, "edit_entry");
+ GRSThttpPrintf (&bp, "<input type=\"hidden\" name=\"entry_no\" value=\"%d\">\n", entry_no);
+
+ GRSTgaclCredTableStart(&bp);
+
+ while (cred!=NULL){
+ // Start with the first namevalue in the credential
+ namevalue=cred->firstname;
+ GRSTgaclCredTableAdd(user, entry, cred, namevalue, cred_no, entry_no, admin, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ // Change to next credential
+ cred=cred->next;
+ cred_no++;
+ }
+ GRSTgaclCredTableEnd (entry, entry_no, admin, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ EndForm(&bp);
+
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+}
+
+
+void edit_entry(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ //Processes the information entered into the form from edit_entry_form() and updates the entry corresponding to entry_no*/
+ int entry_no, cred_no, i;
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSTgaclNamevalue *namevalue;
+ char variable[30];
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ // Load the ACL
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));
+
+ // Get pointer to the entry and perform checks
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ // Start with the first credential and update each one
+ cred=entry->firstcred;
+ cred_no=1;
+
+ while (cred!=NULL){
+ if (strcmp(cred->type, "any-user")!=0){
+ namevalue=cred->firstname;
+ sprintf(variable, "cred%d_value", cred_no);
+ namevalue->value=GRSThttpGetCGI(variable);
+ }
+ //Change to next credential*/
+ cred=cred->next;
+ cred_no++;
+ }
+
+ // Update permissions
+ GACLeditGetPerms(entry);
+ check_acl_save(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, user, acl, &bp);
+ return;
+}
+
+
+void add_cred_form(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Presents the user with a form asking for details required to create a new credential in the entry denoted by entry_no
+ GRSThttpBody bp;
+ int timestamp=atol(GRSThttpGetCGI("timestamp")), entry_no=atol(GRSThttpGetCGI("entry_no"));
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry* entry;
+ GRSTgaclCred* cred;
+ GRSTgaclNamevalue* namevalue;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path)); // Load the ACL
+
+ //Get pointer to the entry and perform checks
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+
+ if (strcmp(GRSThttpGetCGI("cmd"), "add_cred_form")==0){ //if not a new entry check to see if <any-user> cred exists
+ cred=entry->firstcred;
+ while (cred!=NULL) {
+ if (strcmp (cred->type, "any-user")==0) {
+ StartHTML(&bp, dir_uri, dir_path);
+ GRSThttpPrintf (&bp, "ERROR: AND-ing \"any-user\" credential with other credential does not make sense <br>\n");
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+ }
+ cred=cred->next;
+ }
+ }
+
+ StartHTML(&bp, dir_uri, dir_path);
+ GRSThttpPrintf (&bp, " <font size=\"4\"><b>NEW CREDENTIAL IN ENTRY %d OF ACL FOR %s</b></font></p>\n", entry_no, dir_uri);
+ StartForm(&bp, dir_uri, dir_path, admin_file, timestamp, "add_cred");
+
+ GRSThttpPrintf (&bp, " <input type=\"hidden\" name=\"entry_no\" value=\"%d\">\n", entry_no);
+
+ GRSTgaclCredTableStart(&bp);
+ GRSTgaclCredTableAdd(user, entry, cred, namevalue, 0, 0, 0, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ GRSTgaclCredTableEnd (entry, 0, 0, timestamp, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+
+ EndForm(&bp);
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+}
+
+
+void add_cred(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Processes the information entered into the form [add_cred_form()]and adds a new credential to the entry corresponding to entry_no
+ int entry_no;
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSThttpBody bp;
+ char *type, *value;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));// Load the ACL
+
+ // Get pointer to the entry and perform checks
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl)){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ // Create new credential and add it to entry
+ type=GRSThttpGetCGI("type");
+ value=GRSThttpGetCGI("cred0_value");
+ cred=GRSTgaclCredNew(type);
+ if (strcmp(type, "person") ==0) GRSTgaclCredAddValue(cred,"dn", value);
+ else if (strcmp(type, "dn-list") ==0) GRSTgaclCredAddValue(cred, "url", value);
+ else if (strcmp(type, "voms") ==0) GRSTgaclCredAddValue(cred, "fqan", value);
+ else if (strcmp(type, "dns") ==0) GRSTgaclCredAddValue(cred, "hostname", value);
+ else if (strcmp(type, "any-user")==0) {}// namevalue not entered for any-user credential
+ else{
+ GRSThttpError ("500 Credential type not valid");
+ return;
+ }
+ GRSTgaclEntryAddCred(entry, cred);
+
+ check_acl_save(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, user, acl, &bp);
+ return;
+}
+
+
+void del_cred(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Deletes the credential denoted by the GCI variable "cred_no", in the entry denoted by "entry_no"
+ int entry_no, cred_no;
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *previous, *cred;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));
+
+ // Get pointer to the entry and perform checks
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+ // Get pointer the the credential and perform checks
+ cred_no=atol(GRSThttpGetCGI("cred_no"));
+ cred=GACLreturnCred(entry, cred_no);
+ if(entry==NULL || entry_no<1 || cred_no>GRSTgaclCredsInEntry(entry)){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+ // Get pointer to previous credential - if needed
+ if (cred_no!=1) previous = GACLreturnCred(entry, cred_no-1);
+
+ // Perform deletion from the list by changing pointers
+ if (cred_no==1) entry->firstcred=cred->next;
+ else if (cred_no==GRSTgaclCredsInEntry(entry)) previous->next=NULL;
+ else previous->next=cred->next;
+
+ check_acl_save(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, user, acl, &bp);
+ return;
+}
+
+void admin_continue(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file, GRSThttpBody *bp){
+ // Single line printed out to forward users back to show_acl in admin mode
+ // Should ALWAYS called from another function so no HTML header required
+ // Should ALWAYS be the end of a page
+ GRSThttpPrintf (bp, "\n<br><a href=\"%s%s?diruri=%s&cmd=admin_acl×tamp=%d\">Click Here</a> to return to the editor", dir_uri,admin_file,dir_uri, time(NULL));
+ adminfooter(bp, dn, help_uri, dir_uri, NULL);
+ GRSThttpPrintHeaderFooter(bp, dir_path, GRST_FOOTFILE);
+ GRSThttpWriteOut(bp);
+ return;
+}
+
+
+void del_entry_sure(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Prints out entry denoted by entry_no and asks if the user really wants to delete it
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSTgaclNamevalue *namevalue;
+ int entry_no, cred_no, allow, deny, i, timestamp;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));// Load ACL from file
+
+ if (acl==NULL){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ // Get pointer to the entry and check okay
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ StartHTML(&bp, dir_uri, dir_path);
+ GRSThttpPrintf (&bp, "<h1 align=center>Do you really want to delete the following entry?</h1><br><br>\n");
+ GRSThttpPrintf (&bp,"<br>Entry %d:<br>\n", entry_no);
+
+ // Print the entry out
+ // Start with the first credential in the entry and work through
+ cred=entry->firstcred;
+ cred_no=1;
+
+ GRSTgaclCredTableStart(&bp);
+ while (cred!=NULL){
+ // Start with the first namevalue in the credential
+ namevalue=cred->firstname;
+ GRSTgaclCredTableAdd(user, entry, cred, namevalue, cred_no, entry_no, 0, 0, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ // Change to next credential
+ cred=cred->next;
+ cred_no++;
+ }
+
+ GRSTgaclCredTableEnd (entry, entry_no, 0, 0, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+
+ StartForm(&bp, dir_uri, dir_path, admin_file, atol(GRSThttpGetCGI("timestamp")), "del_entry");
+ GRSThttpPrintf (&bp, "<input type=\"hidden\" name=\"entry_no\" value=\"%d\">\n", entry_no);
+ GRSThttpPrintf (&bp, " <p align=center><input type=\"submit\" value=\"Yes\" name=\"B1\"></p>\n</form>\n");
+
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+}
+
+void del_cred_sure(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Prints out credential denoted by entry_no/cred_no and asks if the user really wants to delete it
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSTgaclNamevalue *namevalue;
+ int entry_no, cred_no, allow, deny, timestamp, i;
+ GRSThttpBody bp;
+
+ if (!GRSTgaclPermHasAdmin(perm)) GRSThttpError ("403 Forbidden");
+
+ acl = GRSTgaclAclLoadFile(GRSTgaclFileFindAclname(dir_path));// Load ACL from file
+
+ if (acl==NULL){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ // Get pointer to the entry and check okay
+ entry_no=atol(GRSThttpGetCGI("entry_no"));
+ entry = GACLreturnEntry(acl, entry_no);
+ if(entry==NULL || entry_no<1 || entry_no>GACLentriesInAcl(acl) ){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ // Get pointer to the credential and check okay
+ cred_no=atol(GRSThttpGetCGI("cred_no"));
+ cred=GACLreturnCred(entry, cred_no);
+ if(entry==NULL || entry_no<1 || cred_no>GRSTgaclCredsInEntry(entry)){
+ GRSThttpError ("500 Unable to read from ACL file");
+ return;
+ }
+
+ if(GRSTgaclCredsInEntry(entry)<=1){
+ del_entry_sure(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ return;
+ }
+
+ StartHTML(&bp, dir_uri, dir_path);
+ GRSThttpPrintf (&bp, "<h1 align=center>Do you really want to delete the following credential from entry %d?</h1><br><br>", entry_no);
+
+ // Print the credential out
+ GRSTgaclCredTableStart(&bp);
+ GRSTgaclCredTableAdd(user, entry, cred, cred->firstname, cred_no, entry_no, 0, 0, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ GRSTgaclCredTableEnd (entry, entry_no, 0, 0, &bp, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ GRSThttpPrintf (&bp,"<br>\n");
+
+ // Yes Button
+ StartForm(&bp, dir_uri, dir_path, admin_file, atol(GRSThttpGetCGI("timestamp")), "del_cred");
+ GRSThttpPrintf (&bp, "<input type=\"hidden\" name=\"entry_no\" value=\"%d\">\n", entry_no);
+ GRSThttpPrintf (&bp, "<input type=\"hidden\" name=\"cred_no\" value=\"%d\">\n", cred_no);
+ GRSThttpPrintf (&bp, " <p align=center><input type=\"submit\" value=\"Yes\" name=\"B1\"></p>\n</form>\n");
+
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, &bp);
+ return;
+}
+
+
+int GACLentriesInAcl(GRSTgaclAcl *acl){
+ // Returns the number of entries in acl
+ GRSTgaclEntry *entry;
+ int number;
+
+ entry=acl->firstentry;
+ number=0;
+
+ while (entry!=NULL)
+ {
+ number++;
+ entry=entry->next;
+ }
+
+ return number;
+}
+
+int GRSTgaclCredsInEntry(GRSTgaclEntry *entry){
+ // Returns the number of credentials in entry
+ int number;
+ GRSTgaclCred *cred;
+
+ cred=entry->firstcred;
+ number=0;
+
+ while (cred!=NULL)
+ {
+ number++;
+ cred=cred->next;
+ }
+
+ return number;
+}
+
+
+void GACLeditGetPerms(GRSTgaclEntry *entry){
+ // Updates the permissions entry using permissions from a form produced using GRSTgaclCredTableEnd
+ int i;
+ char buf[30];
+
+
+ for (i=0; grst_perm_syms[i]!=NULL; i++) /* Print the list of allowed permissions*/
+ {
+ sprintf (buf, "allow_%s", grst_perm_syms[i]); // Update allowed
+ if (strcmp (GRSThttpGetCGI(buf), "ON") == 0 ) GRSTgaclEntryAllowPerm(entry, grst_perm_vals[i]); else GRSTgaclEntryUnallowPerm(entry, grst_perm_vals[i]);
+
+ sprintf (buf, "deny_%s", grst_perm_syms[i]); // Update denied
+ if (strcmp (GRSThttpGetCGI(buf), "ON") == 0 ) GRSTgaclEntryDenyPerm(entry, grst_perm_vals[i]); else GRSTgaclEntryUndenyPerm(entry, grst_perm_vals[i]);
+
+ }
+
+ return;
+}
+
+GRSTgaclEntry *GACLreturnEntry(GRSTgaclAcl *acl, int entry_no){
+ // Returns a pointer to entry in ACL denoted by entry_no, returns NULL if not found
+ int number;
+ GRSTgaclEntry *entry;
+
+ if (acl==NULL) return NULL;
+
+ entry=acl->firstentry;
+ number=1;
+
+ while (entry!=NULL)
+ {
+ if (number==entry_no) return entry;
+ number++;
+ entry=entry->next;
+ }
+
+ return NULL;
+}
+
+
+GRSTgaclCred *GACLreturnCred(GRSTgaclEntry *entry, int cred_no){
+ // Returns a pointer to credential denoted by cred_no in entry, returns NULL if not found
+ int number;
+ GRSTgaclCred *cred;
+
+ if (entry==NULL) return NULL;
+
+ cred=entry->firstcred;
+ number=1;
+
+ while (cred!=NULL)
+ {
+ if (number==cred_no) return cred;
+ number++;
+ cred=cred->next;
+ }
+
+ return NULL;
+}
+void StartHTML(GRSThttpBody *bp, char *dir_uri, char* dir_path){
+ //Start HTML output and insert page title
+ printf("Status: 200 OK\nContent-Type: text/html\n");
+ GRSThttpBodyInit(bp);
+ GRSThttpPrintf(bp, "<title>Access Control List for %s</title>\n", dir_uri);
+ GRSThttpPrintHeaderFooter(bp, dir_path, GRST_HEADFILE);
+ return;
+}
+void StartForm(GRSThttpBody *bp, char* dir_uri, char* dir_path, char* admin_file, int timestamp, char* target_function){
+ // Starts an HTML form with gridsite admin as the target and target_function as the value of cmd.
+ // Also inputs the dir_uri and the timestamp
+ GRSThttpPrintf (bp, "<form method=\"POST\" action=\"%s%s?diruri=%s\">\n", dir_uri, admin_file, dir_uri);
+ GRSThttpPrintf (bp, " <input type=\"hidden\" name=\"cmd\" value=\"%s\">\n", target_function);
+ GRSThttpPrintf (bp, " <input type=\"hidden\" name=\"timestamp\" value=\"%d\">\n", timestamp);
+ return;
+}
+
+void EndForm(GRSThttpBody *bp){
+ GRSThttpPrintf (bp, " <br><input type=\"submit\" value=\"Submit\" name=\"B1\"><input type=\"reset\" value=\"Reset\" name=\"B2\"></p>\n");
+ GRSThttpPrintf (bp, "</form>\n");
+ return;
+}
+
+void GRSTgaclCredTableStart(GRSThttpBody *bp){
+ //Starts an HTML table of credentials by setting the column widths and inputting the headings
+ GRSThttpPrintf (bp,"<table border=\"1\" cellpadding=\"2\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"100%\" id=\"CredentialTable\">");
+ GRSThttpPrintf (bp,"<tr><td align=center width=\"10%\"><b>Credential No.</td><td align=center width=\"15%\"><b>Type</td><td align=left width=\"75%\"><b>Value</td></tr>");
+ return;
+}
+
+void GRSTgaclCredTableAdd(GRSTgaclUser *user, GRSTgaclEntry *entry, GRSTgaclCred *cred, GRSTgaclNamevalue *namevalue, int cred_no, int entry_no, int admin, int timestamp, GRSThttpBody *bp, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Adds the credential "cred" to a table started byGRSTgaclCredTableStart allowing the user to edit if appropriate
+ char* cmd = GRSThttpGetCGI("cmd");
+ int edit_values=0, new_cred=0, allow_new_person=1;
+ int site_admin=GRSTgaclDNlistHasUser(getenv("REDIRECT_GRST_ADMIN_LIST"), user);
+
+ if (strcmp(cmd, "new_entry_form")==0 || strcmp(cmd, "add_cred_form")==0) new_cred=1;
+ if (new_cred || strcmp(cmd, "edit_entry_form")==0) edit_values=1;
+
+ if (new_cred) { /*Print out type and descriptor*/
+ if (strcmp(cmd, "add_cred_form")==0){ /*if not a new entry check to see if <person> cred exists.*/
+ cred=entry->firstcred;
+ while (cred!=NULL) {if (strcmp (cred->type, "person")==0) allow_new_person=0; cred=cred->next;}
+ }
+ //create dummy credential for the user to edit
+ cred=GRSTgaclCredNew("new");
+ GRSTgaclCredAddValue(cred, "", "");
+ namevalue=cred->firstname;
+ //Drop down list of types
+ GRSThttpPrintf(bp,"<tr><td align=center >New</td>");
+ GRSThttpPrintf(bp,"<td align=center >");
+ GRSThttpPrintf (bp, " <select size=\"1\" name=\"type\">\n");
+ GRSThttpPrintf (bp, " <option selected value=\"not_chosen\">(choose)</option>\n");
+ if (allow_new_person) GRSThttpPrintf (bp, " <option value=\"person\">Person <dn> </dn></option>\n");
+ GRSThttpPrintf (bp, " <option value=\"dn-list\">DN-List <url> </url></option>\n");
+ GRSThttpPrintf (bp, " <option value=\"dns\">DNS <hostname> </hostname></option>\n");
+ GRSThttpPrintf (bp, " <option value=\"voms\">VOMS <fqan> </fqan></option>\n");
+ // Only alow any-user credential to be chosen if it is new entry
+ if (strcmp(cmd, "new_entry_form")==0) GRSThttpPrintf (bp, " <option value=\"any-user\">Any User</option>\n");
+ GRSThttpPrintf (bp, " </select></td>");
+ }
+
+ else { //Print out type and descriptor for existing cred
+
+ GRSThttpPrintf(bp,"<tr><td align=center >%d", cred_no);
+ if (admin) GRSThttpPrintf (bp,"<a href=\"%s%s?diruri=%s&cmd=del_cred_sure&entry_no=%d&cred_no=%d×tamp=%d\">(Delete)</a>", dir_uri,admin_file,dir_uri, entry_no, cred_no, timestamp);
+ GRSThttpPrintf(bp, "</td><td align=center >%s ", cred->type);
+ }
+
+ if (strcmp(cred->type, "any-user")==0) GRSThttpPrintf (bp, "</td><td> "); /* Do not print out namevalue for any-user credential*/
+ else{
+ if (edit_values){ // Place namevalue in an editable box if appropriate
+ GRSThttpPrintf (bp, "<td align=left><input type=\"text\" name=\"cred%d_value\"\n", cred_no);
+ GRSThttpPrintf (bp, "size=\"50\" value=\"");
+ StringHTMLEncode(namevalue->value, bp);
+ GRSThttpPrintf (bp, "\">");
+ }
+ else if (strcmp(cred->type, "dn-list")==0){
+ GRSThttpPrintf(bp, "<td align=left ><a href=\"");
+ StringHTMLEncode(namevalue->value, bp);
+ GRSThttpPrintf(bp, " \">");
+ StringHTMLEncode(namevalue->value, bp);
+ GRSThttpPrintf(bp, "</a>");
+ }
+ else { GRSThttpPrintf(bp, "<td align=left> "); StringHTMLEncode(namevalue->value, bp);}
+
+ }
+ //Print out warning symbol if cred being printed relates to current user - but NOT for users in site admin list
+ if (GRSTgaclUserHasCred(user, cred) && !site_admin) GRSThttpPrintf(bp, "<font color=red><b> <--</b></font>");
+ GRSThttpPrintf(bp, "</td></tr>");
+}
+
+void GRSTgaclCredTableEnd(GRSTgaclEntry* entry, int entry_no, int admin, int timestamp, GRSThttpBody *bp, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ // Finishes off a table of credentials by inputting "Add Credential" link and a list of premissions in the final row
+ int i, blank_perms, edit_perms, show_perms;
+ char* cmd = GRSThttpGetCGI("cmd");
+
+ if (strcmp(cmd, "add_cred_form")==0 ||strcmp(cmd, "del_cred_sure")==0) show_perms=0; else show_perms=1;
+ if (strcmp(cmd, "edit_entry_form")==0 || strcmp(cmd, "new_entry_form")==0) edit_perms=1; else edit_perms=0;
+ if (strcmp(cmd, "new_entry_form")==0) blank_perms=1; else blank_perms=0;
+
+ // If showing the last row is not required then exit
+ if (show_perms==0){GRSThttpPrintf (bp,"</table><br>\n"); return;}
+
+ GRSThttpPrintf (bp,"<tr><td align=center>");
+
+ if (admin) GRSThttpPrintf (bp,"<a href=\"%s%s?diruri=%s&cmd=add_cred_form&entry_no=%d×tamp=%d\">Add Credential</a>", dir_uri,admin_file,dir_uri, entry_no, timestamp);
+
+ GRSThttpPrintf (bp, "</td>\n<td> </td><td align=left>");
+
+ if (blank_perms) entry->allowed=entry->denied=GRST_PERM_NONE;
+
+ // Show Permissions - will produce a list or a list of check boxes depending on whether the permissions are to be edited or not
+ GRSThttpPrintf (bp, "<b>Allowed:</b> ");
+ for (i=0; grst_perm_syms[i]!=NULL; i++) /* Print the list of allowed permissions*/
+ {
+ if ( entry->allowed & grst_perm_vals[i]){
+ if (edit_perms) GRSThttpPrintf (bp, "%s<input type=\"checkbox\" name=\"allow_%s\" value=\"ON\" checked> \n", grst_perm_syms[i],grst_perm_syms[i]);
+ else GRSThttpPrintf(bp,"%s ", grst_perm_syms[i]); if (strcmp(grst_perm_syms[i], "none")==0) break;
+ }
+ else if (strcmp(grst_perm_syms[i], "none")!=0 && edit_perms) GRSThttpPrintf (bp, "%s<input type=\"checkbox\" name=\"allow_%s\" value=\"ON\" unchecked> \n", grst_perm_syms[i],grst_perm_syms[i]);
+ }
+
+ if (edit_perms) GRSThttpPrintf (bp, "<p>");
+ GRSThttpPrintf (bp, "<b>Denied: </b>");
+ for (i=0; grst_perm_syms[i]!=NULL; i++) /* Print the list of denied permissions*/
+ {
+ if ( entry->denied & grst_perm_vals[i])
+ {
+ if (edit_perms) GRSThttpPrintf (bp, "%s<input type=\"checkbox\" name=\"deny_%s\" value=\"ON\" checked> \n", grst_perm_syms[i],grst_perm_syms[i]);
+ else GRSThttpPrintf(bp,"%s ", grst_perm_syms[i]);
+ if (strcmp(grst_perm_syms[i], "none")==0) break;
+ }
+ else if (strcmp(grst_perm_syms[i], "none")!=0 && edit_perms) GRSThttpPrintf (bp, "%s<input type=\"checkbox\" name=\"deny_%s\" value=\"ON\" unchecked> \n", grst_perm_syms[i],grst_perm_syms[i]);
+ }
+
+ GRSThttpPrintf (bp, "</td></tr>");
+ GRSThttpPrintf (bp,"</table><br>\n");
+ GRSThttpPrintf (bp,"\n");
+}
+
+void check_acl_save(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file, GRSTgaclUser* user, GRSTgaclAcl *acl, GRSThttpBody *bp){
+ // Checks if the acl for the current directory has been changed, check the current user's permissions.
+ // If all is okay the ACl is saved -> returns 1 else returns 0
+ struct stat file_info;
+ GRSTgaclPerm new_perm;
+ char *vfile, *dir_path_vfile, *dir_path_file;
+ FILE *fp;
+
+
+ /*Check ACL has not been modified*/
+ stat(GRSTgaclFileFindAclname(dir_path), &file_info);
+ if (atol(GRSThttpGetCGI("timestamp"))!=file_info.st_mtime){
+ StartHTML(bp, dir_uri, dir_path);
+ GRSThttpPrintf (bp, "ERROR: CANNOT SAVE CHANGES<p><p> The ACL has been modified since it was last viewed\n<p>");
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, bp);
+ return;
+ }
+
+ // check users permissions in the new ACL
+
+ if (!GRSTgaclDNlistHasUser(getenv("REDIRECT_GRST_ADMIN_LIST"), user))
+ {
+ new_perm = GRSTgaclAclTestUser(acl, user);
+ if (new_perm != perm){
+ StartHTML(bp, dir_uri, dir_path);
+ if (!GRSTgaclPermHasAdmin(new_perm)){//Check that user still has Admin permissions - if not then exit without saving the new ACL
+ GRSThttpPrintf (bp, "ERROR: CANNOT SAVE CHANGES\n\n<p><p> You cannot deny yourself admin access from within the editor\n");
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, bp);
+ return;
+ }
+ //Functions to inform of other permission changes come next
+ GRSThttpPrintf (bp, "WARNING: OPERATION CHANGED YOUR PERMISSIONS!\n\n<p><p> You still have Admin permissions<p>\n");
+ admin_continue(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, bp);
+ return;
+ }
+ }
+ // ACL not modified, notified of permission changes - can now save
+
+ dir_path_file=GRSTgaclFileFindAclname(dir_path);
+ vfile=makevfilename(".gacl", file_info.st_size, dn); // Make temporary file name
+ dir_path_vfile = malloc(strlen(dir_path) + strlen(vfile) + 2);
+ strcpy(dir_path_vfile, dir_path);
+ strcat(dir_path_vfile, "/");
+ strcat(dir_path_vfile, vfile);
+
+ GRSTgaclAclSave(acl, dir_path_vfile); // save the new ACL to the temporary file
+ unlink(dir_path_file);
+ if (link (dir_path_vfile,dir_path_file)!=0) GRSThttpError("403 Forbidden");
+
+ printf ("Status: 302 Moved Temporarily\n Content Length: 0\nLocation: %s%s?cmd=admin_acl\n\n", dir_uri, admin_file);
+ return;
+}
+
+void StringHTMLEncode (char* string, GRSThttpBody *bp){
+
+ char* current_char;
+ char* tmp;
+ int n;
+ tmp=malloc(2);
+
+ *(tmp+1)='\0';
+ current_char=string;
+ while(*current_char != '\0'){
+
+ if (*current_char == '<') GRSThttpPrintf (bp,"<");
+ else if (*current_char == '>') GRSThttpPrintf (bp,">");
+ else if (*current_char == '&') GRSThttpPrintf (bp,"&");
+ else if (*current_char == '\'') GRSThttpPrintf (bp,"'");
+ else if (*current_char == '"') GRSThttpPrintf (bp,""");
+ else{
+ *tmp=*current_char;
+ GRSThttpPrintf(bp, "%s", tmp);
+
+ }
+ current_char++;
+ }
+ return;
+}
+
+void revert_acl(GRSTgaclUser *user, char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path, char *file, char *dir_uri, char *admin_file){
+ char *AclFilename;
+ GRSTgaclAcl *acl;
+ GRSThttpBody bp;
+ // Load the old ACL, add the entry and save
+ AclFilename=malloc(strlen(dir_path)+strlen(file)+2);
+ strcpy(AclFilename, dir_path);
+ strcat(AclFilename, "/");
+ strcat(AclFilename, file);
+
+ acl = GRSTgaclAclLoadFile(AclFilename);
+ check_acl_save(dn, perm, help_uri, dir_path, file, dir_uri, admin_file, user, acl, &bp);
+ return;
+}
--- /dev/null
+/*
+ Andrew McNab and Shiv Kaushal, University of Manchester.
+ Copyright (c) 2002-3. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*---------------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef VERSION
+#define VERSION "x.x.x"
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+// when porting: remember that sendfile() is very OS-specific!
+#include <sys/sendfile.h>
+
+#include <gridsite.h>
+
+#include "grst_admin.h"
+
+/*
+
+ GridSite human/interactive management interface. This should produce
+ a CGI executable, usually ./sbin/real-gridsite-admin.cgi, which is
+ called from HTML forms either by GET or POST methods or both (ie input
+ present in both QUERY_STRING and the stdin of the CGI process.)
+
+ The CGI name/value pairs used are:
+
+ cmd = edit, managedir, print, history
+ file = short name of file, without path
+
+ If real-gridsite-admin.cgi is run by an internal redirection inside
+ mod_gridsite (as should ALWAYS be the case) then the environment
+ variable REDIRECT_GRST_DIR_PATH will be set to the full path of
+ the directory holding the file in question. This respects any complex
+ URI -> file path mapping done by Apache.
+
+*/
+
+void GRSThttpError(char *status)
+{
+ printf("Status: %s\n", status);
+ printf("Server-CGI: GridSite Admin %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);
+}
+
+void adminfooter(GRSThttpBody *bp, char *dn, char *help_uri, char *dir_uri,
+ char *admin_file)
+{
+ GRSThttpPrintf(bp, "<p><small>\n");
+
+ if (dn != NULL) GRSThttpPrintf(bp, "<hr>You are %s<br>\n", dn);
+ else GRSThttpPrintf(bp, "<hr>\n");
+
+ if (admin_file != NULL)
+ GRSThttpPrintf(bp, "<a href=\"%s%s?cmd=managedir\">"
+ "Manage directory</a> .\n",
+ dir_uri, admin_file);
+ else GRSThttpPrintf(bp, "<a href=\"%s\">"
+ "Back to directory</a> .\n", dir_uri);
+
+ if (help_uri != NULL)
+ GRSThttpPrintf(bp, "<a href=\"%s\">Website Help</a> .\n", help_uri);
+
+ 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",
+ VERSION);
+
+ GRSThttpPrintf(bp, "</small>\n");
+}
+
+int GRSTstrCmpShort(char *long_s, char *short_s)
+{
+ while (*short_s != '\0')
+ {
+ if (*long_s > *short_s) return +1;
+ if (*long_s < *short_s) return -1;
+
+ ++long_s;
+ ++short_s;
+ }
+
+ return 0;
+}
+
+char *makevfilename(char *publicname, size_t size, char *dn)
+{
+ int i;
+ char *ext, *vfilename, *encpublicname, *encdn, *p;
+ struct timeval tv_now;
+
+ gettimeofday(&tv_now, NULL);
+
+ ext = rindex(publicname, '.');
+ if (ext == NULL) ext = "";
+
+ encpublicname = GRSThttpUrlEncode(publicname);
+ for (p=encpublicname; *p != '\0'; ++p) if (*p == '%') *p = '=';
+
+ encdn = GRSThttpUrlEncode(dn);
+ for (p=encdn; *p != '\0'; ++p) if (*p == '%') *p = '=';
+
+ /* we used zero-padding for times so
+ alphanumeric sorting will sort chronologically too */
+
+ asprintf(&vfilename, "%s:%s:%08X:%05X:%X:%s:%s", GRST_HIST_PREFIX,
+ encpublicname, tv_now.tv_sec, tv_now.tv_usec, size, encdn, ext);
+
+ return vfilename;
+}
+
+void justheader(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *dir_uri, char *admin_file)
+{
+ GRSThttpBody bp;
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_HEADFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+void justfooter(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
+ char *dir_uri, char *admin_file)
+{
+ GRSThttpBody bp;
+
+ puts("Status: 200 OK\nContent-Type: text/html");
+
+ GRSThttpBodyInit(&bp);
+
+ if (GRSTgaclPermHasList(perm) || GRSTgaclPermHasWrite(perm)
+ || GRSTgaclPermHasAdmin(perm))
+ adminfooter(&bp, dn, help_uri, dir_uri, admin_file);
+
+ GRSThttpPrintHeaderFooter(&bp, dir_path, GRST_FOOTFILE);
+
+ GRSThttpWriteOut(&bp);
+}
+
+int main()
+{
+ int gsiproxylimit_i = 1;
+ char *cmd, *dir_uri, *file, *dir_path, *admin_file, *dn = NULL,
+ *help_uri, *p, *content_type, *request_uri, *button,
+ *grst_cred_0, *gsiproxylimit, *dn_lists;
+ GRSTgaclCred *cred;
+ GRSTgaclUser *user = NULL;
+ GRSTgaclAcl *acl;
+ GRSTgaclPerm perm = GRST_PERM_NONE;
+
+ help_uri = getenv("REDIRECT_GRST_HELP_URI"); /* can be NULL */
+ admin_file = getenv("REDIRECT_GRST_ADMIN_FILE");
+ dir_path = getenv("REDIRECT_GRST_DIR_PATH");
+ request_uri = getenv("REQUEST_URI");
+
+ if ((dir_path == NULL) || (admin_file == NULL) || (request_uri == NULL))
+ {
+ puts("Status: 500 Internal Server Error\nContent-type: text/plain\n\n"
+ "REDIRECT_GRST_DIR_PATH or REDIRECT_GRST_ADMIN_FILE "
+ "or REQUEST_URI missing");
+ return;
+ }
+
+ GRSTgaclInit();
+
+ grst_cred_0 = getenv("GRST_CRED_0");
+
+ if ((grst_cred_0 != NULL) && (cred = GRSTx509CompactToCred(grst_cred_0)))
+ {
+ gsiproxylimit = getenv("REDIRECT_GRST_GSIPROXY_LIMIT");
+ if (gsiproxylimit != NULL) sscanf(gsiproxylimit, "%d", &gsiproxylimit_i);
+
+ if (GRSTgaclCredGetDelegation(cred) <= gsiproxylimit_i)
+ {
+ user = GRSTgaclUserNew(cred);
+
+ if ((p = index(grst_cred_0, ' ')) &&
+ (p = index(++p, ' ')) &&
+ (p = index(++p, ' ')) &&
+ (p = index(++p, ' '))) dn = &p[1];
+ }
+ }
+ else if ((dn = getenv("SSL_CLIENT_S_DN")) != NULL)
+ {
+ cred = GRSTgaclCredNew("person");
+ GRSTgaclCredAddValue(cred, "dn", dn);
+ user = GRSTgaclUserNew(cred);
+ }
+
+ dn_lists = getenv("REDIRECT_GRST_DN_LISTS");
+ if (dn_lists == NULL) dn_lists = getenv("GRST_DN_LISTS");
+ if (dn_lists != NULL) GRSTgaclUserSetDNlists(user, dn_lists);
+
+ if (GRSTgaclDNlistHasUser(getenv("REDIRECT_GRST_ADMIN_LIST"),
+ user)) perm = GRST_PERM_ALL;
+ else
+ {
+ p = getenv("REMOTE_HOST");
+ if (p != NULL)
+ {
+ cred = GRSTgaclCredNew("dns");
+ GRSTgaclCredAddValue(cred, "hostname", p);
+
+ if (user == NULL) user = GRSTgaclUserNew(cred);
+ else GRSTgaclUserAddCred(user, cred);
+ }
+
+ acl = GRSTgaclAclLoadforFile(dir_path);
+ if (acl != NULL) perm = GRSTgaclAclTestUser(acl, user);
+ }
+
+ /* we're relying on being a CGI with all this un-free()ed strdup()ing */
+
+ dir_uri = strdup(request_uri);
+ p = rindex(dir_uri, '?');
+ if (p != NULL) *p = '\0';
+ p = rindex(dir_uri, '/');
+ if (p != NULL) p[1] = '\0';
+
+ content_type = getenv("CONTENT_TYPE");
+
+ if ((content_type != NULL) &&
+ (GRSTstrCmpShort(content_type, "multipart/form-data; boundary=") == 0))
+ {
+ uploadfile(dn, perm, help_uri, dir_path, dir_uri, admin_file);
+ return 0;
+ }
+
+ cmd = GRSThttpGetCGI("cmd");
+ file = GRSThttpGetCGI("file");
+ button = GRSThttpGetCGI("button");
+
+ /* file and directory functions in grst_admin_file.c */
+
+ if (strcmp(cmd, "header") == 0)
+ justheader(dn, perm, help_uri, dir_path, dir_uri, admin_file);
+ else if (strcmp(cmd, "footer") == 0)
+ justfooter(dn, perm, help_uri, dir_path, dir_uri, admin_file);
+ else if (strcmp(cmd, "managedir") == 0)
+ managedir(dn, perm, help_uri, dir_path, dir_uri, admin_file);
+ else if (strcmp(cmd, "print") == 0)
+ printfile(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "history") == 0)
+ filehistory(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "editdnlist") == 0)
+ editdnlistform(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "edit") == 0)
+ {
+ if ((strcasecmp(button, "new directory") == 0) ||
+ (strcasecmp(button, "Create") == 0))
+ newdirectory(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else
+ editfileform(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ }
+ else if (strcmp(cmd, "editaction") == 0)
+ editfileaction(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "editdnlistaction") == 0)
+ editdnlistaction(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "delete") == 0)
+ deletefileform(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "deleteaction") == 0)
+ deletefileaction(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "rename") == 0)
+ renameform(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "renameaction") == 0)
+ renameaction(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "ziplist") == 0)
+ ziplist(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "unzipfile") == 0)
+ unzipfile(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "create_acl") == 0)
+ create_acl(dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+
+ /* GACL functions in grst_admin_gacl.c */
+
+ else if (strcmp(cmd, "show_acl") == 0)
+ show_acl(0, user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "admin_acl") == 0)
+ show_acl(1, user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "acl_history") == 0)
+ show_acl(2, user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd, "revert_acl") == 0)
+ revert_acl(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ //show_acl(2, user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"new_entry_form")==0)
+ new_entry_form(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"new_entry")==0)
+ new_entry(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"del_entry_sure")==0)
+ del_entry_sure(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"del_entry")==0)
+ del_entry(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"edit_entry_form")==0)
+ edit_entry_form(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"edit_entry")==0)
+ edit_entry(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"add_cred_form")==0)
+ add_cred_form(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"add_cred")==0)
+ add_cred(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"del_cred_sure")==0)
+ del_cred_sure(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+ else if (strcmp(cmd,"del_cred")==0)
+ del_cred(user, dn, perm, help_uri, dir_path, file, dir_uri, admin_file);
+
+ /* you what? */
+
+ else GRSThttpError("500 Internal Server Error");
+}
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+/*------------------------------------------------------------------------*
+ * For more information about GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <fnmatch.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+#include "gridsite.h"
+
+/* *
+ * Global variables, shared by all GACL functions by private to libgacl *
+ * */
+
+char *grst_perm_syms[] = { "none",
+ "read",
+ "exec",
+ "list",
+ "write",
+ "admin",
+ NULL };
+
+GRSTgaclPerm grst_perm_vals[] = { GRST_PERM_NONE,
+ GRST_PERM_READ,
+ GRST_PERM_EXEC,
+ GRST_PERM_LIST,
+ GRST_PERM_WRITE,
+ GRST_PERM_ADMIN,
+ -1 };
+
+int GRSTgaclInit(void)
+{
+ xmlInitParser();
+
+ LIBXML_TEST_VERSION
+
+ xmlKeepBlanksDefault(0);
+
+ return 1;
+}
+
+/* *
+ * Functions to manipulate GRSTgaclCred structures *
+ * */
+
+GRSTgaclCred *GRSTgaclCredNew(char *type)
+/*
+ GRSTgaclCredNew - allocate a new GRSTgaclCred structure, and return
+ it's pointer or NULL on (malloc) error.
+*/
+{
+ GRSTgaclCred *newcred;
+
+ if (type == NULL) return NULL;
+
+ newcred = malloc(sizeof(GRSTgaclCred));
+ if (newcred == NULL) return NULL;
+
+ newcred->type = strdup(type);
+ newcred->delegation = 0;
+ newcred->firstname = NULL;
+ newcred->next = NULL;
+
+ return newcred;
+}
+
+int GRSTgaclCredAddValue(GRSTgaclCred *cred, char *rawname, char *rawvalue)
+/*
+ GRSTgaclCredAddValue - add a name/value pair to a GRSTgaclCred
+*/
+{
+ int i;
+ char *name, *value;
+ GRSTgaclNamevalue *p;
+
+ name = strdup(rawname);
+
+ /* no leading or trailing space in value */
+
+ value = rawvalue;
+ while ((*value != '\0') && isspace(*value)) ++value;
+
+ value = strdup(value);
+
+ for (i=strlen(value) - 1; (i >= 0) && isspace(value[i]); --i) value[i]='\0';
+
+ if (cred->firstname == NULL)
+ {
+ cred->firstname = malloc(sizeof (GRSTgaclNamevalue));
+ (cred->firstname)->name = name;
+ (cred->firstname)->value = value;
+ (cred->firstname)->next = NULL;
+ }
+ else
+ {
+ p = cred->firstname;
+
+ while (p->next != NULL) p = (GRSTgaclNamevalue *) p->next;
+
+ p->next = malloc(sizeof(GRSTgaclNamevalue));
+ ((GRSTgaclNamevalue *) p->next)->name = name;
+ ((GRSTgaclNamevalue *) p->next)->value = value;
+ ((GRSTgaclNamevalue *) p->next)->next = NULL;
+ }
+
+ return 1;
+}
+
+static int GRSTgaclNamevalueFree(GRSTgaclNamevalue *p)
+{
+ if (p == NULL) return 1;
+
+ if (p->next != NULL)
+ GRSTgaclNamevalueFree((GRSTgaclNamevalue *) p->next);
+ if (p->name != NULL) free(p->name);
+ if (p->value != NULL) free(p->value);
+ free(p);
+
+ return 1;
+}
+
+int GRSTgaclCredFree(GRSTgaclCred *cred)
+/*
+ GRSTgaclCredFree - free memory structures of a GRSTgaclCred,
+ returning 1 always!
+*/
+{
+ if (cred == NULL) return 1;
+
+ GRSTgaclNamevalueFree(cred->firstname);
+ if (cred->type != NULL) free(cred->type);
+ free(cred);
+
+ return 1;
+}
+
+static int GRSTgaclCredsFree(GRSTgaclCred *firstcred)
+/*
+ GRSTgaclCredsFree - free a cred and all the creds in its *next chain
+*/
+{
+ if (firstcred == NULL) return 0;
+
+ if (firstcred->next != NULL) GRSTgaclCredsFree(firstcred->next);
+
+ return GRSTgaclCredsFree(firstcred);
+}
+
+static int GRSTgaclCredInsert(GRSTgaclCred *firstcred, GRSTgaclCred *newcred)
+/*
+ GRSTgaclCredInsert - insert a cred in the *next chain of firstcred
+
+ FOR THE MOMENT THIS JUST APPENDS!
+*/
+{
+ if (firstcred == NULL) return 0;
+
+ if (firstcred->next == NULL)
+ {
+ firstcred->next = newcred;
+ return 1;
+ }
+
+ return GRSTgaclCredInsert(firstcred->next, newcred);
+}
+
+int GRSTgaclEntryAddCred(GRSTgaclEntry *entry, GRSTgaclCred *cred)
+/*
+ GRSTaddCred - add a new credential to an existing entry, returning 1
+ on success or 0 on error
+*/
+{
+ if (entry == NULL) return 0;
+
+ if (entry->firstcred == NULL)
+ {
+ entry->firstcred = cred;
+ return 1;
+ }
+ else return GRSTgaclCredInsert(entry->firstcred, cred);
+}
+
+static int GRSTgaclCredRemoveCred(GRSTgaclCred *firstcred, GRSTgaclCred *oldcred)
+/*
+ (Private)
+
+ GRSTgaclCredRemoveCred - remove a cred in the *next chain of firstcred
+ and relink the chain
+*/
+{
+ if (firstcred == NULL) return 0;
+
+// yeah, I know
+}
+
+int GRSTgaclEntryDelCred(GRSTgaclEntry *entry, GRSTgaclCred *cred)
+/*
+ GRSTgaclEntryDelCred - remove a new cred from an entry, returning 1
+ on success (or absense) or 0 on error.
+*/
+{
+ if (entry == NULL) return 0;
+
+ return GRSTgaclCredRemoveCred(entry->firstcred, cred);
+}
+
+int GRSTgaclCredPrint(GRSTgaclCred *cred, FILE *fp)
+/*
+ GRSTgaclCredPrint - print a credential and any name-value pairs is contains
+*/
+{
+ char *q;
+ GRSTgaclNamevalue *p;
+
+ if (cred->firstname != NULL)
+ {
+ fprintf(fp, "<%s>\n", cred->type);
+
+ p = cred->firstname;
+
+ do {
+ fprintf(fp, "<%s>", p->name);
+
+ for (q=p->value; *q != '\0'; ++q)
+ if (*q == '<') fputs("<", fp);
+ else if (*q == '>') fputs(">", fp);
+ else if (*q == '&') fputs("&" , fp);
+ else if (*q == '\'') fputs("'", fp);
+ else if (*q == '"') fputs(""", fp);
+ else fputc(*q, fp);
+
+ fprintf(fp, "</%s>\n", p->name);
+
+ p = (GRSTgaclNamevalue *) p->next;
+
+ } while (p != NULL);
+
+ fprintf(fp, "</%s>\n", cred->type);
+ }
+ else fprintf(fp, "<%s/>\n", cred->type);
+
+ return 1;
+}
+
+/* *
+ * Functions to manipulate GRSTgaclEntry structures *
+ * */
+
+GRSTgaclEntry *GRSTgaclEntryNew(void)
+/*
+ GRSTgaclEntryNew - allocate space for a new entry, returning its pointer
+ or NULL on failure.
+*/
+{
+ GRSTgaclEntry *newentry;
+
+ newentry = (GRSTgaclEntry *) malloc(sizeof(GRSTgaclEntry));
+ if (newentry == NULL) return NULL;
+
+ newentry->firstcred = NULL;
+ newentry->allowed = 0;
+ newentry->denied = 0;
+ newentry->next = NULL;
+
+ return newentry;
+}
+
+int GRSTgaclEntryFree(GRSTgaclEntry *entry)
+/*
+ GRSTgaclEntryFree - free up space used by an entry (always returns 1)
+*/
+{
+ int i;
+
+ if (entry == NULL) return 1;
+
+ GRSTgaclCredsFree(entry->firstcred);
+
+ free(entry);
+
+ return 1;
+}
+
+static int GRSTgaclEntriesFree(GRSTgaclEntry *entry)
+/*
+ GRSTgaclEntriesFree - free up entry and all entries linked to in its *next
+ chain
+*/
+{
+ if (entry == NULL) return 0;
+
+ if (entry->next != NULL) GRSTgaclEntriesFree(entry->next);
+
+ return GRSTgaclEntryFree(entry);
+}
+
+static int GRSTgaclEntryInsert(GRSTgaclEntry *firstentry, GRSTgaclEntry *newentry)
+/*
+ GRSTgaclEntryInsert - insert an entry in the *next chain of firstentry
+
+ FOR THE MOMENT THIS JUST APPENDS
+*/
+{
+ if (firstentry == NULL) return 0;
+
+ if (firstentry->next == NULL)
+ {
+ firstentry->next = newentry;
+ return 1;
+ }
+
+ return GRSTgaclEntryInsert(firstentry->next, newentry);
+}
+
+int GRSTgaclAclAddEntry(GRSTgaclAcl *acl, GRSTgaclEntry *entry)
+/*
+ GRSTgaclAclAddEntry - add a new entry to an existing acl, returning 1
+ on success or 0 on error
+*/
+{
+ if (acl == NULL) return 0;
+
+ if (acl->firstentry == NULL)
+ {
+ acl->firstentry = entry;
+ return 1;
+ }
+ else return GRSTgaclEntryInsert(acl->firstentry, entry);
+}
+
+int GRSTgaclEntryPrint(GRSTgaclEntry *entry, FILE *fp)
+{
+ GRSTgaclCred *cred;
+ GRSTgaclPerm i;
+
+ fputs("<entry>\n", fp);
+
+ for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+ GRSTgaclCredPrint(cred, fp);
+
+ if (entry->allowed)
+ {
+ fputs("<allow>", fp);
+
+ for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i)
+ if ((entry->allowed) & i) GRSTgaclPermPrint(i, fp);
+
+ fputs("</allow>\n", fp);
+ }
+
+
+ if (entry->denied)
+ {
+ fputs("<deny>", fp);
+
+ for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i)
+ if (entry->denied & i) GRSTgaclPermPrint(i, fp);
+
+ fputs("</deny>\n", fp);
+ }
+
+ fputs("</entry>\n", fp);
+
+ return 1;
+}
+
+/* *
+ * Functions to manipulate GRSTgaclPerm items *
+ * */
+
+int GRSTgaclPermPrint(GRSTgaclPerm perm, FILE *fp)
+{
+ GRSTgaclPerm i;
+
+ for (i=GRST_PERM_READ; grst_perm_syms[i] != NULL; ++i)
+ if (perm == grst_perm_vals[i])
+ {
+ fprintf(fp, "<%s/>", grst_perm_syms[i]);
+ return 1;
+ }
+
+ return 0;
+}
+
+int GRSTgaclEntryAllowPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+ entry->allowed = entry->allowed | perm;
+
+ return 1;
+}
+
+int GRSTgaclEntryUnallowPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+ entry->allowed = entry->allowed & ~perm;
+
+ return 1;
+}
+
+int GRSTgaclEntryDenyPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+ entry->denied = entry->denied | perm;
+
+ return 1;
+}
+
+int GRSTgaclEntryUndenyPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm)
+{
+ entry->denied = entry->denied & ~perm;
+
+ return 1;
+}
+
+char *GRSTgaclPermToChar(GRSTgaclPerm perm)
+/*
+ GRSTgaclPermToChar - return char * or NULL corresponding to most significant
+ set bit of perm.
+*/
+{
+ char *p = NULL;
+ GRSTgaclPerm i;
+
+ for (i=0; grst_perm_syms[i] != NULL; ++i)
+ if (perm & grst_perm_vals[i]) p = grst_perm_syms[i];
+
+ return p;
+}
+
+GRSTgaclPerm GRSTgaclPermFromChar(char *s)
+/*
+ GRSTgaclPermToChar - return access perm corresponding to symbol s[]
+*/
+{
+ GRSTgaclPerm i;
+
+ for (i=0; grst_perm_syms[i] != NULL; ++i)
+ if (strcasecmp(grst_perm_syms[i], s) == 0) return grst_perm_vals[i];
+
+ return -1;
+}
+
+/* *
+ * Functions to manipulate GRSTgaclAcl structures *
+ * */
+
+GRSTgaclAcl *GRSTgaclAclNew(void)
+/*
+ GRSTgaclAclNew - allocate a new acl and return its pointer (or NULL
+ on failure.)
+*/
+{
+ GRSTgaclAcl *newacl;
+
+ newacl = (GRSTgaclAcl *) malloc(sizeof(GRSTgaclAcl));
+ if (newacl == NULL) return NULL;
+
+ newacl->firstentry = NULL;
+
+ return newacl;
+}
+
+int GRSTgaclAclFree(GRSTgaclAcl *acl)
+/*
+ GRSTgaclAclFree - free up space used by *acl. Always returns 1.
+*/
+{
+ if (acl == NULL) return 1;
+
+ GRSTgaclEntriesFree(acl->firstentry);
+
+ return 1;
+}
+
+int GRSTgaclAclPrint(GRSTgaclAcl *acl, FILE *fp)
+{
+ GRSTgaclEntry *entry;
+
+ fputs("<gacl version=\"0.0.1\">\n", fp);
+
+ for (entry = acl->firstentry; entry != NULL; entry = entry->next)
+ GRSTgaclEntryPrint(entry, fp);
+
+ fputs("</gacl>\n", fp);
+
+ return 1;
+}
+
+int GRSTgaclAclSave(GRSTgaclAcl *acl, char *filename)
+{
+ int ret;
+ FILE *fp;
+
+ fp = fopen(filename, "w");
+ if (fp == NULL) return 0;
+
+ fputs("<?xml version=\"1.0\"?>\n", fp);
+
+ ret = GRSTgaclAclPrint(acl, fp);
+
+ fclose(fp);
+
+ return ret;
+}
+
+/* *
+ * Functions for loading and parsing XML using libxml *
+ * */
+
+// need to check these for libxml memory leaks? - what needs to be freed?
+
+static GRSTgaclCred *GRSTgaclCredParse(xmlNodePtr cur)
+/*
+ GRSTgaclCredParse - parse a credential stored in the libxml structure cur,
+ returning it as a pointer or NULL on error.
+*/
+{
+ xmlNodePtr cur2;
+ GRSTgaclCred *cred;
+
+ cred = GRSTgaclCredNew((char *) cur->name);
+
+ cred->firstname = NULL;
+ cred->next = NULL;
+
+ for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+ {
+ GRSTgaclCredAddValue(cred, (char *) cur2->name,
+ (char *) xmlNodeGetContent(cur2));
+ }
+
+ return cred;
+}
+
+static GRSTgaclEntry *GRSTgaclEntryParse(xmlNodePtr cur)
+/*
+ GRSTgaclEntryParse - parse an entry stored in the libxml structure cur,
+ returning it as a pointer or NULL on error.
+*/
+{
+ int i;
+ xmlNodePtr cur2;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+ GRSTgaclPerm perm;
+
+ if (xmlStrcmp(cur->name, (const xmlChar *) "entry") != 0) return NULL;
+
+ cur = cur->xmlChildrenNode;
+
+ entry = GRSTgaclEntryNew();
+
+ while (cur != NULL)
+ {
+ if (xmlStrcmp(cur->name, (const xmlChar *) "allow") == 0)
+ {
+ for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+ for (i=0; grst_perm_syms[i] != NULL; ++i)
+ if (xmlStrcmp(cur2->name,
+ (const xmlChar *) grst_perm_syms[i]) == 0)
+ GRSTgaclEntryAllowPerm(entry, grst_perm_vals[i]);
+ }
+ else if (xmlStrcmp(cur->name, (const xmlChar *) "deny") == 0)
+ {
+ for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next)
+ for (i=0; grst_perm_syms[i] != NULL; ++i)
+ if (xmlStrcmp(cur2->name,
+ (const xmlChar *) grst_perm_syms[i]) == 0)
+ GRSTgaclEntryDenyPerm(entry, grst_perm_vals[i]);
+ }
+ else if ((cred = GRSTgaclCredParse(cur)) != NULL)
+ {
+ if (!GRSTgaclEntryAddCred(entry, cred))
+ {
+ GRSTgaclCredFree(cred);
+ GRSTgaclEntryFree(entry);
+ return NULL;
+ }
+ }
+ else /* I cannot parse this - give up rather than get it wrong */
+ {
+ GRSTgaclEntryFree(entry);
+ return NULL;
+ }
+
+ cur=cur->next;
+ }
+
+ return entry;
+}
+
+GRSTgaclAcl *GRSTgaclAclLoadFile(char *filename)
+{
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+ GRSTgaclAcl *acl;
+ GRSTgaclEntry *entry;
+
+ doc = xmlParseFile(filename);
+ if (doc == NULL) return NULL;
+
+ cur = xmlDocGetRootElement(doc);
+
+ if (xmlStrcmp(cur->name, (const xmlChar *) "gacl"))
+ {
+ free(doc);
+ free(cur);
+ return NULL;
+ }
+
+ cur = cur->xmlChildrenNode;
+
+ acl = GRSTgaclAclNew();
+
+ while (cur != NULL)
+ {
+ entry = GRSTgaclEntryParse(cur);
+ if (entry == NULL)
+ {
+ GRSTgaclAclFree(acl);
+ xmlFreeDoc(doc);
+ return NULL;
+ }
+
+ GRSTgaclAclAddEntry(acl, entry);
+
+ cur=cur->next;
+ }
+
+ xmlFreeDoc(doc);
+ return acl;
+}
+
+int GRSTgaclFileIsAcl(char *pathandfile)
+/* Return 1 if filename in *pathandfile starts GRST_ACL_FILE
+ Return 0 otherwise. */
+{
+ char *filename;
+
+ filename = rindex(pathandfile, '/');
+ if (filename == NULL) filename = pathandfile;
+ else filename++;
+
+ return (strncmp(filename, GRST_ACL_FILE, sizeof(GRST_ACL_FILE) - 1) == 0);
+}
+
+char *GRSTgaclFileFindAclname(char *pathandfile)
+/* Return malloc()ed ACL filename that governs the given file or directory
+ (for directories, the ACL file is in the directory itself), or NULL if none
+ can be found. */
+{
+ char *path, *p;
+ struct stat statbuf;
+
+ path = malloc(strlen(pathandfile) + sizeof(GRST_ACL_FILE) + 1);
+ strcpy(path, pathandfile);
+
+ if (stat(path, &statbuf) == 0)
+ {
+ if (!S_ISDIR(statbuf.st_mode)) /* can strip this / off straightaway */
+ {
+ p = rindex(path, '/');
+ if (p != NULL) *p = '\0';
+ }
+ }
+
+ while (path[0] != '\0')
+ {
+ strcat(path, "/");
+ strcat(path, GRST_ACL_FILE);
+
+ if (stat(path, &statbuf) == 0) return path;
+
+ p = rindex(path, '/');
+ *p = '\0'; /* strip off the / we added for ACL */
+
+ p = rindex(path, '/');
+ if (p == NULL) break; /* must start without / and we there now ??? */
+
+ *p = '\0'; /* strip off another layer of / */
+ }
+
+ free(path);
+ return NULL;
+}
+
+GRSTgaclAcl *GRSTgaclAclLoadforFile(char *pathandfile)
+/* Return ACL that governs the given file or directory (for directories,
+ the ACL file is in the directory itself.) */
+{
+ char *path;
+ GRSTgaclAcl *acl;
+
+ path = GRSTgaclFileFindAclname(pathandfile);
+
+ if (path != NULL)
+ {
+ acl = GRSTgaclAclLoadFile(path);
+ free(path);
+ return acl;
+ }
+
+ return NULL;
+}
+
+/* *
+ * Functions to create and query GACLuser *
+ * */
+
+GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *cred)
+{
+ GRSTgaclUser *user;
+
+ if (cred == NULL) return NULL;
+
+ user = malloc(sizeof(GRSTgaclUser));
+
+ if (user != NULL) user->firstcred = cred;
+
+ user->dnlists = NULL;
+
+ return user;
+}
+
+int GRSTgaclUserFree(GRSTgaclUser *user)
+{
+ if (user == NULL) return 1;
+
+ if (user->firstcred != NULL) GRSTgaclCredsFree(user->firstcred);
+
+ if (user->dnlists != NULL) free(user->dnlists);
+
+ free(user);
+
+ return 1;
+}
+
+int GRSTgaclUserAddCred(GRSTgaclUser *user, GRSTgaclCred *cred)
+{
+ GRSTgaclCred *crediter;
+
+ if ((user == NULL) || (cred == NULL)) return 0;
+
+ if (user->firstcred == NULL)
+ {
+ user->firstcred = cred;
+ cred->next = NULL; /* so cannot be used to add whole lists */
+ return 1;
+ }
+
+ crediter = user->firstcred;
+
+ while (crediter->next != NULL) crediter = crediter->next;
+
+ crediter->next = cred;
+ cred->next = NULL; /* so cannot be used to add whole lists */
+
+ return 1;
+}
+
+int GRSTgaclUserHasCred(GRSTgaclUser *user, GRSTgaclCred *cred)
+/* test if the user has the given credential */
+{
+ GRSTgaclCred *crediter;
+ GRSTgaclNamevalue *usernamevalue, *crednamevalue;
+
+ if (cred == NULL) return 0;
+
+ if (strcmp(cred->type, "any-user") == 0) return 1;
+
+ if (user == NULL) return 0;
+
+ if (strcmp(cred->type, "dn-list") == 0)
+ {
+ if ((cred->firstname == NULL) ||
+ (strcmp((cred->firstname)->name, "url") != 0) ||
+ ((cred->firstname)->next != NULL)) return 0;
+
+ return GRSTgaclDNlistHasUser((cred->firstname)->value, user);
+ }
+
+ if (strcmp(cred->type, "dns") == 0)
+ {
+ if ((user->firstcred == NULL) ||
+ ((user->firstcred)->firstname == NULL) ||
+ (cred->firstname == NULL) ||
+ (strcmp((cred->firstname)->name, "hostname") != 0) ||
+ ((cred->firstname)->next != NULL)) return 0;
+
+ for (crediter=user->firstcred;
+ crediter != NULL;
+ crediter = crediter->next)
+ if (strcmp(crediter->type, "dns") == 0)
+ {
+ if ((crediter->firstname == NULL) ||
+ (strcmp((crediter->firstname)->name, "hostname") != 0)) return 0;
+
+ return (fnmatch((cred->firstname)->value,
+ (crediter->firstname)->value, FNM_CASEFOLD) == 0);
+ }
+
+ return 0;
+ }
+
+ if (strcmp(cred->type, "auth-user") == 0)
+ {
+ if ((user->firstcred == NULL) ||
+ ((user->firstcred)->firstname == NULL)) return 0;
+
+ for (crediter=user->firstcred;
+ crediter != NULL;
+ crediter = crediter->next)
+ if (strcmp(crediter->type, "person") == 0) return 1;
+
+ return 0;
+ }
+
+ for (crediter=user->firstcred; crediter != NULL; crediter = crediter->next)
+ {
+ if (strcmp(crediter->type, cred->type) != 0) continue;
+
+ if ((crediter->firstname == NULL) &&
+ (cred->firstname == NULL)) return 1;
+
+ if ((crediter->firstname == NULL) ||
+ (cred->firstname == NULL)) continue;
+
+ usernamevalue = crediter->firstname;
+ crednamevalue = cred->firstname;
+
+ for (;;)
+ {
+ if (strcmp(usernamevalue->name,crednamevalue->name) != 0) break;
+
+ if (strcmp(cred->type, "person") == 0)
+ {
+ if (GRSTx509NameCmp(usernamevalue->value,
+ crednamevalue->value) != 0) break;
+ }
+ else if (strcmp(usernamevalue->value,
+ crednamevalue->value) != 0) break;
+
+ /* ok if cred list runs out before user's cred list */
+ if (crednamevalue->next == NULL) return 1;
+
+ /* but not ok if more names to match which user doesn't have */
+ if (usernamevalue->next == NULL) break;
+
+ crednamevalue = (GRSTgaclNamevalue *) crednamevalue->next;
+ usernamevalue = (GRSTgaclNamevalue *) usernamevalue->next;
+ }
+ }
+
+ return 0;
+}
+
+GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *user, char *type)
+/* find the first credential of a given type for this user */
+{
+ GRSTgaclCred *cred;
+
+ if (user == NULL) return NULL;
+
+ cred = user->firstcred;
+
+ while (cred != NULL)
+ {
+ if (strcmp(cred->type, type) == 0) return cred;
+
+ cred = cred->next;
+ }
+
+ return NULL;
+}
+
+int GRSTgaclUserSetDNlists(GRSTgaclUser *user, char *dnlists)
+{
+ if ((user == NULL) || (dnlists == NULL)) return 0;
+
+ if (user->dnlists != NULL) free(user->dnlists);
+
+ user->dnlists = strdup(dnlists);
+
+ return 1;
+}
+
+/* *
+ * Functions to test for access perm of an individual *
+ * */
+
+static char *recurse4file(char *dir, char *file, int recurse_level)
+/* try to find file[] in dir[]. try subdirs if not found.
+ return full path to first found version or NULL on failure */
+{
+ char *fullfilename, *fulldirname;
+ struct stat statbuf;
+ DIR *dirDIR;
+ struct dirent *file_ent;
+
+ /* try to find in current directory */
+
+ asprintf(&fullfilename, "%s/%s", dir, file);
+ if (stat(fullfilename, &statbuf) == 0) return fullfilename;
+ free(fullfilename);
+
+ /* maybe search in subdirectories */
+
+ if (recurse_level >= GRST_RECURS_LIMIT) return NULL;
+
+ dirDIR = opendir(dir);
+
+ if (dirDIR == NULL) return NULL;
+
+ while ((file_ent = readdir(dirDIR)) != NULL)
+ {
+ if (file_ent->d_name[0] == '.') continue;
+
+ asprintf(&fulldirname, "%s/%s", dir, file_ent->d_name);
+
+ if ((stat(fulldirname, &statbuf) == 0) &&
+ S_ISDIR(statbuf.st_mode) &&
+ ((fullfilename = recurse4file(fulldirname, file,
+ recurse_level + 1)) != NULL))
+ {
+ closedir(dirDIR);
+ return fullfilename;
+ }
+
+ free(fulldirname);
+ }
+
+ closedir(dirDIR);
+
+ return NULL;
+}
+
+int GRSTgaclDNlistHasUser(char *listurl, GRSTgaclUser *user)
+{
+ char *dn_lists_dirs, *dn_list_ptr, *enclisturl, *filename, *dirname,
+ line[512], *p;
+ FILE *fp;
+ GRSTgaclCred *cred;
+
+ if ((listurl == NULL) || (user == NULL)) return 0;
+
+ enclisturl = GRSThttpUrlEncode(listurl);
+
+ if (user->dnlists != NULL) p = user->dnlists;
+ else p = getenv("GRST_DN_LISTS");
+
+ if (p == NULL) p = GRST_DN_LISTS;
+ dn_lists_dirs = strdup(p); /* we need to keep this for free() later! */
+ dn_list_ptr = dn_lists_dirs; /* copy, for naughty function strsep() */
+
+ while ((dirname = strsep(&dn_list_ptr, ":")) != NULL)
+ {
+ filename = recurse4file(dirname, enclisturl, 0);
+ if (filename == NULL) continue;
+
+ fp = fopen(filename, "r");
+ free(filename);
+
+ if (fp == NULL) continue;
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ p = index(line, '\n');
+ if (p != NULL) *p = '\0';
+
+ cred = user->firstcred;
+
+ while (cred != NULL)
+ {
+ if ((strcmp(cred->type, "person") == 0) &&
+ (cred->firstname != NULL) &&
+ (strcmp("dn", (cred->firstname)->name) == 0) &&
+ (GRSTx509NameCmp(line, (cred->firstname)->value) == 0))
+ {
+ fclose(fp);
+ free(dn_lists_dirs);
+ free(enclisturl);
+ return 1;
+ }
+
+ cred = cred->next;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ free(dn_lists_dirs);
+ free(enclisturl);
+
+ return 0;
+}
+
+GRSTgaclPerm GRSTgaclAclTestUser(GRSTgaclAcl *acl, GRSTgaclUser *user)
+/*
+ GACLgaclAclTestUser - return bit fields depending on access perms user has
+ for given acl. All zero for no access. If *user is
+ NULL, matching to "any-user" will still work.
+*/
+{
+ int flag, onlyanyuser;
+ GRSTgaclPerm allowperms = 0, denyperms = 0, allowed;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred, *usercred;
+
+ if (acl == NULL) return 0;
+
+ for (entry = acl->firstentry; entry != NULL; entry = entry->next)
+ {
+ flag = 1; /* begin by assuming this entry applies to us */
+ onlyanyuser = 1; /* begin by assuming just <any-user/> */
+
+ /* now go through creds, checking they all do apply to us */
+
+ for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+ if (!GRSTgaclUserHasCred(user, cred)) flag = 0;
+ else if (strcmp(cred->type, "any-user") != 0) onlyanyuser = 0;
+
+ if (!flag) continue; /* flag false if a subtest failed */
+
+ /* does apply to us, so we remember this entry's perms */
+
+ /* we dont allow Write or Admin on the basis of any-user alone */
+
+ allowed = entry->allowed;
+
+ if (onlyanyuser)
+ allowed = entry->allowed & ~GRST_PERM_WRITE & ~GRST_PERM_ADMIN;
+ else allowed = entry->allowed;
+
+ allowperms = allowperms | allowed;
+ denyperms = denyperms | entry->denied;
+ }
+
+ return (allowperms & (~ denyperms));
+ /* for each perm type, any deny we saw kills any allow */
+}
+
+GRSTgaclPerm GRSTgaclAclTestexclUser(GRSTgaclAcl *acl, GRSTgaclUser *user)
+/*
+ GRSTgaclAclTestexclUser -
+ return bit fields depending on ALLOW perms OTHER users
+ have for given acl. All zero if they have no access.
+ (used for testing if a user has exclusive access)
+*/
+{
+ int flag;
+ GRSTgaclPerm perm = 0;
+ GRSTgaclEntry *entry;
+ GRSTgaclCred *cred;
+
+ if (acl == NULL) return 0;
+
+ for (entry = acl->firstentry; entry != NULL; entry = entry->next)
+ {
+ flag = 0; /* flag will be set if cred implies other users */
+
+ for (cred = entry->firstcred; cred != NULL; cred = cred->next)
+ {
+ if (strcmp(cred->type, "person") != 0)
+ /* if we ever add support for other person-specific credentials,
+ they must also be recognised here */
+ {
+ flag = 1;
+ break;
+ }
+
+ if (!GRSTgaclUserHasCred(user, cred))
+ /* if user doesnt have this person credential, assume
+ it refers to a different individual */
+ {
+ flag = 1;
+ break;
+ }
+ }
+
+ if (flag) perm = perm | entry->allowed;
+ }
+
+ return perm;
+}
+
+/*
+ Wrapper functions for gridsite-gacl.h support of legacy API
+*/
+
+GRSTgaclEntry *GACLparseEntry(xmlNodePtr cur)
+{
+ return GRSTgaclEntryParse(cur);
+}
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef VERSION
+#define VERSION "x.x.x"
+#endif
+
+#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 <fcntl.h>
+
+#include "gridsite.h"
+
+void GRSThttpBodyInit(GRSThttpBody *thisbody)
+{
+ thisbody->size = 0; /* simple, but we don't expose internals to callers */
+}
+
+void GRSThttpPrintf(GRSThttpBody *thisbody, char *fmt, ...)
+/* append printf() style format and arguments to *thisbody.
+ This requires vasprintf from glibc!! */
+{
+ char *p;
+ size_t size;
+ va_list args;
+
+ va_start(args, fmt);
+ size = vasprintf(&p, fmt, args);
+ va_end(args);
+
+ if (size == 0) free(p); /* don't need to bother in this case */
+ else if (size > 0)
+ {
+ if (thisbody->size == 0) /* need to initialise */
+ {
+ thisbody->first = (GRSThttpCharsList *)malloc(sizeof(GRSThttpCharsList));
+ thisbody->first->text = p;
+ thisbody->first->next = NULL;
+
+ thisbody->last = thisbody->first;
+ thisbody->size = size;
+ }
+ else
+ {
+ thisbody->last->next = (GRSThttpCharsList *)
+ malloc(sizeof(GRSThttpCharsList));
+ ((GRSThttpCharsList *) thisbody->last->next)->text = p;
+ ((GRSThttpCharsList *) thisbody->last->next)->next = NULL;
+
+ thisbody->last = thisbody->last->next;
+ thisbody->size = thisbody->size + size;
+ }
+ }
+}
+
+int GRSThttpCopy(GRSThttpBody *thisbody, char *file)
+/*
+ copy a whole file, named file[], into the body output buffer, returning
+ 1 if file was found and copied ok, or 0 otherwise.
+*/
+{
+ int fd, len;
+ char c, *p;
+ struct stat statbuf;
+
+ fd = open(file, O_RDONLY);
+
+ if (fd == -1) return 0;
+
+ if (fstat(fd, &statbuf) != 0)
+ {
+ close(fd);
+ return 0;
+ }
+
+ p = malloc(statbuf.st_size + 1);
+
+ if (p == NULL)
+ {
+ close(fd);
+ return 0;
+ }
+
+ len = read(fd, p, statbuf.st_size);
+ p[len] = '\0';
+
+ close(fd);
+
+ if (thisbody->size == 0) /* need to initialise */
+ {
+ thisbody->first = (GRSThttpCharsList *) malloc(sizeof(GRSThttpCharsList));
+ thisbody->first->text = p;
+ thisbody->first->next = NULL;
+
+ thisbody->last = thisbody->first;
+ thisbody->size = len;
+ }
+ else
+ {
+ thisbody->last->next=(GRSThttpCharsList *)malloc(sizeof(GRSThttpCharsList));
+ ((GRSThttpCharsList *) thisbody->last->next)->text = p;
+ ((GRSThttpCharsList *) thisbody->last->next)->next = NULL;
+
+ thisbody->last = thisbody->last->next;
+ thisbody->size = thisbody->size + len;
+ }
+
+ return 1;
+}
+
+void GRSThttpWriteOut(GRSThttpBody *thisbody)
+/* output Content-Length header, blank line then whole of the body to
+ standard output */
+{
+ GRSThttpCharsList *p;
+
+ printf("Content-Length: %d\n\n", thisbody->size);
+
+ p = thisbody->first;
+
+ while (p != NULL)
+ {
+ fputs(p->text, stdout);
+
+ p = p->next;
+ }
+}
+
+int GRSThttpPrintHeaderFooter(GRSThttpBody *bp, char *file, char *headfootname)
+/*
+ try to print Header or Footer appropriate for absolute path file[],
+ returning 1 rather than 0 if found.
+*/
+{
+ int found = 0;
+ char *pathfile, *p;
+ struct stat statbuf;
+
+ pathfile = malloc(strlen(file) + strlen(headfootname) + 2);
+ strcpy(pathfile, file);
+
+ if ((pathfile[strlen(pathfile) - 1] != '/') &&
+ (stat(pathfile, &statbuf) == 0) &&
+ S_ISDIR(statbuf.st_mode)) strcat(pathfile, "/");
+
+ for (;;)
+ {
+ p = rindex(pathfile, '/');
+ if (p == NULL) break;
+ p[1] = '\0';
+ strcat(p, headfootname);
+
+ if (stat(pathfile, &statbuf) == 0)
+ {
+ found = GRSThttpCopy(bp, pathfile);
+ break;
+ }
+
+ p[0] = '\0';
+ }
+
+ free(pathfile);
+ return found;
+}
+
+char *GRSThttpGetCGI(char *name)
+/*
+ Return a malloc()ed copy of CGI form parameter identified by name[],
+ either received by QUERY_STRING (via GET) or on stdin (via POST).
+ Caller must free() the returned string itself. If name[] is not found,
+ an empty NUL-terminated malloc()ed string is returned. name[] has any
+ URL-encoding reversed.
+*/
+{
+ char *p, *namepattern, *valuestart, *returnvalue, *querystring;
+ int c, i, j, n, contentlength = 0;
+ static char *cgiposted = NULL;
+ size_t size_needed;
+
+ if (cgiposted == NULL) /* have to initialise cgiposted */
+ {
+ p = getenv("CONTENT_LENGTH");
+ if (p != NULL) sscanf(p, "%d", &contentlength);
+
+ querystring = getenv("REDIRECT_QUERY_STRING");
+ if (querystring == NULL) querystring = getenv("QUERY_STRING");
+
+ if (querystring == NULL) cgiposted = malloc(contentlength + 3);
+ else cgiposted = malloc(contentlength + strlen(querystring) + 4);
+
+ cgiposted[0] = '&';
+
+ for (i = 1; i <= contentlength; ++i)
+ {
+ c = getchar();
+ if (c == EOF) break;
+ cgiposted[i] = c;
+ }
+
+ cgiposted[i] = '&';
+ cgiposted[i+1] = '\0';
+
+ if (querystring != NULL)
+ {
+ strcat(cgiposted, querystring);
+ strcat(cgiposted, "&");
+ }
+ }
+
+ namepattern = malloc(strlen(name) + 3);
+ sprintf(namepattern, "&%s=", name);
+
+ p = strstr(cgiposted, namepattern);
+ free(namepattern);
+ if (p == NULL) return strdup("");
+
+ valuestart = &p[strlen(name) + 2];
+
+ for (n=0; valuestart[n] != '&'; ++n) ;
+
+ returnvalue = malloc(n + 1);
+
+ j=0;
+
+ for (i=0; i < n; ++i)
+ {
+ if ((i < n - 2) && (valuestart[i] == '%')) /* url encoded as %HH */
+ {
+ returnvalue[j] = 0;
+
+ if (isdigit(valuestart[i+1]))
+ returnvalue[j] += 16 * (valuestart[i+1] - '0');
+ else if (isalpha(valuestart[i+1]))
+ returnvalue[j] += 16 * (10 + tolower(valuestart[i+1]) - 'a');
+
+ if (isdigit(valuestart[i+2]))
+ returnvalue[j] += valuestart[i+2] - '0';
+ else if (isalpha(valuestart[i+2]))
+ returnvalue[j] += 10 + tolower(valuestart[i+2]) - 'a';
+
+ i = i + 2;
+ }
+ else if (valuestart[i] == '+') returnvalue[j] = ' ';
+ else returnvalue[j] = valuestart[i];
+
+ if (returnvalue[j] == '\r') continue; /* CR/LF -> LF */
+ ++j;
+ }
+
+ returnvalue[j] = '\0';
+
+ return returnvalue;
+}
+
+/* *
+ * Utility functions *
+ * */
+
+char *GRSThttpUrlDecode(char *in)
+{
+ int i, j, n;
+ char *out;
+
+ n = strlen(in);
+ out = malloc(n + 1);
+
+ j=0;
+
+ for (i=0; i < n; ++i)
+ {
+ if ((i < n - 2) && (in[i] == '%')) /* url encoded as %HH */
+ {
+ out[j] = 0;
+
+ if (isdigit(in[i+1]))
+ out[j] += 16 * (in[i+1] - '0');
+ else if (isalpha(in[i+1]))
+ out[j] += 16 * (10 + tolower(in[i+1]) - 'a');
+
+ if (isdigit(in[i+2]))
+ out[j] += in[i+2] - '0';
+ else if (isalpha(in[i+2]))
+ out[j] += 10 + tolower(in[i+2]) - 'a';
+
+ i = i + 2;
+ }
+ else if (in[i] == '+') out[j] = ' ';
+ else out[j] = in[i];
+
+ ++j;
+ }
+
+ out[j] = '\0';
+
+ return out;
+}
+
+char *GRSThttpUrlEncode(char *in)
+/* Return a pointer to a malloc'd string holding a URL-encoded (RFC 1738)
+ version of *in. Only A-Z a-z 0-9 . _ - are passed through unmodified.
+ (DN's processed by GRSThttpUrlEncode can be used as valid Unix filenames,
+ assuming they do not exceed restrictions on filename length.) */
+{
+ char *out, *p, *q;
+
+ out = malloc(3*strlen(in) + 1);
+
+ p = in;
+ q = out;
+
+ while (*p != '\0')
+ {
+ if (isalnum(*p) || (*p == '.') || (*p == '_') || (*p == '-'))
+ {
+ *q = *p;
+ ++q;
+ }
+ else
+ {
+ sprintf(q, "%%%2X", *p);
+ q = &q[3];
+ }
+
+ ++p;
+ }
+
+ *q = '\0';
+ return out;
+}
+
+char *GRSThttpUrlMildencode(char *in)
+/* Return a pointer to a malloc'd string holding a partially URL-encoded
+ version of *in. "Partially" means that A-Z a-z 0-9 . = - _ @ and /
+ are passed through unmodified. (DN's processed by GRSThttpUrlMildencode()
+ can be used as valid Unix paths+filenames if you are prepared to
+ create or simulate the resulting /X=xyz directories.) */
+{
+ char *out, *p, *q;
+
+ out = malloc(3*strlen(in) + 1);
+
+ p = in;
+ q = out;
+
+ while (*p != '\0')
+ {
+ if (isalnum(*p) || (*p == '.') || (*p == '=') || (*p == '-')
+ || (*p == '/') || (*p == '@') || (*p == '_'))
+ {
+ *q = *p;
+ ++q;
+ }
+ else if (*p == ' ')
+ {
+ *q = '+';
+ ++q;
+ }
+ else
+ {
+ sprintf(q, "%%%2X", *p);
+ q = &q[3];
+ }
+
+ ++p;
+ }
+
+ *q = '\0';
+ return out;
+}
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ ------------------------------------------------------------------------
+ For more information about GridSite: http://www.gridpp.ac.uk/gridsite/
+ ------------------------------------------------------------------------
+*/
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "gridsite.h"
+
+/// Compare X509 Distinguished Name strings
+int GRSTx509NameCmp(char *a, char *b)
+/**
+ * This function attempts to do with string representations what
+ * would ideally be done with OIDs/values. In particular, we equate
+ * "/Email=" == "/emailAddress=" to deal with this important change
+ * between OpenSSL 0.9.6 and 0.9.7.
+ * Other than that, it is currently the same as ordinary strcmp(3).
+ */
+{
+ int ret;
+ char *aa, *bb, *p;
+
+ aa = strdup(a);
+ while ((p = strstr(aa, "/emailAddress=")) != NULL)
+ {
+ memmove(&p[6], &p[13], strlen(&p[13]) + 1);
+ p[1] = 'E';
+ }
+
+ bb = strdup(b);
+ while ((p = strstr(bb, "/emailAddress=")) != NULL)
+ {
+ memmove(&p[6], &p[13], strlen(&p[13]) + 1);
+ p[1] = 'E';
+ }
+
+ ret = strcmp(aa, bb);
+
+ free(aa);
+ free(bb);
+
+ return ret;
+}
+
+
+/// Check critical extensions
+/**
+ * Returning GRST_RET_OK if all of extensions are known to us or
+ * OpenSSL; GRST_REF_FAILED otherwise.
+ *
+ * Since this function relies on functionality (X509_supported_extension)
+ * introduced in 0.9.7, then we do nothing and report an error
+ * (GRST_RET_FAILED) if one of the associated defines
+ * (X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) is absent.
+ */
+
+int GRSTx509KnownCriticalExts(X509 *cert)
+{
+ int i;
+ char s[80];
+ X509_EXTENSION *ex;
+
+#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+ for (i = 0; i < X509_get_ext_count(cert); ++i)
+ {
+ ex = X509_get_ext(cert, i);
+
+ if (X509_EXTENSION_get_critical(ex) &&
+ !X509_supported_extension(ex))
+ {
+ OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1);
+
+ if (strcmp(s, GRST_PROXYCERTINFO_OID) != 0) return GRST_RET_FAILED;
+ }
+ }
+
+ return GRST_RET_OK;
+#else
+ return GRST_RET_FAILED;
+#endif
+}
+
+/// ASN1 time string (in a char *) to time_t
+/**
+ * (Use ASN1_STRING_data() to convert ASN1_GENERALIZEDTIME to char * if
+ * necessary)
+ */
+
+time_t GRSTasn1TimeToTimeT(char *asn1time)
+{
+ char zone;
+ struct tm time_tm;
+
+ if ((sscanf(asn1time, "%02d%02d%02d%02d%02d%02d%c",
+ &(time_tm.tm_year),
+ &(time_tm.tm_mon),
+ &(time_tm.tm_mday),
+ &(time_tm.tm_hour),
+ &(time_tm.tm_min),
+ &(time_tm.tm_sec),
+ &zone) != 7) || (zone != 'Z')) return 0; /* dont understand */
+
+ /* time format fixups */
+
+ if (time_tm.tm_year < 90) time_tm.tm_year += 100;
+ --(time_tm.tm_mon);
+
+ return timegm(&time_tm);
+}
+
+/// Check if certificate can be used as a CA to sign standard X509 certs
+/*
+ * Return GRST_RET_OK if true; GRST_RET_FAILED if not.
+ */
+
+int GRSTx509IsCA(X509 *cert)
+{
+ int idret, purpose_id;
+
+ purpose_id = X509_PURPOSE_get_by_sname("sslclient");
+
+ /* final argument to X509_check_purpose() is whether to check for CAness */
+
+ if (X509_check_purpose(cert, purpose_id + X509_PURPOSE_MIN, 1))
+ return GRST_RET_OK;
+ else return GRST_RET_FAILED;
+}
+
+/// Check certificate chain for GSI proxy acceptability.
+/**
+ * Returns X509_V_OK/GRST_RET_OK if valid; OpenSSL X509 errors otherwise.
+ *
+ * Inspired by GSIcheck written by Mike Jones, SVE, Manchester Computing,
+ * The University of Manchester.
+ *
+ * The GridSite version handles old and new style Globus proxies, and
+ * proxies derived from user certificates issued with "X509v3 Basic
+ * Constraints: CA:FALSE" (eg UK e-Science CA)
+ *
+ * We do not check chain links between certs here: this is done by
+ * GRST_check_issued/X509_check_issued in mod_ssl's ssl_engine_init.c
+ *
+ * TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions
+ * (although via GRSTx509KnownCriticalExts() we can accept them.)
+ */
+
+int GRSTx509CheckChain(int *first_non_ca, X509_STORE_CTX *ctx)
+{
+ STACK_OF(X509) *certstack; /* Points to the client's cert chain */
+ X509 *cert; /* Points to the client's cert */
+ int depth; /* Depth of cert chain */
+ size_t len,len2; /* Lengths of issuer and cert DN */
+ int IsCA; /* Holds whether cert is allowed to sign */
+ int prevIsCA; /* Holds whether previous cert in chain is
+ allowed to sign */
+ int prevIsLimited; /* previous cert was proxy and limited */
+ int i,j; /* Iteration variables */
+ char *cert_DN; /* Pointer to current-certificate-in-chain's
+ DN */
+ char *issuer_DN; /* Pointer to
+ issuer-of-current-cert-in-chain's DN */
+ char *proxy_part_DN; /* Pointer to end part of current-cert-in-chain
+ maybe eg "/CN=proxy" */
+ time_t now;
+
+ time(&now);
+
+ *first_non_ca = 0; /* set to something predictable if things fail */
+
+ /* Check for context */
+ if (!ctx) return X509_V_ERR_INVALID_CA;
+ /* Can't GSI-verify if there is no context. Here and throughout this
+ function we report all errors as X509_V_ERR_INVALID_CA. */
+
+ /* Set necessary preliminary values */
+ IsCA = TRUE; /* =prevIsCA - start from a CA */
+ prevIsLimited = 0;
+
+ /* Get the client cert chain */
+ certstack = X509_STORE_CTX_get_chain(ctx); /* Get the client's chain */
+ depth = sk_X509_num(certstack); /* How deep is that chain? */
+
+ /* Check the client chain */
+ for (i=depth-1; i >= 0; --i)
+ /* loop through client-presented chain starting at CA end */
+ {
+ prevIsCA=IsCA;
+
+ /* Check for X509 certificate and point to it with 'cert' */
+ if (cert = sk_X509_value(certstack, i))
+ {
+ /* we check times and reject immediately if invalid */
+
+ if (now <
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert))))
+ return X509_V_ERR_INVALID_CA;
+
+ if (now >
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert))))
+ return X509_V_ERR_INVALID_CA;
+
+ /* If any forebear certificate is not allowed to sign we must
+ assume all decendents are proxies and cannot sign either */
+ if (prevIsCA)
+ {
+ /* always treat the first cert (from the CA files) as a CA */
+ if (i == depth-1) IsCA = TRUE;
+ /* check if this cert is valid CA for signing certs */
+ else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+
+ if (!IsCA) *first_non_ca = i;
+ }
+ else
+ {
+ IsCA = FALSE;
+ /* Force proxy check next iteration. Important because I can
+ sign any CA I create! */
+ }
+
+ cert_DN = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
+ issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0);
+ len = strlen(cert_DN);
+ len2 = strlen(issuer_DN);
+
+ /* issuer didn't have CA status, so this is (at best) a proxy:
+ check for bad proxy extension*/
+
+ if (!prevIsCA)
+ {
+ if (prevIsLimited) /* we reject proxies of limited proxies! */
+ return X509_V_ERR_INVALID_CA;
+
+ /* User not allowed to sign shortened DN */
+ if (len2 > len) return X509_V_ERR_INVALID_CA;
+
+ /* Proxy subject must begin with issuer. */
+ if (strncmp(cert_DN, issuer_DN, len2) != 0)
+ return X509_V_ERR_INVALID_CA;
+
+ /* Set pointer to end of base DN in cert_DN */
+ proxy_part_DN = &cert_DN[len2];
+
+ /* First attempt at support for Old and New style GSI
+ proxies: /CN=anything is ok for now */
+ if (strncmp(proxy_part_DN, "/CN=", 4) != 0)
+ return X509_V_ERR_INVALID_CA;
+
+ if ((strncmp(proxy_part_DN, "/CN=limited proxy", 17) == 0) &&
+ (i > 0)) prevIsLimited = 1; /* ready for next cert ... */
+ }
+ }
+ }
+
+ /* Check cert whose private key is being used by client. If previous in
+ chain is not allowed to be a CA then need to check this final cert for
+ valid proxy-icity too */
+ if (!prevIsCA)
+ {
+ if (prevIsLimited) return X509_V_ERR_INVALID_CA;
+ /* we do not accept proxies signed by limited proxies */
+
+ if (cert = sk_X509_value(certstack, 0))
+ {
+ /* Load DN & length of DN and either its issuer or the
+ first-bad-issuer-in-chain */
+ cert_DN = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+ issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
+ len = strlen(cert_DN);
+ len2 = strlen(issuer_DN);
+
+ /* issuer didn't have CA status, check for bad proxy extension */
+
+ if (len2 > len) return X509_V_ERR_INVALID_CA;
+ /* User not allowed to sign shortened DN */
+
+ if (strncmp(cert_DN, issuer_DN, len2) != 0)
+ return X509_V_ERR_INVALID_CA;
+ /* Proxy subject must begin with issuer. */
+
+ proxy_part_DN = &cert_DN[len2];
+ /* Set pointer to end of DN base in cert_DN */
+
+ /* Remander of subject must be either "/CN=proxy" or
+ "/CN=limited proxy" (or /CN=XYZ for New style GSI) */
+
+ /* First attempt at support for Old and New style GSI
+ proxies: /CN=anything is ok for now. */
+ if (strncmp(proxy_part_DN, "/CN=", 4) != 0)
+ return X509_V_ERR_INVALID_CA;
+ }
+ }
+
+ return X509_V_OK; /* this is also GRST_RET_OK, of course - by choice */
+}
+
+/// Example VerifyCallback routine
+
+/**
+ *
+ */
+
+int GRSTx509VerifyCallback (int ok, X509_STORE_CTX *ctx)
+{
+ int errnum = X509_STORE_CTX_get_error(ctx);
+ int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+ int first_non_ca;
+
+#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34
+#endif
+
+ if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
+ {
+ if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx))
+ == GRST_RET_OK)
+ {
+ ok = TRUE;
+ errnum = X509_V_OK;
+ X509_STORE_CTX_set_error(ctx, errnum);
+ }
+ }
+ else if ((errdepth == 0) &&
+ (errnum == X509_V_OK) &&
+ (GRSTx509CheckChain(&first_non_ca, ctx) != X509_V_OK)) ok = FALSE;
+
+
+ return ok;
+
+// check this
+
+// if (ok) return GRST_RET_OK;
+// else return GRST_RET_FAILED;
+}
+
+/// 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
+ * starting at *creds. Always returns GRST_RET_OK.
+ */
+
+int GRSTx509GetVomsCreds(int *lastcred, int maxcreds, size_t credlen,
+ char *creds, X509 *cert, X509 *usercert,
+ char *vomsdir)
+{
+ int i, j;
+ unsigned int siglen=-1, datalength=-1, dataoffset = -1;
+ char s[80];
+ unsigned char *charstr, *p, *time1 = NULL, *time2 = NULL, *vo = NULL,
+ *uri = NULL, *user = NULL, *group = "NULL", *role = "NULL",
+ *cap = "NULL", *server = NULL, *ucuser, *signature = NULL,
+ *data = NULL, *datalen = NULL;
+ X509_EXTENSION *ex;
+ ASN1_STRING *asn1str;
+ time_t now, time1_time = 0, time2_time = 0,
+ uctime1_time, uctime2_time;
+
+ time(&now);
+
+ uctime1_time =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)));
+ uctime2_time =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)));
+ ucuser =
+ X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0);
+
+ for (i = 0; i < X509_get_ext_count(cert); ++i)
+ {
+ ex = X509_get_ext(cert, i);
+
+ OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1);
+
+ if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */
+ {
+ asn1str = X509_EXTENSION_get_data(ex);
+ charstr = (char *) malloc(ASN1_STRING_length(asn1str) + 1);
+ memcpy(charstr, ASN1_STRING_data(asn1str),
+ ASN1_STRING_length(asn1str));
+ charstr[ASN1_STRING_length(asn1str)] = '\0';
+
+ siglen = -1;
+
+ if ((sscanf(charstr, "SIGLEN:%u", &siglen) != 1) ||
+ (siglen == -1) ||
+ ((p = index(charstr, '\n')) == NULL))
+ {
+ free(charstr);
+ continue;
+ }
+
+ ++p;
+
+ if (strncmp(p, "SIGNATURE:", sizeof("SIGNATURE:") - 1) != 0)
+ {
+ free(charstr);
+ continue;
+ }
+
+ signature = &p[sizeof("SIGNATURE:") - 1];
+
+ p = &p[siglen + sizeof("SIGNATURE:") - 1];
+ data = p;
+
+ /* nasty pointer arithmetic! */
+ dataoffset = (unsigned int) ((long) data - (long) charstr);
+ datalength = (unsigned int)
+ (ASN1_STRING_length(asn1str) - dataoffset);
+
+ if (datalength <= 0)
+ {
+ free(charstr);
+ continue;
+ }
+
+ while (1)
+ {
+ if (strncmp(p, "USER:", sizeof("USER:") - 1) == 0)
+ {
+ p = &p[sizeof("USER:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ user = p;
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ else if (strncmp(p, "TIME1:", sizeof("TIME1:") - 1) == 0)
+ {
+ p = &p[sizeof("TIME1:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ time1 = p;
+ p = index(p, '\n');
+ if (p != NULL) *p = '\0';
+
+ time1_time = GRSTasn1TimeToTimeT(time1);
+ if (time1_time < uctime1_time) time1_time = uctime1_time;
+ if (p == NULL) break;
+ ++p;
+ }
+ else if (strncmp(p, "TIME2:", sizeof("TIME2:") - 1) == 0)
+ {
+ p = &p[sizeof("TIME2:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ time2 = p;
+ p = index(p, '\n');
+ if (p != NULL) *p = '\0';
+
+ time2_time = GRSTasn1TimeToTimeT(time2);
+ if (time2_time > uctime2_time) time2_time = uctime2_time;
+ if (p == NULL) break;
+ ++p;
+ }
+ else if (strncmp(p, "VO:", sizeof("VO:") - 1) == 0)
+ {
+ p = &p[sizeof("VO:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ vo = p;
+
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ else if (strncmp(p, "SERVER:", sizeof("SERVER:") - 1) == 0)
+ {
+ p = &p[sizeof("SERVER:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ server = p;
+
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ else if (strncmp(p, "DATALEN:", sizeof("DATALEN:") - 1) == 0)
+ {
+ p = &p[sizeof("DATALEN:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ datalen = p;
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ break;
+ }
+ else /* not something we use */
+ {
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ }
+
+ if (
+ (now >= time1_time) &&
+ (now <= time2_time) &&
+ (signature != NULL) &&
+ (data != NULL) &&
+ (siglen > 0) &&
+ (user != NULL) &&
+ (ucuser != NULL) &&
+ (strcmp(user, ucuser) == 0) &&
+ (GRSTx509CheckVomsSig(signature, siglen,
+ &((ASN1_STRING_data(asn1str))[dataoffset]),
+ datalength, vomsdir, vo,
+ server) == GRST_RET_OK)) while (1)
+ {
+ if (strncmp(p, "GROUP:", sizeof("GROUP:") - 1) == 0)
+ {
+ p = &p[sizeof("GROUP:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ group = p;
+ role = "NULL";
+ cap = "NULL";
+
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ else if (strncmp(p, "ROLE:", sizeof("ROLE:") - 1) == 0)
+ {
+ p = &p[sizeof("ROLE:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ role = p;
+
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ else if (strncmp(p, "CAP:", sizeof("CAP:") - 1) == 0)
+ {
+ p = &p[sizeof("CAP:") - 1];
+ while ((*p != '\n') && (*p != '\0') && (*p <= ' ')) ++p;
+ cap = p;
+
+ p = index(p, '\n');
+ if (p != NULL) *p = '\0';
+
+ if (*lastcred < maxcreds - 1)
+ {
+ ++(*lastcred);
+
+ if ((strcmp(role, "NULL") == 0) &&
+ (strcmp(cap , "NULL") == 0))
+ snprintf(&creds[*lastcred * (credlen + 1)], credlen+1,
+ "VOMS %010lu %010lu 0 /%s%s",
+ time1_time, time2_time, vo, group);
+ else if ((strcmp(role, "NULL") != 0) &&
+ (strcmp(cap , "NULL") == 0))
+ snprintf(&creds[*lastcred * (credlen + 1)], credlen+1,
+ "VOMS %010lu %010lu 0 /%s%s/Role=%s",
+ time1_time, time2_time, vo, group, role);
+ else if ((strcmp(role, "NULL") == 0) &&
+ (strcmp(cap , "NULL") != 0))
+ snprintf(&creds[*lastcred * (credlen + 1)], credlen+1,
+ "VOMS %010lu %010lu 0 /%s%s/Capability=%s",
+ time1_time, time2_time, vo, group, cap);
+ else
+ snprintf(&creds[*lastcred * (credlen + 1)], credlen+1,
+ "VOMS %010lu %010lu 0 /%s%s/Role=%s/Capability=%s",
+ time1_time, time2_time, vo, group, role, cap);
+ }
+
+ if (p == NULL) break;
+ ++p;
+ }
+ else /* not something we use */
+ {
+ p = index(p, '\n');
+ if (p == NULL) break;
+ *p = '\0';
+ ++p;
+ }
+ }
+
+ free(charstr);
+ }
+ }
+
+ return GRST_RET_OK;
+}
+
+/// Turn a Compact Cred line into a GRSTgaclCred object
+/**
+ * Returns pointer to created GRSTgaclCred or NULL or failure.
+ */
+
+GRSTgaclCred *GRSTx509CompactToCred(char *grst_cred)
+{
+ int delegation;
+ char *p;
+ time_t now, notbefore, notafter;
+ GRSTgaclCred *cred = NULL;
+
+ time(&now);
+
+ if (grst_cred == NULL) return NULL; /* just in case */
+
+ if (strncmp(grst_cred, "X509USER ", 9) == 0)
+ {
+ if ((sscanf(grst_cred, "X509USER %lu %lu %d",
+ ¬before, ¬after, &delegation) == 3)
+ && (now >= notbefore)
+ && (now <= notafter)
+ && (p = index(grst_cred, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' ')))
+ {
+ cred = GRSTgaclCredNew("person");
+ GRSTgaclCredSetDelegation(cred, delegation);
+ GRSTgaclCredAddValue(cred, "dn", &p[1]);
+ }
+
+ return cred;
+ }
+
+ if (strncmp(grst_cred, "VOMS ", 5) == 0)
+ {
+ if ((sscanf(grst_cred, "VOMS %lu %lu",
+ ¬before, ¬after, &delegation) == 3)
+ && (now >= notbefore)
+ && (now <= notafter)
+ && (p = index(grst_cred, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' ')))
+ {
+ /* include /VO/group/subgroup/Role=role/Capability=cap */
+
+ if (*p != '/') return NULL; /* must begin with / */
+
+ cred = GRSTgaclCredNew("voms");
+ GRSTgaclCredSetDelegation(cred, delegation);
+ GRSTgaclCredAddValue(cred, "fqan", p);
+ }
+
+ return cred;
+ }
+
+ return NULL; /* dont recognise this credential type */
+}
+
+/// Get the credentials in an X509 cert/GSI proxy, including any VOMS
+/**
+ * Credentials are placed in Compact Creds string array at *creds.
+ *
+ * Function returns GRST_RET_OK on success, or GRST_RET_FAILED if
+ * some inconsistency found in certificate.
+ */
+
+int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen,
+ char *creds, STACK_OF(X509) *certstack, char *vomsdir)
+{
+ int i, j, delegation = 0;
+ char credtemp[credlen+1];
+ X509 *cert, *usercert = NULL, *gsiproxycert = NULL;
+
+ *lastcred = -1;
+
+ for (i = sk_X509_num(certstack) - 1; i >= 0; --i)
+ {
+ cert = sk_X509_value(certstack, i);
+
+ if (usercert != NULL)
+ { /* found a (GSI proxy) cert after the user cert */
+ gsiproxycert = cert;
+ ++delegation;
+ }
+
+ if ((usercert == NULL) &&
+ (i < sk_X509_num(certstack) - 1) &&
+ (GRSTx509IsCA(cert) != GRST_RET_OK)) usercert = cert;
+ /* found the 1st non-CA cert */
+ }
+
+ if ((usercert == NULL) /* if no usercert ("EEC"), we're not interested */
+ ||
+ (snprintf(credtemp, credlen+1, "X509USER %010lu %010lu %d %s",
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert))),
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert))),
+ delegation,
+ X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0)) >= credlen+1)
+ ||
+ (*lastcred >= maxcreds-1))
+ {
+ *lastcred = -1; /* just in case the caller looks at it */
+ return GRST_RET_FAILED; /* tell caller that things didn't work out */
+ }
+
+ ++(*lastcred);
+ strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
+
+ if ((gsiproxycert != NULL)
+ &&
+ (snprintf(credtemp, credlen+1, "GSIPROXY %010lu %010lu %d %s",
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(gsiproxycert))),
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(gsiproxycert))),
+ delegation,
+ X509_NAME_oneline(X509_get_subject_name(gsiproxycert), NULL, 0)) < credlen+1)
+ &&
+ (*lastcred < maxcreds-1))
+ {
+ ++(*lastcred);
+ strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
+
+ GRSTx509GetVomsCreds(lastcred, maxcreds, credlen, creds,
+ gsiproxycert, usercert, vomsdir);
+ }
+
+ return GRST_RET_OK;
+}
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*------------------------------------------------------------------------*
+ * For more about GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+#ifndef VERSION
+#define VERSION "0.0.0"
+#endif
+
+#define _GNU_SOURCE
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <malloc.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <curl/curl.h>
+
+/* deal with older versions of libcurl and curl.h */
+
+#ifndef CURLOPT_WRITEDATA
+#define CURLOPT_WRITEDATA CURLOPT_FILE
+#endif
+
+#ifndef CURLOPT_READDATA
+#define CURLOPT_READDATA CURLOPT_FILE
+#endif
+
+#ifndef CURLE_HTTP_RETURNED_ERROR
+#define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
+#endif
+
+#define HTCP_GET 1
+#define HTCP_PUT 2
+#define HTCP_DELETE 3
+#define HTCP_LIST 4
+#define HTCP_LONGLIST 5
+#define HTCP_MKDIR 6
+
+struct grst_stream_data { char *source;
+ char *destination;
+ int ishttps;
+ int method;
+ FILE *fp;
+ char *cert;
+ char *key;
+ char *capath;
+ char *useragent;
+ char *errorbuf;
+ int noverify;
+ int anonymous;
+ int verbose; } ;
+
+struct grst_index_blob { char *text;
+ size_t used;
+ size_t allocated; } ;
+
+struct grst_dir_list { char *filename;
+ size_t length;
+ int length_set;
+ time_t modified;
+ int modified_set; } ;
+
+struct grst_header_data { int retcode;
+ char *location;
+ size_t length;
+ int length_set;
+ time_t modified;
+ int modified_set; } ;
+
+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;
+ size_t realsize;
+ struct tm modified_tm;
+ struct grst_header_data *header_data;
+
+ header_data = (struct grst_header_data *) p;
+ realsize = size * nmemb;
+ s = malloc(realsize + 1);
+ memcpy(s, ptr, realsize);
+ s[realsize] = '\0';
+
+ if (sscanf(s, "Content-Length: %d", &(header_data->length)) == 1)
+ 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]);
+ else if (strncmp(s, "Last-Modified: ", 15) == 0)
+ {
+ /* follow RFC 2616: first try RFC 822 (kosher), then RFC 850 and
+ asctime() formats too. Must be GMT whatever the format. */
+
+ if (strptime(&s[15], "%a, %d %b %Y %T GMT", &modified_tm) != NULL)
+ {
+ header_data->modified = mktime(&modified_tm);
+ header_data->modified_set = 1;
+ }
+ else if (strptime(&s[15], "%a, %d-%b-%y %T GMT", &modified_tm) != NULL)
+ {
+ header_data->modified = mktime(&modified_tm);
+ header_data->modified_set = 1;
+ }
+ else if (strptime(&s[15], "%a %b %d %T %Y", &modified_tm) != NULL)
+ {
+ header_data->modified = mktime(&modified_tm);
+ header_data->modified_set = 1;
+ }
+ }
+
+ free(s);
+ return realsize;
+}
+
+int set_std_opts(CURL *easyhandle, struct grst_stream_data *common_data)
+{
+ struct stat statbuf;
+
+ curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 0);
+
+ if ((common_data->cert != NULL) && (common_data->key != NULL))
+ {
+ curl_easy_setopt(easyhandle, CURLOPT_SSLENGINE, NULL);
+ curl_easy_setopt(easyhandle, CURLOPT_SSLCERTTYPE, "PEM");
+ curl_easy_setopt(easyhandle, CURLOPT_SSLCERT, common_data->cert);
+ curl_easy_setopt(easyhandle, CURLOPT_SSLKEY, common_data->key);
+ }
+ else
+ {
+ curl_easy_setopt(easyhandle, CURLOPT_SSLENGINE, "RSA");
+ curl_easy_setopt(easyhandle, CURLOPT_SSLCERTTYPE, "ENG");
+ }
+
+ if (common_data->capath != NULL)
+ {
+#if (LIBCURL_VERSION_NUM >= 0x070908)
+ if ((stat(common_data->capath, &statbuf) == 0) &&
+ S_ISDIR(statbuf.st_mode))
+ curl_easy_setopt(easyhandle, CURLOPT_CAPATH, common_data->capath);
+ else
+#endif
+ curl_easy_setopt(easyhandle, CURLOPT_CAINFO, common_data->capath);
+ }
+
+ if (common_data->noverify)
+ curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 0);
+ else curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 2);
+
+ return 1;
+}
+
+int do_copies(char *sources[], char *destination,
+ struct grst_stream_data *common_data)
+{
+ char *p, *thisdestination;
+ int isrc, anyerror = 0, thiserror, isdirdest;
+ CURL *easyhandle;
+ struct stat statbuf;
+ struct grst_header_data header_data;
+
+ easyhandle = curl_easy_init();
+
+ 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_HEADERFUNCTION, headers_callback);
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEHEADER, &header_data);
+
+ set_std_opts(easyhandle, common_data);
+
+ curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, common_data->errorbuf);
+
+ if (destination[strlen(destination) - 1] != '/')
+ {
+ isdirdest = 0;
+ thisdestination = destination;
+ }
+ else isdirdest = 1;
+
+ for (isrc=0; sources[isrc] != NULL; ++isrc)
+ {
+ if (isdirdest)
+ {
+ p = rindex(sources[isrc], '/');
+ if (p == NULL) p = sources[isrc];
+ else p++;
+
+ asprintf(&thisdestination, "%s%s", destination, p);
+ }
+
+ if (common_data->verbose > 0)
+ fprintf(stderr, "%s -> %s\n", sources[isrc], thisdestination);
+
+ 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;
+ }
+
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, common_data->fp);
+ curl_easy_setopt(easyhandle, CURLOPT_URL, sources[isrc]);
+ }
+ else if (common_data->method == HTCP_PUT)
+ {
+ if (stat(sources[isrc], &statbuf) != 0)
+ {
+ fprintf(stderr, "... source file %s not found\n", sources[isrc]);
+ anyerror = 99;
+ if (isdirdest) free(thisdestination);
+ continue;
+ }
+
+ 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;
+ }
+
+ curl_easy_setopt(easyhandle, CURLOPT_READDATA, common_data->fp);
+ curl_easy_setopt(easyhandle, CURLOPT_URL, thisdestination);
+ curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE, statbuf.st_size);
+ curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1);
+ }
+
+ header_data.retcode = 0;
+ thiserror = curl_easy_perform(easyhandle);
+
+ 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_easy_cleanup(easyhandle);
+
+ return anyerror;
+}
+
+int do_deletes(char *sources[], struct grst_stream_data *common_data)
+{
+ int isrc, anyerror = 0, thiserror;
+ CURL *easyhandle;
+ struct grst_header_data header_data;
+
+ easyhandle = curl_easy_init();
+
+ 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_HEADERFUNCTION, headers_callback);
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEHEADER, &header_data);
+
+ curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, common_data->errorbuf);
+ curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "DELETE");
+ curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1);
+
+ set_std_opts(easyhandle, common_data);
+
+ for (isrc=0; sources[isrc] != NULL; ++isrc)
+ {
+ if (common_data->verbose > 0)
+ fprintf(stderr, "Deleting %s\n", sources[isrc]);
+
+ curl_easy_setopt(easyhandle, CURLOPT_URL, sources[isrc]);
+
+ header_data.retcode = 0;
+ thiserror = curl_easy_perform(easyhandle);
+
+ 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);
+ }
+
+ curl_easy_cleanup(easyhandle);
+
+ return anyerror;
+}
+
+int do_mkdirs(char *sources[], struct grst_stream_data *common_data)
+{
+ int isrc, anyerror = 0, thiserror;
+ CURL *easyhandle;
+ struct grst_header_data header_data;
+
+ easyhandle = curl_easy_init();
+
+ 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_HEADERFUNCTION, headers_callback);
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEHEADER, &header_data);
+
+ curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, common_data->errorbuf);
+ curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "PUT");
+ curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1);
+
+ set_std_opts(easyhandle, common_data);
+
+ for (isrc=0; sources[isrc] != NULL; ++isrc)
+ {
+ if (common_data->verbose > 0)
+ fprintf(stderr, "Make directory %s\n", sources[isrc]);
+
+ curl_easy_setopt(easyhandle, CURLOPT_URL, sources[isrc]);
+
+ header_data.retcode = 0;
+ thiserror = curl_easy_perform(easyhandle);
+
+ 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);
+ }
+
+ curl_easy_cleanup(easyhandle);
+
+ return anyerror;
+}
+
+size_t rawindex_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ if ( ((struct grst_index_blob *) data)->used + size * nmemb >=
+ ((struct grst_index_blob *) data)->allocated )
+ {
+ ((struct grst_index_blob *) data)->allocated =
+ ((struct grst_index_blob *) data)->used + size * nmemb + 4096;
+
+ ((struct grst_index_blob *) data)->text =
+ realloc( ((struct grst_index_blob *) data)->text,
+ ((struct grst_index_blob *) data)->allocated );
+ }
+
+ memcpy( &( ((struct grst_index_blob *)
+ data)->text[((struct grst_index_blob *) data)->used] ),
+ ptr, size * nmemb);
+
+ ((struct grst_index_blob *) data)->used += size * nmemb;
+
+ return size * nmemb;
+}
+
+char *canonicalise(char *link, char *source)
+{
+ int i, j, srclen;
+ char *s;
+
+ srclen = strlen(source);
+
+ if ((strncmp(link, "https://", 8) == 0) ||
+ (strncmp(link, "http://", 7) == 0))
+ {
+ if (strncmp(link, source, srclen) != 0) return NULL; /* other site */
+
+ if (link[srclen] == '\0') return NULL; /* we dont self-link! */
+
+ for (i=0; link[srclen + i] != '\0'; ++i)
+ if (link[srclen + i] == '/')
+ {
+ if (link[srclen + i + 1] != '\0') return NULL; /* no subdirs */
+ else return strdup(&link[srclen]); /* resolves to this dir */
+ }
+ }
+ else if (link[0] != '/') /* relative link - need to check for subsubdirs */
+ {
+ for (i=0; link[i] != '\0'; ++i)
+ if ((link[i] == '/') && (link[i+1] != '\0')) return NULL;
+
+ s = strdup(link);
+
+ for (i=0; s[i] != '\0'; ++i)
+ if (s[i] == '#')
+ {
+ s[i] = '\0';
+ break;
+ }
+
+ return s;
+ }
+
+ /* absolute link on this server, starting / */
+
+ for (i=8; source[i] != '\0'; ++i) if (source[i] == '/') break;
+
+ if (strncmp(link, &source[i], srclen - i) != 0) return NULL;
+
+ for (j = srclen - i; link[j] != '\0'; ++j)
+ if ((link[j] == '/') && (link[j+1] != '\0')) return NULL;
+
+ s = strdup(&link[srclen - i]);
+
+ for (i=0; s[i] != '\0'; ++i)
+ if (s[i] == '#')
+ {
+ s[i] = '\0';
+ break;
+ }
+
+ if (s[0] == '\0') /* on second thoughts... */
+ {
+ free(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+int grst_dir_list_cmp(const void *a, const void *b)
+{
+ return strcmp( ((struct grst_dir_list *) a)->filename,
+ ((struct grst_dir_list *) b)->filename);
+}
+
+struct grst_dir_list *index_to_dir_list(char *text, char *source)
+{
+ int taglevel = 0, wordnew = 1, i, namestart, used = 0,
+ allocated = 256;
+ char *p, *s;
+ struct grst_dir_list *list;
+
+ list = (struct grst_dir_list *)
+ malloc(allocated * sizeof(struct grst_dir_list));
+
+ list[0].filename = NULL;
+ list[0].length = 0;
+ list[0].length_set = 0;
+ list[0].modified = 0;
+ list[0].modified_set = 0;
+
+ for (p=text; *p != '\0'; ++p)
+ {
+ if (*p == '<')
+ {
+ ++taglevel;
+
+ if ((taglevel == 1) && (list[used].filename != NULL))
+ {
+ ++used;
+ if (used >= allocated)
+ {
+ allocated += 256;
+ list = (struct grst_dir_list *)
+ malloc(allocated * sizeof(struct grst_dir_list));
+ }
+
+ list[used].filename = NULL;
+ list[used].length = 0;
+ list[used].length_set = 0;
+ list[used].modified = 0;
+ list[used].modified_set = 0;
+ }
+
+ wordnew = 1;
+ continue;
+ }
+
+ if (*p == '>')
+ {
+ --taglevel;
+ wordnew = 1;
+ continue;
+ }
+
+ if (isspace(*p))
+ {
+ wordnew = 1;
+ continue;
+ }
+
+ if ((wordnew) && (taglevel == 1))
+ {
+ if (((*p == 'h') || (*p == 'H')) &&
+ (strncasecmp(p, "href=", 5) == 0))
+ {
+ if (p[5] == '"') { namestart = 6;
+ for (i=namestart; (p[i] != '\0') &&
+ (p[i] != '"' ) &&
+ (p[i] != '\n') &&
+ (p[i] != '\t') &&
+ (p[i] != '>' ) ; ++i) ; }
+ else { namestart = 5;
+ for (i=namestart; (p[i] != '\0') &&
+ (p[i] != '"' ) &&
+ (p[i] != ' ' ) &&
+ (p[i] != '\n') &&
+ (p[i] != '\t') &&
+ (p[i] != ')' ) &&
+ (p[i] != '>' ) ; ++i) ; }
+ if (i > namestart)
+ {
+ s = malloc(1 + i - namestart);
+ memcpy(s, &p[namestart], i - namestart);
+ s[i - namestart] = '\0';
+
+ list[used].filename = canonicalise(s, source);
+ free(s);
+ }
+
+ p = &p[i-1]; /* -1 since continue results in ++i */
+ continue;
+ }
+
+ if (((*p == 'c') || (*p == 'C')) &&
+ (strncasecmp(p, "content-length=", 15) == 0))
+ {
+ list[used].length = 0;
+ list[used].length_set = 1;
+
+ if (p[15] == '"') list[used].length = atoi(&p[16]);
+ else list[used].length = atoi(&p[15]);
+
+ p = &p[15];
+ continue;
+ }
+
+ if (((*p == 'l') || (*p == 'L')) &&
+ (strncasecmp(p, "last-modified=", 14) == 0))
+ {
+ list[used].modified = 0;
+ list[used].modified_set = 1;
+
+ if (p[14] == '"') list[used].modified = atoi(&p[15]);
+ else list[used].modified = atoi(&p[14]);
+
+ p = &p[14];
+ continue;
+ }
+ }
+
+ wordnew = 0;
+ }
+
+ qsort((void *) list, used, sizeof(struct grst_dir_list), grst_dir_list_cmp);
+
+ return list;
+}
+
+int do_listings(char *sources[], struct grst_stream_data *common_data,
+ int islonglist)
+{
+ int isrc, anyerror = 0, thiserror, i, isdir, ilast;
+ CURL *easyhandle;
+ const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ char *s;
+ struct grst_index_blob rawindex;
+ struct grst_dir_list *list;
+ struct grst_header_data header_data;
+ struct tm modified_tm;
+ time_t now;
+
+ time(&now);
+
+ easyhandle = curl_easy_init();
+
+ 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_WRITEHEADER, &header_data);
+ curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, headers_callback);
+
+ curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, common_data->errorbuf);
+
+ set_std_opts(easyhandle, common_data);
+
+ for (isrc=0; sources[isrc] != NULL; ++isrc)
+ {
+ if (common_data->verbose > 0)
+ fprintf(stderr, "Listing %s\n", sources[isrc]);
+
+ if (sources[1] != NULL) printf("\n%s:\n", sources[isrc]);
+
+ curl_easy_setopt(easyhandle, CURLOPT_URL, sources[isrc]);
+
+ if (sources[isrc][strlen(sources[isrc])-1] == '/')
+ {
+ isdir = 1;
+ curl_easy_setopt(easyhandle,CURLOPT_WRITEFUNCTION,rawindex_callback);
+ curl_easy_setopt(easyhandle,CURLOPT_WRITEDATA,(void *) &rawindex);
+ curl_easy_setopt(easyhandle,CURLOPT_NOBODY,0);
+ rawindex.text = NULL;
+ rawindex.used = 0;
+ rawindex.allocated = 0;
+ }
+ else
+ {
+ isdir = 0;
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, NULL);
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, NULL);
+ curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1);
+ }
+
+ header_data.length_set = 0;
+ header_data.modified_set = 0;
+ header_data.retcode = 0;
+ thiserror = curl_easy_perform(easyhandle);
+
+ 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 (isdir)
+ {
+ if (common_data->verbose > 0)
+ fprintf(stderr, "... OK (%d)\n", header_data.retcode);
+
+ rawindex.text[rawindex.used] = '\0';
+
+ list = index_to_dir_list(rawindex.text, sources[isrc]);
+ ilast = -1;
+
+ for (i=0; list[i].filename != NULL; ++i)
+ {
+ if (list[i].filename[0] == '.') continue;
+
+ if (strncmp(list[i].filename, "mailto:", 7) == 0) continue;
+
+ if ((ilast >= 0) &&
+ (strcmp(list[i].filename, list[ilast].filename) == 0))
+ continue;
+ ilast=i;
+
+ if (islonglist)
+ {
+ if (!list[i].length_set || !list[i].modified_set)
+ {
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION,
+ NULL);
+ curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, NULL);
+ curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1);
+
+ asprintf(&s, "%s%s", sources[isrc], list[i].filename);
+ curl_easy_setopt(easyhandle, CURLOPT_URL, s);
+
+ header_data.length_set = 0;
+ header_data.modified_set = 0;
+ header_data.retcode = 0;
+ thiserror = curl_easy_perform(easyhandle);
+ free(s);
+
+ if ((thiserror == 0) &&
+ (header_data.retcode >= 200) &&
+ (header_data.retcode <= 299))
+ {
+ if (header_data.length_set)
+ {
+ list[i].length_set = 1;
+ list[i].length = header_data.length;
+ }
+
+ if (header_data.modified_set)
+ {
+ list[i].modified_set = 1;
+ list[i].modified = header_data.modified;
+ }
+ }
+ }
+
+ if (list[i].length_set) printf("%10ld ", list[i].length);
+ else fputs(" ? ", stdout);
+
+ if (list[i].modified_set)
+ {
+ localtime_r(&(list[i].modified), &modified_tm);
+
+ if (list[i].modified < now - 15552000)
+ printf("%s %2d %4d ",
+ months[modified_tm.tm_mon],
+ modified_tm.tm_mday,
+ modified_tm.tm_year + 1900);
+ else printf("%s %2d %02d:%02d ",
+ months[modified_tm.tm_mon],
+ modified_tm.tm_mday,
+ modified_tm.tm_hour,
+ modified_tm.tm_min);
+ }
+ else fputs(" ? ? ? ", stdout);
+ }
+
+ puts(list[i].filename);
+ }
+ }
+ else
+ {
+ if (islonglist)
+ {
+ printf("%10ld ", header_data.length);
+
+ localtime_r(&(header_data.modified), &modified_tm);
+
+ if (header_data.modified < now - 15552000)
+ printf("%s %2d %4d ",
+ months[modified_tm.tm_mon],
+ modified_tm.tm_mday,
+ modified_tm.tm_year + 1900);
+ else printf("%s %2d %02d:%02d ",
+ months[modified_tm.tm_mon],
+ modified_tm.tm_mday,
+ modified_tm.tm_hour,
+ modified_tm.tm_min);
+ }
+
+ puts(sources[isrc]);
+ }
+ }
+
+ curl_easy_cleanup(easyhandle);
+
+ return anyerror;
+}
+
+#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 */
+
+ return NULL;
+}
+#endif
+
+void printsyntax(char *argv0)
+{
+ char *p;
+
+ p = rindex(argv0, '/');
+ if (p != NULL) ++p;
+ else p = argv0;
+
+ fprintf(stderr, "%s [options] Source-URL[s] [Destination URL]\n"
+ "%s is one of a set of clients to fetch files or directory listings\n"
+"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"
+"(Version: %s)\n", p, p, VERSION);
+}
+
+int main(int argc, char *argv[])
+{
+ char **sources, *destination = NULL, *executable, *p;
+ int c, i, option_index, anyerror;
+ 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},
+ {0, 0, 0, 0} };
+
+#if (LIBCURL_VERSION_NUM < 0x070908)
+ char *tmp_ca_roots = NULL;
+#endif
+
+ if (argc == 1)
+ {
+ printsyntax(argv[0]);
+ return 0;
+ }
+
+ common_data.cert = NULL;
+ common_data.key = NULL;
+ common_data.capath = NULL;
+ common_data.method = 0;
+ common_data.errorbuf = malloc(CURL_ERROR_SIZE);
+ asprintf(&(common_data.useragent),
+ "htcp/%s (http://www.gridpp.ac.uk/gridsite/)", VERSION);
+ common_data.verbose = 0;
+ common_data.noverify = 0;
+ common_data.anonymous = 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) common_data.cert = optarg;
+ else if (option_index == 2) common_data.key = optarg;
+ else if (option_index == 3) common_data.capath = optarg;
+ else if (option_index == 4) common_data.method = HTCP_DELETE;
+ else if (option_index == 5) common_data.method = HTCP_LIST;
+ else if (option_index == 6) common_data.method = HTCP_LONGLIST;
+ 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 (c == 'v') ++(common_data.verbose);
+ }
+
+ if (common_data.verbose > 0)
+ {
+ p = rindex(argv[0], '/');
+ if (p != NULL) ++p;
+ else p = argv[0];
+ fprintf(stderr, "%s version %s\n", p, VERSION);
+ }
+
+ if (common_data.anonymous) /* prevent any use of user certs */
+ {
+ common_data.cert = NULL;
+ common_data.key = NULL;
+ }
+ else if ((common_data.cert == NULL) && (common_data.key != NULL))
+ common_data.cert = common_data.key;
+ else if ((common_data.cert != NULL) && (common_data.key == NULL))
+ common_data.key = common_data.cert;
+ else if ((common_data.cert == NULL) && (common_data.key == NULL))
+ {
+ common_data.cert = getenv("X509_USER_PROXY");
+ if (common_data.cert != NULL) common_data.key = common_data.cert;
+ else
+ {
+ asprintf(&(common_data.cert), "/tmp/x509up_u%d", geteuid());
+
+ /* one fine day, we will check the proxy file for expiry too ... */
+
+ if (stat(common_data.cert, &statbuf) == 0)
+ common_data.key = common_data.cert;
+ else
+ {
+ common_data.cert = getenv("X509_USER_CERT");
+ common_data.key = getenv("X509_USER_KEY");
+
+ userpasswd = getpwuid(geteuid());
+
+ if ((common_data.cert == NULL) &&
+ (userpasswd != NULL) &&
+ (userpasswd->pw_dir != NULL))
+ asprintf(&(common_data.cert), "%s/.globus/usercert.pem",
+ userpasswd->pw_dir);
+
+ if ((common_data.key == NULL) &&
+ (userpasswd != NULL) &&
+ (userpasswd->pw_dir != NULL))
+ asprintf(&(common_data.key), "%s/.globus/userkey.pem",
+ userpasswd->pw_dir);
+ }
+ }
+ }
+
+ if (common_data.capath == NULL) common_data.capath = getenv("X509_CERT_DIR");
+
+ if (common_data.capath == NULL)
+ common_data.capath = "/etc/grid-security/certificates";
+
+#if (LIBCURL_VERSION_NUM < 0x070908)
+ /* libcurl before 7.9.8 doesnt support CURLOPT_CAPATH and the directory */
+
+ if ((common_data.capath != NULL) &&
+ (stat(common_data.capath, &statbuf) == 0) && S_ISDIR(statbuf.st_mode))
+ {
+ tmp_ca_roots = make_tmp_ca_roots(common_data.capath);
+ common_data.capath = tmp_ca_roots;
+ }
+#endif
+
+ executable = rindex(argv[0], '/');
+ if (executable != NULL) executable++;
+ else executable = argv[0];
+
+ if (common_data.method == 0) /* command-line options override exec name */
+ {
+ if (strcmp(executable,"htls")==0) common_data.method=HTCP_LIST;
+ else if (strcmp(executable,"htll")==0) common_data.method=HTCP_LONGLIST;
+ else if (strcmp(executable,"htrm")==0) common_data.method=HTCP_DELETE;
+ else if (strcmp(executable,"htmkdir")==0) common_data.method=HTCP_MKDIR;
+ }
+
+ if ((common_data.method == HTCP_DELETE) ||
+ (common_data.method == HTCP_LIST) ||
+ (common_data.method == HTCP_MKDIR) ||
+ (common_data.method == HTCP_LONGLIST))
+ {
+ if (optind >= argc)
+ {
+ fprintf(stderr, "Must give at least 1 non-option argument\n\n");
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ sources = (char **) malloc(sizeof(char *) * (1 + argc - optind));
+ for (i=0; i < argc - optind; ++i)
+ {
+ sources[i] = argv[optind + i];
+
+ if ((common_data.method == HTCP_MKDIR) &&
+ (sources[i][strlen(sources[i])-1] != '/'))
+ {
+ fprintf(stderr, "Argument \"%s\" is not a "
+ "directory URL (no trailing /)\n\n", sources[i]);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+
+ sources[i] = NULL;
+
+ if (common_data.method == HTCP_DELETE)
+ anyerror = do_deletes(sources, &common_data);
+ else if (common_data.method == HTCP_MKDIR)
+ anyerror = do_mkdirs(sources, &common_data);
+ else if (common_data.method == HTCP_LONGLIST)
+ anyerror = do_listings(sources, &common_data, 1);
+ else anyerror = do_listings(sources, &common_data, 0);
+
+ if (anyerror > 99) anyerror = CURLE_HTTP_RETURNED_ERROR;
+
+ return anyerror;
+ }
+
+ if (optind >= argc - 1)
+ {
+ fputs("Must give at least 2 non-option arguments\n\n", stderr);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ sources = (char **) malloc(sizeof(char *) * (argc - optind));
+
+ for (i=0; i < (argc - optind - 1); ++i)
+ {
+ if (strncmp(argv[optind + i], "file:", 5) == 0)
+ sources[i] = &argv[optind + i][5];
+ else sources[i] = argv[optind + i];
+
+ if (sources[i][0] == '\0')
+ {
+ fprintf(stderr, "Source argument %d is empty\n\n", i + 1);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+
+ sources[i] = NULL;
+
+ if (strncmp(argv[optind + i], "file:", 5) == 0)
+ destination = &argv[optind + i][5];
+ else destination = argv[optind + i];
+
+ if (destination[0] == '\0')
+ {
+ fputs("Destination argument is empty\n\n", stderr);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ if ((argc - optind > 2) && (destination[strlen(destination)-1] != '/'))
+ {
+ fputs("For multiple sources, destination "
+ "must be a directory (end in /)\n\n", stderr);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ if ((strncmp(destination, "http://", 7) == 0) ||
+ (strncmp(destination, "https://", 8) == 0))
+ common_data.method = HTCP_PUT;
+ else common_data.method = HTCP_GET;
+
+ for (i=0; sources[i] != NULL; ++i)
+ {
+ if ((common_data.method == HTCP_PUT) &&
+ ((strncmp(sources[i], "http://", 7) == 0) ||
+ (strncmp(sources[i], "https://", 8) == 0)))
+ {
+ fputs("Cannot have both source and destination remote\n\n",stderr);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ if ((common_data.method == HTCP_GET) &&
+ ((strncmp(sources[i], "http://", 7) != 0) &&
+ (strncmp(sources[i], "https://", 8) != 0)))
+ {
+ fputs("Cannot have both source and "
+ "destination local (for now)\n\n",stderr);
+ printsyntax(argv[0]);
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+
+ anyerror = do_copies(sources, destination, &common_data);
+ if (anyerror > 99) anyerror = CURLE_HTTP_RETURNED_ERROR;
+
+ return anyerror;
+}
--- /dev/null
+/*
+ Copyright (c) 2003-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/gridsite/ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef VERSION
+#define VERSION "x.x.x"
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <apr_strings.h>
+
+#include <ap_config.h>
+#include <httpd.h>
+#include <http_config.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_protocol.h>
+#include <http_request.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <string.h>
+#include <dirent.h>
+
+#include <time.h>
+
+#include "mod_ssl-private.h"
+
+#include "gridsite.h"
+
+#ifndef UNSET
+#define UNSET -1
+#endif
+
+module AP_MODULE_DECLARE_DATA gridsite_module;
+
+typedef struct
+{
+ int auth;
+ int envs;
+ int format;
+ int indexes;
+ char *indexheader;
+ int gridsitelink;
+ char *adminfile;
+ char *adminuri;
+ char *helpuri;
+ char *dnlists;
+ char *dnlistsuri;
+ char *adminlist;
+ int gsiproxylimit;
+ char *unzip;
+ char *methods;
+ char *editable;
+ char *headfile;
+ char *footfile;
+} mod_gridsite_cfg; /* per-directory config choices */
+
+char *make_admin_footer(request_rec *r, mod_gridsite_cfg *conf,
+ int isdirectory)
+/*
+ make string holding last modified text and admin links
+*/
+{
+ char *out, *https, *p, *dn = NULL, *file = NULL, *permstr = NULL,
+ *temp, modified[99], *dir_uri, *grst_cred_0 = NULL;
+ GRSTgaclPerm perm = GRST_PERM_NONE;
+ struct tm mtime_tm;
+ time_t mtime_time;
+
+ https = (char *) apr_table_get(r->subprocess_env, "HTTPS");
+
+ dir_uri = apr_pstrdup(r->pool, r->uri);
+ p = rindex(dir_uri, '/');
+
+ if (p == NULL) return "";
+
+ file = apr_pstrdup(r->pool, &p[1]);
+ p[1] = '\0';
+ /* dir_uri always gets both a leading and a trailing slash */
+
+ out = apr_pstrdup(r->pool, "<p><small>\n");
+
+ if (!isdirectory)
+ {
+ mtime_time = apr_time_sec(r->finfo.mtime);
+
+ localtime_r(&mtime_time, &mtime_tm);
+ strftime(modified, sizeof(modified),
+ "%a %e %B %Y", &mtime_tm);
+ temp = apr_psprintf(r->pool,"<hr>Last modified %s\n", modified);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+
+ if ((conf->adminuri != NULL) &&
+ (conf->adminuri[0] != '\0') &&
+ (conf->adminfile != NULL) &&
+ (conf->adminfile[0] != '\0') &&
+ (strncmp(file, GRST_HIST_PREFIX, sizeof(GRST_HIST_PREFIX)-1) != 0))
+ {
+ temp = apr_psprintf(r->pool,
+ ". <a href=\"%s?cmd=history&file=%s\">"
+ "View page history</a>\n",
+ conf->adminfile, file);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+ }
+ }
+
+ out = apr_pstrcat(r->pool, out, "<hr>", NULL);
+
+ if (r->connection->notes != NULL)
+ grst_cred_0 = (char *)
+ apr_table_get(r->connection->notes, "GRST_CRED_0");
+
+ if ((grst_cred_0 != NULL) &&
+ (strncmp(grst_cred_0, "X509USER ", sizeof("X509USER")) == 0))
+ {
+ p = index(grst_cred_0, ' ');
+ if (p != NULL)
+ {
+ p = index(++p, ' ');
+ if (p != NULL)
+ {
+ p = index(++p, ' ');
+ if (p != NULL)
+ {
+ p = index(++p, ' ');
+ if (p != NULL) dn = p;
+ }
+ }
+ }
+ }
+
+ if (dn != NULL)
+ {
+ temp = apr_psprintf(r->pool, "You are %s<br>\n", dn);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+
+ if (r->notes != NULL)
+ permstr = (char *) apr_table_get(r->notes, "GRST_PERM");
+
+ if ((permstr != NULL) &&
+ (conf->adminuri != NULL) &&
+ (conf->adminuri[0] != '\0') &&
+ (conf->adminfile != NULL) &&
+ (conf->adminfile[0] != '\0'))
+ {
+ sscanf(permstr, "%d", &perm);
+
+ if (!isdirectory &&
+ GRSTgaclPermHasWrite(perm) &&
+ (strncmp(file, GRST_HIST_PREFIX,
+ sizeof(GRST_HIST_PREFIX) - 1) != 0))
+ {
+ temp = apr_psprintf(r->pool,
+ "<a href=\"%s?cmd=edit&file=%s\">"
+ "Edit page</a> .\n", conf->adminfile, file);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+ }
+
+ if (GRSTgaclPermHasList(perm) || GRSTgaclPermHasWrite(perm))
+ {
+ temp = apr_psprintf(r->pool,
+ "<a href=\"%s%s?cmd=managedir\">Manage directory</a> .\n",
+ dir_uri, conf->adminfile);
+
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+ }
+ }
+ }
+
+ if ((https != NULL) && (strcasecmp(https, "on") == 0))
+ temp = apr_psprintf(r->pool,
+ "<a href=\"http://%s%s\">Switch to HTTP</a> \n",
+ r->server->server_hostname, r->unparsed_uri);
+ else temp = apr_psprintf(r->pool,
+ "<a href=\"https://%s%s\">Switch to HTTPS</a> \n",
+ r->server->server_hostname, r->unparsed_uri);
+
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+
+ if ((conf->helpuri != NULL) && (conf->helpuri[0] != '\0'))
+ {
+ temp = apr_psprintf(r->pool,
+ ". <a href=\"%s\">Website Help</a>\n", conf->helpuri);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+ }
+
+ if ((!isdirectory) &&
+ (conf->adminuri != NULL) &&
+ (conf->adminuri[0] != '\0') &&
+ (conf->adminfile != NULL) &&
+ (conf->adminfile[0] != '\0'))
+ {
+ temp = apr_psprintf(r->pool, ". <a href=\"%s?cmd=print&file=%s\">"
+ "Print View</a>\n", conf->adminfile, file);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+ }
+
+ if (conf->gridsitelink)
+ {
+ temp = apr_psprintf(r->pool,
+ ". Built with <a href=\"http://www.gridpp.ac.uk/gridsite/\">"
+ "GridSite</a> %s\n", VERSION);
+ out = apr_pstrcat(r->pool, out, temp, NULL);
+ }
+
+ out = apr_pstrcat(r->pool, out, "\n</small>\n", NULL);
+
+ return out;
+}
+
+int html_format(request_rec *r, mod_gridsite_cfg *conf)
+/*
+ try to do GridSite formatting of .html files (NOT .shtml etc)
+*/
+{
+ int i, fd, errstatus;
+ char *buf, *p, *file, *s, *head_formatted, *header_formatted,
+ *body_formatted, *admin_formatted, *footer_formatted;
+ size_t length;
+ struct stat statbuf;
+ apr_file_t *fp;
+
+ if (r->finfo.filetype == APR_NOFILE) return HTTP_NOT_FOUND;
+
+ if (apr_file_open(&fp, r->filename, APR_READ, 0, r->pool) != 0)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ file = rindex(r->uri, '/');
+ if (file != NULL) ++file; /* file points to name without path */
+
+ buf = apr_palloc(r->pool, (size_t)(r->finfo.size + 1));
+ length = r->finfo.size;
+ apr_file_read(fp, buf, &length);
+ buf[r->finfo.size] = '\0';
+ apr_file_close(fp);
+
+ /* **** try to find a header file in this or parent directories **** */
+
+ /* first make a buffer big enough to hold path names we want to try */
+ fd = -1;
+ s = malloc(strlen(r->filename) + strlen(conf->headfile) + 1);
+ strcpy(s, r->filename);
+
+ for (;;)
+ {
+ p = rindex(s, '/');
+ if (p == NULL) break; /* failed to find one */
+ p[1] = '\0';
+ strcat(p, conf->headfile);
+
+ fd = open(s, O_RDONLY);
+ if (fd != -1) break; /* found one */
+
+ *p = '\0';
+ }
+
+ free(s);
+
+ if (fd == -1) /* not found, so set up not to output one */
+ {
+ head_formatted = apr_pstrdup(r->pool, "");
+ header_formatted = apr_pstrdup(r->pool, "");
+ body_formatted = buf;
+ }
+ else /* found a header file, so set up head and body to surround it */
+ {
+ fstat(fd, &statbuf);
+ header_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, header_formatted, statbuf.st_size);
+ header_formatted[statbuf.st_size] = '\0';
+ close(fd);
+
+ p = strstr(buf, "<body");
+ if (p == NULL) p = strstr(buf, "<BODY");
+ if (p == NULL) p = strstr(buf, "<Body");
+
+ if (p == NULL)
+ {
+ head_formatted = apr_pstrdup(r->pool, "");
+ body_formatted = buf;
+ }
+ else
+ {
+ *p = '\0';
+ head_formatted = buf;
+ ++p;
+
+ while ((*p != '>') && (*p != '\0')) ++p;
+
+ if (*p == '\0')
+ {
+ body_formatted = p;
+ }
+ else
+ {
+ *p = '\0';
+ ++p;
+ body_formatted = p;
+ }
+ }
+ }
+
+ /* **** remove closing </body> tag from body **** */
+
+ p = strstr(body_formatted, "</body");
+ if (p == NULL) p = strstr(body_formatted, "</BODY");
+ if (p == NULL) p = strstr(body_formatted, "</Body");
+
+ if (p != NULL) *p = '\0';
+
+ /* **** set up dynamic part of footer to go at end of body **** */
+
+ admin_formatted = make_admin_footer(r, conf, FALSE);
+
+ /* **** try to find a footer file in this or parent directories **** */
+
+ /* first make a buffer big enough to hold path names we want to try */
+ fd = -1;
+ s = malloc(strlen(r->filename) + strlen(conf->footfile));
+ strcpy(s, r->filename);
+
+ for (;;)
+ {
+ p = rindex(s, '/');
+ if (p == NULL) break; /* failed to find one */
+
+ p[1] = '\0';
+ strcat(p, conf->footfile);
+
+ fd = open(s, O_RDONLY);
+ if (fd != -1) break; /* found one */
+
+ *p = '\0';
+ }
+
+ free(s);
+
+ if (fd == -1) /* failed to find a footer, so set up empty default */
+ {
+ footer_formatted = apr_pstrdup(r->pool, "");
+ }
+ else /* found a footer, so set up to use it */
+ {
+ fstat(fd, &statbuf);
+ footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, footer_formatted, statbuf.st_size);
+ footer_formatted[statbuf.st_size] = '\0';
+ close(fd);
+ }
+
+ /* **** can now calculate the Content-Length and output headers **** */
+
+ length = strlen(head_formatted) + strlen(header_formatted) +
+ strlen(body_formatted) + strlen(admin_formatted) +
+ strlen(footer_formatted);
+
+ ap_set_content_length(r, length);
+ ap_set_content_type(r, "text/html");
+
+ /* ** output the HTTP body (HTML Head+Body) ** */
+
+ ap_rputs(head_formatted, r);
+ ap_rputs(header_formatted, r);
+ ap_rputs(body_formatted, r);
+ ap_rputs(admin_formatted, r);
+ ap_rputs(footer_formatted, r);
+
+ return OK;
+}
+
+int html_dir_list(request_rec *r, mod_gridsite_cfg *conf)
+/*
+ output HTML directory listing, with level of formatting controlled
+ by GridSiteHtmlFormat/conf->format
+*/
+{
+ int i, fd, n;
+ char *buf, *p, *s, *head_formatted, *header_formatted,
+ *body_formatted, *admin_formatted, *footer_formatted, *temp,
+ modified[99], *d_namepath, *indexheaderpath, *indexheadertext;
+ size_t length;
+ struct stat statbuf;
+ struct tm mtime_tm;
+ struct dirent **namelist;
+
+ if (r->finfo.filetype == APR_NOFILE) return HTTP_NOT_FOUND;
+
+ head_formatted = apr_psprintf(r->pool,
+ "<head><title>Directory listing %s</title></head>\n", r->uri);
+
+ if (conf->format)
+ {
+ /* **** try to find a header file in this or parent directories **** */
+
+ /* first make a buffer big enough to hold path names we want to try */
+ fd = -1;
+ s = malloc(strlen(r->filename) + strlen(conf->headfile) + 1);
+ strcpy(s, r->filename);
+
+ for (;;)
+ {
+ p = rindex(s, '/');
+ if (p == NULL) break; /* failed to find one */
+ p[1] = '\0';
+ strcat(p, conf->headfile);
+
+ fd = open(s, O_RDONLY);
+ if (fd != -1) break; /* found one */
+
+ *p = '\0';
+ }
+
+ free(s);
+
+ if (fd == -1) /* not found, so set up to output sensible default */
+ {
+ header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+ }
+ else /* found a header file, so set up head and body to surround it */
+ {
+ fstat(fd, &statbuf);
+ header_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, header_formatted, statbuf.st_size);
+ header_formatted[statbuf.st_size] = '\0';
+ close(fd);
+ }
+ }
+ else header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+
+ body_formatted = apr_psprintf(r->pool,
+ "<h1>Directory listing %s</h1>\n", r->uri);
+
+ if (conf->indexheader != NULL)
+ {
+ indexheaderpath = apr_psprintf(r->pool, "%s/%s", r->filename,
+ conf->indexheader);
+ fd = open(indexheaderpath, O_RDONLY);
+ if (fd != -1)
+ {
+ fstat(fd, &statbuf);
+ indexheadertext = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, indexheadertext, statbuf.st_size);
+ indexheadertext[statbuf.st_size] = '\0';
+ close(fd);
+
+ body_formatted = apr_pstrcat(r->pool, body_formatted,
+ indexheadertext, NULL);
+ }
+ }
+
+ body_formatted = apr_pstrcat(r->pool, body_formatted, "<p><table>\n", NULL);
+
+ if (r->unparsed_uri[1] != '\0')
+ body_formatted = apr_pstrcat(r->pool, body_formatted,
+ "<tr><td colspan=3>[<a href=\"../\">Parent directory</a>]</td></tr>\n",
+ NULL);
+
+ n = scandir(r->filename, &namelist, 0, versionsort);
+ while (n--)
+ {
+ if ((namelist[n]->d_name[0] != '.') &&
+ ((conf->indexheader == NULL) ||
+ (strcmp(conf->indexheader, namelist[n]->d_name) != 0)))
+ {
+ d_namepath = apr_psprintf(r->pool, "%s/%s", r->filename,
+ namelist[n]->d_name);
+ stat(d_namepath, &statbuf);
+
+ localtime_r(&(statbuf.st_mtime), &mtime_tm);
+ strftime(modified, sizeof(modified),
+ "<td align=right>%R</td><td align=right>%e %b %y</td>",
+ &mtime_tm);
+
+ if (S_ISDIR(statbuf.st_mode))
+ temp = apr_psprintf(r->pool,
+ "<tr><td><a href=\"%s/\" "
+ "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, modified);
+ else temp = apr_psprintf(r->pool,
+ "<tr><td><a href=\"%s\" content-length=\"%ld\" "
+ "last-modified=\"%ld\">"
+ "%s</a></td>"
+ "<td align=right>%ld</td>%s</tr>\n",
+ namelist[n]->d_name, statbuf.st_size, statbuf.st_mtime,
+ namelist[n]->d_name,
+ statbuf.st_size, modified);
+
+ body_formatted = apr_pstrcat(r->pool,body_formatted,temp,NULL);
+ }
+
+ free(namelist[n]);
+ }
+
+ free(namelist);
+
+ body_formatted = apr_pstrcat(r->pool, body_formatted, "</table>\n", NULL);
+
+ if (conf->format)
+ {
+ /* **** set up dynamic part of footer to go at end of body **** */
+
+ admin_formatted = make_admin_footer(r, conf, TRUE);
+
+ /* **** try to find a footer file in this or parent directories **** */
+
+ /* first make a buffer big enough to hold path names we want to try */
+ fd = -1;
+ s = malloc(strlen(r->filename) + strlen(conf->footfile));
+ strcpy(s, r->filename);
+
+ for (;;)
+ {
+ p = rindex(s, '/');
+ if (p == NULL) break; /* failed to find one */
+
+ p[1] = '\0';
+ strcat(p, conf->footfile);
+
+ fd = open(s, O_RDONLY);
+ if (fd != -1) break; /* found one */
+
+ *p = '\0';
+ }
+
+ free(s);
+
+ if (fd == -1) /* failed to find a footer, so use standard default */
+ {
+ footer_formatted = apr_pstrdup(r->pool, "</body>");
+ }
+ else /* found a footer, so set up to use it */
+ {
+ fstat(fd, &statbuf);
+ footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, footer_formatted, statbuf.st_size);
+ footer_formatted[statbuf.st_size] = '\0';
+ close(fd);
+ }
+ }
+ else
+ {
+ admin_formatted = apr_pstrdup(r->pool, "");
+ footer_formatted = apr_pstrdup(r->pool, "</body>");
+ }
+
+ /* **** can now calculate the Content-Length and output headers **** */
+
+ length = strlen(head_formatted) + strlen(header_formatted) +
+ strlen(body_formatted) + strlen(admin_formatted) +
+ strlen(footer_formatted);
+
+ ap_set_content_length(r, length);
+ ap_set_content_type(r, "text/html");
+
+ /* ** output the HTTP body (HTML Head+Body) ** */
+
+ ap_rputs(head_formatted, r);
+ ap_rputs(header_formatted, r);
+ ap_rputs(body_formatted, r);
+ ap_rputs(admin_formatted, r);
+ ap_rputs(footer_formatted, r);
+
+ return OK;
+}
+
+int http_put_method(request_rec *r, mod_gridsite_cfg *conf)
+{
+ char buf[2048];
+ size_t length;
+ int retcode;
+ apr_file_t *fp;
+
+ /* *** check if directory creation: PUT /.../ *** */
+
+ if ((r->unparsed_uri != NULL) &&
+ (r->unparsed_uri[0] != '\0') &&
+ (r->unparsed_uri[strlen(r->unparsed_uri) - 1] == '/'))
+ {
+ if (apr_dir_make(r->filename, APR_UREAD | APR_UWRITE | APR_UEXECUTE,
+ r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR;
+
+ ap_set_content_length(r, 0);
+ ap_set_content_type(r, "text/html");
+ return OK;
+ }
+
+ /* *** otherwise assume trying to create a regular file *** */
+
+ 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
+
+ retcode = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
+ if (retcode == OK)
+ {
+ if (ap_should_client_block(r))
+ while ((length = ap_get_client_block(r, buf, sizeof(buf))) > 0)
+ if (apr_file_write(fp, buf, &length) != 0)
+ {
+ retcode = HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
+
+ ap_set_content_length(r, 0);
+ ap_set_content_type(r, "text/html");
+ }
+
+ if (apr_file_close(fp) != 0) return HTTP_INTERNAL_SERVER_ERROR;
+
+ return retcode;
+}
+
+int http_delete_method(request_rec *r, mod_gridsite_cfg *conf)
+{
+ if (remove(r->filename) != 0) return HTTP_FORBIDDEN;
+
+ ap_set_content_length(r, 0);
+ ap_set_content_type(r, "text/html");
+
+ return OK;
+}
+
+static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_cfg *conf)
+/*
+ handler switch for directories
+*/
+{
+ /* *** is this a write method? only possible if GridSiteAuth on *** */
+
+ if (conf->auth)
+ {
+ if ((r->method_number == M_PUT) &&
+ (conf->methods != NULL) &&
+ (strstr(conf->methods, " PUT " ) != NULL))
+ return http_put_method(r, conf);
+
+ if ((r->method_number == M_DELETE) &&
+ (conf->methods != NULL) &&
+ (strstr(conf->methods, " DELETE ") != NULL))
+ return http_delete_method(r, conf);
+ }
+
+ /* *** directory listing? *** */
+
+ if ((r->method_number == M_GET) && (conf->indexes))
+ return html_dir_list(r, conf); /* directory listing */
+
+ return DECLINED; /* *** nothing to see here, move along *** */
+}
+
+static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_cfg *conf)
+/*
+ one big handler switch for everything other than directories, since we
+ might be responding to MIME * / * for local PUT, MOVE, COPY and DELETE,
+ and GET inside ghost directories.
+*/
+{
+ /* *** is this a write method? only possible if GridSiteAuth on *** */
+
+ if (conf->auth)
+ {
+ if ((r->method_number == M_PUT) &&
+ (conf->methods != NULL) &&
+ (strstr(conf->methods, " PUT " ) != NULL))
+ return http_put_method(r, conf);
+
+ if ((r->method_number == M_DELETE) &&
+ (conf->methods != NULL) &&
+ (strstr(conf->methods, " DELETE ") != NULL))
+ return http_delete_method(r, conf);
+ }
+
+ /* *** check if a special ghost admin CGI *** */
+
+ if (conf->adminfile && conf->adminuri &&
+ (strlen(r->filename) > strlen(conf->adminfile) + 1) &&
+ (strcmp(&(r->filename[strlen(r->filename) - strlen(conf->adminfile)]),
+ conf->adminfile) == 0) &&
+ (r->filename[strlen(r->filename)-strlen(conf->adminfile)-1] == '/') &&
+ ((r->method_number == M_POST) ||
+ (r->method_number == M_GET)))
+ {
+ ap_internal_redirect(conf->adminuri, r);
+ return OK;
+ }
+
+ /* *** finally look for .html files that we should format *** */
+
+ if ((conf->format) && /* conf->format set by GridSiteHtmlFormat on */
+ (strlen(r->filename) > 5) &&
+ (strcmp(&(r->filename[strlen(r->filename)-5]), ".html") == 0) &&
+ (r->method_number == M_GET)) return html_format(r, conf);
+
+ return DECLINED; /* *** nothing to see here, move along *** */
+}
+
+static void recurse4dirlist(char *dirname, time_t *dirs_time,
+ char *fulluri, int fullurilen,
+ char *encfulluri, int enclen,
+ apr_pool_t *pool, char **body,
+ int recurse_level)
+/* try to find DN Lists in dir[] and its subdirs that match the fulluri[]
+ prefix. add blobs of HTML to body as they are found. */
+{
+ char *unencname, modified[99], *oneline, *d_namepath;
+ DIR *oneDIR;
+ struct dirent *onedirent;
+ struct tm mtime_tm;
+ size_t length;
+ struct stat statbuf;
+
+ if ((stat(dirname, &statbuf) != 0) ||
+ (!S_ISDIR(statbuf.st_mode)) ||
+ ((oneDIR = opendir(dirname)) == NULL)) return;
+
+ if (statbuf.st_mtime > *dirs_time) *dirs_time = statbuf.st_mtime;
+
+ while ((onedirent = readdir(oneDIR)) != NULL)
+ {
+ if (onedirent->d_name[0] == '.') continue;
+
+ d_namepath = apr_psprintf(pool, "%s/%s", dirname, onedirent->d_name);
+ if (stat(d_namepath, &statbuf) != 0) continue;
+
+ if (S_ISDIR(statbuf.st_mode) && (recurse_level < GRST_RECURS_LIMIT))
+ recurse4dirlist(d_namepath, dirs_time, fulluri,
+ fullurilen, encfulluri, enclen,
+ pool, body, recurse_level + 1);
+ else if ((strncmp(onedirent->d_name, encfulluri, enclen) == 0) &&
+ (onedirent->d_name[strlen(onedirent->d_name) - 1] != '~'))
+ {
+ unencname = GRSThttpUrlDecode(onedirent->d_name);
+
+ if (strncmp(unencname, fulluri, fullurilen) == 0)
+ {
+
+ if (statbuf.st_mtime > *dirs_time)
+ *dirs_time = statbuf.st_mtime;
+
+ localtime_r(&(statbuf.st_mtime), &mtime_tm);
+ strftime(modified, sizeof(modified),
+ "<td align=right>%R</td><td align=right>%e %b %y</td>",
+ &mtime_tm);
+
+ oneline = apr_psprintf(pool,
+ "<tr><td><a href=\"%s\" "
+ "content-length=\"%ld\" "
+ "last-modified=\"%ld\">"
+ "%s</a></td>"
+ "<td align=right>%ld</td>%s</tr>\n",
+ &unencname[fullurilen], statbuf.st_size,
+ statbuf.st_mtime, unencname,
+ statbuf.st_size, modified);
+
+ *body = apr_pstrcat(pool, *body, oneline, NULL);
+ }
+
+ free(unencname); /* libgridsite doesnt use pools */
+ }
+ }
+
+ closedir(oneDIR);
+}
+
+static int mod_gridsite_dnlistsuri_dir_handler(request_rec *r,
+ mod_gridsite_cfg *conf)
+/*
+ virtual DN-list file lister: make all DN lists on the dn-lists
+ path of this server appear to be in the dn-lists directory itself
+ (ie where they appear in the DN lists path doesnt matter, as long
+ as their name matches)
+*/
+{
+ int enclen, fullurilen, fd;
+ char *fulluri, *encfulluri, *dn_list_ptr, *dirname, *unencname,
+ *body, *oneline, *p, *s,
+ *head_formatted, *header_formatted, *footer_formatted,
+ *permstr = NULL;
+ struct stat statbuf;
+ size_t length;
+ time_t dirs_time = 0;
+ GRSTgaclPerm perm = GRST_PERM_NONE;
+
+ if (r->notes != NULL)
+ permstr = (char *) apr_table_get(r->notes, "GRST_PERM");
+
+ if (permstr != NULL) sscanf(permstr, "%d", &perm);
+
+ fulluri = apr_psprintf(r->pool, "https://%s%s",
+ ap_get_server_name(r), conf->dnlistsuri);
+ fullurilen = strlen(fulluri);
+
+ encfulluri = GRSThttpUrlEncode(fulluri);
+ enclen = strlen(encfulluri);
+
+ if (conf->dnlists != NULL) p = conf->dnlists;
+ else p = getenv("GRST_DN_LISTS");
+
+ if (p == NULL) p = GRST_DN_LISTS;
+ dn_list_ptr = apr_pstrdup(r->pool, p);
+
+ head_formatted = apr_psprintf(r->pool,
+ "<head><title>Directory listing %s</title></head>\n", r->uri);
+
+ if (conf->format)
+ {
+ /* **** try to find a header file in this or parent directories **** */
+
+ /* first make a buffer big enough to hold path names we want to try */
+ fd = -1;
+ s = malloc(strlen(r->filename) + strlen(conf->headfile) + 1);
+ strcpy(s, r->filename);
+
+ for (;;)
+ {
+ p = rindex(s, '/');
+ if (p == NULL) break; /* failed to find one */
+ p[1] = '\0';
+ strcat(p, conf->headfile);
+
+ fd = open(s, O_RDONLY);
+ if (fd != -1) break; /* found one */
+
+ *p = '\0';
+ }
+
+ free(s);
+
+ if (fd == -1) /* not found, so set up to output sensible default */
+ {
+ header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+ }
+ else /* found a header file, so set up head and body to surround it */
+ {
+ fstat(fd, &statbuf);
+ header_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, header_formatted, statbuf.st_size);
+ header_formatted[statbuf.st_size] = '\0';
+ close(fd);
+ }
+ }
+ else header_formatted = apr_pstrdup(r->pool, "<body bgcolor=white>");
+
+ body = apr_psprintf(r->pool,
+ "<h1>Directory listing %s</h1>\n<table>", r->uri);
+
+ if ((r->uri)[1] != '\0')
+ body = apr_pstrcat(r->pool, body,
+ "<tr><td>[<a href=\"../\">Parent directory</a>]</td></tr>\n",
+ NULL);
+
+ while ((dirname = strsep(&dn_list_ptr, ":")) != NULL)
+ recurse4dirlist(dirname, &dirs_time, fulluri, fullurilen,
+ encfulluri, enclen, r->pool, &body, 0);
+
+ if ((stat(r->filename, &statbuf) == 0) &&
+ S_ISDIR(statbuf.st_mode) &&
+ GRSTgaclPermHasWrite(perm))
+ {
+ oneline = apr_psprintf(r->pool,
+ "<form action=\"%s%s\" method=post>\n"
+ "<input type=hidden name=cmd value=managedir>"
+ "<tr><td colspan=4 align=center><small><input type=submit "
+ "value=\"Manage directory\"></small></td></tr></form>\n",
+ r->uri, conf->adminfile);
+
+ body = apr_pstrcat(r->pool, body, oneline, NULL);
+ }
+
+ body = apr_pstrcat(r->pool, body, "</table>\n", NULL);
+
+ free(encfulluri); /* libgridsite doesnt use pools */
+
+ if (conf->format)
+ {
+ /* **** try to find a footer file in this or parent directories **** */
+
+ /* first make a buffer big enough to hold path names we want to try */
+ fd = -1;
+ s = malloc(strlen(r->filename) + strlen(conf->footfile));
+ strcpy(s, r->filename);
+
+ for (;;)
+ {
+ p = rindex(s, '/');
+ if (p == NULL) break; /* failed to find one */
+
+ p[1] = '\0';
+ strcat(p, conf->footfile);
+
+ fd = open(s, O_RDONLY);
+ if (fd != -1) break; /* found one */
+
+ *p = '\0';
+ }
+
+ free(s);
+
+ if (fd == -1) /* failed to find a footer, so use standard default */
+ {
+ footer_formatted = apr_pstrdup(r->pool, "</body>");
+ }
+ else /* found a footer, so set up to use it */
+ {
+ fstat(fd, &statbuf);
+ footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, footer_formatted, statbuf.st_size);
+ footer_formatted[statbuf.st_size] = '\0';
+ close(fd);
+ }
+ }
+ else footer_formatted = apr_pstrdup(r->pool, "</body>");
+
+ /* **** can now calculate the Content-Length and output headers **** */
+
+ length = strlen(head_formatted) + strlen(header_formatted) +
+ strlen(body) + strlen(footer_formatted);
+
+ ap_set_content_length(r, length);
+ r->mtime = apr_time_from_sec(dirs_time);
+ ap_set_last_modified(r);
+ ap_set_content_type(r, "text/html");
+
+ /* ** output the HTTP body (HTML Head+Body) ** */
+ ap_rputs(head_formatted, r);
+ ap_rputs(header_formatted, r);
+ ap_rputs(body, r);
+ ap_rputs(footer_formatted, r);
+
+ return OK;
+}
+
+static char *recurse4file(char *dir, char *file, apr_pool_t *pool,
+ int recurse_level)
+/* try to find file[] in dir[]. try subdirs if not found.
+ return full path to first found version or NULL on failure */
+{
+ char *fullfilename, *fulldirname;
+ struct stat statbuf;
+ DIR *dirDIR;
+ struct dirent *file_ent;
+
+ /* try to find in current directory */
+
+ fullfilename = apr_psprintf(pool, "%s/%s", dir, file);
+
+ if (stat(fullfilename, &statbuf) == 0) return fullfilename;
+
+ /* maybe search in subdirectories */
+
+ if (recurse_level >= GRST_RECURS_LIMIT) return NULL;
+
+ dirDIR = opendir(dir);
+
+ if (dirDIR == NULL) return NULL;
+
+ while ((file_ent = readdir(dirDIR)) != NULL)
+ {
+ if (file_ent->d_name[0] == '.') continue;
+
+ fulldirname = apr_psprintf(pool, "%s/%s", dir, file_ent->d_name);
+ if ((stat(fulldirname, &statbuf) == 0) &&
+ S_ISDIR(statbuf.st_mode) &&
+ ((fullfilename = recurse4file(fulldirname, file,
+ pool, recurse_level + 1)) != NULL))
+ {
+ closedir(dirDIR);
+ return fullfilename;
+ }
+ }
+
+ closedir(dirDIR);
+
+ return NULL;
+}
+
+static int mod_gridsite_dnlistsuri_handler(request_rec *r,
+ mod_gridsite_cfg *conf)
+/*
+ virtual DN-list file generator
+*/
+{
+ int fd;
+ char *fulluri, *encfulluri, *dn_list_ptr, *filename, *dirname, *p,
+ *buf;
+ struct stat statbuf;
+
+ /* *** check if a special ghost admin CGI *** */
+
+ if (conf->adminfile && conf->adminuri &&
+ (strlen(r->filename) > strlen(conf->adminfile) + 1) &&
+ (strcmp(&(r->filename[strlen(r->filename) - strlen(conf->adminfile)]),
+ conf->adminfile) == 0) &&
+ (r->filename[strlen(r->filename)-strlen(conf->adminfile)-1] == '/') &&
+ ((r->method_number == M_POST) ||
+ (r->method_number == M_GET)))
+ {
+ ap_internal_redirect(conf->adminuri, r);
+ return OK;
+ }
+
+ fulluri = apr_psprintf(r->pool, "https://%s%s",
+ ap_get_server_name(r), r->uri);
+
+ encfulluri = GRSThttpUrlEncode(fulluri);
+
+ if (conf->dnlists != NULL) p = conf->dnlists;
+ else p = getenv("GRST_DN_LISTS");
+
+ if (p == NULL) p = GRST_DN_LISTS;
+ dn_list_ptr = apr_pstrdup(r->pool, p);
+
+ while ((dirname = strsep(&dn_list_ptr, ":")) != NULL)
+ {
+ filename = recurse4file(dirname, encfulluri, r->pool, 0);
+
+ if (filename == NULL) continue;
+
+ fd = open(filename, O_RDONLY);
+
+ if (fd == -1) continue;
+
+ fstat(fd, &statbuf);
+ ap_set_content_length(r, (apr_off_t) statbuf.st_size);
+ r->mtime = apr_time_from_sec(statbuf.st_mtime);
+ ap_set_content_type(r, "text/plain");
+ ap_set_last_modified(r);
+
+ buf = apr_palloc(r->pool, statbuf.st_size + 1);
+ read(fd, buf, statbuf.st_size);
+ buf[statbuf.st_size] = '\0';
+
+ ap_rputs(buf, r);
+
+ close(fd);
+
+ return OK;
+ }
+
+ return HTTP_NOT_FOUND;
+}
+
+static void *create_gridsite_dir_config(apr_pool_t *p, char *path)
+{
+ mod_gridsite_cfg *conf = apr_palloc(p, sizeof(*conf));
+
+ if (path == NULL) /* set up server defaults */
+ {
+ conf->auth = 0; /* GridSiteAuth on/off */
+ conf->envs = 1; /* GridSiteEnvs on/off */
+ conf->format = 0; /* GridSiteHtmlFormat on/off */
+ conf->indexes = 0; /* GridSiteIndexes on/off */
+ conf->indexheader = NULL; /* GridSiteIndexHeader File-value */
+ conf->gridsitelink = 1; /* GridSiteLink on/off */
+ conf->adminfile = apr_pstrdup(p, GRST_ADMIN_FILE);
+ /* GridSiteAdminFile File-value */
+ conf->adminuri = NULL; /* GridSiteAdminURI URI-value */
+ conf->helpuri = NULL; /* GridSiteHelpURI URI-value */
+ conf->dnlists = NULL; /* GridSiteDNlists Search-path */
+ conf->dnlistsuri = NULL; /* GridSiteDNlistsURI URI-value */
+ conf->adminlist = NULL; /* GridSiteAdminList URI-value */
+ conf->gsiproxylimit = 1; /* GridSiteGSIProxyLimit number */
+ conf->unzip = NULL; /* GridSiteUnzip file-path */
+
+ conf->methods = apr_pstrdup(p, " GET ");
+ /* GridSiteMethods methods */
+
+ conf->editable = apr_pstrdup(p, " txt shtml html htm css js php jsp ");
+ /* GridSiteEditable types */
+
+ conf->headfile = apr_pstrdup(p, GRST_HEADFILE);
+ conf->footfile = apr_pstrdup(p, GRST_FOOTFILE);
+ /* GridSiteHeadFile and GridSiteFootFile file name */
+ }
+ else
+ {
+ conf->auth = UNSET; /* GridSiteAuth on/off */
+ conf->envs = UNSET; /* GridSiteEnvs on/off */
+ conf->format = UNSET; /* GridSiteHtmlFormat on/off */
+ conf->indexes = UNSET; /* GridSiteIndexes on/off */
+ conf->indexheader = NULL; /* GridSiteIndexHeader File-value */
+ conf->gridsitelink = UNSET; /* GridSiteLink on/off */
+ conf->adminfile = NULL; /* GridSiteAdminFile File-value */
+ conf->adminuri = NULL; /* GridSiteAdminURI URI-value */
+ conf->helpuri = NULL; /* GridSiteHelpURI URI-value */
+ conf->dnlists = NULL; /* GridSiteDNlists Search-path */
+ conf->dnlistsuri = NULL; /* GridSiteDNlistsURI URI-value */
+ conf->adminlist = NULL; /* GridSiteAdminList URI-value */
+ conf->gsiproxylimit = UNSET; /* GridSiteGSIProxyLimit number */
+ conf->unzip = NULL; /* GridSiteUnzip file-path */
+ conf->methods = NULL; /* GridSiteMethods methods */
+ conf->editable = NULL; /* GridSiteEditable types */
+ conf->headfile = NULL; /* GridSiteHeadFile file name */
+ conf->footfile = NULL; /* GridSiteFootFile file name */
+ }
+
+ return conf;
+}
+
+static void *merge_gridsite_dir_config(apr_pool_t *p, void *vserver,
+ void *vdirect)
+/* merge directory with server-wide directory configs */
+{
+ mod_gridsite_cfg *conf, *server, *direct;
+
+ server = (mod_gridsite_cfg *) vserver;
+ direct = (mod_gridsite_cfg *) vdirect;
+ conf = apr_palloc(p, sizeof(*conf));
+
+ if (direct->auth != UNSET) conf->auth = direct->auth;
+ else conf->auth = server->auth;
+
+ if (direct->envs != UNSET) conf->envs = direct->envs;
+ else conf->envs = server->envs;
+
+ if (direct->format != UNSET) conf->format = direct->format;
+ else conf->format = server->format;
+
+ if (direct->indexes != UNSET) conf->indexes = direct->indexes;
+ else conf->indexes = server->indexes;
+
+ if (direct->gridsitelink != UNSET) conf->gridsitelink=direct->gridsitelink;
+ else conf->gridsitelink=server->gridsitelink;
+
+ if (direct->indexheader != NULL) conf->indexheader = direct->indexheader;
+ else conf->indexheader = server->indexheader;
+
+ if (direct->adminfile != NULL) conf->adminfile = direct->adminfile;
+ else conf->adminfile = server->adminfile;
+
+ if (direct->adminuri != NULL) conf->adminuri = direct->adminuri;
+ else conf->adminuri = server->adminuri;
+
+ if (direct->helpuri != NULL) conf->helpuri = direct->helpuri;
+ else conf->helpuri = server->helpuri;
+
+ if (direct->dnlists != NULL) conf->dnlists = direct->dnlists;
+ else conf->dnlists = server->dnlists;
+
+ if (direct->dnlistsuri != NULL) conf->dnlistsuri = direct->dnlistsuri;
+ else conf->dnlistsuri = server->dnlistsuri;
+
+ if (direct->adminlist != NULL) conf->adminlist = direct->adminlist;
+ else conf->adminlist = server->adminlist;
+
+ if (direct->gsiproxylimit != UNSET)
+ conf->gsiproxylimit = direct->gsiproxylimit;
+ else conf->gsiproxylimit = server->gsiproxylimit;
+
+ if (direct->unzip != NULL) conf->unzip = direct->unzip;
+ else conf->unzip = server->unzip;
+
+ if (direct->methods != NULL) conf->methods = direct->methods;
+ else conf->methods = server->methods;
+
+ if (direct->editable != NULL) conf->editable = direct->editable;
+ else conf->editable = server->editable;
+
+ if (direct->headfile != NULL) conf->headfile = direct->headfile;
+ else conf->headfile = server->headfile;
+
+ if (direct->footfile != NULL) conf->footfile = direct->footfile;
+ else conf->footfile = server->footfile;
+
+ return conf;
+}
+
+static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg,
+ const char *parm)
+{
+ int n;
+ char *p;
+
+ if (strcasecmp(a->cmd->name, "GridSiteAdminFile") == 0)
+ {
+ if (index(parm, '/') != NULL)
+ return "/ not permitted in GridSiteAdminFile";
+
+ ((mod_gridsite_cfg *) cfg)->adminfile =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteAdminURI") == 0)
+ {
+ if (*parm != '/') return "GridSiteAdminURI must begin with /";
+
+ ((mod_gridsite_cfg *) cfg)->adminuri =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteHelpURI") == 0)
+ {
+ if (*parm != '/') return "GridSiteHelpURI must begin with /";
+
+ ((mod_gridsite_cfg *) cfg)->helpuri =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteDNlists") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->dnlists =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteDNlistsURI") == 0)
+ {
+ if (*parm != '/') return "GridSiteDNlistsURI must begin with /";
+
+ if ((*parm != '\0') && (parm[strlen(parm) - 1] == '/'))
+ ((mod_gridsite_cfg *) cfg)->dnlistsuri =
+ apr_pstrdup(a->pool, parm);
+ else
+ ((mod_gridsite_cfg *) cfg)->dnlistsuri =
+ apr_pstrcat(a->pool, parm, "/", NULL);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteAdminList") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->adminlist =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteGSIProxyLimit") == 0)
+ {
+ n = -1;
+
+ if ((sscanf(parm, "%d", &n) == 1) && (n >= 0))
+ ((mod_gridsite_cfg *) cfg)->gsiproxylimit = n;
+ else return "GridSiteGSIProxyLimit must be a number >= 0";
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteUnzip") == 0)
+ {
+ if (*parm != '/') return "GridSiteUnzip must begin with /";
+
+ ((mod_gridsite_cfg *) cfg)->unzip =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteMethods") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->methods =
+ apr_psprintf(a->pool, " %s ", parm);
+
+ for (p = ((mod_gridsite_cfg *) cfg)->methods;
+ *p != '\0';
+ ++p) if (*p == '\t') *p = ' ';
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteEditable") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->editable =
+ apr_psprintf(a->pool, " %s ", parm);
+
+ for (p = ((mod_gridsite_cfg *) cfg)->editable;
+ *p != '\0';
+ ++p) if (*p == '\t') *p = ' ';
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteHeadFile") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->headfile =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteFootFile") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->footfile =
+ apr_pstrdup(a->pool, parm);
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteIndexHeader") == 0)
+ {
+ if (index(parm, '/') != NULL)
+ return "/ not permitted in GridSiteIndexHeader";
+
+ ((mod_gridsite_cfg *) cfg)->indexheader =
+ apr_pstrdup(a->pool, parm);
+ }
+
+ return NULL;
+}
+
+static const char *mod_gridsite_flag_cmds(cmd_parms *a, void *cfg,
+ int flag)
+{
+ if (strcasecmp(a->cmd->name, "GridSiteAuth") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->auth = flag;
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteEnvs") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->envs = flag;
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteHtmlFormat") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->format = flag;
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteIndexes") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->indexes = flag;
+ }
+ else if (strcasecmp(a->cmd->name, "GridSiteLink") == 0)
+ {
+ ((mod_gridsite_cfg *) cfg)->gridsitelink = flag;
+ }
+
+ return NULL;
+}
+
+static const command_rec mod_gridsite_cmds[] =
+{
+ AP_INIT_FLAG("GridSiteAuth", mod_gridsite_flag_cmds,
+ NULL, OR_FILEINFO, "on or off"),
+ AP_INIT_FLAG("GridSiteEnvs", mod_gridsite_flag_cmds,
+ NULL, OR_FILEINFO, "on or off"),
+ AP_INIT_FLAG("GridSiteHtmlFormat", mod_gridsite_flag_cmds,
+ NULL, OR_FILEINFO, "on or off"),
+ AP_INIT_FLAG("GridSiteIndexes", mod_gridsite_flag_cmds,
+ NULL, OR_FILEINFO, "on or off"),
+ AP_INIT_FLAG("GridSiteLink", mod_gridsite_flag_cmds,
+ NULL, OR_FILEINFO, "on or off"),
+
+ AP_INIT_TAKE1("GridSiteAdminFile", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "Ghost per-directory admin CGI"),
+ AP_INIT_TAKE1("GridSiteAdminURI", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "URI of real gridsite-admin.cgi"),
+ AP_INIT_TAKE1("GridSiteHelpURI", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "URI of Website Help pages"),
+ AP_INIT_TAKE1("GridSiteDNlists", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "DN Lists directories search path"),
+ AP_INIT_TAKE1("GridSiteDNlistsURI", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "URI of published DN lists"),
+ AP_INIT_TAKE1("GridSiteAdminList", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "URI of admin DN List"),
+ AP_INIT_TAKE1("GridSiteGSIProxyLimit", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "Max level of GSI proxy validity"),
+ AP_INIT_TAKE1("GridSiteUnzip", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "Absolute path to unzip command"),
+
+ AP_INIT_RAW_ARGS("GridSiteMethods", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "permitted HTTP methods"),
+ AP_INIT_RAW_ARGS("GridSiteEditable", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "editable file extensions"),
+ AP_INIT_TAKE1("GridSiteHeadFile", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "filename of HTML header"),
+ AP_INIT_TAKE1("GridSiteFootFile", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "filename of HTML footer"),
+ AP_INIT_TAKE1("GridSiteIndexHeader", mod_gridsite_take1_cmds,
+ NULL, OR_FILEINFO, "filename of directory header"),
+
+ {NULL}
+};
+
+static int mod_gridsite_first_fixups(request_rec *r)
+{
+ mod_gridsite_cfg *conf;
+
+ if (r->finfo.filetype != APR_DIR) return DECLINED;
+
+ conf = (mod_gridsite_cfg *)
+ ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+ if ((conf != NULL) &&
+ (conf->dnlistsuri != NULL) &&
+ (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0) &&
+ (strcmp(r->uri, conf->dnlistsuri) != 0))
+ {
+ r->finfo.filetype = APR_REG;
+ }
+
+ return DECLINED;
+}
+
+static int mod_gridsite_perm_handler(request_rec *r)
+/*
+ Do authentication/authorization here rather than in the normal module
+ auth functions since the results of mod_ssl are available.
+
+ We also publish environment variables here if requested by GridSiteEnv.
+*/
+{
+ int retcode = DECLINED, i, n;
+ char *dn, *p, envname[14], *grst_cred_0 = NULL, *dir_path,
+ *remotehost, s[99], *grst_cred_i, *file;
+ const char *content_type;
+ time_t now, notbefore, notafter;
+ apr_table_t *env;
+ GRSTgaclCred *cred = NULL, *cred_0 = NULL;
+ GRSTgaclUser *user = NULL;
+ GRSTgaclPerm perm = GRST_PERM_NONE;
+ GRSTgaclAcl *acl = NULL;
+ mod_gridsite_cfg *cfg;
+
+ cfg = (mod_gridsite_cfg *)
+ ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+ if (cfg == NULL) return DECLINED;
+
+ if ((cfg->auth == 0) &&
+ (cfg->envs == 0))
+ return DECLINED; /* if not turned on, look invisible */
+
+ env = r->subprocess_env;
+
+ if (r->connection->notes != NULL)
+ grst_cred_0 = (char *) apr_table_get(r->connection->notes, "GRST_CRED_0");
+
+ if (grst_cred_0 != NULL) /* do we have per-connection cred variable(s)? */
+ {
+ if (((mod_gridsite_cfg *) cfg)->envs)
+ apr_table_setn(env, "GRST_CRED_0", grst_cred_0);
+
+ cred_0 = GRSTx509CompactToCred(grst_cred_0);
+ if ((cred_0 != NULL) &&
+ (GRSTgaclCredGetDelegation(cred_0)
+ <= ((mod_gridsite_cfg *) cfg)->gsiproxylimit))
+ {
+ user = GRSTgaclUserNew(cred_0);
+
+ /* check for VOMS GRST_CRED_i too */
+
+ for (i=1; ; ++i)
+ {
+ snprintf(envname, sizeof(envname), "GRST_CRED_%d", i);
+ if (grst_cred_i = (char *)
+ apr_table_get(r->connection->notes,envname))
+ {
+ if (((mod_gridsite_cfg *) cfg)->envs)
+ apr_table_setn(env,
+ apr_pstrdup(r->pool, envname),
+ grst_cred_i);
+
+ if (cred = GRSTx509CompactToCred(grst_cred_i))
+ GRSTgaclUserAddCred(user, cred);
+ }
+ else break; /* GRST_CRED_i are numbered consecutively */
+ }
+ }
+ }
+
+ if ((user != NULL) && ((mod_gridsite_cfg *) cfg)->dnlists)
+ GRSTgaclUserSetDNlists(user, ((mod_gridsite_cfg *) cfg)->dnlists);
+
+ /* this checks for NULL arguments itself */
+ if (GRSTgaclDNlistHasUser(((mod_gridsite_cfg *) cfg)->adminlist, user))
+ perm = GRST_PERM_ALL;
+ else
+ {
+ remotehost = (char *) ap_get_remote_host(r->connection,
+ r->per_dir_config, REMOTE_DOUBLE_REV, NULL);
+ if ((remotehost != NULL) && (*remotehost != '\0'))
+ {
+ cred = GRSTgaclCredNew("dns");
+ GRSTgaclCredAddValue(cred, "hostname", remotehost);
+
+ if (user == NULL) user = GRSTgaclUserNew(cred);
+ else GRSTgaclUserAddCred(user, cred);
+ }
+
+ acl = GRSTgaclAclLoadforFile(r->filename);
+ if (acl != NULL) perm = GRSTgaclAclTestUser(acl, user);
+ }
+
+ apr_table_setn(r->notes, "GRST_PERM", apr_psprintf(r->pool, "%d", perm));
+
+ if (((mod_gridsite_cfg *) cfg)->envs)
+ {
+ apr_table_setn(env, "GRST_PERM", apr_psprintf(r->pool, "%d", perm));
+
+ if (((dir_path = apr_pstrdup(r->pool, r->filename)) != NULL) &&
+ ((p = rindex(dir_path, '/')) != NULL))
+ {
+ *p = '\0';
+ apr_table_setn(env, "GRST_DIR_PATH", dir_path);
+ }
+
+ if (((mod_gridsite_cfg *) cfg)->helpuri != NULL)
+ apr_table_setn(env, "GRST_HELP_URI",
+ ((mod_gridsite_cfg *) cfg)->helpuri);
+
+ if (((mod_gridsite_cfg *) cfg)->adminfile != NULL)
+ apr_table_setn(env, "GRST_ADMIN_FILE",
+ ((mod_gridsite_cfg *) cfg)->adminfile);
+
+ if (((mod_gridsite_cfg *) cfg)->editable != NULL)
+ apr_table_setn(env, "GRST_EDITABLE",
+ ((mod_gridsite_cfg *) cfg)->editable);
+
+ if (((mod_gridsite_cfg *) cfg)->headfile != NULL)
+ apr_table_setn(env, "GRST_HEAD_FILE",
+ ((mod_gridsite_cfg *) cfg)->headfile);
+
+ if (((mod_gridsite_cfg *) cfg)->footfile != NULL)
+ apr_table_setn(env, "GRST_FOOT_FILE",
+ ((mod_gridsite_cfg *) cfg)->footfile);
+
+ if (((mod_gridsite_cfg *) cfg)->dnlists != NULL)
+ apr_table_setn(env, "GRST_DN_LISTS",
+ ((mod_gridsite_cfg *) cfg)->dnlists);
+
+ if (((mod_gridsite_cfg *) cfg)->dnlistsuri != NULL)
+ apr_table_setn(env, "GRST_DN_LISTS_URI",
+ ((mod_gridsite_cfg *) cfg)->dnlistsuri);
+
+ if (((mod_gridsite_cfg *) cfg)->adminlist != NULL)
+ apr_table_setn(env, "GRST_ADMIN_LIST",
+ ((mod_gridsite_cfg *) cfg)->adminlist);
+
+ apr_table_setn(env, "GRST_GSIPROXY_LIMIT",
+ apr_psprintf(r->pool, "%d",
+ ((mod_gridsite_cfg *)cfg)->gsiproxylimit));
+
+ if (((mod_gridsite_cfg *) cfg)->unzip != NULL)
+ apr_table_setn(env, "GRST_UNZIP",
+ ((mod_gridsite_cfg *) cfg)->unzip);
+
+ if (!(((mod_gridsite_cfg *) cfg)->gridsitelink))
+ apr_table_setn(env, "GRST_NO_LINK", "1");
+ }
+
+ if (((mod_gridsite_cfg *) cfg)->auth)
+ {
+ /* *** Check HTTP method to decide which perm bits to check *** */
+
+ if (r->filename != NULL)
+ {
+ file = rindex(r->filename, '/');
+ if (file != NULL) ++file;
+ else file = r->filename;
+ }
+ else file = NULL;
+
+ content_type = r->content_type;
+ if ((content_type != NULL) &&
+ (strcmp(content_type, DIR_MAGIC_TYPE) == 0) &&
+ (((mod_gridsite_cfg *) cfg)->dnlistsuri != NULL) &&
+ (strncmp(r->uri,
+ ((mod_gridsite_cfg *) cfg)->dnlistsuri,
+ strlen(((mod_gridsite_cfg *) cfg)->dnlistsuri)) == 0) &&
+ (strlen(r->uri) > strlen(((mod_gridsite_cfg *) cfg)->dnlistsuri)))
+ content_type = "text/html";
+
+ if ( GRSTgaclPermHasNone(perm) ||
+
+ /* first two M_GET conditions make the subtle distinction
+ between .../ that maps to .../index.html (governed by
+ Read perm) or to dir list (governed by List perm);
+ third M_GET condition deals with typeless CGI requests */
+
+ ((r->method_number == M_GET) &&
+ !GRSTgaclPermHasRead(perm) &&
+ (content_type != NULL) &&
+ (strcmp(content_type, DIR_MAGIC_TYPE) != 0)) ||
+
+ ((r->method_number == M_GET) &&
+ !GRSTgaclPermHasList(perm) &&
+ (content_type != NULL) &&
+ (strcmp(content_type, DIR_MAGIC_TYPE) == 0)) ||
+
+ ((r->method_number == M_GET) &&
+ !GRSTgaclPermHasRead(perm) &&
+ (content_type == NULL)) ||
+
+ ((r->method_number == M_POST) && !GRSTgaclPermHasRead(perm) ) ||
+
+ (((r->method_number == M_PUT) || (r->method_number == M_DELETE)) &&
+ !GRSTgaclPermHasWrite(perm) &&
+ ((file == NULL) || (strcmp(file, GRST_ACL_FILE) != 0)) ) ||
+
+ (((r->method_number == M_PUT) || (r->method_number == M_DELETE)) &&
+ !GRSTgaclPermHasAdmin(perm) &&
+ (file != NULL) &&
+ (strcmp(file, GRST_ACL_FILE) == 0) ) ) retcode = HTTP_FORBIDDEN;
+ }
+
+ return retcode;
+}
+
+int GRST_X509_check_issued_wrapper(X509_STORE_CTX *ctx,X509 *x,X509 *issuer)
+/* We change the default callback to use our wrapper and discard errors
+ due to GSI proxy chains (ie where users certs act as CAs) */
+{
+ int ret;
+ ret = X509_check_issued(issuer, x);
+ if (ret == X509_V_OK)
+ return 1;
+
+ /* Non self-signed certs without signing are ok if they passed
+ the other checks inside X509_check_issued. Is this enough? */
+ if ((ret == X509_V_ERR_KEYUSAGE_NO_CERTSIGN) &&
+ (X509_NAME_cmp(X509_get_subject_name(issuer),
+ X509_get_subject_name(x)) != 0)) return 1;
+
+ /* If we haven't asked for issuer errors don't set ctx */
+ if (!(ctx->flags & X509_V_FLAG_CB_ISSUER_CHECK)) return 0;
+
+ ctx->error = ret;
+ ctx->current_cert = x;
+ ctx->current_issuer = issuer;
+ return ctx->verify_cb(0, ctx);
+}
+
+/* Later OpenSSL versions add a second pointer ... */
+int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx, void *p)
+
+/* Earlier ones have a single argument ... */
+// int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx)
+
+/* Before 0.9.7 we cannot change the check_issued callback directly in
+ the X509_STORE, so we must insert it in another callback that gets
+ called early enough */
+{
+ ctx->check_issued = GRST_X509_check_issued_wrapper;
+
+ return X509_verify_cert(ctx);
+}
+
+int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx)
+{
+ SSL *ssl = (SSL *) X509_STORE_CTX_get_app_data(ctx);
+ conn_rec *conn = (conn_rec *) SSL_get_app_data(ssl);
+ server_rec *s = conn->base_server;
+ SSLConnRec *sslconn =
+ (SSLConnRec *) ap_get_module_config(conn->conn_config, &ssl_module);
+ int errnum = X509_STORE_CTX_get_error(ctx);
+ int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+ int returned_ok;
+ int first_non_ca;
+
+ /*
+ * GSI Proxy user-cert-as-CA handling:
+ * we skip Invalid CA errors at this stage, since we will check this
+ * again at errdepth=0 for the full chain using GRSTx509CheckChain
+ */
+ if (errnum == X509_V_ERR_INVALID_CA)
+ {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Skip Invalid CA error in case a GSI Proxy");
+
+ sslconn->verify_error = NULL;
+ ok = TRUE;
+ errnum = X509_V_OK;
+ X509_STORE_CTX_set_error(ctx, errnum);
+ }
+
+ /*
+ * New style GSI Proxy handling, with critical ProxyCertInfo
+ * extension: we use GRSTx509KnownCriticalExts() to check this
+ */
+#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34
+#endif
+ if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
+ {
+ if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx))
+ == GRST_RET_OK)
+ {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "GRSTx509KnownCriticalExts() accepts previously "
+ "Unhandled Critical Extension (GSI Proxy?)");
+
+ sslconn->verify_error = NULL;
+ ok = TRUE;
+ errnum = X509_V_OK;
+ X509_STORE_CTX_set_error(ctx, errnum);
+ }
+ }
+
+ returned_ok = ssl_callback_SSLVerify(ok, ctx);
+
+ /* in case ssl_callback_SSLVerify changed it */
+ errnum = X509_STORE_CTX_get_error(ctx);
+
+ if ((errdepth == 0) && (errnum == X509_V_OK))
+ /*
+ * We've now got the last certificate - the identity being used for
+ * this connection. At this point we check the whole chain for valid
+ * CAs or, failing that, GSI-proxy validity using GRSTx509CheckChain.
+ */
+ {
+ errnum = GRSTx509CheckChain(&first_non_ca, ctx);
+
+ if (errnum != X509_V_OK)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+ "Invalid certificate chain reported by "
+ "GRSTx509CheckChain()");
+
+ sslconn->verify_error = X509_verify_cert_error_string(errnum);
+ ok = FALSE;
+ }
+ else
+ {
+ int i, lastcred;
+ STACK_OF(X509) *peer_certs;
+ const int maxcreds = 99;
+ const size_t credlen = 1024;
+ char creds[maxcreds][credlen+1], envname[14];
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Valid certificate"
+ " chain reported by GRSTx509CheckChain()");
+
+ /*
+ * Always put result of GRSTx509CompactCreds() into environment
+ */
+ if (peer_certs = (STACK_OF(X509) *) X509_STORE_CTX_get_chain(ctx))
+ {
+ if (GRSTx509CompactCreds(&lastcred, maxcreds, credlen,
+ (char *) creds, peer_certs, GRST_VOMS_DIR) == GRST_RET_OK)
+ {
+ for (i=0; i <= lastcred; ++i)
+ {
+ apr_table_setn(conn->notes,
+ apr_psprintf(conn->pool, "GRST_CRED_%d", i),
+ apr_pstrdup(conn->pool, creds[i]));
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "store GRST_CRED_%d=%s", i, creds[i]);
+ }
+ }
+ /* free remaining dup'd certs? */
+ }
+ }
+ }
+
+ return returned_ok;
+}
+
+static int mod_gridsite_server_post_config(apr_pool_t *pPool,
+ apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *main_server)
+{
+ SSL_CTX *ctx;
+ SSLSrvConfigRec *sc;
+ server_rec *this_server;
+
+ ap_add_version_component(pPool,
+ apr_psprintf(pPool, "mod_gridsite/%s", VERSION));
+
+ for (this_server = main_server;
+ this_server != NULL;
+ this_server = this_server->next)
+ {
+ sc = ap_get_module_config(this_server->module_config, &ssl_module);
+
+ if ((sc != NULL) &&
+ (sc->enabled) &&
+ (sc->server != NULL) &&
+ (sc->server->ssl_ctx != NULL))
+ {
+ ctx = sc->server->ssl_ctx;
+
+ /* in 0.9.7 we could set the issuer-checking callback directly */
+// ctx->cert_store->check_issued = GRST_X509_check_issued_wrapper;
+
+ /* but in case 0.9.6 we do it indirectly with another wrapper */
+ SSL_CTX_set_cert_verify_callback(ctx,
+ GRST_verify_cert_wrapper,
+ (void *) NULL);
+
+ /* whatever version, we can set the SSLVerify wrapper properly */
+ SSL_CTX_set_verify(ctx, ctx->verify_mode,
+ GRST_callback_SSLVerify_wrapper);
+
+ if (main_server->loglevel >= APLOG_DEBUG)
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
+ "Set mod_ssl verify callbacks to GridSite wrappers");
+ }
+ }
+
+ return OK;
+}
+
+static void mod_gridsite_child_init(apr_pool_t *pPool, server_rec *pServer)
+{
+ GRSTgaclInit();
+}
+
+static int mod_gridsite_handler(request_rec *r)
+{
+ mod_gridsite_cfg *conf;
+
+ conf = (mod_gridsite_cfg *)
+ ap_get_module_config(r->per_dir_config, &gridsite_module);
+
+ if ((conf->dnlistsuri != NULL) &&
+ (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0))
+ {
+ if (strcmp(r->uri, conf->dnlistsuri) == 0)
+ return mod_gridsite_dnlistsuri_dir_handler(r, conf);
+
+ return mod_gridsite_dnlistsuri_handler(r, conf);
+ }
+
+ if (strcmp(r->handler, DIR_MAGIC_TYPE) == 0)
+ return mod_gridsite_dir_handler(r, conf);
+
+ return mod_gridsite_nondir_handler(r, conf);
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+ 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_first_fixups,NULL,NULL,APR_HOOK_FIRST);
+
+ ap_hook_fixups(mod_gridsite_perm_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
+
+ ap_hook_handler(mod_gridsite_handler, NULL, NULL, APR_HOOK_FIRST);
+}
+
+module AP_MODULE_DECLARE_DATA gridsite_module =
+{
+ STANDARD20_MODULE_STUFF,
+ create_gridsite_dir_config, /* dir config creater */
+ merge_gridsite_dir_config, /* dir merger */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ mod_gridsite_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
--- /dev/null
+/*
+ Copyright (c) 2003-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.
+*/
+
+/*
+
+ Portions of this code are derived from Apache mod_ssl, and are covered
+ by the Apache Software License:
+
+ * Copyright 2001-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*---------------------------------------------------------------------------*
+ * This program is part of GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *---------------------------------------------------------------------------*/
+
+
+/*
+ * After 2.0.49, Apache mod_ssl has most of the mod_ssl structures defined
+ * in ssl_private.h, which is not installed along with httpd-devel (eg in
+ * the FC2 RPM.) This include file provides SIMPLIFIED structures for use
+ * by mod_gridsite: for example, pointers to unused structures are replaced
+ * by void * and some of the structures are truncated when only the early
+ * members are used.
+ *
+ * CLEARLY, THIS WILL BREAK IF THERE ARE MAJOR CHANGES TO ssl_private.h!!!
+ */
+
+#include <ssl.h>
+
+typedef enum {
+ SSL_SHUTDOWN_TYPE_UNSET,
+ SSL_SHUTDOWN_TYPE_STANDARD,
+ SSL_SHUTDOWN_TYPE_UNCLEAN,
+ SSL_SHUTDOWN_TYPE_ACCURATE
+} ssl_shutdown_type_e;
+
+typedef struct {
+ SSL *ssl;
+ const char *client_dn;
+ X509 *client_cert;
+ ssl_shutdown_type_e shutdown_type;
+ const char *verify_info;
+ const char *verify_error;
+ int verify_depth;
+ int is_proxy;
+ int disabled;
+ int non_ssl_request;
+} SSLConnRec;
+
+typedef struct {
+ void *sc; /* pointer back to server config */
+ SSL_CTX *ssl_ctx;
+} modssl_ctx_t;
+
+typedef struct {
+ void *mc;
+ unsigned int enabled;
+ unsigned int proxy_enabled;
+ const char *vhost_id;
+ int vhost_id_len;
+ int session_cache_timeout;
+ modssl_ctx_t *server;
+ modssl_ctx_t *proxy;
+} SSLSrvConfigRec;
+
+extern module AP_MODULE_DECLARE_DATA ssl_module;
--- /dev/null
+#!/usr/bin/env perl
+#
+# roffit: convert man page source files to HTML
+#
+# Read an nroff file. Output a HTML file.
+#
+# This is a very simple script, but I use it on very simple man pages and I've
+# found no other script that makes beautiful web pages.
+#
+my $version = "0.3"; # (14 November 2003)
+# Author: Daniel Stenberg <daniel@haxx.se>
+# Please email me improvements.
+#
+# You're free to do whatever you want with this script.
+#
+# Changes:
+#
+# 0.3 - Daniel Fandrich brought:
+# o deal with .lp lines
+# o .TH needs no section portion anymore
+# o added generator meta tag in the header
+#
+# 0.2 - fixed the <a name> name for the SH section
+# - added <a href> links from all words within \fIthis\fP or \fBthis\fP
+# that has the same text as a .SH or .IP.
+#
+
+use strict;
+#use warnings;
+
+my $InFH = \*STDIN;
+my $OutFH = \*STDOUT;
+my $debugFH = \*STDERR;
+
+my %manpage;
+my @out;
+
+my $indentlevel=0; # logical levels, not columns
+my @p;
+my $within_tp;
+my $standalone=1; # by default we make stand-alone HTML pages
+my $pre;
+my %anchor; # hash with all anchors
+
+while($ARGV[0]) {
+ if($ARGV[0] eq "--bare") {
+ # don't include headers and stuff
+ $standalone=0;
+ shift @ARGV;
+ }
+ else {
+ printf $debugFH "unknown option: %s\n", $ARGV[0] if($ARGV[0] ne "-h");
+ print $debugFH "Usage: roffit [options] < infile > outfile\n",
+ "Options:\n",
+ " --bare Do not put in HTML, HEAD, BODY tags\n";
+ exit;
+ }
+}
+
+sub showp {
+ my @p = @_;
+ push @out, "\n<p class=\"level$indentlevel\">", @p;
+}
+
+sub defaultcss {
+ print $OutFH <<ENDOFCSS
+<STYLE type="text/css">
+P.level0 {
+ padding-left: 2em;
+}
+
+P.level1 {
+ padding-left: 4em;
+}
+
+P.level2 {
+ padding-left: 6em;
+}
+
+span.emphasis {
+ font-style: italic;
+}
+
+span.bold {
+ font-weight: bold;
+}
+
+span.manpage {
+ font-weight: bold;
+}
+
+h2.nroffsh {
+ background-color: #e0e0e0;
+}
+
+span.nroffip {
+ font-weight: bold;
+ font-size: 120%;
+ font-family: monospace;
+}
+
+p.roffit {
+ text-align: center;
+ font-size: 80%;
+}
+</STYLE>
+ENDOFCSS
+ ;
+}
+
+sub text2name {
+ my ($text) = @_;
+ $text =~ s/^ *([^ ]*).*/$1/g;
+ $text =~ s/[^a-zA-Z0-9-]//g;
+ return $text;
+}
+
+# scan through the file and check for <span> sections we should convert
+# to proper links
+sub linkfile {
+ my @new;
+ for(@out) {
+ my $line=$_;
+ my $l;
+ while($line =~ s/<span class=\"(emphasis|bold)\">([^<]*)<\/span>/[]/) {
+ my ($style, $name)=($1, $2);
+
+ $l = text2name($name);
+
+ #printf $debugFH "$style - $name - %s - %d\n",
+ #$l, $anchor{$l};
+
+ my $link;
+ if($anchor{$l}) {
+ $link="<a class=\"$style\" href=\"#$l\">$name</a>";
+ }
+ else {
+ $link="<span Class=\"$style\">$name</span>";
+ }
+ $line =~ s/\[\]/$link/;
+ }
+ push @new, $line;
+ }
+ return @new;
+}
+
+sub parsefile {
+
+ while(<$InFH>) {
+ my $in = $_;
+ my $out;
+ # print $debugFH "DEBUG IN: $_";
+
+ $in =~ s/[\r\n]//g if(!$pre); # tear off newlines
+
+ if($in =~ /^\.([^ \n]*)(.*)/) {
+ # this is a line starting with a dot, that means it is special
+ my ($keyword, $rest) = ($1, $2);
+ $out = "";
+
+ # cut off initial spaces
+ $rest =~ s/^ +//g;
+
+ if($keyword eq "\\\"") {
+ # this is a comment, skip this line
+ }
+ elsif($keyword =~ /^TH$/i) {
+ # man page header:
+ # curl 1 "22 Oct 2003" "Curl 7.10.8" "Curl Manual"
+ # NAME SECTION DATE VERSION MANUAL
+ if($rest =~ /([^ ]*) (\d+) \"([^\"]*)\" \"([^\"]*)\"(\"([^\"]*)\")?/) {
+ # strict matching only so far
+ $manpage{'name'} = $1;
+ $manpage{'section'} = $2;
+ $manpage{'date'} = $3;
+ $manpage{'version'} = $4;
+ $manpage{'manual'} = $6;
+ }
+ }
+ elsif($keyword =~ /^SH$/i) {
+ # Section Header
+ showp(@p);
+ @p="";
+ if($pre) {
+ push @out, "</pre>\n";
+ $pre = 0;
+ }
+
+ my $name = text2name($rest);
+ $anchor{$name}=1;
+
+ $rest =~ s/\"//g; # cut off quotes
+ $rest =~ s/</</g;
+ $rest =~ s/>/>/g;
+ $out = "<a name=\"$name\"></a><h2 class=\"nroffsh\">$rest</h2>";
+ $indentlevel=0;
+ $within_tp=0;
+ }
+ elsif(($keyword =~ /^B$/i) || ($keyword =~ /^BI$/i)) {
+ # Make B and BI the same for simplicity
+ $rest =~ s/\"//g; # cut off quotes
+ $rest =~ s/</</g;
+ $rest =~ s/>/>/g;
+ push @p, "<span class=\"bold\">$rest</span> ";
+ }
+ elsif($keyword =~ /^I$/i) {
+ $rest =~ s/\"//g; # cut off quotes
+ $rest =~ s/</</g;
+ $rest =~ s/>/>/g;
+ push @p, "<span class=\"emphasis\">$rest</span> ";
+ }
+ elsif($keyword =~ /^RS$/i) {
+ # the start of another indent-level. for inlined tables
+ # within an "IP"
+ showp(@p);
+ @p="";
+ $indentlevel++;
+ }
+ elsif($keyword =~ /^RE$/i) {
+ # end of the RS section
+ showp(@p);
+ @p="";
+ $indentlevel--;
+ }
+ elsif($keyword =~ /^NF$/i) {
+ # We let nf start a <pre> section
+ showp(@p);
+ @p="";
+ push @out, "<pre>\n";
+ $pre=1
+ }
+ elsif($keyword =~ /^TP$/i) {
+ # Used within an "RS" section to make a new line. The first
+ # TP as a column indicator, but we decide to do that
+ # controlling in the CSS instead.
+ $within_tp=1;
+ showp(@p);
+ @p="";
+ }
+ elsif($keyword =~ /^IP$/i) {
+ # start of a new paragraph coming up
+ showp(@p);
+ @p="";
+
+ my $name= text2name($rest);
+ $anchor{$name}=1;
+
+ $rest =~ s/\"//g; # cut off quotes
+ $rest =~ s/</</g;
+ $rest =~ s/>/>/g;
+
+ $indentlevel-- if ($indentlevel);
+ push @p, "<a name=\"$name\"></a><span class=\"nroffip\">$rest</span> ";
+ # make this a single-line title
+ showp(@p);
+ @p="";
+ $indentlevel++;
+ $within_tp=0;
+ }
+ elsif($keyword =~ /^ad$/i) {
+ showp(@p);
+ @p="";
+ }
+ elsif($keyword =~ /^sp$/i) {
+ showp(@p);
+ @p="";
+ }
+ elsif($keyword =~ /^lp$/i) {
+ # marks end of a paragraph
+ showp(@p);
+ @p="";
+ }
+ elsif($keyword =~ /^pp$/i) {
+ # PP ends a TP section, but some TP sections don't use it
+ $within_tp=0;
+ }
+ elsif($keyword =~ /^so$/i) {
+ # This keyword refers to a different man page, named in the
+ # $rest.
+ # We don't support this
+ push @out, "See the $rest man page.\n";
+ }
+ elsif($keyword =~ /^BR$/i) {
+ # I'm not sure what this does exactly, but this is commonly
+ # used to include pointers to other man pages. Let's assume
+ # it only does that for now.
+ # blabla (3)
+ # or "blabla (3)"
+ # or strcmp "(3), " strcasecmp "(3)"
+ # etc
+
+ $rest =~ s/\"//g; # cut off quotes
+ my @all = split /,/, $rest;
+ for(@all) {
+ if(/([^ ]*) *\((\d+)\)/) {
+ # TODO: this looks like a man page, check if there's a
+ # HTML file for it and if so make a link to it
+ }
+
+ push @p, "<span class=\"manpage\">$_</span> ";
+ }
+ }
+ else {
+ showp(@p);
+ print $debugFH "ALERT: unknown keyword \"$keyword\"\n";
+ }
+ }
+ else {
+ # text line, decode \-stuff
+ my $txt = $in;
+
+ $txt =~ s/</</g;
+ $txt =~ s/>/>/g;
+ $txt =~ s/\\&//g; # cut off \&
+ $txt =~ s/\\fI/<span class=\"emphasis\">/g;
+ $txt =~ s/\\fB/<span class=\"bold\">/g;
+ $txt =~ s/\\fP/<\/span>/g;
+ $txt =~ s/\\//g;
+
+ if($txt =~ /^[ \t\r\n]*$/) {
+ # no contents, marks end of a paragraph
+ showp(@p);
+ @p="";
+ }
+ else {
+ $txt =~ s/^ /\ \;/g;
+ push @p, "$txt ";
+ }
+ $out ="";
+ }
+
+ if($out) {
+ push @out, $out;
+ # print $debugFH "DEBUG OUT: $out\n";
+ }
+ else {
+ # print $debugFH "DEBUG OUT: [withheld]\n";
+ }
+ }
+ showp(@p);
+}
+
+parsefile();
+
+my @conv = linkfile();
+
+my $title=sprintf("%s man page",
+ $manpage{'name'}?$manpage{'name'}:"secret");
+
+if($standalone) {
+ print $OutFH <<MOO
+<html><head>
+<title>$title</title>
+<meta name="generator" content="roffit $version">
+MOO
+ ;
+ defaultcss();
+ print "</head><body>\n";
+}
+
+print $OutFH @conv;
+print $OutFH <<ROFFIT
+<p class="roffit">
+ This HTML page was made with <a href="http://daniel.haxx.se/projects/roffit/">roffit</a>.
+ROFFIT
+ ;
+
+if($standalone) {
+ print "</body></html>\n";
+}
--- /dev/null
+/*
+ Copyright (c) 2002-3, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*------------------------------------------------------------------------*
+ * For more about GridSite: http://www.gridpp.ac.uk/gridsite/ *
+ *------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gridsite.h"
+
+int main(int argn, char *argv[])
+{
+ int i;
+
+ if (argn == 1)
+ {
+ puts("urlencode [-m|-d] string-to-encode-or-decode");
+ return 0;
+ }
+
+ if (strcmp(argv[1], "-d") == 0) /* decode */
+ for (i = 2; i < argn; ++i)
+ {
+ if (i > 2) fputs(" ", stdout);
+ fputs(GRSThttpUrlDecode(argv[i]), stdout);
+ }
+ else if (strcmp(argv[1], "-m") == 0) /* mild encode */
+ for (i = 2; i < argn; ++i)
+ {
+ if (i > 2) fputs("%20", stdout);
+ fputs(GRSThttpUrlMildencode(argv[i]), stdout);
+ }
+ else /* standard encode */
+ for (i = 1; i < argn; ++i)
+ {
+ if (i > 1) fputs("%20", stdout);
+ fputs(GRSThttpUrlEncode(argv[i]), stdout);
+ }
+
+ puts("");
+
+ return 0;
+}