repos.c revision f94aab38f6ee899f463f0118ea395291f7c5b4ce
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*/
/*
** DAV filesystem-based repository provider
*/
#include "apr_file_io.h"
#include "apr_strings.h"
#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 DEBUG_PATHNAME_STYLE 0
/* context needed to identify a resource */
struct dav_resource_private {
const char *pathname; /* full pathname to resource */
};
/* private context for doing a filesystem walk */
typedef struct {
/* the input walk parameters */
const dav_walk_params *params;
/* reused as we walk */
typedef struct {
int is_move; /* is this a MOVE? */
/* 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;
/* 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
/* pull this in from the other source file */
extern const dav_hooks_locks dav_hooks_locks_fs;
/* forward-declare this sucker */
static const dav_hooks_repository dav_hooks_repository_fs;
/*
** The Provider ID is used to differentiate "logical" providers that use
** the same set of hook functions. Essentially, the ID is an instance
** handle and the hooks are a vtable.
**
** In this module, we only have a single provider for each type, so we
** actually ignore the Provider ID.
*/
#define DAV_FS_PROVIDER_ID 0
/*
** The namespace URIs that we use. This list and the enumeration must
** stay in sync.
*/
static const char * const dav_fs_namespace_uris[] =
{
"DAV:",
NULL /* sentinel */
};
enum {
DAV_FS_URI_DAV, /* the DAV: namespace URI */
DAV_FS_URI_MYPROPS /* the namespace URI for our custom props */
};
/*
** The properties that we define.
*/
enum {
/* using DAV_FS_URI_DAV */
/* using DAV_FS_URI_MYPROPS */
};
/* NOTE: the magic "200" is derived from the ranges in mod_dav.h */
typedef struct {
int ns;
const char * name;
int propid;
static const dav_fs_liveprop_name dav_fs_props[] =
{
/* ### these aren't FS specific */
{ 0 } /* sentinel */
};
/* 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 */
};
/* forward declaration for internal treewalkers */
dav_response **response);
const dav_resource *root_dst,
dav_response **response);
/* --------------------------------------------------------------------
**
** PRIVATE REPOSITORY FUNCTIONS
*/
{
}
{
}
void dav_fs_dir_file_name(
const dav_resource *resource,
const char **dirpath_p,
const char **fname_p)
{
if (resource->collection) {
}
else {
/* remove trailing slash from dirpath, unless it's the root dir */
/* ### Win32 check */
}
}
}
/* Note: picked up from ap_gm_timestr_822() */
/* NOTE: buf must be at least DAV_TIMEBUF_SIZE chars in size */
{
/* ### what to do if fails? */
if (style == DAV_STYLE_ISO8601) {
/* ### should we use "-00:00" instead of "Z" ?? */
/* 20 chars plus null term */
return;
}
/* RFC 822 date format; as strftime '%a, %d %b %Y %T GMT' */
/* 29 chars plus null term */
"%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
}
static dav_error * dav_fs_copymove_file(
int is_move,
apr_pool_t * p,
const char *src,
const char *dst,
{
dav_buffer work_buf = { 0 };
!= 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? */
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 writing");
}
while (1) {
/* ### 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");
}
/* write any bytes that were read (applies to APR_EOF, too) */
int save_errno = errno;
/* ### 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.");
}
if (save_errno == ENOSPC) {
return dav_new_error(p, HTTP_INSUFFICIENT_STORAGE, 0,
"There is not enough storage to write to "
"this resource.");
}
/* ### use something besides 500? */
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not write output file");
}
break;
}
/* ### 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? */
"Could not remove source file after move. "
"Destination was removed to ensure consistency.");
return err;
}
return NULL;
}
/* ### 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;
const char *dst;
/* build the propset pathname for the source file */
/* the source file doesn't exist */
return NULL;
}
/* build the pathname for the destination state dir */
/* ### do we need to deal with the umask? */
/* ensure that it exists */
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 */
/* 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 */
/* ### 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 */
/* simple rename is possible since it is on the same device */
/* ### 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 NULL;
}
const dav_resource *src,
const dav_resource *dst,
{
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;
/* Get directory and filename for resources */
/* Get the corresponding state files for each resource */
#if DAV_DEBUG
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
"DESIGN ERROR: dav_dbm_get_statefiles() "
"returned inconsistent results.");
}
#endif
pbuf);
pbuf);
/* ### 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. */
"The server is now in an inconsistent state.";
}
}
return err;
}
{
const char *dirpath;
const char *fname;
const char *state1;
const char *state2;
const char *pathname;
/* Get directory, filename, and state-file names for the resource */
/* build the propset pathname for the file */
pathname = apr_pstrcat(p,
NULL);
/* note: we may get ENOENT if the state dir is not present */
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not remove properties.");
}
/* build the propset pathname for the file */
pathname = apr_pstrcat(p,
NULL);
/* ### 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_resource * dav_fs_get_resource(
request_rec *r,
const char *root_dir,
const char *workspace,
const char *target,
int is_label)
{
char *s;
char *filename;
/* ### optimize this into a single allocation! */
/* Create private resource context descriptor */
/* Preserve case on OSes which fold canonical filenames */
#if 0
/* ### not available in Apache 2.0 yet */
#else
#endif
/*
** If there is anything in the path_info, then this indicates that the
** it onto the filename so that we get a "valid" pathname for null
** resources.
*/
/* make sure the pathname does not have a trailing "/" */
}
/* Create resource descriptor */
/* make sure the URI does not have a trailing "/" */
}
else {
}
if (r->finfo.protection != 0) {
/* unused info in the URL will indicate a null resource */
if (resource->collection) {
/* only a trailing "/" is allowed */
/*
*/
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 NULL; /* becomes HTTP_NOT_FOUND */
}
/* retain proper integrity across the structures */
}
}
}
return resource;
}
{
char *dirpath;
/* If given resource is root, then there is no parent */
#ifdef WIN32
#else
#endif
)
return NULL;
/* ### optimize this into a single allocation! */
/* Create private resource context descriptor */
}
}
return parent_resource;
}
static int dav_fs_is_same_resource(
const dav_resource *res1,
const dav_resource *res2)
{
return 0;
#ifdef WIN32
#else
else
#endif
}
static int dav_fs_is_parent_resource(
const dav_resource *res1,
const dav_resource *res2)
{
return 0;
/* it is safe to use ctx2 now */
}
dav_stream **stream)
{
switch (mode) {
case DAV_MODE_READ:
case DAV_MODE_READ_SEEKABLE:
default:
break;
case DAV_MODE_WRITE_TRUNC:
break;
case DAV_MODE_WRITE_SEEKABLE:
break;
}
ds->p = p;
ds->p) != APR_SUCCESS) {
/* ### use something besides 500? */
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
"An error occurred while opening a resource.");
}
/* (APR registers cleanups for the fd with the pool) */
return NULL;
}
{
if (!commit) {
/* ### use a better description? */
"There was a problem removing (rolling "
"back) the resource "
"when it was being closed.");
}
}
return NULL;
}
{
/* ### use something besides 500? */
"An error occurred while reading from a "
"resource.");
}
return NULL;
}
{
if (status == APR_ENOSPC) {
"There is not enough storage to write to "
"this resource.");
}
else if (status != APR_SUCCESS) {
/* ### use something besides 500? */
"An error occurred while writing to a "
"resource.");
}
return NULL;
}
{
/* ### should check whether apr_seek set abs_pos was set to the
* correct position? */
/* ### use something besides 500? */
"Could not seek to specified position in the "
"resource.");
}
return NULL;
}
const dav_resource *resource)
{
/* ### this function isn't really used since we have a get_pathname */
return NULL;
/* make sure the proper mtime is in the request record */
/* ### note that these use r->filename rather than <resource> */
ap_set_etag(r);
/* we accept byte-ranges */
/* set up the Content-Length header */
/* ### how to set the content type? */
/* ### until this is resolved, the Content-Type header is busted */
#endif
return NULL;
}
static const char * dav_fs_get_pathname(
const dav_resource *resource,
void **free_handle_p)
{
}
#endif
static void dav_fs_free_file(void *free_handle)
{
/* nothing to free ... */
}
{
"There is not enough storage to create "
"this collection.");
}
else if (status != APR_SUCCESS) {
/* ### refine this error message? */
"Unable to create collection.");
}
/* update resource state to show it exists as a collection */
return NULL;
}
int calltype)
{
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? */
}
else {
/* ### assume it was a permissions problem */
/* ### need a description here */
}
}
}
else {
/* ### 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.
*/
/* ### use errno to generate DAV:responsedescription? */
/* 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_buffer work_buf = { 0 };
* including the state dirs
*/
if (src->collection) {
dav_walk_params params = { 0 };
/* params.walk_ctx is managed by dav_fs_internal_walk() */
/* postfix is needed for MOVE to delete source dirs */
if (is_move)
/* note that we return the error OR the multistatus. never both */
&multi_status)) != NULL) {
/* on a "real" error, then just punt. nothing else to do. */
return err;
}
/* some multistatus responses exist. wrap them in a 207 */
"Error(s) occurred on some resources during "
}
return NULL;
}
/* not a collection */
/* ### push a higher-level description? */
return err;
}
}
static dav_error * dav_fs_copy_resource(
const dav_resource *src,
int depth,
{
#if DAV_DEBUG
/*
** ### strictly speaking, this is a design error; we should not
** ### have reached this point.
*/
"DESIGN ERROR: a mix of repositories "
"was passed to copy_resource.");
}
#endif
/* update state of destination resource to show it exists */
}
return err;
}
static dav_error * dav_fs_move_resource(
{
int can_rename = 0;
#if DAV_DEBUG
/*
** ### strictly speaking, this is a design error; we should not
** ### have reached this point.
*/
"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.
*/
/* target exists and is on the same device. */
can_rename = 1;
}
}
else {
const char *dirpath;
/* destination does not exist, but the parent directory should,
* so try it
*/
can_rename = 1;
}
}
/* if we can't simply rename, then do it the hard way... */
if (!can_rename) {
/* update resource states */
src->collection = 0;
}
return err;
}
/* a rename should work. do it, and move properties as well */
/* no multistatus response */
/* ### APR has no rename? */
/* ### should have a better error than this. */
"Could not rename resource.");
}
/* update resource states */
src->collection = 0;
/* no error. we're done. go ahead and return now. */
return NULL;
}
/* error occurred during properties move; try to put resource back */
/* couldn't put it back! */
"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 */
dst->collection = 0;
/* resource moved back, but properties may be inconsistent */
"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);
}
{
/* do not attempt to remove a null resource,
* or a collection with children
*/
/* try to remove the resource */
int result;
/*
** 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 != 0) {
/* ### assume there is a permissions problem */
/* ### use errno to generate DAV:responsedescription? */
}
}
return NULL;
}
{
/* if a collection, recursively remove it and its children,
* including the state dirs
*/
if (resource->collection) {
dav_walk_params params = { 0 };
&multi_status)) != NULL) {
/* on a "real" error, then just punt. nothing else to do. */
return err;
}
/* some multistatus responses exist. wrap them in a 207 */
"Error(s) occurred on some resources during "
"the deletion process.");
}
/* no errors... update resource state */
resource->collection = 0;
return NULL;
}
/* not a collection; remove the file and its properties */
/* ### put a description in here */
}
/* update resource state */
resource->collection = 0;
/* remove properties and return its result */
}
/* ### move this to dav_util? */
/* Walk recursively down through directories, *
* including lock-null resources as we go. */
{
/* ensure the context is prepared properly, then call the func */
return err;
}
return NULL;
}
/* put a trailing slash onto the directory, in preparation for appending
* files to it as we discovery them within the directory */
/* if a secondary path is present, then do that, too */
}
/* Note: the URI should ALREADY have a trailing "/" */
/* for this first pass of files, all resources exist */
/* a file is the default; we'll adjust if we hit a directory */
/* open and scan the directory */
/* ### need a better error */
}
char *name;
/* avoid recursing into our current, parent, or state directories */
continue;
}
/* ### need to authorize each file */
/* ### example: .htaccess is normally configured to fail auth */
/* stuff in the state directory is never authorized! */
continue;
}
}
/* skip the state dir unless a HIDDEN is performed */
continue;
}
/* append this file onto the path buffer (copy null term) */
/* woah! where'd it go? */
/* ### should have a better error here */
break;
}
/* copy the file to the URI, too. NOTE: we will pad an extra byte
for the trailing slash later. */
/* if there is a secondary path, then do that, too */
}
/* set up the (internal) pathnames for the two resources */
/* set up the URI for the current resource */
/* ### for now, only process regular files (e.g. skip symlinks) */
/* call the function for the specified dir + file */
DAV_CALLTYPE_MEMBER)) != NULL) {
/* ### maybe add a higher-level description? */
break;
}
}
/* adjust length to incorporate the subdir name */
/* adjust URI length to incorporate subdir and a slash */
/* switch over to a collection */
/* recurse on the subdir */
/* ### don't always want to quit on error from single child */
/* ### maybe add a higher-level description? */
break;
}
/* put the various information back */
/* assert: res1.exists == 1 */
}
}
/* ### check the return value of this? */
return err;
/* null terminate the directory name */
/* Include any lock null resources found in this collection */
/* ### maybe add a higher-level description? */
return err;
}
/* put a slash back on the end of the directory */
/* these are all non-existant (files) */
/*
** 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.
*/
len + 1, 0);
}
/* set up the (internal) pathnames for the two resources */
/* set up the URI for the current resource */
/*
** 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).
*/
/* ### maybe add a higher-level description? */
return err;
}
/* call the function for the specified dir + file */
DAV_CALLTYPE_LOCKNULL)) != NULL) {
/* ### maybe add a higher-level description? */
return err;
}
}
/* reset the exists flag */
}
/* replace the dirs' trailing slashes with null terms */
}
/* this is a collection which exists */
}
return NULL;
}
const dav_resource *root_dst,
{
dav_fs_walker_context fsctx = { 0 };
dav_fs_copymove_walk_ctx cm_ctx = { 0 };
#if DAV_DEBUG
"DESIGN ERROR: walker called to walk locknull "
"resources, but a lockdb was not provided.");
}
#endif
/* ### zero out versioned, working, baselined? */
/* the pathname is stored in the path1 buffer */
/* res2 does not exist -- clear its finfo structure */
/* the pathname is stored in the path2 buffer */
}
/* prep the URI buffer */
/* if we have a directory, then ensure the URI has a trailing "/" */
/* this will fall into the pad area */
}
/* the current resource's URI is stored in the uri_buf buffer */
/* point the callback's resource at our structure */
return err;
}
{
}
/* 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 dav_hooks_repository dav_hooks_repository_fs =
{
DEBUG_GET_HANDLER, /* normally: special GET handling not required */
#else
0,
#endif
};
{
const dav_fs_liveprop_name *scan;
int ns;
if (*ns_uri == 'h'
}
ns = DAV_FS_URI_DAV;
}
else {
/* we don't define this property */
return 0;
}
return 0;
}
{
const char *value;
const char *s;
const dav_fs_liveprop_name *scan;
int 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];
if (!DAV_PROPID_FS_OURS(propid))
return DAV_PROP_INSERT_NOTME;
/*
** None of FS provider properties are defined if the resource does not
** exist. Just bail for this case.
**
** Note that DAV:displayname and DAV:source will be stored as dead
** properties; the NOTDEF return code indicates that dav_props.c should
** look there for the value.
**
** 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.
*/
return DAV_PROP_INSERT_NOTDEF;
switch (propid) {
/*
** 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.
*/
buf);
break;
/* our property, but not defined on collection resources */
if (resource->collection)
return DAV_PROP_INSERT_NOTDEF;
break;
case DAV_PROPID_FS_getetag:
break;
buf);
break;
case DAV_PROPID_FS_executable:
#ifdef WIN32
/* our property, but not defined on the Win32 platform */
return DAV_PROP_INSERT_NOTDEF;
#else
/* our property, but not defined on collection resources */
if (resource->collection)
return DAV_PROP_INSERT_NOTDEF;
/* the files are "ours" so we only need to check owner exec privs */
value = "T";
else
value = "F";
break;
#endif /* WIN32 */
case DAV_PROPID_FS_source:
default:
/*
** This property is not defined. However, it may be a dead
** property.
*/
return DAV_PROP_INSERT_NOTDEF;
}
/* assert: value != NULL */
break;
/* assert: scan->name != NULL */
/* map our namespace into a global NS index */
/* DBG3("FS: inserting lp%d:%s (local %d)", ns, scan->name, scan->ns); */
if (insvalue) {
/* use D: prefix to refer to the DAV: namespace URI */
}
else {
/* use D: prefix to refer to the DAV: namespace URI */
}
ap_text_append(p, phdr, s);
/* we inserted a name or value (this prop is done) */
return which;
}
{
/* 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;
}
#ifndef WIN32
/*
** Note: this property is not defined on the Win32 platform.
** dav_fs_insert_prop() won't insert it, but we may as
** well not even call it.
*/
#endif
/* ### we know the others aren't defined as liveprops */
}
int propid)
{
if (!DAV_PROPID_FS_OURS(propid))
return DAV_PROP_RW_NOTME;
if (propid == DAV_PROPID_FS_displayname
|| propid == DAV_PROPID_FS_source
#ifndef WIN32
/* this property is not usable (writeable) on the Win32 platform */
#endif
)
return DAV_PROP_RW_YES;
return DAV_PROP_RW_NO;
}
const ap_xml_elem *elem,
int operation,
void **context,
int *defer_to_dead)
{
char value;
*defer_to_dead = 1;
return NULL;
}
if (operation == DAV_PROP_OP_DELETE) {
"The 'executable' property cannot be removed.");
}
/* ### hmm. this isn't actually looking at all the possible text items */
? NULL
/* DBG3("name=%s cdata=%s f_cdata=%s",elem->name,cdata ? cdata->text : "[null]",f_cdata ? f_cdata->text : "[null]"); */
"The 'executable' property expects a single "
"character, valued 'T' or 'F'. There was no "
"value submitted.");
}
}
goto too_long;
goto too_long;
"The 'executable' property expects a single "
"character, valued 'T' or 'F'. The value "
"submitted is invalid.");
}
return NULL;
"The 'executable' property expects a single "
"character, valued 'T' or 'F'. The value submitted "
"has too many characters.");
}
const ap_xml_elem *elem,
int operation,
void *context,
{
/* 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); */
return NULL;
perms &= ~APR_UEXECUTE;
if (value)
perms |= APR_UEXECUTE;
"Could not set the executable flag of the "
"target resource.");
}
/* update the resource and set up the rollback context */
return NULL;
}
int operation,
void *context,
{
/* nothing to do */
}
int operation,
void *context,
{
/* assert: prop == executable. operation == SET. */
/* restore the executable bit */
if (value)
perms |= APR_UEXECUTE;
"After a failure occurred, the resource's "
"executable flag could not be restored.");
}
/* restore the resource's state */
return NULL;
}
static const dav_hooks_liveprop dav_hooks_liveprop_fs =
{
#ifdef WIN32
NULL,
#else
"http://apache.org/dav/propset/fs/1", /* filesystem, set 1 */
#endif
};
static const dav_provider dav_fs_provider =
{
};
{
#ifndef WIN32
*(const char **)apr_push_array(uris) =
#endif
}
const dav_hooks_liveprop **hooks)
{
if (propid == 0)
return 0;
return propid;
}
{
}
void dav_fs_register(apr_pool_t *p)
{
/* register the namespace URIs */
const char * const * uris = dav_fs_namespace_uris;
}
/* register the repository provider */
}