Skip to content

Commit

Permalink
Reorganize. Start getting proper locking bits enabled.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisroberts committed Sep 5, 2012
1 parent 380dad2 commit a7d13dc
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 11 deletions.
4 changes: 4 additions & 0 deletions lib/dav4rack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
require 'dav4rack/resource'
require 'dav4rack/handler'
require 'dav4rack/controller'

module DAV4Rack
IS_18 = RUBY_VERSION[0,3] == '1.8'
end
10 changes: 5 additions & 5 deletions lib/dav4rack/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def put
elsif(!resource.parent_exists? || !resource.parent.collection?)
Conflict
else
resource.lock_check
resource.lock_check if resource.supports_locking?
status = resource.put(request, response)
response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created
response.body = response['Location']
Expand All @@ -96,7 +96,7 @@ def post
# Return response to DELETE
def delete
if(resource.exist?)
resource.lock_check
resource.lock_check if resource.supports_locking?
resource.delete
else
NotFound
Expand All @@ -105,7 +105,7 @@ def delete

# Return response to MKCOL
def mkcol
resource.lock_check
resource.lock_check if resource.supports_locking?
status = resource.make_collection
gen_url = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created
if(resource.use_compat_mkcol_response?)
Expand Down Expand Up @@ -133,7 +133,7 @@ def move(*args)
unless(resource.exist?)
NotFound
else
resource.lock_check unless args.include?(:copy)
resource.lock_check if resource.supports_locking? && !args.include(:copy)
destination = url_unescape(env['HTTP_DESTINATION'].sub(%r{https?://([^/]+)}, ''))
dest_host = $1
if(dest_host && dest_host.gsub(/:\d{2,5}$/, '') != request.host)
Expand Down Expand Up @@ -211,7 +211,7 @@ def proppatch
unless(resource.exist?)
NotFound
else
resource.lock_check
resource.lock_check if resource.supports_locking?
prop_actions = []
request_document.xpath("/#{ns}propertyupdate").children.each do |element|
case element.name
Expand Down
143 changes: 143 additions & 0 deletions lib/dav4rack/file_resource_lock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
require 'pstore'

module DAV4Rack
class FileResourceLock
attr_accessor :path
attr_accessor :token
attr_accessor :timeout
attr_accessor :depth
attr_reader :created_at
attr_reader :owner
attr_reader :root

class << self
def explicitly_locked?(path, croot=nil)
store = init_pstore(croot)
!!store.transaction(true){
store[:paths][path]
}
end

def implicilty_locked?(path, croot=nil)
store = init_pstore(croot)
!!store.transaction(true){
store[:paths].keys.detect do |check|
check.start_with?(path)
end
}
end

def find_by_path(path, croot=nil)
lock = self.class.new(:path => path, :root => croot)
lock.token.nil? ? nil : lock
end

def find_by_token(token, croot=nil)
store = init_pstore(croot)
struct = store.transaction(true){
store[:tokens][token]
}
if(tok)
self.class.new(:path => struct[:path], :root => croot)
else
nil
end
end

def generate(path, token)
lock = self.new
lock.path = path
lock.token = token
lock.save
lock
end

def root=(path)
@root = path
end

def root
@root || '/tmp/dav4rack'
end

def init_pstore(root)
path = File.join(root, '.attribs', 'locks.pstore')
FileUtils.mkdir_p(File.dirname(path)) unless File.directory?(File.dirname(path))
store = IS_18 ? PStore.new(path) : PStore.new(path, true)
store.transaction do
store[:paths] = {}
store[:tokens] = {}
store.commit
end
end
end

def initialize(args={})
@store = init_pstore(args[:root])
@max_timeout = args[:max_timeout] || 86400
@default_timeout = args[:max_timeout] || 60
@path = args[:path]
@root = args[:root]
@owner = args[:owner]
load_if_exists!
@new_record = true if token.nil?
end

def owner?(user)
user == owner
end

def reload
load_if_exists
self
end

def remaining_timeout
t = timeout.to_i - (Time.now.to_i - created_at.to_i)
t < 0 ? 0 : t
end

def save
struct = {
:path => path,
:token => token,
:timeout => timeout,
:depth => depth,
:created_at => Time.now,
:owner => owner
}
@store.transaction do
@store[:paths][path] = struct
@store[:tokens][token] = struct
@store.commit
end
@new_record = false
self
end

def destroy
@store.transaction do
@store[:paths].delete(path)
@store[:tokens].delete(token)
@store.commit
end
nil
end

private

def load_if_exists!
struct = @store.transaction do
@store[:paths][path]
end
@path = struct[:path]
@token = struct[:token]
@timeout = struct[:timeout]
@depth = struct[:depth]
@created_at = struct[:created_at]
@owner = struct[:owner]
self
end

end
end
2 changes: 1 addition & 1 deletion lib/dav4rack/handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Handler
def initialize(options={})
@options = options.dup
unless(@options[:resource_class])
require 'dav4rack/file_resource'
require 'dav4rack/resources/file_resource'
@options[:resource_class] = FileResource
@options[:root] ||= Dir.pwd
end
Expand Down
7 changes: 6 additions & 1 deletion lib/dav4rack/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ def method_missing(*args)
@runner.call(class_sym, :after, orig)
result
end


# Returns if resource supports locking
def supports_locking?
false #true
end

# If this is a collection, return the child resources.
def children
NotImplemented
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ module DAV4Rack

class FileResource < Resource

IS_18 = RUBY_VERSION[0,3] == '1.8'

include WEBrick::HTTPUtils
include DAV4Rack::Utils

# If this is a collection, return the child resources.
def children
Dir[file_path + '/*'].map do |path|
Expand Down Expand Up @@ -223,8 +221,82 @@ def remove_property(element)
val = prop_hash.transaction{ prop_hash[to_element_key(element)] }
end

def lock(args)
unless(parent_exists?)
Conflict
else
lock_check(args[:type])
lock = FileResourceLock.explicit_locks(@path).find(:first, :conditions => ["scope = ? AND kind = ? AND user_id = ?", args[:scope], args[:type], @user.id])
unless(lock)
token = UUIDTools::UUID.random_create.to_s
lock = FileResourceLock.generate(@path, @user, token)
lock.scope = args[:scope]
lock.kind = args[:type]
lock.owner = args[:owner]
lock.depth = args[:depth]
if(args[:timeout])
lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout
else
lock.timeout = @default_timeout
end
lock.save
end
begin
lock_check(args[:type])
rescue DAV4Rack::LockFailure => lock_failure
lock.destroy
raise lock_failure
rescue HTTPStatus::Status => status
status
end
[lock.remaining_timeout, lock.token]
end
end

def unlock(token)
token = token.slice(1, token.length - 2)
if(token.nil? || token.empty?)
BadRequest
else
lock = FileResourceLock.find_by_token(token)
if(lock.nil? || lock.user_id != @user.id)
Forbidden
elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/)
Conflict
else
lock.destroy
NoContent
end
end
end

protected

def lock_check(lock_type=nil)
if(FileResourceLock.explicitly_locked?(@path))
raise Locked if lock_type && lock_type == 'exclusive'
raise Locked if FileResourceLock.explicit_locks(@path).find(:all, :conditions => ["scope = 'exclusive' AND user_id != ?", @user.id]).size > 0
elsif(FileResouceLock.implicitly_locked?(@path))
if(lock_type.to_s == 'exclusive')
locks = FileResouceLock.implicit_locks(@path)
failure = DAV4Rack::LockFailure.new("Failed to lock: #{@path}")
locks.each do |lock|
failure.add_failure(@path, Locked)
end
raise failure
else
locks = FileResourceLock.implict_locks(@path).find(:all, :conditions => ["scope = 'exclusive' AND user_id != ?", @user.id])
if(locks.size > 0)
failure = LockFailure.new("Failed to lock: #{@path}")
locks.each do |lock|
failure.add_failure(@path, Locked)
end
raise failure
end
end
end
end

def set_custom_props(element, val)
prop_hash.transaction do
prop_hash[to_element_key(element)] = val
Expand Down
2 changes: 1 addition & 1 deletion spec/mongo_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require 'dav4rack'
require 'fileutils'
require 'nokogiri'
require 'dav4rack/mongo_resource'
require 'dav4rack/resources/mongo_resource'
require 'mongo'
require 'mongoid'
require 'rspec'
Expand Down

0 comments on commit a7d13dc

Please sign in to comment.