/* ### 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; }
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_generic_really_open_lockdb: * * If the database hasn't been opened yet, then open the thing. */ static dav_error * dav_generic_really_open_lockdb(dav_lockdb *lockdb) { dav_error *err; apr_status_t status; if (lockdb->info->opened) { return NULL; } status = apr_dbm_open(&lockdb->info->db, lockdb->info->lockdb_path, lockdb->ro ? APR_DBM_READONLY : APR_DBM_RWCREATE, APR_OS_DEFAULT, lockdb->info->pool); if (status) { err = dav_generic_dbm_new_error(lockdb->info->db, lockdb->info->pool, status); 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_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; }
/* ** 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_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; }
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); } }
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; }
/* ** 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, 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_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; const dav_hooks_acl *acl_hooks = dav_get_acl_hooks(ctx->r); dav_prop_name name; ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback)); 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; if (ctx->is_liveprop) { err = (*priv->provider->patch_exec)(propdb->resource, ctx->prop, ctx->operation, ctx->liveprop_ctx, &ctx->rollback->liveprop); } else { /* 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); } } if(acl_hooks) { /* update any DAV:property ACE(s) that depend on this property */ const char *newval = dav_xml_get_cdata(dav_find_child(ctx->prop, "href"), propdb->p, 1); /* @NOTE: works only in presence of transactions, * currently no rollback state being stored for ACE changes */ /* assuming cdata is NULL for D:remove prop elem */ if(newval) (*acl_hooks->update_principal_property_aces)( (dav_resource *)propdb->resource, &name, newval); } error: /* push a more specific error here */ if (err != NULL) { if (err->status == HTTP_CONFLICT) { /* semantics of the value are not appropriate for the property */ ctx->err = err; } else { /* ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen ** any other errors at this point. */ ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, DAV_ERR_PROP_EXEC, "Could not execute PROPPATCH.", err); } } }