Roxen.git / server / base_server / module.pike

version» Context lines:

Roxen.git/server/base_server/module.pike:943:    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:1187:    }    // 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:1267:   //! @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.