Roxen.git
/
server
/
base_server
/
module.pike
version
»
Context lines:
10
20
40
80
file
none
3
Roxen.git/server/base_server/module.pike:889:
//! any above @[path]. (This is appropriate to use to get the //! list of locks that need to be unlocked on DELETE.) //! //! @param exclude_shared //! If @expr{1@} do not return shared locks that are held by users //! other than the one the request is authenticated as. (This is //! appropriate to get the list of locks that would conflict if the //! current user were to make a shared lock.) //! //! @returns
-
//! Returns a
multiset
containing all applicable locks in
+
//! Returns a
mapping
containing all applicable locks in
//! this location module, or @expr{0@} (zero) if there are none. //! //! @note //! @[DAVLock] objects may be created if the filesystem has some //! persistent storage of them. The default implementation does not //! store locks persistently. //! //! @note //! The default implementation only handles the @expr{"DAV:write"@} //! lock type.
-
multiset
(DAVLock) find_locks(string path,
+
mapping
(
string:
DAVLock) find_locks(string path,
int(-1..1) recursive, int(0..1) exclude_shared, RequestID id) { // Common case. if (!sizeof(file_locks) && !sizeof(prefix_locks)) return 0; TRACE_ENTER(sprintf("find_locks(%O, %O, %O, X)", path, recursive, exclude_shared), this); string rsc = resource_id (path, id);
-
multiset
(DAVLock) locks = (
<>
);
+
mapping
(
string:
DAVLock) locks = (
[]
);
function(mapping(mixed:DAVLock):void) add_locks; if (exclude_shared) { mixed auth_user = authenticated_user_id (path, id); add_locks = lambda (mapping(mixed:DAVLock) sub_locks) { foreach (sub_locks; string user; DAVLock lock) if (user == auth_user || lock->lockscope == "DAV:exclusive")
-
locks[lock] =
1
;
+
locks[lock
->locktoken
] =
lock
;
}; } else add_locks = lambda (mapping(mixed:DAVLock) sub_locks) {
-
locks |=
mkmultiset
(values (sub_locks));
+
locks |=
mkmapping
(values
(sub_locks)->locktoken,
+
values
(sub_locks));
}; if (file_locks[rsc]) { add_locks (file_locks[rsc]); } if (recursive >= 0) { foreach(prefix_locks; string prefix; mapping(mixed:DAVLock) sub_locks) { if (has_prefix(rsc, prefix)) {
Roxen.git/server/base_server/module.pike:999:
TRACE_ENTER(sprintf("Unlocking %d locks for path %O...", sizeof(sub_locks), prefix), this); m_delete(prefix_locks, prefix); TRACE_LEAVE(""); } } TRACE_LEAVE("Done."); }
-
//! Check if there are one or more locks that apply to @[path] for the
-
//! user the request is authenticated as.
-
//!
-
//! WARNING: This function has some design issues and will very likely
-
//! get a different interface. Compatibility is NOT guaranteed.
-
//!
-
//! @param path
-
//! Normalized path below the filesystem location.
-
//!
-
//! @param recursive
-
//! If @expr{1@} also check recursively under @[path] for locks.
-
//!
-
//! @returns
-
//! The valid return values are:
-
//! @mixed
-
//! @type DAVLock
-
//! The lock owned by the authenticated user that apply to
-
//! @[path]. (It doesn't matter if the @expr{recursive@} flag in
-
//! the lock doesn't match the @[recursive] argument.)
-
//! @type LockFlag
-
//! @int
-
//! @value LOCK_NONE
-
//! No locks apply. (0)
-
//! @value LOCK_SHARED_BELOW
-
//! There are only one or more shared locks held by other
-
//! users somewhere below @[path] (but not on @[path]
-
//! itself). Only returned if @[recursive] is set. (2)
-
//! @value LOCK_SHARED_AT
-
//! There are only one or more shared locks held by other
-
//! users on @[path]. (3)
-
//! @value LOCK_OWN_BELOW
-
//! The authenticated user has locks under @[path] (but not
-
//! on @[path] itself) and there are no exclusive locks held
-
//! by other users. Only returned if @[recursive] is set. (4)
-
//! @value LOCK_EXCL_BELOW
-
//! There are one or more exclusive locks held by other
-
//! users somewhere below @[path] (but not on @[path]
-
//! itself). Only returned if @[recursive] is set. (6)
-
//! @value LOCK_EXCL_AT
-
//! There are one or more exclusive locks held by other
-
//! users on @[path]. (7)
-
//! @endint
-
//! Note that the lowest bit is set for all flags that apply to
-
//! @[path] itself.
-
//! @endmixed
-
//!
-
//! @note
-
//! @[DAVLock] objects may be created if the filesystem has some
-
//! persistent storage of them. The default implementation does not
-
//! store locks persistently.
-
//!
-
//! @note
-
//! The default implementation only handles the @expr{"DAV:write"@}
-
//! lock type.
-
DAVLock|LockFlag check_locks(string path,
-
int(0..1) recursive,
-
RequestID id)
-
{
-
TRACE_ENTER(sprintf("check_locks(%O, %d, X)", path, recursive), this);
-
-
// Common case.
-
if (!sizeof(file_locks) && !sizeof(prefix_locks)) {
-
TRACE_LEAVE ("Got no locks");
-
return 0;
-
}
-
-
mixed auth_user = authenticated_user_id (path, id);
-
path = resource_id (path, id);
-
-
if (DAVLock lock =
-
file_locks[path] && file_locks[path][auth_user] ||
-
prefix_locks[path] && prefix_locks[path][auth_user]) {
-
TRACE_LEAVE(sprintf("Found own lock %O.", lock->locktoken));
-
return lock;
-
}
-
-
LockFlag shared;
-
-
if (mapping(mixed:DAVLock) locks = file_locks[path]) {
-
foreach(locks;; DAVLock lock) {
-
if (lock->lockscope == "DAV:exclusive") {
-
TRACE_LEAVE(sprintf("Found other user's exclusive lock %O.",
-
lock->locktoken));
-
return LOCK_EXCL_AT;
-
}
-
shared = LOCK_SHARED_AT;
-
break;
-
}
-
}
-
-
foreach(prefix_locks;
-
string prefix; mapping(mixed:DAVLock) locks) {
-
if (has_prefix(path, prefix)) {
-
if (DAVLock lock = locks[auth_user]) {
-
SIMPLE_TRACE_LEAVE ("Found own lock %O on %O.", lock->locktoken, prefix);
-
return lock;
-
}
-
if (!shared)
-
// If we've found a shared lock then we won't find an
-
// exclusive one anywhere else.
-
foreach(locks;; DAVLock lock) {
-
if (lock->lockscope == "DAV:exclusive") {
-
TRACE_LEAVE(sprintf("Found other user's exclusive lock %O.",
-
lock->locktoken));
-
return LOCK_EXCL_AT;
-
}
-
shared = LOCK_SHARED_AT;
-
break;
-
}
-
}
-
}
-
-
if (!recursive) {
-
SIMPLE_TRACE_LEAVE("Returning %O.", shared);
-
return shared;
-
}
-
-
int(0..1) locked_by_auth_user;
-
-
// We want to know if there are any locks with @[path] as prefix
-
// that apply to us.
-
LOOP_OVER_BOTH (string prefix, mapping(mixed:DAVLock) locks, {
-
if (has_prefix(prefix, path)) {
-
if (locks[auth_user])
-
locked_by_auth_user = 1;
-
else
-
foreach(locks;; DAVLock lock) {
-
if (lock->lockscope == "DAV:exclusive") {
-
TRACE_LEAVE(sprintf("Found other user's exclusive lock %O.",
-
lock->locktoken));
-
return LOCK_EXCL_BELOW;
-
}
-
if (!shared) shared = LOCK_SHARED_BELOW;
-
break;
-
}
-
}
-
});
-
-
SIMPLE_TRACE_LEAVE("Returning %O.", locked_by_auth_user ? LOCK_OWN_BELOW : shared);
-
return locked_by_auth_user ? LOCK_OWN_BELOW : shared;
-
}
-
+
//! Register @[lock] on the path @[path] under the assumption that
-
//! there is no other lock already that conflicts with this one
, i
.
e.
-
//! that @expr{check_locks(path,lock->recursive,id)@} would return
-
//! @expr{LOCK_NONE@} if @expr{lock->lockscope@} is
-
//! @expr{"DAV:exclusive"@}, or @expr{< LOCK_OWN_BELOW@} if
-
//! @expr{lock->lockscope@} is @expr{"DAV:shared"@}.
+
//! there is no other lock already that conflicts with this one.
//! //! This function is only provided as a helper to call from //! @[lock_file] if the default lock implementation is to be used. //! //! @param path //! Normalized path (below the filesystem location) that the lock //! applies to. //! //! @param lock //! The lock to register.
Roxen.git/server/base_server/module.pike:1243:
} // NB: The lock may have already been removed in the !id case. ASSERT_IF_DEBUG (!(id || removed_lock) || (lock /*%O*/ == removed_lock /*%O*/), lock, removed_lock); TRACE_LEAVE("Ok."); return 0; } //! Register @[lock] on the path @[path] under the assumption that
-
//! there is no other lock already that conflicts with this one
, i
.
e.
-
//! that @expr{check_locks(path,lock->recursive,id)@} would return
-
//! @expr{LOCK_NONE@} if @expr{lock->lockscope@} is
-
//! @expr{"DAV:exclusive"@}, or @expr{<= LOCK_SHARED_AT@} if
-
//! @expr{lock->lockscope@} is @expr{"DAV:shared"@}.
+
//! there is no other lock already that conflicts with this one.
//! //! The implementation must at least support the @expr{"DAV:write"@} //! lock type (RFC 2518, section 7). Briefly: An exclusive lock on a //! file prohibits other users from changing its content. An exclusive //! lock on a directory (aka collection) prohibits other users from //! adding or removing files or directories in it. An exclusive lock //! on a file or directory prohibits other users from setting or //! deleting any of its properties. A shared lock prohibits users //! without locks to do any of this, and it prohibits other users from //! obtaining an exclusive lock. A resource that doesn't exist can be
Roxen.git/server/base_server/module.pike:1323:
//! @returns //! Returns a status mapping on any error, zero otherwise. //! //! @note //! To use the default lock implementation, call @[unregister_lock] //! from this function. mapping(string:mixed) unlock_file (string path, DAVLock lock, RequestID|int(0..0) id);
-
//! Checks that the conditions specified by the WebDAV @expr{"If"@}
-
//! header are fulfilled on the given path (RFC 2518 9.4). This means
-
//! that locks are checked as necessary using @[check_locks].
-
//!
-
//! WARNING: This function has some design issues and will very likely
-
//! get a different interface. Compatibility is NOT guaranteed.
-
//!
-
//! @param relative_path
-
//! Path (below the filesystem location) to check the if header for.
-
//!
-
//! @param recursive
-
//! If @expr{1@} also check write access recursively under @[path].
-
//!
-
//! @returns
-
//! Returns @expr{0@} (zero) on success, a status mapping on
-
//! failure, or @expr{1@} if @[recursive] is set and write access is
-
//! allowed on this level but maybe not somewhere below. The caller
-
//! should in the last case do the operation on this level if
-
//! possible and then handle each member in the directory
-
//! recursively with @[write_access] etc.
-
mapping(string:mixed)|int(0..1) check_if_header(string relative_path,
-
int(0..1) recursive,
-
RequestID id)
-
{
-
SIMPLE_TRACE_ENTER(this, "Checking \"If\" header for %O",
-
relative_path);
-
-
int/*LockFlag*/|DAVLock lock = check_locks(relative_path, recursive, id);
-
-
int(0..1) got_sublocks;
-
if (lock && intp(lock)) {
-
if (lock & 1) {
-
TRACE_LEAVE("Locked by other user.");
-
return Roxen.http_dav_error(Protocols.HTTP.DAV_LOCKED,
-
"lock-token-submitted");
-
}
-
else if (recursive)
-
// This is set for LOCK_OWN_BELOW too since it might be
-
// necessary to come back here and check the If header for
-
// those locks.
-
got_sublocks = 1;
-
}
-
-
string path = relative_path;
-
if (!has_suffix (path, "/")) path += "/"; // get_if_data always adds a "/".
-
path = query_location() + path; // No need for fancy combine_path stuff here.
-
-
mapping(string:array(array(array(string)))) if_data = id->get_if_data();
-
array(array(array(string))) condition;
-
if (!if_data || !sizeof(condition = if_data[path] || if_data[0])) {
-
if (lock) {
-
TRACE_LEAVE("Locked, no if header.");
-
return Roxen.http_dav_error(Protocols.HTTP.DAV_LOCKED,
-
"lock-token-submitted");
-
}
-
SIMPLE_TRACE_LEAVE("No lock and no if header - ok%s.",
-
got_sublocks ? " (this level only)" : "");
-
return got_sublocks; // No condition and no lock -- Ok.
-
}
-
-
string|int(-1..0) etag;
-
-
int(0..1) locked_fail = !!lock;
-
next_condition:
-
foreach(condition, array(array(string)) sub_cond) {
-
SIMPLE_TRACE_ENTER(this,
-
"Trying condition ( %{%s:%O %})...", sub_cond);
-
int negate;
-
int|DAVLock locked = lock;
-
foreach(sub_cond, array(string) token) {
-
switch(token[0]) {
-
case "not":
-
negate = !negate;
-
break;
-
case "etag":
-
if (!etag) {
-
// Get the etag for this resource (if any).
-
// FIXME: We only support straight strings as etag properties.
-
if (!stringp(etag = query_property(relative_path,
-
"DAV:getetag", id))) {
-
etag = -1;
-
}
-
}
-
if (etag != token[1]) {
-
// No etag available for this resource, or mismatch.
-
if (!negate) {
-
TRACE_LEAVE("Etag mismatch.");
-
continue next_condition;
-
}
-
} else if (negate) {
-
// Etag match with negated expression.
-
TRACE_LEAVE("Matched negated etag.");
-
continue next_condition;
-
}
-
negate = 0;
-
break;
-
case "key":
-
// The user has specified a key, so don't fail with DAV_LOCKED.
-
locked_fail = 0;
-
if (negate) {
-
if (objectp(lock) && lock->locktoken == token[1]) {
-
TRACE_LEAVE("Matched negated lock.");
-
continue next_condition; // Fail.
-
}
-
} else if (!objectp(lock) || lock->locktoken != token[1]) {
-
// Lock mismatch.
-
TRACE_LEAVE("Lock mismatch.");
-
continue next_condition; // Fail.
-
} else {
-
locked = 0;
-
}
-
negate = 0;
-
break;
-
}
-
}
-
if (!locked) {
-
TRACE_LEAVE("Found match.");
-
SIMPLE_TRACE_LEAVE("Ok%s.",
-
got_sublocks ? " (this level only)" : "");
-
return got_sublocks; // Found matching sub-condition.
-
}
-
SIMPLE_TRACE_LEAVE("Conditional ok, but still locked (locktoken: %O).",
-
lock->locktoken);
-
locked_fail = 1;
-
}
-
-
if (locked_fail) {
-
TRACE_LEAVE("Failed (locked).");
-
return Roxen.http_dav_error(Protocols.HTTP.DAV_LOCKED,
-
"lock-token-submitted");
-
}
-
-
TRACE_LEAVE("Precondition failed.");
-
return Roxen.http_status(Protocols.HTTP.HTTP_PRECOND_FAILED);
-
}
-
+
//! Used by some default implementations to check if we may perform a //! write access to @[path]. It should at least call
-
//! @[check_
if_header
] to check DAV locks. It takes the
same arguments
-
//! and has the same return value as that function.
+
//! @[
Configuration::
check_
locks()
] to check DAV locks. It takes the
+
//!
same arguments
and has the same return value as that function.
//! //! WARNING: This function has some design issues and will very likely //! get a different interface. Compatibility is NOT guaranteed. //! //! A filesystem module should typically put all needed write access //! checks here and then use this from @[find_file()], //! @[delete_file()] etc. //! //! @returns //! Returns @expr{0@} (zero) on success, a status mapping on //! failure, or @expr{1@} if @[recursive] is set and write access is //! allowed on this level but maybe not somewhere below. The caller //! should in the last case do the operation on this level if //! possible and then handle each member in the directory //! recursively with @[write_access] etc. protected mapping(string:mixed)|int(0..1) write_access(string relative_path, int(0..1) recursive, RequestID id) {
-
return
check_if_header
(relative_path, recursive, id);
+
string
path = query
_
location()
+
relative_path
;
+
return id->conf->check_locks(path
, recursive, id);
} //! protected variant mapping(string:mixed)|int(0..1) write_access(array(string) paths, int(0..1) recursive, RequestID id) { mapping(string:mixed)|int(0..1) ret;
-
int got_ok;
+
foreach(paths, string path) { ret = write_access(path, recursive, id);
-
if (
!ret) {
-
got_ok = 1;
-
continue;
-
}
-
if
(ret
== 1
)
{
-
continue;
-
}
-
if (ret->error == Protocols.HTTP.HTTP_PRECOND_FAILED
) {
-
continue;
-
}
+
if (
mappingp
(ret)) {
return ret; }
-
+
}
-
if (got_ok) {
-
//
The
if headers are valid for at least one
of the paths
,
-
// and none of the other paths
are locked.
+
//
None
of the paths are locked.
return 0; }
-
// HTTP_PRECOND_FAILED for all of the paths.
-
return ret;
-
}
-
+
mapping(string:mixed)|int(-1..0)|Stdio.File find_file(string path, RequestID id); //! Used by the default @[recurse_delete_files] implementation to //! delete a file or an empty directory. //! //! @returns //! Returns a 2xx series status mapping on success (typically 204 No //! Content). Returns 0 if the file doesn't exist. Returns an //! appropriate status mapping for any other error.
Roxen.git/server/base_server/module.pike:1568:
if (!stat) stat = id->get_multi_status()->prefix (id->url_base() + query_location()[1..]); Stat st = stat_file(path, id); if (!st) { SIMPLE_TRACE_LEAVE ("No such file or directory"); return 0; }
+
mapping(string:mixed) ret = write_access(path, 1, id);
+
if (ret) {
+
SIMPLE_TRACE_LEAVE("Write access denied: %O", ret);
+
return ret;
+
}
+
mapping(string:mixed) recurse (string path, Stat st) { // Note: Already got an extra TRACE_ENTER level on entry here. if (st->isdir) { // RFC 2518 8.6.2 // The DELETE operation on a collection MUST act as if a // "Depth: infinity" header was used on it. int fail; if (!has_suffix(path, "/")) path += "/";
Roxen.git/server/base_server/module.pike:2041:
Overwrite overwrite, RequestID id) { // Fall back to find_file(). RequestID tmp_id = id->clone_me(); tmp_id->not_query = query_location() + source; tmp_id->misc["new-uri"] = query_location() + destination; tmp_id->request_headers->destination = id->url_base() + query_location()[1..] + destination; tmp_id->method = "MOVE"; mapping(string:mixed) res = find_file(source, tmp_id);
-
if (!res || res->error != 501) return res;
+
if (!res || res->error != 501)
{
+
if (res && !sizeof(res)) {
+
foreach(tmp_id->get_multi_status()->get_responses_by_prefix("");
+
string href; MultiStatusNode status) {
+
id->add_status(href, status);
+
}
+
}
+
return res;
+
}
// Not implemented. Fall back to COPY + DELETE. string prefix = map(query_location()[1..]/"/", Roxen.http_encode_url)*"/"; MultiStatus.Prefixed result = id->get_multi_status()->prefix (id->url_base() + prefix); res = copy_collection(source, destination, behavior, overwrite, result, id); if (res && (res->error >= 300 || !sizeof(res))) { // Copy failed. return res; } int fail;