diff --git a/lib/dav4rack/controller.rb b/lib/dav4rack/controller.rb index f75086dd..da77a260 100644 --- a/lib/dav4rack/controller.rb +++ b/lib/dav4rack/controller.rb @@ -102,12 +102,14 @@ module DAV4Rack # Return response to HEAD def head if(resource.exist?) - response['Etag'] = resource.etag - response['Content-Type'] = resource.content_type - response['Content-Length'] = resource.content_length.to_s - response['Last-Modified'] = resource.last_modified.httpdate - resource.head(request, response) - OK + res = resource.head(request, response) + if(res == OK) + response['Etag'] ||= resource.etag + response['Content-Type'] ||= resource.content_type + response['Content-Length'] ||= resource.content_length.to_s + response['Last-Modified'] ||= resource.last_modified.httpdate + end + res else NotFound end @@ -197,7 +199,8 @@ module DAV4Rack return BadRequest unless request.depth == :infinity return BadRequest unless dest = request.destination - if status = dest.validate + if status = dest.validate(host: request.host, + resource_path: resource.path) return status end diff --git a/lib/dav4rack/destination_header.rb b/lib/dav4rack/destination_header.rb index fe43e7e1..bdaae6ec 100644 --- a/lib/dav4rack/destination_header.rb +++ b/lib/dav4rack/destination_header.rb @@ -5,24 +5,13 @@ module DAV4Rack attr_reader :host, :path, :path_info - def initialize(value, script_name: nil) - @script_name = script_name.to_s - @value = value.to_s.strip - parse - end - - def parse - uri = Addressable::URI.parse @value - + # uri is expected to be a DAV4Rack::Uri instance + def initialize(uri) @host = uri.host - @path = Addressable::URI.unencode uri.path - - if @script_name - if @path =~ /\A(?#{Regexp.escape @script_name}(?\/.*))\z/ - @path_info = $~[:path_info] - else - raise ArgumentError, 'invalid destination header value' - end + @path = uri.path + unless @path_info = uri.path_info + # nil path info means path is outside the realm of script_name + raise ArgumentError, "invalid destination header value: #{uri.to_s}" end end @@ -30,7 +19,7 @@ module DAV4Rack def validate(host: nil, resource_path: nil) if host and self.host and self.host != host DAV4Rack::HTTPStatus::BadGateway - elsif self.path == resource_path + elsif resource_path and self.path_info == resource_path DAV4Rack::HTTPStatus::Forbidden end end diff --git a/lib/dav4rack/request.rb b/lib/dav4rack/request.rb index 7851988b..3a867db7 100644 --- a/lib/dav4rack/request.rb +++ b/lib/dav4rack/request.rb @@ -3,6 +3,7 @@ require 'uri' require 'addressable/uri' require 'dav4rack/logger' +require 'dav4rack/uri' module DAV4Rack class Request < Rack::Request @@ -84,7 +85,7 @@ module DAV4Rack # Destination header def destination @destination ||= if h = get_header('HTTP_DESTINATION') - DestinationHeader.new h, script_name: script_name + DestinationHeader.new DAV4Rack::Uri.new(h, script_name: script_name) end end @@ -123,6 +124,13 @@ module DAV4Rack "#{script_name}#{expand_path path}" end + # returns the given path, but with the leading script_name removed. Will + # return nil if the path does not begin with the script_name + def path_info_for(full_path, script_name: self.script_name) + uri = DAV4Rack::Uri.new full_path, script_name: script_name + return uri.path_info + end + # expands '/foo/../bar' to '/bar' def expand_path(path) path.squeeze! '/' diff --git a/lib/dav4rack/resource.rb b/lib/dav4rack/resource.rb index 2981e852..6eb62d3e 100644 --- a/lib/dav4rack/resource.rb +++ b/lib/dav4rack/resource.rb @@ -208,8 +208,12 @@ module DAV4Rack NotImplemented end + # HTTP HEAD request. + # + # Like GET, but without content. Override if you set custom headers in GET + # to set them here as well. def head(request, response) - #no-op, but called by the controller + OK end # HTTP PUT request. diff --git a/lib/dav4rack/uri.rb b/lib/dav4rack/uri.rb new file mode 100644 index 00000000..20a5dbcf --- /dev/null +++ b/lib/dav4rack/uri.rb @@ -0,0 +1,42 @@ +require 'addressable/uri' + +module DAV4Rack + + # adds a bit of parsing logic around a header URI or path value + class Uri + + attr_reader :host, :path, :path_info, :script_name + + def initialize(uri_or_path, script_name: nil) + # more than one leading slash confuses Addressable::URI, resulting e.g. + # with //remote.php/dav/files in a path of /dav/files with a host + # remote.php. + @uri_or_path = uri_or_path.to_s.strip.sub %r{\A/+}, '/' + + @script_name = script_name + parse + end + + def to_s + @uri_or_path + end + + private + + def parse + uri = Addressable::URI.parse @uri_or_path + + @host = uri.host + @path = Addressable::URI.unencode uri.path + + if @script_name + if @path =~ /\A(?#{Regexp.escape @script_name}(?\/.*))\z/ + @path_info = $~[:path_info] + end + else + @path_info = @path + end + end + + end +end diff --git a/lib/dav4rack/version.rb b/lib/dav4rack/version.rb index 221a3e9a..d386f6c5 100644 --- a/lib/dav4rack/version.rb +++ b/lib/dav4rack/version.rb @@ -13,5 +13,5 @@ module DAV4Rack end end - VERSION = Version.new('1.0.0') + VERSION = Version.new('1.1.0') end