From 6a9fa3236f983110eac0478a89f45da667872240 Mon Sep 17 00:00:00 2001 From: Daniel Munn Date: Wed, 27 Jun 2012 16:19:49 +0100 Subject: [PATCH] Fix: Webdav sometimes called without scope or type is actually a lock extension Fix: Controller did not properly search lock attributes because of gem bug --- lib/redmine_dmsf/lockable.rb | 17 ++++++++-- lib/redmine_dmsf/webdav/controller.rb | 27 ++++++++++++++++ lib/redmine_dmsf/webdav/dmsf_resource.rb | 40 +++++++++++++++++++++--- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/lib/redmine_dmsf/lockable.rb b/lib/redmine_dmsf/lockable.rb index 9a92354b..8f272f47 100644 --- a/lib/redmine_dmsf/lockable.rb +++ b/lib/redmine_dmsf/lockable.rb @@ -26,8 +26,21 @@ module RedmineDmsf existing = locks(false) raise DmsfLockError.new("Unable to complete lock - resource (or parent) is locked") if self.locked? && existing.empty? unless existing.empty? - if existing[0].scope == :scope_exclusive - raise DmsfLockError.new("Unable to complete lock - resource (or parent) is locked") + if existing[0].lock_scope == :scope_exclusive + # If it's an exclusive lock and you're re-requesting the actual desired behaviour is to not return a new lock, + # but the same lock (extended) + if self.folder.locked? + raise DmsfLockError.new("Unable to complete lock - resource parent is locked") + else + if existing[0].user.id == User.current.id then + l = existing[0] + l.expires_at = expire + l.save! + return l + else + raise DmsfLockError.new("Unable to complete lock - resource is locked") + end + end else raise DmsfLockError.new("unable to exclusively lock a shared-locked resource") if scope == :scope_exclusive end diff --git a/lib/redmine_dmsf/webdav/controller.rb b/lib/redmine_dmsf/webdav/controller.rb index e497b29c..ce411fb6 100644 --- a/lib/redmine_dmsf/webdav/controller.rb +++ b/lib/redmine_dmsf/webdav/controller.rb @@ -29,6 +29,23 @@ module RedmineDmsf OK end + # This is just pain DIRTY + # to fix some gem bugs we're overriding their controller + def lock + begin + request.env['Timeout'] = request.env['HTTP_TIMEOUT'].split('-',2).join(',') unless request.env['HTTP_TIMEOUT'].nil? + rescue + #Nothing here + end + + request_document.remove_namespaces! if ns.empty? + # We re-imlement the function ns - if its return is empty, there are no usable namespaces + # so to prevent never returning data, we stip all namespaces + + super + end + + #Overload the default propfind function with this def propfind unless(resource.exist?) @@ -63,6 +80,16 @@ module RedmineDmsf end end + private + def ns(opt_head = '') + _ns = opt_head + if(request_document && request_document.root && request_document.root.namespace_definitions.size > 0) + _ns = request_document.root.namespace_definitions.first.prefix.to_s + _ns += ':' unless _ns.empty? + end + _ns.empty? ? opt_head : _ns + end + end end end diff --git a/lib/redmine_dmsf/webdav/dmsf_resource.rb b/lib/redmine_dmsf/webdav/dmsf_resource.rb index 6e1d58e7..606631a0 100644 --- a/lib/redmine_dmsf/webdav/dmsf_resource.rb +++ b/lib/redmine_dmsf/webdav/dmsf_resource.rb @@ -397,7 +397,7 @@ module RedmineDmsf # Lock def lock(args) - return Conflict unless (parent.projectless_path == "/" || parent_exists?) && !collection? + return Conflict unless (parent.projectless_path == "/" || parent_exists?) token = UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, projectless_path).to_s lock_check(args[:scope]) entity = file? ? file : folder @@ -405,10 +405,40 @@ module RedmineDmsf if (entity.locked? && entity.locked_for_user?) raise DAV4Rack::LockFailure.new("Failed to lock: #{@path}") else - entity.lock! + + # If scope and type are not defined, the only thing we can + # logically assume is that the lock is being refreshed (office loves + # to do this for example, so we do a few checks, try to find the lock + # and ultimately extend it, otherwise we return Conflict for any failure + if (!args[:scope] && !args[:type]) #Perhaps a lock refresh + http_if = request.env['HTTP_IF'] + + return Conflict if http_if.nil? + + http_if = http_if.slice(1, http_if.length - 2) + + return Conflict unless http_if == token + + entity.lock(false).each {|l| + if l.user.id == User.current.id + l.expires_at = Time.now + 1.hour + l.save! + @response['Lock-Token'] = token + return [1.hours.to_i, token] + end + } + + #Unfortunately if we're here, then it's updating a lock we can't find + + return Conflict + end + + scope = "scope_#{(args[:scope] || "exclusive")}".to_sym + type = "type_#{(args[:type] || "write")}".to_sym + + entity.lock! scope, type, Time.now + 1.hours @response['Lock-Token'] = token - Locked - [8600, token] + [1.hours.to_i, token] end rescue DmsfLockError raise DAV4Rack::LockFailure.new("Failed to lock: #{@path}") @@ -433,7 +463,7 @@ module RedmineDmsf entity.unlock! NoContent end - resue + rescue Forbidden end end