props.c revision 26250b0077972bf21b6d8a8d23772a4cf53f9477
2818N/A/* ==================================================================== 2818N/A * The Apache Software License, Version 1.1 2818N/A * Copyright (c) 2000 The Apache Software Foundation. All rights 2818N/A * Redistribution and use in source and binary forms, with or without 2818N/A * modification, are permitted provided that the following conditions 2818N/A * 1. Redistributions of source code must retain the above copyright 2818N/A * notice, this list of conditions and the following disclaimer. 2818N/A * 2. Redistributions in binary form must reproduce the above copyright 2818N/A * notice, this list of conditions and the following disclaimer in 2818N/A * the documentation and/or other materials provided with the 2818N/A * 3. The end-user documentation included with the redistribution, 2818N/A * if any, must include the following acknowledgment: 2818N/A * "This product includes software developed by the 5466N/A * Alternately, this acknowledgment may appear in the software itself, 2818N/A * if and wherever such third-party acknowledgments normally appear. 2818N/A * 4. The names "Apache" and "Apache Software Foundation" must 2818N/A * not be used to endorse or promote products derived from this 6444N/A * software without prior written permission. For written 2818N/A * permission, please contact apache@apache.org. 2818N/A * 5. Products derived from this software may not be called "Apache", 2818N/A * nor may "Apache" appear in their name, without prior written 6444N/A * permission of the Apache Software Foundation. 3869N/A * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 2818N/A * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2818N/A * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 6444N/A * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 2899N/A * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3817N/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3817N/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 3817N/A * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 3817N/A * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2818N/A * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2818N/A * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2818N/A * ==================================================================== 2818N/A * This software consists of voluntary contributions made by many 2818N/A * individuals on behalf of the Apache Software Foundation. For more 4976N/A * information on the Apache Software Foundation, please see 2818N/A** DAV extension module for Apache 2.0.* 2818N/A** - Property database handling (repository-independent) 6901N/A** This version assumes that there is a per-resource database provider 2818N/A** to record properties. The database provider decides how and where to 2818N/A** The DBM keys for the properties have the following form: 2818N/A** The namespace provides an integer index into the namespace table 2818N/A** (see below). propname is simply the property name, without a namespace 2818N/A** A special case exists for properties that had a prefix starting with 2818N/A** "xml". The XML Specification reserves these for future use. mod_dav 2818N/A** stores and retrieves them unchanged. The keys for these properties 5934N/A** The propname will contain the prefix and the property name. For 5934N/A** example, a key might be ":xmlfoo:name" 4302N/A** The ":name" style will also be used for properties that do not 3633N/A** exist within a namespace. 3633N/A** The DBM values consist of two null-terminated strings, appended 2818N/A** together (the null-terms are retained and stored in the database). 2818N/A** The first string is the xml:lang value for the property. An empty 2818N/A** string signifies that a lang value was not in context for the value. 2818N/A** The second string is the property value itself. 2818N/A** The namespace table is an array that lists each of the namespaces 2818N/A** that are in use by the properties in the given propdb. Each entry 2818N/A** in the array is a simple URI. 2818N/A** The prefix used for the property is stripped and the URI for it 3693N/A** is entered into the namespace table. Also, any namespaces used 3693N/A** within the property value will be entered into the table (and 3693N/A** stripped from the child elements). 3693N/A** The namespaces are stored in the DBM database under the "METADATA" key. 2818N/A** Within the property values, the namespace declarations (xmlns...) 2818N/A** are stripped. Each element and attribute will have its prefix removed 2818N/A** and a new prefix inserted. 2818N/A** This must be done so that we can return multiple properties in a 2818N/A** PROPFIND which may have (originally) used conflicting prefixes. For 3251N/A** that case, we must bind all property value elements to new namespace 2818N/A** This implies that clients must NOT be sensitive to the namespace 2818N/A** prefix used for their properties. It WILL change when the properties 2818N/A** are returned (we return them as "ns<index>", e.g. "ns5"). Also, the 2818N/A** property value can contain ONLY XML elements and CDATA. PI and comment 2818N/A** elements will be stripped. CDATA whitespace will be preserved, but 2818N/A** whitespace within element tags will be altered. Attribute ordering 3693N/A** may be altered. Element and CDATA ordering will be preserved. 2818N/A** ATTRIBUTES ON PROPERTY NAME ELEMENTS 2818N/A** <propname1>value</propname1> 3497N/A** <propname2>value</propname1> 3497N/A** This implementation (mod_dav) DOES NOT save any attributes that are 3497N/A** associated with the <propname1> element. The property value is deemed 3497N/A** to be only the contents ("value" in the above example). 3497N/A** We do store the xml:lang value (if any) that applies to the context 3497N/A** of the <propname1> element. Whether the xml:lang attribute is on 3497N/A** <propname1> itself, or from a higher level element, we will store it 3497N/A** The DBM db contains a key named "METADATA" that holds database-level 3497N/A** information, such as the namespace table. The record also contains the 4828N/A** db's version number as the very first 16-bit value. This first number 4828N/A** is actually stored as two single bytes: the first byte is a "major" 3497N/A** version number. The second byte is a "minor" number. 3497N/A** If the major number is not what mod_dav expects, then the db is closed 3497N/A** immediately and an error is returned. A minor number change is 4828N/A** the database format. For example, a newer dav_props might update the 3497N/A** minor value and append information to the end of the metadata record 2818N/A** (which would be ignored by previous versions). 2818N/A** At the moment, for the dav_get_allprops() and dav_get_props() functions, 2818N/A** we must return a set of xmlns: declarations for ALL known namespaces 2818N/A** in the file. There isn't a way to filter this because we don't know 2818N/A** which are going to be used or not. Examining property names is not 2818N/A** sufficient because the property values could use entirely different 2818N/A** ==> we must devise a scheme where we can "garbage collect" the namespace 2818N/A** entries from the property database. 4089N/A** There is some rough support for writeable DAV:getcontenttype and 4089N/A** DAV:getcontentlanguage properties. If this #define is (1), then 2818N/A** this support is disabled. 3747N/A** We are disabling it because of a lack of support in GET and PUT 3747N/A** operations. For GET, it would be "expensive" to look for a propdb, 3747N/A** open it, and attempt to extract the Content-Type and Content-Language 3747N/A** (Handling the PUT would not be difficult, though) 3817N/A/* the namespace URI was not found; no ID is available */ 3817N/A ** Prior versions could have keys or values with invalid 3817N/A ** 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". ** 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 ** This introduced the xml:lang value into the property value's int version;
/* *minor* version of this db */ int deferred;
/* open of db has been deferred */ dav_db *
db;
/* underlying database containing props */ short ns_count;
/* number of entries in table */ int *
ns_map;
/* map elem->ns to propdb ns values */ /* if we ever run a GET subreq, it will be stored here */ /* hooks we should use for processing (based on the target resource) */ /* ### move these into a "core" liveprop provider? */ ** This structure is used to track information needed for a rollback. ** If a SET was performed and no prior value existed, then value.dptr /* or use the following (choice selected by dav_prop_ctx.is_liveprop) */ /* didn't find it. fall thru. a provider can define DAV: props */ /* policy: liveprop providers cannot define no-namespace properties */ /* these are defined as read-only */ ** Check the liveprop providers ** No provider recognized the property, so it must be dead (and writable) /* do a sub-request to fetch properties for the target resource's URI. */ /* perform a "GET" on the resource's URI (note that the resource may not correspond to the current request!). */ /* fast-path the common case */ value =
"<D:collection/>";
/* ### should we denote lock-null resources? */ value =
"";
/* becomes: <D:resourcetype/> */ value =
"<D:workspace/>";
value =
"<D:configuration/>";
"DAV:lockdiscovery could not be " "determined due to a problem fetching " "the locks for this resource.",
/* fast-path the no-locks case */ ** This may modify the buffer. value may point to ** wb_lock.pbuf or a string constant. /* make a copy to isolate it from changes to wb_lock */ "Content-Language")) !=
NULL) {
/* fall through to interpret as a dead property */ /* if something was supplied, then insert it */ /* use D: prefix to refer to the DAV: namespace URI */ /* use D: prefix to refer to the DAV: namespace URI */ /* ask the provider (that defined this prop) to insert the prop */ /* ### the provider should have returned NOTDEF, at least */ "DESIGN ERROR: a liveprop provider defined " "a property, but did not respond to the " "insert_prop hook for it.");
/* skip past the xml:lang value */ /* the property is an empty value */ /* "no namespace" case */ else if (*
lang !=
'\0') {
/* "no namespace" case */ /* "no namespace" case */ ** Prepare the ns_map variable in the propdb structure. This entails copying ** all URIs from the "input" namespace list (in propdb->ns_xlate) into the ** propdb's list of namespaces. As each URI is copied (or pre-existing ** URI looked up), the index mapping is stored into the ns_map variable. ** Note: we must copy all declared namespaces because we cannot easily ** determine which input namespaces were actually used within the property ** values that are being stored within the propdb. Theoretically, we can ** determine this at the point where we serialize the property values ** back into strings. This would require a bit more work, and will be ** left to future optimizations. ** ### we should always initialize the propdb namespace array with "DAV:" ** ### since we know it will be entered anyhow (by virtue of it always ** ### occurring in the ns_xlate array). That will allow us to use ** ### DAV_NS_DAV_ID for propdb ns values, too. int updating = 0;
/* are we updating an existing ns_map? */ /* we must revisit the map and insert new entries */ /* nothing to do: we have a proper ns_map */ /* ### stupid O(n * orig_count) algorithm */ /* updating an existing mapping... we can skip a lot of stuff */ /* This entry has been filled in, so we can skip it */ ** GIVEN: uri (a namespace URI from the request input) ** FIND: an equivalent URI in the propdb namespace table /* only scan original entries (we may have added some in here) */ ** This flag indicates that we have an ns_map with missing ** entries. If dav_prep_ns_map() is called with add_ns==1 AND ** this flag is set, then we zip thru the array and add those ** URIs (effectively updating the ns_map as if add_ns=1 was ** passed when the initial prep was called). ** The input URI was not found in the propdb namespace table, and ** we are supposed to add it. Append it to the table and store ** the index into the ns_map. /* find the "DAV:" namespace in our table and return its ID. */ /* the "DAV:" namespace is not present */ /* return all known namespaces (in this propdb) */ /* note: ns_count == 0 when we have no propdb file */ /* add a namespace decl from one of the namespace tables */ ** 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 * 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". * Note that we prep the map and do NOT add namespaces. If that * is required, then the caller should have called prep * beforehand, passing the correct values. /* build the database key */ /* we're trying to open the db; turn off the 'deferred' flag */ /* ask the DB provider to open the thing */ "Could not open the property database.",
** 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. /* ### push a higher-level description? */ * 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. /* call it a major version error */ "Prop database has the wrong major " "version number and cannot be used.");
/* initialize a new metadata structure */ "Prop database has the wrong major " "version number and cannot be used.");
"INTERNAL DESIGN ERROR: resource must define " /* ### what to do about closing the propdb on server failure? */ /* fill in the metadata that we store into the prop db. */ /* ### what to do with the error? */ /* generate all the namespaces that are in the propdb */ /* initialize the result with some start tags... */ /* if there ARE properties, then scan them */ /* any keys with leading capital letters should be skipped (real keys start with a number or a colon) */ ** See if this is the <DAV:resourcetype> property. We need to ** know whether it was found (and therefore, whether to supply ** 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). else if (
colon[
1] ==
'g') {
else if (
strcmp(
colon +
1,
"getcontentlanguage") == 0) {
/* ### anything better to do? */ /* ### probably should enter a 500 error */ /* put the prop name and value into the result */ /* simple, empty element if a value isn't needed */ /* add namespaces for all the liveprop providers */ /* ask the liveprop providers to insert their properties */ /* insert the standard properties */ /* ### should be handling the return errors here */ /* if the resourcetype wasn't stored, then prepare one */ /* ### should be handling the return error here */ /* if we didn't find these, then do the whole subreq thing. */ /* ### should be handling the return error here */ /* ### should be handling the return error here */ /* terminate the result */ "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR /* ### NOTE: we should pass in TWO buffers -- one for keys, one for /* we will ALWAYS provide a "good" result, even if it is EMPTY */ /* generate all the namespaces that are in the propdb */ /* ### the marks should be in a buffer! */ /* allocate zeroed-memory for the marks. These marks indicate which input namespaces we've generated into the output xmlns buffer */ /* same for the liveprops */ ** Note: the key may be NULL if we have no properties that are in ** a namespace that matches the requested prop's namespace. /* fetch IF we have a db and a key. otherwise, value is NULL */ ** If we did not find the property in the database, then it may ** be a liveprop that we can handle specially. /* cache the propid; dav_get_props() could be called many times */ /* insert the property. returns 1 if an insertion was done. */ /* ### need to propagate the error to the caller... */ /* ### skip it for now, as if nothing was inserted */ ** Add the liveprop's namespace URIs. Note that provider==NULL /* not found. add a record to the "bad" propstats */ /* make sure we've started our "bad" propstat */ /* note: key.dptr may be NULL if the propdb doesn't have an equivalent namespace stored */ * elem has a prefix already (xml...:name) or the elem * simply has no namespace. /* ensure that an xmlns is generated for the /* add in the bad prop using our namespace decl */ /* found it. put the value into the "good" propstats */ "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR /* default to start with the good */ /* we may not have any "bad" results */ "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR /* if there are no good props, then just return the bad */ /* hook the bad propstat to the end of the good one */ ** 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 /* 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 */ "Property is read-only.");
/* clear is_liveprop -- act as a dead prop now */ ** The property is supposed to be stored into the dead-property ** database. Make sure the thing is truly open (and writeable). ** There should be an open, writable database in here! ** Note: the database would be NULL if it was opened readonly and it ** Prep the element => propdb namespace index mapping, inserting ** namespace URIs into the propdb that don't exist. ** 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 ** Note that if a property does not exist, that does not rule out ** that a SET will occur during this PROPPATCH (thusly creating it). /* we're going to need the key for all operations */ /* save the old value so that we can do a rollback. */ /* Note: propdb->ns_map was set in dav_prop_validate() */ /* quote all the values in the element */ /* generate a text blob for the xml:lang plus the contents */ ** 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. ** Delete the property. Ignore errors -- the property is there, or ** we are deleting it for a second time. /* ### but what about other errors? */ /* push a more specific error here */ ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen ** any errors at this point. "Could not execute PROPPATCH.",
err);
** Note that a commit implies ctx->err is NULL. The caller should assume ** a status of HTTP_OK for this case. /* do nothing if there is no rollback information. */ ** ### 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. /* don't fail if the thing isn't really there */ /* ### but what about other errors? */ /* hook previous errors at the end of the rollback error */