diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/dav')
28 files changed, 0 insertions, 17122 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/.deps b/rubbos/app/httpd-2.0.64/modules/dav/fs/.deps deleted file mode 100644 index e69de29b..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/.deps +++ /dev/null diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/Makefile b/rubbos/app/httpd-2.0.64/modules/dav/fs/Makefile deleted file mode 100644 index 11144e34..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -top_srcdir = /bottlenecks/rubbos/app/httpd-2.0.64 -top_builddir = /bottlenecks/rubbos/app/httpd-2.0.64 -srcdir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/dav/fs -builddir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/dav/fs -VPATH = /bottlenecks/rubbos/app/httpd-2.0.64/modules/dav/fs -# a modules Makefile has no explicit targets -- they will be defined by -# whatever modules are enabled. just grab special.mk to deal with this. -include $(top_srcdir)/build/special.mk diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/Makefile.in b/rubbos/app/httpd-2.0.64/modules/dav/fs/Makefile.in deleted file mode 100644 index 7c5c149d..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/Makefile.in +++ /dev/null @@ -1,3 +0,0 @@ -# a modules Makefile has no explicit targets -- they will be defined by -# whatever modules are enabled. just grab special.mk to deal with this. -include $(top_srcdir)/build/special.mk diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/NWGNUmakefile b/rubbos/app/httpd-2.0.64/modules/dav/fs/NWGNUmakefile deleted file mode 100644 index 1569be42..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/NWGNUmakefile +++ /dev/null @@ -1,266 +0,0 @@ -# -# Declare the sub-directories to be built here -# - -SUBDIRS = \ - $(EOLIST) - -# -# Get the 'head' of the build environment. This includes default targets and -# paths to tools -# - -include $(AP_WORK)\build\NWGNUhead.inc - -# -# build this level's files - -# -# Make sure all needed macro's are defined -# - -# -# These directories will be at the beginning of the include list, followed by -# INCDIRS -# -XINCDIRS += \ - $(APR)/include \ - $(APRUTIL)/include \ - $(AP_WORK)/include \ - $(AP_WORK)/modules/dav/main \ - $(AP_WORK)/server/mpm/NetWare \ - $(NWOS) \ - $(EOLIST) - -# -# These flags will come after CFLAGS -# -XCFLAGS += \ - $(EOLIST) - -# -# These defines will come after DEFINES -# -XDEFINES += \ - $(EOLIST) - -# -# These flags will be added to the link.opt file -# -XLFLAGS += \ - $(EOLIST) - -# -# These values will be appended to the correct variables based on the value of -# RELEASE -# -ifeq "$(RELEASE)" "debug" -XINCDIRS += \ - $(EOLIST) - -XCFLAGS += \ - $(EOLIST) - -XDEFINES += \ - $(EOLIST) - -XLFLAGS += \ - $(EOLIST) -endif - -ifeq "$(RELEASE)" "noopt" -XINCDIRS += \ - $(EOLIST) - -XCFLAGS += \ - $(EOLIST) - -XDEFINES += \ - $(EOLIST) - -XLFLAGS += \ - $(EOLIST) -endif - -ifeq "$(RELEASE)" "release" -XINCDIRS += \ - $(EOLIST) - -XCFLAGS += \ - $(EOLIST) - -XDEFINES += \ - $(EOLIST) - -XLFLAGS += \ - $(EOLIST) -endif - -# -# These are used by the link target if an NLM is being generated -# This is used by the link 'name' directive to name the nlm. If left blank -# TARGET_nlm (see below) will be used. -# -NLM_NAME = modDAVFS - -# -# This is used by the link '-desc ' directive. -# If left blank, NLM_NAME will be used. -# -NLM_DESCRIPTION = Apache $(VERSION_STR) DAV FileSystem Sub-Module - -# -# This is used by the '-threadname' directive. If left blank, -# NLM_NAME Thread will be used. -# -NLM_THREAD_NAME = modDAVFS Thread - -# -# If this is specified, it will override VERSION value in -# $(AP_WORK)\build\NWGNUenvironment.inc -# -NLM_VERSION = - -# -# If this is specified, it will override the default of 64K -# -NLM_STACK_SIZE = 65536 - - -# -# If this is specified it will be used by the link '-entry' directive -# -NLM_ENTRY_SYM = _LibCPrelude - -# -# If this is specified it will be used by the link '-exit' directive -# -NLM_EXIT_SYM = _LibCPostlude - -# -# If this is specified it will be used by the link '-check' directive -# -NLM_CHECK_SYM = - -# -# If this is specified it will be used by the link '-flags' directive -# -NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION - -# -# Declare all target files (you must add your files here) -# - -# -# If there is an NLM target, put it here -# -TARGET_nlm = \ - $(OBJDIR)/moddavfs.nlm \ - $(EOLIST) - -# -# If there is an LIB target, put it here -# -TARGET_lib = \ - $(EOLIST) - -# -# These are the OBJ files needed to create the NLM target above. -# Paths must all use the '/' character -# -FILES_nlm_objs = \ - $(OBJDIR)/mod_dav_fs.o \ - $(OBJDIR)/dbm.o \ - $(OBJDIR)/lock.o \ - $(OBJDIR)/repos.o \ - $(OBJDIR)/libprews.o \ - $(EOLIST) - -# -# These are the LIB files needed to create the NLM target above. -# These will be added as a library command in the link.opt file. -# -FILES_nlm_libs = \ - libcpre.o \ - $(EOLIST) - -# -# These are the modules that the above NLM target depends on to load. -# These will be added as a module command in the link.opt file. -# -FILES_nlm_modules = \ - Apache2 \ - Libc \ - mod_dav \ - $(EOLIST) - -# -# If the nlm has a msg file, put it's path here -# -FILE_nlm_msg = - -# -# If the nlm has a hlp file put it's path here -# -FILE_nlm_hlp = - -# -# If this is specified, it will override $(NWOS)\copyright.txt. -# -FILE_nlm_copyright = - -# -# Any additional imports go here -# -FILES_nlm_Ximports = \ - @libc.imp \ - @$(APR)/aprlib.imp \ - @httpd.imp \ - @ws2nlm.imp \ - @../main/dav.imp \ - $(EOLIST) - -# -# Any symbols exported to here -# -FILES_nlm_exports = \ - dav_fs_module \ - $(EOLIST) - -# -# These are the OBJ files needed to create the LIB target above. -# Paths must all use the '/' character -# -FILES_lib_objs = \ - $(EOLIST) - -# -# implement targets and dependancies (leave this section alone) -# - -libs :: $(OBJDIR) $(TARGET_lib) - -nlms :: libs $(TARGET_nlm) - -# -# Updated this target to create necessary directories and copy files to the -# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) -# -install :: nlms FORCE - copy $(OBJDIR)\moddavfs.nlm $(INSTALL)\Apache2\modules -# -# Any specialized rules here -# - -$(OBJDIR)/%.o: ../../arch/netware/%.c $(OBJDIR)\$(NLM_NAME)_cc.opt - @echo compiling $< - $(CC) $< -o=$(OBJDIR)\$(@F) @$(OBJDIR)\$(NLM_NAME)_cc.opt - -# -# Include the 'tail' makefile that has targets that depend on variables defined -# in this makefile -# - -include $(AP_WORK)\build\NWGNUtail.inc - - diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/config6.m4 b/rubbos/app/httpd-2.0.64/modules/dav/fs/config6.m4 deleted file mode 100644 index 515111cd..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/config6.m4 +++ /dev/null @@ -1,23 +0,0 @@ -dnl modules enabled in this directory by default - -APACHE_MODPATH_INIT(dav/fs) - -dav_fs_objects="mod_dav_fs.lo dbm.lo lock.lo repos.lo" - -if test "x$enable_dav" != "x"; then - dav_fs_enable=$enable_dav -else - dav_fs_enable=$dav_enable -fi - -case "$host" in - *os2*) - # OS/2 DLLs must resolve all symbols at build time - # and we need some from main DAV module - dav_fs_objects="$dav_fs_objects ../main/mod_dav.la" - ;; -esac - -APACHE_MODULE(dav_fs, DAV provider for the filesystem, $dav_fs_objects, , $dav_fs_enable) - -APACHE_MODPATH_FINISH diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/dbm.c b/rubbos/app/httpd-2.0.64/modules/dav/fs/dbm.c deleted file mode 100644 index a772a75d..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/dbm.c +++ /dev/null @@ -1,753 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV extension module for Apache 2.0.* -** - Database support using DBM-style databases, -** part of the filesystem repository implementation -*/ - -/* -** This implementation uses a SDBM database per file and directory to -** record the properties. These databases are kept in a subdirectory (of -** the directory in question or the directory that holds the file in -** question) named by the macro DAV_FS_STATE_DIR (.DAV). The filename of the -** database is equivalent to the target filename, and is -** DAV_FS_STATE_FILE_FOR_DIR (.state_for_dir) for the directory itself. -*/ - -#include "apr_strings.h" -#include "apr_file_io.h" - -#include "apr_dbm.h" - -#define APR_WANT_BYTEFUNC -#include "apr_want.h" /* for ntohs and htons */ - -#include "mod_dav.h" -#include "repos.h" - - -struct dav_db { - apr_pool_t *pool; - apr_dbm_t *file; - - /* when used as a property database: */ - - int version; /* *minor* version of this db */ - - dav_buffer ns_table; /* table of namespace URIs */ - short ns_count; /* number of entries in table */ - int ns_table_dirty; /* ns_table was modified */ - apr_hash_t *uri_index; /* map URIs to (1-based) table indices */ - - dav_buffer wb_key; /* work buffer for dav_gdbm_key */ - - apr_datum_t iter; /* iteration key */ -}; - -/* ------------------------------------------------------------------------- - * - * GENERIC DBM ACCESS - * - * For the most part, this just uses the APR DBM functions. They are wrapped - * a bit with some error handling (using the mod_dav error functions). - */ - -void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname, - const char **state1, const char **state2) -{ - if (fname == NULL) - fname = DAV_FS_STATE_FILE_FOR_DIR; - - apr_dbm_get_usednames(p, fname, state1, state2); -} - -static dav_error * dav_fs_dbm_error(dav_db *db, apr_pool_t *p, - apr_status_t status) -{ - int save_errno = errno; - int errcode; - const char *errstr; - dav_error *err; - char errbuf[200]; - - if (status == APR_SUCCESS) - return NULL; - - p = db ? db->pool : p; - - /* There might not be a <db> if we had problems creating it. */ - if (db == NULL) { - errcode = 1; - errstr = "Could not open property database."; - } - else { - (void) apr_dbm_geterror(db->file, &errcode, errbuf, sizeof(errbuf)); - errstr = apr_pstrdup(p, errbuf); - } - - err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, errstr); - err->save_errno = save_errno; - return err; -} - -/* ensure that our state subdirectory is present */ -/* ### does this belong here or in dav_fs_repos.c ?? */ -void dav_fs_ensure_state_dir(apr_pool_t * p, const char *dirname) -{ - const char *pathname = apr_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL); - - /* ### do we need to deal with the umask? */ - - /* just try to make it, ignoring any resulting errors */ - (void) apr_dir_make(pathname, APR_OS_DEFAULT, p); -} - -/* dav_dbm_open_direct: Opens a *dbm database specified by path. - * ro = boolean read-only flag. - */ -dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro, - dav_db **pdb) -{ - apr_status_t status; - apr_dbm_t *file; - - *pdb = NULL; - - if ((status = apr_dbm_open(&file, pathname, - ro ? APR_DBM_READONLY : APR_DBM_RWCREATE, - APR_OS_DEFAULT, p)) - != APR_SUCCESS - && !ro) { - /* ### do something with 'status' */ - - /* we can't continue if we couldn't open the file - and we need to write */ - return dav_fs_dbm_error(NULL, p, status); - } - - /* may be NULL if we tried to open a non-existent db as read-only */ - if (file != NULL) { - /* we have an open database... return it */ - *pdb = apr_pcalloc(p, sizeof(**pdb)); - (*pdb)->pool = p; - (*pdb)->file = file; - } - - return NULL; -} - -static dav_error * dav_dbm_open(apr_pool_t * p, const dav_resource *resource, - int ro, dav_db **pdb) -{ - const char *dirpath; - const char *fname; - const char *pathname; - - /* Get directory and filename for resource */ - /* ### should test this result value... */ - (void) dav_fs_dir_file_name(resource, &dirpath, &fname); - - /* If not opening read-only, ensure the state dir exists */ - if (!ro) { - /* ### what are the perf implications of always checking this? */ - dav_fs_ensure_state_dir(p, dirpath); - } - - pathname = apr_pstrcat(p, dirpath, "/" DAV_FS_STATE_DIR "/", - fname ? fname : DAV_FS_STATE_FILE_FOR_DIR, - NULL); - - /* ### readers cannot open while a writer has this open; we should - ### perform a few retries with random pauses. */ - - /* ### do we need to deal with the umask? */ - - return dav_dbm_open_direct(p, pathname, ro, pdb); -} - -void dav_dbm_close(dav_db *db) -{ - apr_dbm_close(db->file); -} - -dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue) -{ - apr_status_t status = apr_dbm_fetch(db->file, key, pvalue); - - return dav_fs_dbm_error(db, NULL, status); -} - -dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value) -{ - apr_status_t status = apr_dbm_store(db->file, key, value); - - return dav_fs_dbm_error(db, NULL, status); -} - -dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key) -{ - apr_status_t status = apr_dbm_delete(db->file, key); - - return dav_fs_dbm_error(db, NULL, status); -} - -int dav_dbm_exists(dav_db *db, apr_datum_t key) -{ - return apr_dbm_exists(db->file, key); -} - -static dav_error * dav_dbm_firstkey(dav_db *db, apr_datum_t *pkey) -{ - apr_status_t status = apr_dbm_firstkey(db->file, pkey); - - return dav_fs_dbm_error(db, NULL, status); -} - -static dav_error * dav_dbm_nextkey(dav_db *db, apr_datum_t *pkey) -{ - apr_status_t status = apr_dbm_nextkey(db->file, pkey); - - return dav_fs_dbm_error(db, NULL, status); -} - -void dav_dbm_freedatum(dav_db *db, apr_datum_t data) -{ - apr_dbm_freedatum(db->file, data); -} - -/* ------------------------------------------------------------------------- - * - * PROPERTY DATABASE FUNCTIONS - */ - - -#define DAV_GDBM_NS_KEY "METADATA" -#define DAV_GDBM_NS_KEY_LEN 8 - -typedef struct { - unsigned char major; -#define DAV_DBVSN_MAJOR 4 - /* - ** V4 -- 0.9.9 .. - ** Prior versions could have keys or values with invalid - ** namespace prefixes as a result of the xmlns="" form not - ** resetting the default namespace to be "no namespace". The - ** namespace would be set to "" which is invalid; it should - ** be set to "no namespace". - ** - ** V3 -- 0.9.8 - ** Prior versions could have values with invalid namespace - ** prefixes due to an incorrect mapping of input to propdb - ** namespace indices. Version bumped to obsolete the old - ** values. - ** - ** V2 -- 0.9.7 - ** This introduced the xml:lang value into the property value's - ** record in the propdb. - ** - ** V1 -- .. 0.9.6 - ** Initial version. - */ - - - unsigned char minor; -#define DAV_DBVSN_MINOR 0 - - short ns_count; - -} dav_propdb_metadata; - -struct dav_deadprop_rollback { - apr_datum_t key; - apr_datum_t value; -}; - -struct dav_namespace_map { - int *ns_map; -}; - -/* -** Internal function to build a key -** -** WARNING: returns a pointer to a "static" buffer holding the key. The -** value must be copied or no longer used if this function is -** called again. -*/ -static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name) -{ - char nsbuf[20]; - apr_size_t l_ns, l_name = strlen(name->name); - apr_datum_t key = { 0 }; - - /* - * Convert namespace ID to a string. "no namespace" is an empty string, - * so the keys will have the form ":name". Otherwise, the keys will - * have the form "#:name". - */ - if (*name->ns == '\0') { - nsbuf[0] = '\0'; - l_ns = 0; - } - else { - int ns_id = (int)apr_hash_get(db->uri_index, name->ns, - APR_HASH_KEY_STRING); - - - if (ns_id == 0) { - /* the namespace was not found(!) */ - return key; /* zeroed */ - } - - l_ns = sprintf(nsbuf, "%d", ns_id - 1); - } - - /* assemble: #:name */ - dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1); - memcpy(db->wb_key.buf, nsbuf, l_ns); - db->wb_key.buf[l_ns] = ':'; - memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1); - - /* build the database key */ - key.dsize = l_ns + 1 + l_name + 1; - key.dptr = db->wb_key.buf; - - return key; -} - -static void dav_append_prop(apr_pool_t *pool, - const char *name, const char *value, - apr_text_header *phdr) -{ - const char *s; - const char *lang = value; - - /* skip past the xml:lang value */ - value += strlen(lang) + 1; - - if (*value == '\0') { - /* the property is an empty value */ - if (*name == ':') { - /* "no namespace" case */ - s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1); - } - else { - s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name); - } - } - else if (*lang != '\0') { - if (*name == ':') { - /* "no namespace" case */ - s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR, - name+1, lang, value, name+1); - } - else { - s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR, - name, lang, value, name); - } - } - else if (*name == ':') { - /* "no namespace" case */ - s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1); - } - else { - s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name); - } - - apr_text_append(pool, phdr, s); -} - -static dav_error * dav_propdb_open(apr_pool_t *pool, - const dav_resource *resource, int ro, - dav_db **pdb) -{ - dav_db *db; - dav_error *err; - apr_datum_t key; - apr_datum_t value = { 0 }; - - *pdb = NULL; - - /* - ** Return if an error occurred, or there is no database. - ** - ** NOTE: db could be NULL if we attempted to open a readonly - ** database that doesn't exist. If we require read/write - ** access, then a database was created and opened. - */ - if ((err = dav_dbm_open(pool, resource, ro, &db)) != NULL - || db == NULL) - return err; - - db->uri_index = apr_hash_make(pool); - - key.dptr = DAV_GDBM_NS_KEY; - key.dsize = DAV_GDBM_NS_KEY_LEN; - if ((err = dav_dbm_fetch(db, key, &value)) != NULL) { - /* ### push a higher-level description? */ - return err; - } - - if (value.dptr == NULL) { - dav_propdb_metadata m = { - DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0 - }; - - /* - ** If there is no METADATA key, then the database may be - ** from versions 0.9.0 .. 0.9.4 (which would be incompatible). - ** These can be identified by the presence of an NS_TABLE entry. - */ - key.dptr = "NS_TABLE"; - key.dsize = 8; - if (dav_dbm_exists(db, key)) { - dav_dbm_close(db); - - /* call it a major version error */ - return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_PROP_BAD_MAJOR, - "Prop database has the wrong major " - "version number and cannot be used."); - } - - /* initialize a new metadata structure */ - dav_set_bufsize(pool, &db->ns_table, sizeof(m)); - memcpy(db->ns_table.buf, &m, sizeof(m)); - } - else { - dav_propdb_metadata m; - int ns; - const char *uri; - - dav_set_bufsize(pool, &db->ns_table, value.dsize); - memcpy(db->ns_table.buf, value.dptr, value.dsize); - - memcpy(&m, value.dptr, sizeof(m)); - if (m.major != DAV_DBVSN_MAJOR) { - dav_dbm_close(db); - - return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_PROP_BAD_MAJOR, - "Prop database has the wrong major " - "version number and cannot be used."); - } - db->version = m.minor; - db->ns_count = ntohs(m.ns_count); - - dav_dbm_freedatum(db, value); - - /* create db->uri_index */ - for (ns = 0, uri = db->ns_table.buf + sizeof(dav_propdb_metadata); - ns++ < db->ns_count; - uri += strlen(uri) + 1) { - - /* we must copy the key, in case ns_table.buf moves */ - apr_hash_set(db->uri_index, - apr_pstrdup(pool, uri), APR_HASH_KEY_STRING, - (void *)ns); - } - } - - *pdb = db; - return NULL; -} - -static void dav_propdb_close(dav_db *db) -{ - - if (db->ns_table_dirty) { - dav_propdb_metadata m; - apr_datum_t key; - apr_datum_t value; - dav_error *err; - - key.dptr = DAV_GDBM_NS_KEY; - key.dsize = DAV_GDBM_NS_KEY_LEN; - - value.dptr = db->ns_table.buf; - value.dsize = db->ns_table.cur_len; - - /* fill in the metadata that we store into the prop db. */ - m.major = DAV_DBVSN_MAJOR; - m.minor = db->version; /* ### keep current minor version? */ - m.ns_count = htons(db->ns_count); - - memcpy(db->ns_table.buf, &m, sizeof(m)); - - err = dav_dbm_store(db, key, value); - /* ### what to do with the error? */ - } - - dav_dbm_close(db); -} - -static dav_error * dav_propdb_define_namespaces(dav_db *db, dav_xmlns_info *xi) -{ - int ns; - const char *uri = db->ns_table.buf + sizeof(dav_propdb_metadata); - - /* within the prop values, we use "ns%d" for prefixes... register them */ - for (ns = 0; ns < db->ns_count; ++ns, uri += strlen(uri) + 1) { - - /* Empty URIs signify the empty namespace. These do not get a - namespace prefix. when we generate the value, we will simply - leave off the prefix, which is defined by mod_dav to be the - empty namespace. */ - if (*uri == '\0') - continue; - - /* ns_table.buf can move, so copy its value (we want the values to - last as long as the provided dav_xmlns_info). */ - dav_xmlns_add(xi, - apr_psprintf(xi->pool, "ns%d", ns), - apr_pstrdup(xi->pool, uri)); - } - - return NULL; -} - -static dav_error * dav_propdb_output_value(dav_db *db, - const dav_prop_name *name, - dav_xmlns_info *xi, - apr_text_header *phdr, - int *found) -{ - apr_datum_t key = dav_build_key(db, name); - apr_datum_t value; - dav_error *err; - - if ((err = dav_dbm_fetch(db, key, &value)) != NULL) - return err; - if (value.dptr == NULL) { - *found = 0; - return NULL; - } - *found = 1; - - dav_append_prop(db->pool, key.dptr, value.dptr, phdr); - - dav_dbm_freedatum(db, value); - - return NULL; -} - -static dav_error * dav_propdb_map_namespaces( - dav_db *db, - const apr_array_header_t *namespaces, - dav_namespace_map **mapping) -{ - dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m)); - int i; - int *pmap; - const char **puri; - - /* - ** Iterate over the provided namespaces. If a namespace already appears - ** in our internal map of URI -> ns_id, then store that in the map. If - ** we don't know the namespace yet, then add it to the map and to our - ** table of known namespaces. - */ - m->ns_map = pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap)); - for (i = namespaces->nelts, puri = (const char **)namespaces->elts; - i-- > 0; - ++puri, ++pmap) { - - const char *uri = *puri; - apr_size_t uri_len = strlen(uri); - int ns_id = (int)apr_hash_get(db->uri_index, uri, uri_len); - - if (ns_id == 0) { - dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1); - memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1); - db->ns_table.cur_len += uri_len + 1; - - /* copy the uri in case the passed-in namespaces changes in - some way. */ - apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len, - (void *)(db->ns_count + 1)); - - db->ns_table_dirty = 1; - - *pmap = db->ns_count++; - } - else { - *pmap = ns_id - 1; - } - } - - *mapping = m; - return NULL; -} - -static dav_error * dav_propdb_store(dav_db *db, const dav_prop_name *name, - const apr_xml_elem *elem, - dav_namespace_map *mapping) -{ - apr_datum_t key = dav_build_key(db, name); - apr_datum_t value; - - /* Note: mapping->ns_map was set up in dav_propdb_map_namespaces() */ - - /* ### use a db- subpool for these values? clear on exit? */ - - /* quote all the values in the element */ - /* ### be nice to do this without affecting the element itself */ - /* ### of course, the cast indicates Badness is occurring here */ - apr_xml_quote_elem(db->pool, (apr_xml_elem *)elem); - - /* generate a text blob for the xml:lang plus the contents */ - apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL, - mapping->ns_map, - (const char **)&value.dptr, &value.dsize); - - return dav_dbm_store(db, key, value); -} - -static dav_error * dav_propdb_remove(dav_db *db, const dav_prop_name *name) -{ - apr_datum_t key = dav_build_key(db, name); - return dav_dbm_delete(db, key); -} - -static int dav_propdb_exists(dav_db *db, const dav_prop_name *name) -{ - apr_datum_t key = dav_build_key(db, name); - return dav_dbm_exists(db, key); -} - -static const char *dav_get_ns_table_uri(dav_db *db, int ns_id) -{ - const char *p = db->ns_table.buf + sizeof(dav_propdb_metadata); - - while (ns_id--) - p += strlen(p) + 1; - - return p; -} - -static void dav_set_name(dav_db *db, dav_prop_name *pname) -{ - const char *s = db->iter.dptr; - - if (s == NULL) { - pname->ns = pname->name = NULL; - } - else if (*s == ':') { - pname->ns = ""; - pname->name = s + 1; - } - else { - int id = atoi(s); - - pname->ns = dav_get_ns_table_uri(db, id); - if (s[1] == ':') { - pname->name = s + 2; - } - else { - pname->name = ap_strchr_c(s + 2, ':') + 1; - } - } -} - -static dav_error * dav_propdb_next_name(dav_db *db, dav_prop_name *pname) -{ - dav_error *err; - - /* free the previous key. note: if the loop is aborted, then the DBM - will toss the key (via pool cleanup) */ - if (db->iter.dptr != NULL) - dav_dbm_freedatum(db, db->iter); - - if ((err = dav_dbm_nextkey(db, &db->iter)) != NULL) - return err; - - /* skip past the METADATA key */ - if (db->iter.dptr != NULL && *db->iter.dptr == 'M') - return dav_propdb_next_name(db, pname); - - dav_set_name(db, pname); - return NULL; -} - -static dav_error * dav_propdb_first_name(dav_db *db, dav_prop_name *pname) -{ - dav_error *err; - - if ((err = dav_dbm_firstkey(db, &db->iter)) != NULL) - return err; - - /* skip past the METADATA key */ - if (db->iter.dptr != NULL && *db->iter.dptr == 'M') - return dav_propdb_next_name(db, pname); - - dav_set_name(db, pname); - return NULL; -} - -static dav_error * dav_propdb_get_rollback(dav_db *db, - const dav_prop_name *name, - dav_deadprop_rollback **prollback) -{ - dav_deadprop_rollback *rb = apr_pcalloc(db->pool, sizeof(*rb)); - apr_datum_t key; - apr_datum_t value; - dav_error *err; - - key = dav_build_key(db, name); - rb->key.dptr = apr_pstrdup(db->pool, key.dptr); - rb->key.dsize = key.dsize; - - if ((err = dav_dbm_fetch(db, key, &value)) != NULL) - return err; - if (value.dptr != NULL) { - rb->value.dptr = apr_pmemdup(db->pool, value.dptr, value.dsize); - rb->value.dsize = value.dsize; - } - - *prollback = rb; - return NULL; -} - -static dav_error * dav_propdb_apply_rollback(dav_db *db, - dav_deadprop_rollback *rollback) -{ - if (rollback->value.dptr == NULL) { - /* don't fail if the thing isn't really there. */ - (void) dav_dbm_delete(db, rollback->key); - return NULL; - } - - return dav_dbm_store(db, rollback->key, rollback->value); -} - -const dav_hooks_db dav_hooks_db_dbm = -{ - dav_propdb_open, - dav_propdb_close, - dav_propdb_define_namespaces, - dav_propdb_output_value, - dav_propdb_map_namespaces, - dav_propdb_store, - dav_propdb_remove, - dav_propdb_exists, - dav_propdb_first_name, - dav_propdb_next_name, - dav_propdb_get_rollback, - dav_propdb_apply_rollback, - - NULL /* ctx */ -}; diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/lock.c b/rubbos/app/httpd-2.0.64/modules/dav/fs/lock.c deleted file mode 100644 index 20780e15..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/lock.c +++ /dev/null @@ -1,1517 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV filesystem lock implementation -*/ - -#include "apr.h" -#include "apr_strings.h" -#include "apr_file_io.h" -#include "apr_uuid.h" - -#define APR_WANT_MEMFUNC -#include "apr_want.h" - -#include "httpd.h" -#include "http_log.h" - -#include "mod_dav.h" -#include "repos.h" - - -/* --------------------------------------------------------------- -** -** Lock database primitives -** -*/ - -/* -** LOCK DATABASES -** -** Lockdiscovery information is stored in the single lock database specified -** by the DAVLockDB directive. Information about this db is stored in the -** global server configuration. -** -** KEY -** -** The database is keyed by a key_type unsigned char (DAV_TYPE_INODE or -** DAV_TYPE_FNAME) followed by inode and device number if possible, -** otherwise full path (in the case of Win32 or lock-null resources). -** -** VALUE -** -** The value consists of a list of elements. -** DIRECT LOCK: [char (DAV_LOCK_DIRECT), -** char (dav_lock_scope), -** char (dav_lock_type), -** int depth, -** time_t expires, -** apr_uuid_t locktoken, -** char[] owner, -** char[] auth_user] -** -** INDIRECT LOCK: [char (DAV_LOCK_INDIRECT), -** apr_uuid_t locktoken, -** time_t expires, -** apr_size_t key_size, -** char[] key] -** The key is to the collection lock that resulted in this indirect lock -*/ - -#define DAV_TRUE 1 -#define DAV_FALSE 0 - -#define DAV_CREATE_LIST 23 -#define DAV_APPEND_LIST 24 - -/* Stored lock_discovery prefix */ -#define DAV_LOCK_DIRECT 1 -#define DAV_LOCK_INDIRECT 2 - -#define DAV_TYPE_INODE 10 -#define DAV_TYPE_FNAME 11 - - -/* ack. forward declare. */ -static dav_error * dav_fs_remove_locknull_member(apr_pool_t *p, - const char *filename, - dav_buffer *pbuf); - -/* -** Use the opaquelock scheme for locktokens -*/ -struct dav_locktoken { - apr_uuid_t uuid; -}; -#define dav_compare_locktoken(plt1, plt2) \ - memcmp(&(plt1)->uuid, &(plt2)->uuid, sizeof((plt1)->uuid)) - - -/* ################################################################# -** ### keep these structures (internal) or move fully to dav_lock? -*/ - -/* -** We need to reliably size the fixed-length portion of -** dav_lock_discovery; best to separate it into another -** struct for a convenient sizeof, unless we pack lock_discovery. -*/ -typedef struct dav_lock_discovery_fixed -{ - char scope; - char type; - int depth; - time_t timeout; -} dav_lock_discovery_fixed; - -typedef struct dav_lock_discovery -{ - struct dav_lock_discovery_fixed f; - - dav_locktoken *locktoken; - const char *owner; /* owner field from activelock */ - const char *auth_user; /* authenticated user who created the lock */ - struct dav_lock_discovery *next; -} dav_lock_discovery; - -/* Indirect locks represent locks inherited from containing collections. - * They reference the lock token for the collection the lock is - * inherited from. A lock provider may also define a key to the - * inherited lock, for fast datbase lookup. The key is opaque outside - * the lock provider. - */ -typedef struct dav_lock_indirect -{ - dav_locktoken *locktoken; - apr_datum_t key; - struct dav_lock_indirect *next; - time_t timeout; -} dav_lock_indirect; - -/* ################################################################# */ - - -/* -** Stored direct lock info - full lock_discovery length: -** prefix + Fixed length + lock token + 2 strings + 2 nulls (one for each string) -*/ -#define dav_size_direct(a) (1 + sizeof(dav_lock_discovery_fixed) \ - + sizeof(apr_uuid_t) \ - + ((a)->owner ? strlen((a)->owner) : 0) \ - + ((a)->auth_user ? strlen((a)->auth_user) : 0) \ - + 2) - -/* Stored indirect lock info - lock token and apr_datum_t */ -#define dav_size_indirect(a) (1 + sizeof(apr_uuid_t) \ - + sizeof(time_t) \ - + sizeof((a)->key.dsize) + (a)->key.dsize) - -/* -** The lockdb structure. -** -** The <db> field may be NULL, meaning one of two things: -** 1) That we have not actually opened the underlying database (yet). The -** <opened> field should be false. -** 2) We opened it readonly and it wasn't present. -** -** The delayed opening (determined by <opened>) makes creating a lockdb -** quick, while deferring the underlying I/O until it is actually required. -** -** We export the notion of a lockdb, but hide the details of it. Most -** implementations will use a database of some kind, but it is certainly -** possible that alternatives could be used. -*/ -struct dav_lockdb_private -{ - request_rec *r; /* for accessing the uuid state */ - apr_pool_t *pool; /* a pool to use */ - const char *lockdb_path; /* where is the lock database? */ - - int opened; /* we opened the database */ - dav_db *db; /* if non-NULL, the lock database */ -}; -typedef struct -{ - dav_lockdb pub; - dav_lockdb_private priv; -} dav_lockdb_combined; - -/* -** The private part of the lock structure. -*/ -struct dav_lock_private -{ - apr_datum_t key; /* key into the lock database */ -}; -typedef struct -{ - dav_lock pub; - dav_lock_private priv; - dav_locktoken token; -} dav_lock_combined; - -/* -** This must be forward-declared so the open_lockdb function can use it. -*/ -extern const dav_hooks_locks dav_hooks_locks_fs; - - -/* internal function for creating locks */ -static dav_lock *dav_fs_alloc_lock(dav_lockdb *lockdb, apr_datum_t key, - const dav_locktoken *locktoken) -{ - dav_lock_combined *comb; - - comb = apr_pcalloc(lockdb->info->pool, sizeof(*comb)); - comb->pub.rectype = DAV_LOCKREC_DIRECT; - comb->pub.info = &comb->priv; - comb->priv.key = key; - - if (locktoken == NULL) { - comb->pub.locktoken = &comb->token; - apr_uuid_get(&comb->token.uuid); - } - else { - comb->pub.locktoken = locktoken; - } - - return &comb->pub; -} - -/* -** dav_fs_parse_locktoken -** -** Parse an opaquelocktoken URI into a locktoken. -*/ -static dav_error * dav_fs_parse_locktoken( - apr_pool_t *p, - const char *char_token, - dav_locktoken **locktoken_p) -{ - dav_locktoken *locktoken; - - if (ap_strstr_c(char_token, "opaquelocktoken:") != char_token) { - return dav_new_error(p, - HTTP_BAD_REQUEST, DAV_ERR_LOCK_UNK_STATE_TOKEN, - "The lock token uses an unknown State-token " - "format and could not be parsed."); - } - char_token += 16; - - locktoken = apr_pcalloc(p, sizeof(*locktoken)); - if (apr_uuid_parse(&locktoken->uuid, char_token)) { - return dav_new_error(p, HTTP_BAD_REQUEST, DAV_ERR_LOCK_PARSE_TOKEN, - "The opaquelocktoken has an incorrect format " - "and could not be parsed."); - } - - *locktoken_p = locktoken; - return NULL; -} - -/* -** dav_fs_format_locktoken -** -** Generate the URI for a locktoken -*/ -static const char *dav_fs_format_locktoken( - apr_pool_t *p, - const dav_locktoken *locktoken) -{ - char buf[APR_UUID_FORMATTED_LENGTH + 1]; - - apr_uuid_format(buf, &locktoken->uuid); - return apr_pstrcat(p, "opaquelocktoken:", buf, NULL); -} - -/* -** dav_fs_compare_locktoken -** -** Determine whether two locktokens are the same -*/ -static int dav_fs_compare_locktoken( - const dav_locktoken *lt1, - const dav_locktoken *lt2) -{ - return dav_compare_locktoken(lt1, lt2); -} - -/* -** dav_fs_really_open_lockdb: -** -** If the database hasn't been opened yet, then open the thing. -*/ -static dav_error * dav_fs_really_open_lockdb(dav_lockdb *lockdb) -{ - dav_error *err; - - if (lockdb->info->opened) - return NULL; - - err = dav_dbm_open_direct(lockdb->info->pool, - lockdb->info->lockdb_path, - lockdb->ro, - &lockdb->info->db); - if (err != NULL) { - return dav_push_error(lockdb->info->pool, - HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_LOCK_OPENDB, - "Could not open the lock database.", - err); - } - - /* all right. it is opened now. */ - lockdb->info->opened = 1; - - return NULL; -} - -/* -** dav_fs_open_lockdb: -** -** "open" the lock database, as specified in the global server configuration. -** If force is TRUE, then the database is opened now, rather than lazily. -** -** Note that only one can be open read/write. -*/ -static dav_error * dav_fs_open_lockdb(request_rec *r, int ro, int force, - dav_lockdb **lockdb) -{ - dav_lockdb_combined *comb; - - comb = apr_pcalloc(r->pool, sizeof(*comb)); - comb->pub.hooks = &dav_hooks_locks_fs; - comb->pub.ro = ro; - comb->pub.info = &comb->priv; - comb->priv.r = r; - comb->priv.pool = r->pool; - - comb->priv.lockdb_path = dav_get_lockdb_path(r); - if (comb->priv.lockdb_path == NULL) { - return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_LOCK_NO_DB, - "A lock database was not specified with the " - "DAVLockDB directive. One must be specified " - "to use the locking functionality."); - } - - /* done initializing. return it. */ - *lockdb = &comb->pub; - - if (force) { - /* ### add a higher-level comment? */ - return dav_fs_really_open_lockdb(*lockdb); - } - - return NULL; -} - -/* -** dav_fs_close_lockdb: -** -** Close it. Duh. -*/ -static void dav_fs_close_lockdb(dav_lockdb *lockdb) -{ - if (lockdb->info->db != NULL) - dav_dbm_close(lockdb->info->db); -} - -/* -** dav_fs_build_fname_key -** -** Given a pathname, build a DAV_TYPE_FNAME lock database key. -*/ -static apr_datum_t dav_fs_build_fname_key(apr_pool_t *p, const char *pathname) -{ - apr_datum_t key; - - /* ### does this allocation have a proper lifetime? need to check */ - /* ### can we use a buffer for this? */ - - /* size is TYPE + pathname + null */ - key.dsize = strlen(pathname) + 2; - key.dptr = apr_palloc(p, key.dsize); - *key.dptr = DAV_TYPE_FNAME; - memcpy(key.dptr + 1, pathname, key.dsize - 1); - if (key.dptr[key.dsize - 2] == '/') - key.dptr[--key.dsize - 1] = '\0'; - return key; -} - -/* -** dav_fs_build_key: Given a resource, return a apr_datum_t key -** to look up lock information for this file. -** -** (inode/dev not supported or file is lock-null): -** apr_datum_t->dvalue = full path -** -** (inode/dev supported and file exists ): -** apr_datum_t->dvalue = inode, dev -*/ -static apr_datum_t dav_fs_build_key(apr_pool_t *p, - const dav_resource *resource) -{ - const char *file = dav_fs_pathname(resource); - apr_datum_t key; - apr_finfo_t finfo; - apr_status_t rv; - - /* ### use lstat() ?? */ - /* - * XXX: What for platforms with no IDENT (dev/inode)? - */ - rv = apr_stat(&finfo, file, APR_FINFO_IDENT, p); - if ((rv == APR_SUCCESS || rv == APR_INCOMPLETE) - && ((finfo.valid & APR_FINFO_IDENT) == APR_FINFO_IDENT)) - { - /* ### can we use a buffer for this? */ - key.dsize = 1 + sizeof(finfo.inode) + sizeof(finfo.device); - key.dptr = apr_palloc(p, key.dsize); - *key.dptr = DAV_TYPE_INODE; - memcpy(key.dptr + 1, &finfo.inode, sizeof(finfo.inode)); - memcpy(key.dptr + 1 + sizeof(finfo.inode), &finfo.device, - sizeof(finfo.device)); - - return key; - } - - return dav_fs_build_fname_key(p, file); -} - -/* -** dav_fs_lock_expired: return 1 (true) if the given timeout is in the past -** or present (the lock has expired), or 0 (false) if in the future -** (the lock has not yet expired). -*/ -static int dav_fs_lock_expired(time_t expires) -{ - return expires != DAV_TIMEOUT_INFINITE && time(NULL) >= expires; -} - -/* -** dav_fs_save_lock_record: Saves the lock information specified in the -** direct and indirect lock lists about path into the lock database. -** If direct and indirect == NULL, the key is removed. -*/ -static dav_error * dav_fs_save_lock_record(dav_lockdb *lockdb, apr_datum_t key, - dav_lock_discovery *direct, - dav_lock_indirect *indirect) -{ - dav_error *err; - apr_datum_t val = { 0 }; - char *ptr; - dav_lock_discovery *dp = direct; - dav_lock_indirect *ip = indirect; - -#if DAV_DEBUG - if (lockdb->ro) { - return dav_new_error(lockdb->info->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "INTERNAL DESIGN ERROR: the lockdb was opened " - "readonly, but an attempt to save locks was " - "performed."); - } -#endif - - if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) { - /* ### add a higher-level error? */ - return err; - } - - /* If nothing to save, delete key */ - if (dp == NULL && ip == NULL) { - /* don't fail if the key is not present */ - /* ### but what about other errors? */ - (void) dav_dbm_delete(lockdb->info->db, key); - return NULL; - } - - while(dp) { - val.dsize += dav_size_direct(dp); - dp = dp->next; - } - while(ip) { - val.dsize += dav_size_indirect(ip); - ip = ip->next; - } - - /* ### can this be apr_palloc() ? */ - /* ### hmmm.... investigate the use of a buffer here */ - ptr = val.dptr = apr_pcalloc(lockdb->info->pool, val.dsize); - dp = direct; - ip = indirect; - - while(dp) { - *ptr++ = DAV_LOCK_DIRECT; /* Direct lock - lock_discovery struct follows */ - memcpy(ptr, dp, sizeof(dp->f)); /* Fixed portion of struct */ - ptr += sizeof(dp->f); - memcpy(ptr, dp->locktoken, sizeof(*dp->locktoken)); - ptr += sizeof(*dp->locktoken); - if (dp->owner == NULL) { - *ptr++ = '\0'; - } - else { - memcpy(ptr, dp->owner, strlen(dp->owner) + 1); - ptr += strlen(dp->owner) + 1; - } - if (dp->auth_user == NULL) { - *ptr++ = '\0'; - } - else { - memcpy(ptr, dp->auth_user, strlen(dp->auth_user) + 1); - ptr += strlen(dp->auth_user) + 1; - } - - dp = dp->next; - } - - while(ip) { - *ptr++ = DAV_LOCK_INDIRECT; /* Indirect lock prefix */ - memcpy(ptr, ip->locktoken, sizeof(*ip->locktoken)); /* Locktoken */ - ptr += sizeof(*ip->locktoken); - memcpy(ptr, &ip->timeout, sizeof(ip->timeout)); /* Expire time */ - ptr += sizeof(ip->timeout); - memcpy(ptr, &ip->key.dsize, sizeof(ip->key.dsize)); /* Size of key */ - ptr += sizeof(ip->key.dsize); - memcpy(ptr, ip->key.dptr, ip->key.dsize); /* Key data */ - ptr += ip->key.dsize; - ip = ip->next; - } - - if ((err = dav_dbm_store(lockdb->info->db, key, val)) != NULL) { - /* ### more details? add an error_id? */ - return dav_push_error(lockdb->info->pool, - HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_LOCK_SAVE_LOCK, - "Could not save lock information.", - err); - } - - return NULL; -} - -/* -** dav_load_lock_record: Reads lock information about key from lock db; -** creates linked lists of the direct and indirect locks. -** -** If add_method = DAV_APPEND_LIST, the result will be appended to the -** head of the direct and indirect lists supplied. -** -** Passive lock removal: If lock has timed out, it will not be returned. -** ### How much "logging" does RFC 2518 require? -*/ -static dav_error * dav_fs_load_lock_record(dav_lockdb *lockdb, apr_datum_t key, - int add_method, - dav_lock_discovery **direct, - dav_lock_indirect **indirect) -{ - apr_pool_t *p = lockdb->info->pool; - dav_error *err; - apr_size_t offset = 0; - int need_save = DAV_FALSE; - apr_datum_t val = { 0 }; - dav_lock_discovery *dp; - dav_lock_indirect *ip; - dav_buffer buf = { 0 }; - - if (add_method != DAV_APPEND_LIST) { - *direct = NULL; - *indirect = NULL; - } - - if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) { - /* ### add a higher-level error? */ - return err; - } - - /* - ** If we opened readonly and the db wasn't there, then there are no - ** locks for this resource. Just exit. - */ - if (lockdb->info->db == NULL) - return NULL; - - if ((err = dav_dbm_fetch(lockdb->info->db, key, &val)) != NULL) - return err; - - if (!val.dsize) - return NULL; - - while (offset < val.dsize) { - switch (*(val.dptr + offset++)) { - case DAV_LOCK_DIRECT: - /* Create and fill a dav_lock_discovery structure */ - - dp = apr_pcalloc(p, sizeof(*dp)); - memcpy(dp, val.dptr + offset, sizeof(dp->f)); - offset += sizeof(dp->f); - dp->locktoken = apr_palloc(p, sizeof(*dp->locktoken)); - memcpy(dp->locktoken, val.dptr + offset, sizeof(*dp->locktoken)); - offset += sizeof(*dp->locktoken); - if (*(val.dptr + offset) == '\0') { - ++offset; - } - else { - dp->owner = apr_pstrdup(p, val.dptr + offset); - offset += strlen(dp->owner) + 1; - } - - if (*(val.dptr + offset) == '\0') { - ++offset; - } - else { - dp->auth_user = apr_pstrdup(p, val.dptr + offset); - offset += strlen(dp->auth_user) + 1; - } - - if (!dav_fs_lock_expired(dp->f.timeout)) { - dp->next = *direct; - *direct = dp; - } - else { - need_save = DAV_TRUE; - - /* Remove timed-out locknull fm .locknull list */ - if (*key.dptr == DAV_TYPE_FNAME) { - const char *fname = key.dptr + 1; - apr_finfo_t finfo; - apr_status_t rv; - - /* if we don't see the file, then it's a locknull */ - rv = apr_lstat(&finfo, fname, APR_FINFO_MIN, p); - if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { - if ((err = dav_fs_remove_locknull_member(p, fname, &buf)) != NULL) { - /* ### push a higher-level description? */ - return err; - } - } - } - } - break; - - case DAV_LOCK_INDIRECT: - /* Create and fill a dav_lock_indirect structure */ - - ip = apr_pcalloc(p, sizeof(*ip)); - ip->locktoken = apr_palloc(p, sizeof(*ip->locktoken)); - memcpy(ip->locktoken, val.dptr + offset, sizeof(*ip->locktoken)); - offset += sizeof(*ip->locktoken); - memcpy(&ip->timeout, val.dptr + offset, sizeof(ip->timeout)); - offset += sizeof(ip->timeout); - memcpy(&ip->key.dsize, val.dptr + offset, sizeof(ip->key.dsize)); /* length of datum */ - offset += sizeof(ip->key.dsize); - ip->key.dptr = apr_palloc(p, ip->key.dsize); - memcpy(ip->key.dptr, val.dptr + offset, ip->key.dsize); - offset += ip->key.dsize; - - if (!dav_fs_lock_expired(ip->timeout)) { - ip->next = *indirect; - *indirect = ip; - } - else { - need_save = DAV_TRUE; - /* A locknull resource will never be locked indirectly */ - } - - break; - - default: - dav_dbm_freedatum(lockdb->info->db, val); - - /* ### should use a computed_desc and insert corrupt token data */ - --offset; - return dav_new_error(p, - HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_LOCK_CORRUPT_DB, - apr_psprintf(p, - "The lock database was found to " - "be corrupt. offset %" - APR_SIZE_T_FMT ", c=%02x", - offset, val.dptr[offset])); - } - } - - dav_dbm_freedatum(lockdb->info->db, val); - - /* Clean up this record if we found expired locks */ - /* - ** ### shouldn't do this if we've been opened READONLY. elide the - ** ### timed-out locks from the response, but don't save that info back - */ - if (need_save == DAV_TRUE) { - return dav_fs_save_lock_record(lockdb, key, *direct, *indirect); - } - - return NULL; -} - -/* resolve <indirect>, returning <*direct> */ -static dav_error * dav_fs_resolve(dav_lockdb *lockdb, - dav_lock_indirect *indirect, - dav_lock_discovery **direct, - dav_lock_discovery **ref_dp, - dav_lock_indirect **ref_ip) -{ - dav_error *err; - dav_lock_discovery *dir; - dav_lock_indirect *ind; - - if ((err = dav_fs_load_lock_record(lockdb, indirect->key, - DAV_CREATE_LIST, - &dir, &ind)) != NULL) { - /* ### insert a higher-level description? */ - return err; - } - if (ref_dp != NULL) { - *ref_dp = dir; - *ref_ip = ind; - } - - for (; dir != NULL; dir = dir->next) { - if (!dav_compare_locktoken(indirect->locktoken, dir->locktoken)) { - *direct = dir; - return NULL; - } - } - - /* No match found (but we should have found one!) */ - - /* ### use a different description and/or error ID? */ - return dav_new_error(lockdb->info->pool, - HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_LOCK_CORRUPT_DB, - "The lock database was found to be corrupt. " - "An indirect lock's direct lock could not " - "be found."); -} - -/* --------------------------------------------------------------- -** -** Property-related lock functions -** -*/ - -/* -** dav_fs_get_supportedlock: Returns a static string for all supportedlock -** properties. I think we save more returning a static string than -** constructing it every time, though it might look cleaner. -*/ -static const char *dav_fs_get_supportedlock(const dav_resource *resource) -{ - static const char supported[] = DEBUG_CR - "<D:lockentry>" DEBUG_CR - "<D:lockscope><D:exclusive/></D:lockscope>" DEBUG_CR - "<D:locktype><D:write/></D:locktype>" DEBUG_CR - "</D:lockentry>" DEBUG_CR - "<D:lockentry>" DEBUG_CR - "<D:lockscope><D:shared/></D:lockscope>" DEBUG_CR - "<D:locktype><D:write/></D:locktype>" DEBUG_CR - "</D:lockentry>" DEBUG_CR; - - return supported; -} - -/* --------------------------------------------------------------- -** -** General lock functions -** -*/ - -/* --------------------------------------------------------------- -** -** Functions dealing with lock-null resources -** -*/ - -/* -** dav_fs_load_locknull_list: Returns a dav_buffer dump of the locknull file -** for the given directory. -*/ -static dav_error * dav_fs_load_locknull_list(apr_pool_t *p, const char *dirpath, - dav_buffer *pbuf) -{ - apr_finfo_t finfo; - apr_file_t *file = NULL; - dav_error *err = NULL; - apr_size_t amt; - apr_status_t rv; - - dav_buffer_init(p, pbuf, dirpath); - - if (pbuf->buf[pbuf->cur_len - 1] == '/') - pbuf->buf[--pbuf->cur_len] = '\0'; - - dav_buffer_place(p, pbuf, "/" DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE); - - /* reset this in case we leave w/o reading into the buffer */ - pbuf->cur_len = 0; - - if (apr_file_open(&file, pbuf->buf, APR_READ | APR_BINARY, APR_OS_DEFAULT, - p) != APR_SUCCESS) { - return NULL; - } - - rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file); - if (rv != APR_SUCCESS) { - err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(p, - "Opened but could not stat file %s", - pbuf->buf)); - goto loaderror; - } - - if (finfo.size != (apr_size_t)finfo.size) { - err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(p, - "Opened but rejected huge file %s", - pbuf->buf)); - goto loaderror; - } - - amt = (apr_size_t)finfo.size; - dav_set_bufsize(p, pbuf, amt); - if (apr_file_read(file, pbuf->buf, &amt) != APR_SUCCESS - || amt != finfo.size) { - err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(p, - "Failure reading locknull file " - "for %s", dirpath)); - - /* just in case the caller disregards the returned error */ - pbuf->cur_len = 0; - goto loaderror; - } - - loaderror: - apr_file_close(file); - return err; -} - -/* -** dav_fs_save_locknull_list: Saves contents of pbuf into the -** locknull file for dirpath. -*/ -static dav_error * dav_fs_save_locknull_list(apr_pool_t *p, const char *dirpath, - dav_buffer *pbuf) -{ - const char *pathname; - apr_file_t *file = NULL; - dav_error *err = NULL; - apr_size_t amt; - - if (pbuf->buf == NULL) - return NULL; - - dav_fs_ensure_state_dir(p, dirpath); - pathname = apr_pstrcat(p, - dirpath, - dirpath[strlen(dirpath) - 1] == '/' ? "" : "/", - DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE, - NULL); - - if (pbuf->cur_len == 0) { - /* delete the file if cur_len == 0 */ - if (apr_file_remove(pathname, p) != 0) { - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(p, - "Error removing %s", pathname)); - } - return NULL; - } - - if (apr_file_open(&file, pathname, - APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY, - APR_OS_DEFAULT, p) != APR_SUCCESS) { - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(p, - "Error opening %s for writing", - pathname)); - } - - amt = pbuf->cur_len; - if (apr_file_write(file, pbuf->buf, &amt) != APR_SUCCESS - || amt != pbuf->cur_len) { - err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(p, - "Error writing %" APR_SIZE_T_FMT - " bytes to %s", - pbuf->cur_len, pathname)); - } - - apr_file_close(file); - return err; -} - -/* -** dav_fs_remove_locknull_member: Removes filename from the locknull list -** for directory path. -*/ -static dav_error * dav_fs_remove_locknull_member(apr_pool_t *p, - const char *filename, - dav_buffer *pbuf) -{ - dav_error *err; - apr_size_t len; - apr_size_t scanlen; - char *scan; - const char *scanend; - char *dirpath = apr_pstrdup(p, filename); - char *fname = strrchr(dirpath, '/'); - int dirty = 0; - - if (fname != NULL) - *fname++ = '\0'; - else - fname = dirpath; - len = strlen(fname) + 1; - - if ((err = dav_fs_load_locknull_list(p, dirpath, pbuf)) != NULL) { - /* ### add a higher level description? */ - return err; - } - - for (scan = pbuf->buf, scanend = scan + pbuf->cur_len; - scan < scanend; - scan += scanlen) { - scanlen = strlen(scan) + 1; - if (len == scanlen && memcmp(fname, scan, scanlen) == 0) { - pbuf->cur_len -= scanlen; - memmove(scan, scan + scanlen, scanend - (scan + scanlen)); - dirty = 1; - break; - } - } - - if (dirty) { - if ((err = dav_fs_save_locknull_list(p, dirpath, pbuf)) != NULL) { - /* ### add a higher level description? */ - return err; - } - } - - return NULL; -} - -/* Note: used by dav_fs_repos.c */ -dav_error * dav_fs_get_locknull_members( - const dav_resource *resource, - dav_buffer *pbuf) -{ - const char *dirpath; - - /* ### should test this result value... */ - (void) dav_fs_dir_file_name(resource, &dirpath, NULL); - return dav_fs_load_locknull_list(dav_fs_pool(resource), dirpath, pbuf); -} - -/* ### fold into append_lock? */ -/* ### take an optional buf parameter? */ -static dav_error * dav_fs_add_locknull_state( - dav_lockdb *lockdb, - const dav_resource *resource) -{ - dav_buffer buf = { 0 }; - apr_pool_t *p = lockdb->info->pool; - const char *dirpath; - const char *fname; - dav_error *err; - - /* ### should test this result value... */ - (void) dav_fs_dir_file_name(resource, &dirpath, &fname); - - if ((err = dav_fs_load_locknull_list(p, dirpath, &buf)) != NULL) { - return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not load .locknull file.", err); - } - - dav_buffer_append(p, &buf, fname); - buf.cur_len++; /* we want the null-term here */ - - if ((err = dav_fs_save_locknull_list(p, dirpath, &buf)) != NULL) { - return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not save .locknull file.", err); - } - - return NULL; -} - -/* -** dav_fs_remove_locknull_state: Given a request, check to see if r->filename -** is/was a lock-null resource. If so, return it to an existant state. -** -** ### this function is broken... it doesn't check! -** -** In this implementation, this involves two things: -** (a) remove it from the list in the appropriate .DAV/locknull file -** (b) on *nix, convert the key from a filename to an inode. -*/ -static dav_error * dav_fs_remove_locknull_state( - dav_lockdb *lockdb, - const dav_resource *resource) -{ - dav_buffer buf = { 0 }; - dav_error *err; - apr_pool_t *p = lockdb->info->pool; - const char *pathname = dav_fs_pathname(resource); - - if ((err = dav_fs_remove_locknull_member(p, pathname, &buf)) != NULL) { - /* ### add a higher-level description? */ - return err; - } - - { - dav_lock_discovery *ld; - dav_lock_indirect *id; - apr_datum_t key; - - /* - ** Fetch the lock(s) that made the resource lock-null. Remove - ** them under the filename key. Obtain the new inode key, and - ** save the same lock information under it. - */ - key = dav_fs_build_fname_key(p, pathname); - if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, - &ld, &id)) != NULL) { - /* ### insert a higher-level error description */ - return err; - } - - if ((err = dav_fs_save_lock_record(lockdb, key, NULL, NULL)) != NULL) { - /* ### insert a higher-level error description */ - return err; - } - - key = dav_fs_build_key(p, resource); - if ((err = dav_fs_save_lock_record(lockdb, key, ld, id)) != NULL) { - /* ### insert a higher-level error description */ - return err; - } - } - - return NULL; -} - -static dav_error * dav_fs_create_lock(dav_lockdb *lockdb, - const dav_resource *resource, - dav_lock **lock) -{ - apr_datum_t key; - - key = dav_fs_build_key(lockdb->info->pool, resource); - - *lock = dav_fs_alloc_lock(lockdb, - key, - NULL); - - (*lock)->is_locknull = !resource->exists; - - return NULL; -} - -static dav_error * dav_fs_get_locks(dav_lockdb *lockdb, - const dav_resource *resource, - int calltype, - dav_lock **locks) -{ - apr_pool_t *p = lockdb->info->pool; - apr_datum_t key; - dav_error *err; - dav_lock *lock = NULL; - dav_lock *newlock; - dav_lock_discovery *dp; - dav_lock_indirect *ip; - -#if DAV_DEBUG - if (calltype == DAV_GETLOCKS_COMPLETE) { - return dav_new_error(lockdb->info->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "INTERNAL DESIGN ERROR: DAV_GETLOCKS_COMPLETE " - "is not yet supported"); - } -#endif - - key = dav_fs_build_key(p, resource); - if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, - &dp, &ip)) != NULL) { - /* ### push a higher-level desc? */ - return err; - } - - /* copy all direct locks to the result list */ - for (; dp != NULL; dp = dp->next) { - newlock = dav_fs_alloc_lock(lockdb, key, dp->locktoken); - newlock->is_locknull = !resource->exists; - newlock->scope = dp->f.scope; - newlock->type = dp->f.type; - newlock->depth = dp->f.depth; - newlock->timeout = dp->f.timeout; - newlock->owner = dp->owner; - newlock->auth_user = dp->auth_user; - - /* hook into the result list */ - newlock->next = lock; - lock = newlock; - } - - /* copy all the indirect locks to the result list. resolve as needed. */ - for (; ip != NULL; ip = ip->next) { - newlock = dav_fs_alloc_lock(lockdb, ip->key, ip->locktoken); - newlock->is_locknull = !resource->exists; - - if (calltype == DAV_GETLOCKS_RESOLVED) { - if ((err = dav_fs_resolve(lockdb, ip, &dp, NULL, NULL)) != NULL) { - /* ### push a higher-level desc? */ - return err; - } - - newlock->scope = dp->f.scope; - newlock->type = dp->f.type; - newlock->depth = dp->f.depth; - newlock->timeout = dp->f.timeout; - newlock->owner = dp->owner; - newlock->auth_user = dp->auth_user; - } - else { - /* DAV_GETLOCKS_PARTIAL */ - newlock->rectype = DAV_LOCKREC_INDIRECT_PARTIAL; - } - - /* hook into the result list */ - newlock->next = lock; - lock = newlock; - } - - *locks = lock; - return NULL; -} - -static dav_error * dav_fs_find_lock(dav_lockdb *lockdb, - const dav_resource *resource, - const dav_locktoken *locktoken, - int partial_ok, - dav_lock **lock) -{ - dav_error *err; - apr_datum_t key; - dav_lock_discovery *dp; - dav_lock_indirect *ip; - - *lock = NULL; - - key = dav_fs_build_key(lockdb->info->pool, resource); - if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, - &dp, &ip)) != NULL) { - /* ### push a higher-level desc? */ - return err; - } - - for (; dp != NULL; dp = dp->next) { - if (!dav_compare_locktoken(locktoken, dp->locktoken)) { - *lock = dav_fs_alloc_lock(lockdb, key, locktoken); - (*lock)->is_locknull = !resource->exists; - (*lock)->scope = dp->f.scope; - (*lock)->type = dp->f.type; - (*lock)->depth = dp->f.depth; - (*lock)->timeout = dp->f.timeout; - (*lock)->owner = dp->owner; - (*lock)->auth_user = dp->auth_user; - return NULL; - } - } - - for (; ip != NULL; ip = ip->next) { - if (!dav_compare_locktoken(locktoken, ip->locktoken)) { - *lock = dav_fs_alloc_lock(lockdb, ip->key, locktoken); - (*lock)->is_locknull = !resource->exists; - - /* ### nobody uses the resolving right now! */ - if (partial_ok) { - (*lock)->rectype = DAV_LOCKREC_INDIRECT_PARTIAL; - } - else { - (*lock)->rectype = DAV_LOCKREC_INDIRECT; - if ((err = dav_fs_resolve(lockdb, ip, &dp, - NULL, NULL)) != NULL) { - /* ### push a higher-level desc? */ - return err; - } - (*lock)->scope = dp->f.scope; - (*lock)->type = dp->f.type; - (*lock)->depth = dp->f.depth; - (*lock)->timeout = dp->f.timeout; - (*lock)->owner = dp->owner; - (*lock)->auth_user = dp->auth_user; - } - return NULL; - } - } - - return NULL; -} - -static dav_error * dav_fs_has_locks(dav_lockdb *lockdb, - const dav_resource *resource, - int *locks_present) -{ - dav_error *err; - apr_datum_t key; - - *locks_present = 0; - - if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) { - /* ### insert a higher-level error description */ - return err; - } - - /* - ** If we opened readonly and the db wasn't there, then there are no - ** locks for this resource. Just exit. - */ - if (lockdb->info->db == NULL) - return NULL; - - key = dav_fs_build_key(lockdb->info->pool, resource); - - *locks_present = dav_dbm_exists(lockdb->info->db, key); - - return NULL; -} - -static dav_error * dav_fs_append_locks(dav_lockdb *lockdb, - const dav_resource *resource, - int make_indirect, - const dav_lock *lock) -{ - apr_pool_t *p = lockdb->info->pool; - dav_error *err; - dav_lock_indirect *ip; - dav_lock_discovery *dp; - apr_datum_t key; - - key = dav_fs_build_key(lockdb->info->pool, resource); - if ((err = dav_fs_load_lock_record(lockdb, key, 0, &dp, &ip)) != NULL) { - /* ### maybe add in a higher-level description */ - return err; - } - - /* - ** ### when we store the lock more directly, we need to update - ** ### lock->rectype and lock->is_locknull - */ - - if (make_indirect) { - for (; lock != NULL; lock = lock->next) { - - /* ### this works for any <lock> rectype */ - dav_lock_indirect *newi = apr_pcalloc(p, sizeof(*newi)); - - /* ### shut off the const warning for now */ - newi->locktoken = (dav_locktoken *)lock->locktoken; - newi->timeout = lock->timeout; - newi->key = lock->info->key; - newi->next = ip; - ip = newi; - } - } - else { - for (; lock != NULL; lock = lock->next) { - /* create and link in the right kind of lock */ - - if (lock->rectype == DAV_LOCKREC_DIRECT) { - dav_lock_discovery *newd = apr_pcalloc(p, sizeof(*newd)); - - newd->f.scope = lock->scope; - newd->f.type = lock->type; - newd->f.depth = lock->depth; - newd->f.timeout = lock->timeout; - /* ### shut off the const warning for now */ - newd->locktoken = (dav_locktoken *)lock->locktoken; - newd->owner = lock->owner; - newd->auth_user = lock->auth_user; - newd->next = dp; - dp = newd; - } - else { - /* DAV_LOCKREC_INDIRECT(_PARTIAL) */ - - dav_lock_indirect *newi = apr_pcalloc(p, sizeof(*newi)); - - /* ### shut off the const warning for now */ - newi->locktoken = (dav_locktoken *)lock->locktoken; - newi->key = lock->info->key; - newi->next = ip; - ip = newi; - } - } - } - - if ((err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) { - /* ### maybe add a higher-level description */ - return err; - } - - /* we have a special list for recording locknull resources */ - /* ### ack! this can add two copies to the locknull list */ - if (!resource->exists - && (err = dav_fs_add_locknull_state(lockdb, resource)) != NULL) { - /* ### maybe add a higher-level description */ - return err; - } - - return NULL; -} - -static dav_error * dav_fs_remove_lock(dav_lockdb *lockdb, - const dav_resource *resource, - const dav_locktoken *locktoken) -{ - dav_error *err; - dav_buffer buf = { 0 }; - dav_lock_discovery *dh = NULL; - dav_lock_indirect *ih = NULL; - apr_datum_t key; - - key = dav_fs_build_key(lockdb->info->pool, resource); - - if (locktoken != NULL) { - dav_lock_discovery *dp; - dav_lock_discovery *dprev = NULL; - dav_lock_indirect *ip; - dav_lock_indirect *iprev = NULL; - - if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, - &dh, &ih)) != NULL) { - /* ### maybe add a higher-level description */ - return err; - } - - for (dp = dh; dp != NULL; dp = dp->next) { - if (dav_compare_locktoken(locktoken, dp->locktoken) == 0) { - if (dprev) - dprev->next = dp->next; - else - dh = dh->next; - } - dprev = dp; - } - - for (ip = ih; ip != NULL; ip = ip->next) { - if (dav_compare_locktoken(locktoken, ip->locktoken) == 0) { - if (iprev) - iprev->next = ip->next; - else - ih = ih->next; - } - iprev = ip; - } - - } - - /* save the modified locks, or remove all locks (dh=ih=NULL). */ - if ((err = dav_fs_save_lock_record(lockdb, key, dh, ih)) != NULL) { - /* ### maybe add a higher-level description */ - return err; - } - - /* - ** If this resource is a locknull resource AND no more locks exist, - ** then remove the locknull member. - ** - ** Note: remove_locknull_state() attempts to convert a locknull member - ** to a real member. In this case, all locks are gone, so the - ** locknull resource returns to the null state (ie. doesn't exist), - ** so there is no need to update the lockdb (and it won't find - ** any because a precondition is that none exist). - */ - if (!resource->exists && dh == NULL && ih == NULL - && (err = dav_fs_remove_locknull_member(lockdb->info->pool, - dav_fs_pathname(resource), - &buf)) != NULL) { - /* ### maybe add a higher-level description */ - return err; - } - - return NULL; -} - -static int dav_fs_do_refresh(dav_lock_discovery *dp, - const dav_locktoken_list *ltl, - time_t new_time) -{ - int dirty = 0; - - for (; ltl != NULL; ltl = ltl->next) { - if (dav_compare_locktoken(dp->locktoken, ltl->locktoken) == 0) - { - dp->f.timeout = new_time; - dirty = 1; - } - } - - return dirty; -} - -static dav_error * dav_fs_refresh_locks(dav_lockdb *lockdb, - const dav_resource *resource, - const dav_locktoken_list *ltl, - time_t new_time, - dav_lock **locks) -{ - dav_error *err; - apr_datum_t key; - dav_lock_discovery *dp; - dav_lock_discovery *dp_scan; - dav_lock_indirect *ip; - int dirty = 0; - dav_lock *newlock; - - *locks = NULL; - - key = dav_fs_build_key(lockdb->info->pool, resource); - if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST, - &dp, &ip)) != NULL) { - /* ### maybe add in a higher-level description */ - return err; - } - - /* ### we should be refreshing direct AND (resolved) indirect locks! */ - - /* refresh all of the direct locks on this resource */ - for (dp_scan = dp; dp_scan != NULL; dp_scan = dp_scan->next) { - if (dav_fs_do_refresh(dp_scan, ltl, new_time)) { - /* the lock was refreshed. return the lock. */ - newlock = dav_fs_alloc_lock(lockdb, key, dp_scan->locktoken); - newlock->is_locknull = !resource->exists; - newlock->scope = dp_scan->f.scope; - newlock->type = dp_scan->f.type; - newlock->depth = dp_scan->f.depth; - newlock->timeout = dp_scan->f.timeout; - newlock->owner = dp_scan->owner; - newlock->auth_user = dp_scan->auth_user; - - newlock->next = *locks; - *locks = newlock; - - dirty = 1; - } - } - - /* if we refreshed any locks, then save them back. */ - if (dirty - && (err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) { - /* ### maybe add in a higher-level description */ - return err; - } - - /* for each indirect lock, find its direct lock and refresh it. */ - for (; ip != NULL; ip = ip->next) { - dav_lock_discovery *ref_dp; - dav_lock_indirect *ref_ip; - - if ((err = dav_fs_resolve(lockdb, ip, &dp_scan, - &ref_dp, &ref_ip)) != NULL) { - /* ### push a higher-level desc? */ - return err; - } - if (dav_fs_do_refresh(dp_scan, ltl, new_time)) { - /* the lock was refreshed. return the lock. */ - newlock = dav_fs_alloc_lock(lockdb, ip->key, dp_scan->locktoken); - newlock->is_locknull = !resource->exists; - newlock->scope = dp_scan->f.scope; - newlock->type = dp_scan->f.type; - newlock->depth = dp_scan->f.depth; - newlock->timeout = dp_scan->f.timeout; - newlock->owner = dp_scan->owner; - newlock->auth_user = dp_scan->auth_user; - - newlock->next = *locks; - *locks = newlock; - - /* save the (resolved) direct lock back */ - if ((err = dav_fs_save_lock_record(lockdb, ip->key, ref_dp, - ref_ip)) != NULL) { - /* ### push a higher-level desc? */ - return err; - } - } - } - - return NULL; -} - - -const dav_hooks_locks dav_hooks_locks_fs = -{ - dav_fs_get_supportedlock, - dav_fs_parse_locktoken, - dav_fs_format_locktoken, - dav_fs_compare_locktoken, - dav_fs_open_lockdb, - dav_fs_close_lockdb, - dav_fs_remove_locknull_state, - dav_fs_create_lock, - dav_fs_get_locks, - dav_fs_find_lock, - dav_fs_has_locks, - dav_fs_append_locks, - dav_fs_remove_lock, - dav_fs_refresh_locks, - NULL, /* lookup_resource */ - - NULL /* ctx */ -}; diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/mod_dav_fs.c b/rubbos/app/httpd-2.0.64/modules/dav/fs/mod_dav_fs.c deleted file mode 100644 index dfd190b3..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/mod_dav_fs.c +++ /dev/null @@ -1,108 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "httpd.h" -#include "http_config.h" -#include "apr_strings.h" - -#include "mod_dav.h" -#include "repos.h" - -/* per-server configuration */ -typedef struct { - const char *lockdb_path; - -} dav_fs_server_conf; - -extern module AP_MODULE_DECLARE_DATA dav_fs_module; - -const char *dav_get_lockdb_path(const request_rec *r) -{ - dav_fs_server_conf *conf; - - conf = ap_get_module_config(r->server->module_config, &dav_fs_module); - return conf->lockdb_path; -} - -static void *dav_fs_create_server_config(apr_pool_t *p, server_rec *s) -{ - return apr_pcalloc(p, sizeof(dav_fs_server_conf)); -} - -static void *dav_fs_merge_server_config(apr_pool_t *p, - void *base, void *overrides) -{ - dav_fs_server_conf *parent = base; - dav_fs_server_conf *child = overrides; - dav_fs_server_conf *newconf; - - newconf = apr_pcalloc(p, sizeof(*newconf)); - - newconf->lockdb_path = - child->lockdb_path ? child->lockdb_path : parent->lockdb_path; - - return newconf; -} - -/* - * Command handler for the DAVLockDB directive, which is TAKE1 - */ -static const char *dav_fs_cmd_davlockdb(cmd_parms *cmd, void *config, - const char *arg1) -{ - dav_fs_server_conf *conf; - conf = ap_get_module_config(cmd->server->module_config, - &dav_fs_module); - conf->lockdb_path = ap_server_root_relative(cmd->pool, arg1); - - if (!conf->lockdb_path) { - return apr_pstrcat(cmd->pool, "Invalid DAVLockDB path ", - arg1, NULL); - } - - return NULL; -} - -static const command_rec dav_fs_cmds[] = -{ - /* per server */ - AP_INIT_TAKE1("DAVLockDB", dav_fs_cmd_davlockdb, NULL, RSRC_CONF, - "specify a lock database"), - - { NULL } -}; - -static void register_hooks(apr_pool_t *p) -{ - dav_hook_gather_propsets(dav_fs_gather_propsets, NULL, NULL, - APR_HOOK_MIDDLE); - dav_hook_find_liveprop(dav_fs_find_liveprop, NULL, NULL, APR_HOOK_MIDDLE); - dav_hook_insert_all_liveprops(dav_fs_insert_all_liveprops, NULL, NULL, - APR_HOOK_MIDDLE); - - dav_fs_register(p); -} - -module AP_MODULE_DECLARE_DATA dav_fs_module = -{ - STANDARD20_MODULE_STUFF, - NULL, /* dir config creater */ - NULL, /* dir merger --- default is to override */ - dav_fs_create_server_config, /* server config */ - dav_fs_merge_server_config, /* merge server config */ - dav_fs_cmds, /* command table */ - register_hooks, /* register hooks */ -}; diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/mod_dav_fs.dsp b/rubbos/app/httpd-2.0.64/modules/dav/fs/mod_dav_fs.dsp deleted file mode 100644 index 5c2239c9..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/mod_dav_fs.dsp +++ /dev/null @@ -1,152 +0,0 @@ -# Microsoft Developer Studio Project File - Name="mod_dav_fs" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=mod_dav_fs - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "mod_dav_fs.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "mod_dav_fs.mak" CFG="mod_dav_fs - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "mod_dav_fs - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "mod_dav_fs - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "mod_dav_fs - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c -# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_dav_fs_src" /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_dav_fs.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav_fs.so -# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_dav_fs.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav_fs.so /opt:ref - -!ELSEIF "$(CFG)" == "mod_dav_fs - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c -# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_dav_fs_src" /FD /c -# ADD BASE MTL /nologo /D "_DEBUG" /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_dav_fs.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav_fs.so -# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_dav_fs.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav_fs.so - -!ENDIF - -# Begin Target - -# Name "mod_dav_fs - Win32 Release" -# Name "mod_dav_fs - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" -# Begin Source File - -SOURCE=.\dbm.c -# End Source File -# Begin Source File - -SOURCE=.\lock.c -# End Source File -# Begin Source File - -SOURCE=.\mod_dav_fs.c -# End Source File -# Begin Source File - -SOURCE=.\repos.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" -# Begin Source File - -SOURCE=.\repos.h -# End Source File -# End Group -# Begin Source File - -SOURCE=.\mod_dav_fs.rc -# End Source File -# Begin Source File - -SOURCE=..\..\..\build\win32\win32ver.awk - -!IF "$(CFG)" == "mod_dav_fs - Win32 Release" - -# PROP Ignore_Default_Tool 1 -# Begin Custom Build - Creating Version Resource -InputPath=..\..\..\build\win32\win32ver.awk - -".\mod_dav_fs.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - awk -f ../../../build/win32/win32ver.awk mod_dav_fs.so "dav_fs_module for Apache" ../../../include/ap_release.h > .\mod_dav_fs.rc - -# End Custom Build - -!ELSEIF "$(CFG)" == "mod_dav_fs - Win32 Debug" - -# PROP Ignore_Default_Tool 1 -# Begin Custom Build - Creating Version Resource -InputPath=..\..\..\build\win32\win32ver.awk - -".\mod_dav_fs.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - awk -f ../../../build/win32/win32ver.awk mod_dav_fs.so "dav_fs_module for Apache" ../../../include/ap_release.h > .\mod_dav_fs.rc - -# End Custom Build - -!ENDIF - -# End Source File -# End Target -# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/modules.mk b/rubbos/app/httpd-2.0.64/modules/dav/fs/modules.mk deleted file mode 100644 index ceb52a1b..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/modules.mk +++ /dev/null @@ -1,3 +0,0 @@ -DISTCLEAN_TARGETS = modules.mk -static = -shared = diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/repos.c b/rubbos/app/httpd-2.0.64/modules/dav/fs/repos.c deleted file mode 100644 index bf8da8d0..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/repos.c +++ /dev/null @@ -1,2130 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV filesystem-based repository provider -*/ - -#include "apr.h" -#include "apr_file_io.h" -#include "apr_strings.h" -#include "apr_buckets.h" - -#if APR_HAVE_STDIO_H -#include <stdio.h> /* for sprintf() */ -#endif - -#include "httpd.h" -#include "http_log.h" -#include "http_protocol.h" /* for ap_set_* (in dav_fs_set_headers) */ -#include "http_request.h" /* for ap_update_mtime() */ - -#include "mod_dav.h" -#include "repos.h" - - -/* to assist in debugging mod_dav's GET handling */ -#define DEBUG_GET_HANDLER 0 - -#define DAV_FS_COPY_BLOCKSIZE 16384 /* copy 16k at a time */ - -/* context needed to identify a resource */ -struct dav_resource_private { - apr_pool_t *pool; /* memory storage pool associated with request */ - const char *pathname; /* full pathname to resource */ - apr_finfo_t finfo; /* filesystem info */ -}; - -/* private context for doing a filesystem walk */ -typedef struct { - /* the input walk parameters */ - const dav_walk_params *params; - - /* reused as we walk */ - dav_walk_resource wres; - - dav_resource res1; - dav_resource_private info1; - dav_buffer path1; - dav_buffer uri_buf; - - /* MOVE/COPY need a secondary path */ - dav_resource res2; - dav_resource_private info2; - dav_buffer path2; - - dav_buffer locknull_buf; - -} dav_fs_walker_context; - -typedef struct { - int is_move; /* is this a MOVE? */ - dav_buffer work_buf; /* handy buffer for copymove_file() */ - - /* CALLBACK: this is a secondary resource managed specially for us */ - const dav_resource *res_dst; - - /* copied from dav_walk_params (they are invariant across the walk) */ - const dav_resource *root; - apr_pool_t *pool; - -} dav_fs_copymove_walk_ctx; - -/* an internal WALKTYPE to walk hidden files (the .DAV directory) */ -#define DAV_WALKTYPE_HIDDEN 0x4000 - -/* an internal WALKTYPE to call collections (again) after their contents */ -#define DAV_WALKTYPE_POSTFIX 0x8000 - -#define DAV_CALLTYPE_POSTFIX 1000 /* a private call type */ - - -/* pull this in from the other source file */ -extern const dav_hooks_locks dav_hooks_locks_fs; - -/* forward-declare the hook structures */ -static const dav_hooks_repository dav_hooks_repository_fs; -static const dav_hooks_liveprop dav_hooks_liveprop_fs; - -/* -** The namespace URIs that we use. This list and the enumeration must -** stay in sync. -*/ -static const char * const dav_fs_namespace_uris[] = -{ - "DAV:", - "http://apache.org/dav/props/", - - NULL /* sentinel */ -}; -enum { - DAV_FS_URI_DAV, /* the DAV: namespace URI */ - DAV_FS_URI_MYPROPS /* the namespace URI for our custom props */ -}; - -/* -** Does this platform support an executable flag? -** -** ### need a way to portably abstract this query -*/ -#ifndef WIN32 -#define DAV_FS_HAS_EXECUTABLE -#endif - -/* -** The single property that we define (in the DAV_FS_URI_MYPROPS namespace) -*/ -#define DAV_PROPID_FS_executable 1 - -static const dav_liveprop_spec dav_fs_props[] = -{ - /* standard DAV properties */ - { - DAV_FS_URI_DAV, - "creationdate", - DAV_PROPID_creationdate, - 0 - }, - { - DAV_FS_URI_DAV, - "getcontentlength", - DAV_PROPID_getcontentlength, - 0 - }, - { - DAV_FS_URI_DAV, - "getetag", - DAV_PROPID_getetag, - 0 - }, - { - DAV_FS_URI_DAV, - "getlastmodified", - DAV_PROPID_getlastmodified, - 0 - }, - - /* our custom properties */ - { - DAV_FS_URI_MYPROPS, - "executable", - DAV_PROPID_FS_executable, - 0 /* handled special in dav_fs_is_writable */ - }, - - { 0 } /* sentinel */ -}; - -static const dav_liveprop_group dav_fs_liveprop_group = -{ - dav_fs_props, - dav_fs_namespace_uris, - &dav_hooks_liveprop_fs -}; - - -/* define the dav_stream structure for our use */ -struct dav_stream { - apr_pool_t *p; - apr_file_t *f; - const char *pathname; /* we may need to remove it at close time */ -}; - -/* returns an appropriate HTTP status code given an APR status code for a - * failed I/O operation. ### use something besides 500? */ -#define MAP_IO2HTTP(e) (APR_STATUS_IS_ENOSPC(e) ? HTTP_INSUFFICIENT_STORAGE : \ - HTTP_INTERNAL_SERVER_ERROR) - -/* forward declaration for internal treewalkers */ -static dav_error * dav_fs_walk(const dav_walk_params *params, int depth, - dav_response **response); -static dav_error * dav_fs_internal_walk(const dav_walk_params *params, - int depth, int is_move, - const dav_resource *root_dst, - dav_response **response); - -/* -------------------------------------------------------------------- -** -** PRIVATE REPOSITORY FUNCTIONS -*/ -apr_pool_t *dav_fs_pool(const dav_resource *resource) -{ - return resource->info->pool; -} - -const char *dav_fs_pathname(const dav_resource *resource) -{ - return resource->info->pathname; -} - -dav_error * dav_fs_dir_file_name( - const dav_resource *resource, - const char **dirpath_p, - const char **fname_p) -{ - dav_resource_private *ctx = resource->info; - - if (resource->collection) { - *dirpath_p = ctx->pathname; - if (fname_p != NULL) - *fname_p = NULL; - } - else { - const char *testpath, *rootpath; - char *dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname); - apr_size_t dirlen = strlen(dirpath); - apr_status_t rv = APR_SUCCESS; - - testpath = dirpath; - if (dirlen > 0) { - rv = apr_filepath_root(&rootpath, &testpath, 0, ctx->pool); - } - - /* remove trailing slash from dirpath, unless it's a root path - */ - if ((rv == APR_SUCCESS && testpath && *testpath) - || rv == APR_ERELATIVE) { - if (dirpath[dirlen - 1] == '/') { - dirpath[dirlen - 1] = '\0'; - } - } - - /* ###: Looks like a response could be appropriate - * - * APR_SUCCESS here tells us the dir is a root - * APR_ERELATIVE told us we had no root (ok) - * APR_EINCOMPLETE an incomplete testpath told us - * there was no -file- name here! - * APR_EBADPATH or other errors tell us this file - * path is undecipherable - */ - - if (rv == APR_SUCCESS || rv == APR_ERELATIVE) { - *dirpath_p = dirpath; - if (fname_p != NULL) - *fname_p = ctx->pathname + dirlen; - } - else { - return dav_new_error(ctx->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "An incomplete/bad path was found in " - "dav_fs_dir_file_name."); - } - } - - return NULL; -} - -/* Note: picked up from ap_gm_timestr_822() */ -/* NOTE: buf must be at least DAV_TIMEBUF_SIZE chars in size */ -static void dav_format_time(int style, apr_time_t sec, char *buf) -{ - apr_time_exp_t tms; - - /* ### what to do if fails? */ - (void) apr_time_exp_gmt(&tms, sec); - - if (style == DAV_STYLE_ISO8601) { - /* ### should we use "-00:00" instead of "Z" ?? */ - - /* 20 chars plus null term */ - sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ", - tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, - tms.tm_hour, tms.tm_min, tms.tm_sec); - return; - } - - /* RFC 822 date format; as strftime '%a, %d %b %Y %T GMT' */ - - /* 29 chars plus null term */ - sprintf(buf, - "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", - apr_day_snames[tms.tm_wday], - tms.tm_mday, apr_month_snames[tms.tm_mon], - tms.tm_year + 1900, - tms.tm_hour, tms.tm_min, tms.tm_sec); -} - -static dav_error * dav_fs_copymove_file( - int is_move, - apr_pool_t * p, - const char *src, - const char *dst, - dav_buffer *pbuf) -{ - dav_buffer work_buf = { 0 }; - apr_file_t *inf = NULL; - apr_file_t *outf = NULL; - apr_status_t status; - - if (pbuf == NULL) - pbuf = &work_buf; - - dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE); - - if ((apr_file_open(&inf, src, APR_READ | APR_BINARY, APR_OS_DEFAULT, p)) - != APR_SUCCESS) { - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not open file for reading"); - } - - /* ### do we need to deal with the umask? */ - status = apr_file_open(&outf, dst, APR_WRITE | APR_CREATE | APR_TRUNCATE - | APR_BINARY, APR_OS_DEFAULT, p); - if (status != APR_SUCCESS) { - apr_file_close(inf); - - return dav_new_error(p, MAP_IO2HTTP(status), 0, - "Could not open file for writing"); - } - - while (1) { - apr_size_t len = DAV_FS_COPY_BLOCKSIZE; - - status = apr_file_read(inf, pbuf->buf, &len); - if (status != APR_SUCCESS && status != APR_EOF) { - apr_file_close(inf); - apr_file_close(outf); - - if (apr_file_remove(dst, p) != APR_SUCCESS) { - /* ### ACK! Inconsistent state... */ - - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not delete output after read " - "failure. Server is now in an " - "inconsistent state."); - } - - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not read input file"); - } - - if (status == APR_EOF) - break; - - /* write any bytes that were read */ - status = apr_file_write_full(outf, pbuf->buf, len, NULL); - if (status != APR_SUCCESS) { - apr_file_close(inf); - apr_file_close(outf); - - if (apr_file_remove(dst, p) != APR_SUCCESS) { - /* ### ACK! Inconsistent state... */ - - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not delete output after write " - "failure. Server is now in an " - "inconsistent state."); - } - - return dav_new_error(p, MAP_IO2HTTP(status), 0, - "Could not write output file"); - } - } - - apr_file_close(inf); - apr_file_close(outf); - - if (is_move && apr_file_remove(src, p) != APR_SUCCESS) { - dav_error *err; - int save_errno = errno; /* save the errno that got us here */ - - if (apr_file_remove(dst, p) != APR_SUCCESS) { - /* ### ACK. this creates an inconsistency. do more!? */ - - /* ### use something besides 500? */ - /* Note that we use the latest errno */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not remove source or destination " - "file. Server is now in an inconsistent " - "state."); - } - - /* ### use something besides 500? */ - err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not remove source file after move. " - "Destination was removed to ensure consistency."); - err->save_errno = save_errno; - return err; - } - - return NULL; -} - -/* copy/move a file from within a state dir to another state dir */ -/* ### need more buffers to replace the pool argument */ -static dav_error * dav_fs_copymove_state( - int is_move, - apr_pool_t * p, - const char *src_dir, const char *src_file, - const char *dst_dir, const char *dst_file, - dav_buffer *pbuf) -{ - apr_finfo_t src_finfo; /* finfo for source file */ - apr_finfo_t dst_state_finfo; /* finfo for STATE directory */ - apr_status_t rv; - const char *src; - const char *dst; - - /* build the propset pathname for the source file */ - src = apr_pstrcat(p, src_dir, "/" DAV_FS_STATE_DIR "/", src_file, NULL); - - /* the source file doesn't exist */ - rv = apr_stat(&src_finfo, src, APR_FINFO_NORM, p); - if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { - return NULL; - } - - /* build the pathname for the destination state dir */ - dst = apr_pstrcat(p, dst_dir, "/" DAV_FS_STATE_DIR, NULL); - - /* ### do we need to deal with the umask? */ - - /* ensure that it exists */ - rv = apr_dir_make(dst, APR_OS_DEFAULT, p); - if (rv != APR_SUCCESS) { - if (!APR_STATUS_IS_EEXIST(rv)) { - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not create internal state directory"); - } - } - - /* get info about the state directory */ - rv = apr_stat(&dst_state_finfo, dst, APR_FINFO_NORM, p); - if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { - /* Ack! Where'd it go? */ - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "State directory disappeared"); - } - - /* The mkdir() may have failed because a *file* exists there already */ - if (dst_state_finfo.filetype != APR_DIR) { - /* ### try to recover by deleting this file? (and mkdir again) */ - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "State directory is actually a file"); - } - - /* append the target file to the state directory pathname */ - dst = apr_pstrcat(p, dst, "/", dst_file, NULL); - - /* copy/move the file now */ - if (is_move && src_finfo.device == dst_state_finfo.device) { - /* simple rename is possible since it is on the same device */ - if (apr_file_rename(src, dst, p) != APR_SUCCESS) { - /* ### use something besides 500? */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not move state file."); - } - } - else - { - /* gotta copy (and delete) */ - return dav_fs_copymove_file(is_move, p, src, dst, pbuf); - } - - return NULL; -} - -static dav_error *dav_fs_copymoveset(int is_move, apr_pool_t *p, - const dav_resource *src, - const dav_resource *dst, - dav_buffer *pbuf) -{ - const char *src_dir; - const char *src_file; - const char *src_state1; - const char *src_state2; - const char *dst_dir; - const char *dst_file; - const char *dst_state1; - const char *dst_state2; - dav_error *err; - - /* Get directory and filename for resources */ - /* ### should test these result values... */ - (void) dav_fs_dir_file_name(src, &src_dir, &src_file); - (void) dav_fs_dir_file_name(dst, &dst_dir, &dst_file); - - /* Get the corresponding state files for each resource */ - dav_dbm_get_statefiles(p, src_file, &src_state1, &src_state2); - dav_dbm_get_statefiles(p, dst_file, &dst_state1, &dst_state2); -#if DAV_DEBUG - if ((src_state2 != NULL && dst_state2 == NULL) || - (src_state2 == NULL && dst_state2 != NULL)) { - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "DESIGN ERROR: dav_dbm_get_statefiles() " - "returned inconsistent results."); - } -#endif - - err = dav_fs_copymove_state(is_move, p, - src_dir, src_state1, - dst_dir, dst_state1, - pbuf); - - if (err == NULL && src_state2 != NULL) { - err = dav_fs_copymove_state(is_move, p, - src_dir, src_state2, - dst_dir, dst_state2, - pbuf); - - if (err != NULL) { - /* ### CRAP. inconsistency. */ - /* ### should perform some cleanup at the target if we still - ### have the original files */ - - /* Change the error to reflect the bad server state. */ - err->status = HTTP_INTERNAL_SERVER_ERROR; - err->desc = - "Could not fully copy/move the properties. " - "The server is now in an inconsistent state."; - } - } - - return err; -} - -static dav_error *dav_fs_deleteset(apr_pool_t *p, const dav_resource *resource) -{ - const char *dirpath; - const char *fname; - const char *state1; - const char *state2; - const char *pathname; - apr_status_t status; - - /* Get directory, filename, and state-file names for the resource */ - /* ### should test this result value... */ - (void) dav_fs_dir_file_name(resource, &dirpath, &fname); - dav_dbm_get_statefiles(p, fname, &state1, &state2); - - /* build the propset pathname for the file */ - pathname = apr_pstrcat(p, - dirpath, - "/" DAV_FS_STATE_DIR "/", - state1, - NULL); - - /* note: we may get ENOENT if the state dir is not present */ - if ((status = apr_file_remove(pathname, p)) != APR_SUCCESS - && !APR_STATUS_IS_ENOENT(status)) { - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not remove properties."); - } - - if (state2 != NULL) { - /* build the propset pathname for the file */ - pathname = apr_pstrcat(p, - dirpath, - "/" DAV_FS_STATE_DIR "/", - state2, - NULL); - - if ((status = apr_file_remove(pathname, p)) != APR_SUCCESS - && !APR_STATUS_IS_ENOENT(status)) { - /* ### CRAP. only removed half. */ - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not fully remove properties. " - "The server is now in an inconsistent " - "state."); - } - } - - return NULL; -} - -/* -------------------------------------------------------------------- -** -** REPOSITORY HOOK FUNCTIONS -*/ - -static dav_error * dav_fs_get_resource( - request_rec *r, - const char *root_dir, - const char *label, - int use_checked_in, - dav_resource **result_resource) -{ - dav_resource_private *ctx; - dav_resource *resource; - char *s; - char *filename; - apr_size_t len; - - /* ### optimize this into a single allocation! */ - - /* Create private resource context descriptor */ - ctx = apr_pcalloc(r->pool, sizeof(*ctx)); - ctx->finfo = r->finfo; - - /* ### this should go away */ - ctx->pool = r->pool; - - /* Preserve case on OSes which fold canonical filenames */ -#if 0 - /* ### not available in Apache 2.0 yet */ - filename = r->case_preserved_filename; -#else - filename = r->filename; -#endif - - /* - ** If there is anything in the path_info, then this indicates that the - ** entire path was not used to specify the file/dir. We want to append - ** it onto the filename so that we get a "valid" pathname for null - ** resources. - */ - s = apr_pstrcat(r->pool, filename, r->path_info, NULL); - - /* make sure the pathname does not have a trailing "/" */ - len = strlen(s); - if (len > 1 && s[len - 1] == '/') { - s[len - 1] = '\0'; - } - ctx->pathname = s; - - /* Create resource descriptor */ - resource = apr_pcalloc(r->pool, sizeof(*resource)); - resource->type = DAV_RESOURCE_TYPE_REGULAR; - resource->info = ctx; - resource->hooks = &dav_hooks_repository_fs; - resource->pool = r->pool; - - /* make sure the URI does not have a trailing "/" */ - len = strlen(r->uri); - if (len > 1 && r->uri[len - 1] == '/') { - s = apr_pstrdup(r->pool, r->uri); - s[len - 1] = '\0'; - resource->uri = s; - } - else { - resource->uri = r->uri; - } - - if (r->finfo.filetype != 0) { - resource->exists = 1; - resource->collection = r->finfo.filetype == APR_DIR; - - /* unused info in the URL will indicate a null resource */ - - if (r->path_info != NULL && *r->path_info != '\0') { - if (resource->collection) { - /* only a trailing "/" is allowed */ - if (*r->path_info != '/' || r->path_info[1] != '\0') { - - /* - ** This URL/filename represents a locknull resource or - ** possibly a destination of a MOVE/COPY - */ - resource->exists = 0; - resource->collection = 0; - } - } - else - { - /* - ** The base of the path refers to a file -- nothing should - ** be in path_info. The resource is simply an error: it - ** can't be a null or a locknull resource. - */ - return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, - "The URL contains extraneous path " - "components. The resource could not " - "be identified."); - } - - /* retain proper integrity across the structures */ - if (!resource->exists) { - ctx->finfo.filetype = 0; - } - } - } - - *result_resource = resource; - return NULL; -} - -static dav_error * dav_fs_get_parent_resource(const dav_resource *resource, - dav_resource **result_parent) -{ - dav_resource_private *ctx = resource->info; - dav_resource_private *parent_ctx; - dav_resource *parent_resource; - apr_status_t rv; - char *dirpath; - const char *testroot; - const char *testpath; - - /* If we're at the root of the URL space, then there is no parent. */ - if (strcmp(resource->uri, "/") == 0) { - *result_parent = NULL; - return NULL; - } - - /* If given resource is root, then there is no parent. - * Unless we can retrieve the filepath root, this is - * intendend to fail. If we split the root and - * no path info remains, then we also fail. - */ - testpath = ctx->pathname; - rv = apr_filepath_root(&testroot, &testpath, 0, ctx->pool); - if ((rv != APR_SUCCESS && rv != APR_ERELATIVE) - || !testpath || !*testpath) { - *result_parent = NULL; - return NULL; - } - - /* ### optimize this into a single allocation! */ - - /* Create private resource context descriptor */ - parent_ctx = apr_pcalloc(ctx->pool, sizeof(*parent_ctx)); - - /* ### this should go away */ - parent_ctx->pool = ctx->pool; - - dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname); - if (strlen(dirpath) > 1 && dirpath[strlen(dirpath) - 1] == '/') - dirpath[strlen(dirpath) - 1] = '\0'; - parent_ctx->pathname = dirpath; - - parent_resource = apr_pcalloc(ctx->pool, sizeof(*parent_resource)); - parent_resource->info = parent_ctx; - parent_resource->collection = 1; - parent_resource->hooks = &dav_hooks_repository_fs; - parent_resource->pool = resource->pool; - - if (resource->uri != NULL) { - char *uri = ap_make_dirstr_parent(ctx->pool, resource->uri); - if (strlen(uri) > 1 && uri[strlen(uri) - 1] == '/') - uri[strlen(uri) - 1] = '\0'; - parent_resource->uri = uri; - } - - rv = apr_stat(&parent_ctx->finfo, parent_ctx->pathname, - APR_FINFO_NORM, ctx->pool); - if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) { - parent_resource->exists = 1; - } - - *result_parent = parent_resource; - return NULL; -} - -static int dav_fs_is_same_resource( - const dav_resource *res1, - const dav_resource *res2) -{ - dav_resource_private *ctx1 = res1->info; - dav_resource_private *ctx2 = res2->info; - - if (res1->hooks != res2->hooks) - return 0; - - if ((ctx1->finfo.filetype != 0) && (ctx2->finfo.filetype != 0) - && (ctx1->finfo.valid & ctx2->finfo.valid & APR_FINFO_INODE)) { - return ctx1->finfo.inode == ctx2->finfo.inode; - } - else { - return strcmp(ctx1->pathname, ctx2->pathname) == 0; - } -} - -static int dav_fs_is_parent_resource( - const dav_resource *res1, - const dav_resource *res2) -{ - dav_resource_private *ctx1 = res1->info; - dav_resource_private *ctx2 = res2->info; - apr_size_t len1 = strlen(ctx1->pathname); - apr_size_t len2; - - if (res1->hooks != res2->hooks) - return 0; - - /* it is safe to use ctx2 now */ - len2 = strlen(ctx2->pathname); - - return (len2 > len1 - && memcmp(ctx1->pathname, ctx2->pathname, len1) == 0 - && ctx2->pathname[len1] == '/'); -} - -static dav_error * dav_fs_open_stream(const dav_resource *resource, - dav_stream_mode mode, - dav_stream **stream) -{ - apr_pool_t *p = resource->info->pool; - dav_stream *ds = apr_pcalloc(p, sizeof(*ds)); - apr_int32_t flags; - apr_status_t rv; - - switch (mode) { - default: - flags = APR_READ | APR_BINARY; - break; - - case DAV_MODE_WRITE_TRUNC: - flags = APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY; - break; - case DAV_MODE_WRITE_SEEKABLE: - flags = APR_WRITE | APR_CREATE | APR_BINARY; - break; - } - - ds->p = p; - ds->pathname = resource->info->pathname; - rv = apr_file_open(&ds->f, ds->pathname, flags, APR_OS_DEFAULT, ds->p); - if (rv != APR_SUCCESS) { - return dav_new_error(p, MAP_IO2HTTP(rv), 0, - "An error occurred while opening a resource."); - } - - /* (APR registers cleanups for the fd with the pool) */ - - *stream = ds; - return NULL; -} - -static dav_error * dav_fs_close_stream(dav_stream *stream, int commit) -{ - apr_file_close(stream->f); - - if (!commit) { - if (apr_file_remove(stream->pathname, stream->p) != APR_SUCCESS) { - /* ### use a better description? */ - return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, - "There was a problem removing (rolling " - "back) the resource " - "when it was being closed."); - } - } - - return NULL; -} - -static dav_error * dav_fs_write_stream(dav_stream *stream, - const void *buf, apr_size_t bufsize) -{ - apr_status_t status; - - status = apr_file_write_full(stream->f, buf, bufsize, NULL); - if (APR_STATUS_IS_ENOSPC(status)) { - return dav_new_error(stream->p, HTTP_INSUFFICIENT_STORAGE, 0, - "There is not enough storage to write to " - "this resource."); - } - else if (status != APR_SUCCESS) { - /* ### use something besides 500? */ - return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, - "An error occurred while writing to a " - "resource."); - } - return NULL; -} - -static dav_error * dav_fs_seek_stream(dav_stream *stream, apr_off_t abs_pos) -{ - if (apr_file_seek(stream->f, APR_SET, &abs_pos) != APR_SUCCESS) { - /* ### should check whether apr_file_seek set abs_pos was set to the - * correct position? */ - /* ### use something besides 500? */ - return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not seek to specified position in the " - "resource."); - } - return NULL; -} - - -#if DEBUG_GET_HANDLER - -/* only define set_headers() and deliver() for debug purposes */ - - -static dav_error * dav_fs_set_headers(request_rec *r, - const dav_resource *resource) -{ - /* ### this function isn't really used since we have a get_pathname */ - if (!resource->exists) - return NULL; - - /* make sure the proper mtime is in the request record */ - ap_update_mtime(r, resource->info->finfo.mtime); - - /* ### note that these use r->filename rather than <resource> */ - ap_set_last_modified(r); - ap_set_etag(r); - - /* we accept byte-ranges */ - apr_table_setn(r->headers_out, "Accept-Ranges", "bytes"); - - /* set up the Content-Length header */ - ap_set_content_length(r, resource->info->finfo.size); - - /* ### how to set the content type? */ - /* ### until this is resolved, the Content-Type header is busted */ - - return NULL; -} - -static dav_error * dav_fs_deliver(const dav_resource *resource, - ap_filter_t *output) -{ - apr_pool_t *pool = resource->pool; - apr_bucket_brigade *bb; - apr_file_t *fd; - apr_status_t status; - apr_bucket *bkt; - - /* Check resource type */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR - && resource->type != DAV_RESOURCE_TYPE_VERSION - && resource->type != DAV_RESOURCE_TYPE_WORKING) { - return dav_new_error(pool, HTTP_CONFLICT, 0, - "Cannot GET this type of resource."); - } - if (resource->collection) { - return dav_new_error(pool, HTTP_CONFLICT, 0, - "There is no default response to GET for a " - "collection."); - } - - if ((status = apr_file_open(&fd, resource->info->pathname, - APR_READ | APR_BINARY, 0, - pool)) != APR_SUCCESS) { - return dav_new_error(pool, HTTP_FORBIDDEN, 0, - "File permissions deny server access."); - } - - bb = apr_brigade_create(pool, output->c->bucket_alloc); - - /* ### this does not handle large files. but this is test code anyway */ - bkt = apr_bucket_file_create(fd, 0, - (apr_size_t)resource->info->finfo.size, - pool, output->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, bkt); - - bkt = apr_bucket_eos_create(output->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, bkt); - - if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) { - return dav_new_error(pool, HTTP_FORBIDDEN, 0, - "Could not write contents to filter."); - } - - return NULL; -} - -#endif /* DEBUG_GET_HANDLER */ - - -static dav_error * dav_fs_create_collection(dav_resource *resource) -{ - dav_resource_private *ctx = resource->info; - apr_status_t status; - - status = apr_dir_make(ctx->pathname, APR_OS_DEFAULT, ctx->pool); - if (APR_STATUS_IS_ENOSPC(status)) { - return dav_new_error(ctx->pool, HTTP_INSUFFICIENT_STORAGE, 0, - "There is not enough storage to create " - "this collection."); - } - else if (APR_STATUS_IS_ENOENT(status)) { - return dav_new_error(ctx->pool, HTTP_CONFLICT, 0, - "Cannot create collection; intermediate " - "collection does not exist."); - } - else if (status != APR_SUCCESS) { - /* ### refine this error message? */ - return dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0, - "Unable to create collection."); - } - - /* update resource state to show it exists as a collection */ - resource->exists = 1; - resource->collection = 1; - - return NULL; -} - -static dav_error * dav_fs_copymove_walker(dav_walk_resource *wres, - int calltype) -{ - dav_fs_copymove_walk_ctx *ctx = wres->walk_ctx; - dav_resource_private *srcinfo = wres->resource->info; - dav_resource_private *dstinfo = ctx->res_dst->info; - dav_error *err = NULL; - - if (wres->resource->collection) { - if (calltype == DAV_CALLTYPE_POSTFIX) { - /* Postfix call for MOVE. delete the source dir. - * Note: when copying, we do not enable the postfix-traversal. - */ - /* ### we are ignoring any error here; what should we do? */ - (void) apr_dir_remove(srcinfo->pathname, ctx->pool); - } - else { - /* copy/move of a collection. Create the new, target collection */ - if (apr_dir_make(dstinfo->pathname, APR_OS_DEFAULT, - ctx->pool) != APR_SUCCESS) { - /* ### assume it was a permissions problem */ - /* ### need a description here */ - err = dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0, NULL); - } - } - } - else { - err = dav_fs_copymove_file(ctx->is_move, ctx->pool, - srcinfo->pathname, dstinfo->pathname, - &ctx->work_buf); - /* ### push a higher-level description? */ - } - - /* - ** If we have a "not so bad" error, then it might need to go into a - ** multistatus response. - ** - ** For a MOVE, it will always go into the multistatus. It could be - ** that everything has been moved *except* for the root. Using a - ** multistatus (with no errors for the other resources) will signify - ** this condition. - ** - ** For a COPY, we are traversing in a prefix fashion. If the root fails, - ** then we can just bail out now. - */ - if (err != NULL - && !ap_is_HTTP_SERVER_ERROR(err->status) - && (ctx->is_move - || !dav_fs_is_same_resource(wres->resource, ctx->root))) { - /* ### use errno to generate DAV:responsedescription? */ - dav_add_response(wres, err->status, NULL); - - /* the error is in the multistatus now. do not stop the traversal. */ - return NULL; - } - - return err; -} - -static dav_error *dav_fs_copymove_resource( - int is_move, - const dav_resource *src, - const dav_resource *dst, - int depth, - dav_response **response) -{ - dav_error *err = NULL; - dav_buffer work_buf = { 0 }; - - *response = NULL; - - /* if a collection, recursively copy/move it and its children, - * including the state dirs - */ - if (src->collection) { - dav_walk_params params = { 0 }; - dav_response *multi_status; - - params.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_HIDDEN; - params.func = dav_fs_copymove_walker; - params.pool = src->info->pool; - params.root = src; - - /* params.walk_ctx is managed by dav_fs_internal_walk() */ - - /* postfix is needed for MOVE to delete source dirs */ - if (is_move) - params.walk_type |= DAV_WALKTYPE_POSTFIX; - - /* note that we return the error OR the multistatus. never both */ - - if ((err = dav_fs_internal_walk(¶ms, depth, is_move, dst, - &multi_status)) != NULL) { - /* on a "real" error, then just punt. nothing else to do. */ - return err; - } - - if ((*response = multi_status) != NULL) { - /* some multistatus responses exist. wrap them in a 207 */ - return dav_new_error(src->info->pool, HTTP_MULTI_STATUS, 0, - "Error(s) occurred on some resources during " - "the COPY/MOVE process."); - } - - return NULL; - } - - /* not a collection */ - if ((err = dav_fs_copymove_file(is_move, src->info->pool, - src->info->pathname, dst->info->pathname, - &work_buf)) != NULL) { - /* ### push a higher-level description? */ - return err; - } - - /* copy/move properties as well */ - return dav_fs_copymoveset(is_move, src->info->pool, src, dst, &work_buf); -} - -static dav_error * dav_fs_copy_resource( - const dav_resource *src, - dav_resource *dst, - int depth, - dav_response **response) -{ - dav_error *err; - -#if DAV_DEBUG - if (src->hooks != dst->hooks) { - /* - ** ### strictly speaking, this is a design error; we should not - ** ### have reached this point. - */ - return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "DESIGN ERROR: a mix of repositories " - "was passed to copy_resource."); - } -#endif - - if ((err = dav_fs_copymove_resource(0, src, dst, depth, - response)) == NULL) { - - /* update state of destination resource to show it exists */ - dst->exists = 1; - dst->collection = src->collection; - } - - return err; -} - -static dav_error * dav_fs_move_resource( - dav_resource *src, - dav_resource *dst, - dav_response **response) -{ - dav_resource_private *srcinfo = src->info; - dav_resource_private *dstinfo = dst->info; - dav_error *err; - int can_rename = 0; - -#if DAV_DEBUG - if (src->hooks != dst->hooks) { - /* - ** ### strictly speaking, this is a design error; we should not - ** ### have reached this point. - */ - return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "DESIGN ERROR: a mix of repositories " - "was passed to move_resource."); - } -#endif - - /* determine whether a simple rename will work. - * Assume source exists, else we wouldn't get called. - */ - if (dstinfo->finfo.filetype != 0) { - if (dstinfo->finfo.device == srcinfo->finfo.device) { - /* target exists and is on the same device. */ - can_rename = 1; - } - } - else { - const char *dirpath; - apr_finfo_t finfo; - apr_status_t rv; - - /* destination does not exist, but the parent directory should, - * so try it - */ - dirpath = ap_make_dirstr_parent(dstinfo->pool, dstinfo->pathname); - /* - * XXX: If missing dev ... then what test? - * Really need a try and failover for those platforms. - * - */ - rv = apr_stat(&finfo, dirpath, APR_FINFO_DEV, dstinfo->pool); - if ((rv == APR_SUCCESS || rv == APR_INCOMPLETE) - && (finfo.valid & srcinfo->finfo.valid & APR_FINFO_DEV) - && (finfo.device == srcinfo->finfo.device)) { - can_rename = 1; - } - } - - /* if we can't simply rename, then do it the hard way... */ - if (!can_rename) { - if ((err = dav_fs_copymove_resource(1, src, dst, DAV_INFINITY, - response)) == NULL) { - /* update resource states */ - dst->exists = 1; - dst->collection = src->collection; - src->exists = 0; - src->collection = 0; - } - - return err; - } - - /* a rename should work. do it, and move properties as well */ - - /* no multistatus response */ - *response = NULL; - - /* ### APR has no rename? */ - if (apr_file_rename(srcinfo->pathname, dstinfo->pathname, - srcinfo->pool) != APR_SUCCESS) { - /* ### should have a better error than this. */ - return dav_new_error(srcinfo->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not rename resource."); - } - - /* update resource states */ - dst->exists = 1; - dst->collection = src->collection; - src->exists = 0; - src->collection = 0; - - if ((err = dav_fs_copymoveset(1, src->info->pool, - src, dst, NULL)) == NULL) { - /* no error. we're done. go ahead and return now. */ - return NULL; - } - - /* error occurred during properties move; try to put resource back */ - if (apr_file_rename(dstinfo->pathname, srcinfo->pathname, - srcinfo->pool) != APR_SUCCESS) { - /* couldn't put it back! */ - return dav_push_error(srcinfo->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "The resource was moved, but a failure " - "occurred during the move of its " - "properties. The resource could not be " - "restored to its original location. The " - "server is now in an inconsistent state.", - err); - } - - /* update resource states again */ - src->exists = 1; - src->collection = dst->collection; - dst->exists = 0; - dst->collection = 0; - - /* resource moved back, but properties may be inconsistent */ - return dav_push_error(srcinfo->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "The resource was moved, but a failure " - "occurred during the move of its properties. " - "The resource was moved back to its original " - "location, but its properties may have been " - "partially moved. The server may be in an " - "inconsistent state.", - err); -} - -static dav_error * dav_fs_delete_walker(dav_walk_resource *wres, int calltype) -{ - dav_resource_private *info = wres->resource->info; - - /* do not attempt to remove a null resource, - * or a collection with children - */ - if (wres->resource->exists && - (!wres->resource->collection || calltype == DAV_CALLTYPE_POSTFIX)) { - /* try to remove the resource */ - apr_status_t result; - - result = wres->resource->collection - ? apr_dir_remove(info->pathname, wres->pool) - : apr_file_remove(info->pathname, wres->pool); - - /* - ** If an error occurred, then add it to multistatus response. - ** Note that we add it for the root resource, too. It is quite - ** possible to delete the whole darn tree, yet fail on the root. - ** - ** (also: remember we are deleting via a postfix traversal) - */ - if (result != APR_SUCCESS) { - /* ### assume there is a permissions problem */ - - /* ### use errno to generate DAV:responsedescription? */ - dav_add_response(wres, HTTP_FORBIDDEN, NULL); - } - } - - return NULL; -} - -static dav_error * dav_fs_remove_resource(dav_resource *resource, - dav_response **response) -{ - dav_resource_private *info = resource->info; - - *response = NULL; - - /* if a collection, recursively remove it and its children, - * including the state dirs - */ - if (resource->collection) { - dav_walk_params params = { 0 }; - dav_error *err = NULL; - dav_response *multi_status; - - params.walk_type = (DAV_WALKTYPE_NORMAL - | DAV_WALKTYPE_HIDDEN - | DAV_WALKTYPE_POSTFIX); - params.func = dav_fs_delete_walker; - params.pool = info->pool; - params.root = resource; - - if ((err = dav_fs_walk(¶ms, DAV_INFINITY, - &multi_status)) != NULL) { - /* on a "real" error, then just punt. nothing else to do. */ - return err; - } - - if ((*response = multi_status) != NULL) { - /* some multistatus responses exist. wrap them in a 207 */ - return dav_new_error(info->pool, HTTP_MULTI_STATUS, 0, - "Error(s) occurred on some resources during " - "the deletion process."); - } - - /* no errors... update resource state */ - resource->exists = 0; - resource->collection = 0; - - return NULL; - } - - /* not a collection; remove the file and its properties */ - if (apr_file_remove(info->pathname, info->pool) != APR_SUCCESS) { - /* ### put a description in here */ - return dav_new_error(info->pool, HTTP_FORBIDDEN, 0, NULL); - } - - /* update resource state */ - resource->exists = 0; - resource->collection = 0; - - /* remove properties and return its result */ - return dav_fs_deleteset(info->pool, resource); -} - -/* ### move this to dav_util? */ -/* Walk recursively down through directories, * - * including lock-null resources as we go. */ -static dav_error * dav_fs_walker(dav_fs_walker_context *fsctx, int depth) -{ - const dav_walk_params *params = fsctx->params; - apr_pool_t *pool = params->pool; - dav_error *err = NULL; - int isdir = fsctx->res1.collection; - apr_finfo_t dirent; - apr_dir_t *dirp; - - /* ensure the context is prepared properly, then call the func */ - err = (*params->func)(&fsctx->wres, - isdir - ? DAV_CALLTYPE_COLLECTION - : DAV_CALLTYPE_MEMBER); - if (err != NULL) { - return err; - } - - if (depth == 0 || !isdir) { - return NULL; - } - - /* put a trailing slash onto the directory, in preparation for appending - * files to it as we discovery them within the directory */ - dav_check_bufsize(pool, &fsctx->path1, DAV_BUFFER_PAD); - fsctx->path1.buf[fsctx->path1.cur_len++] = '/'; - fsctx->path1.buf[fsctx->path1.cur_len] = '\0'; /* in pad area */ - - /* if a secondary path is present, then do that, too */ - if (fsctx->path2.buf != NULL) { - dav_check_bufsize(pool, &fsctx->path2, DAV_BUFFER_PAD); - fsctx->path2.buf[fsctx->path2.cur_len++] = '/'; - fsctx->path2.buf[fsctx->path2.cur_len] = '\0'; /* in pad area */ - } - - /* Note: the URI should ALREADY have a trailing "/" */ - - /* for this first pass of files, all resources exist */ - fsctx->res1.exists = 1; - - /* a file is the default; we'll adjust if we hit a directory */ - fsctx->res1.collection = 0; - fsctx->res2.collection = 0; - - /* open and scan the directory */ - if ((apr_dir_open(&dirp, fsctx->path1.buf, pool)) != APR_SUCCESS) { - /* ### need a better error */ - return dav_new_error(pool, HTTP_NOT_FOUND, 0, NULL); - } - while ((apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp)) == APR_SUCCESS) { - apr_size_t len; - apr_status_t status; - - len = strlen(dirent.name); - - /* avoid recursing into our current, parent, or state directories */ - if (dirent.name[0] == '.' - && (len == 1 || (dirent.name[1] == '.' && len == 2))) { - continue; - } - - if (params->walk_type & DAV_WALKTYPE_AUTH) { - /* ### need to authorize each file */ - /* ### example: .htaccess is normally configured to fail auth */ - - /* stuff in the state directory is never authorized! */ - if (!strcmp(dirent.name, DAV_FS_STATE_DIR)) { - continue; - } - } - /* skip the state dir unless a HIDDEN is performed */ - if (!(params->walk_type & DAV_WALKTYPE_HIDDEN) - && !strcmp(dirent.name, DAV_FS_STATE_DIR)) { - continue; - } - - /* append this file onto the path buffer (copy null term) */ - dav_buffer_place_mem(pool, &fsctx->path1, dirent.name, len + 1, 0); - - - /* ### Optimize me, dirent can give us what we need! */ - status = apr_lstat(&fsctx->info1.finfo, fsctx->path1.buf, - APR_FINFO_NORM, pool); - if (status != APR_SUCCESS && status != APR_INCOMPLETE) { - /* woah! where'd it go? */ - /* ### should have a better error here */ - err = dav_new_error(pool, HTTP_NOT_FOUND, 0, NULL); - break; - } - - /* copy the file to the URI, too. NOTE: we will pad an extra byte - for the trailing slash later. */ - dav_buffer_place_mem(pool, &fsctx->uri_buf, dirent.name, len + 1, 1); - - /* if there is a secondary path, then do that, too */ - if (fsctx->path2.buf != NULL) { - dav_buffer_place_mem(pool, &fsctx->path2, dirent.name, len + 1, 0); - } - - /* set up the (internal) pathnames for the two resources */ - fsctx->info1.pathname = fsctx->path1.buf; - fsctx->info2.pathname = fsctx->path2.buf; - - /* set up the URI for the current resource */ - fsctx->res1.uri = fsctx->uri_buf.buf; - - /* ### for now, only process regular files (e.g. skip symlinks) */ - if (fsctx->info1.finfo.filetype == APR_REG) { - /* call the function for the specified dir + file */ - if ((err = (*params->func)(&fsctx->wres, - DAV_CALLTYPE_MEMBER)) != NULL) { - /* ### maybe add a higher-level description? */ - break; - } - } - else if (fsctx->info1.finfo.filetype == APR_DIR) { - apr_size_t save_path_len = fsctx->path1.cur_len; - apr_size_t save_uri_len = fsctx->uri_buf.cur_len; - apr_size_t save_path2_len = fsctx->path2.cur_len; - - /* adjust length to incorporate the subdir name */ - fsctx->path1.cur_len += len; - fsctx->path2.cur_len += len; - - /* adjust URI length to incorporate subdir and a slash */ - fsctx->uri_buf.cur_len += len + 1; - fsctx->uri_buf.buf[fsctx->uri_buf.cur_len - 1] = '/'; - fsctx->uri_buf.buf[fsctx->uri_buf.cur_len] = '\0'; - - /* switch over to a collection */ - fsctx->res1.collection = 1; - fsctx->res2.collection = 1; - - /* recurse on the subdir */ - /* ### don't always want to quit on error from single child */ - if ((err = dav_fs_walker(fsctx, depth - 1)) != NULL) { - /* ### maybe add a higher-level description? */ - break; - } - - /* put the various information back */ - fsctx->path1.cur_len = save_path_len; - fsctx->path2.cur_len = save_path2_len; - fsctx->uri_buf.cur_len = save_uri_len; - - fsctx->res1.collection = 0; - fsctx->res2.collection = 0; - - /* assert: res1.exists == 1 */ - } - } - - /* ### check the return value of this? */ - apr_dir_close(dirp); - - if (err != NULL) - return err; - - if (params->walk_type & DAV_WALKTYPE_LOCKNULL) { - apr_size_t offset = 0; - - /* null terminate the directory name */ - fsctx->path1.buf[fsctx->path1.cur_len - 1] = '\0'; - - /* Include any lock null resources found in this collection */ - fsctx->res1.collection = 1; - if ((err = dav_fs_get_locknull_members(&fsctx->res1, - &fsctx->locknull_buf)) != NULL) { - /* ### maybe add a higher-level description? */ - return err; - } - - /* put a slash back on the end of the directory */ - fsctx->path1.buf[fsctx->path1.cur_len - 1] = '/'; - - /* these are all non-existant (files) */ - fsctx->res1.exists = 0; - fsctx->res1.collection = 0; - memset(&fsctx->info1.finfo, 0, sizeof(fsctx->info1.finfo)); - - while (offset < fsctx->locknull_buf.cur_len) { - apr_size_t len = strlen(fsctx->locknull_buf.buf + offset); - dav_lock *locks = NULL; - - /* - ** Append the locknull file to the paths and the URI. Note that - ** we don't have to pad the URI for a slash since a locknull - ** resource is not a collection. - */ - dav_buffer_place_mem(pool, &fsctx->path1, - fsctx->locknull_buf.buf + offset, len + 1, 0); - dav_buffer_place_mem(pool, &fsctx->uri_buf, - fsctx->locknull_buf.buf + offset, len + 1, 0); - if (fsctx->path2.buf != NULL) { - dav_buffer_place_mem(pool, &fsctx->path2, - fsctx->locknull_buf.buf + offset, - len + 1, 0); - } - - /* set up the (internal) pathnames for the two resources */ - fsctx->info1.pathname = fsctx->path1.buf; - fsctx->info2.pathname = fsctx->path2.buf; - - /* set up the URI for the current resource */ - fsctx->res1.uri = fsctx->uri_buf.buf; - - /* - ** To prevent a PROPFIND showing an expired locknull - ** resource, query the lock database to force removal - ** of both the lock entry and .locknull, if necessary.. - ** Sure, the query in PROPFIND would do this.. after - ** the locknull resource was already included in the - ** return. - ** - ** NOTE: we assume the caller has opened the lock database - ** if they have provided DAV_WALKTYPE_LOCKNULL. - */ - /* ### we should also look into opening it read-only and - ### eliding timed-out items from the walk, yet leaving - ### them in the locknull database until somebody opens - ### the thing writable. - */ - /* ### probably ought to use has_locks. note the problem - ### mentioned above, though... we would traverse this as - ### a locknull, but then a PROPFIND would load the lock - ### info, causing a timeout and the locks would not be - ### reported. Therefore, a null resource would be returned - ### in the PROPFIND. - ### - ### alternative: just load unresolved locks. any direct - ### locks will be timed out (correct). any indirect will - ### not (correct; consider if a parent timed out -- the - ### timeout routines do not walk and remove indirects; - ### even the resolve func would probably fail when it - ### tried to find a timed-out direct lock). - */ - if ((err = dav_lock_query(params->lockdb, &fsctx->res1, - &locks)) != NULL) { - /* ### maybe add a higher-level description? */ - return err; - } - - /* call the function for the specified dir + file */ - if (locks != NULL && - (err = (*params->func)(&fsctx->wres, - DAV_CALLTYPE_LOCKNULL)) != NULL) { - /* ### maybe add a higher-level description? */ - return err; - } - - offset += len + 1; - } - - /* reset the exists flag */ - fsctx->res1.exists = 1; - } - - if (params->walk_type & DAV_WALKTYPE_POSTFIX) { - /* replace the dirs' trailing slashes with null terms */ - fsctx->path1.buf[--fsctx->path1.cur_len] = '\0'; - fsctx->uri_buf.buf[--fsctx->uri_buf.cur_len] = '\0'; - if (fsctx->path2.buf != NULL) { - fsctx->path2.buf[--fsctx->path2.cur_len] = '\0'; - } - - /* this is a collection which exists */ - fsctx->res1.collection = 1; - - return (*params->func)(&fsctx->wres, DAV_CALLTYPE_POSTFIX); - } - - return NULL; -} - -static dav_error * dav_fs_internal_walk(const dav_walk_params *params, - int depth, int is_move, - const dav_resource *root_dst, - dav_response **response) -{ - dav_fs_walker_context fsctx = { 0 }; - dav_error *err; - dav_fs_copymove_walk_ctx cm_ctx = { 0 }; - -#if DAV_DEBUG - if ((params->walk_type & DAV_WALKTYPE_LOCKNULL) != 0 - && params->lockdb == NULL) { - return dav_new_error(params->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "DESIGN ERROR: walker called to walk locknull " - "resources, but a lockdb was not provided."); - } -#endif - - fsctx.params = params; - fsctx.wres.walk_ctx = params->walk_ctx; - fsctx.wres.pool = params->pool; - - /* ### zero out versioned, working, baselined? */ - - fsctx.res1 = *params->root; - fsctx.res1.pool = params->pool; - - fsctx.res1.info = &fsctx.info1; - fsctx.info1 = *params->root->info; - - /* the pathname is stored in the path1 buffer */ - dav_buffer_init(params->pool, &fsctx.path1, fsctx.info1.pathname); - fsctx.info1.pathname = fsctx.path1.buf; - - if (root_dst != NULL) { - /* internal call from the COPY/MOVE code. set it up. */ - - fsctx.wres.walk_ctx = &cm_ctx; - cm_ctx.is_move = is_move; - cm_ctx.res_dst = &fsctx.res2; - cm_ctx.root = params->root; - cm_ctx.pool = params->pool; - - fsctx.res2 = *root_dst; - fsctx.res2.exists = 0; - fsctx.res2.collection = 0; - fsctx.res2.uri = NULL; /* we don't track this */ - fsctx.res2.pool = params->pool; - - fsctx.res2.info = &fsctx.info2; - fsctx.info2 = *root_dst->info; - - /* res2 does not exist -- clear its finfo structure */ - memset(&fsctx.info2.finfo, 0, sizeof(fsctx.info2.finfo)); - - /* the pathname is stored in the path2 buffer */ - dav_buffer_init(params->pool, &fsctx.path2, fsctx.info2.pathname); - fsctx.info2.pathname = fsctx.path2.buf; - } - - /* prep the URI buffer */ - dav_buffer_init(params->pool, &fsctx.uri_buf, params->root->uri); - - /* if we have a directory, then ensure the URI has a trailing "/" */ - if (fsctx.res1.collection - && fsctx.uri_buf.buf[fsctx.uri_buf.cur_len - 1] != '/') { - - /* this will fall into the pad area */ - fsctx.uri_buf.buf[fsctx.uri_buf.cur_len++] = '/'; - fsctx.uri_buf.buf[fsctx.uri_buf.cur_len] = '\0'; - } - - /* the current resource's URI is stored in the uri_buf buffer */ - fsctx.res1.uri = fsctx.uri_buf.buf; - - /* point the callback's resource at our structure */ - fsctx.wres.resource = &fsctx.res1; - - /* always return the error, and any/all multistatus responses */ - err = dav_fs_walker(&fsctx, depth); - *response = fsctx.wres.response; - return err; -} - -static dav_error * dav_fs_walk(const dav_walk_params *params, int depth, - dav_response **response) -{ - /* always return the error, and any/all multistatus responses */ - return dav_fs_internal_walk(params, depth, 0, NULL, response); -} - -/* dav_fs_etag: Stolen from ap_make_etag. Creates a strong etag - * for file path. - * ### do we need to return weak tags sometimes? - */ -static const char *dav_fs_getetag(const dav_resource *resource) -{ - dav_resource_private *ctx = resource->info; - - if (!resource->exists) - return apr_pstrdup(ctx->pool, ""); - - if (ctx->finfo.filetype != 0) { - return apr_psprintf(ctx->pool, "\"%lx-%lx-%lx\"", - (unsigned long) ctx->finfo.inode, - (unsigned long) ctx->finfo.size, - (unsigned long) ctx->finfo.mtime); - } - - return apr_psprintf(ctx->pool, "\"%lx\"", (unsigned long) ctx->finfo.mtime); -} - -static const dav_hooks_repository dav_hooks_repository_fs = -{ - DEBUG_GET_HANDLER, /* normally: special GET handling not required */ - dav_fs_get_resource, - dav_fs_get_parent_resource, - dav_fs_is_same_resource, - dav_fs_is_parent_resource, - dav_fs_open_stream, - dav_fs_close_stream, - dav_fs_write_stream, - dav_fs_seek_stream, -#if DEBUG_GET_HANDLER - dav_fs_set_headers, - dav_fs_deliver, -#else - NULL, - NULL, -#endif - dav_fs_create_collection, - dav_fs_copy_resource, - dav_fs_move_resource, - dav_fs_remove_resource, - dav_fs_walk, - dav_fs_getetag, -}; - -static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource, - int propid, dav_prop_insert what, - apr_text_header *phdr) -{ - const char *value; - const char *s; - apr_pool_t *p = resource->info->pool; - const dav_liveprop_spec *info; - int global_ns; - - /* an HTTP-date can be 29 chars plus a null term */ - /* a 64-bit size can be 20 chars plus a null term */ - char buf[DAV_TIMEBUF_SIZE]; - - /* - ** None of FS provider properties are defined if the resource does not - ** exist. Just bail for this case. - ** - ** Even though we state that the FS properties are not defined, the - ** client cannot store dead values -- we deny that thru the is_writable - ** hook function. - */ - if (!resource->exists) - return DAV_PROP_INSERT_NOTDEF; - - switch (propid) { - case DAV_PROPID_creationdate: - /* - ** Closest thing to a creation date. since we don't actually - ** perform the operations that would modify ctime (after we - ** create the file), then we should be pretty safe here. - */ - dav_format_time(DAV_STYLE_ISO8601, - resource->info->finfo.ctime, - buf); - value = buf; - break; - - case DAV_PROPID_getcontentlength: - /* our property, but not defined on collection resources */ - if (resource->collection) - return DAV_PROP_INSERT_NOTDEF; - - (void) sprintf(buf, "%" APR_OFF_T_FMT, resource->info->finfo.size); - value = buf; - break; - - case DAV_PROPID_getetag: - value = dav_fs_getetag(resource); - break; - - case DAV_PROPID_getlastmodified: - dav_format_time(DAV_STYLE_RFC822, - resource->info->finfo.mtime, - buf); - value = buf; - break; - - case DAV_PROPID_FS_executable: - /* our property, but not defined on collection resources */ - if (resource->collection) - return DAV_PROP_INSERT_NOTDEF; - - /* our property, but not defined on this platform */ - if (!(resource->info->finfo.valid & APR_FINFO_UPROT)) - return DAV_PROP_INSERT_NOTDEF; - - /* the files are "ours" so we only need to check owner exec privs */ - if (resource->info->finfo.protection & APR_UEXECUTE) - value = "T"; - else - value = "F"; - break; - - default: - /* ### what the heck was this property? */ - return DAV_PROP_INSERT_NOTDEF; - } - - /* assert: value != NULL */ - - /* get the information and global NS index for the property */ - global_ns = dav_get_liveprop_info(propid, &dav_fs_liveprop_group, &info); - - /* assert: info != NULL && info->name != NULL */ - - /* DBG3("FS: inserting lp%d:%s (local %d)", ns, scan->name, scan->ns); */ - - if (what == DAV_PROP_INSERT_VALUE) { - s = apr_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR, - global_ns, info->name, value, global_ns, info->name); - } - else if (what == DAV_PROP_INSERT_NAME) { - s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name); - } - else { - /* assert: what == DAV_PROP_INSERT_SUPPORTED */ - s = apr_psprintf(p, - "<D:supported-live-property D:name=\"%s\" " - "D:namespace=\"%s\"/>" DEBUG_CR, - info->name, dav_fs_namespace_uris[info->ns]); - } - apr_text_append(p, phdr, s); - - /* we inserted what was asked for */ - return what; -} - -static int dav_fs_is_writable(const dav_resource *resource, int propid) -{ - const dav_liveprop_spec *info; - -#ifdef DAV_FS_HAS_EXECUTABLE - /* if we have the executable property, and this isn't a collection, - then the property is writable. */ - if (propid == DAV_PROPID_FS_executable && !resource->collection) - return 1; -#endif - - (void) dav_get_liveprop_info(propid, &dav_fs_liveprop_group, &info); - return info->is_writable; -} - -static dav_error *dav_fs_patch_validate(const dav_resource *resource, - const apr_xml_elem *elem, - int operation, - void **context, - int *defer_to_dead) -{ - const apr_text *cdata; - const apr_text *f_cdata; - char value; - dav_elem_private *priv = elem->priv; - - if (priv->propid != DAV_PROPID_FS_executable) { - *defer_to_dead = 1; - return NULL; - } - - if (operation == DAV_PROP_OP_DELETE) { - return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, - "The 'executable' property cannot be removed."); - } - - cdata = elem->first_cdata.first; - - /* ### hmm. this isn't actually looking at all the possible text items */ - f_cdata = elem->first_child == NULL - ? NULL - : elem->first_child->following_cdata.first; - - /* DBG3("name=%s cdata=%s f_cdata=%s",elem->name,cdata ? cdata->text : "[null]",f_cdata ? f_cdata->text : "[null]"); */ - - if (cdata == NULL) { - if (f_cdata == NULL) { - return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, - "The 'executable' property expects a single " - "character, valued 'T' or 'F'. There was no " - "value submitted."); - } - cdata = f_cdata; - } - else if (f_cdata != NULL) - goto too_long; - - if (cdata->next != NULL || strlen(cdata->text) != 1) - goto too_long; - - value = cdata->text[0]; - if (value != 'T' && value != 'F') { - return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, - "The 'executable' property expects a single " - "character, valued 'T' or 'F'. The value " - "submitted is invalid."); - } - - *context = (void *)(value == 'T'); - - return NULL; - - too_long: - return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, - "The 'executable' property expects a single " - "character, valued 'T' or 'F'. The value submitted " - "has too many characters."); - -} - -static dav_error *dav_fs_patch_exec(const dav_resource *resource, - const apr_xml_elem *elem, - int operation, - void *context, - dav_liveprop_rollback **rollback_ctx) -{ - int value = context != NULL; - apr_fileperms_t perms = resource->info->finfo.protection; - int old_value = (perms & APR_UEXECUTE) != 0; - - /* assert: prop == executable. operation == SET. */ - - /* don't do anything if there is no change. no rollback info either. */ - /* DBG2("new value=%d (old=%d)", value, old_value); */ - if (value == old_value) - return NULL; - - perms &= ~APR_UEXECUTE; - if (value) - perms |= APR_UEXECUTE; - - if (apr_file_perms_set(resource->info->pathname, perms) != APR_SUCCESS) { - return dav_new_error(resource->info->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not set the executable flag of the " - "target resource."); - } - - /* update the resource and set up the rollback context */ - resource->info->finfo.protection = perms; - *rollback_ctx = (dav_liveprop_rollback *)old_value; - - return NULL; -} - -static void dav_fs_patch_commit(const dav_resource *resource, - int operation, - void *context, - dav_liveprop_rollback *rollback_ctx) -{ - /* nothing to do */ -} - -static dav_error *dav_fs_patch_rollback(const dav_resource *resource, - int operation, - void *context, - dav_liveprop_rollback *rollback_ctx) -{ - apr_fileperms_t perms = resource->info->finfo.protection & ~APR_UEXECUTE; - int value = rollback_ctx != NULL; - - /* assert: prop == executable. operation == SET. */ - - /* restore the executable bit */ - if (value) - perms |= APR_UEXECUTE; - - if (apr_file_perms_set(resource->info->pathname, perms) != APR_SUCCESS) { - return dav_new_error(resource->info->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "After a failure occurred, the resource's " - "executable flag could not be restored."); - } - - /* restore the resource's state */ - resource->info->finfo.protection = perms; - - return NULL; -} - - -static const dav_hooks_liveprop dav_hooks_liveprop_fs = -{ - dav_fs_insert_prop, - dav_fs_is_writable, - dav_fs_namespace_uris, - dav_fs_patch_validate, - dav_fs_patch_exec, - dav_fs_patch_commit, - dav_fs_patch_rollback -}; - -static const dav_provider dav_fs_provider = -{ - &dav_hooks_repository_fs, - &dav_hooks_db_dbm, - &dav_hooks_locks_fs, - NULL, /* vsn */ - NULL, /* binding */ - NULL, /* search */ - - NULL /* ctx */ -}; - -void dav_fs_gather_propsets(apr_array_header_t *uris) -{ -#ifdef DAV_FS_HAS_EXECUTABLE - *(const char **)apr_array_push(uris) = - "<http://apache.org/dav/propset/fs/1>"; -#endif -} - -int dav_fs_find_liveprop(const dav_resource *resource, - const char *ns_uri, const char *name, - const dav_hooks_liveprop **hooks) -{ - /* don't try to find any liveprops if this isn't "our" resource */ - if (resource->hooks != &dav_hooks_repository_fs) - return 0; - return dav_do_find_liveprop(ns_uri, name, &dav_fs_liveprop_group, hooks); -} - -void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource, - dav_prop_insert what, apr_text_header *phdr) -{ - /* don't insert any liveprops if this isn't "our" resource */ - if (resource->hooks != &dav_hooks_repository_fs) - return; - - if (!resource->exists) { - /* a lock-null resource */ - /* - ** ### technically, we should insert empty properties. dunno offhand - ** ### what part of the spec said this, but it was essentially thus: - ** ### "the properties should be defined, but may have no value". - */ - return; - } - - (void) dav_fs_insert_prop(resource, DAV_PROPID_creationdate, - what, phdr); - (void) dav_fs_insert_prop(resource, DAV_PROPID_getcontentlength, - what, phdr); - (void) dav_fs_insert_prop(resource, DAV_PROPID_getlastmodified, - what, phdr); - (void) dav_fs_insert_prop(resource, DAV_PROPID_getetag, - what, phdr); - -#ifdef DAV_FS_HAS_EXECUTABLE - /* Only insert this property if it is defined for this platform. */ - (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_executable, - what, phdr); -#endif - - /* ### we know the others aren't defined as liveprops */ -} - -void dav_fs_register(apr_pool_t *p) -{ - /* register the namespace URIs */ - dav_register_liveprop_group(p, &dav_fs_liveprop_group); - - /* register the repository provider */ - dav_register_provider(p, "filesystem", &dav_fs_provider); -} diff --git a/rubbos/app/httpd-2.0.64/modules/dav/fs/repos.h b/rubbos/app/httpd-2.0.64/modules/dav/fs/repos.h deleted file mode 100644 index d7962d56..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/fs/repos.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** Declarations for the filesystem repository implementation -*/ - -#ifndef _DAV_FS_REPOS_H_ -#define _DAV_FS_REPOS_H_ - -/* the subdirectory to hold all DAV-related information for a directory */ -#define DAV_FS_STATE_DIR ".DAV" -#define DAV_FS_STATE_FILE_FOR_DIR ".state_for_dir" -#define DAV_FS_LOCK_NULL_FILE ".locknull" - - -/* ensure that our state subdirectory is present */ -void dav_fs_ensure_state_dir(apr_pool_t *p, const char *dirname); - -/* return the storage pool associated with a resource */ -apr_pool_t *dav_fs_pool(const dav_resource *resource); - -/* return the full pathname for a resource */ -const char *dav_fs_pathname(const dav_resource *resource); - -/* return the directory and filename for a resource */ -dav_error * dav_fs_dir_file_name(const dav_resource *resource, - const char **dirpath, - const char **fname); - -/* return the list of locknull members in this resource's directory */ -dav_error * dav_fs_get_locknull_members(const dav_resource *resource, - dav_buffer *pbuf); - - -/* DBM functions used by the repository and locking providers */ -extern const dav_hooks_db dav_hooks_db_dbm; - -dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro, - dav_db **pdb); -void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname, - const char **state1, const char **state2); -dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key); -dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value); -dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue); -void dav_dbm_freedatum(dav_db *db, apr_datum_t data); -int dav_dbm_exists(dav_db *db, apr_datum_t key); -void dav_dbm_close(dav_db *db); - -/* where is the lock database located? */ -const char *dav_get_lockdb_path(const request_rec *r); - -const dav_hooks_locks *dav_fs_get_lock_hooks(request_rec *r); -const dav_hooks_propdb *dav_fs_get_propdb_hooks(request_rec *r); - -void dav_fs_gather_propsets(apr_array_header_t *uris); -int dav_fs_find_liveprop(const dav_resource *resource, - const char *ns_uri, const char *name, - const dav_hooks_liveprop **hooks); -void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource, - dav_prop_insert what, apr_text_header *phdr); - -void dav_fs_register(apr_pool_t *p); - -#endif /* _DAV_FS_REPOS_H_ */ diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/.deps b/rubbos/app/httpd-2.0.64/modules/dav/main/.deps deleted file mode 100644 index e69de29b..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/.deps +++ /dev/null diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/Makefile b/rubbos/app/httpd-2.0.64/modules/dav/main/Makefile deleted file mode 100644 index 509e3634..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -top_srcdir = /bottlenecks/rubbos/app/httpd-2.0.64 -top_builddir = /bottlenecks/rubbos/app/httpd-2.0.64 -srcdir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/dav/main -builddir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/dav/main -VPATH = /bottlenecks/rubbos/app/httpd-2.0.64/modules/dav/main -# a modules Makefile has no explicit targets -- they will be defined by -# whatever modules are enabled. just grab special.mk to deal with this. -include $(top_srcdir)/build/special.mk diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/Makefile.in b/rubbos/app/httpd-2.0.64/modules/dav/main/Makefile.in deleted file mode 100644 index 7c5c149d..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/Makefile.in +++ /dev/null @@ -1,3 +0,0 @@ -# a modules Makefile has no explicit targets -- they will be defined by -# whatever modules are enabled. just grab special.mk to deal with this. -include $(top_srcdir)/build/special.mk diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/NWGNUmakefile b/rubbos/app/httpd-2.0.64/modules/dav/main/NWGNUmakefile deleted file mode 100644 index 8546d6a3..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/NWGNUmakefile +++ /dev/null @@ -1,268 +0,0 @@ -# -# Declare the sub-directories to be built here -# - -SUBDIRS = \ - $(EOLIST) - -# -# Get the 'head' of the build environment. This includes default targets and -# paths to tools -# - -include $(AP_WORK)\build\NWGNUhead.inc - -# -# build this level's files -# -# Make sure all needed macro's are defined -# - -# -# These directories will be at the beginning of the include list, followed by -# INCDIRS -# -XINCDIRS += \ - $(APR)/include \ - $(APRUTIL)/include \ - $(AP_WORK)/include \ - $(AP_WORK)/server/mpm/NetWare \ - $(NWOS) \ - $(EOLIST) - -# -# These flags will come after CFLAGS -# -XCFLAGS += \ - $(EOLIST) - -# -# These defines will come after DEFINES -# -XDEFINES += \ - $(EOLIST) - -# -# These flags will be added to the link.opt file -# -XLFLAGS += \ - $(EOLIST) - -# -# These values will be appended to the correct variables based on the value of -# RELEASE -# -ifeq "$(RELEASE)" "debug" -XINCDIRS += \ - $(EOLIST) - -XCFLAGS += \ - $(EOLIST) - -XDEFINES += \ - $(EOLIST) - -XLFLAGS += \ - $(EOLIST) -endif - -ifeq "$(RELEASE)" "noopt" -XINCDIRS += \ - $(EOLIST) - -XCFLAGS += \ - $(EOLIST) - -XDEFINES += \ - $(EOLIST) - -XLFLAGS += \ - $(EOLIST) -endif - -ifeq "$(RELEASE)" "release" -XINCDIRS += \ - $(EOLIST) - -XCFLAGS += \ - $(EOLIST) - -XDEFINES += \ - $(EOLIST) - -XLFLAGS += \ - $(EOLIST) -endif - -# -# These are used by the link target if an NLM is being generated -# This is used by the link 'name' directive to name the nlm. If left blank -# TARGET_nlm (see below) will be used. -# -NLM_NAME = mod_DAV - -# -# This is used by the link '-desc ' directive. -# If left blank, NLM_NAME will be used. -# -NLM_DESCRIPTION = Apache $(VERSION_STR) DAV module - -# -# This is used by the '-threadname' directive. If left blank, -# NLM_NAME Thread will be used. -# -NLM_THREAD_NAME = mod_DAV - -# -# If this is specified, it will override VERSION value in -# $(AP_WORK)\build\NWGNUenvironment.inc -# -NLM_VERSION = - -# -# If this is specified, it will override the default of 64K -# -NLM_STACK_SIZE = 65536 - - -# -# If this is specified it will be used by the link '-entry' directive -# -NLM_ENTRY_SYM = _LibCPrelude - -# -# If this is specified it will be used by the link '-exit' directive -# -NLM_EXIT_SYM = _LibCPostlude - -# -# If this is specified it will be used by the link '-check' directive -# -NLM_CHECK_SYM = - -# -# If this is specified it will be used by the link '-flags' directive -# -NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION - -# -# If this is specified it will be linked in with the XDCData option in the def -# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled -# by setting APACHE_UNIPROC in the environment -# -XDCDATA = - -# -# Declare all target files (you must add your files here) -# - -# -# If there is an NLM target, put it here -# -TARGET_nlm = \ - $(OBJDIR)/mod_dav.nlm \ - $(EOLIST) - -# -# If there is an LIB target, put it here -# -TARGET_lib = \ - $(EOLIST) - -# -# These are the OBJ files needed to create the NLM target above. -# Paths must all use the '/' character -# -FILES_nlm_objs = \ - $(OBJDIR)/mod_dav.o \ - $(OBJDIR)/props.o \ - $(OBJDIR)/util.o \ - $(OBJDIR)/util_lock.o \ - $(OBJDIR)/liveprop.o \ - $(OBJDIR)/providers.o \ - $(OBJDIR)/std_liveprop.o \ - $(EOLIST) - -# -# These are the LIB files needed to create the NLM target above. -# These will be added as a library command in the link.opt file. -# -FILES_nlm_libs = \ - libcpre.o \ - $(EOLIST) - -# -# These are the modules that the above NLM target depends on to load. -# These will be added as a module command in the link.opt file. -# -FILES_nlm_modules = \ - Apache2 \ - Libc \ - $(EOLIST) - -# -# If the nlm has a msg file, put it's path here -# -FILE_nlm_msg = - -# -# If the nlm has a hlp file put it's path here -# -FILE_nlm_hlp = - -# -# If this is specified, it will override $(NWOS)\copyright.txt. -# -FILE_nlm_copyright = - -# -# Any additional imports go here -# -FILES_nlm_Ximports = \ - @libc.imp \ - @$(APR)/aprlib.imp \ - @httpd.imp \ - $(EOLIST) - -# -# Any symbols exported to here -# -FILES_nlm_exports = \ - dav_module \ - @dav.imp \ - $(EOLIST) - -# -# These are the OBJ files needed to create the LIB target above. -# Paths must all use the '/' character -# -FILES_lib_objs = \ - $(EOLIST) - -# -# implement targets and dependancies (leave this section alone) -# - -libs :: $(OBJDIR) $(TARGET_lib) - -nlms :: libs $(TARGET_nlm) - -# -# Updated this target to create necessary directories and copy files to the -# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) -# -install :: nlms FORCE - copy $(OBJDIR)\mod_dav.nlm $(INSTALL)\Apache2\modules\*.* - -# -# Any specialized rules here -# - -# -# Include the 'tail' makefile that has targets that depend on variables defined -# in this makefile -# - -include $(AP_WORK)\build\NWGNUtail.inc - - diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/config5.m4 b/rubbos/app/httpd-2.0.64/modules/dav/main/config5.m4 deleted file mode 100644 index fa2eee47..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/config5.m4 +++ /dev/null @@ -1,22 +0,0 @@ -dnl modules enabled in this directory by default - -APACHE_MODPATH_INIT(dav/main) - -dav_objects="mod_dav.lo props.lo util.lo util_lock.lo liveprop.lo providers.lo std_liveprop.lo" - -if test "$enable_http" = "no"; then - dav_enable=no -else - dav_enable=most -fi - -APACHE_MODULE(dav, WebDAV protocol handling, $dav_objects, , $dav_enable) - -if test "$dav_enable" != "no" -o "$enable_dav" != "no"; then - apache_need_expat=yes - - APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) -fi - - -APACHE_MODPATH_FINISH diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/dav.imp b/rubbos/app/httpd-2.0.64/modules/dav/main/dav.imp deleted file mode 100644 index 88b306da..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/dav.imp +++ /dev/null @@ -1,64 +0,0 @@ - - (mod_dav) - dav_add_all_liveprop_xmlns, - dav_add_lock, - dav_add_response, - dav_add_vary_header, - dav_auto_checkin, - dav_auto_checkout, - dav_buffer_append, - dav_buffer_init, - dav_buffer_place, - dav_buffer_place_mem, - dav_check_bufsize, - dav_close_propdb, - dav_core_find_liveprop, - dav_core_insert_all_liveprops, - dav_core_register_uris, - dav_do_find_liveprop, - dav_find_child, - dav_get_allprops, - dav_get_binding_hooks, - dav_get_depth, - dav_get_liveprop_info, - dav_get_liveprop_ns_count, - dav_get_liveprop_ns_index, - dav_get_liveprop_supported, - dav_get_lock_hooks, - dav_get_locktoken_list, - dav_get_propdb_hooks, - dav_get_props, - dav_get_resource_state, - dav_get_search_hooks, - dav_get_timeout, - dav_get_vsn_hooks, - dav_hook_find_liveprop, - dav_hook_gather_propsets, - dav_hook_insert_all_liveprops, - dav_lock_get_activelock, - dav_lock_parse_lockinfo, - dav_lock_query, - dav_lookup_provider, - dav_lookup_uri, - dav_new_error, - dav_new_error_tag, - dav_notify_created, - dav_open_propdb, - dav_prop_commit, - dav_prop_exec, - dav_prop_rollback, - dav_prop_validate, - dav_push_error, - dav_register_liveprop_group, - dav_register_provider, - dav_set_bufsize, - dav_unlock, - dav_validate_request, - dav_validate_root, - dav_xml_get_cdata, - dav_xmlns_add, - dav_xmlns_add_uri, - dav_xmlns_create, - dav_xmlns_generate, - dav_xmlns_get_prefix, - dav_xmlns_get_uri diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/liveprop.c b/rubbos/app/httpd-2.0.64/modules/dav/main/liveprop.c deleted file mode 100644 index 88461a80..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/liveprop.c +++ /dev/null @@ -1,140 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apr_pools.h" -#include "apr_hash.h" -#include "apr_errno.h" -#include "apr_strings.h" -#include "util_xml.h" /* for apr_text_header */ -#include "mod_dav.h" - - -static apr_hash_t *dav_liveprop_uris = NULL; -static int dav_liveprop_count = 0; - - -static apr_status_t dav_cleanup_liveprops(void *ctx) -{ - dav_liveprop_uris = NULL; - dav_liveprop_count = 0; - return APR_SUCCESS; -} - -static void dav_register_liveprop_namespace(apr_pool_t *p, const char *uri) -{ - int value; - - if (dav_liveprop_uris == NULL) { - dav_liveprop_uris = apr_hash_make(p); - apr_pool_cleanup_register(p, NULL, dav_cleanup_liveprops, apr_pool_cleanup_null); - } - - value = (int)apr_hash_get(dav_liveprop_uris, uri, APR_HASH_KEY_STRING); - if (value != 0) { - /* already registered */ - return; - } - - /* start at 1, and count up */ - apr_hash_set(dav_liveprop_uris, uri, APR_HASH_KEY_STRING, - (void *)++dav_liveprop_count); -} - -DAV_DECLARE(int) dav_get_liveprop_ns_index(const char *uri) -{ - return (int)apr_hash_get(dav_liveprop_uris, uri, APR_HASH_KEY_STRING); -} - -DAV_DECLARE(int) dav_get_liveprop_ns_count(void) -{ - return dav_liveprop_count; -} - -DAV_DECLARE(void) dav_add_all_liveprop_xmlns(apr_pool_t *p, - apr_text_header *phdr) -{ - apr_hash_index_t *idx = apr_hash_first(p, dav_liveprop_uris); - - for ( ; idx != NULL; idx = apr_hash_next(idx) ) { - const void *key; - void *val; - const char *s; - - apr_hash_this(idx, &key, NULL, &val); - - s = apr_psprintf(p, " xmlns:lp%d=\"%s\"", (int)val, (const char *)key); - apr_text_append(p, phdr, s); - } -} - -DAV_DECLARE(int) dav_do_find_liveprop(const char *ns_uri, const char *name, - const dav_liveprop_group *group, - const dav_hooks_liveprop **hooks) -{ - const char * const *uris = group->namespace_uris; - const dav_liveprop_spec *scan; - int ns; - - /* first: locate the namespace in the namespace table */ - for (ns = 0; uris[ns] != NULL; ++ns) - if (strcmp(ns_uri, uris[ns]) == 0) - break; - if (uris[ns] == NULL) { - /* not our property (the namespace matched none of ours) */ - return 0; - } - - /* second: look for the property in the liveprop specs */ - for (scan = group->specs; scan->name != NULL; ++scan) - if (ns == scan->ns && strcmp(name, scan->name) == 0) { - *hooks = group->hooks; - return scan->propid; - } - - /* not our property (same namespace, but no matching prop name) */ - return 0; -} - -DAV_DECLARE(int) dav_get_liveprop_info(int propid, - const dav_liveprop_group *group, - const dav_liveprop_spec **info) -{ - const dav_liveprop_spec *scan; - - for (scan = group->specs; scan->name != NULL; ++scan) { - if (scan->propid == propid) { - *info = scan; - - /* map the provider-local NS into a global NS index */ - return dav_get_liveprop_ns_index(group->namespace_uris[scan->ns]); - } - } - - /* assert: should not reach this point */ - *info = NULL; - return 0; -} - -DAV_DECLARE(void) dav_register_liveprop_group(apr_pool_t *p, - const dav_liveprop_group *group) -{ - /* register the namespace URIs */ - const char * const * uris = group->namespace_uris; - - for ( ; *uris != NULL; ++uris) { - dav_register_liveprop_namespace(p, *uris); - } -} diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.c b/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.c deleted file mode 100644 index 3d3b47bb..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.c +++ /dev/null @@ -1,4834 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* - * DAV extension module for Apache 2.0.* - * - * This module is repository-independent. It depends on hooks provided by a - * repository implementation. - * - * APACHE ISSUES: - * - within a DAV hierarchy, if an unknown method is used and we default - * to Apache's implementation, it sends back an OPTIONS with the wrong - * set of methods -- there is NO HOOK for us. - * therefore: we need to manually handle the HTTP_METHOD_NOT_ALLOWED - * and HTTP_NOT_IMPLEMENTED responses (not ap_send_error_response). - * - process_mkcol_body() had to dup code from ap_setup_client_block(). - * - it would be nice to get status lines from Apache for arbitrary - * status codes - * - it would be nice to be able to extend Apache's set of response - * codes so that it doesn't return 500 when an unknown code is placed - * into r->status. - * - http_vhost functions should apply "const" to their params - * - * DESIGN NOTES: - * - For PROPFIND, we batch up the entire response in memory before - * sending it. We may want to reorganize around sending the information - * as we suck it in from the propdb. Alternatively, we should at least - * generate a total Content-Length if we're going to buffer in memory - * so that we can keep the connection open. - */ - -#include "apr_strings.h" -#include "apr_lib.h" /* for apr_is* */ - -#define APR_WANT_STRFUNC -#include "apr_want.h" - -#include "httpd.h" -#include "http_config.h" -#include "http_core.h" -#include "http_log.h" -#include "http_main.h" -#include "http_protocol.h" -#include "http_request.h" -#include "util_script.h" - -#include "mod_dav.h" - - -/* ### what is the best way to set this? */ -#define DAV_DEFAULT_PROVIDER "filesystem" - -/* used to denote that mod_dav will be handling this request */ -#define DAV_HANDLER_NAME "dav-handler" - -enum { - DAV_ENABLED_UNSET = 0, - DAV_ENABLED_OFF, - DAV_ENABLED_ON -}; - -/* per-dir configuration */ -typedef struct { - const char *provider_name; - const dav_provider *provider; - const char *dir; - int locktimeout; - int allow_depthinfinity; - -} dav_dir_conf; - -/* per-server configuration */ -typedef struct { - int unused; - -} dav_server_conf; - -#define DAV_INHERIT_VALUE(parent, child, field) \ - ((child)->field ? (child)->field : (parent)->field) - - -/* forward-declare for use in configuration lookup */ -extern module DAV_DECLARE_DATA dav_module; - -/* DAV methods */ -enum { - DAV_M_BIND = 0, - DAV_M_SEARCH, - DAV_M_LAST -}; -static int dav_methods[DAV_M_LAST]; - - -static int dav_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, - server_rec *s) -{ - /* DBG0("dav_init_handler"); */ - - /* Register DAV methods */ - dav_methods[DAV_M_BIND] = ap_method_register(p, "BIND"); - dav_methods[DAV_M_SEARCH] = ap_method_register(p, "SEARCH"); - - ap_add_version_component(p, "DAV/2"); - - return OK; -} - -static void *dav_create_server_config(apr_pool_t *p, server_rec *s) -{ - dav_server_conf *newconf; - - newconf = (dav_server_conf *)apr_pcalloc(p, sizeof(*newconf)); - - /* ### this isn't used at the moment... */ - - return newconf; -} - -static void *dav_merge_server_config(apr_pool_t *p, void *base, void *overrides) -{ -#if 0 - dav_server_conf *child = overrides; -#endif - dav_server_conf *newconf; - - newconf = (dav_server_conf *)apr_pcalloc(p, sizeof(*newconf)); - - /* ### nothing to merge right now... */ - - return newconf; -} - -static void *dav_create_dir_config(apr_pool_t *p, char *dir) -{ - /* NOTE: dir==NULL creates the default per-dir config */ - - dav_dir_conf *conf; - - conf = (dav_dir_conf *)apr_pcalloc(p, sizeof(*conf)); - - /* clean up the directory to remove any trailing slash */ - if (dir != NULL) { - char *d; - apr_size_t l; - - d = apr_pstrdup(p, dir); - l = strlen(d); - if (l > 1 && d[l - 1] == '/') - d[l - 1] = '\0'; - conf->dir = d; - } - - return conf; -} - -static void *dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides) -{ - dav_dir_conf *parent = base; - dav_dir_conf *child = overrides; - dav_dir_conf *newconf = (dav_dir_conf *)apr_pcalloc(p, sizeof(*newconf)); - - /* DBG3("dav_merge_dir_config: new=%08lx base=%08lx overrides=%08lx", - (long)newconf, (long)base, (long)overrides); */ - - newconf->provider_name = DAV_INHERIT_VALUE(parent, child, provider_name); - newconf->provider = DAV_INHERIT_VALUE(parent, child, provider); - if (parent->provider_name != NULL) { - if (child->provider_name == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "\"DAV Off\" cannot be used to turn off a subtree " - "of a DAV-enabled location."); - } - else if (strcasecmp(child->provider_name, - parent->provider_name) != 0) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "A subtree cannot specify a different DAV provider " - "than its parent."); - } - } - - newconf->locktimeout = DAV_INHERIT_VALUE(parent, child, locktimeout); - newconf->dir = DAV_INHERIT_VALUE(parent, child, dir); - newconf->allow_depthinfinity = DAV_INHERIT_VALUE(parent, child, - allow_depthinfinity); - - return newconf; -} - -static const dav_provider *dav_get_provider(request_rec *r) -{ - dav_dir_conf *conf; - - conf = ap_get_module_config(r->per_dir_config, &dav_module); - /* assert: conf->provider_name != NULL - (otherwise, DAV is disabled, and we wouldn't be here) */ - - /* assert: conf->provider != NULL - (checked when conf->provider_name is set) */ - return conf->provider; -} - -DAV_DECLARE(const dav_hooks_locks *) dav_get_lock_hooks(request_rec *r) -{ - return dav_get_provider(r)->locks; -} - -DAV_DECLARE(const dav_hooks_propdb *) dav_get_propdb_hooks(request_rec *r) -{ - return dav_get_provider(r)->propdb; -} - -DAV_DECLARE(const dav_hooks_vsn *) dav_get_vsn_hooks(request_rec *r) -{ - return dav_get_provider(r)->vsn; -} - -DAV_DECLARE(const dav_hooks_binding *) dav_get_binding_hooks(request_rec *r) -{ - return dav_get_provider(r)->binding; -} - -DAV_DECLARE(const dav_hooks_search *) dav_get_search_hooks(request_rec *r) -{ - return dav_get_provider(r)->search; -} - -/* - * Command handler for the DAV directive, which is TAKE1. - */ -static const char *dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1) -{ - dav_dir_conf *conf = (dav_dir_conf *)config; - - if (strcasecmp(arg1, "on") == 0) { - conf->provider_name = DAV_DEFAULT_PROVIDER; - } - else if (strcasecmp(arg1, "off") == 0) { - conf->provider_name = NULL; - conf->provider = NULL; - } - else { - conf->provider_name = apr_pstrdup(cmd->pool, arg1); - } - - if (conf->provider_name != NULL) { - /* lookup and cache the actual provider now */ - conf->provider = dav_lookup_provider(conf->provider_name); - - if (conf->provider == NULL) { - /* by the time they use it, the provider should be loaded and - registered with us. */ - return apr_psprintf(cmd->pool, - "Unknown DAV provider: %s", - conf->provider_name); - } - } - - return NULL; -} - -/* - * Command handler for the DAVDepthInfinity directive, which is FLAG. - */ -static const char *dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config, - int arg) -{ - dav_dir_conf *conf = (dav_dir_conf *)config; - - if (arg) - conf->allow_depthinfinity = DAV_ENABLED_ON; - else - conf->allow_depthinfinity = DAV_ENABLED_OFF; - return NULL; -} - -/* - * Command handler for DAVMinTimeout directive, which is TAKE1 - */ -static const char *dav_cmd_davmintimeout(cmd_parms *cmd, void *config, - const char *arg1) -{ - dav_dir_conf *conf = (dav_dir_conf *)config; - - conf->locktimeout = atoi(arg1); - if (conf->locktimeout < 0) - return "DAVMinTimeout requires a non-negative integer."; - - return NULL; -} - -/* -** dav_error_response() -** -** Send a nice response back to the user. In most cases, Apache doesn't -** allow us to provide details in the body about what happened. This -** function allows us to completely specify the response body. -** -** ### this function is not logging any errors! (e.g. the body) -*/ -static int dav_error_response(request_rec *r, int status, const char *body) -{ - r->status = status; - - /* ### I really don't think this is needed; gotta test */ - r->status_line = ap_get_status_line(status); - - ap_set_content_type(r, "text/html; charset=ISO-8859-1"); - - /* begin the response now... */ - ap_rvputs(r, - DAV_RESPONSE_BODY_1, - r->status_line, - DAV_RESPONSE_BODY_2, - &r->status_line[4], - DAV_RESPONSE_BODY_3, - body, - DAV_RESPONSE_BODY_4, - ap_psignature("<hr />\n", r), - DAV_RESPONSE_BODY_5, - NULL); - - /* the response has been sent. */ - /* - * ### Use of DONE obviates logging..! - */ - return DONE; -} - - -/* - * Send a "standardized" error response based on the error's namespace & tag - */ -static int dav_error_response_tag(request_rec *r, - dav_error *err) -{ - r->status = err->status; - - /* ### I really don't think this is needed; gotta test */ - r->status_line = ap_get_status_line(err->status); - - ap_set_content_type(r, DAV_XML_CONTENT_TYPE); - - ap_rputs(DAV_XML_HEADER DEBUG_CR - "<D:error xmlns:D=\"DAV:\"", r); - - if (err->desc != NULL) { - /* ### should move this namespace somewhere (with the others!) */ - ap_rputs(" xmlns:m=\"http://apache.org/dav/xmlns\"", r); - } - - if (err->namespace != NULL) { - ap_rprintf(r, - " xmlns:C=\"%s\">" DEBUG_CR - "<C:%s/>" DEBUG_CR, - err->namespace, err->tagname); - } - else { - ap_rprintf(r, - ">" DEBUG_CR - "<D:%s/>" DEBUG_CR, err->tagname); - } - - /* here's our mod_dav specific tag: */ - if (err->desc != NULL) { - ap_rprintf(r, - "<m:human-readable errcode=\"%d\">" DEBUG_CR - "%s" DEBUG_CR - "</m:human-readable>" DEBUG_CR, - err->error_id, - apr_xml_quote_string(r->pool, err->desc, 0)); - } - - ap_rputs("</D:error>" DEBUG_CR, r); - - /* the response has been sent. */ - /* - * ### Use of DONE obviates logging..! - */ - return DONE; -} - - -/* - * Apache's URI escaping does not replace '&' since that is a valid character - * in a URI (to form a query section). We must explicitly handle it so that - * we can embed the URI into an XML document. - */ -static const char *dav_xml_escape_uri(apr_pool_t *p, const char *uri) -{ - const char *e_uri = ap_escape_uri(p, uri); - - /* check the easy case... */ - if (ap_strchr_c(e_uri, '&') == NULL) - return e_uri; - - /* there was a '&', so more work is needed... sigh. */ - - /* - * Note: this is a teeny bit of overkill since we know there are no - * '<' or '>' characters, but who cares. - */ - return apr_xml_quote_string(p, e_uri, 0); -} - - -/* Write a complete RESPONSE object out as a <DAV:repsonse> xml - element. Data is sent into brigade BB, which is auto-flushed into - OUTPUT filter stack. Use POOL for any temporary allocations. - - [Presumably the <multistatus> tag has already been written; this - routine is shared by dav_send_multistatus and dav_stream_response.] -*/ -static void dav_send_one_response(dav_response *response, - apr_bucket_brigade *bb, - ap_filter_t *output, - apr_pool_t *pool) -{ - apr_text *t = NULL; - - if (response->propresult.xmlns == NULL) { - ap_fputs(output, bb, "<D:response>"); - } - else { - ap_fputs(output, bb, "<D:response"); - for (t = response->propresult.xmlns; t; t = t->next) { - ap_fputs(output, bb, t->text); - } - ap_fputc(output, bb, '>'); - } - - ap_fputstrs(output, bb, - DEBUG_CR "<D:href>", - dav_xml_escape_uri(pool, response->href), - "</D:href>" DEBUG_CR, - NULL); - - if (response->propresult.propstats == NULL) { - /* use the Status-Line text from Apache. Note, this will - * default to 500 Internal Server Error if first->status - * is not a known (or valid) status code. - */ - ap_fputstrs(output, bb, - "<D:status>HTTP/1.1 ", - ap_get_status_line(response->status), - "</D:status>" DEBUG_CR, - NULL); - } - else { - /* assume this includes <propstat> and is quoted properly */ - for (t = response->propresult.propstats; t; t = t->next) { - ap_fputs(output, bb, t->text); - } - } - - if (response->desc != NULL) { - /* - * We supply the description, so we know it doesn't have to - * have any escaping/encoding applied to it. - */ - ap_fputstrs(output, bb, - "<D:responsedescription>", - response->desc, - "</D:responsedescription>" DEBUG_CR, - NULL); - } - - ap_fputs(output, bb, "</D:response>" DEBUG_CR); -} - - -/* Factorized helper function: prep request_rec R for a multistatus - response and write <multistatus> tag into BB, destined for - R->output_filters. Use xml NAMESPACES in initial tag, if - non-NULL. */ -static void dav_begin_multistatus(apr_bucket_brigade *bb, - request_rec *r, int status, - apr_array_header_t *namespaces) -{ - /* Set the correct status and Content-Type */ - r->status = status; - ap_set_content_type(r, DAV_XML_CONTENT_TYPE); - - /* Send the headers and actual multistatus response now... */ - ap_fputs(r->output_filters, bb, DAV_XML_HEADER DEBUG_CR - "<D:multistatus xmlns:D=\"DAV:\""); - - if (namespaces != NULL) { - int i; - - for (i = namespaces->nelts; i--; ) { - ap_fprintf(r->output_filters, bb, " xmlns:ns%d=\"%s\"", i, - APR_XML_GET_URI_ITEM(namespaces, i)); - } - } - - ap_fputs(r->output_filters, bb, ">" DEBUG_CR); -} - -/* Finish a multistatus response started by dav_begin_multistatus: */ -static apr_status_t dav_finish_multistatus(request_rec *r, - apr_bucket_brigade *bb) -{ - apr_bucket *b; - - ap_fputs(r->output_filters, bb, "</D:multistatus>" DEBUG_CR); - - /* indicate the end of the response body */ - b = apr_bucket_eos_create(r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - - /* deliver whatever might be remaining in the brigade */ - return ap_pass_brigade(r->output_filters, bb); -} - -static void dav_send_multistatus(request_rec *r, int status, - dav_response *first, - apr_array_header_t *namespaces) -{ - apr_pool_t *subpool; - apr_bucket_brigade *bb = apr_brigade_create(r->pool, - r->connection->bucket_alloc); - - dav_begin_multistatus(bb, r, status, namespaces); - - apr_pool_create(&subpool, r->pool); - - for (; first != NULL; first = first->next) { - apr_pool_clear(subpool); - dav_send_one_response(first, bb, r->output_filters, subpool); - } - apr_pool_destroy(subpool); - - dav_finish_multistatus(r, bb); -} - -/* - * dav_log_err() - * - * Write error information to the log. - */ -static void dav_log_err(request_rec *r, dav_error *err, int level) -{ - dav_error *errscan; - - /* Log the errors */ - /* ### should have a directive to log the first or all */ - for (errscan = err; errscan != NULL; errscan = errscan->prev) { - if (errscan->desc == NULL) - continue; - - if (errscan->save_errno != 0) { - errno = errscan->save_errno; - ap_log_rerror(APLOG_MARK, level, errno, r, "%s [%d, #%d]", - errscan->desc, errscan->status, errscan->error_id); - } - else { - ap_log_rerror(APLOG_MARK, level, 0, r, - "%s [%d, #%d]", - errscan->desc, errscan->status, errscan->error_id); - } - } -} - -/* - * dav_handle_err() - * - * Handle the standard error processing. <err> must be non-NULL. - * - * <response> is set by the following: - * - dav_validate_request() - * - dav_add_lock() - * - repos_hooks->remove_resource - * - repos_hooks->move_resource - * - repos_hooks->copy_resource - * - vsn_hooks->update - */ -static int dav_handle_err(request_rec *r, dav_error *err, - dav_response *response) -{ - /* log the errors */ - dav_log_err(r, err, APLOG_ERR); - - if (response == NULL) { - dav_error *stackerr = err; - - /* our error messages are safe; tell Apache this */ - apr_table_setn(r->notes, "verbose-error-to", "*"); - - /* Didn't get a multistatus response passed in, but we still - might be able to generate a standard <D:error> response. - Search the error stack for an errortag. */ - while (stackerr != NULL && stackerr->tagname == NULL) - stackerr = stackerr->prev; - - if (stackerr != NULL && stackerr->tagname != NULL) - return dav_error_response_tag(r, stackerr); - - return err->status; - } - - /* send the multistatus and tell Apache the request/response is DONE. */ - dav_send_multistatus(r, err->status, response, NULL); - return DONE; -} - -/* handy function for return values of methods that (may) create things */ -static int dav_created(request_rec *r, const char *locn, const char *what, - int replaced) -{ - const char *body; - - if (locn == NULL) { - locn = r->uri; - } - - /* did the target resource already exist? */ - if (replaced) { - /* Apache will supply a default message */ - return HTTP_NO_CONTENT; - } - - /* Per HTTP/1.1, S10.2.2: add a Location header to contain the - * URI that was created. */ - - /* Convert locn to an absolute URI, and return in Location header */ - apr_table_setn(r->headers_out, "Location", ap_construct_url(r->pool, locn, r)); - - /* ### insert an ETag header? see HTTP/1.1 S10.2.2 */ - - /* Apache doesn't allow us to set a variable body for HTTP_CREATED, so - * we must manufacture the entire response. */ - body = apr_psprintf(r->pool, "%s %s has been created.", - what, ap_escape_html(r->pool, locn)); - return dav_error_response(r, HTTP_CREATED, body); -} - -/* ### move to dav_util? */ -DAV_DECLARE(int) dav_get_depth(request_rec *r, int def_depth) -{ - const char *depth = apr_table_get(r->headers_in, "Depth"); - - if (depth == NULL) { - return def_depth; - } - - if (strcasecmp(depth, "infinity") == 0) { - return DAV_INFINITY; - } - else if (strcmp(depth, "0") == 0) { - return 0; - } - else if (strcmp(depth, "1") == 0) { - return 1; - } - - /* The caller will return an HTTP_BAD_REQUEST. This will augment the - * default message that Apache provides. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "An invalid Depth header was specified."); - return -1; -} - -static int dav_get_overwrite(request_rec *r) -{ - const char *overwrite = apr_table_get(r->headers_in, "Overwrite"); - - if (overwrite == NULL) { - return 1; /* default is "T" */ - } - - if ((*overwrite == 'F' || *overwrite == 'f') && overwrite[1] == '\0') { - return 0; - } - - if ((*overwrite == 'T' || *overwrite == 't') && overwrite[1] == '\0') { - return 1; - } - - /* The caller will return an HTTP_BAD_REQUEST. This will augment the - * default message that Apache provides. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "An invalid Overwrite header was specified."); - return -1; -} - -/* resolve a request URI to a resource descriptor. - * - * If label_allowed != 0, then allow the request target to be altered by - * a Label: header. - * - * If use_checked_in is true, then the repository provider should return - * the resource identified by the DAV:checked-in property of the resource - * identified by the Request-URI. - */ -static dav_error *dav_get_resource(request_rec *r, int label_allowed, - int use_checked_in, dav_resource **res_p) -{ - dav_dir_conf *conf; - const char *label = NULL; - dav_error *err; - - /* if the request target can be overridden, get any target selector */ - if (label_allowed) { - label = apr_table_get(r->headers_in, "label"); - } - - conf = ap_get_module_config(r->per_dir_config, &dav_module); - /* assert: conf->provider != NULL */ - - /* resolve the resource */ - err = (*conf->provider->repos->get_resource)(r, conf->dir, - label, use_checked_in, - res_p); - if (err != NULL) { - err = dav_push_error(r->pool, err->status, 0, - "Could not fetch resource information.", err); - return err; - } - - /* Note: this shouldn't happen, but just be sure... */ - if (*res_p == NULL) { - /* ### maybe use HTTP_INTERNAL_SERVER_ERROR */ - return dav_new_error(r->pool, HTTP_NOT_FOUND, 0, - apr_psprintf(r->pool, - "The provider did not define a " - "resource for %s.", - ap_escape_html(r->pool, r->uri))); - } - - /* ### hmm. this doesn't feel like the right place or thing to do */ - /* if there were any input headers requiring a Vary header in the response, - * add it now */ - dav_add_vary_header(r, r, *res_p); - - return NULL; -} - -static dav_error * dav_open_lockdb(request_rec *r, int ro, dav_lockdb **lockdb) -{ - const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); - - if (hooks == NULL) { - *lockdb = NULL; - return NULL; - } - - /* open the thing lazily */ - return (*hooks->open_lockdb)(r, ro, 0, lockdb); -} - -static int dav_parse_range(request_rec *r, - apr_off_t *range_start, apr_off_t *range_end) -{ - const char *range_c; - char *range; - char *dash; - char *slash; - - range_c = apr_table_get(r->headers_in, "content-range"); - if (range_c == NULL) - return 0; - - range = apr_pstrdup(r->pool, range_c); - if (strncasecmp(range, "bytes ", 6) != 0 - || (dash = ap_strchr(range, '-')) == NULL - || (slash = ap_strchr(range, '/')) == NULL) { - /* malformed header. ignore it (per S14.16 of RFC2616) */ - return 0; - } - - *dash = *slash = '\0'; - - *range_start = apr_atoi64(range + 6); - *range_end = apr_atoi64(dash + 1); - - if (*range_end < *range_start - || (slash[1] != '*' && apr_atoi64(slash + 1) <= *range_end)) { - /* invalid range. ignore it (per S14.16 of RFC2616) */ - return 0; - } - - /* we now have a valid range */ - return 1; -} - -/* handle the GET method */ -static int dav_method_get(request_rec *r) -{ - dav_resource *resource; - dav_error *err; - - /* This method should only be called when the resource is not - * visible to Apache. We will fetch the resource from the repository, - * then create a subrequest for Apache to handle. - */ - err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* set up the HTTP headers for the response */ - if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - "Unable to set up HTTP headers.", - err); - return dav_handle_err(r, err, NULL); - } - - if (r->header_only) { - return DONE; - } - - /* okay... time to deliver the content */ - if ((err = (*resource->hooks->deliver)(resource, - r->output_filters)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - "Unable to deliver content.", - err); - return dav_handle_err(r, err, NULL); - } - - return DONE; -} - -/* validate resource/locks on POST, then pass to the default handler */ -static int dav_method_post(request_rec *r) -{ - dav_resource *resource; - dav_error *err; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* Note: depth == 0. Implies no need for a multistatus response. */ - if ((err = dav_validate_request(r, resource, 0, NULL, NULL, - DAV_VALIDATE_RESOURCE, NULL)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - return DECLINED; -} - -/* handle the PUT method */ -static int dav_method_put(request_rec *r) -{ - dav_resource *resource; - int resource_state; - dav_auto_version_info av_info; - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - const char *body; - dav_error *err; - dav_error *err2; - dav_stream_mode mode; - dav_stream *stream; - dav_response *multi_response; - int has_range; - apr_off_t range_start; - apr_off_t range_end; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* If not a file or collection resource, PUT not allowed */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR - && resource->type != DAV_RESOURCE_TYPE_WORKING) { - body = apr_psprintf(r->pool, - "Cannot create resource %s with PUT.", - ap_escape_html(r->pool, r->uri)); - return dav_error_response(r, HTTP_CONFLICT, body); - } - - /* Cannot PUT a collection */ - if (resource->collection) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot PUT to a collection."); - - } - - resource_state = dav_get_resource_state(r, resource); - - /* - * Note: depth == 0 normally requires no multistatus response. However, - * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI - * other than the Request-URI, thereby requiring a multistatus. - * - * If the resource does not exist (DAV_RESOURCE_NULL), then we must - * check the resource *and* its parent. If the resource exists or is - * a locknull resource, then we check only the resource. - */ - if ((err = dav_validate_request(r, resource, 0, NULL, &multi_response, - resource_state == DAV_RESOURCE_NULL ? - DAV_VALIDATE_PARENT : - DAV_VALIDATE_RESOURCE, NULL)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, multi_response); - } - - /* make sure the resource can be modified (if versioning repository) */ - if ((err = dav_auto_checkout(r, resource, - 0 /* not parent_only */, - &av_info)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - /* truncate and rewrite the file unless we see a Content-Range */ - mode = DAV_MODE_WRITE_TRUNC; - - has_range = dav_parse_range(r, &range_start, &range_end); - if (has_range) { - mode = DAV_MODE_WRITE_SEEKABLE; - } - - /* Create the new file in the repository */ - if ((err = (*resource->hooks->open_stream)(resource, mode, - &stream)) != NULL) { - /* ### assuming FORBIDDEN is probably not quite right... */ - err = dav_push_error(r->pool, HTTP_FORBIDDEN, 0, - apr_psprintf(r->pool, - "Unable to PUT new contents for %s.", - ap_escape_html(r->pool, r->uri)), - err); - } - - if (err == NULL && has_range) { - /* a range was provided. seek to the start */ - err = (*resource->hooks->seek_stream)(stream, range_start); - } - - if (err == NULL) { - apr_bucket_brigade *bb; - apr_bucket *b; - int seen_eos = 0; - - bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - - do { - apr_status_t rc; - - rc = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, - APR_BLOCK_READ, DAV_READ_BLOCKSIZE); - - if (rc != APR_SUCCESS) { - err = dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not get next bucket brigade"); - break; - } - - for (b = APR_BRIGADE_FIRST(bb); - b != APR_BRIGADE_SENTINEL(bb); - b = APR_BUCKET_NEXT(b)) - { - const char *data; - apr_size_t len; - - if (APR_BUCKET_IS_EOS(b)) { - seen_eos = 1; - break; - } - - if (APR_BUCKET_IS_METADATA(b)) { - continue; - } - - rc = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); - if (rc != APR_SUCCESS) { - err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, - "An error occurred while reading " - "the request body."); - break; - } - - if (err == NULL) { - /* write whatever we read, until we see an error */ - err = (*resource->hooks->write_stream)(stream, data, len); - } - } - - apr_brigade_cleanup(bb); - } while (!seen_eos); - - apr_brigade_destroy(bb); - - err2 = (*resource->hooks->close_stream)(stream, - err == NULL /* commit */); - if (err2 != NULL && err == NULL) { - /* no error during the write, but we hit one at close. use it. */ - err = err2; - } - } - - /* - * Ensure that we think the resource exists now. - * ### eek. if an error occurred during the write and we did not commit, - * ### then the resource might NOT exist (e.g. dav_fs_repos.c) - */ - if (err == NULL) { - resource->exists = 1; - } - - /* restore modifiability of resources back to what they were */ - err2 = dav_auto_checkin(r, resource, err != NULL /* undo if error */, - 0 /*unlock*/, &av_info); - - /* check for errors now */ - if (err != NULL) { - return dav_handle_err(r, err, NULL); - } - - if (err2 != NULL) { - /* just log a warning */ - err2 = dav_push_error(r->pool, err2->status, 0, - "The PUT was successful, but there " - "was a problem automatically checking in " - "the resource or its parent collection.", - err2); - dav_log_err(r, err2, APLOG_WARNING); - } - - /* ### place the Content-Type and Content-Language into the propdb */ - - if (locks_hooks != NULL) { - dav_lockdb *lockdb; - - if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) { - /* The file creation was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The file was PUT successfully, but there " - "was a problem opening the lock database " - "which prevents inheriting locks from the " - "parent resources.", - err); - return dav_handle_err(r, err, NULL); - } - - /* notify lock system that we have created/replaced a resource */ - err = dav_notify_created(r, lockdb, resource, resource_state, 0); - - (*locks_hooks->close_lockdb)(lockdb); - - if (err != NULL) { - /* The file creation was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The file was PUT successfully, but there " - "was a problem updating its lock " - "information.", - err); - return dav_handle_err(r, err, NULL); - } - } - - /* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */ - - /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */ - return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS); -} - - -/* Use POOL to temporarily construct a dav_response object (from WRES - STATUS, and PROPSTATS) and stream it via WRES's ctx->brigade. */ -static void dav_stream_response(dav_walk_resource *wres, - int status, - dav_get_props_result *propstats, - apr_pool_t *pool) -{ - dav_response resp = { 0 }; - dav_walker_ctx *ctx = wres->walk_ctx; - - resp.href = wres->resource->uri; - resp.status = status; - if (propstats) { - resp.propresult = *propstats; - } - - dav_send_one_response(&resp, ctx->bb, ctx->r->output_filters, pool); -} - - -/* ### move this to dav_util? */ -DAV_DECLARE(void) dav_add_response(dav_walk_resource *wres, - int status, dav_get_props_result *propstats) -{ - dav_response *resp; - - /* just drop some data into an dav_response */ - resp = apr_pcalloc(wres->pool, sizeof(*resp)); - resp->href = apr_pstrdup(wres->pool, wres->resource->uri); - resp->status = status; - if (propstats) { - resp->propresult = *propstats; - } - - resp->next = wres->response; - wres->response = resp; -} - - -/* handle the DELETE method */ -static int dav_method_delete(request_rec *r) -{ - dav_resource *resource; - dav_auto_version_info av_info; - dav_error *err; - dav_error *err2; - dav_response *multi_response; - int result; - int depth; - - /* We don't use the request body right now, so torch it. */ - if ((result = ap_discard_request_body(r)) != OK) { - return result; - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* 2518 says that depth must be infinity only for collections. - * For non-collections, depth is ignored, unless it is an illegal value (1). - */ - depth = dav_get_depth(r, DAV_INFINITY); - - if (resource->collection && depth != DAV_INFINITY) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Depth must be \"infinity\" for DELETE of a collection."); - return HTTP_BAD_REQUEST; - } - - if (!resource->collection && depth == 1) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Depth of \"1\" is not allowed for DELETE."); - return HTTP_BAD_REQUEST; - } - - /* - ** If any resources fail the lock/If: conditions, then we must fail - ** the delete. Each of the failing resources will be listed within - ** a DAV:multistatus body, wrapped into a 424 response. - ** - ** Note that a failure on the resource itself does not generate a - ** multistatus response -- only internal members/collections. - */ - if ((err = dav_validate_request(r, resource, depth, NULL, - &multi_response, - DAV_VALIDATE_PARENT - | DAV_VALIDATE_USE_424, NULL)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not DELETE %s due to a failed " - "precondition (e.g. locks).", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - /* ### RFC 2518 s. 8.10.5 says to remove _all_ locks, not just those - * locked by the token(s) in the if_header. - */ - if ((result = dav_unlock(r, resource, NULL)) != OK) { - return result; - } - - /* if versioned resource, make sure parent is checked out */ - if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */, - &av_info)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - /* try to remove the resource */ - err = (*resource->hooks->remove_resource)(resource, &multi_response); - - /* restore writability of parent back to what it was */ - err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */, - 0 /*unlock*/, &av_info); - - /* check for errors now */ - if (err != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not DELETE %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - if (err2 != NULL) { - /* just log a warning */ - err = dav_push_error(r->pool, err2->status, 0, - "The DELETE was successful, but there " - "was a problem automatically checking in " - "the parent collection.", - err2); - dav_log_err(r, err, APLOG_WARNING); - } - - /* ### HTTP_NO_CONTENT if no body, HTTP_OK if there is a body (some day) */ - - /* Apache will supply a default error for this. */ - return HTTP_NO_CONTENT; -} - -/* generate DAV:supported-method-set OPTIONS response */ -static dav_error *dav_gen_supported_methods(request_rec *r, - const apr_xml_elem *elem, - const apr_table_t *methods, - apr_text_header *body) -{ - const apr_array_header_t *arr; - const apr_table_entry_t *elts; - apr_xml_elem *child; - apr_xml_attr *attr; - char *s; - int i; - - apr_text_append(r->pool, body, "<D:supported-method-set>" DEBUG_CR); - - if (elem->first_child == NULL) { - /* show all supported methods */ - arr = apr_table_elts(methods); - elts = (const apr_table_entry_t *)arr->elts; - - for (i = 0; i < arr->nelts; ++i) { - if (elts[i].key == NULL) - continue; - - s = apr_psprintf(r->pool, - "<D:supported-method D:name=\"%s\"/>" - DEBUG_CR, - elts[i].key); - apr_text_append(r->pool, body, s); - } - } - else { - /* check for support of specific methods */ - for (child = elem->first_child; child != NULL; child = child->next) { - if (child->ns == APR_XML_NS_DAV_ID - && strcmp(child->name, "supported-method") == 0) { - const char *name = NULL; - - /* go through attributes to find method name */ - for (attr = child->attr; attr != NULL; attr = attr->next) { - if (attr->ns == APR_XML_NS_DAV_ID - && strcmp(attr->name, "name") == 0) - name = attr->value; - } - - if (name == NULL) { - return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, - "A DAV:supported-method element " - "does not have a \"name\" attribute"); - } - - /* see if method is supported */ - if (apr_table_get(methods, name) != NULL) { - s = apr_psprintf(r->pool, - "<D:supported-method D:name=\"%s\"/>" - DEBUG_CR, - name); - apr_text_append(r->pool, body, s); - } - } - } - } - - apr_text_append(r->pool, body, "</D:supported-method-set>" DEBUG_CR); - return NULL; -} - -/* generate DAV:supported-live-property-set OPTIONS response */ -static dav_error *dav_gen_supported_live_props(request_rec *r, - const dav_resource *resource, - const apr_xml_elem *elem, - apr_text_header *body) -{ - dav_lockdb *lockdb; - dav_propdb *propdb; - apr_xml_elem *child; - apr_xml_attr *attr; - dav_error *err; - - /* open lock database, to report on supported lock properties */ - /* ### should open read-only */ - if ((err = dav_open_lockdb(r, 0, &lockdb)) != NULL) { - return dav_push_error(r->pool, err->status, 0, - "The lock database could not be opened, " - "preventing the reporting of supported lock " - "properties.", - err); - } - - /* open the property database (readonly) for the resource */ - if ((err = dav_open_propdb(r, lockdb, resource, 1, NULL, - &propdb)) != NULL) { - if (lockdb != NULL) - (*lockdb->hooks->close_lockdb)(lockdb); - - return dav_push_error(r->pool, err->status, 0, - "The property database could not be opened, " - "preventing report of supported properties.", - err); - } - - apr_text_append(r->pool, body, "<D:supported-live-property-set>" DEBUG_CR); - - if (elem->first_child == NULL) { - /* show all supported live properties */ - dav_get_props_result props = dav_get_allprops(propdb, DAV_PROP_INSERT_SUPPORTED); - body->last->next = props.propstats; - while (body->last->next != NULL) - body->last = body->last->next; - } - else { - /* check for support of specific live property */ - for (child = elem->first_child; child != NULL; child = child->next) { - if (child->ns == APR_XML_NS_DAV_ID - && strcmp(child->name, "supported-live-property") == 0) { - const char *name = NULL; - const char *nmspace = NULL; - - /* go through attributes to find name and namespace */ - for (attr = child->attr; attr != NULL; attr = attr->next) { - if (attr->ns == APR_XML_NS_DAV_ID) { - if (strcmp(attr->name, "name") == 0) - name = attr->value; - else if (strcmp(attr->name, "namespace") == 0) - nmspace = attr->value; - } - } - - if (name == NULL) { - err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, - "A DAV:supported-live-property " - "element does not have a \"name\" " - "attribute"); - break; - } - - /* default namespace to DAV: */ - if (nmspace == NULL) - nmspace = "DAV:"; - - /* check for support of property */ - dav_get_liveprop_supported(propdb, nmspace, name, body); - } - } - } - - apr_text_append(r->pool, body, "</D:supported-live-property-set>" DEBUG_CR); - - dav_close_propdb(propdb); - - if (lockdb != NULL) - (*lockdb->hooks->close_lockdb)(lockdb); - - return err; -} - -/* generate DAV:supported-report-set OPTIONS response */ -static dav_error *dav_gen_supported_reports(request_rec *r, - const dav_resource *resource, - const apr_xml_elem *elem, - const dav_hooks_vsn *vsn_hooks, - apr_text_header *body) -{ - apr_xml_elem *child; - apr_xml_attr *attr; - dav_error *err; - char *s; - - apr_text_append(r->pool, body, "<D:supported-report-set>" DEBUG_CR); - - if (vsn_hooks != NULL) { - const dav_report_elem *reports; - const dav_report_elem *rp; - - if ((err = (*vsn_hooks->avail_reports)(resource, &reports)) != NULL) { - return dav_push_error(r->pool, err->status, 0, - "DAV:supported-report-set could not be " - "determined due to a problem fetching the " - "available reports for this resource.", - err); - } - - if (reports != NULL) { - if (elem->first_child == NULL) { - /* show all supported reports */ - for (rp = reports; rp->nmspace != NULL; ++rp) { - /* Note: we presume reports->namespace is - * properly XML/URL quoted */ - s = apr_psprintf(r->pool, - "<D:supported-report D:name=\"%s\" " - "D:namespace=\"%s\"/>" DEBUG_CR, - rp->name, rp->nmspace); - apr_text_append(r->pool, body, s); - } - } - else { - /* check for support of specific report */ - for (child = elem->first_child; child != NULL; child = child->next) { - if (child->ns == APR_XML_NS_DAV_ID - && strcmp(child->name, "supported-report") == 0) { - const char *name = NULL; - const char *nmspace = NULL; - - /* go through attributes to find name and namespace */ - for (attr = child->attr; attr != NULL; attr = attr->next) { - if (attr->ns == APR_XML_NS_DAV_ID) { - if (strcmp(attr->name, "name") == 0) - name = attr->value; - else if (strcmp(attr->name, "namespace") == 0) - nmspace = attr->value; - } - } - - if (name == NULL) { - return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, - "A DAV:supported-report element " - "does not have a \"name\" attribute"); - } - - /* default namespace to DAV: */ - if (nmspace == NULL) - nmspace = "DAV:"; - - for (rp = reports; rp->nmspace != NULL; ++rp) { - if (strcmp(name, rp->name) == 0 - && strcmp(nmspace, rp->nmspace) == 0) { - /* Note: we presume reports->nmspace is - * properly XML/URL quoted - */ - s = apr_psprintf(r->pool, - "<D:supported-report " - "D:name=\"%s\" " - "D:namespace=\"%s\"/>" - DEBUG_CR, - rp->name, rp->nmspace); - apr_text_append(r->pool, body, s); - break; - } - } - } - } - } - } - } - - apr_text_append(r->pool, body, "</D:supported-report-set>" DEBUG_CR); - return NULL; -} - - -/* handle the SEARCH method */ -static int dav_method_search(request_rec *r) -{ - const dav_hooks_search *search_hooks = DAV_GET_HOOKS_SEARCH(r); - dav_resource *resource; - dav_error *err; - dav_response *multi_status; - - /* If no search provider, decline the request */ - if (search_hooks == NULL) - return DECLINED; - - /* This method should only be called when the resource is not - * visible to Apache. We will fetch the resource from the repository, - * then create a subrequest for Apache to handle. - */ - err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* set up the HTTP headers for the response */ - if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - "Unable to set up HTTP headers.", - err); - return dav_handle_err(r, err, NULL); - } - - if (r->header_only) { - return DONE; - } - - /* okay... time to search the content */ - /* Let's validate XML and process walk function - * in the hook function - */ - if ((err = (*search_hooks->search_resource)(r, &multi_status)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - /* We have results in multi_status */ - /* Should I pass namespace?? */ - dav_send_multistatus(r, HTTP_MULTI_STATUS, multi_status, NULL); - - return DONE; -} - - -/* handle the OPTIONS method */ -static int dav_method_options(request_rec *r) -{ - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r); - const dav_hooks_search *search_hooks = DAV_GET_HOOKS_SEARCH(r); - dav_resource *resource; - const char *dav_level; - char *allow; - char *s; - const apr_array_header_t *arr; - const apr_table_entry_t *elts; - apr_table_t *methods = apr_table_make(r->pool, 12); - apr_text_header vsn_options = { 0 }; - apr_text_header body = { 0 }; - apr_text *t; - int text_size; - int result; - int i; - apr_array_header_t *uri_ary; - apr_xml_doc *doc; - const apr_xml_elem *elem; - dav_error *err; - - /* resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* parse any request body */ - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - /* note: doc == NULL if no request body */ - - if (doc && !dav_validate_root(doc, "options")) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"options\" element was not found."); - return HTTP_BAD_REQUEST; - } - - /* determine which providers are available */ - dav_level = "1"; - - if (locks_hooks != NULL) { - dav_level = "1,2"; - } - - if (binding_hooks != NULL) - dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL); - - /* ### - * MSFT Web Folders chokes if length of DAV header value > 63 characters! - * To workaround that, we use separate DAV headers for versioning and - * live prop provider namespace URIs. - * ### - */ - apr_table_setn(r->headers_out, "DAV", dav_level); - - /* - * If there is a versioning provider, generate DAV headers - * for versioning options. - */ - if (vsn_hooks != NULL) { - (*vsn_hooks->get_vsn_options)(r->pool, &vsn_options); - - for (t = vsn_options.first; t != NULL; t = t->next) - apr_table_addn(r->headers_out, "DAV", t->text); - } - - /* - * Gather property set URIs from all the liveprop providers, - * and generate a separate DAV header for each URI, to avoid - * problems with long header lengths. - */ - uri_ary = apr_array_make(r->pool, 5, sizeof(const char *)); - dav_run_gather_propsets(uri_ary); - for (i = 0; i < uri_ary->nelts; ++i) { - if (((char **)uri_ary->elts)[i] != NULL) - apr_table_addn(r->headers_out, "DAV", ((char **)uri_ary->elts)[i]); - } - - /* this tells MSFT products to skip looking for FrontPage extensions */ - apr_table_setn(r->headers_out, "MS-Author-Via", "DAV"); - - /* - * Determine which methods are allowed on the resource. - * Three cases: resource is null (3), is lock-null (7.4), or exists. - * - * All cases support OPTIONS, and if there is a lock provider, LOCK. - * (Lock-) null resources also support MKCOL and PUT. - * Lock-null supports PROPFIND and UNLOCK. - * Existing resources support lots of stuff. - */ - - apr_table_addn(methods, "OPTIONS", ""); - - /* ### take into account resource type */ - switch (dav_get_resource_state(r, resource)) - { - case DAV_RESOURCE_EXISTS: - /* resource exists */ - apr_table_addn(methods, "GET", ""); - apr_table_addn(methods, "HEAD", ""); - apr_table_addn(methods, "POST", ""); - apr_table_addn(methods, "DELETE", ""); - apr_table_addn(methods, "TRACE", ""); - apr_table_addn(methods, "PROPFIND", ""); - apr_table_addn(methods, "PROPPATCH", ""); - apr_table_addn(methods, "COPY", ""); - apr_table_addn(methods, "MOVE", ""); - - if (!resource->collection) - apr_table_addn(methods, "PUT", ""); - - if (locks_hooks != NULL) { - apr_table_addn(methods, "LOCK", ""); - apr_table_addn(methods, "UNLOCK", ""); - } - - break; - - case DAV_RESOURCE_LOCK_NULL: - /* resource is lock-null. */ - apr_table_addn(methods, "MKCOL", ""); - apr_table_addn(methods, "PROPFIND", ""); - apr_table_addn(methods, "PUT", ""); - - if (locks_hooks != NULL) { - apr_table_addn(methods, "LOCK", ""); - apr_table_addn(methods, "UNLOCK", ""); - } - - break; - - case DAV_RESOURCE_NULL: - /* resource is null. */ - apr_table_addn(methods, "MKCOL", ""); - apr_table_addn(methods, "PUT", ""); - - if (locks_hooks != NULL) - apr_table_addn(methods, "LOCK", ""); - - break; - - default: - /* ### internal error! */ - break; - } - - /* If there is a versioning provider, add versioning methods */ - if (vsn_hooks != NULL) { - if (!resource->exists) { - if ((*vsn_hooks->versionable)(resource)) - apr_table_addn(methods, "VERSION-CONTROL", ""); - - if (vsn_hooks->can_be_workspace != NULL - && (*vsn_hooks->can_be_workspace)(resource)) - apr_table_addn(methods, "MKWORKSPACE", ""); - - if (vsn_hooks->can_be_activity != NULL - && (*vsn_hooks->can_be_activity)(resource)) - apr_table_addn(methods, "MKACTIVITY", ""); - } - else if (!resource->versioned) { - if ((*vsn_hooks->versionable)(resource)) - apr_table_addn(methods, "VERSION-CONTROL", ""); - } - else if (resource->working) { - apr_table_addn(methods, "CHECKIN", ""); - - /* ### we might not support this DeltaV option */ - apr_table_addn(methods, "UNCHECKOUT", ""); - } - else if (vsn_hooks->add_label != NULL) { - apr_table_addn(methods, "CHECKOUT", ""); - apr_table_addn(methods, "LABEL", ""); - } - else { - apr_table_addn(methods, "CHECKOUT", ""); - } - } - - /* If there is a bindings provider, see if resource is bindable */ - if (binding_hooks != NULL - && (*binding_hooks->is_bindable)(resource)) { - apr_table_addn(methods, "BIND", ""); - } - - /* If there is a search provider, set SEARCH in option */ - if (search_hooks != NULL) { - apr_table_addn(methods, "SEARCH", ""); - } - - /* Generate the Allow header */ - arr = apr_table_elts(methods); - elts = (const apr_table_entry_t *)arr->elts; - text_size = 0; - - /* first, compute total length */ - for (i = 0; i < arr->nelts; ++i) { - if (elts[i].key == NULL) - continue; - - /* add 1 for comma or null */ - text_size += strlen(elts[i].key) + 1; - } - - s = allow = apr_palloc(r->pool, text_size); - - for (i = 0; i < arr->nelts; ++i) { - if (elts[i].key == NULL) - continue; - - if (s != allow) - *s++ = ','; - - strcpy(s, elts[i].key); - s += strlen(s); - } - - apr_table_setn(r->headers_out, "Allow", allow); - - - /* If there is search set_option_head function, set head */ - /* DASL: <DAV:basicsearch> - * DASL: <http://foo.bar.com/syntax1> - * DASL: <http://akuma.com/syntax2> - */ - if (search_hooks != NULL - && *search_hooks->set_option_head != NULL) { - if ((err = (*search_hooks->set_option_head)(r)) != NULL) { - return dav_handle_err(r, err, NULL); - } - } - - /* if there was no request body, then there is no response body */ - if (doc == NULL) { - ap_set_content_length(r, 0); - - /* ### this sends a Content-Type. the default OPTIONS does not. */ - - /* ### the default (ap_send_http_options) returns OK, but I believe - * ### that is because it is the default handler and nothing else - * ### will run after the thing. */ - return DONE; - } - - /* handle each options request */ - for (elem = doc->root->first_child; elem != NULL; elem = elem->next) { - /* check for something we recognize first */ - int core_option = 0; - dav_error *err = NULL; - - if (elem->ns == APR_XML_NS_DAV_ID) { - if (strcmp(elem->name, "supported-method-set") == 0) { - err = dav_gen_supported_methods(r, elem, methods, &body); - core_option = 1; - } - else if (strcmp(elem->name, "supported-live-property-set") == 0) { - err = dav_gen_supported_live_props(r, resource, elem, &body); - core_option = 1; - } - else if (strcmp(elem->name, "supported-report-set") == 0) { - err = dav_gen_supported_reports(r, resource, elem, vsn_hooks, &body); - core_option = 1; - } - } - - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* if unrecognized option, pass to versioning provider */ - if (!core_option && vsn_hooks != NULL) { - if ((err = (*vsn_hooks->get_option)(resource, elem, &body)) - != NULL) { - return dav_handle_err(r, err, NULL); - } - } - } - - /* send the options response */ - r->status = HTTP_OK; - ap_set_content_type(r, DAV_XML_CONTENT_TYPE); - - /* send the headers and response body */ - ap_rputs(DAV_XML_HEADER DEBUG_CR - "<D:options-response xmlns:D=\"DAV:\">" DEBUG_CR, r); - - for (t = body.first; t != NULL; t = t->next) - ap_rputs(t->text, r); - - ap_rputs("</D:options-response>" DEBUG_CR, r); - - /* we've sent everything necessary to the client. */ - return DONE; -} - -static void dav_cache_badprops(dav_walker_ctx *ctx) -{ - const apr_xml_elem *elem; - apr_text_header hdr = { 0 }; - - /* just return if we built the thing already */ - if (ctx->propstat_404 != NULL) { - return; - } - - apr_text_append(ctx->w.pool, &hdr, - "<D:propstat>" DEBUG_CR - "<D:prop>" DEBUG_CR); - - elem = dav_find_child(ctx->doc->root, "prop"); - for (elem = elem->first_child; elem; elem = elem->next) { - apr_text_append(ctx->w.pool, &hdr, - apr_xml_empty_elem(ctx->w.pool, elem)); - } - - apr_text_append(ctx->w.pool, &hdr, - "</D:prop>" DEBUG_CR - "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR - "</D:propstat>" DEBUG_CR); - - ctx->propstat_404 = hdr.first; -} - -static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype) -{ - dav_walker_ctx *ctx = wres->walk_ctx; - dav_error *err; - dav_propdb *propdb; - dav_get_props_result propstats = { 0 }; - - /* - ** Note: ctx->doc can only be NULL for DAV_PROPFIND_IS_ALLPROP. Since - ** dav_get_allprops() does not need to do namespace translation, - ** we're okay. - ** - ** Note: we cast to lose the "const". The propdb won't try to change - ** the resource, however, since we are opening readonly. - */ - err = dav_open_propdb(ctx->r, ctx->w.lockdb, wres->resource, 1, - ctx->doc ? ctx->doc->namespaces : NULL, &propdb); - if (err != NULL) { - /* ### do something with err! */ - - if (ctx->propfind_type == DAV_PROPFIND_IS_PROP) { - dav_get_props_result badprops = { 0 }; - - /* some props were expected on this collection/resource */ - dav_cache_badprops(ctx); - badprops.propstats = ctx->propstat_404; - dav_stream_response(wres, 0, &badprops, ctx->scratchpool); - } - else { - /* no props on this collection/resource */ - dav_stream_response(wres, HTTP_OK, NULL, ctx->scratchpool); - } - - apr_pool_clear(ctx->scratchpool); - return NULL; - } - /* ### what to do about closing the propdb on server failure? */ - - if (ctx->propfind_type == DAV_PROPFIND_IS_PROP) { - propstats = dav_get_props(propdb, ctx->doc); - } - else { - dav_prop_insert what = ctx->propfind_type == DAV_PROPFIND_IS_ALLPROP - ? DAV_PROP_INSERT_VALUE - : DAV_PROP_INSERT_NAME; - propstats = dav_get_allprops(propdb, what); - } - dav_close_propdb(propdb); - - dav_stream_response(wres, 0, &propstats, ctx->scratchpool); - - /* at this point, ctx->scratchpool has been used to stream a - single response. this function fully controls the pool, and - thus has the right to clear it for the next iteration of this - callback. */ - apr_pool_clear(ctx->scratchpool); - - return NULL; -} - -/* handle the PROPFIND method */ -static int dav_method_propfind(request_rec *r) -{ - dav_resource *resource; - int depth; - dav_error *err; - int result; - apr_xml_doc *doc; - const apr_xml_elem *child; - dav_walker_ctx ctx = { { 0 } }; - dav_response *multi_status; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (dav_get_resource_state(r, resource) == DAV_RESOURCE_NULL) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) { - /* dav_get_depth() supplies additional information for the - * default message. */ - return HTTP_BAD_REQUEST; - } - - if (depth == DAV_INFINITY && resource->collection) { - dav_dir_conf *conf; - conf = (dav_dir_conf *)ap_get_module_config(r->per_dir_config, - &dav_module); - /* default is to DISALLOW these requests */ - if (conf->allow_depthinfinity != DAV_ENABLED_ON) { - return dav_error_response(r, HTTP_FORBIDDEN, - apr_psprintf(r->pool, - "PROPFIND requests with a " - "Depth of \"infinity\" are " - "not allowed for %s.", - ap_escape_html(r->pool, - r->uri))); - } - } - - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - /* note: doc == NULL if no request body */ - - if (doc && !dav_validate_root(doc, "propfind")) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"propfind\" element was not found."); - return HTTP_BAD_REQUEST; - } - - /* ### validate that only one of these three elements is present */ - - if (doc == NULL - || (child = dav_find_child(doc->root, "allprop")) != NULL) { - /* note: no request body implies allprop */ - ctx.propfind_type = DAV_PROPFIND_IS_ALLPROP; - } - else if ((child = dav_find_child(doc->root, "propname")) != NULL) { - ctx.propfind_type = DAV_PROPFIND_IS_PROPNAME; - } - else if ((child = dav_find_child(doc->root, "prop")) != NULL) { - ctx.propfind_type = DAV_PROPFIND_IS_PROP; - } - else { - /* "propfind" element must have one of the above three children */ - - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"propfind\" element does not contain one of " - "the required child elements (the specific command)."); - return HTTP_BAD_REQUEST; - } - - ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH; - ctx.w.func = dav_propfind_walker; - ctx.w.walk_ctx = &ctx; - ctx.w.pool = r->pool; - ctx.w.root = resource; - - ctx.doc = doc; - ctx.r = r; - ctx.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - apr_pool_create(&ctx.scratchpool, r->pool); - - /* ### should open read-only */ - if ((err = dav_open_lockdb(r, 0, &ctx.w.lockdb)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - "The lock database could not be opened, " - "preventing access to the various lock " - "properties for the PROPFIND.", - err); - return dav_handle_err(r, err, NULL); - } - if (ctx.w.lockdb != NULL) { - /* if we have a lock database, then we can walk locknull resources */ - ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL; - } - - /* send <multistatus> tag, with all doc->namespaces attached. */ - - /* NOTE: we *cannot* leave out the doc's namespaces from the - initial <multistatus> tag. if a 404 was generated for an HREF, - then we need to spit out the doc's namespaces for use by the - 404. Note that <response> elements will override these ns0, - ns1, etc, but NOT within the <response> scope for the - badprops. */ - dav_begin_multistatus(ctx.bb, r, HTTP_MULTI_STATUS, - doc ? doc->namespaces : NULL); - - /* Have the provider walk the resource. */ - err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status); - - if (ctx.w.lockdb != NULL) { - (*ctx.w.lockdb->hooks->close_lockdb)(ctx.w.lockdb); - } - - if (err != NULL) { - /* If an error occurred during the resource walk, there's - basically nothing we can do but abort the connection and - log an error. This is one of the limitations of HTTP; it - needs to "know" the entire status of the response before - generating it, which is just impossible in these streamy - response situations. */ - err = dav_push_error(r->pool, err->status, 0, - "Provider encountered an error while streaming" - " a multistatus PROPFIND response.", err); - dav_log_err(r, err, APLOG_ERR); - r->connection->aborted = 1; - return DONE; - } - - dav_finish_multistatus(r, ctx.bb); - ap_filter_flush(ctx.bb, r->output_filters); - - /* the response has been sent. */ - return DONE; -} - -static apr_text * dav_failed_proppatch(apr_pool_t *p, - apr_array_header_t *prop_ctx) -{ - apr_text_header hdr = { 0 }; - int i = prop_ctx->nelts; - dav_prop_ctx *ctx = (dav_prop_ctx *)prop_ctx->elts; - dav_error *err424_set = NULL; - dav_error *err424_delete = NULL; - const char *s; - - /* ### might be nice to sort by status code and description */ - - for ( ; i-- > 0; ++ctx ) { - apr_text_append(p, &hdr, - "<D:propstat>" DEBUG_CR - "<D:prop>"); - apr_text_append(p, &hdr, apr_xml_empty_elem(p, ctx->prop)); - apr_text_append(p, &hdr, "</D:prop>" DEBUG_CR); - - if (ctx->err == NULL) { - /* nothing was assigned here yet, so make it a 424 */ - - if (ctx->operation == DAV_PROP_OP_SET) { - if (err424_set == NULL) - err424_set = dav_new_error(p, HTTP_FAILED_DEPENDENCY, 0, - "Attempted DAV:set operation " - "could not be completed due " - "to other errors."); - ctx->err = err424_set; - } - else if (ctx->operation == DAV_PROP_OP_DELETE) { - if (err424_delete == NULL) - err424_delete = dav_new_error(p, HTTP_FAILED_DEPENDENCY, 0, - "Attempted DAV:remove " - "operation could not be " - "completed due to other " - "errors."); - ctx->err = err424_delete; - } - } - - s = apr_psprintf(p, - "<D:status>" - "HTTP/1.1 %d (status)" - "</D:status>" DEBUG_CR, - ctx->err->status); - apr_text_append(p, &hdr, s); - - /* ### we should use compute_desc if necessary... */ - if (ctx->err->desc != NULL) { - apr_text_append(p, &hdr, "<D:responsedescription>" DEBUG_CR); - apr_text_append(p, &hdr, ctx->err->desc); - apr_text_append(p, &hdr, "</D:responsedescription>" DEBUG_CR); - } - - apr_text_append(p, &hdr, "</D:propstat>" DEBUG_CR); - } - - return hdr.first; -} - -static apr_text * dav_success_proppatch(apr_pool_t *p, apr_array_header_t *prop_ctx) -{ - apr_text_header hdr = { 0 }; - int i = prop_ctx->nelts; - dav_prop_ctx *ctx = (dav_prop_ctx *)prop_ctx->elts; - - /* - * ### we probably need to revise the way we assemble the response... - * ### this code assumes everything will return status==200. - */ - - apr_text_append(p, &hdr, - "<D:propstat>" DEBUG_CR - "<D:prop>" DEBUG_CR); - - for ( ; i-- > 0; ++ctx ) { - apr_text_append(p, &hdr, apr_xml_empty_elem(p, ctx->prop)); - } - - apr_text_append(p, &hdr, - "</D:prop>" DEBUG_CR - "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR - "</D:propstat>" DEBUG_CR); - - return hdr.first; -} - -static void dav_prop_log_errors(dav_prop_ctx *ctx) -{ - dav_log_err(ctx->r, ctx->err, APLOG_ERR); -} - -/* - * Call <func> for each context. This can stop when an error occurs, or - * simply iterate through the whole list. - * - * Returns 1 if an error occurs (and the iteration is aborted). Returns 0 - * if all elements are processed. - * - * If <reverse> is true (non-zero), then the list is traversed in - * reverse order. - */ -static int dav_process_ctx_list(void (*func)(dav_prop_ctx *ctx), - apr_array_header_t *ctx_list, int stop_on_error, - int reverse) -{ - int i = ctx_list->nelts; - dav_prop_ctx *ctx = (dav_prop_ctx *)ctx_list->elts; - - if (reverse) - ctx += i; - - while (i--) { - if (reverse) - --ctx; - - (*func)(ctx); - if (stop_on_error && DAV_PROP_CTX_HAS_ERR(*ctx)) { - return 1; - } - - if (!reverse) - ++ctx; - } - - return 0; -} - -/* handle the PROPPATCH method */ -static int dav_method_proppatch(request_rec *r) -{ - dav_error *err; - dav_resource *resource; - int result; - apr_xml_doc *doc; - apr_xml_elem *child; - dav_propdb *propdb; - int failure = 0; - dav_response resp = { 0 }; - apr_text *propstat_text; - apr_array_header_t *ctx_list; - dav_prop_ctx *ctx; - dav_auto_version_info av_info; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - /* note: doc == NULL if no request body */ - - if (doc == NULL || !dav_validate_root(doc, "propertyupdate")) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body does not contain " - "a \"propertyupdate\" element."); - return HTTP_BAD_REQUEST; - } - - /* Check If-Headers and existing locks */ - /* Note: depth == 0. Implies no need for a multistatus response. */ - if ((err = dav_validate_request(r, resource, 0, NULL, NULL, - DAV_VALIDATE_RESOURCE, NULL)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - /* make sure the resource can be modified (if versioning repository) */ - if ((err = dav_auto_checkout(r, resource, - 0 /* not parent_only */, - &av_info)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - if ((err = dav_open_propdb(r, NULL, resource, 0, doc->namespaces, - &propdb)) != NULL) { - /* undo any auto-checkout */ - dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info); - - err = dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(r->pool, - "Could not open the property " - "database for %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - /* ### what to do about closing the propdb on server failure? */ - - /* ### validate "live" properties */ - - /* set up an array to hold property operation contexts */ - ctx_list = apr_array_make(r->pool, 10, sizeof(dav_prop_ctx)); - - /* do a first pass to ensure that all "remove" properties exist */ - for (child = doc->root->first_child; child; child = child->next) { - int is_remove; - apr_xml_elem *prop_group; - apr_xml_elem *one_prop; - - /* Ignore children that are not set/remove */ - if (child->ns != APR_XML_NS_DAV_ID - || (!(is_remove = strcmp(child->name, "remove") == 0) - && strcmp(child->name, "set") != 0)) { - continue; - } - - /* make sure that a "prop" child exists for set/remove */ - if ((prop_group = dav_find_child(child, "prop")) == NULL) { - dav_close_propdb(propdb); - - /* undo any auto-checkout */ - dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info); - - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "A \"prop\" element is missing inside " - "the propertyupdate command."); - return HTTP_BAD_REQUEST; - } - - for (one_prop = prop_group->first_child; one_prop; - one_prop = one_prop->next) { - - ctx = (dav_prop_ctx *)apr_array_push(ctx_list); - ctx->propdb = propdb; - ctx->operation = is_remove ? DAV_PROP_OP_DELETE : DAV_PROP_OP_SET; - ctx->prop = one_prop; - - ctx->r = r; /* for later use by dav_prop_log_errors() */ - - dav_prop_validate(ctx); - - if ( DAV_PROP_CTX_HAS_ERR(*ctx) ) { - failure = 1; - } - } - } - - /* ### should test that we found at least one set/remove */ - - /* execute all of the operations */ - if (!failure && dav_process_ctx_list(dav_prop_exec, ctx_list, 1, 0)) { - failure = 1; - } - - /* generate a failure/success response */ - if (failure) { - (void)dav_process_ctx_list(dav_prop_rollback, ctx_list, 0, 1); - propstat_text = dav_failed_proppatch(r->pool, ctx_list); - } - else { - (void)dav_process_ctx_list(dav_prop_commit, ctx_list, 0, 0); - propstat_text = dav_success_proppatch(r->pool, ctx_list); - } - - /* make sure this gets closed! */ - dav_close_propdb(propdb); - - /* complete any auto-versioning */ - dav_auto_checkin(r, resource, failure, 0 /*unlock*/, &av_info); - - /* log any errors that occurred */ - (void)dav_process_ctx_list(dav_prop_log_errors, ctx_list, 0, 0); - - resp.href = resource->uri; - - /* ### should probably use something new to pass along this text... */ - resp.propresult.propstats = propstat_text; - - dav_send_multistatus(r, HTTP_MULTI_STATUS, &resp, doc->namespaces); - - /* the response has been sent. */ - return DONE; -} - -static int process_mkcol_body(request_rec *r) -{ - /* This is snarfed from ap_setup_client_block(). We could get pretty - * close to this behavior by passing REQUEST_NO_BODY, but we need to - * return HTTP_UNSUPPORTED_MEDIA_TYPE (while ap_setup_client_block - * returns HTTP_REQUEST_ENTITY_TOO_LARGE). */ - - const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); - const char *lenp = apr_table_get(r->headers_in, "Content-Length"); - - /* make sure to set the Apache request fields properly. */ - r->read_body = REQUEST_NO_BODY; - r->read_chunked = 0; - r->remaining = 0; - - if (tenc) { - if (strcasecmp(tenc, "chunked")) { - /* Use this instead of Apache's default error string */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Unknown Transfer-Encoding %s", tenc); - return HTTP_NOT_IMPLEMENTED; - } - - r->read_chunked = 1; - } - else if (lenp) { - const char *pos = lenp; - - while (apr_isdigit(*pos) || apr_isspace(*pos)) { - ++pos; - } - - if (*pos != '\0') { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid Content-Length %s", lenp); - return HTTP_BAD_REQUEST; - } - - r->remaining = apr_atoi64(lenp); - } - - if (r->read_chunked || r->remaining > 0) { - /* ### log something? */ - - /* Apache will supply a default error for this. */ - return HTTP_UNSUPPORTED_MEDIA_TYPE; - } - - /* - * Get rid of the body. this will call ap_setup_client_block(), but - * our copy above has already verified its work. - */ - return ap_discard_request_body(r); -} - -/* handle the MKCOL method */ -static int dav_method_mkcol(request_rec *r) -{ - dav_resource *resource; - int resource_state; - dav_auto_version_info av_info; - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - dav_error *err; - dav_error *err2; - int result; - dav_dir_conf *conf; - dav_response *multi_status; - - /* handle the request body */ - /* ### this may move lower once we start processing bodies */ - if ((result = process_mkcol_body(r)) != OK) { - return result; - } - - conf = (dav_dir_conf *)ap_get_module_config(r->per_dir_config, - &dav_module); - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (resource->exists) { - /* oops. something was already there! */ - - /* Apache will supply a default error for this. */ - /* ### we should provide a specific error message! */ - return HTTP_METHOD_NOT_ALLOWED; - } - - resource_state = dav_get_resource_state(r, resource); - - /* - * Check If-Headers and existing locks. - * - * Note: depth == 0 normally requires no multistatus response. However, - * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI - * other than the Request-URI, thereby requiring a multistatus. - * - * If the resource does not exist (DAV_RESOURCE_NULL), then we must - * check the resource *and* its parent. If the resource exists or is - * a locknull resource, then we check only the resource. - */ - if ((err = dav_validate_request(r, resource, 0, NULL, &multi_status, - resource_state == DAV_RESOURCE_NULL ? - DAV_VALIDATE_PARENT : - DAV_VALIDATE_RESOURCE, NULL)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, multi_status); - } - - /* if versioned resource, make sure parent is checked out */ - if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */, - &av_info)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - /* try to create the collection */ - resource->collection = 1; - err = (*resource->hooks->create_collection)(resource); - - /* restore modifiability of parent back to what it was */ - err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */, - 0 /*unlock*/, &av_info); - - /* check for errors now */ - if (err != NULL) { - return dav_handle_err(r, err, NULL); - } - if (err2 != NULL) { - /* just log a warning */ - err = dav_push_error(r->pool, err2->status, 0, - "The MKCOL was successful, but there " - "was a problem automatically checking in " - "the parent collection.", - err2); - dav_log_err(r, err, APLOG_WARNING); - } - - if (locks_hooks != NULL) { - dav_lockdb *lockdb; - - if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) { - /* The directory creation was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The MKCOL was successful, but there " - "was a problem opening the lock database " - "which prevents inheriting locks from the " - "parent resources.", - err); - return dav_handle_err(r, err, NULL); - } - - /* notify lock system that we have created/replaced a resource */ - err = dav_notify_created(r, lockdb, resource, resource_state, 0); - - (*locks_hooks->close_lockdb)(lockdb); - - if (err != NULL) { - /* The dir creation was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The MKCOL was successful, but there " - "was a problem updating its lock " - "information.", - err); - return dav_handle_err(r, err, NULL); - } - } - - /* return an appropriate response (HTTP_CREATED) */ - return dav_created(r, NULL, "Collection", 0); -} - -/* handle the COPY and MOVE methods */ -static int dav_method_copymove(request_rec *r, int is_move) -{ - dav_resource *resource; - dav_resource *resnew; - dav_auto_version_info src_av_info = { 0 }; - dav_auto_version_info dst_av_info = { 0 }; - const char *body; - const char *dest; - dav_error *err; - dav_error *err2; - dav_error *err3; - dav_response *multi_response; - dav_lookup_result lookup; - int is_dir; - int overwrite; - int depth; - int result; - dav_lockdb *lockdb; - int replace_dest; - int resnew_state; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, !is_move /* label_allowed */, - 0 /* use_checked_in */, &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* If not a file or collection resource, COPY/MOVE not allowed */ - /* ### allow COPY/MOVE of DeltaV resource types */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR) { - body = apr_psprintf(r->pool, - "Cannot COPY/MOVE resource %s.", - ap_escape_html(r->pool, r->uri)); - return dav_error_response(r, HTTP_METHOD_NOT_ALLOWED, body); - } - - /* get the destination URI */ - dest = apr_table_get(r->headers_in, "Destination"); - if (dest == NULL) { - /* Look in headers provided by Netscape's Roaming Profiles */ - const char *nscp_host = apr_table_get(r->headers_in, "Host"); - const char *nscp_path = apr_table_get(r->headers_in, "New-uri"); - - if (nscp_host != NULL && nscp_path != NULL) - dest = apr_psprintf(r->pool, "http://%s%s", nscp_host, nscp_path); - } - if (dest == NULL) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request is missing a Destination header."); - return HTTP_BAD_REQUEST; - } - - lookup = dav_lookup_uri(dest, r, 1 /* must_be_absolute */); - if (lookup.rnew == NULL) { - if (lookup.err.status == HTTP_BAD_REQUEST) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "%s", lookup.err.desc); - return HTTP_BAD_REQUEST; - } - - /* ### this assumes that dav_lookup_uri() only generates a status - * ### that Apache can provide a status line for!! */ - - return dav_error_response(r, lookup.err.status, lookup.err.desc); - } - if (lookup.rnew->status != HTTP_OK) { - const char *auth = apr_table_get(lookup.rnew->err_headers_out, - "WWW-Authenticate"); - if (lookup.rnew->status == HTTP_UNAUTHORIZED && auth != NULL) { - /* propagate the WWW-Authorization header up from the - * subreq so the client sees it. */ - apr_table_set(r->err_headers_out, "WWW-Authenticate", - apr_pstrdup(r->pool, auth)); - } - - /* ### how best to report this... */ - return dav_error_response(r, lookup.rnew->status, - "Destination URI had an error."); - } - - /* Resolve destination resource */ - err = dav_get_resource(lookup.rnew, 0 /* label_allowed */, - 0 /* use_checked_in */, &resnew); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* are the two resources handled by the same repository? */ - if (resource->hooks != resnew->hooks) { - /* ### this message exposes some backend config, but screw it... */ - return dav_error_response(r, HTTP_BAD_GATEWAY, - "Destination URI is handled by a " - "different repository than the source URI. " - "MOVE or COPY between repositories is " - "not possible."); - } - - /* get and parse the overwrite header value */ - if ((overwrite = dav_get_overwrite(r)) < 0) { - /* dav_get_overwrite() supplies additional information for the - * default message. */ - return HTTP_BAD_REQUEST; - } - - /* quick failure test: if dest exists and overwrite is false. */ - if (resnew->exists && !overwrite) { - /* Supply some text for the error response body. */ - return dav_error_response(r, HTTP_PRECONDITION_FAILED, - "Destination is not empty and " - "Overwrite is not \"T\""); - } - - /* are the source and destination the same? */ - if ((*resource->hooks->is_same_resource)(resource, resnew)) { - /* Supply some text for the error response body. */ - return dav_error_response(r, HTTP_FORBIDDEN, - "Source and Destination URIs are the same."); - - } - - is_dir = resource->collection; - - /* get and parse the Depth header value. "0" and "infinity" are legal. */ - if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) { - /* dav_get_depth() supplies additional information for the - * default message. */ - return HTTP_BAD_REQUEST; - } - if (depth == 1) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Depth must be \"0\" or \"infinity\" for COPY or MOVE."); - return HTTP_BAD_REQUEST; - } - if (is_move && is_dir && depth != DAV_INFINITY) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Depth must be \"infinity\" when moving a collection."); - return HTTP_BAD_REQUEST; - } - - /* - * Check If-Headers and existing locks for each resource in the source - * if we are performing a MOVE. We will return a 424 response with a - * DAV:multistatus body. The multistatus responses will contain the - * information about any resource that fails the validation. - * - * We check the parent resource, too, since this is a MOVE. Moving the - * resource effectively removes it from the parent collection, so we - * must ensure that we have met the appropriate conditions. - * - * If a problem occurs with the Request-URI itself, then a plain error - * (rather than a multistatus) will be returned. - */ - if (is_move - && (err = dav_validate_request(r, resource, depth, NULL, - &multi_response, - DAV_VALIDATE_PARENT - | DAV_VALIDATE_USE_424, - NULL)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not MOVE %s due to a failed " - "precondition on the source " - "(e.g. locks).", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - /* - * Check If-Headers and existing locks for destination. Note that we - * use depth==infinity since the target (hierarchy) will be deleted - * before the move/copy is completed. - * - * Note that we are overwriting the target, which implies a DELETE, so - * we are subject to the error/response rules as a DELETE. Namely, we - * will return a 424 error if any of the validations fail. - * (see dav_method_delete() for more information) - */ - if ((err = dav_validate_request(lookup.rnew, resnew, DAV_INFINITY, NULL, - &multi_response, - DAV_VALIDATE_PARENT - | DAV_VALIDATE_USE_424, NULL)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not MOVE/COPY %s due to a " - "failed precondition on the " - "destination (e.g. locks).", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - if (is_dir - && depth == DAV_INFINITY - && (*resource->hooks->is_parent_resource)(resource, resnew)) { - /* Supply some text for the error response body. */ - return dav_error_response(r, HTTP_FORBIDDEN, - "Source collection contains the " - "Destination."); - - } - if (is_dir - && (*resnew->hooks->is_parent_resource)(resnew, resource)) { - /* The destination must exist (since it contains the source), and - * a condition above implies Overwrite==T. Obviously, we cannot - * delete the Destination before the MOVE/COPY, as that would - * delete the Source. - */ - - /* Supply some text for the error response body. */ - return dav_error_response(r, HTTP_FORBIDDEN, - "Destination collection contains the Source " - "and Overwrite has been specified."); - } - - /* ### for now, we don't need anything in the body */ - if ((result = ap_discard_request_body(r)) != OK) { - return result; - } - - if ((err = dav_open_lockdb(r, 0, &lockdb)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - /* remove any locks from the old resources */ - /* - * ### this is Yet Another Traversal. if we do a rename(), then we - * ### really don't have to do this in some cases since the inode - * ### values will remain constant across the move. but we can't - * ### know that fact from outside the provider :-( - * - * ### note that we now have a problem atomicity in the move/copy - * ### since a failure after this would have removed locks (technically, - * ### this is okay to do, but really...) - */ - if (is_move && lockdb != NULL) { - /* ### this is wrong! it blasts direct locks on parent resources */ - /* ### pass lockdb! */ - (void)dav_unlock(r, resource, NULL); - } - - /* if this is a move, then the source parent collection will be modified */ - if (is_move) { - if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */, - &src_av_info)) != NULL) { - if (lockdb != NULL) - (*lockdb->hooks->close_lockdb)(lockdb); - - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - } - - /* - * Remember the initial state of the destination, so the lock system - * can be notified as to how it changed. - */ - resnew_state = dav_get_resource_state(lookup.rnew, resnew); - - /* In a MOVE operation, the destination is replaced by the source. - * In a COPY operation, if the destination exists, is under version - * control, and is the same resource type as the source, - * then it should not be replaced, but modified to be a copy of - * the source. - */ - if (!resnew->exists) - replace_dest = 0; - else if (is_move || !resource->versioned) - replace_dest = 1; - else if (resource->type != resnew->type) - replace_dest = 1; - else if ((resource->collection == 0) != (resnew->collection == 0)) - replace_dest = 1; - else - replace_dest = 0; - - /* If the destination must be created or replaced, - * make sure the parent collection is writable - */ - if (!resnew->exists || replace_dest) { - if ((err = dav_auto_checkout(r, resnew, 1 /*parent_only*/, - &dst_av_info)) != NULL) { - /* could not make destination writable: - * if move, restore state of source parent - */ - if (is_move) { - (void)dav_auto_checkin(r, NULL, 1 /* undo */, - 0 /*unlock*/, &src_av_info); - } - - if (lockdb != NULL) - (*lockdb->hooks->close_lockdb)(lockdb); - - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - } - - /* If source and destination parents are the same, then - * use the same resource object, so status updates to one are reflected - * in the other, when doing auto-versioning. Otherwise, - * we may try to checkin the parent twice. - */ - if (src_av_info.parent_resource != NULL - && dst_av_info.parent_resource != NULL - && (*src_av_info.parent_resource->hooks->is_same_resource) - (src_av_info.parent_resource, dst_av_info.parent_resource)) { - - dst_av_info.parent_resource = src_av_info.parent_resource; - } - - /* If destination is being replaced, remove it first - * (we know Ovewrite must be TRUE). Then try to copy/move the resource. - */ - if (replace_dest) - err = (*resnew->hooks->remove_resource)(resnew, &multi_response); - - if (err == NULL) { - if (is_move) - err = (*resource->hooks->move_resource)(resource, resnew, - &multi_response); - else - err = (*resource->hooks->copy_resource)(resource, resnew, depth, - &multi_response); - } - - /* perform any auto-versioning cleanup */ - err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */, - 0 /*unlock*/, &dst_av_info); - - if (is_move) { - err3 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */, - 0 /*unlock*/, &src_av_info); - } - else - err3 = NULL; - - /* check for error from remove/copy/move operations */ - if (err != NULL) { - if (lockdb != NULL) - (*lockdb->hooks->close_lockdb)(lockdb); - - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not MOVE/COPY %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - /* check for errors from auto-versioning */ - if (err2 != NULL) { - /* just log a warning */ - err = dav_push_error(r->pool, err2->status, 0, - "The MOVE/COPY was successful, but there was a " - "problem automatically checking in the " - "source parent collection.", - err2); - dav_log_err(r, err, APLOG_WARNING); - } - if (err3 != NULL) { - /* just log a warning */ - err = dav_push_error(r->pool, err3->status, 0, - "The MOVE/COPY was successful, but there was a " - "problem automatically checking in the " - "destination or its parent collection.", - err3); - dav_log_err(r, err, APLOG_WARNING); - } - - /* propagate any indirect locks at the target */ - if (lockdb != NULL) { - - /* notify lock system that we have created/replaced a resource */ - err = dav_notify_created(r, lockdb, resnew, resnew_state, depth); - - (*lockdb->hooks->close_lockdb)(lockdb); - - if (err != NULL) { - /* The move/copy was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The MOVE/COPY was successful, but there " - "was a problem updating the lock " - "information.", - err); - return dav_handle_err(r, err, NULL); - } - } - - /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */ - return dav_created(r, lookup.rnew->uri, "Destination", - resnew_state == DAV_RESOURCE_EXISTS); -} - -/* dav_method_lock: Handler to implement the DAV LOCK method - * Returns appropriate HTTP_* response. - */ -static int dav_method_lock(request_rec *r) -{ - dav_error *err; - dav_resource *resource; - const dav_hooks_locks *locks_hooks; - int result; - int depth; - int new_lock_request = 0; - apr_xml_doc *doc; - dav_lock *lock; - dav_response *multi_response = NULL; - dav_lockdb *lockdb; - int resource_state; - - /* If no locks provider, decline the request */ - locks_hooks = DAV_GET_HOOKS_LOCKS(r); - if (locks_hooks == NULL) - return DECLINED; - - if ((result = ap_xml_parse_input(r, &doc)) != OK) - return result; - - depth = dav_get_depth(r, DAV_INFINITY); - if (depth != 0 && depth != DAV_INFINITY) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Depth must be 0 or \"infinity\" for LOCK."); - return HTTP_BAD_REQUEST; - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* - * Open writable. Unless an error occurs, we'll be - * writing into the database. - */ - if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, NULL); - } - - if (doc != NULL) { - if ((err = dav_lock_parse_lockinfo(r, resource, lockdb, doc, - &lock)) != NULL) { - /* ### add a higher-level description to err? */ - goto error; - } - new_lock_request = 1; - - lock->auth_user = apr_pstrdup(r->pool, r->user); - } - - resource_state = dav_get_resource_state(r, resource); - - /* - * Check If-Headers and existing locks. - * - * If this will create a locknull resource, then the LOCK will affect - * the parent collection (much like a PUT/MKCOL). For that case, we must - * validate the parent resource's conditions. - */ - if ((err = dav_validate_request(r, resource, depth, NULL, &multi_response, - (resource_state == DAV_RESOURCE_NULL - ? DAV_VALIDATE_PARENT - : DAV_VALIDATE_RESOURCE) - | (new_lock_request ? lock->scope : 0) - | DAV_VALIDATE_ADD_LD, - lockdb)) != OK) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not LOCK %s due to a failed " - "precondition (e.g. other locks).", - ap_escape_html(r->pool, r->uri)), - err); - goto error; - } - - if (new_lock_request == 0) { - dav_locktoken_list *ltl; - - /* - * Refresh request - * ### Assumption: We can renew multiple locks on the same resource - * ### at once. First harvest all the positive lock-tokens given in - * ### the If header. Then modify the lock entries for this resource - * ### with the new Timeout val. - */ - - if ((err = dav_get_locktoken_list(r, <l)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "The lock refresh for %s failed " - "because no lock tokens were " - "specified in an \"If:\" " - "header.", - ap_escape_html(r->pool, r->uri)), - err); - goto error; - } - - if ((err = (*locks_hooks->refresh_locks)(lockdb, resource, ltl, - dav_get_timeout(r), - &lock)) != NULL) { - /* ### add a higher-level description to err? */ - goto error; - } - } else { - /* New lock request */ - char *locktoken_txt; - dav_dir_conf *conf; - - conf = (dav_dir_conf *)ap_get_module_config(r->per_dir_config, - &dav_module); - - /* apply lower bound (if any) from DAVMinTimeout directive */ - if (lock->timeout != DAV_TIMEOUT_INFINITE - && lock->timeout < time(NULL) + conf->locktimeout) - lock->timeout = time(NULL) + conf->locktimeout; - - err = dav_add_lock(r, resource, lockdb, lock, &multi_response); - if (err != NULL) { - /* ### add a higher-level description to err? */ - goto error; - } - - locktoken_txt = apr_pstrcat(r->pool, "<", - (*locks_hooks->format_locktoken)(r->pool, - lock->locktoken), - ">", NULL); - - apr_table_set(r->headers_out, "Lock-Token", locktoken_txt); - } - - (*locks_hooks->close_lockdb)(lockdb); - - r->status = HTTP_OK; - ap_set_content_type(r, DAV_XML_CONTENT_TYPE); - - ap_rputs(DAV_XML_HEADER DEBUG_CR "<D:prop xmlns:D=\"DAV:\">" DEBUG_CR, r); - if (lock == NULL) - ap_rputs("<D:lockdiscovery/>" DEBUG_CR, r); - else { - ap_rprintf(r, - "<D:lockdiscovery>" DEBUG_CR - "%s" DEBUG_CR - "</D:lockdiscovery>" DEBUG_CR, - dav_lock_get_activelock(r, lock, NULL)); - } - ap_rputs("</D:prop>", r); - - /* the response has been sent. */ - return DONE; - - error: - (*locks_hooks->close_lockdb)(lockdb); - return dav_handle_err(r, err, multi_response); -} - -/* dav_method_unlock: Handler to implement the DAV UNLOCK method - * Returns appropriate HTTP_* response. - */ -static int dav_method_unlock(request_rec *r) -{ - dav_error *err; - dav_resource *resource; - const dav_hooks_locks *locks_hooks; - int result; - const char *const_locktoken_txt; - char *locktoken_txt; - dav_locktoken *locktoken = NULL; - int resource_state; - dav_response *multi_response; - - /* If no locks provider, decline the request */ - locks_hooks = DAV_GET_HOOKS_LOCKS(r); - if (locks_hooks == NULL) - return DECLINED; - - if ((const_locktoken_txt = apr_table_get(r->headers_in, - "Lock-Token")) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Unlock failed (%s): " - "No Lock-Token specified in header", r->filename); - return HTTP_BAD_REQUEST; - } - - locktoken_txt = apr_pstrdup(r->pool, const_locktoken_txt); - if (locktoken_txt[0] != '<') { - /* ### should provide more specifics... */ - return HTTP_BAD_REQUEST; - } - locktoken_txt++; - - if (locktoken_txt[strlen(locktoken_txt) - 1] != '>') { - /* ### should provide more specifics... */ - return HTTP_BAD_REQUEST; - } - locktoken_txt[strlen(locktoken_txt) - 1] = '\0'; - - if ((err = (*locks_hooks->parse_locktoken)(r->pool, locktoken_txt, - &locktoken)) != NULL) { - err = dav_push_error(r->pool, HTTP_BAD_REQUEST, 0, - apr_psprintf(r->pool, - "The UNLOCK on %s failed -- an " - "invalid lock token was specified " - "in the \"If:\" header.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - resource_state = dav_get_resource_state(r, resource); - - /* - * Check If-Headers and existing locks. - * - * Note: depth == 0 normally requires no multistatus response. However, - * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI - * other than the Request-URI, thereby requiring a multistatus. - * - * If the resource is a locknull resource, then the UNLOCK will affect - * the parent collection (much like a delete). For that case, we must - * validate the parent resource's conditions. - */ - if ((err = dav_validate_request(r, resource, 0, locktoken, - &multi_response, - resource_state == DAV_RESOURCE_LOCK_NULL - ? DAV_VALIDATE_PARENT - : DAV_VALIDATE_RESOURCE, NULL)) != NULL) { - /* ### add a higher-level description? */ - return dav_handle_err(r, err, multi_response); - } - - /* ### RFC 2518 s. 8.11: If this resource is locked by locktoken, - * _all_ resources locked by locktoken are released. It does not say - * resource has to be the root of an infinte lock. Thus, an UNLOCK - * on any part of an infinte lock will remove the lock on all resources. - * - * For us, if r->filename represents an indirect lock (part of an infinity lock), - * we must actually perform an UNLOCK on the direct lock for this resource. - */ - if ((result = dav_unlock(r, resource, locktoken)) != OK) { - return result; - } - - return HTTP_NO_CONTENT; -} - -static int dav_method_vsn_control(request_rec *r) -{ - dav_resource *resource; - int resource_state; - dav_auto_version_info av_info; - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - apr_xml_doc *doc; - const char *target = NULL; - int result; - - /* if no versioning provider, decline the request */ - if (vsn_hooks == NULL) - return DECLINED; - - /* ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* remember the pre-creation resource state */ - resource_state = dav_get_resource_state(r, resource); - - /* parse the request body (may be a version-control element) */ - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - /* note: doc == NULL if no request body */ - - if (doc != NULL) { - const apr_xml_elem *child; - apr_size_t tsize; - - if (!dav_validate_root(doc, "version-control")) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body does not contain " - "a \"version-control\" element."); - return HTTP_BAD_REQUEST; - } - - /* get the version URI */ - if ((child = dav_find_child(doc->root, "version")) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"version-control\" element does not contain " - "a \"version\" element."); - return HTTP_BAD_REQUEST; - } - - if ((child = dav_find_child(child, "href")) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"version\" element does not contain " - "an \"href\" element."); - return HTTP_BAD_REQUEST; - } - - /* get version URI */ - apr_xml_to_text(r->pool, child, APR_XML_X2T_INNER, NULL, NULL, - &target, &tsize); - if (tsize == 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "An \"href\" element does not contain a URI."); - return HTTP_BAD_REQUEST; - } - } - - /* Check request preconditions */ - - /* ### need a general mechanism for reporting precondition violations - * ### (should be returning XML document for 403/409 responses) - */ - - /* if not versioning existing resource, must specify version to select */ - if (!resource->exists && target == NULL) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:initial-version-required/>"); - return dav_handle_err(r, err, NULL); - } - else if (resource->exists) { - /* cannot add resource to existing version history */ - if (target != NULL) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:cannot-add-to-existing-history/>"); - return dav_handle_err(r, err, NULL); - } - - /* resource must be unversioned and versionable, or version selector */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR - || (!resource->versioned && !(vsn_hooks->versionable)(resource))) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:must-be-versionable/>"); - return dav_handle_err(r, err, NULL); - } - - /* the DeltaV spec says if resource is a version selector, - * then VERSION-CONTROL is a no-op - */ - if (resource->versioned) { - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* no body */ - ap_set_content_length(r, 0); - - return DONE; - } - } - - /* Check If-Headers and existing locks */ - /* Note: depth == 0. Implies no need for a multistatus response. */ - if ((err = dav_validate_request(r, resource, 0, NULL, NULL, - resource_state == DAV_RESOURCE_NULL ? - DAV_VALIDATE_PARENT : - DAV_VALIDATE_RESOURCE, NULL)) != NULL) { - return dav_handle_err(r, err, NULL); - } - - /* if in versioned collection, make sure parent is checked out */ - if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */, - &av_info)) != NULL) { - return dav_handle_err(r, err, NULL); - } - - /* attempt to version-control the resource */ - if ((err = (*vsn_hooks->vsn_control)(resource, target)) != NULL) { - dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info); - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Could not VERSION-CONTROL resource %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* revert writability of parent directory */ - err = dav_auto_checkin(r, resource, 0 /*undo*/, 0 /*unlock*/, &av_info); - if (err != NULL) { - /* just log a warning */ - err = dav_push_error(r->pool, err->status, 0, - "The VERSION-CONTROL was successful, but there " - "was a problem automatically checking in " - "the parent collection.", - err); - dav_log_err(r, err, APLOG_WARNING); - } - - /* if the resource is lockable, let lock system know of new resource */ - if (locks_hooks != NULL - && (*locks_hooks->get_supportedlock)(resource) != NULL) { - dav_lockdb *lockdb; - - if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) { - /* The resource creation was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The VERSION-CONTROL was successful, but there " - "was a problem opening the lock database " - "which prevents inheriting locks from the " - "parent resources.", - err); - return dav_handle_err(r, err, NULL); - } - - /* notify lock system that we have created/replaced a resource */ - err = dav_notify_created(r, lockdb, resource, resource_state, 0); - - (*locks_hooks->close_lockdb)(lockdb); - - if (err != NULL) { - /* The dir creation was successful, but the locking failed. */ - err = dav_push_error(r->pool, err->status, 0, - "The VERSION-CONTROL was successful, but there " - "was a problem updating its lock " - "information.", - err); - return dav_handle_err(r, err, NULL); - } - } - - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* return an appropriate response (HTTP_CREATED) */ - return dav_created(r, resource->uri, "Version selector", 0 /*replaced*/); -} - -/* handle the CHECKOUT method */ -static int dav_method_checkout(request_rec *r) -{ - dav_resource *resource; - dav_resource *working_resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - int result; - apr_xml_doc *doc; - int apply_to_vsn = 0; - int is_unreserved = 0; - int is_fork_ok = 0; - int create_activity = 0; - apr_array_header_t *activities = NULL; - - /* If no versioning provider, decline the request */ - if (vsn_hooks == NULL) - return DECLINED; - - if ((result = ap_xml_parse_input(r, &doc)) != OK) - return result; - - if (doc != NULL) { - const apr_xml_elem *aset; - - if (!dav_validate_root(doc, "checkout")) { - /* This supplies additional information for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body, if present, must be a " - "DAV:checkout element."); - return HTTP_BAD_REQUEST; - } - - if (dav_find_child(doc->root, "apply-to-version") != NULL) { - if (apr_table_get(r->headers_in, "label") != NULL) { - /* ### we want generic 403/409 XML reporting here */ - /* ### DAV:must-not-have-label-and-apply-to-version */ - return dav_error_response(r, HTTP_CONFLICT, - "DAV:apply-to-version cannot be " - "used in conjunction with a " - "Label header."); - } - apply_to_vsn = 1; - } - - is_unreserved = dav_find_child(doc->root, "unreserved") != NULL; - is_fork_ok = dav_find_child(doc->root, "fork-ok") != NULL; - - if ((aset = dav_find_child(doc->root, "activity-set")) != NULL) { - if (dav_find_child(aset, "new") != NULL) { - create_activity = 1; - } - else { - const apr_xml_elem *child = aset->first_child; - - activities = apr_array_make(r->pool, 1, sizeof(const char *)); - - for (; child != NULL; child = child->next) { - if (child->ns == APR_XML_NS_DAV_ID - && strcmp(child->name, "href") == 0) { - const char *href; - - href = dav_xml_get_cdata(child, r->pool, - 1 /* strip_white */); - *(const char **)apr_array_push(activities) = href; - } - } - - if (activities->nelts == 0) { - /* no href's is a DTD violation: - <!ELEMENT activity-set (href+ | new)> - */ - - /* This supplies additional info for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Within the DAV:activity-set element, the " - "DAV:new element must be used, or at least " - "one DAV:href must be specified."); - return HTTP_BAD_REQUEST; - } - } - } - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 1 /*label_allowed*/, apply_to_vsn, &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* Check the state of the resource: must be a file or collection, - * must be versioned, and must not already be checked out. - */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR - && resource->type != DAV_RESOURCE_TYPE_VERSION) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot checkout this type of resource."); - } - - if (!resource->versioned) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot checkout unversioned resource."); - } - - if (resource->working) { - return dav_error_response(r, HTTP_CONFLICT, - "The resource is already checked out to the workspace."); - } - - /* ### do lock checks, once behavior is defined */ - - /* Do the checkout */ - if ((err = (*vsn_hooks->checkout)(resource, 0 /*auto_checkout*/, - is_unreserved, is_fork_ok, - create_activity, activities, - &working_resource)) != NULL) { - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Could not CHECKOUT resource %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* if no working resource created, return OK, - * else return CREATED with working resource URL in Location header - */ - if (working_resource == NULL) { - /* no body */ - ap_set_content_length(r, 0); - return DONE; - } - - return dav_created(r, working_resource->uri, "Checked-out resource", 0); -} - -/* handle the UNCHECKOUT method */ -static int dav_method_uncheckout(request_rec *r) -{ - dav_resource *resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - int result; - - /* If no versioning provider, decline the request */ - if (vsn_hooks == NULL) - return DECLINED; - - if ((result = ap_discard_request_body(r)) != OK) { - return result; - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* Check the state of the resource: must be a file or collection, - * must be versioned, and must be checked out. - */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot uncheckout this type of resource."); - } - - if (!resource->versioned) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot uncheckout unversioned resource."); - } - - if (!resource->working) { - return dav_error_response(r, HTTP_CONFLICT, - "The resource is not checked out to the workspace."); - } - - /* ### do lock checks, once behavior is defined */ - - /* Do the uncheckout */ - if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) { - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Could not UNCHECKOUT resource %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* no body */ - ap_set_content_length(r, 0); - - return DONE; -} - -/* handle the CHECKIN method */ -static int dav_method_checkin(request_rec *r) -{ - dav_resource *resource; - dav_resource *new_version; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - int result; - apr_xml_doc *doc; - int keep_checked_out = 0; - - /* If no versioning provider, decline the request */ - if (vsn_hooks == NULL) - return DECLINED; - - if ((result = ap_xml_parse_input(r, &doc)) != OK) - return result; - - if (doc != NULL) { - if (!dav_validate_root(doc, "checkin")) { - /* This supplies additional information for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body, if present, must be a " - "DAV:checkin element."); - return HTTP_BAD_REQUEST; - } - - keep_checked_out = dav_find_child(doc->root, "keep-checked-out") != NULL; - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* Check the state of the resource: must be a file or collection, - * must be versioned, and must be checked out. - */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot checkin this type of resource."); - } - - if (!resource->versioned) { - return dav_error_response(r, HTTP_CONFLICT, - "Cannot checkin unversioned resource."); - } - - if (!resource->working) { - return dav_error_response(r, HTTP_CONFLICT, - "The resource is not checked out."); - } - - /* ### do lock checks, once behavior is defined */ - - /* Do the checkin */ - if ((err = (*vsn_hooks->checkin)(resource, keep_checked_out, &new_version)) - != NULL) { - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Could not CHECKIN resource %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - return dav_created(r, new_version->uri, "Version", 0); -} - -static int dav_method_update(request_rec *r) -{ - dav_resource *resource; - dav_resource *version = NULL; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - apr_xml_doc *doc; - apr_xml_elem *child; - int is_label = 0; - int depth; - int result; - apr_size_t tsize; - const char *target; - dav_response *multi_response; - dav_error *err; - dav_lookup_result lookup; - - /* If no versioning provider, or UPDATE not supported, - * decline the request */ - if (vsn_hooks == NULL || vsn_hooks->update == NULL) - return DECLINED; - - if ((depth = dav_get_depth(r, 0)) < 0) { - /* dav_get_depth() supplies additional information for the - * default message. */ - return HTTP_BAD_REQUEST; - } - - /* parse the request body */ - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - - if (doc == NULL || !dav_validate_root(doc, "update")) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body does not contain " - "an \"update\" element."); - return HTTP_BAD_REQUEST; - } - - /* check for label-name or version element, but not both */ - if ((child = dav_find_child(doc->root, "label-name")) != NULL) - is_label = 1; - else if ((child = dav_find_child(doc->root, "version")) != NULL) { - /* get the href element */ - if ((child = dav_find_child(child, "href")) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The version element does not contain " - "an \"href\" element."); - return HTTP_BAD_REQUEST; - } - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"update\" element does not contain " - "a \"label-name\" or \"version\" element."); - return HTTP_BAD_REQUEST; - } - - /* a depth greater than zero is only allowed for a label */ - if (!is_label && depth != 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Depth must be zero for UPDATE with a version"); - return HTTP_BAD_REQUEST; - } - - /* get the target value (a label or a version URI) */ - apr_xml_to_text(r->pool, child, APR_XML_X2T_INNER, NULL, NULL, - &target, &tsize); - if (tsize == 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "A \"label-name\" or \"href\" element does not contain " - "any content."); - return HTTP_BAD_REQUEST; - } - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* ### need a general mechanism for reporting precondition violations - * ### (should be returning XML document for 403/409 responses) - */ - if (resource->type != DAV_RESOURCE_TYPE_REGULAR - || !resource->versioned || resource->working) { - return dav_error_response(r, HTTP_CONFLICT, - "<DAV:must-be-checked-in-version-controlled-resource>"); - } - - /* if target is a version, resolve the version resource */ - /* ### dav_lookup_uri only allows absolute URIs; is that OK? */ - if (!is_label) { - lookup = dav_lookup_uri(target, r, 0 /* must_be_absolute */); - if (lookup.rnew == NULL) { - if (lookup.err.status == HTTP_BAD_REQUEST) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "%s", lookup.err.desc); - return HTTP_BAD_REQUEST; - } - - /* ### this assumes that dav_lookup_uri() only generates a status - * ### that Apache can provide a status line for!! */ - - return dav_error_response(r, lookup.err.status, lookup.err.desc); - } - if (lookup.rnew->status != HTTP_OK) { - /* ### how best to report this... */ - return dav_error_response(r, lookup.rnew->status, - "Version URI had an error."); - } - - /* resolve version resource */ - err = dav_get_resource(lookup.rnew, 0 /* label_allowed */, - 0 /* use_checked_in */, &version); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* NULL out target, since we're using a version resource */ - target = NULL; - } - - /* do the UPDATE operation */ - err = (*vsn_hooks->update)(resource, version, target, depth, &multi_response); - - if (err != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not UPDATE %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* no body */ - ap_set_content_length(r, 0); - - return DONE; -} - -/* context maintained during LABEL treewalk */ -typedef struct dav_label_walker_ctx -{ - /* input: */ - dav_walk_params w; - - /* label being manipulated */ - const char *label; - - /* label operation */ - int label_op; -#define DAV_LABEL_ADD 1 -#define DAV_LABEL_SET 2 -#define DAV_LABEL_REMOVE 3 - - /* version provider hooks */ - const dav_hooks_vsn *vsn_hooks; - -} dav_label_walker_ctx; - -static dav_error * dav_label_walker(dav_walk_resource *wres, int calltype) -{ - dav_label_walker_ctx *ctx = wres->walk_ctx; - dav_error *err = NULL; - - /* Check the state of the resource: must be a version or - * non-checkedout version selector - */ - /* ### need a general mechanism for reporting precondition violations - * ### (should be returning XML document for 403/409 responses) - */ - if (wres->resource->type != DAV_RESOURCE_TYPE_VERSION && - (wres->resource->type != DAV_RESOURCE_TYPE_REGULAR - || !wres->resource->versioned)) { - err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0, - "<DAV:must-be-version-or-version-selector/>"); - } - else if (wres->resource->working) { - err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0, - "<DAV:must-not-be-checked-out/>"); - } - else { - /* do the label operation */ - if (ctx->label_op == DAV_LABEL_REMOVE) - err = (*ctx->vsn_hooks->remove_label)(wres->resource, ctx->label); - else - err = (*ctx->vsn_hooks->add_label)(wres->resource, ctx->label, - ctx->label_op == DAV_LABEL_SET); - } - - if (err != NULL) { - /* ### need utility routine to add response with description? */ - dav_add_response(wres, err->status, NULL); - wres->response->desc = err->desc; - } - - return NULL; -} - -static int dav_method_label(request_rec *r) -{ - dav_resource *resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - apr_xml_doc *doc; - apr_xml_elem *child; - int depth; - int result; - apr_size_t tsize; - dav_error *err; - dav_label_walker_ctx ctx = { { 0 } }; - dav_response *multi_status; - - /* If no versioning provider, or the provider doesn't support - * labels, decline the request */ - if (vsn_hooks == NULL || vsn_hooks->add_label == NULL) - return DECLINED; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - if ((depth = dav_get_depth(r, 0)) < 0) { - /* dav_get_depth() supplies additional information for the - * default message. */ - return HTTP_BAD_REQUEST; - } - - /* parse the request body */ - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - - if (doc == NULL || !dav_validate_root(doc, "label")) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body does not contain " - "a \"label\" element."); - return HTTP_BAD_REQUEST; - } - - /* check for add, set, or remove element */ - if ((child = dav_find_child(doc->root, "add")) != NULL) { - ctx.label_op = DAV_LABEL_ADD; - } - else if ((child = dav_find_child(doc->root, "set")) != NULL) { - ctx.label_op = DAV_LABEL_SET; - } - else if ((child = dav_find_child(doc->root, "remove")) != NULL) { - ctx.label_op = DAV_LABEL_REMOVE; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The \"label\" element does not contain " - "an \"add\", \"set\", or \"remove\" element."); - return HTTP_BAD_REQUEST; - } - - /* get the label string */ - if ((child = dav_find_child(child, "label-name")) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The label command element does not contain " - "a \"label-name\" element."); - return HTTP_BAD_REQUEST; - } - - apr_xml_to_text(r->pool, child, APR_XML_X2T_INNER, NULL, NULL, - &ctx.label, &tsize); - if (tsize == 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "A \"label-name\" element does not contain " - "a label name."); - return HTTP_BAD_REQUEST; - } - - /* do the label operation walk */ - ctx.w.walk_type = DAV_WALKTYPE_NORMAL; - ctx.w.func = dav_label_walker; - ctx.w.walk_ctx = &ctx; - ctx.w.pool = r->pool; - ctx.w.root = resource; - ctx.vsn_hooks = vsn_hooks; - - err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status); - - if (err != NULL) { - /* some sort of error occurred which terminated the walk */ - err = dav_push_error(r->pool, err->status, 0, - "The LABEL operation was terminated prematurely.", - err); - return dav_handle_err(r, err, multi_status); - } - - if (multi_status != NULL) { - /* One or more resources had errors. If depth was zero, convert - * response to simple error, else make sure there is an - * overall error to pass to dav_handle_err() - */ - if (depth == 0) { - err = dav_new_error(r->pool, multi_status->status, 0, multi_status->desc); - multi_status = NULL; - } - else { - err = dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, - "Errors occurred during the LABEL operation."); - } - - return dav_handle_err(r, err, multi_status); - } - - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* no body */ - ap_set_content_length(r, 0); - - return DONE; -} - -static int dav_method_report(request_rec *r) -{ - dav_resource *resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - int result; - int label_allowed; - apr_xml_doc *doc; - dav_error *err; - - /* If no versioning provider, decline the request */ - if (vsn_hooks == NULL) - return DECLINED; - - if ((result = ap_xml_parse_input(r, &doc)) != OK) - return result; - if (doc == NULL) { - /* This supplies additional information for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body must specify a report."); - return HTTP_BAD_REQUEST; - } - - /* Ask repository module to resolve the resource. - * First determine whether a Target-Selector header is allowed - * for this report. - */ - label_allowed = (*vsn_hooks->report_label_header_allowed)(doc); - err = dav_get_resource(r, label_allowed, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* set up defaults for the report response */ - r->status = HTTP_OK; - ap_set_content_type(r, DAV_XML_CONTENT_TYPE); - - /* run report hook */ - if ((err = (*vsn_hooks->deliver_report)(r, resource, doc, - r->output_filters)) != NULL) { - if (! r->sent_bodyct) - /* No data has been sent to client yet; throw normal error. */ - return dav_handle_err(r, err, NULL); - - /* If an error occurred during the report delivery, there's - basically nothing we can do but abort the connection and - log an error. This is one of the limitations of HTTP; it - needs to "know" the entire status of the response before - generating it, which is just impossible in these streamy - response situations. */ - err = dav_push_error(r->pool, err->status, 0, - "Provider encountered an error while streaming" - " a REPORT response.", err); - dav_log_err(r, err, APLOG_ERR); - r->connection->aborted = 1; - return DONE; - } - - return DONE; -} - -static int dav_method_make_workspace(request_rec *r) -{ - dav_resource *resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - apr_xml_doc *doc; - int result; - - /* if no versioning provider, or the provider does not support workspaces, - * decline the request - */ - if (vsn_hooks == NULL || vsn_hooks->make_workspace == NULL) - return DECLINED; - - /* ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* parse the request body (must be a mkworkspace element) */ - if ((result = ap_xml_parse_input(r, &doc)) != OK) { - return result; - } - - if (doc == NULL - || !dav_validate_root(doc, "mkworkspace")) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body does not contain " - "a \"mkworkspace\" element."); - return HTTP_BAD_REQUEST; - } - - /* Check request preconditions */ - - /* ### need a general mechanism for reporting precondition violations - * ### (should be returning XML document for 403/409 responses) - */ - - /* resource must not already exist */ - if (resource->exists) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:resource-must-be-null/>"); - return dav_handle_err(r, err, NULL); - } - - /* ### what about locking? */ - - /* attempt to create the workspace */ - if ((err = (*vsn_hooks->make_workspace)(resource, doc)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not create workspace %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* return an appropriate response (HTTP_CREATED) */ - return dav_created(r, resource->uri, "Workspace", 0 /*replaced*/); -} - -static int dav_method_make_activity(request_rec *r) -{ - dav_resource *resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - int result; - - /* if no versioning provider, or the provider does not support activities, - * decline the request - */ - if (vsn_hooks == NULL || vsn_hooks->make_activity == NULL) - return DECLINED; - - /* ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* MKACTIVITY does not have a defined request body. */ - if ((result = ap_discard_request_body(r)) != OK) { - return result; - } - - /* Check request preconditions */ - - /* ### need a general mechanism for reporting precondition violations - * ### (should be returning XML document for 403/409 responses) - */ - - /* resource must not already exist */ - if (resource->exists) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:resource-must-be-null/>"); - return dav_handle_err(r, err, NULL); - } - - /* the provider must say whether the resource can be created as - an activity, i.e. whether the location is ok. */ - if (vsn_hooks->can_be_activity != NULL - && !(*vsn_hooks->can_be_activity)(resource)) { - err = dav_new_error(r->pool, HTTP_FORBIDDEN, 0, - "<DAV:activity-location-ok/>"); - return dav_handle_err(r, err, NULL); - } - - /* ### what about locking? */ - - /* attempt to create the activity */ - if ((err = (*vsn_hooks->make_activity)(resource)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not create activity %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* set the Cache-Control header, per the spec */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* return an appropriate response (HTTP_CREATED) */ - return dav_created(r, resource->uri, "Activity", 0 /*replaced*/); -} - -static int dav_method_baseline_control(request_rec *r) -{ - /* ### */ - return HTTP_METHOD_NOT_ALLOWED; -} - -static int dav_method_merge(request_rec *r) -{ - dav_resource *resource; - dav_resource *source_resource; - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err; - int result; - apr_xml_doc *doc; - apr_xml_elem *source_elem; - apr_xml_elem *href_elem; - apr_xml_elem *prop_elem; - const char *source; - int no_auto_merge; - int no_checkout; - dav_lookup_result lookup; - - /* If no versioning provider, decline the request */ - if (vsn_hooks == NULL) - return DECLINED; - - if ((result = ap_xml_parse_input(r, &doc)) != OK) - return result; - - if (doc == NULL || !dav_validate_root(doc, "merge")) { - /* This supplies additional information for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request body must be present and must be a " - "DAV:merge element."); - return HTTP_BAD_REQUEST; - } - - if ((source_elem = dav_find_child(doc->root, "source")) == NULL) { - /* This supplies additional information for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The DAV:merge element must contain a DAV:source " - "element."); - return HTTP_BAD_REQUEST; - } - if ((href_elem = dav_find_child(source_elem, "href")) == NULL) { - /* This supplies additional information for the default msg. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The DAV:source element must contain a DAV:href " - "element."); - return HTTP_BAD_REQUEST; - } - source = dav_xml_get_cdata(href_elem, r->pool, 1 /* strip_white */); - - /* get a subrequest for the source, so that we can get a dav_resource - for that source. */ - lookup = dav_lookup_uri(source, r, 0 /* must_be_absolute */); - if (lookup.rnew == NULL) { - if (lookup.err.status == HTTP_BAD_REQUEST) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "%s", lookup.err.desc); - return HTTP_BAD_REQUEST; - } - - /* ### this assumes that dav_lookup_uri() only generates a status - * ### that Apache can provide a status line for!! */ - - return dav_error_response(r, lookup.err.status, lookup.err.desc); - } - if (lookup.rnew->status != HTTP_OK) { - /* ### how best to report this... */ - return dav_error_response(r, lookup.rnew->status, - "Merge source URI had an error."); - } - err = dav_get_resource(lookup.rnew, 0 /* label_allowed */, - 0 /* use_checked_in */, &source_resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - no_auto_merge = dav_find_child(doc->root, "no-auto-merge") != NULL; - no_checkout = dav_find_child(doc->root, "no-checkout") != NULL; - - prop_elem = dav_find_child(doc->root, "prop"); - - /* ### check RFC. I believe the DAV:merge element may contain any - ### element also allowed within DAV:checkout. need to extract them - ### here, and pass them along. - ### if so, then refactor the CHECKOUT method handling so we can reuse - ### the code. maybe create a structure to hold CHECKOUT parameters - ### which can be passed to the checkout() and merge() hooks. */ - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* ### check the source and target resources flags/types */ - - /* ### do lock checks, once behavior is defined */ - - /* set the Cache-Control header, per the spec */ - /* ### correct? */ - apr_table_setn(r->headers_out, "Cache-Control", "no-cache"); - - /* Initialize these values for a standard MERGE response. If the MERGE - is going to do something different (i.e. an error), then it must - return a dav_error, and we'll reset these values properly. */ - r->status = HTTP_OK; - ap_set_content_type(r, "text/xml"); - - /* ### should we do any preliminary response generation? probably not, - ### because we may have an error, thus demanding something else in - ### the response body. */ - - /* Do the merge, including any response generation. */ - if ((err = (*vsn_hooks->merge)(resource, source_resource, - no_auto_merge, no_checkout, - prop_elem, - r->output_filters)) != NULL) { - /* ### is err->status the right error here? */ - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not MERGE resource \"%s\" " - "into \"%s\".", - ap_escape_html(r->pool, source), - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, NULL); - } - - /* the response was fully generated by the merge() hook. */ - /* ### urk. does this prevent logging? need to check... */ - return DONE; -} - -static int dav_method_bind(request_rec *r) -{ - dav_resource *resource; - dav_resource *binding; - dav_auto_version_info av_info; - const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r); - const char *dest; - dav_error *err; - dav_error *err2; - dav_response *multi_response = NULL; - dav_lookup_result lookup; - int overwrite; - - /* If no bindings provider, decline the request */ - if (binding_hooks == NULL) - return DECLINED; - - /* Ask repository module to resolve the resource */ - err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */, - &resource); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - if (!resource->exists) { - /* Apache will supply a default error for this. */ - return HTTP_NOT_FOUND; - } - - /* get the destination URI */ - dest = apr_table_get(r->headers_in, "Destination"); - if (dest == NULL) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "The request is missing a Destination header."); - return HTTP_BAD_REQUEST; - } - - lookup = dav_lookup_uri(dest, r, 0 /* must_be_absolute */); - if (lookup.rnew == NULL) { - if (lookup.err.status == HTTP_BAD_REQUEST) { - /* This supplies additional information for the default message. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "%s", lookup.err.desc); - return HTTP_BAD_REQUEST; - } - else if (lookup.err.status == HTTP_BAD_GATEWAY) { - /* ### Bindings protocol draft 02 says to return 507 - * ### (Cross Server Binding Forbidden); Apache already defines 507 - * ### as HTTP_INSUFFICIENT_STORAGE. So, for now, we'll return - * ### HTTP_FORBIDDEN - */ - return dav_error_response(r, HTTP_FORBIDDEN, - "Cross server bindings are not " - "allowed by this server."); - } - - /* ### this assumes that dav_lookup_uri() only generates a status - * ### that Apache can provide a status line for!! */ - - return dav_error_response(r, lookup.err.status, lookup.err.desc); - } - if (lookup.rnew->status != HTTP_OK) { - /* ### how best to report this... */ - return dav_error_response(r, lookup.rnew->status, - "Destination URI had an error."); - } - - /* resolve binding resource */ - err = dav_get_resource(lookup.rnew, 0 /* label_allowed */, - 0 /* use_checked_in */, &binding); - if (err != NULL) - return dav_handle_err(r, err, NULL); - - /* are the two resources handled by the same repository? */ - if (resource->hooks != binding->hooks) { - /* ### this message exposes some backend config, but screw it... */ - return dav_error_response(r, HTTP_BAD_GATEWAY, - "Destination URI is handled by a " - "different repository than the source URI. " - "BIND between repositories is not possible."); - } - - /* get and parse the overwrite header value */ - if ((overwrite = dav_get_overwrite(r)) < 0) { - /* dav_get_overwrite() supplies additional information for the - * default message. */ - return HTTP_BAD_REQUEST; - } - - /* quick failure test: if dest exists and overwrite is false. */ - if (binding->exists && !overwrite) { - return dav_error_response(r, HTTP_PRECONDITION_FAILED, - "Destination is not empty and " - "Overwrite is not \"T\""); - } - - /* are the source and destination the same? */ - if ((*resource->hooks->is_same_resource)(resource, binding)) { - return dav_error_response(r, HTTP_FORBIDDEN, - "Source and Destination URIs are the same."); - } - - /* - * Check If-Headers and existing locks for destination. Note that we - * use depth==infinity since the target (hierarchy) will be deleted - * before the move/copy is completed. - * - * Note that we are overwriting the target, which implies a DELETE, so - * we are subject to the error/response rules as a DELETE. Namely, we - * will return a 424 error if any of the validations fail. - * (see dav_method_delete() for more information) - */ - if ((err = dav_validate_request(lookup.rnew, binding, DAV_INFINITY, NULL, - &multi_response, - DAV_VALIDATE_PARENT - | DAV_VALIDATE_USE_424, NULL)) != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not BIND %s due to a " - "failed precondition on the " - "destination (e.g. locks).", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - /* guard against creating circular bindings */ - if (resource->collection - && (*resource->hooks->is_parent_resource)(resource, binding)) { - return dav_error_response(r, HTTP_FORBIDDEN, - "Source collection contains the Destination."); - } - if (resource->collection - && (*resource->hooks->is_parent_resource)(binding, resource)) { - /* The destination must exist (since it contains the source), and - * a condition above implies Overwrite==T. Obviously, we cannot - * delete the Destination before the BIND, as that would - * delete the Source. - */ - - return dav_error_response(r, HTTP_FORBIDDEN, - "Destination collection contains the Source and " - "Overwrite has been specified."); - } - - /* prepare the destination collection for modification */ - if ((err = dav_auto_checkout(r, binding, 1 /* parent_only */, - &av_info)) != NULL) { - /* could not make destination writable */ - return dav_handle_err(r, err, NULL); - } - - /* If target exists, remove it first (we know Ovewrite must be TRUE). - * Then try to bind to the resource. - */ - if (binding->exists) - err = (*resource->hooks->remove_resource)(binding, &multi_response); - - if (err == NULL) { - err = (*binding_hooks->bind_resource)(resource, binding); - } - - /* restore parent collection states */ - err2 = dav_auto_checkin(r, NULL, - err != NULL /* undo if error */, - 0 /* unlock */, &av_info); - - /* check for error from remove/bind operations */ - if (err != NULL) { - err = dav_push_error(r->pool, err->status, 0, - apr_psprintf(r->pool, - "Could not BIND %s.", - ap_escape_html(r->pool, r->uri)), - err); - return dav_handle_err(r, err, multi_response); - } - - /* check for errors from reverting writability */ - if (err2 != NULL) { - /* just log a warning */ - err = dav_push_error(r->pool, err2->status, 0, - "The BIND was successful, but there was a " - "problem automatically checking in the " - "source parent collection.", - err2); - dav_log_err(r, err, APLOG_WARNING); - } - - /* return an appropriate response (HTTP_CREATED) */ - /* ### spec doesn't say what happens when destination was replaced */ - return dav_created(r, lookup.rnew->uri, "Binding", 0); -} - - -/* - * Response handler for DAV resources - */ -static int dav_handler(request_rec *r) -{ - if (strcmp(r->handler, DAV_HANDLER_NAME) != 0) - return DECLINED; - - /* Reject requests with an unescaped hash character, as these may - * be more destructive than the user intended. */ - if (r->parsed_uri.fragment != NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "buggy client used un-escaped hash in Request-URI"); - return dav_error_response(r, HTTP_BAD_REQUEST, - "The request was invalid: the URI included " - "an un-escaped hash character"); - } - - /* ### do we need to do anything with r->proxyreq ?? */ - - /* - * ### anything else to do here? could another module and/or - * ### config option "take over" the handler here? i.e. how do - * ### we lock down this hierarchy so that we are the ultimate - * ### arbiter? (or do we simply depend on the administrator - * ### to avoid conflicting configurations?) - */ - - /* - * Set up the methods mask, since that's one of the reasons this handler - * gets called, and lower-level things may need the info. - * - * First, set the mask to the methods we handle directly. Since by - * definition we own our managed space, we unconditionally set - * the r->allowed field rather than ORing our values with anything - * any other module may have put in there. - * - * These are the HTTP-defined methods that we handle directly. - */ - r->allowed = 0 - | (AP_METHOD_BIT << M_GET) - | (AP_METHOD_BIT << M_PUT) - | (AP_METHOD_BIT << M_DELETE) - | (AP_METHOD_BIT << M_OPTIONS) - | (AP_METHOD_BIT << M_INVALID); - - /* - * These are the DAV methods we handle. - */ - r->allowed |= 0 - | (AP_METHOD_BIT << M_COPY) - | (AP_METHOD_BIT << M_LOCK) - | (AP_METHOD_BIT << M_UNLOCK) - | (AP_METHOD_BIT << M_MKCOL) - | (AP_METHOD_BIT << M_MOVE) - | (AP_METHOD_BIT << M_PROPFIND) - | (AP_METHOD_BIT << M_PROPPATCH); - - /* - * These are methods that we don't handle directly, but let the - * server's default handler do for us as our agent. - */ - r->allowed |= 0 - | (AP_METHOD_BIT << M_POST); - - /* ### hrm. if we return HTTP_METHOD_NOT_ALLOWED, then an Allow header - * ### is sent; it will need the other allowed states; since the default - * ### handler is not called on error, then it doesn't add the other - * ### allowed states, so we must - */ - - /* ### we might need to refine this for just where we return the error. - * ### also, there is the issue with other methods (see ISSUES) - */ - - /* dispatch the appropriate method handler */ - if (r->method_number == M_GET) { - return dav_method_get(r); - } - - if (r->method_number == M_PUT) { - return dav_method_put(r); - } - - if (r->method_number == M_POST) { - return dav_method_post(r); - } - - if (r->method_number == M_DELETE) { - return dav_method_delete(r); - } - - if (r->method_number == M_OPTIONS) { - return dav_method_options(r); - } - - if (r->method_number == M_PROPFIND) { - return dav_method_propfind(r); - } - - if (r->method_number == M_PROPPATCH) { - return dav_method_proppatch(r); - } - - if (r->method_number == M_MKCOL) { - return dav_method_mkcol(r); - } - - if (r->method_number == M_COPY) { - return dav_method_copymove(r, DAV_DO_COPY); - } - - if (r->method_number == M_MOVE) { - return dav_method_copymove(r, DAV_DO_MOVE); - } - - if (r->method_number == M_LOCK) { - return dav_method_lock(r); - } - - if (r->method_number == M_UNLOCK) { - return dav_method_unlock(r); - } - - if (r->method_number == M_VERSION_CONTROL) { - return dav_method_vsn_control(r); - } - - if (r->method_number == M_CHECKOUT) { - return dav_method_checkout(r); - } - - if (r->method_number == M_UNCHECKOUT) { - return dav_method_uncheckout(r); - } - - if (r->method_number == M_CHECKIN) { - return dav_method_checkin(r); - } - - if (r->method_number == M_UPDATE) { - return dav_method_update(r); - } - - if (r->method_number == M_LABEL) { - return dav_method_label(r); - } - - if (r->method_number == M_REPORT) { - return dav_method_report(r); - } - - if (r->method_number == M_MKWORKSPACE) { - return dav_method_make_workspace(r); - } - - if (r->method_number == M_MKACTIVITY) { - return dav_method_make_activity(r); - } - - if (r->method_number == M_BASELINE_CONTROL) { - return dav_method_baseline_control(r); - } - - if (r->method_number == M_MERGE) { - return dav_method_merge(r); - } - - /* BIND method */ - if (r->method_number == dav_methods[DAV_M_BIND]) { - return dav_method_bind(r); - } - - /* DASL method */ - if (r->method_number == dav_methods[DAV_M_SEARCH]) { - return dav_method_search(r); - } - - /* ### add'l methods for Advanced Collections, ACLs */ - - return DECLINED; -} - -static int dav_fixups(request_rec *r) -{ - dav_dir_conf *conf; - - /* quickly ignore any HTTP/0.9 requests which aren't subreqs. */ - if (r->assbackwards && !r->main) { - return DECLINED; - } - - conf = (dav_dir_conf *)ap_get_module_config(r->per_dir_config, - &dav_module); - - /* if DAV is not enabled, then we've got nothing to do */ - if (conf->provider == NULL) { - return DECLINED; - } - - /* We are going to handle almost every request. In certain cases, - the provider maps to the filesystem (thus, handle_get is - FALSE), and core Apache will handle it. a For that case, we - just return right away. */ - if (r->method_number == M_GET) { - /* - * ### need some work to pull Content-Type and Content-Language - * ### from the property database. - */ - - /* - * If the repository hasn't indicated that it will handle the - * GET method, then just punt. - * - * ### this isn't quite right... taking over the response can break - * ### things like mod_negotiation. need to look into this some more. - */ - if (!conf->provider->repos->handle_get) { - return DECLINED; - } - } - - /* ### this is wrong. We should only be setting the r->handler for the - * requests that mod_dav knows about. If we set the handler for M_POST - * requests, then CGI scripts that use POST will return the source for the - * script. However, mod_dav DOES handle POST, so something else needs - * to be fixed. - */ - if (r->method_number != M_POST) { - - /* We are going to be handling the response for this resource. */ - r->handler = DAV_HANDLER_NAME; - return OK; - } - - return DECLINED; -} - -static void register_hooks(apr_pool_t *p) -{ - ap_hook_handler(dav_handler, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_post_config(dav_init_handler, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_fixups(dav_fixups, NULL, NULL, APR_HOOK_MIDDLE); - - dav_hook_find_liveprop(dav_core_find_liveprop, NULL, NULL, APR_HOOK_LAST); - dav_hook_insert_all_liveprops(dav_core_insert_all_liveprops, - NULL, NULL, APR_HOOK_MIDDLE); - - dav_core_register_uris(p); -} - -/*--------------------------------------------------------------------------- - * - * Configuration info for the module - */ - -static const command_rec dav_cmds[] = -{ - /* per directory/location */ - AP_INIT_TAKE1("DAV", dav_cmd_dav, NULL, ACCESS_CONF, - "specify the DAV provider for a directory or location"), - - /* per directory/location, or per server */ - AP_INIT_TAKE1("DAVMinTimeout", dav_cmd_davmintimeout, NULL, - ACCESS_CONF|RSRC_CONF, - "specify minimum allowed timeout"), - - /* per directory/location, or per server */ - AP_INIT_FLAG("DAVDepthInfinity", dav_cmd_davdepthinfinity, NULL, - ACCESS_CONF|RSRC_CONF, - "allow Depth infinity PROPFIND requests"), - - { NULL } -}; - -module DAV_DECLARE_DATA dav_module = -{ - STANDARD20_MODULE_STUFF, - dav_create_dir_config, /* dir config creater */ - dav_merge_dir_config, /* dir merger --- default is to override */ - dav_create_server_config, /* server config */ - dav_merge_server_config, /* merge server config */ - dav_cmds, /* command table */ - register_hooks, /* register hooks */ -}; - -APR_HOOK_STRUCT( - APR_HOOK_LINK(gather_propsets) - APR_HOOK_LINK(find_liveprop) - APR_HOOK_LINK(insert_all_liveprops) - ) - -APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, gather_propsets, - (apr_array_header_t *uris), - (uris)) - -APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dav, DAV, int, find_liveprop, - (const dav_resource *resource, - const char *ns_uri, const char *name, - const dav_hooks_liveprop **hooks), - (resource, ns_uri, name, hooks), 0) - -APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, insert_all_liveprops, - (request_rec *r, const dav_resource *resource, - dav_prop_insert what, apr_text_header *phdr), - (r, resource, what, phdr)) diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.dsp b/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.dsp deleted file mode 100644 index 38972a11..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.dsp +++ /dev/null @@ -1,164 +0,0 @@ -# Microsoft Developer Studio Project File - Name="mod_dav" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=mod_dav - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "mod_dav.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "mod_dav.mak" CFG="mod_dav - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "mod_dav - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "mod_dav - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "mod_dav - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c -# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "DAV_DECLARE_EXPORT" /Fd"Release\mod_dav_src" /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_dav.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav.so -# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_dav.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav.so /opt:ref - -!ELSEIF "$(CFG)" == "mod_dav - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c -# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "DAV_DECLARE_EXPORT" /Fd"Debug\mod_dav_src" /FD /c -# ADD BASE MTL /nologo /D "_DEBUG" /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_dav.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav.so -# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_dav.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_dav.so - -!ENDIF - -# Begin Target - -# Name "mod_dav - Win32 Release" -# Name "mod_dav - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" -# Begin Source File - -SOURCE=.\liveprop.c -# End Source File -# Begin Source File - -SOURCE=.\mod_dav.c -# End Source File -# Begin Source File - -SOURCE=.\props.c -# End Source File -# Begin Source File - -SOURCE=.\providers.c -# End Source File -# Begin Source File - -SOURCE=.\std_liveprop.c -# End Source File -# Begin Source File - -SOURCE=.\util.c -# End Source File -# Begin Source File - -SOURCE=.\util_lock.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" -# Begin Source File - -SOURCE=.\mod_dav.h -# End Source File -# End Group -# Begin Source File - -SOURCE=.\mod_dav.rc -# End Source File -# Begin Source File - -SOURCE=..\..\..\build\win32\win32ver.awk - -!IF "$(CFG)" == "mod_dav - Win32 Release" - -# PROP Ignore_Default_Tool 1 -# Begin Custom Build - Creating Version Resource -InputPath=..\..\..\build\win32\win32ver.awk - -".\mod_dav.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - awk -f ../../../build/win32/win32ver.awk mod_dav.so "dav_module for Apache" ../../../include/ap_release.h > .\mod_dav.rc - -# End Custom Build - -!ELSEIF "$(CFG)" == "mod_dav - Win32 Debug" - -# PROP Ignore_Default_Tool 1 -# Begin Custom Build - Creating Version Resource -InputPath=..\..\..\build\win32\win32ver.awk - -".\mod_dav.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - awk -f ../../../build/win32/win32ver.awk mod_dav.so "dav_module for Apache" ../../../include/ap_release.h > .\mod_dav.rc - -# End Custom Build - -!ENDIF - -# End Source File -# End Target -# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.h b/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.h deleted file mode 100644 index 0fb9ac9b..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/mod_dav.h +++ /dev/null @@ -1,2420 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV extension module for Apache 2.0.* -*/ - -#ifndef _MOD_DAV_H_ -#define _MOD_DAV_H_ - -#include "apr_hooks.h" -#include "apr_hash.h" -#include "apr_dbm.h" -#include "apr_tables.h" - -#include "httpd.h" -#include "util_filter.h" -#include "util_xml.h" - -#include <limits.h> /* for INT_MAX */ -#include <time.h> /* for time_t */ - -#ifdef __cplusplus -extern "C" { -#endif - - -#define DAV_VERSION AP_SERVER_BASEREVISION - -#define DAV_XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>" -#define DAV_XML_CONTENT_TYPE "text/xml; charset=\"utf-8\"" - -#define DAV_READ_BLOCKSIZE 2048 /* used for reading input blocks */ - -#define DAV_RESPONSE_BODY_1 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>" -#define DAV_RESPONSE_BODY_2 "</title>\n</head><body>\n<h1>" -#define DAV_RESPONSE_BODY_3 "</h1>\n<p>" -#define DAV_RESPONSE_BODY_4 "</p>\n" -#define DAV_RESPONSE_BODY_5 "</body></html>\n" - -#define DAV_DO_COPY 0 -#define DAV_DO_MOVE 1 - - -#if 1 -#define DAV_DEBUG 1 -#define DEBUG_CR "\n" -#define DBG0(f) ap_log_error(APLOG_MARK, \ - APLOG_ERR, 0, NULL, (f)) -#define DBG1(f,a1) ap_log_error(APLOG_MARK, \ - APLOG_ERR, 0, NULL, f, a1) -#define DBG2(f,a1,a2) ap_log_error(APLOG_MARK, \ - APLOG_ERR, 0, NULL, f, a1, a2) -#define DBG3(f,a1,a2,a3) ap_log_error(APLOG_MARK, \ - APLOG_ERR, 0, NULL, f, a1, a2, a3) -#else -#undef DAV_DEBUG -#define DEBUG_CR "" -#endif - -#define DAV_INFINITY INT_MAX /* for the Depth: header */ - -/* Create a set of DAV_DECLARE(type), DAV_DECLARE_NONSTD(type) and - * DAV_DECLARE_DATA with appropriate export and import tags for the platform - */ -#if !defined(WIN32) -#define DAV_DECLARE(type) type -#define DAV_DECLARE_NONSTD(type) type -#define DAV_DECLARE_DATA -#elif defined(DAV_DECLARE_STATIC) -#define DAV_DECLARE(type) type __stdcall -#define DAV_DECLARE_NONSTD(type) type -#define DAV_DECLARE_DATA -#elif defined(DAV_DECLARE_EXPORT) -#define DAV_DECLARE(type) __declspec(dllexport) type __stdcall -#define DAV_DECLARE_NONSTD(type) __declspec(dllexport) type -#define DAV_DECLARE_DATA __declspec(dllexport) -#else -#define DAV_DECLARE(type) __declspec(dllimport) type __stdcall -#define DAV_DECLARE_NONSTD(type) __declspec(dllimport) type -#define DAV_DECLARE_DATA __declspec(dllimport) -#endif - -/* -------------------------------------------------------------------- -** -** ERROR MANAGEMENT -*/ - -/* -** dav_error structure. -** -** In most cases, mod_dav uses a pointer to a dav_error structure. If the -** pointer is NULL, then no error has occurred. -** -** In certain cases, a dav_error structure is directly used. In these cases, -** a status value of 0 means that an error has not occurred. -** -** Note: this implies that status != 0 whenever an error occurs. -** -** The desc field is optional (it may be NULL). When NULL, it typically -** implies that Apache has a proper description for the specified status. -*/ -typedef struct dav_error { - int status; /* suggested HTTP status (0 for no error) */ - int error_id; /* DAV-specific error ID */ - const char *desc; /* DAV:responsedescription and error log */ - - int save_errno; /* copy of errno causing the error */ - - const char *namespace; /* [optional] namespace of error */ - const char *tagname; /* name of error-tag */ - - struct dav_error *prev; /* previous error (in stack) */ - -} dav_error; - -/* -** Create a new error structure. save_errno will be filled with the current -** errno value. -*/ -DAV_DECLARE(dav_error*) dav_new_error(apr_pool_t *p, int status, - int error_id, const char *desc); - - -/* -** Create a new error structure with tagname and (optional) namespace; -** namespace may be NULL, which means "DAV:". save_errno will be -** filled with the current errno value. -*/ -DAV_DECLARE(dav_error*) dav_new_error_tag(apr_pool_t *p, int status, - int error_id, const char *desc, - const char *namespace, - const char *tagname); - - -/* -** Push a new error description onto the stack of errors. -** -** This function is used to provide an additional description to an existing -** error. -** -** <status> should contain the caller's view of what the current status is, -** given the underlying error. If it doesn't have a better idea, then the -** caller should pass prev->status. -** -** <error_id> can specify a new error_id since the topmost description has -** changed. -*/ -DAV_DECLARE(dav_error*) dav_push_error(apr_pool_t *p, int status, int error_id, - const char *desc, dav_error *prev); - - -/* error ID values... */ - -/* IF: header errors */ -#define DAV_ERR_IF_PARSE 100 /* general parsing error */ -#define DAV_ERR_IF_MULTIPLE_NOT 101 /* multiple "Not" found */ -#define DAV_ERR_IF_UNK_CHAR 102 /* unknown char in header */ -#define DAV_ERR_IF_ABSENT 103 /* no locktokens given */ -#define DAV_ERR_IF_TAGGED 104 /* in parsing tagged-list */ -#define DAV_ERR_IF_UNCLOSED_PAREN 105 /* in no-tagged-list */ - -/* Prop DB errors */ -#define DAV_ERR_PROP_BAD_MAJOR 200 /* major version was wrong */ -#define DAV_ERR_PROP_READONLY 201 /* prop is read-only */ -#define DAV_ERR_PROP_NO_DATABASE 202 /* writable db not avail */ -#define DAV_ERR_PROP_NOT_FOUND 203 /* prop not found */ -#define DAV_ERR_PROP_BAD_LOCKDB 204 /* could not open lockdb */ -#define DAV_ERR_PROP_OPENING 205 /* problem opening propdb */ -#define DAV_ERR_PROP_EXEC 206 /* problem exec'ing patch */ - -/* Predefined DB errors */ -/* ### any to define?? */ - -/* Predefined locking system errors */ -#define DAV_ERR_LOCK_OPENDB 400 /* could not open lockdb */ -#define DAV_ERR_LOCK_NO_DB 401 /* no database defined */ -#define DAV_ERR_LOCK_CORRUPT_DB 402 /* DB is corrupt */ -#define DAV_ERR_LOCK_UNK_STATE_TOKEN 403 /* unknown State-token */ -#define DAV_ERR_LOCK_PARSE_TOKEN 404 /* bad opaquelocktoken */ -#define DAV_ERR_LOCK_SAVE_LOCK 405 /* err saving locks */ - -/* -** Some comments on Error ID values: -** -** The numbers do not necessarily need to be unique. Uniqueness simply means -** that two errors that have not been predefined above can be distinguished -** from each other. At the moment, mod_dav does not use this distinguishing -** feature, but it could be used in the future to collapse <response> elements -** into groups based on the error ID (and associated responsedescription). -** -** If a compute_desc is provided, then the error ID should be unique within -** the context of the compute_desc function (so the function can figure out -** what to filled into the desc). -** -** Basically, subsystems can ignore defining new error ID values if they want -** to. The subsystems *do* need to return the predefined errors when -** appropriate, so that mod_dav can figure out what to do. Subsystems can -** simply leave the error ID field unfilled (zero) if there isn't an error -** that must be placed there. -*/ - - -/* -------------------------------------------------------------------- -** -** HOOK STRUCTURES -** -** These are here for forward-declaration purposes. For more info, see -** the section title "HOOK HANDLING" for more information, plus each -** structure definition. -*/ - -/* forward-declare this structure */ -typedef struct dav_hooks_propdb dav_hooks_propdb; -typedef struct dav_hooks_locks dav_hooks_locks; -typedef struct dav_hooks_vsn dav_hooks_vsn; -typedef struct dav_hooks_repository dav_hooks_repository; -typedef struct dav_hooks_liveprop dav_hooks_liveprop; -typedef struct dav_hooks_binding dav_hooks_binding; -typedef struct dav_hooks_search dav_hooks_search; - -/* ### deprecated name */ -typedef dav_hooks_propdb dav_hooks_db; - - -/* -------------------------------------------------------------------- -** -** RESOURCE HANDLING -*/ - -/* -** Resource Types: -** The base protocol defines only file and collection resources. -** The versioning protocol defines several additional resource types -** to represent artifacts of a version control system. -** -** This enumeration identifies the type of URL used to identify the -** resource. Since the same resource may have more than one type of -** URL which can identify it, dav_resource_type cannot be used -** alone to determine the type of the resource; attributes of the -** dav_resource object must also be consulted. -*/ -typedef enum { - DAV_RESOURCE_TYPE_UNKNOWN, - - DAV_RESOURCE_TYPE_REGULAR, /* file or collection; could be - * unversioned, or version selector, - * or baseline selector */ - - DAV_RESOURCE_TYPE_VERSION, /* version or baseline URL */ - - DAV_RESOURCE_TYPE_HISTORY, /* version or baseline history URL */ - - DAV_RESOURCE_TYPE_WORKING, /* working resource URL */ - - DAV_RESOURCE_TYPE_WORKSPACE, /* workspace URL */ - - DAV_RESOURCE_TYPE_ACTIVITY, /* activity URL */ - - DAV_RESOURCE_TYPE_PRIVATE /* repository-private type */ - -} dav_resource_type; - -/* -** Opaque, repository-specific information for a resource. -*/ -typedef struct dav_resource_private dav_resource_private; - -/* -** Resource descriptor, generated by a repository provider. -** -** Note: the lock-null state is not explicitly represented here, -** since it may be expensive to compute. Use dav_get_resource_state() -** to determine whether a non-existent resource is a lock-null resource. -** -** A quick explanation of how the flags can apply to different resources: -** -** unversioned file or collection: -** type = DAV_RESOURCE_TYPE_REGULAR -** exists = ? (1 if exists) -** collection = ? (1 if collection) -** versioned = 0 -** baselined = 0 -** working = 0 -** -** version-controlled resource or configuration: -** type = DAV_RESOURCE_TYPE_REGULAR -** exists = 1 -** collection = ? (1 if collection) -** versioned = 1 -** baselined = ? (1 if configuration) -** working = ? (1 if checked out) -** -** version/baseline history: -** type = DAV_RESOURCE_TYPE_HISTORY -** exists = 1 -** collection = 0 -** versioned = 0 -** baselined = 0 -** working = 0 -** -** version/baseline: -** type = DAV_RESOURCE_TYPE_VERSION -** exists = 1 -** collection = ? (1 if collection) -** versioned = 1 -** baselined = ? (1 if baseline) -** working = 0 -** -** working resource: -** type = DAV_RESOURCE_TYPE_WORKING -** exists = 1 -** collection = ? (1 if collection) -** versioned = 1 -** baselined = 0 -** working = 1 -** -** workspace: -** type = DAV_RESOURCE_TYPE_WORKSPACE -** exists = ? (1 if exists) -** collection = 1 -** versioned = ? (1 if version-controlled) -** baselined = ? (1 if baseline-controlled) -** working = ? (1 if checked out) -** -** activity: -** type = DAV_RESOURCE_TYPE_ACTIVITY -** exists = ? (1 if exists) -** collection = 0 -** versioned = 0 -** baselined = 0 -** working = 0 -*/ -typedef struct dav_resource { - dav_resource_type type; - - int exists; /* 0 => null resource */ - - int collection; /* 0 => file; can be 1 for - * REGULAR, VERSION, and WORKING resources, - * and is always 1 for WORKSPACE */ - - int versioned; /* 0 => unversioned; can be 1 for - * REGULAR and WORKSPACE resources, - * and is always 1 for VERSION and WORKING */ - - int baselined; /* 0 => not baselined; can be 1 for - * REGULAR, VERSION, and WORKSPACE resources; - * versioned == 1 when baselined == 1 */ - - int working; /* 0 => not checked out; can be 1 for - * REGULAR and WORKSPACE resources, - * and is always 1 for WORKING */ - - const char *uri; /* the URI for this resource */ - - dav_resource_private *info; /* the provider's private info */ - - const dav_hooks_repository *hooks; /* hooks used for this resource */ - - /* When allocating items related specifically to this resource, the - following pool should be used. Its lifetime will be at least as - long as the dav_resource structure. */ - apr_pool_t *pool; - -} dav_resource; - -/* -** Lock token type. Lock providers define the details of a lock token. -** However, all providers are expected to at least be able to parse -** the "opaquelocktoken" scheme, which is represented by a uuid_t. -*/ -typedef struct dav_locktoken dav_locktoken; - - -/* -------------------------------------------------------------------- -** -** BUFFER HANDLING -** -** These buffers are used as a lightweight buffer reuse mechanism. Apache -** provides sub-pool creation and destruction to much the same effect, but -** the sub-pools are a bit more general and heavyweight than these buffers. -*/ - -/* buffer for reuse; can grow to accomodate needed size */ -typedef struct -{ - apr_size_t alloc_len; /* how much has been allocated */ - apr_size_t cur_len; /* how much is currently being used */ - char *buf; /* buffer contents */ -} dav_buffer; -#define DAV_BUFFER_MINSIZE 256 /* minimum size for buffer */ -#define DAV_BUFFER_PAD 64 /* amount of pad when growing */ - -/* set the cur_len to the given size and ensure space is available */ -DAV_DECLARE(void) dav_set_bufsize(apr_pool_t *p, dav_buffer *pbuf, - apr_size_t size); - -/* initialize a buffer and copy the specified (null-term'd) string into it */ -DAV_DECLARE(void) dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, - const char *str); - -/* check that the buffer can accomodate <extra_needed> more bytes */ -DAV_DECLARE(void) dav_check_bufsize(apr_pool_t *p, dav_buffer *pbuf, - apr_size_t extra_needed); - -/* append a string to the end of the buffer, adjust length */ -DAV_DECLARE(void) dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, - const char *str); - -/* place a string on the end of the buffer, do NOT adjust length */ -DAV_DECLARE(void) dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, - const char *str); - -/* place some memory on the end of a buffer; do NOT adjust length */ -DAV_DECLARE(void) dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, - const void *mem, apr_size_t amt, - apr_size_t pad); - - -/* -------------------------------------------------------------------- -** -** HANDY UTILITIES -*/ - -/* contains results from one of the getprop functions */ -typedef struct -{ - apr_text * propstats; /* <propstat> element text */ - apr_text * xmlns; /* namespace decls for <response> elem */ -} dav_get_props_result; - -/* holds the contents of a <response> element */ -typedef struct dav_response -{ - const char *href; /* always */ - const char *desc; /* optional description at <response> level */ - - /* use status if propresult.propstats is NULL. */ - dav_get_props_result propresult; - - int status; - - struct dav_response *next; -} dav_response; - -typedef struct -{ - request_rec *rnew; /* new subrequest */ - dav_error err; /* potential error response */ -} dav_lookup_result; - - -DAV_DECLARE(dav_lookup_result) dav_lookup_uri(const char *uri, request_rec *r, - int must_be_absolute); - -/* defines type of property info a provider is to return */ -typedef enum { - DAV_PROP_INSERT_NOTDEF, /* property is defined by this provider, - but nothing was inserted because the - (live) property is not defined for this - resource (it may be present as a dead - property). */ - DAV_PROP_INSERT_NOTSUPP, /* property is recognized by this provider, - but it is not supported, and cannot be - treated as a dead property */ - DAV_PROP_INSERT_NAME, /* a property name (empty elem) was - inserted into the text block */ - DAV_PROP_INSERT_VALUE, /* a property name/value pair was inserted - into the text block */ - DAV_PROP_INSERT_SUPPORTED /* a supported live property was added to - the text block as a - <DAV:supported-live-property> element */ -} dav_prop_insert; - -/* ### this stuff is private to dav/fs/repos.c; move it... */ -/* format a time string (buf must be at least DAV_TIMEBUF_SIZE chars) */ -#define DAV_STYLE_ISO8601 1 -#define DAV_STYLE_RFC822 2 -#define DAV_TIMEBUF_SIZE 30 - -DAV_DECLARE(int) dav_get_depth(request_rec *r, int def_depth); - -DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc, - const char *tagname); -DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem, - const char *tagname); - -/* gather up all the CDATA into a single string */ -DAV_DECLARE(const char *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool, - int strip_white); - -/* -** XML namespace handling -** -** This structure tracks namespace declarations (xmlns:prefix="URI"). -** It maintains a one-to-many relationship of URIs-to-prefixes. In other -** words, one URI may be defined by many prefixes, but any specific -** prefix will specify only one URI. -** -** Prefixes using the "g###" pattern can be generated automatically if -** the caller does not have specific prefix requirements. -*/ -typedef struct { - apr_pool_t *pool; - apr_hash_t *uri_prefix; /* map URIs to an available prefix */ - apr_hash_t *prefix_uri; /* map all prefixes to their URIs */ - int count; /* counter for "g###" prefixes */ -} dav_xmlns_info; - -/* create an empty dav_xmlns_info structure */ -DAV_DECLARE(dav_xmlns_info *) dav_xmlns_create(apr_pool_t *pool); - -/* add a specific prefix/URI pair. the prefix/uri should have a lifetime - at least that of xmlns->pool */ -DAV_DECLARE(void) dav_xmlns_add(dav_xmlns_info *xi, - const char *prefix, const char *uri); - -/* add a URI (if not present); any prefix is acceptable and is returned. - the uri should have a lifetime at least that xmlns->pool */ -DAV_DECLARE(const char *) dav_xmlns_add_uri(dav_xmlns_info *xi, - const char *uri); - -/* return the URI for a specified prefix (or NULL if the prefix is unknown) */ -DAV_DECLARE(const char *) dav_xmlns_get_uri(dav_xmlns_info *xi, - const char *prefix); - -/* return an available prefix for a specified URI (or NULL if the URI - is unknown) */ -DAV_DECLARE(const char *) dav_xmlns_get_prefix(dav_xmlns_info *xi, - const char *uri); - -/* generate xmlns declarations (appending into the given text) */ -DAV_DECLARE(void) dav_xmlns_generate(dav_xmlns_info *xi, - apr_text_header *phdr); - -/* -------------------------------------------------------------------- -** -** DAV PLUGINS -*/ - -/* ### docco ... */ - -/* -** dav_provider -** -** This structure wraps up all of the hooks that a mod_dav provider can -** supply. The provider MUST supply <repos> and <propdb>. The rest are -** optional and should contain NULL if that feature is not supplied. -** -** Note that a provider cannot pick and choose portions from various -** underlying implementations (which was theoretically possible in -** mod_dav 1.0). There are too many dependencies between a dav_resource -** (defined by <repos>) and the other functionality. -** -** Live properties are not part of the dav_provider structure because they -** are handled through the APR_HOOK interface (to allow for multiple liveprop -** providers). The core always provides some properties, and then a given -** provider will add more properties. -** -** Some providers may need to associate a context with the dav_provider -** structure -- the ctx field is available for storing this context. Just -** leave it NULL if it isn't required. -*/ -typedef struct { - const dav_hooks_repository *repos; - const dav_hooks_propdb *propdb; - const dav_hooks_locks *locks; - const dav_hooks_vsn *vsn; - const dav_hooks_binding *binding; - const dav_hooks_search *search; - - void *ctx; -} dav_provider; - -/* -** gather_propsets: gather all live property propset-URIs -** -** The hook implementor should push one or more URIs into the specified -** array. These URIs are returned in the DAV: header to let clients know -** what sets of live properties are supported by the installation. mod_dav -** will place open/close angle brackets around each value (much like -** a Coded-URL); quotes and brackets should not be in the value. -** -** Example: http://apache.org/dav/props/ -** -** (of course, use your own domain to ensure a unique value) -*/ -APR_DECLARE_EXTERNAL_HOOK(dav, DAV, void, gather_propsets, - (apr_array_header_t *uris)) - -/* -** find_liveprop: find a live property, returning a non-zero, unique, -** opaque identifier. -** -** If the hook implementor determines the specified URI/name refers to -** one of its properties, then it should fill in HOOKS and return a -** non-zero value. The returned value is the "property ID" and will -** be passed to the various liveprop hook functions. -** -** Return 0 if the property is not defined by the hook implementor. -*/ -APR_DECLARE_EXTERNAL_HOOK(dav, DAV, int, find_liveprop, - (const dav_resource *resource, - const char *ns_uri, const char *name, - const dav_hooks_liveprop **hooks)) - -/* -** insert_all_liveprops: insert all (known) live property names/values. -** -** The hook implementor should append XML text to PHDR, containing liveprop -** names. If INSVALUE is true, then the property values should also be -** inserted into the output XML stream. -** -** The liveprop provider should insert *all* known and *defined* live -** properties on the specified resource. If a particular liveprop is -** not defined for this resource, then it should not be inserted. -*/ -APR_DECLARE_EXTERNAL_HOOK(dav, DAV, void, insert_all_liveprops, - (request_rec *r, const dav_resource *resource, - dav_prop_insert what, apr_text_header *phdr)) - -DAV_DECLARE(const dav_hooks_locks *) dav_get_lock_hooks(request_rec *r); -DAV_DECLARE(const dav_hooks_propdb *) dav_get_propdb_hooks(request_rec *r); -DAV_DECLARE(const dav_hooks_vsn *) dav_get_vsn_hooks(request_rec *r); -DAV_DECLARE(const dav_hooks_binding *) dav_get_binding_hooks(request_rec *r); -DAV_DECLARE(const dav_hooks_search *) dav_get_search_hooks(request_rec *r); - -DAV_DECLARE(void) dav_register_provider(apr_pool_t *p, const char *name, - const dav_provider *hooks); -DAV_DECLARE(const dav_provider *) dav_lookup_provider(const char *name); - - -/* ### deprecated */ -#define DAV_GET_HOOKS_PROPDB(r) dav_get_propdb_hooks(r) -#define DAV_GET_HOOKS_LOCKS(r) dav_get_lock_hooks(r) -#define DAV_GET_HOOKS_VSN(r) dav_get_vsn_hooks(r) -#define DAV_GET_HOOKS_BINDING(r) dav_get_binding_hooks(r) -#define DAV_GET_HOOKS_SEARCH(r) dav_get_search_hooks(r) - - -/* -------------------------------------------------------------------- -** -** IF HEADER PROCESSING -** -** Here is the definition of the If: header from RFC 2518, S9.4: -** -** If = "If" ":" (1*No-tag-list | 1*Tagged-list) -** No-tag-list = List -** Tagged-list = Resource 1*List -** Resource = Coded-URL -** List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")" -** State-token = Coded-URL -** Coded-URL = "<" absoluteURI ">" ; absoluteURI from RFC 2616 -** -** List corresponds to dav_if_state_list. No-tag-list corresponds to -** dav_if_header with uri==NULL. Tagged-list corresponds to a sequence of -** dav_if_header structures with (duplicate) uri==Resource -- one -** dav_if_header per state_list. A second Tagged-list will start a new -** sequence of dav_if_header structures with the new URI. -** -** A summary of the semantics, mapped into our structures: -** - Chained dav_if_headers: OR -** - Chained dav_if_state_lists: AND -** - NULL uri matches all resources -*/ - -typedef enum -{ - dav_if_etag, - dav_if_opaquelock -} dav_if_state_type; - -typedef struct dav_if_state_list -{ - dav_if_state_type type; - - int condition; -#define DAV_IF_COND_NORMAL 0 -#define DAV_IF_COND_NOT 1 /* "Not" was applied */ - - const char *etag; - dav_locktoken *locktoken; - - struct dav_if_state_list *next; -} dav_if_state_list; - -typedef struct dav_if_header -{ - const char *uri; - apr_size_t uri_len; - struct dav_if_state_list *state; - struct dav_if_header *next; - - int dummy_header; /* used internally by the lock/etag validation */ -} dav_if_header; - -typedef struct dav_locktoken_list -{ - dav_locktoken *locktoken; - struct dav_locktoken_list *next; -} dav_locktoken_list; - -DAV_DECLARE(dav_error *) dav_get_locktoken_list(request_rec *r, - dav_locktoken_list **ltl); - - -/* -------------------------------------------------------------------- -** -** LIVE PROPERTY HANDLING -*/ - -/* opaque type for PROPPATCH rollback information */ -typedef struct dav_liveprop_rollback dav_liveprop_rollback; - -struct dav_hooks_liveprop -{ - /* - ** Insert property information into a text block. The property to - ** insert is identified by the propid value. The information to insert - ** is identified by the "what" argument, as follows: - ** DAV_PROP_INSERT_NAME - ** property name, as an empty XML element - ** DAV_PROP_INSERT_VALUE - ** property name/value, as an XML element - ** DAV_PROP_INSERT_SUPPORTED - ** if the property is defined on the resource, then - ** a DAV:supported-live-property element, as defined - ** by the DeltaV extensions to RFC2518. - ** - ** Providers should return DAV_PROP_INSERT_NOTDEF if the property is - ** known and not defined for this resource, so should be handled as a - ** dead property. If a provider recognizes, but does not support, a - ** property, and does not want it handled as a dead property, it should - ** return DAV_PROP_INSERT_NOTSUPP. - ** - ** Returns one of DAV_PROP_INSERT_* based on what happened. - ** - ** ### we may need more context... ie. the lock database - */ - dav_prop_insert (*insert_prop)(const dav_resource *resource, - int propid, dav_prop_insert what, - apr_text_header *phdr); - - /* - ** Determine whether a given property is writable. - ** - ** ### we may want a different semantic. i.e. maybe it should be - ** ### "can we write <value> into this property?" - ** - ** Returns 1 if the live property can be written, 0 if read-only. - */ - int (*is_writable)(const dav_resource *resource, int propid); - - /* - ** This member defines the set of namespace URIs that the provider - ** uses for its properties. When insert_all is called, it will be - ** passed a list of integers that map from indices into this list - ** to namespace IDs for output generation. - ** - ** The last entry in this list should be a NULL value (sentinel). - */ - const char * const * namespace_uris; - - /* - ** ### this is not the final design. we want an open-ended way for - ** ### liveprop providers to attach *new* properties. To this end, - ** ### we'll have a "give me a list of the props you define", a way - ** ### to check for a prop's existence, a way to validate a set/remove - ** ### of a prop, and a way to execute/commit/rollback that change. - */ - - /* - ** Validate that the live property can be assigned a value, and that - ** the provided value is valid. - ** - ** elem will point to the XML element that names the property. For - ** example: - ** <lp1:executable>T</lp1:executable> - ** - ** The provider can access the cdata fields and the child elements - ** to extract the relevant pieces. - ** - ** operation is one of DAV_PROP_OP_SET or _DELETE. - ** - ** The provider may return a value in *context which will be passed - ** to each of the exec/commit/rollback functions. For example, this - ** may contain an internal value which has been processed from the - ** input element. - ** - ** The provider must set defer_to_dead to true (non-zero) or false. - ** If true, then the set/remove is deferred to the dead property - ** database. Note: it will be set to zero on entry. - */ - dav_error * (*patch_validate)(const dav_resource *resource, - const apr_xml_elem *elem, - int operation, - void **context, - int *defer_to_dead); - - /* ### doc... */ - dav_error * (*patch_exec)(const dav_resource *resource, - const apr_xml_elem *elem, - int operation, - void *context, - dav_liveprop_rollback **rollback_ctx); - - /* ### doc... */ - void (*patch_commit)(const dav_resource *resource, - int operation, - void *context, - dav_liveprop_rollback *rollback_ctx); - - /* ### doc... */ - dav_error * (*patch_rollback)(const dav_resource *resource, - int operation, - void *context, - dav_liveprop_rollback *rollback_ctx); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; -}; - -/* -** dav_liveprop_spec: specify a live property -** -** This structure is used as a standard way to determine if a particular -** property is a live property. Its use is not part of the mandated liveprop -** interface, but can be used by liveprop providers in conjuction with the -** utility routines below. -** -** spec->name == NULL is the defined end-sentinel for a list of specs. -*/ -typedef struct { - int ns; /* provider-local namespace index */ - const char *name; /* name of the property */ - - int propid; /* provider-local property ID */ - - int is_writable; /* is the property writable? */ - -} dav_liveprop_spec; - -/* -** dav_liveprop_group: specify a group of liveprops -** -** This structure specifies a group of live properties, their namespaces, -** and how to handle them. -*/ -typedef struct { - const dav_liveprop_spec *specs; - const char * const *namespace_uris; - const dav_hooks_liveprop *hooks; - -} dav_liveprop_group; - -/* ### docco */ -DAV_DECLARE(int) dav_do_find_liveprop(const char *ns_uri, const char *name, - const dav_liveprop_group *group, - const dav_hooks_liveprop **hooks); - -/* ### docco */ -DAV_DECLARE(int) dav_get_liveprop_info(int propid, - const dav_liveprop_group *group, - const dav_liveprop_spec **info); - -/* ### docco */ -DAV_DECLARE(void) dav_register_liveprop_group(apr_pool_t *pool, - const dav_liveprop_group *group); - -/* ### docco */ -DAV_DECLARE(int) dav_get_liveprop_ns_index(const char *uri); - -/* ### docco */ -DAV_DECLARE(int) dav_get_liveprop_ns_count(void); - -/* ### docco */ -DAV_DECLARE(void) dav_add_all_liveprop_xmlns(apr_pool_t *p, - apr_text_header *phdr); - -/* -** The following three functions are part of mod_dav's internal handling -** for the core WebDAV properties. They are not part of mod_dav's API. -*/ -DAV_DECLARE_NONSTD(int) dav_core_find_liveprop( - const dav_resource *resource, - const char *ns_uri, - const char *name, - const dav_hooks_liveprop **hooks); -DAV_DECLARE_NONSTD(void) dav_core_insert_all_liveprops( - request_rec *r, - const dav_resource *resource, - dav_prop_insert what, - apr_text_header *phdr); -DAV_DECLARE_NONSTD(void) dav_core_register_uris(apr_pool_t *p); - - -/* -** Standard WebDAV Property Identifiers -** -** A live property provider does not need to use these; they are simply -** provided for convenience. -** -** Property identifiers need to be unique within a given provider, but not -** *across* providers (note: this uniqueness constraint was different in -** older versions of mod_dav). -** -** The identifiers start at 20000 to make it easier for providers to avoid -** conflicts with the standard properties. The properties are arranged -** alphabetically, and may be reordered from time to time (as properties -** are introduced). -** -** NOTE: there is no problem with reordering (e.g. binary compat) since the -** identifiers are only used within a given provider, which would pick up -** the entire set of changes upon a recompile. -*/ -enum { - DAV_PROPID_BEGIN = 20000, - - /* Standard WebDAV properties (RFC 2518) */ - DAV_PROPID_creationdate, - DAV_PROPID_displayname, - DAV_PROPID_getcontentlanguage, - DAV_PROPID_getcontentlength, - DAV_PROPID_getcontenttype, - DAV_PROPID_getetag, - DAV_PROPID_getlastmodified, - DAV_PROPID_lockdiscovery, - DAV_PROPID_resourcetype, - DAV_PROPID_source, - DAV_PROPID_supportedlock, - - /* DeltaV properties (from the I-D (#14)) */ - DAV_PROPID_activity_checkout_set, - DAV_PROPID_activity_set, - DAV_PROPID_activity_version_set, - DAV_PROPID_auto_merge_set, - DAV_PROPID_auto_version, - DAV_PROPID_baseline_collection, - DAV_PROPID_baseline_controlled_collection, - DAV_PROPID_baseline_controlled_collection_set, - DAV_PROPID_checked_in, - DAV_PROPID_checked_out, - DAV_PROPID_checkin_fork, - DAV_PROPID_checkout_fork, - DAV_PROPID_checkout_set, - DAV_PROPID_comment, - DAV_PROPID_creator_displayname, - DAV_PROPID_current_activity_set, - DAV_PROPID_current_workspace_set, - DAV_PROPID_default_variant, - DAV_PROPID_eclipsed_set, - DAV_PROPID_label_name_set, - DAV_PROPID_merge_set, - DAV_PROPID_precursor_set, - DAV_PROPID_predecessor_set, - DAV_PROPID_root_version, - DAV_PROPID_subactivity_set, - DAV_PROPID_subbaseline_set, - DAV_PROPID_successor_set, - DAV_PROPID_supported_method_set, - DAV_PROPID_supported_live_property_set, - DAV_PROPID_supported_report_set, - DAV_PROPID_unreserved, - DAV_PROPID_variant_set, - DAV_PROPID_version_controlled_binding_set, - DAV_PROPID_version_controlled_configuration, - DAV_PROPID_version_history, - DAV_PROPID_version_name, - DAV_PROPID_workspace, - DAV_PROPID_workspace_checkout_set, - - DAV_PROPID_END -}; - -/* -** Property Identifier Registration -** -** At the moment, mod_dav requires live property providers to ensure that -** each property returned has a unique value. For now, this is done through -** central registration (there are no known providers other than the default, -** so this remains manageable). -** -** WARNING: the TEST ranges should never be "shipped". -*/ -#define DAV_PROPID_CORE 10000 /* ..10099. defined by mod_dav */ -#define DAV_PROPID_FS 10100 /* ..10299. - mod_dav filesystem provider. */ -#define DAV_PROPID_TEST1 10300 /* ..10399 */ -#define DAV_PROPID_TEST2 10400 /* ..10499 */ -#define DAV_PROPID_TEST3 10500 /* ..10599 */ -/* Next: 10600 */ - - -/* -------------------------------------------------------------------- -** -** DATABASE FUNCTIONS -*/ - -typedef struct dav_db dav_db; -typedef struct dav_namespace_map dav_namespace_map; -typedef struct dav_deadprop_rollback dav_deadprop_rollback; - -typedef struct { - const char *ns; /* "" signals "no namespace" */ - const char *name; -} dav_prop_name; - -/* hook functions to enable pluggable databases */ -struct dav_hooks_propdb -{ - dav_error * (*open)(apr_pool_t *p, const dav_resource *resource, int ro, - dav_db **pdb); - void (*close)(dav_db *db); - - /* - ** In bulk, define any namespaces that the values and their name - ** elements may need. - ** - ** Note: sometimes mod_dav will defer calling this until output_value - ** returns found==1. If the output process needs the dav_xmlns_info - ** filled for its work, then it will need to fill it on demand rather - ** than depending upon this hook to fill in the structure. - ** - ** Note: this will *always* be called during an output sequence. Thus, - ** the provider may rely solely on using this to fill the xmlns info. - */ - dav_error * (*define_namespaces)(dav_db *db, dav_xmlns_info *xi); - - /* - ** Output the value from the database (i.e. add an element name and - ** the value into *phdr). Set *found based on whether the name/value - ** was found in the propdb. - ** - ** Note: it is NOT an error for the key/value pair to not exist. - ** - ** The dav_xmlns_info passed to define_namespaces() is also passed to - ** each output_value() call so that namespaces can be added on-demand. - ** It can also be used to look up prefixes or URIs during the output - ** process. - */ - dav_error * (*output_value)(dav_db *db, const dav_prop_name *name, - dav_xmlns_info *xi, - apr_text_header *phdr, int *found); - - /* - ** Build a mapping from "global" namespaces (stored in apr_xml_*) - ** into provider-local namespace identifiers. - ** - ** This mapping should be done once per set of namespaces, and the - ** resulting mapping should be passed into the store() hook function. - ** - ** Note: usually, there is just a single document/namespaces for all - ** elements passed. However, the generality of creating multiple - ** mappings and passing them to store() is provided here. - ** - ** Note: this is only in preparation for a series of store() calls. - ** As a result, the propdb must be open for read/write access when - ** this function is called. - */ - dav_error * (*map_namespaces)(dav_db *db, - const apr_array_header_t *namespaces, - dav_namespace_map **mapping); - - /* - ** Store a property value for a given name. The value->combined field - ** MUST be set for this call. - ** - ** ### WARNING: current providers will quote the text within ELEM. - ** ### this implies you can call this function only once with a given - ** ### element structure (a second time will quote it again). - */ - dav_error * (*store)(dav_db *db, const dav_prop_name *name, - const apr_xml_elem *elem, - dav_namespace_map *mapping); - - /* remove a given property */ - dav_error * (*remove)(dav_db *db, const dav_prop_name *name); - - /* returns 1 if the record specified by "key" exists; 0 otherwise */ - int (*exists)(dav_db *db, const dav_prop_name *name); - - /* - ** Iterate over the property names in the database. - ** - ** iter->name.ns == iter->name.name == NULL when there are no more names. - ** - ** Note: only one iteration may occur over the propdb at a time. - */ - dav_error * (*first_name)(dav_db *db, dav_prop_name *pname); - dav_error * (*next_name)(dav_db *db, dav_prop_name *pname); - - /* - ** Rollback support: get rollback context, and apply it. - ** - ** struct dav_deadprop_rollback is a provider-private structure; it - ** should remember the name, and the name's old value (or the fact that - ** the value was not present, and should be deleted if a rollback occurs). - */ - dav_error * (*get_rollback)(dav_db *db, const dav_prop_name *name, - dav_deadprop_rollback **prollback); - dav_error * (*apply_rollback)(dav_db *db, - dav_deadprop_rollback *rollback); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; -}; - - -/* -------------------------------------------------------------------- -** -** LOCK FUNCTIONS -*/ - -/* Used to represent a Timeout header of "Infinity" */ -#define DAV_TIMEOUT_INFINITE 0 - -DAV_DECLARE(time_t) dav_get_timeout(request_rec *r); - -/* -** Opaque, provider-specific information for a lock database. -*/ -typedef struct dav_lockdb_private dav_lockdb_private; - -/* -** Opaque, provider-specific information for a lock record. -*/ -typedef struct dav_lock_private dav_lock_private; - -/* -** Lock database type. Lock providers are urged to implement a "lazy" open, so -** doing an "open" is cheap until something is actually needed from the DB. -*/ -typedef struct -{ - const dav_hooks_locks *hooks; /* the hooks used for this lockdb */ - int ro; /* was it opened readonly? */ - - dav_lockdb_private *info; - -} dav_lockdb; - -typedef enum { - DAV_LOCKSCOPE_UNKNOWN, - DAV_LOCKSCOPE_EXCLUSIVE, - DAV_LOCKSCOPE_SHARED -} dav_lock_scope; - -typedef enum { - DAV_LOCKTYPE_UNKNOWN, - DAV_LOCKTYPE_WRITE -} dav_lock_type; - -typedef enum { - DAV_LOCKREC_DIRECT, /* lock asserted on this resource */ - DAV_LOCKREC_INDIRECT, /* lock inherited from a parent */ - DAV_LOCKREC_INDIRECT_PARTIAL /* most info is not filled in */ -} dav_lock_rectype; - -/* -** dav_lock: hold information about a lock on a resource. -** -** This structure is used for both direct and indirect locks. A direct lock -** is a lock applied to a specific resource by the client. An indirect lock -** is one that is inherited from a parent resource by virtue of a non-zero -** Depth: header when the lock was applied. -** -** mod_dav records both types of locks in the lock database, managing their -** addition/removal as resources are moved about the namespace. -** -** Note that the lockdb is free to marshal this structure in any form that -** it likes. -** -** For a "partial" lock, the <rectype> and <locktoken> fields must be filled -** in. All other (user) fields should be zeroed. The lock provider will -** usually fill in the <info> field, and the <next> field may be used to -** construct a list of partial locks. -** -** The lock provider MUST use the info field to store a value such that a -** dav_lock structure can locate itself in the underlying lock database. -** This requirement is needed for refreshing: when an indirect dav_lock is -** refreshed, its reference to the direct lock does not specify the direct's -** resource, so the only way to locate the (refreshed, direct) lock in the -** database is to use the info field. -** -** Note that <is_locknull> only refers to the resource where this lock was -** found. -** ### hrm. that says the abstraction is wrong. is_locknull may disappear. -*/ -typedef struct dav_lock -{ - dav_lock_rectype rectype; /* type of lock record */ - int is_locknull; /* lock establishes a locknull resource */ - - /* ### put the resource in here? */ - - dav_lock_scope scope; /* scope of the lock */ - dav_lock_type type; /* type of lock */ - int depth; /* depth of the lock */ - time_t timeout; /* when the lock will timeout */ - - const dav_locktoken *locktoken; /* the token that was issued */ - - const char *owner; /* (XML) owner of the lock */ - const char *auth_user; /* auth'd username owning lock */ - - dav_lock_private *info; /* private to the lockdb */ - - struct dav_lock *next; /* for managing a list of locks */ -} dav_lock; - -/* Property-related public lock functions */ -DAV_DECLARE(const char *)dav_lock_get_activelock(request_rec *r, - dav_lock *locks, - dav_buffer *pbuf); - -/* LockDB-related public lock functions */ -DAV_DECLARE(dav_error *) dav_lock_parse_lockinfo(request_rec *r, - const dav_resource *resrouce, - dav_lockdb *lockdb, - const apr_xml_doc *doc, - dav_lock **lock_request); -DAV_DECLARE(int) dav_unlock(request_rec *r, - const dav_resource *resource, - const dav_locktoken *locktoken); -DAV_DECLARE(dav_error *) dav_add_lock(request_rec *r, - const dav_resource *resource, - dav_lockdb *lockdb, dav_lock *request, - dav_response **response); -DAV_DECLARE(dav_error *) dav_notify_created(request_rec *r, - dav_lockdb *lockdb, - const dav_resource *resource, - int resource_state, - int depth); - -DAV_DECLARE(dav_error*) dav_lock_query(dav_lockdb *lockdb, - const dav_resource *resource, - dav_lock **locks); - -DAV_DECLARE(dav_error *) dav_validate_request(request_rec *r, - dav_resource *resource, - int depth, - dav_locktoken *locktoken, - dav_response **response, - int flags, - dav_lockdb *lockdb); -/* -** flags: -** 0x0F -- reserved for <dav_lock_scope> values -** -** other flags, detailed below -*/ -#define DAV_VALIDATE_RESOURCE 0x0010 /* validate just the resource */ -#define DAV_VALIDATE_PARENT 0x0020 /* validate resource AND its parent */ -#define DAV_VALIDATE_ADD_LD 0x0040 /* add DAV:lockdiscovery into - the 424 DAV:response */ -#define DAV_VALIDATE_USE_424 0x0080 /* return 424 status, not 207 */ -#define DAV_VALIDATE_IS_PARENT 0x0100 /* for internal use */ - -/* Lock-null related public lock functions */ -DAV_DECLARE(int) dav_get_resource_state(request_rec *r, - const dav_resource *resource); - -/* Lock provider hooks. Locking is optional, so there may be no - * lock provider for a given repository. - */ -struct dav_hooks_locks -{ - /* Return the supportedlock property for a resource */ - const char * (*get_supportedlock)( - const dav_resource *resource - ); - - /* Parse a lock token URI, returning a lock token object allocated - * in the given pool. - */ - dav_error * (*parse_locktoken)( - apr_pool_t *p, - const char *char_token, - dav_locktoken **locktoken_p - ); - - /* Format a lock token object into a URI string, allocated in - * the given pool. - * - * Always returns non-NULL. - */ - const char * (*format_locktoken)( - apr_pool_t *p, - const dav_locktoken *locktoken - ); - - /* Compare two lock tokens. - * - * Result < 0 => lt1 < lt2 - * Result == 0 => lt1 == lt2 - * Result > 0 => lt1 > lt2 - */ - int (*compare_locktoken)( - const dav_locktoken *lt1, - const dav_locktoken *lt2 - ); - - /* Open the provider's lock database. - * - * The provider may or may not use a "real" database for locks - * (a lock could be an attribute on a resource, for example). - * - * The provider may choose to use the value of the DAVLockDB directive - * (as returned by dav_get_lockdb_path()) to decide where to place - * any storage it may need. - * - * The request storage pool should be associated with the lockdb, - * so it can be used in subsequent operations. - * - * If ro != 0, only readonly operations will be performed. - * If force == 0, the open can be "lazy"; no subsequent locking operations - * may occur. - * If force != 0, locking operations will definitely occur. - */ - dav_error * (*open_lockdb)( - request_rec *r, - int ro, - int force, - dav_lockdb **lockdb - ); - - /* Indicates completion of locking operations */ - void (*close_lockdb)( - dav_lockdb *lockdb - ); - - /* Take a resource out of the lock-null state. */ - dav_error * (*remove_locknull_state)( - dav_lockdb *lockdb, - const dav_resource *resource - ); - - /* - ** Create a (direct) lock structure for the given resource. A locktoken - ** will be created. - ** - ** The lock provider may store private information into lock->info. - */ - dav_error * (*create_lock)(dav_lockdb *lockdb, - const dav_resource *resource, - dav_lock **lock); - - /* - ** Get the locks associated with the specified resource. - ** - ** If resolve_locks is true (non-zero), then any indirect locks are - ** resolved to their actual, direct lock (i.e. the reference to followed - ** to the original lock). - ** - ** The locks, if any, are returned as a linked list in no particular - ** order. If no locks are present, then *locks will be NULL. - */ - dav_error * (*get_locks)(dav_lockdb *lockdb, - const dav_resource *resource, - int calltype, - dav_lock **locks); - -#define DAV_GETLOCKS_RESOLVED 0 /* resolve indirects to directs */ -#define DAV_GETLOCKS_PARTIAL 1 /* leave indirects partially filled */ -#define DAV_GETLOCKS_COMPLETE 2 /* fill out indirect locks */ - - /* - ** Find a particular lock on a resource (specified by its locktoken). - ** - ** *lock will be set to NULL if the lock is not found. - ** - ** Note that the provider can optimize the unmarshalling -- only one - ** lock (or none) must be constructed and returned. - ** - ** If partial_ok is true (non-zero), then an indirect lock can be - ** partially filled in. Otherwise, another lookup is done and the - ** lock structure will be filled out as a DAV_LOCKREC_INDIRECT. - */ - dav_error * (*find_lock)(dav_lockdb *lockdb, - const dav_resource *resource, - const dav_locktoken *locktoken, - int partial_ok, - dav_lock **lock); - - /* - ** Quick test to see if the resource has *any* locks on it. - ** - ** This is typically used to determine if a non-existent resource - ** has a lock and is (therefore) a locknull resource. - ** - ** WARNING: this function may return TRUE even when timed-out locks - ** exist (i.e. it may not perform timeout checks). - */ - dav_error * (*has_locks)(dav_lockdb *lockdb, - const dav_resource *resource, - int *locks_present); - - /* - ** Append the specified lock(s) to the set of locks on this resource. - ** - ** If "make_indirect" is true (non-zero), then the specified lock(s) - ** should be converted to an indirect lock (if it is a direct lock) - ** before appending. Note that the conversion to an indirect lock does - ** not alter the passed-in lock -- the change is internal the - ** append_locks function. - ** - ** Multiple locks are specified using the lock->next links. - */ - dav_error * (*append_locks)(dav_lockdb *lockdb, - const dav_resource *resource, - int make_indirect, - const dav_lock *lock); - - /* - ** Remove any lock that has the specified locktoken. - ** - ** If locktoken == NULL, then ALL locks are removed. - */ - dav_error * (*remove_lock)(dav_lockdb *lockdb, - const dav_resource *resource, - const dav_locktoken *locktoken); - - /* - ** Refresh all locks, found on the specified resource, which has a - ** locktoken in the provided list. - ** - ** If the lock is indirect, then the direct lock is referenced and - ** refreshed. - ** - ** Each lock that is updated is returned in the <locks> argument. - ** Note that the locks will be fully resolved. - */ - dav_error * (*refresh_locks)(dav_lockdb *lockdb, - const dav_resource *resource, - const dav_locktoken_list *ltl, - time_t new_time, - dav_lock **locks); - - /* - ** Look up the resource associated with a particular locktoken. - ** - ** The search begins at the specified <start_resource> and the lock - ** specified by <locktoken>. - ** - ** If the resource/token specifies an indirect lock, then the direct - ** lock will be looked up, and THAT resource will be returned. In other - ** words, this function always returns the resource where a particular - ** lock (token) was asserted. - ** - ** NOTE: this function pointer is allowed to be NULL, indicating that - ** the provider does not support this type of functionality. The - ** caller should then traverse up the repository hierarchy looking - ** for the resource defining a lock with this locktoken. - */ - dav_error * (*lookup_resource)(dav_lockdb *lockdb, - const dav_locktoken *locktoken, - const dav_resource *start_resource, - const dav_resource **resource); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; -}; - -/* what types of resources can be discovered by dav_get_resource_state() */ -#define DAV_RESOURCE_LOCK_NULL 10 /* resource lock-null */ -#define DAV_RESOURCE_NULL 11 /* resource null */ -#define DAV_RESOURCE_EXISTS 12 /* resource exists */ -#define DAV_RESOURCE_ERROR 13 /* an error occurred */ - - -/* -------------------------------------------------------------------- -** -** PROPERTY HANDLING -*/ - -typedef struct dav_propdb dav_propdb; - - -DAV_DECLARE(dav_error *) dav_open_propdb( - request_rec *r, - dav_lockdb *lockdb, - const dav_resource *resource, - int ro, - apr_array_header_t *ns_xlate, - dav_propdb **propdb); - -DAV_DECLARE(void) dav_close_propdb(dav_propdb *db); - -DAV_DECLARE(dav_get_props_result) dav_get_props( - dav_propdb *db, - apr_xml_doc *doc); - -DAV_DECLARE(dav_get_props_result) dav_get_allprops( - dav_propdb *db, - dav_prop_insert what); - -DAV_DECLARE(void) dav_get_liveprop_supported( - dav_propdb *propdb, - const char *ns_uri, - const char *propname, - apr_text_header *body); - -/* -** 3-phase property modification. -** -** 1) validate props. readable? unlocked? ACLs allow access? -** 2) execute operation (set/delete) -** 3) commit or rollback -** -** ### eventually, auth must be available. a ref to the request_rec (which -** ### contains the auth info) should be in the shared context struct. -** -** Each function may alter the error values and information contained within -** the context record. This should be done as an "increasing" level of -** error, rather than overwriting any previous error. -** -** Note that commit() cannot generate errors. It should simply free the -** rollback information. -** -** rollback() may generate additional errors because the rollback operation -** can sometimes fail(!). -** -** The caller should allocate an array of these, one per operation. It should -** be zero-initialized, then the db, operation, and prop fields should be -** filled in before calling dav_prop_validate. Note that the set/delete -** operations are order-dependent. For a given (logical) context, the same -** pointer must be passed to each phase. -** -** error_type is an internal value, but will have the same numeric value -** for each possible "desc" value. This allows the caller to group the -** descriptions via the error_type variable, rather than through string -** comparisons. Note that "status" does not provide enough granularity to -** differentiate/group the "desc" values. -** -** Note that the propdb will maintain some (global) context across all -** of the property change contexts. This implies that you can have only -** one open transaction per propdb. -*/ -typedef struct dav_prop_ctx -{ - dav_propdb *propdb; - - int operation; -#define DAV_PROP_OP_SET 1 /* set a property value */ -#define DAV_PROP_OP_DELETE 2 /* delete a prop value */ -/* ### add a GET? */ - - apr_xml_elem *prop; /* property to affect */ - - dav_error *err; /* error (if any) */ - - /* private items to the propdb */ - int is_liveprop; - void *liveprop_ctx; - struct dav_rollback_item *rollback; /* optional rollback info */ - - /* private to mod_dav.c */ - request_rec *r; - -} dav_prop_ctx; - -DAV_DECLARE_NONSTD(void) dav_prop_validate(dav_prop_ctx *ctx); -DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx); -DAV_DECLARE_NONSTD(void) dav_prop_commit(dav_prop_ctx *ctx); -DAV_DECLARE_NONSTD(void) dav_prop_rollback(dav_prop_ctx *ctx); - -#define DAV_PROP_CTX_HAS_ERR(dpc) ((dpc).err && (dpc).err->status >= 300) - - -/* -------------------------------------------------------------------- -** -** WALKER STRUCTURE -*/ - -enum { - DAV_CALLTYPE_MEMBER = 1, /* called for a member resource */ - DAV_CALLTYPE_COLLECTION, /* called for a collection */ - DAV_CALLTYPE_LOCKNULL /* called for a locknull resource */ -}; - -typedef struct -{ - /* the client-provided context */ - void *walk_ctx; - - /* pool to use for allocations in the callback */ - apr_pool_t *pool; - - /* the current resource */ - const dav_resource *resource; - - /* OUTPUT: add responses to this */ - dav_response *response; - -} dav_walk_resource; - -typedef struct -{ - int walk_type; -#define DAV_WALKTYPE_AUTH 0x0001 /* limit to authorized files */ -#define DAV_WALKTYPE_NORMAL 0x0002 /* walk normal files */ -#define DAV_WALKTYPE_LOCKNULL 0x0004 /* walk locknull resources */ - - /* callback function and a client context for the walk */ - dav_error * (*func)(dav_walk_resource *wres, int calltype); - void *walk_ctx; - - /* what pool to use for allocations needed by walk logic */ - apr_pool_t *pool; - - /* beginning root of the walk */ - const dav_resource *root; - - /* lock database to enable walking LOCKNULL resources */ - dav_lockdb *lockdb; - -} dav_walk_params; - -/* directory tree walking context */ -typedef struct dav_walker_ctx -{ - /* input: */ - dav_walk_params w; - - - /* ### client data... phasing out this big glom */ - - /* this brigade buffers data being sent to r->output_filters */ - apr_bucket_brigade *bb; - - /* a scratch pool, used to stream responses and iteratively cleared. */ - apr_pool_t *scratchpool; - - request_rec *r; /* original request */ - - /* for PROPFIND operations */ - apr_xml_doc *doc; - int propfind_type; -#define DAV_PROPFIND_IS_ALLPROP 1 -#define DAV_PROPFIND_IS_PROPNAME 2 -#define DAV_PROPFIND_IS_PROP 3 - - apr_text *propstat_404; /* (cached) propstat giving a 404 error */ - - const dav_if_header *if_header; /* for validation */ - const dav_locktoken *locktoken; /* for UNLOCK */ - const dav_lock *lock; /* for LOCK */ - int skip_root; /* for dav_inherit_locks() */ - - int flags; - - dav_buffer work_buf; /* for dav_validate_request() */ - -} dav_walker_ctx; - -DAV_DECLARE(void) dav_add_response(dav_walk_resource *wres, - int status, - dav_get_props_result *propstats); - - -/* -------------------------------------------------------------------- -** -** "STREAM" STRUCTURE -** -** mod_dav uses this abstraction for interacting with the repository -** while fetching/storing resources. mod_dav views resources as a stream -** of bytes. -** -** Note that the structure is opaque -- it is private to the repository -** that created the stream in the repository's "open" function. -** -** ### THIS STUFF IS GOING AWAY ... GET/read requests are handled by -** ### having the provider jam stuff straight into the filter stack. -** ### this is only left for handling PUT/write requests. -*/ - -typedef struct dav_stream dav_stream; - -typedef enum { - DAV_MODE_WRITE_TRUNC, /* truncate and open for writing */ - DAV_MODE_WRITE_SEEKABLE /* open for writing; random access */ -} dav_stream_mode; - - -/* -------------------------------------------------------------------- -** -** REPOSITORY FUNCTIONS -*/ - -/* Repository provider hooks */ -struct dav_hooks_repository -{ - /* Flag for whether repository requires special GET handling. - * If resources in the repository are not visible in the - * filesystem location which URLs map to, then special handling - * is required to first fetch a resource from the repository, - * respond to the GET request, then free the resource copy. - */ - int handle_get; - - /* Get a resource descriptor for the URI in a request. A descriptor - * should always be returned even if the resource does not exist. This - * repository has been identified as handling the resource given by - * the URI, so an answer must be given. If there is a problem with the - * URI or accessing the resource or whatever, then an error should be - * returned. - * - * root_dir: - * the root of the directory for which this repository is configured. - * - * label: - * if a Label: header is present (and allowed), this is the label - * to use to identify a version resource from the resource's - * corresponding version history. Otherwise, it will be NULL. - * - * use_checked_in: - * use the DAV:checked-in property of the resource identified by the - * Request-URI to identify and return a version resource - * - * The provider may associate the request storage pool with the resource - * (in the resource->pool field), to use in other operations on that - * resource. - */ - dav_error * (*get_resource)( - request_rec *r, - const char *root_dir, - const char *label, - int use_checked_in, - dav_resource **resource - ); - - /* Get a resource descriptor for the parent of the given resource. - * The resources need not exist. NULL is returned if the resource - * is the root collection. - * - * An error should be returned only if there is a fatal error in - * fetching information about the parent resource. - */ - dav_error * (*get_parent_resource)( - const dav_resource *resource, - dav_resource **parent_resource - ); - - /* Determine whether two resource descriptors refer to the same resource. - * - * Result != 0 => the resources are the same. - */ - int (*is_same_resource)( - const dav_resource *res1, - const dav_resource *res2 - ); - - /* Determine whether one resource is a parent (immediate or otherwise) - * of another. - * - * Result != 0 => res1 is a parent of res2. - */ - int (*is_parent_resource)( - const dav_resource *res1, - const dav_resource *res2 - ); - - /* - ** Open a stream for this resource, using the specified mode. The - ** stream will be returned in *stream. - */ - dav_error * (*open_stream)(const dav_resource *resource, - dav_stream_mode mode, - dav_stream **stream); - - /* - ** Close the specified stream. - ** - ** mod_dav will (ideally) make sure to call this. For safety purposes, - ** a provider should (ideally) register a cleanup function with the - ** request pool to get this closed and cleaned up. - ** - ** Note the possibility of an error from the close -- it is entirely - ** feasible that the close does a "commit" of some kind, which can - ** produce an error. - ** - ** commit should be TRUE (non-zero) or FALSE (0) if the stream was - ** opened for writing. This flag states whether to retain the file - ** or not. - ** Note: the commit flag is ignored for streams opened for reading. - */ - dav_error * (*close_stream)(dav_stream *stream, int commit); - - /* - ** Write data to the stream. - ** - ** All of the bytes must be written, or an error should be returned. - */ - dav_error * (*write_stream)(dav_stream *stream, - const void *buf, apr_size_t bufsize); - - /* - ** Seek to an absolute position in the stream. This is used to support - ** Content-Range in a GET/PUT. - ** - ** NOTE: if this function is NULL (which is allowed), then any - ** operations using Content-Range will be refused. - */ - dav_error * (*seek_stream)(dav_stream *stream, apr_off_t abs_position); - - /* - ** If a GET is processed using a stream (open_stream, read_stream) - ** rather than via a sub-request (on get_pathname), then this function - ** is used to provide the repository with a way to set the headers - ** in the response. - ** - ** This function may be called without a following deliver(), to - ** handle a HEAD request. - ** - ** This may be NULL if handle_get is FALSE. - */ - dav_error * (*set_headers)(request_rec *r, - const dav_resource *resource); - - /* - ** The provider should deliver the resource into the specified filter. - ** Basically, this is the response to the GET method. - ** - ** Note that this is called for all resources, including collections. - ** The provider should determine what has content to deliver or not. - ** - ** set_headers will be called prior to this function, allowing the - ** provider to set the appropriate response headers. - ** - ** This may be NULL if handle_get is FALSE. - ** ### maybe toss handle_get and just use this function as the marker - */ - dav_error * (*deliver)(const dav_resource *resource, - ap_filter_t *output); - - /* Create a collection resource. The resource must not already exist. - * - * Result == NULL if the collection was created successfully. Also, the - * resource object is updated to reflect that the resource exists, and - * is a collection. - */ - dav_error * (*create_collection)( - dav_resource *resource - ); - - /* Copy one resource to another. The destination may exist, if it is - * versioned. - * Handles both files and collections. Properties are copied as well. - * If the destination exists and is versioned, the provider must update - * the destination to have identical content to the source, - * recursively for collections. - * The depth argument is ignored for a file, and can be either 0 or - * DAV_INFINITY for a collection. - * If an error occurs in a child resource, then the return value is - * non-NULL, and *response is set to a multistatus response. - * If the copy is successful, the dst resource object is - * updated to reflect that the resource exists. - */ - dav_error * (*copy_resource)( - const dav_resource *src, - dav_resource *dst, - int depth, - dav_response **response - ); - - /* Move one resource to another. The destination must not exist. - * Handles both files and collections. Properties are moved as well. - * If an error occurs in a child resource, then the return value is - * non-NULL, and *response is set to a multistatus response. - * If the move is successful, the src and dst resource objects are - * updated to reflect that the source no longer exists, and the - * destination does. - */ - dav_error * (*move_resource)( - dav_resource *src, - dav_resource *dst, - dav_response **response - ); - - /* Remove a resource. Handles both files and collections. - * Removes any associated properties as well. - * If an error occurs in a child resource, then the return value is - * non-NULL, and *response is set to a multistatus response. - * If the delete is successful, the resource object is updated to - * reflect that the resource no longer exists. - */ - dav_error * (*remove_resource)( - dav_resource *resource, - dav_response **response - ); - - /* Walk a resource hierarchy. - * - * Iterates over the resource hierarchy specified by params->root. - * Control of the walk and the callback are specified by 'params'. - * - * An error may be returned. *response will contain multistatus - * responses (if any) suitable for the body of the error. It is also - * possible to return NULL, yet still have multistatus responses. - * In this case, typically the caller should return a 207 (Multistatus) - * and the responses (in the body) as the HTTP response. - */ - dav_error * (*walk)(const dav_walk_params *params, int depth, - dav_response **response); - - /* Get the entity tag for a resource */ - const char * (*getetag)(const dav_resource *resource); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; -}; - - -/* -------------------------------------------------------------------- -** -** VERSIONING FUNCTIONS -*/ - - -/* dav_add_vary_header - * - * If there were any headers in the request which require a Vary header - * in the response, add it. - */ -DAV_DECLARE(void) dav_add_vary_header(request_rec *in_req, - request_rec *out_req, - const dav_resource *resource); - -/* -** Flags specifying auto-versioning behavior, returned by -** the auto_versionable hook. The value returned depends -** on both the state of the resource and the value of the -** DAV:auto-versioning property for the resource. -** -** If the resource does not exist (null or lock-null), -** DAV_AUTO_VERSION_ALWAYS causes creation of a new version-controlled resource -** -** If the resource is checked in, -** DAV_AUTO_VERSION_ALWAYS causes it to be checked out always, -** DAV_AUTO_VERSION_LOCKED causes it to be checked out only when locked -** -** If the resource is checked out, -** DAV_AUTO_VERSION_ALWAYS causes it to be checked in always, -** DAV_AUTO_VERSION_LOCKED causes it to be checked in when unlocked -** (note: a provider should allow auto-checkin only for resources which -** were automatically checked out) -** -** In all cases, DAV_AUTO_VERSION_NEVER results in no auto-versioning behavior. -*/ -typedef enum { - DAV_AUTO_VERSION_NEVER, - DAV_AUTO_VERSION_ALWAYS, - DAV_AUTO_VERSION_LOCKED -} dav_auto_version; - -/* -** This structure is used to record what auto-versioning operations -** were done to make a resource writable, so that they can be undone -** at the end of a request. -*/ -typedef struct { - int resource_versioned; /* 1 => resource was auto-version-controlled */ - int resource_checkedout; /* 1 => resource was auto-checked-out */ - int parent_checkedout; /* 1 => parent was auto-checked-out */ - dav_resource *parent_resource; /* parent resource, if it was needed */ -} dav_auto_version_info; - -/* Ensure that a resource is writable. If there is no versioning - * provider, then this is essentially a no-op. Versioning repositories - * require explicit resource creation and checkout before they can - * be written to. If a new resource is to be created, or an existing - * resource deleted, the parent collection must be checked out as well. - * - * Set the parent_only flag to only make the parent collection writable. - * Otherwise, both parent and child are made writable as needed. If the - * child does not exist, then a new versioned resource is created and - * checked out. - * - * If auto-versioning is not enabled for a versioned resource, then an error is - * returned, since the resource cannot be modified. - * - * The dav_auto_version_info structure is filled in with enough information - * to restore both parent and child resources to the state they were in - * before the auto-versioning operations occurred. - */ -DAV_DECLARE(dav_error *) dav_auto_checkout( - request_rec *r, - dav_resource *resource, - int parent_only, - dav_auto_version_info *av_info); - -/* Revert the writability of resources back to what they were - * before they were modified. If undo == 0, then the resource - * modifications are maintained (i.e. they are checked in). - * If undo != 0, then resource modifications are discarded - * (i.e. they are unchecked out). - * - * Set the unlock flag to indicate that the resource is about - * to be unlocked; it will be checked in if the resource - * auto-versioning property indicates it should be. In this case, - * av_info is ignored, so it can be NULL. - * - * The resource argument may be NULL if only the parent resource - * was checked out (i.e. the parent_only was != 0 in the - * dav_auto_checkout call). - */ -DAV_DECLARE(dav_error *) dav_auto_checkin( - request_rec *r, - dav_resource *resource, - int undo, - int unlock, - dav_auto_version_info *av_info); - -/* -** This structure is used to describe available reports -** -** "nmspace" should be valid XML and URL-quoted. mod_dav will place -** double-quotes around it and use it in an xmlns declaration. -*/ -typedef struct { - const char *nmspace; /* namespace of the XML report element */ - const char *name; /* element name for the XML report */ -} dav_report_elem; - - -/* Versioning provider hooks */ -struct dav_hooks_vsn -{ - /* - ** MANDATORY HOOKS - ** The following hooks are mandatory for all versioning providers; - ** they define the functionality needed to implement "core" versioning. - */ - - /* Return supported versioning options. - * Each dav_text item in the list will be returned as a separate - * DAV header. Providers are advised to limit the length of an - * individual text item to 63 characters, to conform to the limit - * used by MS Web Folders. - */ - void (*get_vsn_options)(apr_pool_t *p, apr_text_header *phdr); - - /* Get the value of a specific option for an OPTIONS request. - * The option being requested is given by the parsed XML - * element object "elem". The value of the option should be - * appended to the "option" text object. - */ - dav_error * (*get_option)(const dav_resource *resource, - const apr_xml_elem *elem, - apr_text_header *option); - - /* Determine whether a non-versioned (or non-existent) resource - * is versionable. Returns != 0 if resource can be versioned. - */ - int (*versionable)(const dav_resource *resource); - - /* Determine whether auto-versioning is enabled for a resource - * (which may not exist, or may not be versioned). If the resource - * is a checked-out resource, the provider must only enable - * auto-checkin if the resource was automatically checked out. - * - * The value returned depends on both the state of the resource - * and the value of its DAV:auto-version property. See the description - * of the dav_auto_version enumeration above for the details. - */ - dav_auto_version (*auto_versionable)(const dav_resource *resource); - - /* Put a resource under version control. If the resource already - * exists unversioned, then it becomes the initial version of the - * new version history, and it is replaced by a version selector - * which targets the new version. - * - * If the resource does not exist, then a new version-controlled - * resource is created which either targets an existing version (if the - * "target" argument is not NULL), or the initial, empty version - * in a new history resource (if the "target" argument is NULL). - * - * If successful, the resource object state is updated appropriately - * (that is, changed to refer to the new version-controlled resource). - */ - dav_error * (*vsn_control)(dav_resource *resource, - const char *target); - - /* Checkout a resource. If successful, the resource - * object state is updated appropriately. - * - * The auto_checkout flag will be set if this checkout is being - * done automatically, as part of some method which modifies - * the resource. The provider must remember that the resource - * was automatically checked out, so it can determine whether it - * can be automatically checked in. (Auto-checkin should only be - * enabled for resources which were automatically checked out.) - * - * If the working resource has a different URL from the - * target resource, a dav_resource descriptor is returned - * for the new working resource. Otherwise, the original - * resource descriptor will refer to the working resource. - * The working_resource argument can be NULL if the caller - * is not interested in the working resource. - * - * If the client has specified DAV:unreserved or DAV:fork-ok in the - * checkout request, then the corresponding flags are set. If - * DAV:activity-set has been specified, then create_activity is set - * if DAV:new was specified; otherwise, the DAV:href elements' CDATA - * (the actual href text) is passed in the "activities" array (each - * element of the array is a const char *). activities will be NULL - * no DAV:activity-set was provided or when create_activity is set. - */ - dav_error * (*checkout)(dav_resource *resource, - int auto_checkout, - int is_unreserved, int is_fork_ok, - int create_activity, - apr_array_header_t *activities, - dav_resource **working_resource); - - /* Uncheckout a checked-out resource. If successful, the resource - * object state is updated appropriately. - */ - dav_error * (*uncheckout)(dav_resource *resource); - - /* Checkin a checked-out resource. If successful, the resource - * object state is updated appropriately, and the - * version_resource descriptor will refer to the new version. - * The version_resource argument can be NULL if the caller - * is not interested in the new version resource. - * - * If the client has specified DAV:keep-checked-out in the checkin - * request, then the keep_checked_out flag is set. The provider - * should create a new version, but keep the resource in the - * checked-out state. - */ - dav_error * (*checkin)(dav_resource *resource, - int keep_checked_out, - dav_resource **version_resource); - - /* - ** Return the set of reports available at this resource. - ** - ** An array of report elements should be returned, with an end-marker - ** element containing namespace==NULL. The value of the - ** DAV:supported-report-set property will be constructed and - ** returned. - */ - dav_error * (*avail_reports)(const dav_resource *resource, - const dav_report_elem **reports); - - /* - ** Determine whether a Label header can be used - ** with a particular report. The dav_xml_doc structure - ** contains the parsed report request body. - ** Returns 0 if the Label header is not allowed. - */ - int (*report_label_header_allowed)(const apr_xml_doc *doc); - - /* - ** Generate a report on a resource. Since a provider is free - ** to define its own reports, and the value of request headers - ** may affect the interpretation of a report, the request record - ** must be passed to this routine. - ** - ** The dav_xml_doc structure contains the parsed report request - ** body. The report response should be generated into the specified - ** output filter. - ** - ** If an error occurs, and a response has not yet been generated, - ** then an error can be returned from this function. mod_dav will - ** construct an appropriate error response. Once some output has - ** been placed into the filter, however, the provider should not - ** return an error -- there is no way that mod_dav can deliver it - ** properly. - ** - ** ### maybe we need a way to signal an error anyways, and then - ** ### apache can abort the connection? - */ - dav_error * (*deliver_report)(request_rec *r, - const dav_resource *resource, - const apr_xml_doc *doc, - ap_filter_t *output); - - /* - ** OPTIONAL HOOKS - ** The following hooks are optional; if not defined, then the - ** corresponding protocol methods will be unsupported. - */ - - /* - ** Set the state of a checked-in version-controlled resource. - ** - ** If the request specified a version, the version resource - ** represents that version. If the request specified a label, - ** then "version" is NULL, and "label" is the label. - ** - ** The depth argument is ignored for a file, and can be 0, 1, or - ** DAV_INFINITY for a collection. The depth argument only applies - ** with a label, not a version. - ** - ** If an error occurs in a child resource, then the return value is - ** non-NULL, and *response is set to a multistatus response. - ** - ** This hook is optional; if not defined, then the UPDATE method - ** will not be supported. - */ - dav_error * (*update)(const dav_resource *resource, - const dav_resource *version, - const char *label, - int depth, - dav_response **response); - - /* - ** Add a label to a version. The resource is either a specific - ** version, or a version selector, in which case the label should - ** be added to the current target of the version selector. The - ** version selector cannot be checked out. - ** - ** If replace != 0, any existing label by the same name is - ** effectively deleted first. Otherwise, it is an error to - ** attempt to add a label which already exists on some version - ** of the same history resource. - ** - ** This hook is optional; if not defined, then the LABEL method - ** will not be supported. If it is defined, then the remove_label - ** hook must be defined also. - */ - dav_error * (*add_label)(const dav_resource *resource, - const char *label, - int replace); - - /* - ** Remove a label from a version. The resource is either a specific - ** version, or a version selector, in which case the label should - ** be added to the current target of the version selector. The - ** version selector cannot be checked out. - ** - ** It is an error if no such label exists on the specified version. - ** - ** This hook is optional, but if defined, the add_label hook - ** must be defined also. - */ - dav_error * (*remove_label)(const dav_resource *resource, - const char *label); - - /* - ** Determine whether a null resource can be created as a workspace. - ** The provider may restrict workspaces to certain locations. - ** Returns 0 if the resource cannot be a workspace. - ** - ** This hook is optional; if the provider does not support workspaces, - ** it should be set to NULL. - */ - int (*can_be_workspace)(const dav_resource *resource); - - /* - ** Create a workspace resource. The resource must not already - ** exist. Any <DAV:mkworkspace> element is passed to the provider - ** in the "doc" structure; it may be empty. - ** - ** If workspace creation is succesful, the state of the resource - ** object is updated appropriately. - ** - ** This hook is optional; if the provider does not support workspaces, - ** it should be set to NULL. - */ - dav_error * (*make_workspace)(dav_resource *resource, - apr_xml_doc *doc); - - /* - ** Determine whether a null resource can be created as an activity. - ** The provider may restrict activities to certain locations. - ** Returns 0 if the resource cannot be an activity. - ** - ** This hook is optional; if the provider does not support activities, - ** it should be set to NULL. - */ - int (*can_be_activity)(const dav_resource *resource); - - /* - ** Create an activity resource. The resource must not already - ** exist. - ** - ** If activity creation is succesful, the state of the resource - ** object is updated appropriately. - ** - ** This hook is optional; if the provider does not support activities, - ** it should be set to NULL. - */ - dav_error * (*make_activity)(dav_resource *resource); - - /* - ** Merge a resource (tree) into target resource (tree). - ** - ** ### more doc... - ** - ** This hook is optional; if the provider does not support merging, - ** then this should be set to NULL. - */ - dav_error * (*merge)(dav_resource *target, dav_resource *source, - int no_auto_merge, int no_checkout, - apr_xml_elem *prop_elem, - ap_filter_t *output); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; -}; - - -/* -------------------------------------------------------------------- -** -** BINDING FUNCTIONS -*/ - -/* binding provider hooks */ -struct dav_hooks_binding { - - /* Determine whether a resource can be the target of a binding. - * Returns 0 if the resource cannot be a binding target. - */ - int (*is_bindable)(const dav_resource *resource); - - /* Create a binding to a resource. - * The resource argument is the target of the binding; - * the binding argument must be a resource which does not already - * exist. - */ - dav_error * (*bind_resource)(const dav_resource *resource, - dav_resource *binding); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; - -}; - - -/* -------------------------------------------------------------------- -** -** SEARCH(DASL) FUNCTIONS -*/ - -/* search provider hooks */ -struct dav_hooks_search { - /* Set header for a OPTION method - * An error may be returned. - * To set a hadder, this function might call - * apr_table_setn(r->headers_out, "DASL", dasl_optin1); - * - * Examples: - * DASL: <DAV:basicsearch> - * DASL: <http://foo.bar.com/syntax1> - * DASL: <http://akuma.com/syntax2> - */ - dav_error * (*set_option_head)(request_rec *r); - - /* Search resources - * An error may be returned. *response will contain multistatus - * responses (if any) suitable for the body of the error. It is also - * possible to return NULL, yet still have multistatus responses. - * In this case, typically the caller should return a 207 (Multistatus) - * and the responses (in the body) as the HTTP response. - */ - dav_error * (*search_resource)(request_rec *r, - dav_response **response); - - /* - ** If a provider needs a context to associate with this hooks structure, - ** then this field may be used. In most cases, it will just be NULL. - */ - void *ctx; - -}; - - -/* -------------------------------------------------------------------- -** -** MISCELLANEOUS STUFF -*/ - -/* fetch the "LimitXMLRequestBody" in force for this resource */ -DAV_DECLARE(apr_size_t) dav_get_limit_xml_body(const request_rec *r); - -typedef struct { - int propid; /* live property ID */ - const dav_hooks_liveprop *provider; /* the provider defining this prop */ -} dav_elem_private; - -#ifdef __cplusplus -} -#endif - -#endif /* _MOD_DAV_H_ */ diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/modules.mk b/rubbos/app/httpd-2.0.64/modules/dav/main/modules.mk deleted file mode 100644 index ceb52a1b..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/modules.mk +++ /dev/null @@ -1,3 +0,0 @@ -DISTCLEAN_TARGETS = modules.mk -static = -shared = diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/props.c b/rubbos/app/httpd-2.0.64/modules/dav/main/props.c deleted file mode 100644 index fc28532d..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/props.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV extension module for Apache 2.0.* -** - Property database handling (repository-independent) -** -** NOTES: -** -** PROPERTY DATABASE -** -** This version assumes that there is a per-resource database provider -** to record properties. The database provider decides how and where to -** store these databases. -** -** The DBM keys for the properties have the following form: -** -** namespace ":" propname -** -** For example: 5:author -** -** The namespace provides an integer index into the namespace table -** (see below). propname is simply the property name, without a namespace -** prefix. -** -** A special case exists for properties that had a prefix starting with -** "xml". The XML Specification reserves these for future use. mod_dav -** stores and retrieves them unchanged. The keys for these properties -** have the form: -** -** ":" propname -** -** The propname will contain the prefix and the property name. For -** example, a key might be ":xmlfoo:name" -** -** The ":name" style will also be used for properties that do not -** exist within a namespace. -** -** The DBM values consist of two null-terminated strings, appended -** together (the null-terms are retained and stored in the database). -** The first string is the xml:lang value for the property. An empty -** string signifies that a lang value was not in context for the value. -** The second string is the property value itself. -** -** -** NAMESPACE TABLE -** -** The namespace table is an array that lists each of the namespaces -** that are in use by the properties in the given propdb. Each entry -** in the array is a simple URI. -** -** For example: http://www.foo.bar/standards/props/ -** -** The prefix used for the property is stripped and the URI for it -** is entered into the namespace table. Also, any namespaces used -** within the property value will be entered into the table (and -** stripped from the child elements). -** -** The namespaces are stored in the DBM database under the "METADATA" key. -** -** -** STRIPPING NAMESPACES -** -** Within the property values, the namespace declarations (xmlns...) -** are stripped. Each element and attribute will have its prefix removed -** and a new prefix inserted. -** -** This must be done so that we can return multiple properties in a -** PROPFIND which may have (originally) used conflicting prefixes. For -** that case, we must bind all property value elements to new namespace -** values. -** -** This implies that clients must NOT be sensitive to the namespace -** prefix used for their properties. It WILL change when the properties -** are returned (we return them as "ns<index>", e.g. "ns5"). Also, the -** property value can contain ONLY XML elements and CDATA. PI and comment -** elements will be stripped. CDATA whitespace will be preserved, but -** whitespace within element tags will be altered. Attribute ordering -** may be altered. Element and CDATA ordering will be preserved. -** -** -** ATTRIBUTES ON PROPERTY NAME ELEMENTS -** -** When getting/setting properties, the XML used looks like: -** -** <prop> -** <propname1>value</propname1> -** <propname2>value</propname1> -** </prop> -** -** This implementation (mod_dav) DOES NOT save any attributes that are -** associated with the <propname1> element. The property value is deemed -** to be only the contents ("value" in the above example). -** -** We do store the xml:lang value (if any) that applies to the context -** of the <propname1> element. Whether the xml:lang attribute is on -** <propname1> itself, or from a higher level element, we will store it -** with the property value. -** -** -** VERSIONING -** -** The DBM db contains a key named "METADATA" that holds database-level -** information, such as the namespace table. The record also contains the -** db's version number as the very first 16-bit value. This first number -** is actually stored as two single bytes: the first byte is a "major" -** version number. The second byte is a "minor" number. -** -** If the major number is not what mod_dav expects, then the db is closed -** immediately and an error is returned. A minor number change is -** acceptable -- it is presumed that old/new dav_props.c can deal with -** the database format. For example, a newer dav_props might update the -** minor value and append information to the end of the metadata record -** (which would be ignored by previous versions). -** -** -** ISSUES: -** -** At the moment, for the dav_get_allprops() and dav_get_props() functions, -** we must return a set of xmlns: declarations for ALL known namespaces -** in the file. There isn't a way to filter this because we don't know -** which are going to be used or not. Examining property names is not -** sufficient because the property values could use entirely different -** namespaces. -** -** ==> we must devise a scheme where we can "garbage collect" the namespace -** entries from the property database. -*/ - -#include "apr.h" -#include "apr_strings.h" - -#define APR_WANT_STDIO -#define APR_WANT_BYTEFUNC -#include "apr_want.h" - -#include "mod_dav.h" - -#include "http_log.h" -#include "http_request.h" - -/* -** There is some rough support for writable DAV:getcontenttype and -** DAV:getcontentlanguage properties. If this #define is (1), then -** this support is disabled. -** -** We are disabling it because of a lack of support in GET and PUT -** operations. For GET, it would be "expensive" to look for a propdb, -** open it, and attempt to extract the Content-Type and Content-Language -** values for the response. -** (Handling the PUT would not be difficult, though) -*/ -#define DAV_DISABLE_WRITABLE_PROPS 1 - -#define DAV_EMPTY_VALUE "\0" /* TWO null terms */ - -struct dav_propdb { - apr_pool_t *p; /* the pool we should use */ - request_rec *r; /* the request record */ - - const dav_resource *resource; /* the target resource */ - - int deferred; /* open of db has been deferred */ - dav_db *db; /* underlying database containing props */ - - apr_array_header_t *ns_xlate; /* translation of an elem->ns to URI */ - dav_namespace_map *mapping; /* namespace mapping */ - - dav_lockdb *lockdb; /* the lock database */ - - dav_buffer wb_lock; /* work buffer for lockdiscovery property */ - - /* if we ever run a GET subreq, it will be stored here */ - request_rec *subreq; - - /* hooks we should use for processing (based on the target resource) */ - const dav_hooks_db *db_hooks; -}; - -/* NOTE: dav_core_props[] and the following enum must stay in sync. */ -/* ### move these into a "core" liveprop provider? */ -static const char * const dav_core_props[] = -{ - "getcontenttype", - "getcontentlanguage", - "lockdiscovery", - "supportedlock", - - NULL /* sentinel */ -}; -enum { - DAV_PROPID_CORE_getcontenttype = DAV_PROPID_CORE, - DAV_PROPID_CORE_getcontentlanguage, - DAV_PROPID_CORE_lockdiscovery, - DAV_PROPID_CORE_supportedlock, - - DAV_PROPID_CORE_UNKNOWN -}; - -/* -** This structure is used to track information needed for a rollback. -*/ -typedef struct dav_rollback_item { - /* select one of the two rollback context structures based on the - value of dav_prop_ctx.is_liveprop */ - dav_deadprop_rollback *deadprop; - dav_liveprop_rollback *liveprop; - -} dav_rollback_item; - - -static int dav_find_liveprop_provider(dav_propdb *propdb, - const char *ns_uri, - const char *propname, - const dav_hooks_liveprop **provider) -{ - int propid; - - *provider = NULL; - - if (ns_uri == NULL) { - /* policy: liveprop providers cannot define no-namespace properties */ - return DAV_PROPID_CORE_UNKNOWN; - } - - /* check liveprop providers first, so they can define core properties */ - propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname, - provider); - if (propid != 0) { - return propid; - } - - /* check for core property */ - if (strcmp(ns_uri, "DAV:") == 0) { - const char * const *p = dav_core_props; - - for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid) - if (strcmp(propname, *p) == 0) { - return propid; - } - } - - /* no provider for this property */ - return DAV_PROPID_CORE_UNKNOWN; -} - -static void dav_find_liveprop(dav_propdb *propdb, apr_xml_elem *elem) -{ - const char *ns_uri; - dav_elem_private *priv = elem->priv; - const dav_hooks_liveprop *hooks; - - - if (elem->ns == APR_XML_NS_NONE) - ns_uri = NULL; - else if (elem->ns == APR_XML_NS_DAV_ID) - ns_uri = "DAV:"; - else - ns_uri = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns); - - priv->propid = dav_find_liveprop_provider(propdb, ns_uri, elem->name, - &hooks); - - /* ### this test seems redundant... */ - if (priv->propid != DAV_PROPID_CORE_UNKNOWN) { - priv->provider = hooks; - } -} - -/* is the live property read/write? */ -static int dav_rw_liveprop(dav_propdb *propdb, dav_elem_private *priv) -{ - int propid = priv->propid; - - /* - ** Check the liveprop provider (if this is a provider-defined prop) - */ - if (priv->provider != NULL) { - return (*priv->provider->is_writable)(propdb->resource, propid); - } - - /* these are defined as read-only */ - if (propid == DAV_PROPID_CORE_lockdiscovery -#if DAV_DISABLE_WRITABLE_PROPS - || propid == DAV_PROPID_CORE_getcontenttype - || propid == DAV_PROPID_CORE_getcontentlanguage -#endif - || propid == DAV_PROPID_CORE_supportedlock - ) { - - return 0; - } - - /* these are defined as read/write */ - if (propid == DAV_PROPID_CORE_getcontenttype - || propid == DAV_PROPID_CORE_getcontentlanguage - || propid == DAV_PROPID_CORE_UNKNOWN) { - - return 1; - } - - /* - ** We don't recognize the property, so it must be dead (and writable) - */ - return 1; -} - -/* do a sub-request to fetch properties for the target resource's URI. */ -static void dav_do_prop_subreq(dav_propdb *propdb) -{ - /* perform a "GET" on the resource's URI (note that the resource - may not correspond to the current request!). */ - propdb->subreq = ap_sub_req_lookup_uri(propdb->resource->uri, propdb->r, - NULL); -} - -static dav_error * dav_insert_coreprop(dav_propdb *propdb, - int propid, const char *name, - dav_prop_insert what, - apr_text_header *phdr, - dav_prop_insert *inserted) -{ - const char *value = NULL; - dav_error *err; - - *inserted = DAV_PROP_INSERT_NOTDEF; - - /* fast-path the common case */ - if (propid == DAV_PROPID_CORE_UNKNOWN) - return NULL; - - switch (propid) { - - case DAV_PROPID_CORE_lockdiscovery: - if (propdb->lockdb != NULL) { - dav_lock *locks; - - if ((err = dav_lock_query(propdb->lockdb, propdb->resource, - &locks)) != NULL) { - return dav_push_error(propdb->p, err->status, 0, - "DAV:lockdiscovery could not be " - "determined due to a problem fetching " - "the locks for this resource.", - err); - } - - /* fast-path the no-locks case */ - if (locks == NULL) { - value = ""; - } - else { - /* - ** This may modify the buffer. value may point to - ** wb_lock.pbuf or a string constant. - */ - value = dav_lock_get_activelock(propdb->r, locks, - &propdb->wb_lock); - - /* make a copy to isolate it from changes to wb_lock */ - value = apr_pstrdup(propdb->p, propdb->wb_lock.buf); - } - } - break; - - case DAV_PROPID_CORE_supportedlock: - if (propdb->lockdb != NULL) { - value = (*propdb->lockdb->hooks->get_supportedlock)(propdb->resource); - } - break; - - case DAV_PROPID_CORE_getcontenttype: - if (propdb->subreq == NULL) { - dav_do_prop_subreq(propdb); - } - if (propdb->subreq->content_type != NULL) { - value = propdb->subreq->content_type; - } - break; - - case DAV_PROPID_CORE_getcontentlanguage: - { - const char *lang; - - if (propdb->subreq == NULL) { - dav_do_prop_subreq(propdb); - } - if ((lang = apr_table_get(propdb->subreq->headers_out, - "Content-Language")) != NULL) { - value = lang; - } - break; - } - - default: - /* fall through to interpret as a dead property */ - break; - } - - /* if something was supplied, then insert it */ - if (value != NULL) { - const char *s; - - if (what == DAV_PROP_INSERT_SUPPORTED) { - /* use D: prefix to refer to the DAV: namespace URI, - * and let the namespace attribute default to "DAV:" - */ - s = apr_psprintf(propdb->p, - "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR, - name); - } - else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') { - /* use D: prefix to refer to the DAV: namespace URI */ - s = apr_psprintf(propdb->p, "<D:%s>%s</D:%s>" DEBUG_CR, - name, value, name); - } - else { - /* use D: prefix to refer to the DAV: namespace URI */ - s = apr_psprintf(propdb->p, "<D:%s/>" DEBUG_CR, name); - } - apr_text_append(propdb->p, phdr, s); - - *inserted = what; - } - - return NULL; -} - -static dav_error * dav_insert_liveprop(dav_propdb *propdb, - const apr_xml_elem *elem, - dav_prop_insert what, - apr_text_header *phdr, - dav_prop_insert *inserted) -{ - dav_elem_private *priv = elem->priv; - - *inserted = DAV_PROP_INSERT_NOTDEF; - - if (priv->provider == NULL) { - /* this is a "core" property that we define */ - return dav_insert_coreprop(propdb, priv->propid, elem->name, - what, phdr, inserted); - } - - /* ask the provider (that defined this prop) to insert the prop */ - *inserted = (*priv->provider->insert_prop)(propdb->resource, priv->propid, - what, phdr); - - return NULL; -} - -static void dav_output_prop_name(apr_pool_t *pool, - const dav_prop_name *name, - dav_xmlns_info *xi, - apr_text_header *phdr) -{ - const char *s; - - if (*name->ns == '\0') - s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name->name); - else { - const char *prefix = dav_xmlns_add_uri(xi, name->ns); - - s = apr_psprintf(pool, "<%s:%s/>" DEBUG_CR, prefix, name->name); - } - - apr_text_append(pool, phdr, s); -} - -static void dav_insert_xmlns(apr_pool_t *p, const char *pre_prefix, int ns, - const char *ns_uri, apr_text_header *phdr) -{ - const char *s; - - s = apr_psprintf(p, " xmlns:%s%d=\"%s\"", pre_prefix, ns, ns_uri); - apr_text_append(p, phdr, s); -} - -static dav_error *dav_really_open_db(dav_propdb *propdb, int ro) -{ - dav_error *err; - - /* we're trying to open the db; turn off the 'deferred' flag */ - propdb->deferred = 0; - - /* ask the DB provider to open the thing */ - err = (*propdb->db_hooks->open)(propdb->p, propdb->resource, ro, - &propdb->db); - if (err != NULL) { - return dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_PROP_OPENING, - "Could not open the property database.", - err); - } - - /* - ** NOTE: propdb->db could be NULL if we attempted to open a readonly - ** database that doesn't exist. If we require read/write - ** access, then a database was created and opened. - */ - - return NULL; -} - -DAV_DECLARE(dav_error *)dav_open_propdb(request_rec *r, dav_lockdb *lockdb, - const dav_resource *resource, - int ro, - apr_array_header_t * ns_xlate, - dav_propdb **p_propdb) -{ - dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb)); - - *p_propdb = NULL; - -#if DAV_DEBUG - if (resource->uri == NULL) { - return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "INTERNAL DESIGN ERROR: resource must define " - "its URI."); - } -#endif - - propdb->r = r; - propdb->p = r->pool; /* ### get rid of this */ - propdb->resource = resource; - propdb->ns_xlate = ns_xlate; - - propdb->db_hooks = DAV_GET_HOOKS_PROPDB(r); - - propdb->lockdb = lockdb; - - /* always defer actual open, to avoid expense of accessing db - * when only live properties are involved - */ - propdb->deferred = 1; - - /* ### what to do about closing the propdb on server failure? */ - - *p_propdb = propdb; - return NULL; -} - -DAV_DECLARE(void) dav_close_propdb(dav_propdb *propdb) -{ - if (propdb->db == NULL) - return; - - (*propdb->db_hooks->close)(propdb->db); -} - -DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb, - dav_prop_insert what) -{ - const dav_hooks_db *db_hooks = propdb->db_hooks; - apr_text_header hdr = { 0 }; - apr_text_header hdr_ns = { 0 }; - dav_get_props_result result = { 0 }; - int found_contenttype = 0; - int found_contentlang = 0; - dav_prop_insert unused_inserted; - - /* if not just getting supported live properties, - * scan all properties in the dead prop database - */ - if (what != DAV_PROP_INSERT_SUPPORTED) { - if (propdb->deferred) { - /* ### what to do with db open error? */ - (void) dav_really_open_db(propdb, 1 /*ro*/); - } - - /* initialize the result with some start tags... */ - apr_text_append(propdb->p, &hdr, - "<D:propstat>" DEBUG_CR - "<D:prop>" DEBUG_CR); - - /* if there ARE properties, then scan them */ - if (propdb->db != NULL) { - dav_xmlns_info *xi = dav_xmlns_create(propdb->p); - dav_prop_name name; - - /* define (up front) any namespaces the db might need */ - (void) (*db_hooks->define_namespaces)(propdb->db, xi); - - /* get the first property name, beginning the scan */ - (void) (*db_hooks->first_name)(propdb->db, &name); - while (name.ns != NULL) { - - /* - ** We also look for <DAV:getcontenttype> and - ** <DAV:getcontentlanguage>. If they are not stored as dead - ** properties, then we need to perform a subrequest to get - ** their values (if any). - */ - if (*name.ns == 'D' && strcmp(name.ns, "DAV:") == 0 - && *name.name == 'g') { - if (strcmp(name.name, "getcontenttype") == 0) { - found_contenttype = 1; - } - else if (strcmp(name.name, "getcontentlanguage") == 0) { - found_contentlang = 1; - } - } - - if (what == DAV_PROP_INSERT_VALUE) { - dav_error *err; - int found; - - if ((err = (*db_hooks->output_value)(propdb->db, &name, - xi, &hdr, - &found)) != NULL) { - /* ### anything better to do? */ - /* ### probably should enter a 500 error */ - goto next_key; - } - /* assert: found == 1 */ - } - else { - /* the value was not requested, so just add an empty - tag specifying the property name. */ - dav_output_prop_name(propdb->p, &name, xi, &hdr); - } - - next_key: - (void) (*db_hooks->next_name)(propdb->db, &name); - } - - /* all namespaces have been entered into xi. generate them into - the output now. */ - dav_xmlns_generate(xi, &hdr_ns); - - } /* propdb->db != NULL */ - - /* add namespaces for all the liveprop providers */ - dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns); - } - - /* ask the liveprop providers to insert their properties */ - dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr); - - /* insert the standard properties */ - /* ### should be handling the return errors here */ - (void)dav_insert_coreprop(propdb, - DAV_PROPID_CORE_supportedlock, "supportedlock", - what, &hdr, &unused_inserted); - (void)dav_insert_coreprop(propdb, - DAV_PROPID_CORE_lockdiscovery, "lockdiscovery", - what, &hdr, &unused_inserted); - - /* if we didn't find these, then do the whole subreq thing. */ - if (!found_contenttype) { - /* ### should be handling the return error here */ - (void)dav_insert_coreprop(propdb, - DAV_PROPID_CORE_getcontenttype, - "getcontenttype", - what, &hdr, &unused_inserted); - } - if (!found_contentlang) { - /* ### should be handling the return error here */ - (void)dav_insert_coreprop(propdb, - DAV_PROPID_CORE_getcontentlanguage, - "getcontentlanguage", - what, &hdr, &unused_inserted); - } - - /* if not just reporting on supported live props, - * terminate the result */ - if (what != DAV_PROP_INSERT_SUPPORTED) { - apr_text_append(propdb->p, &hdr, - "</D:prop>" DEBUG_CR - "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR - "</D:propstat>" DEBUG_CR); - } - - result.propstats = hdr.first; - result.xmlns = hdr_ns.first; - return result; -} - -DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb, - apr_xml_doc *doc) -{ - const dav_hooks_db *db_hooks = propdb->db_hooks; - apr_xml_elem *elem = dav_find_child(doc->root, "prop"); - apr_text_header hdr_good = { 0 }; - apr_text_header hdr_bad = { 0 }; - apr_text_header hdr_ns = { 0 }; - int have_good = 0; - dav_get_props_result result = { 0 }; - char *marks_liveprop; - dav_xmlns_info *xi; - int xi_filled = 0; - - /* ### NOTE: we should pass in TWO buffers -- one for keys, one for - the marks */ - - /* we will ALWAYS provide a "good" result, even if it is EMPTY */ - apr_text_append(propdb->p, &hdr_good, - "<D:propstat>" DEBUG_CR - "<D:prop>" DEBUG_CR); - - /* ### the marks should be in a buffer! */ - /* allocate zeroed-memory for the marks. These marks indicate which - liveprop namespaces we've generated into the output xmlns buffer */ - - /* same for the liveprops */ - marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1); - - xi = dav_xmlns_create(propdb->p); - - for (elem = elem->first_child; elem; elem = elem->next) { - dav_elem_private *priv; - dav_error *err; - dav_prop_insert inserted; - dav_prop_name name; - - /* - ** First try live property providers; if they don't handle - ** the property, then try looking it up in the propdb. - */ - - if (elem->priv == NULL) { - elem->priv = apr_pcalloc(propdb->p, sizeof(*priv)); - } - priv = elem->priv; - - /* cache the propid; dav_get_props() could be called many times */ - if (priv->propid == 0) - dav_find_liveprop(propdb, elem); - - if (priv->propid != DAV_PROPID_CORE_UNKNOWN) { - - /* insert the property. returns 1 if an insertion was done. */ - if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE, - &hdr_good, &inserted)) != NULL) { - /* ### need to propagate the error to the caller... */ - /* ### skip it for now, as if nothing was inserted */ - } - if (inserted == DAV_PROP_INSERT_VALUE) { - have_good = 1; - - /* - ** Add the liveprop's namespace URIs. Note that provider==NULL - ** for core properties. - */ - if (priv->provider != NULL) { - const char * const * scan_ns_uri; - - for (scan_ns_uri = priv->provider->namespace_uris; - *scan_ns_uri != NULL; - ++scan_ns_uri) { - int ns; - - ns = dav_get_liveprop_ns_index(*scan_ns_uri); - if (marks_liveprop[ns]) - continue; - marks_liveprop[ns] = 1; - - dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri, - &hdr_ns); - } - } - - /* property added. move on to the next property. */ - continue; - } - else if (inserted == DAV_PROP_INSERT_NOTDEF) { - /* nothing to do. fall thru to allow property to be handled - as a dead property */ - } -#if DAV_DEBUG - else { -#if 0 - /* ### need to change signature to return an error */ - return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0, - "INTERNAL DESIGN ERROR: insert_liveprop " - "did not insert what was asked for."); -#endif - } -#endif - } - - /* The property wasn't a live property, so look in the dead property - database. */ - - /* make sure propdb is really open */ - if (propdb->deferred) { - /* ### what to do with db open error? */ - (void) dav_really_open_db(propdb, 1 /*ro*/); - } - - if (elem->ns == APR_XML_NS_NONE) - name.ns = ""; - else - name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns); - name.name = elem->name; - - /* only bother to look if a database exists */ - if (propdb->db != NULL) { - int found; - - if ((err = (*db_hooks->output_value)(propdb->db, &name, - xi, &hdr_good, - &found)) != NULL) { - /* ### what to do? continue doesn't seem right... */ - continue; - } - - if (found) { - have_good = 1; - - /* if we haven't added the db's namespaces, then do so... */ - if (!xi_filled) { - (void) (*db_hooks->define_namespaces)(propdb->db, xi); - xi_filled = 1; - } - continue; - } - } - - /* not found as a live OR dead property. add a record to the "bad" - propstats */ - - /* make sure we've started our "bad" propstat */ - if (hdr_bad.first == NULL) { - apr_text_append(propdb->p, &hdr_bad, - "<D:propstat>" DEBUG_CR - "<D:prop>" DEBUG_CR); - } - - /* output this property's name (into the bad propstats) */ - dav_output_prop_name(propdb->p, &name, xi, &hdr_bad); - } - - apr_text_append(propdb->p, &hdr_good, - "</D:prop>" DEBUG_CR - "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR - "</D:propstat>" DEBUG_CR); - - /* default to start with the good */ - result.propstats = hdr_good.first; - - /* we may not have any "bad" results */ - if (hdr_bad.first != NULL) { - /* "close" the bad propstat */ - apr_text_append(propdb->p, &hdr_bad, - "</D:prop>" DEBUG_CR - "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR - "</D:propstat>" DEBUG_CR); - - /* if there are no good props, then just return the bad */ - if (!have_good) { - result.propstats = hdr_bad.first; - } - else { - /* hook the bad propstat to the end of the good one */ - hdr_good.last->next = hdr_bad.first; - } - } - - /* add in all the various namespaces, and return them */ - dav_xmlns_generate(xi, &hdr_ns); - result.xmlns = hdr_ns.first; - - return result; -} - -DAV_DECLARE(void) dav_get_liveprop_supported(dav_propdb *propdb, - const char *ns_uri, - const char *propname, - apr_text_header *body) -{ - int propid; - const dav_hooks_liveprop *hooks; - - propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks); - - if (propid != DAV_PROPID_CORE_UNKNOWN) { - if (hooks == NULL) { - /* this is a "core" property that we define */ - dav_prop_insert unused_inserted; - dav_insert_coreprop(propdb, propid, propname, - DAV_PROP_INSERT_SUPPORTED, body, &unused_inserted); - } - else { - (*hooks->insert_prop)(propdb->resource, propid, - DAV_PROP_INSERT_SUPPORTED, body); - } - } -} - -DAV_DECLARE_NONSTD(void) dav_prop_validate(dav_prop_ctx *ctx) -{ - dav_propdb *propdb = ctx->propdb; - apr_xml_elem *prop = ctx->prop; - dav_elem_private *priv; - - priv = ctx->prop->priv = apr_pcalloc(propdb->p, sizeof(*priv)); - - /* - ** Check to see if this is a live property, and fill the fields - ** in the XML elem, as appropriate. - ** - ** Verify that the property is read/write. If not, then it cannot - ** be SET or DELETEd. - */ - if (priv->propid == 0) { - dav_find_liveprop(propdb, prop); - - /* it's a liveprop if a provider was found */ - /* ### actually the "core" props should really be liveprops, but - ### there is no "provider" for those and the r/w props are - ### treated as dead props anyhow */ - ctx->is_liveprop = priv->provider != NULL; - } - - if (!dav_rw_liveprop(propdb, priv)) { - ctx->err = dav_new_error(propdb->p, HTTP_CONFLICT, - DAV_ERR_PROP_READONLY, - "Property is read-only."); - return; - } - - if (ctx->is_liveprop) { - int defer_to_dead = 0; - - ctx->err = (*priv->provider->patch_validate)(propdb->resource, - prop, ctx->operation, - &ctx->liveprop_ctx, - &defer_to_dead); - if (ctx->err != NULL || !defer_to_dead) - return; - - /* clear is_liveprop -- act as a dead prop now */ - ctx->is_liveprop = 0; - } - - /* - ** The property is supposed to be stored into the dead-property - ** database. Make sure the thing is truly open (and writable). - */ - if (propdb->deferred - && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) { - return; - } - - /* - ** There should be an open, writable database in here! - ** - ** Note: the database would be NULL if it was opened readonly and it - ** did not exist. - */ - if (propdb->db == NULL) { - ctx->err = dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_PROP_NO_DATABASE, - "Attempted to set/remove a property " - "without a valid, open, read/write " - "property database."); - return; - } - - if (ctx->operation == DAV_PROP_OP_SET) { - /* - ** Prep the element => propdb namespace index mapping, inserting - ** namespace URIs into the propdb that don't exist. - */ - (void) (*propdb->db_hooks->map_namespaces)(propdb->db, - propdb->ns_xlate, - &propdb->mapping); - } - else if (ctx->operation == DAV_PROP_OP_DELETE) { - /* - ** There are no checks to perform here. If a property exists, then - ** we will delete it. If it does not exist, then it does not matter - ** (see S12.13.1). - ** - ** Note that if a property does not exist, that does not rule out - ** that a SET will occur during this PROPPATCH (thusly creating it). - */ - } -} - -DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx) -{ - dav_propdb *propdb = ctx->propdb; - dav_error *err = NULL; - dav_elem_private *priv = ctx->prop->priv; - - ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback)); - - if (ctx->is_liveprop) { - err = (*priv->provider->patch_exec)(propdb->resource, - ctx->prop, ctx->operation, - ctx->liveprop_ctx, - &ctx->rollback->liveprop); - } - else { - dav_prop_name name; - - if (ctx->prop->ns == APR_XML_NS_NONE) - name.ns = ""; - else - name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns); - name.name = ctx->prop->name; - - /* save the old value so that we can do a rollback. */ - if ((err = (*propdb->db_hooks - ->get_rollback)(propdb->db, &name, - &ctx->rollback->deadprop)) != NULL) - goto error; - - if (ctx->operation == DAV_PROP_OP_SET) { - - /* Note: propdb->mapping was set in dav_prop_validate() */ - err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop, - propdb->mapping); - - /* - ** If an error occurred, then assume that we didn't change the - ** value. Remove the rollback item so that we don't try to set - ** its value during the rollback. - */ - /* ### euh... where is the removal? */ - } - else if (ctx->operation == DAV_PROP_OP_DELETE) { - - /* - ** Delete the property. Ignore errors -- the property is there, or - ** we are deleting it for a second time. - */ - /* ### but what about other errors? */ - (void) (*propdb->db_hooks->remove)(propdb->db, &name); - } - } - - error: - /* push a more specific error here */ - if (err != NULL) { - /* - ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen - ** any errors at this point. - */ - ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, - DAV_ERR_PROP_EXEC, - "Could not execute PROPPATCH.", err); - } -} - -DAV_DECLARE_NONSTD(void) dav_prop_commit(dav_prop_ctx *ctx) -{ - dav_elem_private *priv = ctx->prop->priv; - - /* - ** Note that a commit implies ctx->err is NULL. The caller should assume - ** a status of HTTP_OK for this case. - */ - - if (ctx->is_liveprop) { - (*priv->provider->patch_commit)(ctx->propdb->resource, - ctx->operation, - ctx->liveprop_ctx, - ctx->rollback->liveprop); - } -} - -DAV_DECLARE_NONSTD(void) dav_prop_rollback(dav_prop_ctx *ctx) -{ - dav_error *err = NULL; - dav_elem_private *priv = ctx->prop->priv; - - /* do nothing if there is no rollback information. */ - if (ctx->rollback == NULL) - return; - - /* - ** ### if we have an error, and a rollback occurs, then the namespace - ** ### mods should not happen at all. Basically, the namespace management - ** ### is simply a bitch. - */ - - if (ctx->is_liveprop) { - err = (*priv->provider->patch_rollback)(ctx->propdb->resource, - ctx->operation, - ctx->liveprop_ctx, - ctx->rollback->liveprop); - } - else { - err = (*ctx->propdb->db_hooks - ->apply_rollback)(ctx->propdb->db, ctx->rollback->deadprop); - } - - if (err != NULL) { - if (ctx->err == NULL) - ctx->err = err; - else { - dav_error *scan = err; - - /* hook previous errors at the end of the rollback error */ - while (scan->prev != NULL) - scan = scan->prev; - scan->prev = ctx->err; - ctx->err = err; - } - } -} diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/providers.c b/rubbos/app/httpd-2.0.64/modules/dav/main/providers.c deleted file mode 100644 index a2ccd1ca..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/providers.c +++ /dev/null @@ -1,33 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apr_pools.h" -#include "apr_hash.h" -#include "ap_provider.h" -#include "mod_dav.h" - -#define DAV_PROVIDER_GROUP "dav" - -DAV_DECLARE(void) dav_register_provider(apr_pool_t *p, const char *name, - const dav_provider *provider) -{ - ap_register_provider(p, DAV_PROVIDER_GROUP, name, "0", provider); -} - -DAV_DECLARE(const dav_provider *) dav_lookup_provider(const char *name) -{ - return ap_lookup_provider(DAV_PROVIDER_GROUP, name, "0"); -} diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/std_liveprop.c b/rubbos/app/httpd-2.0.64/modules/dav/main/std_liveprop.c deleted file mode 100644 index e97d0fda..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/std_liveprop.c +++ /dev/null @@ -1,194 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "httpd.h" -#include "util_xml.h" -#include "apr_strings.h" - -#include "mod_dav.h" - -/* forward-declare */ -static const dav_hooks_liveprop dav_core_hooks_liveprop; - -/* -** The namespace URIs that we use. There will only ever be "DAV:". -*/ -static const char * const dav_core_namespace_uris[] = -{ - "DAV:", - NULL /* sentinel */ -}; - -/* -** Define each of the core properties that this provider will handle. -** Note that all of them are in the DAV: namespace, which has a -** provider-local index of 0. -*/ -static const dav_liveprop_spec dav_core_props[] = -{ - { 0, "comment", DAV_PROPID_comment, 1 }, - { 0, "creator-displayname", DAV_PROPID_creator_displayname, 1 }, - { 0, "displayname", DAV_PROPID_displayname, 1 }, - { 0, "resourcetype", DAV_PROPID_resourcetype, 0 }, - { 0, "source", DAV_PROPID_source, 1 }, - - { 0 } /* sentinel */ -}; - -static const dav_liveprop_group dav_core_liveprop_group = -{ - dav_core_props, - dav_core_namespace_uris, - &dav_core_hooks_liveprop -}; - -static dav_prop_insert dav_core_insert_prop(const dav_resource *resource, - int propid, dav_prop_insert what, - apr_text_header *phdr) -{ - const char *value; - const char *s; - apr_pool_t *p = resource->pool; - const dav_liveprop_spec *info; - int global_ns; - - switch (propid) - { - case DAV_PROPID_resourcetype: - switch (resource->type) { - case DAV_RESOURCE_TYPE_VERSION: - if (resource->baselined) { - value = "<D:baseline/>"; - break; - } - /* fall through */ - case DAV_RESOURCE_TYPE_REGULAR: - case DAV_RESOURCE_TYPE_WORKING: - if (resource->collection) { - value = "<D:collection/>"; - } - else { - /* ### should we denote lock-null resources? */ - - value = ""; /* becomes: <D:resourcetype/> */ - } - break; - case DAV_RESOURCE_TYPE_HISTORY: - value = "<D:version-history/>"; - break; - case DAV_RESOURCE_TYPE_WORKSPACE: - value = "<D:collection/>"; - break; - case DAV_RESOURCE_TYPE_ACTIVITY: - value = "<D:activity/>"; - break; - - default: - /* ### bad juju */ - return DAV_PROP_INSERT_NOTDEF; - } - break; - - case DAV_PROPID_comment: - case DAV_PROPID_creator_displayname: - case DAV_PROPID_displayname: - case DAV_PROPID_source: - default: - /* - ** This property is known, but not defined as a liveprop. However, - ** it may be a dead property. - */ - return DAV_PROP_INSERT_NOTDEF; - } - - /* assert: value != NULL */ - - /* get the information and global NS index for the property */ - global_ns = dav_get_liveprop_info(propid, &dav_core_liveprop_group, &info); - - /* assert: info != NULL && info->name != NULL */ - - if (what == DAV_PROP_INSERT_SUPPORTED) { - s = apr_psprintf(p, - "<D:supported-live-property D:name=\"%s\" " - "D:namespace=\"%s\"/>" DEBUG_CR, - info->name, dav_core_namespace_uris[info->ns]); - } - else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') { - s = apr_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR, - global_ns, info->name, value, global_ns, info->name); - } - else { - s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name); - } - apr_text_append(p, phdr, s); - - /* we inserted what was asked for */ - return what; -} - -static int dav_core_is_writable(const dav_resource *resource, int propid) -{ - const dav_liveprop_spec *info; - - (void) dav_get_liveprop_info(propid, &dav_core_liveprop_group, &info); - return info->is_writable; -} - -static dav_error * dav_core_patch_validate(const dav_resource *resource, - const apr_xml_elem *elem, - int operation, void **context, - int *defer_to_dead) -{ - /* all of our writable props go in the dead prop database */ - *defer_to_dead = 1; - - return NULL; -} - -static const dav_hooks_liveprop dav_core_hooks_liveprop = { - dav_core_insert_prop, - dav_core_is_writable, - dav_core_namespace_uris, - dav_core_patch_validate, - NULL, /* patch_exec */ - NULL, /* patch_commit */ - NULL, /* patch_rollback */ -}; - -DAV_DECLARE_NONSTD(int) dav_core_find_liveprop( - const dav_resource *resource, - const char *ns_uri, const char *name, - const dav_hooks_liveprop **hooks) -{ - return dav_do_find_liveprop(ns_uri, name, &dav_core_liveprop_group, hooks); -} - -DAV_DECLARE_NONSTD(void) dav_core_insert_all_liveprops( - request_rec *r, - const dav_resource *resource, - dav_prop_insert what, - apr_text_header *phdr) -{ - (void) dav_core_insert_prop(resource, DAV_PROPID_resourcetype, - what, phdr); -} - -DAV_DECLARE_NONSTD(void) dav_core_register_uris(apr_pool_t *p) -{ - /* register the namespace URIs */ - dav_register_liveprop_group(p, &dav_core_liveprop_group); -} diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/util.c b/rubbos/app/httpd-2.0.64/modules/dav/main/util.c deleted file mode 100644 index 3ff3a19f..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/util.c +++ /dev/null @@ -1,2021 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV extension module for Apache 2.0.* -** - various utilities, repository-independent -*/ - -#include "apr_strings.h" -#include "apr_lib.h" - -#define APR_WANT_STRFUNC -#include "apr_want.h" - -#include "mod_dav.h" - -#include "http_request.h" -#include "http_config.h" -#include "http_vhost.h" -#include "http_log.h" -#include "http_protocol.h" - -DAV_DECLARE(dav_error*) dav_new_error(apr_pool_t *p, int status, - int error_id, const char *desc) -{ - int save_errno = errno; - dav_error *err = apr_pcalloc(p, sizeof(*err)); - - /* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */ - - err->status = status; - err->error_id = error_id; - err->desc = desc; - err->save_errno = save_errno; - - return err; -} - -DAV_DECLARE(dav_error*) dav_new_error_tag(apr_pool_t *p, int status, - int error_id, const char *desc, - const char *namespace, - const char *tagname) -{ - dav_error *err = dav_new_error(p, status, error_id, desc); - - err->tagname = tagname; - err->namespace = namespace; - - return err; -} - - -DAV_DECLARE(dav_error*) dav_push_error(apr_pool_t *p, int status, - int error_id, const char *desc, - dav_error *prev) -{ - dav_error *err = apr_pcalloc(p, sizeof(*err)); - - err->status = status; - err->error_id = error_id; - err->desc = desc; - err->prev = prev; - - return err; -} - -DAV_DECLARE(void) dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf, - apr_size_t extra_needed) -{ - /* grow the buffer if necessary */ - if (pbuf->cur_len + extra_needed > pbuf->alloc_len) { - char *newbuf; - - pbuf->alloc_len += extra_needed + DAV_BUFFER_PAD; - newbuf = apr_palloc(p, pbuf->alloc_len); - memcpy(newbuf, pbuf->buf, pbuf->cur_len); - pbuf->buf = newbuf; - } -} - -DAV_DECLARE(void) dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf, - apr_size_t size) -{ - /* NOTE: this does not retain prior contents */ - - /* NOTE: this function is used to init the first pointer, too, since - the PAD will be larger than alloc_len (0) for zeroed structures */ - - /* grow if we don't have enough for the requested size plus padding */ - if (size + DAV_BUFFER_PAD > pbuf->alloc_len) { - /* set the new length; min of MINSIZE */ - pbuf->alloc_len = size + DAV_BUFFER_PAD; - if (pbuf->alloc_len < DAV_BUFFER_MINSIZE) - pbuf->alloc_len = DAV_BUFFER_MINSIZE; - - pbuf->buf = apr_palloc(p, pbuf->alloc_len); - } - pbuf->cur_len = size; -} - - -/* initialize a buffer and copy the specified (null-term'd) string into it */ -DAV_DECLARE(void) dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, - const char *str) -{ - dav_set_bufsize(p, pbuf, strlen(str)); - memcpy(pbuf->buf, str, pbuf->cur_len + 1); -} - -/* append a string to the end of the buffer, adjust length */ -DAV_DECLARE(void) dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, - const char *str) -{ - apr_size_t len = strlen(str); - - dav_check_bufsize(p, pbuf, len + 1); - memcpy(pbuf->buf + pbuf->cur_len, str, len + 1); - pbuf->cur_len += len; -} - -/* place a string on the end of the buffer, do NOT adjust length */ -DAV_DECLARE(void) dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, - const char *str) -{ - apr_size_t len = strlen(str); - - dav_check_bufsize(p, pbuf, len + 1); - memcpy(pbuf->buf + pbuf->cur_len, str, len + 1); -} - -/* place some memory on the end of a buffer; do NOT adjust length */ -DAV_DECLARE(void) dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, - const void *mem, apr_size_t amt, - apr_size_t pad) -{ - dav_check_bufsize(p, pbuf, amt + pad); - memcpy(pbuf->buf + pbuf->cur_len, mem, amt); -} - -/* -** dav_lookup_uri() -** -** Extension for ap_sub_req_lookup_uri() which can't handle absolute -** URIs properly. -** -** If NULL is returned, then an error occurred with parsing the URI or -** the URI does not match the current server. -*/ -DAV_DECLARE(dav_lookup_result) dav_lookup_uri(const char *uri, - request_rec * r, - int must_be_absolute) -{ - dav_lookup_result result = { 0 }; - const char *scheme; - apr_port_t port; - apr_uri_t comp; - char *new_file; - const char *domain; - - /* first thing to do is parse the URI into various components */ - if (apr_uri_parse(r->pool, uri, &comp) != APR_SUCCESS) { - result.err.status = HTTP_BAD_REQUEST; - result.err.desc = "Invalid syntax in Destination URI."; - return result; - } - - /* the URI must be an absoluteURI (WEBDAV S9.3) */ - if (comp.scheme == NULL && must_be_absolute) { - result.err.status = HTTP_BAD_REQUEST; - result.err.desc = "Destination URI must be an absolute URI."; - return result; - } - - /* the URI must not have a query (args) or a fragment */ - if (comp.query != NULL || comp.fragment != NULL) { - result.err.status = HTTP_BAD_REQUEST; - result.err.desc = - "Destination URI contains invalid components " - "(a query or a fragment)."; - return result; - } - - /* If the scheme or port was provided, then make sure that it matches - the scheme/port of this request. If the request must be absolute, - then require the (explicit/implicit) scheme/port be matching. - - ### hmm. if a port wasn't provided (does the parse return port==0?), - ### but we're on a non-standard port, then we won't detect that the - ### URI's port implies the wrong one. - */ - if (comp.scheme != NULL || comp.port != 0 || must_be_absolute) - { - /* ### not sure this works if the current request came in via https: */ - scheme = r->parsed_uri.scheme; - if (scheme == NULL) - scheme = ap_http_method(r); - - /* insert a port if the URI did not contain one */ - if (comp.port == 0) - comp.port = apr_uri_port_of_scheme(comp.scheme); - - /* now, verify that the URI uses the same scheme as the current. - request. the port must match our port. - */ - apr_sockaddr_port_get(&port, r->connection->local_addr); - if (strcasecmp(comp.scheme, scheme) != 0 -#ifdef APACHE_PORT_HANDLING_IS_BUSTED - || comp.port != port -#endif - ) { - result.err.status = HTTP_BAD_GATEWAY; - result.err.desc = apr_psprintf(r->pool, - "Destination URI refers to " - "different scheme or port " - "(%s://hostname:%d)" APR_EOL_STR - "(want: %s://hostname:%d)", - comp.scheme ? comp.scheme : scheme, - comp.port ? comp.port : port, - scheme, port); - return result; - } - } - - /* we have verified the scheme, port, and general structure */ - - /* - ** Hrm. IE5 will pass unqualified hostnames for both the - ** Host: and Destination: headers. This breaks the - ** http_vhost.c::matches_aliases function. - ** - ** For now, qualify unqualified comp.hostnames with - ** r->server->server_hostname. - ** - ** ### this is a big hack. Apache should provide a better way. - ** ### maybe the admin should list the unqualified hosts in a - ** ### <ServerAlias> block? - */ - if (comp.hostname != NULL - && strrchr(comp.hostname, '.') == NULL - && (domain = strchr(r->server->server_hostname, '.')) != NULL) { - comp.hostname = apr_pstrcat(r->pool, comp.hostname, domain, NULL); - } - - /* now, if a hostname was provided, then verify that it represents the - same server as the current connection. note that we just use our - port, since we've verified the URI matches ours */ -#ifdef APACHE_PORT_HANDLING_IS_BUSTED - if (comp.hostname != NULL && - !ap_matches_request_vhost(r, comp.hostname, port)) { - result.err.status = HTTP_BAD_GATEWAY; - result.err.desc = "Destination URI refers to a different server."; - return result; - } -#endif - - /* we have verified that the requested URI denotes the same server as - the current request. Therefore, we can use ap_sub_req_lookup_uri() */ - - /* reconstruct a URI as just the path */ - new_file = apr_uri_unparse(r->pool, &comp, APR_URI_UNP_OMITSITEPART); - - /* - * Lookup the URI and return the sub-request. Note that we use the - * same HTTP method on the destination. This allows the destination - * to apply appropriate restrictions (e.g. readonly). - */ - result.rnew = ap_sub_req_method_uri(r->method, new_file, r, NULL); - - return result; -} - -/* --------------------------------------------------------------- -** -** XML UTILITY FUNCTIONS -*/ - -/* validate that the root element uses a given DAV: tagname (TRUE==valid) */ -DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc, - const char *tagname) -{ - return doc->root && - doc->root->ns == APR_XML_NS_DAV_ID && - strcmp(doc->root->name, tagname) == 0; -} - -/* find and return the (unique) child with a given DAV: tagname */ -DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem, - const char *tagname) -{ - apr_xml_elem *child = elem->first_child; - - for (; child; child = child->next) - if (child->ns == APR_XML_NS_DAV_ID && !strcmp(child->name, tagname)) - return child; - return NULL; -} - -/* gather up all the CDATA into a single string */ -DAV_DECLARE(const char *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool, - int strip_white) -{ - apr_size_t len = 0; - apr_text *scan; - const apr_xml_elem *child; - char *cdata; - char *s; - apr_size_t tlen; - const char *found_text = NULL; /* initialize to avoid gcc warning */ - int found_count = 0; - - for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) { - found_text = scan->text; - ++found_count; - len += strlen(found_text); - } - - for (child = elem->first_child; child != NULL; child = child->next) { - for (scan = child->following_cdata.first; - scan != NULL; - scan = scan->next) { - found_text = scan->text; - ++found_count; - len += strlen(found_text); - } - } - - /* some fast-path cases: - * 1) zero-length cdata - * 2) a single piece of cdata with no whitespace to strip - */ - if (len == 0) - return ""; - if (found_count == 1) { - if (!strip_white - || (!apr_isspace(*found_text) - && !apr_isspace(found_text[len - 1]))) - return found_text; - } - - cdata = s = apr_palloc(pool, len + 1); - - for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) { - tlen = strlen(scan->text); - memcpy(s, scan->text, tlen); - s += tlen; - } - - for (child = elem->first_child; child != NULL; child = child->next) { - for (scan = child->following_cdata.first; - scan != NULL; - scan = scan->next) { - tlen = strlen(scan->text); - memcpy(s, scan->text, tlen); - s += tlen; - } - } - - *s = '\0'; - - if (strip_white) { - /* trim leading whitespace */ - while (apr_isspace(*cdata)) /* assume: return false for '\0' */ - ++cdata; - - /* trim trailing whitespace */ - while (len-- > 0 && apr_isspace(cdata[len])) - continue; - cdata[len + 1] = '\0'; - } - - return cdata; -} - -DAV_DECLARE(dav_xmlns_info *) dav_xmlns_create(apr_pool_t *pool) -{ - dav_xmlns_info *xi = apr_pcalloc(pool, sizeof(*xi)); - - xi->pool = pool; - xi->uri_prefix = apr_hash_make(pool); - xi->prefix_uri = apr_hash_make(pool); - - return xi; -} - -DAV_DECLARE(void) dav_xmlns_add(dav_xmlns_info *xi, - const char *prefix, const char *uri) -{ - /* this "should" not overwrite a prefix mapping */ - apr_hash_set(xi->prefix_uri, prefix, APR_HASH_KEY_STRING, uri); - - /* note: this may overwrite an existing URI->prefix mapping, but it - doesn't matter -- any prefix is usuable to specify the URI. */ - apr_hash_set(xi->uri_prefix, uri, APR_HASH_KEY_STRING, prefix); -} - -DAV_DECLARE(const char *) dav_xmlns_add_uri(dav_xmlns_info *xi, - const char *uri) -{ - const char *prefix; - - if ((prefix = apr_hash_get(xi->uri_prefix, uri, - APR_HASH_KEY_STRING)) != NULL) - return prefix; - - prefix = apr_psprintf(xi->pool, "g%d", xi->count++); - dav_xmlns_add(xi, prefix, uri); - return prefix; -} - -DAV_DECLARE(const char *) dav_xmlns_get_uri(dav_xmlns_info *xi, - const char *prefix) -{ - return apr_hash_get(xi->prefix_uri, prefix, APR_HASH_KEY_STRING); -} - -DAV_DECLARE(const char *) dav_xmlns_get_prefix(dav_xmlns_info *xi, - const char *uri) -{ - return apr_hash_get(xi->uri_prefix, uri, APR_HASH_KEY_STRING); -} - -DAV_DECLARE(void) dav_xmlns_generate(dav_xmlns_info *xi, - apr_text_header *phdr) -{ - apr_hash_index_t *hi = apr_hash_first(xi->pool, xi->prefix_uri); - - for (; hi != NULL; hi = apr_hash_next(hi)) { - const void *prefix; - void *uri; - const char *s; - - apr_hash_this(hi, &prefix, NULL, &uri); - - s = apr_psprintf(xi->pool, " xmlns:%s=\"%s\"", - (const char *)prefix, (const char *)uri); - apr_text_append(xi->pool, phdr, s); - } -} - -/* --------------------------------------------------------------- -** -** Timeout header processing -** -*/ - -/* dav_get_timeout: If the Timeout: header exists, return a time_t - * when this lock is expected to expire. Otherwise, return - * a time_t of DAV_TIMEOUT_INFINITE. - * - * It's unclear if DAV clients are required to understand - * Seconds-xxx and Infinity time values. We assume that they do. - * In addition, for now, that's all we understand, too. - */ -DAV_DECLARE(time_t) dav_get_timeout(request_rec *r) -{ - time_t now, expires = DAV_TIMEOUT_INFINITE; - - const char *timeout_const = apr_table_get(r->headers_in, "Timeout"); - const char *timeout = apr_pstrdup(r->pool, timeout_const), *val; - - if (timeout == NULL) - return DAV_TIMEOUT_INFINITE; - - /* Use the first thing we understand, or infinity if - * we don't understand anything. - */ - - while ((val = ap_getword_white(r->pool, &timeout)) && strlen(val)) { - if (!strncmp(val, "Infinite", 8)) { - return DAV_TIMEOUT_INFINITE; - } - - if (!strncmp(val, "Second-", 7)) { - val += 7; - /* ### We need to handle overflow better: - * ### timeout will be <= 2^32 - 1 - */ - expires = atol(val); - now = time(NULL); - return now + expires; - } - } - - return DAV_TIMEOUT_INFINITE; -} - -/* --------------------------------------------------------------- -** -** If Header processing -** -*/ - -/* add_if_resource returns a new if_header, linking it to next_ih. - */ -static dav_if_header *dav_add_if_resource(apr_pool_t *p, dav_if_header *next_ih, - const char *uri, apr_size_t uri_len) -{ - dav_if_header *ih; - - if ((ih = apr_pcalloc(p, sizeof(*ih))) == NULL) - return NULL; - - ih->uri = uri; - ih->uri_len = uri_len; - ih->next = next_ih; - - return ih; -} - -/* add_if_state adds a condition to an if_header. - */ -static dav_error * dav_add_if_state(apr_pool_t *p, dav_if_header *ih, - const char *state_token, - dav_if_state_type t, int condition, - const dav_hooks_locks *locks_hooks) -{ - dav_if_state_list *new_sl; - - new_sl = apr_pcalloc(p, sizeof(*new_sl)); - - new_sl->condition = condition; - new_sl->type = t; - - if (t == dav_if_opaquelock) { - dav_error *err; - - if ((err = (*locks_hooks->parse_locktoken)(p, state_token, - &new_sl->locktoken)) != NULL) { - /* In cases where the state token is invalid, we'll just skip - * it rather than return 400. - */ - if (err->error_id == DAV_ERR_LOCK_UNK_STATE_TOKEN) { - return NULL; - } - else { - /* ### maybe add a higher-level description */ - return err; - } - } - } - else - new_sl->etag = state_token; - - new_sl->next = ih->state; - ih->state = new_sl; - - return NULL; -} - -/* fetch_next_token returns the substring from str+1 - * to the next occurence of char term, or \0, whichever - * occurs first. Leading whitespace is ignored. - */ -static char *dav_fetch_next_token(char **str, char term) -{ - char *sp; - char *token; - - token = *str + 1; - - while (*token && (*token == ' ' || *token == '\t')) - token++; - - if ((sp = strchr(token, term)) == NULL) - return NULL; - - *sp = '\0'; - *str = sp; - return token; -} - -/* dav_process_if_header: - * - * If NULL (no error) is returned, then **if_header points to the - * "If" productions structure (or NULL if "If" is not present). - * - * ### this part is bogus: - * If an error is encountered, the error is logged. Parent should - * return err->status. - */ -static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih) -{ - dav_error *err; - char *str; - char *list; - const char *state_token; - const char *uri = NULL; /* scope of current production; NULL=no-tag */ - apr_size_t uri_len = 0; - dav_if_header *ih = NULL; - apr_uri_t parsed_uri; - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - enum {no_tagged, tagged, unknown} list_type = unknown; - int condition; - - *p_ih = NULL; - - if ((str = apr_pstrdup(r->pool, apr_table_get(r->headers_in, "If"))) == NULL) - return NULL; - - while (*str) { - switch(*str) { - case '<': - /* Tagged-list production - following states apply to this uri */ - if (list_type == no_tagged - || ((uri = dav_fetch_next_token(&str, '>')) == NULL)) { - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_TAGGED, - "Invalid If-header: unclosed \"<\" or " - "unexpected tagged-list production."); - } - - /* 2518 specifies this must be an absolute URI; just take the - * relative part for later comparison against r->uri */ - if (apr_uri_parse(r->pool, uri, &parsed_uri) != APR_SUCCESS - || !parsed_uri.path) { - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_TAGGED, - "Invalid URI in tagged If-header."); - } - /* note that parsed_uri.path is allocated; we can trash it */ - - /* clean up the URI a bit */ - ap_getparents(parsed_uri.path); - uri_len = strlen(parsed_uri.path); - if (uri_len > 1 && parsed_uri.path[uri_len - 1] == '/') - parsed_uri.path[--uri_len] = '\0'; - - uri = parsed_uri.path; - list_type = tagged; - break; - - case '(': - /* List production */ - - /* If a uri has not been encountered, this is a No-Tagged-List */ - if (list_type == unknown) - list_type = no_tagged; - - if ((list = dav_fetch_next_token(&str, ')')) == NULL) { - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_UNCLOSED_PAREN, - "Invalid If-header: unclosed \"(\"."); - } - - if ((ih = dav_add_if_resource(r->pool, ih, uri, uri_len)) == NULL) { - /* ### dav_add_if_resource() should return an error for us! */ - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_PARSE, - "Internal server error parsing \"If:\" " - "header."); - } - - condition = DAV_IF_COND_NORMAL; - - while (*list) { - /* List is the entire production (in a uri scope) */ - - switch (*list) { - case '<': - if ((state_token = dav_fetch_next_token(&list, '>')) == NULL) { - /* ### add a description to this error */ - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_PARSE, NULL); - } - - if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_opaquelock, - condition, locks_hooks)) != NULL) { - /* ### maybe add a higher level description */ - return err; - } - condition = DAV_IF_COND_NORMAL; - break; - - case '[': - if ((state_token = dav_fetch_next_token(&list, ']')) == NULL) { - /* ### add a description to this error */ - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_PARSE, NULL); - } - - if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_etag, - condition, locks_hooks)) != NULL) { - /* ### maybe add a higher level description */ - return err; - } - condition = DAV_IF_COND_NORMAL; - break; - - case 'N': - if (list[1] == 'o' && list[2] == 't') { - if (condition != DAV_IF_COND_NORMAL) { - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_MULTIPLE_NOT, - "Invalid \"If:\" header: " - "Multiple \"not\" entries " - "for the same state."); - } - condition = DAV_IF_COND_NOT; - } - list += 2; - break; - - case ' ': - case '\t': - break; - - default: - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_UNK_CHAR, - apr_psprintf(r->pool, - "Invalid \"If:\" " - "header: Unexpected " - "character encountered " - "(0x%02x, '%c').", - *list, *list)); - } - - list++; - } - break; - - case ' ': - case '\t': - break; - - default: - return dav_new_error(r->pool, HTTP_BAD_REQUEST, - DAV_ERR_IF_UNK_CHAR, - apr_psprintf(r->pool, - "Invalid \"If:\" header: " - "Unexpected character " - "encountered (0x%02x, '%c').", - *str, *str)); - } - - str++; - } - - *p_ih = ih; - return NULL; -} - -static int dav_find_submitted_locktoken(const dav_if_header *if_header, - const dav_lock *lock_list, - const dav_hooks_locks *locks_hooks) -{ - for (; if_header != NULL; if_header = if_header->next) { - const dav_if_state_list *state_list; - - for (state_list = if_header->state; - state_list != NULL; - state_list = state_list->next) { - - if (state_list->type == dav_if_opaquelock) { - const dav_lock *lock; - - /* given state_list->locktoken, match it */ - - /* - ** The resource will have one or more lock tokens. We only - ** need to match one of them against any token in the - ** If: header. - ** - ** One token case: It is an exclusive or shared lock. Either - ** way, we must find it. - ** - ** N token case: They are shared locks. By policy, we need - ** to match only one. The resource's other - ** tokens may belong to somebody else (so we - ** shouldn't see them in the If: header anyway) - */ - for (lock = lock_list; lock != NULL; lock = lock->next) { - - if (!(*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) { - return 1; - } - } - } - } - } - - return 0; -} - -/* dav_validate_resource_state: - * Returns NULL if path/uri meets if-header and lock requirements - */ -static dav_error * dav_validate_resource_state(apr_pool_t *p, - const dav_resource *resource, - dav_lockdb *lockdb, - const dav_if_header *if_header, - int flags, - dav_buffer *pbuf, - request_rec *r) -{ - dav_error *err; - const char *uri; - const char *etag; - const dav_hooks_locks *locks_hooks = (lockdb ? lockdb->hooks : NULL); - const dav_if_header *ifhdr_scan; - dav_if_state_list *state_list; - dav_lock *lock_list; - dav_lock *lock; - int num_matched; - int num_that_apply; - int seen_locktoken; - apr_size_t uri_len; - const char *reason = NULL; - - /* DBG1("validate: <%s>", resource->uri); */ - - /* - ** The resource will have one of three states: - ** - ** 1) No locks. We have no special requirements that the user supply - ** specific locktokens. One of the state lists must match, and - ** we're done. - ** - ** 2) One exclusive lock. The locktoken must appear *anywhere* in the - ** If: header. Of course, asserting the token in a "Not" term will - ** quickly fail that state list :-). If the locktoken appears in - ** one of the state lists *and* one state list matches, then we're - ** done. - ** - ** 3) One or more shared locks. One of the locktokens must appear - ** *anywhere* in the If: header. If one of the locktokens appears, - ** and we match one state list, then we are done. - ** - ** The <seen_locktoken> variable determines whether we have seen one - ** of this resource's locktokens in the If: header. - */ - - /* - ** If this is a new lock request, <flags> will contain the requested - ** lock scope. Three rules apply: - ** - ** 1) Do not require a (shared) locktoken to be seen (when we are - ** applying another shared lock) - ** 2) If the scope is exclusive and we see any locks, fail. - ** 3) If the scope is shared and we see an exclusive lock, fail. - */ - - if (lockdb == NULL) { - /* we're in State 1. no locks. */ - lock_list = NULL; - } - else { - /* - ** ### hrm... we don't need to have these fully - ** ### resolved since we're only looking at the - ** ### locktokens... - ** - ** ### use get_locks w/ calltype=PARTIAL - */ - if ((err = dav_lock_query(lockdb, resource, &lock_list)) != NULL) { - return dav_push_error(p, - HTTP_INTERNAL_SERVER_ERROR, 0, - "The locks could not be queried for " - "verification against a possible \"If:\" " - "header.", - err); - } - - /* lock_list now determines whether we're in State 1, 2, or 3. */ - } - - /* - ** For a new, exclusive lock: if any locks exist, fail. - ** For a new, shared lock: if an exclusive lock exists, fail. - ** else, do not require a token to be seen. - */ - if (flags & DAV_LOCKSCOPE_EXCLUSIVE) { - if (lock_list != NULL) { - return dav_new_error(p, HTTP_LOCKED, 0, - "Existing lock(s) on the requested resource " - "prevent an exclusive lock."); - } - - /* - ** There are no locks, so we can pretend that we've already met - ** any requirement to find the resource's locks in an If: header. - */ - seen_locktoken = 1; - } - else if (flags & DAV_LOCKSCOPE_SHARED) { - /* - ** Strictly speaking, we don't need this loop. Either the first - ** (and only) lock will be EXCLUSIVE, or none of them will be. - */ - for (lock = lock_list; lock != NULL; lock = lock->next) { - if (lock->scope == DAV_LOCKSCOPE_EXCLUSIVE) { - return dav_new_error(p, HTTP_LOCKED, 0, - "The requested resource is already " - "locked exclusively."); - } - } - - /* - ** The locks on the resource (if any) are all shared. Set the - ** <seen_locktoken> flag to indicate that we do not need to find - ** the locks in an If: header. - */ - seen_locktoken = 1; - } - else { - /* - ** For methods other than LOCK: - ** - ** If we have no locks, then <seen_locktoken> can be set to true -- - ** pretending that we've already met the requirement of seeing one - ** of the resource's locks in the If: header. - ** - ** Otherwise, it must be cleared and we'll look for one. - */ - seen_locktoken = (lock_list == NULL); - } - - /* - ** If there is no If: header, then we can shortcut some logic: - ** - ** 1) if we do not need to find a locktoken in the (non-existent) If: - ** header, then we are successful. - ** - ** 2) if we must find a locktoken in the (non-existent) If: header, then - ** we fail. - */ - if (if_header == NULL) { - if (seen_locktoken) - return NULL; - - return dav_new_error(p, HTTP_LOCKED, 0, - "This resource is locked and an \"If:\" header " - "was not supplied to allow access to the " - "resource."); - } - /* the If: header is present */ - - /* - ** If a dummy header is present (because of a Lock-Token: header), then - ** we are required to find that token in this resource's set of locks. - ** If we have no locks, then we immediately fail. - ** - ** This is a 400 (Bad Request) since they should only submit a locktoken - ** that actually exists. - ** - ** Don't issue this response if we're talking about the parent resource. - ** It is okay for that resource to NOT have this locktoken. - ** (in fact, it certainly will not: a dummy_header only occurs for the - ** UNLOCK method, the parent is checked only for locknull resources, - ** and the parent certainly does not have the (locknull's) locktoken) - */ - if (lock_list == NULL && if_header->dummy_header) { - if (flags & DAV_VALIDATE_IS_PARENT) - return NULL; - return dav_new_error(p, HTTP_BAD_REQUEST, 0, - "The locktoken specified in the \"Lock-Token:\" " - "header is invalid because this resource has no " - "outstanding locks."); - } - - /* - ** Prepare the input URI. We want the URI to never have a trailing slash. - ** - ** When URIs are placed into the dav_if_header structure, they are - ** guaranteed to never have a trailing slash. If the URIs are equivalent, - ** then it doesn't matter if they both lack a trailing slash -- they're - ** still equivalent. - ** - ** Note: we could also ensure that a trailing slash is present on both - ** URIs, but the majority of URIs provided to us via a resource walk - ** will not contain that trailing slash. - */ - uri = resource->uri; - uri_len = strlen(uri); - if (uri[uri_len - 1] == '/') { - dav_set_bufsize(p, pbuf, uri_len); - memcpy(pbuf->buf, uri, uri_len); - pbuf->buf[--uri_len] = '\0'; - uri = pbuf->buf; - } - - /* get the resource's etag; we may need it during the checks */ - etag = (*resource->hooks->getetag)(resource); - - /* how many state_lists apply to this URI? */ - num_that_apply = 0; - - /* If there are if-headers, fail if this resource - * does not match at least one state_list. - */ - for (ifhdr_scan = if_header; - ifhdr_scan != NULL; - ifhdr_scan = ifhdr_scan->next) { - - /* DBG2("uri=<%s> if_uri=<%s>", uri, ifhdr_scan->uri ? ifhdr_scan->uri : "(no uri)"); */ - - if (ifhdr_scan->uri != NULL - && (uri_len != ifhdr_scan->uri_len - || memcmp(uri, ifhdr_scan->uri, uri_len) != 0)) { - /* - ** A tagged-list's URI doesn't match this resource's URI. - ** Skip to the next state_list to see if it will match. - */ - continue; - } - - /* this state_list applies to this resource */ - - /* - ** ### only one state_list should ever apply! a no-tag, or a tagged - ** ### where S9.4.2 states only one can match. - ** - ** ### revamp this code to loop thru ifhdr_scan until we find the - ** ### matching state_list. process it. stop. - */ - ++num_that_apply; - - /* To succeed, resource must match *all* of the states - * specified in the state_list. - */ - for (state_list = ifhdr_scan->state; - state_list != NULL; - state_list = state_list->next) { - - switch(state_list->type) { - case dav_if_etag: - { - int mismatch = strcmp(state_list->etag, etag); - - if (state_list->condition == DAV_IF_COND_NORMAL && mismatch) { - /* - ** The specified entity-tag does not match the - ** entity-tag on the resource. This state_list is - ** not going to match. Bust outta here. - */ - reason = - "an entity-tag was specified, but the resource's " - "actual ETag does not match."; - goto state_list_failed; - } - else if (state_list->condition == DAV_IF_COND_NOT - && !mismatch) { - /* - ** The specified entity-tag DOES match the - ** entity-tag on the resource. This state_list is - ** not going to match. Bust outta here. - */ - reason = - "an entity-tag was specified using the \"Not\" form, " - "but the resource's actual ETag matches the provided " - "entity-tag."; - goto state_list_failed; - } - break; - } - - case dav_if_opaquelock: - if (lockdb == NULL) { - if (state_list->condition == DAV_IF_COND_NOT) { - /* the locktoken is definitely not there! (success) */ - continue; - } - - /* condition == DAV_IF_COND_NORMAL */ - - /* - ** If no lockdb is provided, then validation fails for - ** this state_list (NORMAL means we were supposed to - ** find the token, which we obviously cannot do without - ** a lock database). - ** - ** Go and try the next state list. - */ - reason = - "a State-token was supplied, but a lock database " - "is not available for to provide the required lock."; - goto state_list_failed; - } - - /* Resource validation 'fails' if: - * ANY of the lock->locktokens match - * a NOT state_list->locktoken, - * OR - * NONE of the lock->locktokens match - * a NORMAL state_list->locktoken. - */ - num_matched = 0; - for (lock = lock_list; lock != NULL; lock = lock->next) { - - /* - DBG2("compare: rsrc=%s ifhdr=%s", - (*locks_hooks->format_locktoken)(p, lock->locktoken), - (*locks_hooks->format_locktoken)(p, state_list->locktoken)); - */ - - /* nothing to do if the locktokens do not match. */ - if ((*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) { - continue; - } - - /* - ** We have now matched up one of the resource's locktokens - ** to a locktoken in a State-token in the If: header. - ** Note this fact, so that we can pass the overall - ** requirement of seeing at least one of the resource's - ** locktokens. - */ - seen_locktoken = 1; - - if (state_list->condition == DAV_IF_COND_NOT) { - /* - ** This state requires that the specified locktoken - ** is NOT present on the resource. But we just found - ** it. There is no way this state-list can now - ** succeed, so go try another one. - */ - reason = - "a State-token was supplied, which used a " - "\"Not\" condition. The State-token was found " - "in the locks on this resource"; - goto state_list_failed; - } - - /* condition == DAV_IF_COND_NORMAL */ - - /* Validate auth_user: If an authenticated user created - ** the lock, only the same user may submit that locktoken - ** to manipulate a resource. - */ - if (lock->auth_user && - (!r->user || - strcmp(lock->auth_user, r->user))) { - const char *errmsg; - - errmsg = apr_pstrcat(p, "User \"", - r->user, - "\" submitted a locktoken created " - "by user \"", - lock->auth_user, "\".", NULL); - return dav_new_error(p, HTTP_FORBIDDEN, 0, errmsg); - } - - /* - ** We just matched a specified State-Token to one of the - ** resource's locktokens. - ** - ** Break out of the lock scan -- we only needed to find - ** one match (actually, there shouldn't be any other - ** matches in the lock list). - */ - num_matched = 1; - break; - } - - if (num_matched == 0 - && state_list->condition == DAV_IF_COND_NORMAL) { - /* - ** We had a NORMAL state, meaning that we should have - ** found the State-Token within the locks on this - ** resource. We didn't, so this state_list must fail. - */ - reason = - "a State-token was supplied, but it was not found " - "in the locks on this resource."; - goto state_list_failed; - } - - break; - - } /* switch */ - } /* foreach ( state_list ) */ - - /* - ** We've checked every state in this state_list and none of them - ** have failed. Since all of them succeeded, then we have a matching - ** state list and we may be done. - ** - ** The next requirement is that we have seen one of the resource's - ** locktokens (if any). If we have, then we can just exit. If we - ** haven't, then we need to keep looking. - */ - if (seen_locktoken) { - /* woo hoo! */ - return NULL; - } - - /* - ** Haven't seen one. Let's break out of the search and just look - ** for a matching locktoken. - */ - break; - - /* - ** This label is used when we detect that a state_list is not - ** going to match this resource. We bust out and try the next - ** state_list. - */ - state_list_failed: - ; - - } /* foreach ( ifhdr_scan ) */ - - /* - ** The above loop exits for one of two reasons: - ** 1) a state_list matched and seen_locktoken is false. - ** 2) all if_header structures were scanned, without (1) occurring - */ - - if (ifhdr_scan == NULL) { - /* - ** We finished the loop without finding any matching state lists. - */ - - /* - ** If none of the state_lists apply to this resource, then we - ** may have succeeded. Note that this scenario implies a - ** tagged-list with no matching state_lists. If the If: header - ** was a no-tag-list, then it would have applied to this resource. - ** - ** S9.4.2 states that when no state_lists apply, then the header - ** should be ignored. - ** - ** If we saw one of the resource's locktokens, then we're done. - ** If we did not see a locktoken, then we fail. - */ - if (num_that_apply == 0) { - if (seen_locktoken) - return NULL; - - /* - ** We may have aborted the scan before seeing the locktoken. - ** Rescan the If: header to see if we can find the locktoken - ** somewhere. - ** - ** Note that seen_locktoken == 0 implies lock_list != NULL - ** which implies locks_hooks != NULL. - */ - if (dav_find_submitted_locktoken(if_header, lock_list, - locks_hooks)) { - /* - ** We found a match! We're set... none of the If: header - ** assertions apply (implicit success), and the If: header - ** specified the locktoken somewhere. We're done. - */ - return NULL; - } - - return dav_new_error(p, HTTP_LOCKED, 0 /* error_id */, - "This resource is locked and the \"If:\" " - "header did not specify one of the " - "locktokens for this resource's lock(s)."); - } - /* else: one or more state_lists were applicable, but failed. */ - - /* - ** If the dummy_header did not match, then they specified an - ** incorrect token in the Lock-Token header. Forget whether the - ** If: statement matched or not... we'll tell them about the - ** bad Lock-Token first. That is considered a 400 (Bad Request). - */ - if (if_header->dummy_header) { - return dav_new_error(p, HTTP_BAD_REQUEST, 0, - "The locktoken specified in the " - "\"Lock-Token:\" header did not specify one " - "of this resource's locktoken(s)."); - } - - if (reason == NULL) { - return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, - "The preconditions specified by the \"If:\" " - "header did not match this resource."); - } - - return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, - apr_psprintf(p, - "The precondition(s) specified by " - "the \"If:\" header did not match " - "this resource. At least one " - "failure is because: %s", reason)); - } - - /* assert seen_locktoken == 0 */ - - /* - ** ifhdr_scan != NULL implies we found a matching state_list. - ** - ** Since we're still here, it also means that we have not yet found - ** one the resource's locktokens in the If: header. - ** - ** Scan all the if_headers and states looking for one of this - ** resource's locktokens. Note that we need to go back and scan them - ** all -- we may have aborted a scan with a failure before we saw a - ** matching token. - ** - ** Note that seen_locktoken == 0 implies lock_list != NULL which implies - ** locks_hooks != NULL. - */ - if (dav_find_submitted_locktoken(if_header, lock_list, locks_hooks)) { - /* - ** We found a match! We're set... we have a matching state list, - ** and the If: header specified the locktoken somewhere. We're done. - */ - return NULL; - } - - /* - ** We had a matching state list, but the user agent did not specify one - ** of this resource's locktokens. Tell them so. - ** - ** Note that we need to special-case the message on whether a "dummy" - ** header exists. If it exists, yet we didn't see a needed locktoken, - ** then that implies the dummy header (Lock-Token header) did NOT - ** specify one of this resource's locktokens. (this implies something - ** in the real If: header matched) - ** - ** We want to note the 400 (Bad Request) in favor of a 423 (Locked). - */ - if (if_header->dummy_header) { - return dav_new_error(p, HTTP_BAD_REQUEST, 0, - "The locktoken specified in the " - "\"Lock-Token:\" header did not specify one " - "of this resource's locktoken(s)."); - } - - return dav_new_error(p, HTTP_LOCKED, 1 /* error_id */, - "This resource is locked and the \"If:\" header " - "did not specify one of the " - "locktokens for this resource's lock(s)."); -} - -/* dav_validate_walker: Walker callback function to validate resource state */ -static dav_error * dav_validate_walker(dav_walk_resource *wres, int calltype) -{ - dav_walker_ctx *ctx = wres->walk_ctx; - dav_error *err; - - if ((err = dav_validate_resource_state(ctx->w.pool, wres->resource, - ctx->w.lockdb, - ctx->if_header, ctx->flags, - &ctx->work_buf, ctx->r)) == NULL) { - /* There was no error, so just bug out. */ - return NULL; - } - - /* - ** If we have a serious server error, or if the request itself failed, - ** then just return error (not a multistatus). - */ - if (ap_is_HTTP_SERVER_ERROR(err->status) - || (*wres->resource->hooks->is_same_resource)(wres->resource, - ctx->w.root)) { - /* ### maybe push a higher-level description? */ - return err; - } - - /* associate the error with the current URI */ - dav_add_response(wres, err->status, NULL); - - return NULL; -} - -/* -** dav_validate_request: Validate if-headers (and check for locks) on: -** (1) r->filename @ depth; -** (2) Parent of r->filename if check_parent == 1 -** -** The check of parent should be done when it is necessary to verify that -** the parent collection will accept a new member (ie current resource -** state is null). -** -** Return OK on successful validation. -** On error, return appropriate HTTP_* code, and log error. If a multi-stat -** error is necessary, response will point to it, else NULL. -*/ -DAV_DECLARE(dav_error *) dav_validate_request(request_rec *r, - dav_resource *resource, - int depth, - dav_locktoken *locktoken, - dav_response **response, - int flags, - dav_lockdb *lockdb) -{ - dav_error *err; - int result; - dav_if_header *if_header; - int lock_db_opened_locally = 0; - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - const dav_hooks_repository *repos_hooks = resource->hooks; - dav_buffer work_buf = { 0 }; - dav_response *new_response; - -#if DAV_DEBUG - if (depth && response == NULL) { - /* - ** ### bleck. we can't return errors for other URIs unless we have - ** ### a "response" ptr. - */ - return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "DESIGN ERROR: dav_validate_request called " - "with depth>0, but no response ptr."); - } -#endif - - if (response != NULL) - *response = NULL; - - /* Do the standard checks for conditional requests using - * If-..-Since, If-Match etc */ - if ((result = ap_meets_conditions(r)) != OK) { - /* ### fix this up... how? */ - return dav_new_error(r->pool, result, 0, NULL); - } - - /* always parse (and later process) the If: header */ - if ((err = dav_process_if_header(r, &if_header)) != NULL) { - /* ### maybe add higher-level description */ - return err; - } - - /* If a locktoken was specified, create a dummy if_header with which - * to validate resources. In the interim, figure out why DAV uses - * locktokens in an if-header without a Lock-Token header to refresh - * locks, but a Lock-Token header without an if-header to remove them. - */ - if (locktoken != NULL) { - dav_if_header *ifhdr_new; - - ifhdr_new = apr_pcalloc(r->pool, sizeof(*ifhdr_new)); - ifhdr_new->uri = resource->uri; - ifhdr_new->uri_len = strlen(resource->uri); - ifhdr_new->dummy_header = 1; - - ifhdr_new->state = apr_pcalloc(r->pool, sizeof(*ifhdr_new->state)); - ifhdr_new->state->type = dav_if_opaquelock; - ifhdr_new->state->condition = DAV_IF_COND_NORMAL; - ifhdr_new->state->locktoken = locktoken; - - ifhdr_new->next = if_header; - if_header = ifhdr_new; - } - - /* - ** If necessary, open the lock database (read-only, lazily); - ** the validation process may need to retrieve or update lock info. - ** Otherwise, assume provided lockdb is valid and opened rw. - */ - if (lockdb == NULL) { - if (locks_hooks != NULL) { - if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) { - /* ### maybe insert higher-level comment */ - return err; - } - lock_db_opened_locally = 1; - } - } - - /* (1) Validate the specified resource, at the specified depth */ - if (resource->exists && depth > 0) { - dav_walker_ctx ctx = { { 0 } }; - dav_response *multi_status; - - ctx.w.walk_type = DAV_WALKTYPE_NORMAL; - ctx.w.func = dav_validate_walker; - ctx.w.walk_ctx = &ctx; - ctx.w.pool = r->pool; - ctx.w.root = resource; - - ctx.if_header = if_header; - ctx.r = r; - ctx.flags = flags; - - if (lockdb != NULL) { - ctx.w.lockdb = lockdb; - ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL; - } - - err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); - if (err == NULL) { - *response = multi_status;; - } - /* else: implies a 5xx status code occurred. */ - } - else { - err = dav_validate_resource_state(r->pool, resource, lockdb, - if_header, flags, &work_buf, r); - } - - /* (2) Validate the parent resource if requested */ - if (err == NULL && (flags & DAV_VALIDATE_PARENT)) { - dav_resource *parent_resource; - - err = (*repos_hooks->get_parent_resource)(resource, &parent_resource); - - if (err == NULL && parent_resource == NULL) { - err = dav_new_error(r->pool, HTTP_FORBIDDEN, 0, - "Cannot access parent of repository root."); - } - else if (err == NULL) { - err = dav_validate_resource_state(r->pool, parent_resource, lockdb, - if_header, - flags | DAV_VALIDATE_IS_PARENT, - &work_buf, r); - - /* - ** This error occurred on the parent resource. This implies that - ** we have to create a multistatus response (to report the error - ** against a URI other than the Request-URI). "Convert" this error - ** into a multistatus response. - */ - if (err != NULL) { - new_response = apr_pcalloc(r->pool, sizeof(*new_response)); - - new_response->href = parent_resource->uri; - new_response->status = err->status; - new_response->desc = - "A validation error has occurred on the parent resource, " - "preventing the operation on the resource specified by " - "the Request-URI."; - if (err->desc != NULL) { - new_response->desc = apr_pstrcat(r->pool, - new_response->desc, - " The error was: ", - err->desc, NULL); - } - - /* assert: DAV_VALIDATE_PARENT implies response != NULL */ - new_response->next = *response; - *response = new_response; - - err = NULL; - } - } - } - - if (lock_db_opened_locally) - (*locks_hooks->close_lockdb)(lockdb); - - /* - ** If we don't have a (serious) error, and we have multistatus responses, - ** then we need to construct an "error". This error will be the overall - ** status returned, and the multistatus responses will go into its body. - ** - ** For certain methods, the overall error will be a 424. The default is - ** to construct a standard 207 response. - */ - if (err == NULL && response != NULL && *response != NULL) { - apr_text *propstat = NULL; - - if ((flags & DAV_VALIDATE_USE_424) != 0) { - /* manufacture a 424 error to hold the multistatus response(s) */ - return dav_new_error(r->pool, HTTP_FAILED_DEPENDENCY, 0, - "An error occurred on another resource, " - "preventing the requested operation on " - "this resource."); - } - - /* - ** Whatever caused the error, the Request-URI should have a 424 - ** associated with it since we cannot complete the method. - ** - ** For a LOCK operation, insert an empty DAV:lockdiscovery property. - ** For other methods, return a simple 424. - */ - if ((flags & DAV_VALIDATE_ADD_LD) != 0) { - propstat = apr_pcalloc(r->pool, sizeof(*propstat)); - propstat->text = - "<D:propstat>" DEBUG_CR - "<D:prop><D:lockdiscovery/></D:prop>" DEBUG_CR - "<D:status>HTTP/1.1 424 Failed Dependency</D:status>" DEBUG_CR - "</D:propstat>" DEBUG_CR; - } - - /* create the 424 response */ - new_response = apr_pcalloc(r->pool, sizeof(*new_response)); - new_response->href = resource->uri; - new_response->status = HTTP_FAILED_DEPENDENCY; - new_response->propresult.propstats = propstat; - new_response->desc = - "An error occurred on another resource, preventing the " - "requested operation on this resource."; - - new_response->next = *response; - *response = new_response; - - /* manufacture a 207 error for the multistatus response(s) */ - return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, - "Error(s) occurred on resources during the " - "validation process."); - } - - return err; -} - -/* dav_get_locktoken_list: - * - * Sets ltl to a locktoken_list of all positive locktokens in header, - * else NULL if no If-header, or no positive locktokens. - */ -DAV_DECLARE(dav_error *) dav_get_locktoken_list(request_rec *r, - dav_locktoken_list **ltl) -{ - dav_error *err; - dav_if_header *if_header; - dav_if_state_list *if_state; - dav_locktoken_list *lock_token = NULL; - - *ltl = NULL; - - if ((err = dav_process_if_header(r, &if_header)) != NULL) { - /* ### add a higher-level description? */ - return err; - } - - while (if_header != NULL) { - if_state = if_header->state; /* Begining of the if_state linked list */ - while (if_state != NULL) { - if (if_state->condition == DAV_IF_COND_NORMAL - && if_state->type == dav_if_opaquelock) { - lock_token = apr_pcalloc(r->pool, sizeof(dav_locktoken_list)); - lock_token->locktoken = if_state->locktoken; - lock_token->next = *ltl; - *ltl = lock_token; - } - if_state = if_state->next; - } - if_header = if_header->next; - } - if (*ltl == NULL) { - /* No nodes added */ - return dav_new_error(r->pool, HTTP_BAD_REQUEST, DAV_ERR_IF_ABSENT, - "No locktokens were specified in the \"If:\" " - "header, so the refresh could not be performed."); - } - - return NULL; -} - -#if 0 /* not needed right now... */ - -static const char *strip_white(const char *s, apr_pool_t *pool) -{ - apr_size_t idx; - - /* trim leading whitespace */ - while (apr_isspace(*s)) /* assume: return false for '\0' */ - ++s; - - /* trim trailing whitespace */ - idx = strlen(s) - 1; - if (apr_isspace(s[idx])) { - char *s2 = apr_pstrdup(pool, s); - - while (apr_isspace(s2[idx]) && idx > 0) - --idx; - s2[idx + 1] = '\0'; - return s2; - } - - return s; -} -#endif - -#define DAV_LABEL_HDR "Label" - -/* dav_add_vary_header - * - * If there were any headers in the request which require a Vary header - * in the response, add it. - */ -DAV_DECLARE(void) dav_add_vary_header(request_rec *in_req, - request_rec *out_req, - const dav_resource *resource) -{ - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(in_req); - - /* ### this is probably all wrong... I think there is a function in - ### the Apache API to add things to the Vary header. need to check */ - - /* Only versioning headers require a Vary response header, - * so only do this check if there is a versioning provider */ - if (vsn_hooks != NULL) { - const char *target = apr_table_get(in_req->headers_in, DAV_LABEL_HDR); - const char *vary = apr_table_get(out_req->headers_out, "Vary"); - - /* If Target-Selector specified, add it to the Vary header */ - if (target != NULL) { - if (vary == NULL) - vary = DAV_LABEL_HDR; - else - vary = apr_pstrcat(out_req->pool, vary, "," DAV_LABEL_HDR, - NULL); - - apr_table_setn(out_req->headers_out, "Vary", vary); - } - } -} - -/* dav_can_auto_checkout - * - * Determine whether auto-checkout is enabled for a resource. - * r - the request_rec - * resource - the resource - * auto_version - the value of the auto_versionable hook for the resource - * lockdb - pointer to lock database (opened if necessary) - * auto_checkout - set to 1 if auto-checkout enabled - */ -static dav_error * dav_can_auto_checkout( - request_rec *r, - dav_resource *resource, - dav_auto_version auto_version, - dav_lockdb **lockdb, - int *auto_checkout) -{ - dav_error *err; - dav_lock *lock_list; - - *auto_checkout = 0; - - if (auto_version == DAV_AUTO_VERSION_ALWAYS) { - *auto_checkout = 1; - } - else if (auto_version == DAV_AUTO_VERSION_LOCKED) { - if (*lockdb == NULL) { - const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); - - if (locks_hooks == NULL) { - return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Auto-checkout is only enabled for locked resources, " - "but there is no lock provider."); - } - - if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, lockdb)) != NULL) { - return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Cannot open lock database to determine " - "auto-versioning behavior.", - err); - } - } - - if ((err = dav_lock_query(*lockdb, resource, &lock_list)) != NULL) { - return dav_push_error(r->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "The locks could not be queried for " - "determining auto-versioning behavior.", - err); - } - - if (lock_list != NULL) - *auto_checkout = 1; - } - - return NULL; -} - -/* see mod_dav.h for docco */ -DAV_DECLARE(dav_error *) dav_auto_checkout( - request_rec *r, - dav_resource *resource, - int parent_only, - dav_auto_version_info *av_info) -{ - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_lockdb *lockdb = NULL; - dav_error *err = NULL; - - /* Initialize results */ - memset(av_info, 0, sizeof(*av_info)); - - /* if no versioning provider, just return */ - if (vsn_hooks == NULL) - return NULL; - - /* check parent resource if requested or if resource must be created */ - if (!resource->exists || parent_only) { - dav_resource *parent; - - if ((err = (*resource->hooks->get_parent_resource)(resource, - &parent)) != NULL) - goto done; - - if (parent == NULL || !parent->exists) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Missing one or more intermediate " - "collections. Cannot create resource %s.", - ap_escape_html(r->pool, resource->uri))); - goto done; - } - - av_info->parent_resource = parent; - - /* if parent versioned and not checked out, see if it can be */ - if (parent->versioned && !parent->working) { - int checkout_parent; - - if ((err = dav_can_auto_checkout(r, parent, - (*vsn_hooks->auto_versionable)(parent), - &lockdb, &checkout_parent)) - != NULL) { - goto done; - } - - if (!checkout_parent) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:cannot-modify-checked-in-parent>"); - goto done; - } - - /* Try to checkout the parent collection. - * Note that auto-versioning can only be applied to a version selector, - * so no separate working resource will be created. - */ - if ((err = (*vsn_hooks->checkout)(parent, 1 /*auto_checkout*/, - 0, 0, 0, NULL, NULL)) - != NULL) - { - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Unable to auto-checkout parent collection. " - "Cannot create resource %s.", - ap_escape_html(r->pool, resource->uri)), - err); - goto done; - } - - /* remember that parent was checked out */ - av_info->parent_checkedout = 1; - } - } - - /* if only checking parent, we're done */ - if (parent_only) - goto done; - - /* if creating a new resource, see if it should be version-controlled */ - if (!resource->exists - && (*vsn_hooks->auto_versionable)(resource) == DAV_AUTO_VERSION_ALWAYS) { - - if ((err = (*vsn_hooks->vsn_control)(resource, NULL)) != NULL) { - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Unable to create versioned resource %s.", - ap_escape_html(r->pool, resource->uri)), - err); - goto done; - } - - /* remember that resource was created */ - av_info->resource_versioned = 1; - } - - /* if resource is versioned, make sure it is checked out */ - if (resource->versioned && !resource->working) { - int checkout_resource; - - if ((err = dav_can_auto_checkout(r, resource, - (*vsn_hooks->auto_versionable)(resource), - &lockdb, &checkout_resource)) != NULL) { - goto done; - } - - if (!checkout_resource) { - err = dav_new_error(r->pool, HTTP_CONFLICT, 0, - "<DAV:cannot-modify-version-controlled-content>"); - goto done; - } - - /* Auto-versioning can only be applied to version selectors, so - * no separate working resource will be created. */ - if ((err = (*vsn_hooks->checkout)(resource, 1 /*auto_checkout*/, - 0, 0, 0, NULL, NULL)) - != NULL) - { - err = dav_push_error(r->pool, HTTP_CONFLICT, 0, - apr_psprintf(r->pool, - "Unable to checkout resource %s.", - ap_escape_html(r->pool, resource->uri)), - err); - goto done; - } - - /* remember that resource was checked out */ - av_info->resource_checkedout = 1; - } - -done: - - /* make sure lock database is closed */ - if (lockdb != NULL) - (*lockdb->hooks->close_lockdb)(lockdb); - - /* if an error occurred, undo any auto-versioning operations already done */ - if (err != NULL) { - dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, av_info); - return err; - } - - return NULL; -} - -/* see mod_dav.h for docco */ -DAV_DECLARE(dav_error *) dav_auto_checkin( - request_rec *r, - dav_resource *resource, - int undo, - int unlock, - dav_auto_version_info *av_info) -{ - const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r); - dav_error *err = NULL; - dav_auto_version auto_version; - - /* If no versioning provider, this is a no-op */ - if (vsn_hooks == NULL) - return NULL; - - /* If undoing auto-checkouts, then do uncheckouts */ - if (undo) { - if (resource != NULL) { - if (av_info->resource_checkedout) { - if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) { - return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(r->pool, - "Unable to undo auto-checkout " - "of resource %s.", - ap_escape_html(r->pool, resource->uri)), - err); - } - } - - if (av_info->resource_versioned) { - dav_response *response; - - /* ### should we do anything with the response? */ - if ((err = (*resource->hooks->remove_resource)(resource, - &response)) != NULL) { - return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(r->pool, - "Unable to undo auto-version-control " - "of resource %s.", - ap_escape_html(r->pool, resource->uri)), - err); - } - } - } - - if (av_info->parent_resource != NULL && av_info->parent_checkedout) { - if ((err = (*vsn_hooks->uncheckout)(av_info->parent_resource)) != NULL) { - return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(r->pool, - "Unable to undo auto-checkout " - "of parent collection %s.", - ap_escape_html(r->pool, av_info->parent_resource->uri)), - err); - } - } - - return NULL; - } - - /* If the resource was checked out, and auto-checkin is enabled, - * then check it in. - */ - if (resource != NULL && resource->working - && (unlock || av_info->resource_checkedout)) { - - auto_version = (*vsn_hooks->auto_versionable)(resource); - - if (auto_version == DAV_AUTO_VERSION_ALWAYS || - (unlock && (auto_version == DAV_AUTO_VERSION_LOCKED))) { - - if ((err = (*vsn_hooks->checkin)(resource, - 0 /*keep_checked_out*/, NULL)) - != NULL) { - return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(r->pool, - "Unable to auto-checkin resource %s.", - ap_escape_html(r->pool, resource->uri)), - err); - } - } - } - - /* If parent resource was checked out, and auto-checkin is enabled, - * then check it in. - */ - if (!unlock - && av_info->parent_checkedout - && av_info->parent_resource != NULL - && av_info->parent_resource->working) { - - auto_version = (*vsn_hooks->auto_versionable)(av_info->parent_resource); - - if (auto_version == DAV_AUTO_VERSION_ALWAYS) { - if ((err = (*vsn_hooks->checkin)(av_info->parent_resource, - 0 /*keep_checked_out*/, NULL)) - != NULL) { - return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - apr_psprintf(r->pool, - "Unable to auto-checkin parent collection %s.", - ap_escape_html(r->pool, av_info->parent_resource->uri)), - err); - } - } - } - - return NULL; -} diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c b/rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c deleted file mode 100644 index cd39f067..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c +++ /dev/null @@ -1,791 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -/* -** DAV repository-independent lock functions -*/ - -#include "apr.h" -#include "apr_strings.h" - -#if APR_HAVE_STDIO_H -#include <stdio.h> /* for sprintf() */ -#endif - -#include "mod_dav.h" -#include "http_log.h" -#include "http_config.h" -#include "http_protocol.h" -#include "http_core.h" - - -/* --------------------------------------------------------------- -** -** Property-related lock functions -** -*/ - -/* -** dav_lock_get_activelock: Returns a <lockdiscovery> containing -** an activelock element for every item in the lock_discovery tree -*/ -DAV_DECLARE(const char *) dav_lock_get_activelock(request_rec *r, - dav_lock *lock, - dav_buffer *pbuf) -{ - dav_lock *lock_scan; - const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); - int count = 0; - dav_buffer work_buf = { 0 }; - apr_pool_t *p = r->pool; - - /* If no locks or no lock provider, there are no locks */ - if (lock == NULL || hooks == NULL) { - /* - ** Since resourcediscovery is defined with (activelock)*, - ** <D:activelock/> shouldn't be necessary for an empty lock. - */ - return ""; - } - - /* - ** Note: it could be interesting to sum the lengths of the owners - ** and locktokens during this loop. However, the buffer - ** mechanism provides some rough padding so that we don't - ** really need to have an exact size. Further, constructing - ** locktoken strings could be relatively expensive. - */ - for (lock_scan = lock; lock_scan != NULL; lock_scan = lock_scan->next) - count++; - - /* if a buffer was not provided, then use an internal buffer */ - if (pbuf == NULL) - pbuf = &work_buf; - - /* reset the length before we start appending stuff */ - pbuf->cur_len = 0; - - /* prep the buffer with a "good" size */ - dav_check_bufsize(p, pbuf, count * 300); - - for (; lock != NULL; lock = lock->next) { - char tmp[100]; - -#if DAV_DEBUG - if (lock->rectype == DAV_LOCKREC_INDIRECT_PARTIAL) { - /* ### crap. design error */ - dav_buffer_append(p, pbuf, - "DESIGN ERROR: attempted to product an " - "activelock element from a partial, indirect " - "lock record. Creating an XML parsing error " - "to ease detection of this situation: <"); - } -#endif - - dav_buffer_append(p, pbuf, "<D:activelock>" DEBUG_CR "<D:locktype>"); - switch (lock->type) { - case DAV_LOCKTYPE_WRITE: - dav_buffer_append(p, pbuf, "<D:write/>"); - break; - default: - /* ### internal error. log something? */ - break; - } - dav_buffer_append(p, pbuf, "</D:locktype>" DEBUG_CR "<D:lockscope>"); - switch (lock->scope) { - case DAV_LOCKSCOPE_EXCLUSIVE: - dav_buffer_append(p, pbuf, "<D:exclusive/>"); - break; - case DAV_LOCKSCOPE_SHARED: - dav_buffer_append(p, pbuf, "<D:shared/>"); - break; - default: - /* ### internal error. log something? */ - break; - } - dav_buffer_append(p, pbuf, "</D:lockscope>" DEBUG_CR); - sprintf(tmp, "<D:depth>%s</D:depth>" DEBUG_CR, - lock->depth == DAV_INFINITY ? "infinity" : "0"); - dav_buffer_append(p, pbuf, tmp); - - if (lock->owner) { - /* - ** This contains a complete, self-contained <DAV:owner> element, - ** with namespace declarations and xml:lang handling. Just drop - ** it in. - */ - dav_buffer_append(p, pbuf, lock->owner); - } - - dav_buffer_append(p, pbuf, "<D:timeout>"); - if (lock->timeout == DAV_TIMEOUT_INFINITE) { - dav_buffer_append(p, pbuf, "Infinite"); - } - else { - time_t now = time(NULL); - sprintf(tmp, "Second-%lu", (long unsigned int)(lock->timeout - now)); - dav_buffer_append(p, pbuf, tmp); - } - - dav_buffer_append(p, pbuf, - "</D:timeout>" DEBUG_CR - "<D:locktoken>" DEBUG_CR - "<D:href>"); - dav_buffer_append(p, pbuf, - (*hooks->format_locktoken)(p, lock->locktoken)); - dav_buffer_append(p, pbuf, - "</D:href>" DEBUG_CR - "</D:locktoken>" DEBUG_CR - "</D:activelock>" DEBUG_CR); - } - - return pbuf->buf; -} - -/* -** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a -** lockinfo XML element, then populates a dav_lock structure -** with its contents. -*/ -DAV_DECLARE(dav_error *) dav_lock_parse_lockinfo(request_rec *r, - const dav_resource *resource, - dav_lockdb *lockdb, - const apr_xml_doc *doc, - dav_lock **lock_request) -{ - apr_pool_t *p = r->pool; - dav_error *err; - apr_xml_elem *child; - dav_lock *lock; - - if (!dav_validate_root(doc, "lockinfo")) { - return dav_new_error(p, HTTP_BAD_REQUEST, 0, - "The request body contains an unexpected " - "XML root element."); - } - - if ((err = (*lockdb->hooks->create_lock)(lockdb, resource, - &lock)) != NULL) { - return dav_push_error(p, err->status, 0, - "Could not parse the lockinfo due to an " - "internal problem creating a lock structure.", - err); - } - - lock->depth = dav_get_depth(r, DAV_INFINITY); - if (lock->depth == -1) { - return dav_new_error(p, HTTP_BAD_REQUEST, 0, - "An invalid Depth header was specified."); - } - lock->timeout = dav_get_timeout(r); - - /* Parse elements in the XML body */ - for (child = doc->root->first_child; child; child = child->next) { - if (strcmp(child->name, "locktype") == 0 - && child->first_child - && lock->type == DAV_LOCKTYPE_UNKNOWN) { - if (strcmp(child->first_child->name, "write") == 0) { - lock->type = DAV_LOCKTYPE_WRITE; - continue; - } - } - if (strcmp(child->name, "lockscope") == 0 - && child->first_child - && lock->scope == DAV_LOCKSCOPE_UNKNOWN) { - if (strcmp(child->first_child->name, "exclusive") == 0) - lock->scope = DAV_LOCKSCOPE_EXCLUSIVE; - else if (strcmp(child->first_child->name, "shared") == 0) - lock->scope = DAV_LOCKSCOPE_SHARED; - if (lock->scope != DAV_LOCKSCOPE_UNKNOWN) - continue; - } - - if (strcmp(child->name, "owner") == 0 && lock->owner == NULL) { - const char *text; - - /* quote all the values in the <DAV:owner> element */ - apr_xml_quote_elem(p, child); - - /* - ** Store a full <DAV:owner> element with namespace definitions - ** and an xml:lang definition, if applicable. - */ - apr_xml_to_text(p, child, APR_XML_X2T_FULL_NS_LANG, doc->namespaces, - NULL, &text, NULL); - lock->owner = text; - - continue; - } - - return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, - apr_psprintf(p, - "The server cannot satisfy the " - "LOCK request due to an unknown XML " - "element (\"%s\") within the " - "DAV:lockinfo element.", - child->name)); - } - - *lock_request = lock; - return NULL; -} - -/* --------------------------------------------------------------- -** -** General lock functions -** -*/ - -/* dav_lock_walker: Walker callback function to record indirect locks */ -static dav_error * dav_lock_walker(dav_walk_resource *wres, int calltype) -{ - dav_walker_ctx *ctx = wres->walk_ctx; - dav_error *err; - - /* We don't want to set indirects on the target */ - if ((*wres->resource->hooks->is_same_resource)(wres->resource, - ctx->w.root)) - return NULL; - - if ((err = (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb, - wres->resource, 1, - ctx->lock)) != NULL) { - if (ap_is_HTTP_SERVER_ERROR(err->status)) { - /* ### add a higher-level description? */ - return err; - } - - /* add to the multistatus response */ - dav_add_response(wres, err->status, NULL); - - /* - ** ### actually, this is probably wrong: we want to fail the whole - ** ### LOCK process if something goes bad. maybe the caller should - ** ### do a dav_unlock() (e.g. a rollback) if any errors occurred. - */ - } - - return NULL; -} - -/* -** dav_add_lock: Add a direct lock for resource, and indirect locks for -** all children, bounded by depth. -** ### assume request only contains one lock -*/ -DAV_DECLARE(dav_error *) dav_add_lock(request_rec *r, - const dav_resource *resource, - dav_lockdb *lockdb, dav_lock *lock, - dav_response **response) -{ - dav_error *err; - int depth = lock->depth; - - *response = NULL; - - /* Requested lock can be: - * Depth: 0 for null resource, existing resource, or existing collection - * Depth: Inf for existing collection - */ - - /* - ** 2518 9.2 says to ignore depth if target is not a collection (it has - ** no internal children); pretend the client gave the correct depth. - */ - if (!resource->collection) { - depth = 0; - } - - /* In all cases, first add direct entry in lockdb */ - - /* - ** Append the new (direct) lock to the resource's existing locks. - ** - ** Note: this also handles locknull resources - */ - if ((err = (*lockdb->hooks->append_locks)(lockdb, resource, 0, - lock)) != NULL) { - /* ### maybe add a higher-level description */ - return err; - } - - if (depth > 0) { - /* Walk existing collection and set indirect locks */ - dav_walker_ctx ctx = { { 0 } }; - dav_response *multi_status; - - ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH; - ctx.w.func = dav_lock_walker; - ctx.w.walk_ctx = &ctx; - ctx.w.pool = r->pool; - ctx.w.root = resource; - ctx.w.lockdb = lockdb; - - ctx.r = r; - ctx.lock = lock; - - err = (*resource->hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); - if (err != NULL) { - /* implies a 5xx status code occurred. screw the multistatus */ - return err; - } - - if (multi_status != NULL) { - /* manufacture a 207 error for the multistatus response */ - *response = multi_status; - return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, - "Error(s) occurred on resources during the " - "addition of a depth lock."); - } - } - - return NULL; -} - -/* -** dav_lock_query: Opens the lock database. Returns a linked list of -** dav_lock structures for all direct locks on path. -*/ -DAV_DECLARE(dav_error*) dav_lock_query(dav_lockdb *lockdb, - const dav_resource *resource, - dav_lock **locks) -{ - /* If no lock database, return empty result */ - if (lockdb == NULL) { - *locks = NULL; - return NULL; - } - - /* ### insert a higher-level description? */ - return (*lockdb->hooks->get_locks)(lockdb, resource, - DAV_GETLOCKS_RESOLVED, - locks); -} - -/* dav_unlock_walker: Walker callback function to remove indirect locks */ -static dav_error * dav_unlock_walker(dav_walk_resource *wres, int calltype) -{ - dav_walker_ctx *ctx = wres->walk_ctx; - dav_error *err; - - /* Before removing the lock, do any auto-checkin required */ - if (wres->resource->working) { - /* ### get rid of this typecast */ - if ((err = dav_auto_checkin(ctx->r, (dav_resource *) wres->resource, - 0 /*undo*/, 1 /*unlock*/, NULL)) - != NULL) { - return err; - } - } - - if ((err = (*ctx->w.lockdb->hooks->remove_lock)(ctx->w.lockdb, - wres->resource, - ctx->locktoken)) != NULL) { - /* ### should we stop or return a multistatus? looks like STOP */ - /* ### add a higher-level description? */ - return err; - } - - return NULL; -} - -/* -** dav_get_direct_resource: -** -** Find a lock on the specified resource, then return the resource the -** lock was applied to (in other words, given a (possibly) indirect lock, -** return the direct lock's corresponding resource). -** -** If the lock is an indirect lock, this usually means traversing up the -** namespace [repository] hierarchy. Note that some lock providers may be -** able to return this information with a traversal. -*/ -static dav_error * dav_get_direct_resource(apr_pool_t *p, - dav_lockdb *lockdb, - const dav_locktoken *locktoken, - const dav_resource *resource, - const dav_resource **direct_resource) -{ - if (lockdb->hooks->lookup_resource != NULL) { - return (*lockdb->hooks->lookup_resource)(lockdb, locktoken, - resource, direct_resource); - } - - *direct_resource = NULL; - - /* Find the top of this lock- - * If r->filename's direct locks include locktoken, use r->filename. - * If r->filename's indirect locks include locktoken, retry r->filename/.. - * Else fail. - */ - while (resource != NULL) { - dav_error *err; - dav_lock *lock; - dav_resource *parent; - - /* - ** Find the lock specified by <locktoken> on <resource>. If it is - ** an indirect lock, then partial results are okay. We're just - ** trying to find the thing and know whether it is a direct or - ** an indirect lock. - */ - if ((err = (*lockdb->hooks->find_lock)(lockdb, resource, locktoken, - 1, &lock)) != NULL) { - /* ### add a higher-level desc? */ - return err; - } - - /* not found! that's an error. */ - if (lock == NULL) { - return dav_new_error(p, HTTP_BAD_REQUEST, 0, - "The specified locktoken does not correspond " - "to an existing lock on this resource."); - } - - if (lock->rectype == DAV_LOCKREC_DIRECT) { - /* we found the direct lock. return this resource. */ - - *direct_resource = resource; - return NULL; - } - - /* the lock was indirect. move up a level in the URL namespace */ - if ((err = (*resource->hooks->get_parent_resource)(resource, - &parent)) != NULL) { - /* ### add a higher-level desc? */ - return err; - } - resource = parent; - } - - return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, - "The lock database is corrupt. A direct lock could " - "not be found for the corresponding indirect lock " - "on this resource."); -} - -/* -** dav_unlock: Removes all direct and indirect locks for r->filename, -** with given locktoken. If locktoken == null_locktoken, all locks -** are removed. If r->filename represents an indirect lock, -** we must unlock the appropriate direct lock. -** Returns OK or appropriate HTTP_* response and logs any errors. -** -** ### We've already crawled the tree to ensure everything was locked -** by us; there should be no need to incorporate a rollback. -*/ -DAV_DECLARE(int) dav_unlock(request_rec *r, const dav_resource *resource, - const dav_locktoken *locktoken) -{ - int result; - dav_lockdb *lockdb; - const dav_resource *lock_resource = resource; - const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); - const dav_hooks_repository *repos_hooks = resource->hooks; - dav_walker_ctx ctx = { { 0 } }; - dav_response *multi_status; - dav_error *err; - - /* If no locks provider, then there is nothing to unlock. */ - if (hooks == NULL) { - return OK; - } - - /* 2518 requires the entire lock to be removed if resource/locktoken - * point to an indirect lock. We need resource of the _direct_ - * lock in order to walk down the tree and remove the locks. So, - * If locktoken != null_locktoken, - * Walk up the resource hierarchy until we see a direct lock. - * Or, we could get the direct lock's db/key, pick out the URL - * and do a subrequest. I think walking up is faster and will work - * all the time. - * Else - * Just start removing all locks at and below resource. - */ - - if ((err = (*hooks->open_lockdb)(r, 0, 1, &lockdb)) != NULL) { - /* ### return err! maybe add a higher-level desc */ - /* ### map result to something nice; log an error */ - return HTTP_INTERNAL_SERVER_ERROR; - } - - if (locktoken != NULL - && (err = dav_get_direct_resource(r->pool, lockdb, - locktoken, resource, - &lock_resource)) != NULL) { - /* ### add a higher-level desc? */ - /* ### should return err! */ - return err->status; - } - - /* At this point, lock_resource/locktoken refers to a direct lock (key), ie - * the root of a depth > 0 lock, or locktoken is null. - */ - ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL; - ctx.w.func = dav_unlock_walker; - ctx.w.walk_ctx = &ctx; - ctx.w.pool = r->pool; - ctx.w.root = lock_resource; - ctx.w.lockdb = lockdb; - - ctx.r = r; - ctx.locktoken = locktoken; - - err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); - - /* ### fix this! */ - /* ### do something with multi_status */ - result = err == NULL ? OK : err->status; - - (*hooks->close_lockdb)(lockdb); - - return result; -} - -/* dav_inherit_walker: Walker callback function to inherit locks */ -static dav_error * dav_inherit_walker(dav_walk_resource *wres, int calltype) -{ - dav_walker_ctx *ctx = wres->walk_ctx; - - if (ctx->skip_root - && (*wres->resource->hooks->is_same_resource)(wres->resource, - ctx->w.root)) { - return NULL; - } - - /* ### maybe add a higher-level desc */ - return (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb, - wres->resource, 1, - ctx->lock); -} - -/* -** dav_inherit_locks: When a resource or collection is added to a collection, -** locks on the collection should be inherited to the resource/collection. -** (MOVE, MKCOL, etc) Here we propagate any direct or indirect locks from -** parent of resource to resource and below. -*/ -static dav_error * dav_inherit_locks(request_rec *r, dav_lockdb *lockdb, - const dav_resource *resource, - int use_parent) -{ - dav_error *err; - const dav_resource *which_resource; - dav_lock *locks; - dav_lock *scan; - dav_lock *prev; - dav_walker_ctx ctx = { { 0 } }; - const dav_hooks_repository *repos_hooks = resource->hooks; - dav_response *multi_status; - - if (use_parent) { - dav_resource *parent; - if ((err = (*repos_hooks->get_parent_resource)(resource, - &parent)) != NULL) { - /* ### add a higher-level desc? */ - return err; - } - if (parent == NULL) { - /* ### map result to something nice; log an error */ - return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not fetch parent resource. Unable to " - "inherit locks from the parent and apply " - "them to this resource."); - } - which_resource = parent; - } - else { - which_resource = resource; - } - - if ((err = (*lockdb->hooks->get_locks)(lockdb, which_resource, - DAV_GETLOCKS_PARTIAL, - &locks)) != NULL) { - /* ### maybe add a higher-level desc */ - return err; - } - - if (locks == NULL) { - /* No locks to propagate, just return */ - return NULL; - } - - /* - ** (1) Copy all indirect locks from our parent; - ** (2) Create indirect locks for the depth infinity, direct locks - ** in our parent. - ** - ** The append_locks call in the walker callback will do the indirect - ** conversion, but we need to remove any direct locks that are NOT - ** depth "infinity". - */ - for (scan = locks, prev = NULL; - scan != NULL; - prev = scan, scan = scan->next) { - - if (scan->rectype == DAV_LOCKREC_DIRECT - && scan->depth != DAV_INFINITY) { - - if (prev == NULL) - locks = scan->next; - else - prev->next = scan->next; - } - } - - /* <locks> has all our new locks. Walk down and propagate them. */ - - ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL; - ctx.w.func = dav_inherit_walker; - ctx.w.walk_ctx = &ctx; - ctx.w.pool = r->pool; - ctx.w.root = resource; - ctx.w.lockdb = lockdb; - - ctx.r = r; - ctx.lock = locks; - ctx.skip_root = !use_parent; - - /* ### do something with multi_status */ - return (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); -} - -/* --------------------------------------------------------------- -** -** Functions dealing with lock-null resources -** -*/ - -/* -** dav_get_resource_state: Returns the state of the resource -** r->filename: DAV_RESOURCE_NULL, DAV_RESOURCE_LOCK_NULL, -** or DAV_RESOURCE_EXIST. -** -** Returns DAV_RESOURCE_ERROR if an error occurs. -*/ -DAV_DECLARE(int) dav_get_resource_state(request_rec *r, - const dav_resource *resource) -{ - const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); - - if (resource->exists) - return DAV_RESOURCE_EXISTS; - - if (hooks != NULL) { - dav_error *err; - dav_lockdb *lockdb; - int locks_present; - - /* - ** A locknull resource has the form: - ** - ** known-dir "/" locknull-file - ** - ** It would be nice to look into <resource> to verify this form, - ** but it does not have enough information for us. Instead, we - ** can look at the path_info. If the form does not match, then - ** there is no way we could have a locknull resource -- it must - ** be a plain, null resource. - ** - ** Apache sets r->filename to known-dir/unknown-file and r->path_info - ** to "" for the "proper" case. If anything is in path_info, then - ** it can't be a locknull resource. - ** - ** ### I bet this path_info hack doesn't work for repositories. - ** ### Need input from repository implementors! What kind of - ** ### restructure do we need? New provider APIs? - */ - if (r->path_info != NULL && *r->path_info != '\0') { - return DAV_RESOURCE_NULL; - } - - if ((err = (*hooks->open_lockdb)(r, 1, 1, &lockdb)) == NULL) { - /* note that we might see some expired locks... *shrug* */ - err = (*hooks->has_locks)(lockdb, resource, &locks_present); - (*hooks->close_lockdb)(lockdb); - } - - if (err != NULL) { - /* ### don't log an error. return err. add higher-level desc. */ - - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Failed to query lock-null status for %s", - r->filename); - - return DAV_RESOURCE_ERROR; - } - - if (locks_present) - return DAV_RESOURCE_LOCK_NULL; - } - - return DAV_RESOURCE_NULL; -} - -DAV_DECLARE(dav_error *) dav_notify_created(request_rec *r, - dav_lockdb *lockdb, - const dav_resource *resource, - int resource_state, - int depth) -{ - dav_error *err; - - if (resource_state == DAV_RESOURCE_LOCK_NULL) { - - /* - ** The resource is no longer a locknull resource. This will remove - ** the special marker. - ** - ** Note that a locknull resource has already inherited all of the - ** locks from the parent. We do not need to call dav_inherit_locks. - ** - ** NOTE: some lock providers record locks for locknull resources using - ** a different key than for regular resources. this will shift - ** the lock information between the two key types. - */ - (void)(*lockdb->hooks->remove_locknull_state)(lockdb, resource); - - /* - ** There are resources under this one, which are new. We must - ** propagate the locks down to the new resources. - */ - if (depth > 0 && - (err = dav_inherit_locks(r, lockdb, resource, 0)) != NULL) { - /* ### add a higher level desc? */ - return err; - } - } - else if (resource_state == DAV_RESOURCE_NULL) { - - /* ### should pass depth to dav_inherit_locks so that it can - ** ### optimize for the depth==0 case. - */ - - /* this resource should inherit locks from its parent */ - if ((err = dav_inherit_locks(r, lockdb, resource, 1)) != NULL) { - - err = dav_push_error(r->pool, err->status, 0, - "The resource was created successfully, but " - "there was a problem inheriting locks from " - "the parent resource.", - err); - return err; - } - } - /* else the resource already exists and its locks are correct. */ - - return NULL; -} |