props.c revision 96056e0b952146325e0e4fb1ac7dd77e3ee752d6
* 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * 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 * ==================================================================== * 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 extension module for Apache 2.0.* ** - Property database handling (repository-independent) ** 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 ** The namespace provides an integer index into the namespace table ** (see below). propname is simply the property name, without a namespace ** 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 ** 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. ** 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. ** 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. ** 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 ** 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 ** <propname1>value</propname1> ** <propname2>value</propname1> ** 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. ** 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 ** 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). ** 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 ** ==> we must devise a scheme where we can "garbage collect" the namespace ** entries from the property database. ** 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) /* the namespace URI was not found; no ID is available */ ** 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". ** 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) */ /* NOTE: dav_core_props[] and the following enum must stay in sync. */ /* ### 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) */ /* policy: liveprop providers cannot define no-namespace properties */ /* didn't find it. fall thru. a provider can define DAV: props */ /* is there a liveprop provider for this property? */ /* no provider for this property */ /* ### this test seems redundant... */ ** Check the liveprop provider (if this is a provider-defined prop) /* these are defined as read-only */ ** We don't recognize 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 */ "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, * and let the namespace attribute default to "DAV:" "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR,
/* use D: prefix to refer to the DAV: namespace URI */ /* use D: prefix to refer to the DAV: namespace URI */ /* this is a "core" property that we define */ /* ask the provider (that defined this prop) to insert the prop */ /* 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 ** ### AP_XML_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 " /* always defer actual open, to avoid expense of accessing db * when only live properties are involved /* ### 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? */ /* if not just getting supported live properties, * scan all properties in the dead prop database /* ### what to do with db open 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) */ ** 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 (
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 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 */ /* if not just reporting on supported live props, * 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 */ /* ### 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 */ ** First try live property providers; if they don't handle ** the property, then try looking it up in the propdb. /* 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 /* allow property to be handled as a dead property */ ** If not handled as a live property, look in the dead property /* make sure propdb is really open */ /* ### what to do with db open error? */ * generate all the namespaces that are in the propdb ** 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 */ /* 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 */ /* this is a "core" property that we define */ ** 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 writable). ** 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 */