/* ** 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_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_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; }