diff --git a/Gemfile b/Gemfile index 24f21fd0..f75e15e1 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ gem 'rubyzip', '>= 1.0.0' gem 'zip-zip' # Just to avoid 'cannot load such file -- zip/zip' error gem 'simple_enum' gem 'uuidtools', '~> 2.1.1' +gem 'dav4rack', '=0.2.11' group :production do gem 'nokogiri', '>= 1.5.10' diff --git a/lib/redmine_dmsf/vendor/dav4rack.rb b/lib/redmine_dmsf/vendor/dav4rack.rb deleted file mode 100644 index c8f3b107..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'time' -require 'uri' -require 'nokogiri' - -require 'rack' -require 'dav4rack/http_status' -require 'dav4rack/resource' -require 'dav4rack/handler' -require 'dav4rack/controller' diff --git a/lib/redmine_dmsf/vendor/dav4rack/controller.rb b/lib/redmine_dmsf/vendor/dav4rack/controller.rb deleted file mode 100644 index f65cd0a7..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/controller.rb +++ /dev/null @@ -1,545 +0,0 @@ -require 'uri' - -module DAV4Rack - - class Controller - include DAV4Rack::HTTPStatus - - attr_reader :request, :response, :resource - - # request:: Rack::Request - # response:: Rack::Response - # options:: Options hash - # Create a new Controller. - # NOTE: options will be passed to Resource - def initialize(request, response, options={}) - raise Forbidden if request.path_info.include?('..') - @request = request - @response = response - @options = options - @resource = resource_class.new(actual_path, implied_path, @request, @response, @options) - end - - # s:: string - # Escape URL string - def url_format(resource) - ret = URI.escape(resource.public_path) - if resource.collection? and ret[-1,1] != '/' - ret += '/' - end - ret - end - - # s:: string - # Unescape URL string - def url_unescape(s) - URI.unescape(s) - end - - # Return response to OPTIONS - def options - response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' - response["Dav"] = "1, 2" - response["Ms-Author-Via"] = "DAV" - OK - end - - # Return response to HEAD - def head - if(resource.exist?) - response['Etag'] = resource.etag - response['Content-Type'] = resource.content_type - response['Last-Modified'] = resource.last_modified.httpdate - OK - else - NotFound - end - end - - # Return response to GET - def get - if(resource.exist?) - res = resource.get(request, response) - if(res == OK && !resource.collection?) - 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 - end - - # Return response to PUT - def put - if(resource.collection?) - Forbidden - elsif(!resource.parent_exists? || !resource.parent.collection?) - Conflict - else - resource.lock_check - status = resource.put(request, response) - response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created - response.body = response['Location'] - status - end - end - - # Return response to POST - def post - resource.post(request, response) - end - - # Return response to DELETE - def delete - if(resource.exist?) - resource.lock_check - resource.delete - else - NotFound - end - end - - # Return response to MKCOL - def mkcol - resource.lock_check - status = resource.make_collection - gen_url = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created - if(resource.use_compat_mkcol_response?) - multistatus do |xml| - xml.response do - xml.href gen_url - xml.status "#{http_version} #{status.status_line}" - end - end - else - response['Location'] = gen_url - status - end - end - - # Return response to COPY - def copy - move(:copy) - end - - # args:: Only argument used: :copy - # Move Resource to new location. If :copy is provided, - # Resource will be copied (implementation ease) - def move(*args) - unless(resource.exist?) - NotFound - else - resource.lock_check unless 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) - BadGateway - elsif(destination == resource.public_path) - Forbidden - else - collection = resource.collection? - dest = resource_class.new(destination, clean_path(destination), @request, @response, @options.merge(:user => resource.user)) - status = nil - if(args.include?(:copy)) - status = resource.copy(dest, overwrite) - else - return Conflict unless depth.is_a?(Symbol) || depth > 1 - status = resource.move(dest, overwrite) - end - response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(dest)}" if status == Created - # RFC 2518 - if collection - multistatus do |xml| - xml.response do - xml.href "#{scheme}://#{host}:#{port}#{url_format(status == Created ? dest : resource)}" - xml.status "#{http_version} #{status.status_line}" - end - end - else - status - end - end - end - end - - # Return respoonse to PROPFIND - def propfind - unless(resource.exist?) - NotFound - else - unless(request_document.xpath("//#{ns}propfind/#{ns}allprop").empty?) - names = resource.property_names - else - names = ( - ns.empty? ? request_document.remove_namespaces! : request_document - ).xpath( - "//#{ns}propfind/#{ns}prop" - ).children.find_all{ |item| - item.element? && item.name.start_with?(ns) - }.map{ |item| - item.name.sub("#{ns}::", '') - } - raise BadRequest if names.empty? - names = resource.property_names if names.empty? - end - multistatus do |xml| - find_resources.each do |resource| - xml.response do - unless(resource.propstat_relative_path) - xml.href "#{scheme}://#{host}:#{port}#{url_format(resource)}" - else - xml.href url_format(resource) - end - propstats(xml, get_properties(resource, names)) - end - end - end - end - end - - # Return response to PROPPATCH - def proppatch - unless(resource.exist?) - NotFound - else - resource.lock_check - prop_rem = request_match('/propertyupdate/remove/prop').children.map{|n| [n.name] } - prop_set = request_match('/propertyupdate/set/prop').children.map{|n| [n.name, n.text] } - multistatus do |xml| - find_resources.each do |resource| - xml.response do - xml.href "#{scheme}://#{host}:#{port}#{url_format(resource)}" - propstats(xml, set_properties(resource, prop_set)) - end - end - end - end - end - - - # Lock current resource - # NOTE: This will pass an argument hash to Resource#lock and - # wait for a success/failure response. - def lock - lockinfo = request_document.xpath("//#{ns}lockinfo") - asked = {} - asked[:timeout] = request.env['Timeout'].split(',').map{|x|x.strip} if request.env['Timeout'] - asked[:depth] = depth - unless([0, :infinity].include?(asked[:depth])) - BadRequest - else - asked[:scope] = lockinfo.xpath("//#{ns}lockscope").children.find_all{|n|n.element?}.map{|n|n.name}.first - asked[:type] = lockinfo.xpath("#{ns}locktype").children.find_all{|n|n.element?}.map{|n|n.name}.first - asked[:owner] = lockinfo.xpath("//#{ns}owner/#{ns}href").children.map{|n|n.text}.first - begin - lock_time, locktoken = resource.lock(asked) - render_xml(:prop) do |xml| - xml.lockdiscovery do - xml.activelock do - if(asked[:scope]) - xml.lockscope do - xml.send(asked[:scope]) - end - end - if(asked[:type]) - xml.locktype do - xml.send(asked[:type]) - end - end - xml.depth asked[:depth].to_s - xml.timeout lock_time ? "Second-#{lock_time}" : 'infinity' - xml.locktoken do - xml.href locktoken - end - if(asked[:owner]) - xml.owner asked[:owner] - end - end - end - end - response.status = resource.exist? ? OK : Created - rescue LockFailure => e - multistatus do |xml| - e.path_status.each_pair do |path, status| - xml.response do - xml.href path - xml.status "#{http_version} #{status.status_line}" - end - end - end - end - end - end - - # Unlock current resource - def unlock - resource.unlock(lock_token) - end - - # Perform authentication - # NOTE: Authentication will only be performed if the Resource - # has defined an #authenticate method - def authenticate - authed = true - if(resource.respond_to?(:authenticate, true)) - authed = false - uname = nil - password = nil - if(request.env['HTTP_AUTHORIZATION']) - auth = Rack::Auth::Basic::Request.new(request.env) - if(auth.basic? && auth.credentials) - uname = auth.credentials[0] - password = auth.credentials[1] - end - end - authed = resource.send(:authenticate, uname, password) - end - raise Unauthorized unless authed - end - - # ************************************************************ - # private methods - - private - - # Request environment variables - def env - @request.env - end - - # Current request scheme (http/https) - def scheme - request.scheme - end - - # Request host - def host - request.host - end - - # Request port - def port - request.port - end - - # Class of the resource in use - def resource_class - @options[:resource_class] - end - - # Root URI path for the resource - def root_uri_path - @options[:root_uri_path] - end - - # Returns Resource path with root URI removed - def implied_path - - return clean_path(@request.path_info.dup) unless @request.path_info.empty? - c_path = clean_path(@request.path.dup) - return c_path if c_path.length != @request.path.length - - #if we're here then it's probably down to thin - return @request.path.dup.gsub!(/^#{Regexp.escape(@request.script_name)}/, '') unless @request.script_name.empty? - - return c_path #This will probably result in a processing error if we hit here - end - - # x:: request path - # Unescapes path and removes root URI if applicable - def clean_path(x) - ip = url_unescape(x) - ip.gsub!(/^#{Regexp.escape(root_uri_path)}/, '') if root_uri_path - ip - end - - # Unescaped request path - def actual_path - url_unescape(@request.path.dup) - end - - # Lock token if provided by client - def lock_token - env['HTTP_LOCK_TOKEN'] || nil - end - - # Requested depth - def depth - d = env['HTTP_DEPTH'] - if(d =~ /^\d+$/) - d = d.to_i - else - d = :infinity - end - d - end - - # Current HTTP version being used - def http_version - env['HTTP_VERSION'] || env['SERVER_PROTOCOL'] || 'HTTP/1.0' - end - - # Overwrite is allowed - def overwrite - env['HTTP_OVERWRITE'].to_s.upcase != 'F' - end - - # Find resources at depth requested - def find_resources(with_current_resource=true) - ary = nil - case depth - when 0 - ary = [] - when 1 - ary = resource.children - else - ary = resource.descendants - end - with_current_resource ? [resource] + ary : ary - end - - # XML parsed request - def request_document - @request_document ||= Nokogiri.XML(request.body.read) - rescue - raise BadRequest - end - - # Namespace being used within XML document - # TODO: Make this better - def ns - _ns = '' - 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 - end - - # pattern:: XPath pattern - # Search XML document for given XPath - # TODO: Stripping namespaces not so great - def request_match(pattern) - request_document.remove_namespaces!.xpath(pattern, request_document.root.namespaces) - end - - # root_type:: Root tag name - # Render XML and set Rack::Response#body= to final XML - def render_xml(root_type) - raise ArgumentError.new 'Expecting block' unless block_given? - doc = Nokogiri::XML::Builder.new do |xml_base| - xml_base.send(root_type.to_s, {'xmlns:D' => 'DAV:'}.merge(resource.root_xml_attributes)) do - xml_base.parent.namespace = xml_base.parent.namespace_definitions.first - xml = xml_base['D'] - yield xml - end - end - - if(@options[:pretty_xml]) - response.body = doc.to_xml - else - response.body = doc.to_xml( - :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML - ) - end - response["Content-Type"] = 'text/xml; charset="utf-8"' - response["Content-Length"] = response.body.size.to_s - end - - # block:: block - # Creates a multistatus response using #render_xml and - # returns the correct status - def multistatus(&block) - render_xml(:multistatus, &block) - MultiStatus - end - - # xml:: Nokogiri::XML::Builder - # errors:: Array of errors - # Crafts responses for errors - def response_errors(xml, errors) - for path, status in errors - xml.response do - xml.href "#{scheme}://#{host}:#{port}#{URI.escape(path)}" - xml.status "#{http_version} #{status.status_line}" - end - end - end - - # resource:: Resource - # names:: Property names - # Returns array of property values for given names - def get_properties(resource, names) - stats = Hash.new { |h, k| h[k] = [] } - for name in names - begin - val = resource.get_property(name) - stats[OK].push [name, val] - rescue Unauthorized => u - raise u - rescue Status - stats[$!.class] << name - end - end - stats - end - - # resource:: Resource - # pairs:: name value pairs - # Sets the given properties - def set_properties(resource, pairs) - stats = Hash.new { |h, k| h[k] = [] } - for name, value in pairs - begin - stats[OK] << [name, resource.set_property(name, value)] - rescue Unauthorized => u - raise u - rescue Status - stats[$!.class] << name - end - end - stats - end - - # xml:: Nokogiri::XML::Builder - # stats:: Array of stats - # Build propstats response - def propstats(xml, stats) - return if stats.empty? - for status, props in stats - xml.propstat do - xml.prop do - for name, value in props - if(value.is_a?(Nokogiri::XML::DocumentFragment)) - xml.__send__ :insert, value - elsif(value.is_a?(Nokogiri::XML::Node)) - xml.send(name) do - xml_convert(xml, value) - end - elsif(value.is_a?(Symbol)) - xml.send(name) do - xml.send(value) - end - else - xml.send(name, value) - end - end - end - xml.status "#{http_version} #{status.status_line}" - end - end - end - - # xml:: Nokogiri::XML::Builder - # element:: Nokogiri::XML::Element - # Converts element into proper text - def xml_convert(xml, element) - xml.doc.root.add_child(element) - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/file.rb b/lib/redmine_dmsf/vendor/dav4rack/file.rb deleted file mode 100644 index 5a3c74db..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/file.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module DAV4Rack - # DAV4Rack::File simply allows us to use Rack::File but with the - # specific location we deem appropriate - class File < Rack::File - attr_accessor :path - - alias :to_path :path - - def initialize(path) - @path = path - end - - def _call(env) - begin - if F.file?(@path) && F.readable?(@path) - serving - else - raise Errno::EPERM - end - rescue SystemCallError - not_found - end - end - - def not_found - body = "File not found: #{Rack::Utils.unescape(env["PATH_INFO"])}\n" - [404, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s, - "X-Cascade" => "pass"}, - [body]] - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/file_resource.rb b/lib/redmine_dmsf/vendor/dav4rack/file_resource.rb deleted file mode 100644 index c6616965..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/file_resource.rb +++ /dev/null @@ -1,257 +0,0 @@ -require 'webrick/httputils' - -module DAV4Rack - - class FileResource < Resource - - include WEBrick::HTTPUtils - - # If this is a collection, return the child resources. - def children - Dir[file_path + '/*'].map do |path| - child File.basename(path) - end - end - - # Is this resource a collection? - def collection? - File.directory?(file_path) - end - - # Does this recource exist? - def exist? - File.exist?(file_path) - end - - # Return the creation time. - def creation_date - stat.ctime - end - - # Return the time of last modification. - def last_modified - stat.mtime - end - - # Set the time of last modification. - def last_modified=(time) - File.utime(Time.now, time, file_path) - end - - # Return an Etag, an unique hash value for this resource. - def etag - sprintf('%x-%x-%x', stat.ino, stat.size, stat.mtime.to_i) - end - - # Return the mime type of this resource. - def content_type - if stat.directory? - "text/html" - else - mime_type(file_path, DefaultMimeTypes) - end - end - - # Return the size in bytes for this resource. - def content_length - stat.size - end - - # HTTP GET request. - # - # Write the content of the resource to the response.body. - def get(request, response) - raise NotFound unless exist? - if stat.directory? - response.body = "" - Rack::Directory.new(root).call(request.env)[2].each do |line| - response.body << line - end - response['Content-Length'] = response.body.bytesize.to_s - else - file = Rack::File.new(root) - response.body = file - end - OK - end - - # HTTP PUT request. - # - # Save the content of the request.body. - def put(request, response) - write(request.body) - Created - end - - # HTTP POST request. - # - # Usually forbidden. - def post(request, response) - raise HTTPStatus::Forbidden - end - - # HTTP DELETE request. - # - # Delete this resource. - def delete - if stat.directory? - FileUtils.rm_rf(file_path) - else - File.unlink(file_path) - end - NoContent - end - - # HTTP COPY request. - # - # Copy this resource to given destination resource. - # Copy this resource to given destination resource. - def copy(dest, overwrite) - if(collection?) - if(dest.exist?) - if(dest.collection? && overwrite) - FileUtils.cp_r(file_path, dest.send(:file_path)) - Created - else - if(overwrite) - FileUtils.rm(dest.send(:file_path)) - FileUtils.cp_r(file_path, dest.send(:file_path)) - NoContent - else - PreconditionFailed - end - end - else - FileUtils.cp_r(file_path, dest.send(:file_path)) - Created - end - else - if(dest.exist? && !overwrite) - PreconditionFailed - else - if(File.directory?(File.dirname(dest.send(:file_path)))) - new = !dest.exist? - if(dest.collection? && dest.exist?) - FileUtils.rm_rf(dest.send(:file_path)) - end - FileUtils.cp(file_path, dest.send(:file_path).sub(/\/$/, '')) - new ? Created : NoContent - else - Conflict - end - end - end - end - - # HTTP MOVE request. - # - # Move this resource to given destination resource. - def move(*args) - result = copy(*args) - delete if [Created, NoContent].include?(result) - result - end - - # HTTP MKCOL request. - # - # Create this resource as collection. - def make_collection - if(request.body.read.to_s == '') - if(File.directory?(file_path)) - MethodNotAllowed - else - if(File.directory?(File.dirname(file_path))) - Dir.mkdir(file_path) - Created - else - Conflict - end - end - else - UnsupportedMediaType - end - end - - # Write to this resource from given IO. - def write(io) - tempfile = "#{file_path}.#{Process.pid}.#{object_id}" - - open(tempfile, "wb") do |file| - while part = io.read(8192) - file << part - end - end - - File.rename(tempfile, file_path) - ensure - File.unlink(tempfile) rescue nil - end - - # name:: String - Property name - # Returns the value of the given property - def get_property(name) - super || custom_props(name) - end - - # name:: String - Property name - # value:: New value - # Set the property to the given value - def set_property(name, value) - super || set_custom_props(name,value) - end - - protected - - def set_custom_props(key,val) - prop_hash[key.to_sym] = val - File.open(prop_path, 'w') do |file| - file.write(YAML.dump(prop_hash)) - end - end - - def custom_props(key) - prop_hash[key.to_sym] - end - - def prop_path - path = File.join(root, '.props', File.dirname(file_path), File.basename(file_path)) - unless(File.directory?(File.dirname(path))) - FileUtils.mkdir_p(File.dirname(path)) - end - path - end - - def prop_hash - unless(@_prop_hash) - if(File.exists?(prop_path)) - @_prop_hash = YAML.load(File.read(prop_path)) - else - @_prop_hash = {} - end - end - @_prop_hash - end - - def authenticate(user, pass) - if(options[:username]) - options[:username] == user && options[:password] == pass - else - true - end - end - - def root - @options[:root] - end - - def file_path - File.join(root, path) - end - - def stat - @stat ||= File.stat(file_path) - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/handler.rb b/lib/redmine_dmsf/vendor/dav4rack/handler.rb deleted file mode 100644 index 424072f2..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/handler.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'dav4rack/logger' - -module DAV4Rack - - class Handler - include DAV4Rack::HTTPStatus - def initialize(options={}) - @options = options.dup - unless(@options[:resource_class]) - require 'dav4rack/file_resource' - @options[:resource_class] = FileResource - @options[:root] ||= Dir.pwd - end - Logger.set(*@options[:log_to]) - end - - def call(env) - begin - start = Time.now - request = Rack::Request.new(env) - response = Rack::Response.new - - Logger.info "Processing WebDAV request: #{request.path} (for #{request.ip} at #{Time.now}) [#{request.request_method}]" - - controller = nil - begin - controller_class = @options[:controller_class] || Controller - controller = controller_class.new(request, response, @options.dup) - controller.authenticate - res = controller.send(request.request_method.downcase) - response.status = res.code if res.respond_to?(:code) - rescue HTTPStatus::Unauthorized => status - response.body = controller.resource.respond_to?(:authentication_error_msg) ? controller.resource.authentication_error_msg : 'Not Authorized' - response['WWW-Authenticate'] = "Basic realm=\"#{controller.resource.respond_to?(:authentication_realm) ? controller.resource.authentication_realm : 'Locked content'}\"" - response.status = status.code - rescue HTTPStatus::Status => status - response.status = status.code - end - - # Strings in Ruby 1.9 are no longer enumerable. Rack still expects the response.body to be - # enumerable, however. - - response['Content-Length'] = response.body.to_s.length unless response['Content-Length'] || !response.body.is_a?(String) - response.body = [response.body] unless response.body.respond_to? :each - response.status = response.status ? response.status.to_i : 200 - response.headers.keys.each{|k| response.headers[k] = response[k].to_s} - - # Apache wants the body dealt with, so just read it and junk it - buf = true - buf = request.body.read(8192) while buf - - Logger.debug "Response in string form. Outputting contents: \n#{response.body}" if response.body.is_a?(String) - Logger.info "Completed in: #{((Time.now.to_f - start.to_f) * 1000).to_i} ms | #{response.status} [#{request.url}]" - - response.body.is_a?(Rack::File) ? response.body.call(env) : response.finish - rescue Exception => e - Logger.error "WebDAV Error: #{e}\n#{e.backtrace.join("\n")}" - raise e - end - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/http_status.rb b/lib/redmine_dmsf/vendor/dav4rack/http_status.rb deleted file mode 100644 index f41b80d2..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/http_status.rb +++ /dev/null @@ -1,108 +0,0 @@ -module DAV4Rack - - module HTTPStatus - - class Status < Exception - - class << self - attr_accessor :code, :reason_phrase - alias_method :to_i, :code - - def status_line - "#{code} #{reason_phrase}" - end - - end - - def code - self.class.code - end - - def reason_phrase - self.class.reason_phrase - end - - def status_line - self.class.status_line - end - - def to_i - self.class.to_i - end - - end - - StatusMessage = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Request Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 507 => 'Insufficient Storage' - } - - StatusMessage.each do |code, reason_phrase| - klass = Class.new(Status) - klass.code = code - klass.reason_phrase = reason_phrase - klass_name = reason_phrase.gsub(/[ \-]/,'') - const_set(klass_name, klass) - end - - end - -end - - -module Rack - class Response - module Helpers - DAV4Rack::HTTPStatus::StatusMessage.each do |code, reason_phrase| - name = reason_phrase.gsub(/[ \-]/,'_').downcase - define_method(name + '?') do - @status == code - end - end - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/interceptor.rb b/lib/redmine_dmsf/vendor/dav4rack/interceptor.rb deleted file mode 100644 index d22d922b..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/interceptor.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'dav4rack/interceptor_resource' -module DAV4Rack - class Interceptor - def initialize(app, args={}) - @roots = args[:mappings].keys - @args = args - @app = app - @intercept_methods = %w(OPTIONS PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK) - @intercept_methods -= args[:ignore_methods] if args[:ignore_methods] - end - - def call(env) - path = env['PATH_INFO'].downcase - method = env['REQUEST_METHOD'].upcase - app = nil - if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? && @intercept_methods.include?(method)) - app = DAV4Rack::Handler.new(:resource_class => InterceptorResource, :mappings => @args[:mappings], :log_to => @args[:log_to]) - end - app ? app.call(env) : @app.call(env) - end - end -end \ No newline at end of file diff --git a/lib/redmine_dmsf/vendor/dav4rack/interceptor_resource.rb b/lib/redmine_dmsf/vendor/dav4rack/interceptor_resource.rb deleted file mode 100644 index 00e502d3..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/interceptor_resource.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'digest/sha1' - -module DAV4Rack - - class InterceptorResource < Resource - attr_reader :path, :options - - def initialize(*args) - super - @root_paths = @options[:mappings].keys - @mappings = @options[:mappings] - end - - def children - childs = @root_paths.find_all{|x|x =~ /^#{Regexp.escape(@path)}/} - childs = childs.map{|a| child a.gsub(/^#{Regexp.escape(@path)}/, '').split('/').delete_if{|x|x.empty?}.first }.flatten - end - - def collection? - true if exist? - end - - def exist? - !@root_paths.find_all{|x| x =~ /^#{Regexp.escape(@path)}/}.empty? - end - - def creation_date - Time.now - end - - def last_modified - Time.now - end - - def last_modified=(time) - Time.now - end - - def etag - Digest::SHA1.hexdigest(@path) - end - - def content_type - 'text/html' - end - - def content_length - 0 - end - - def get(request, response) - raise Forbidden - end - - def put(request, response) - raise Forbidden - end - - def post(request, response) - raise Forbidden - end - - def delete - raise Forbidden - end - - def copy(dest) - raise Forbidden - end - - def move(dest) - raise Forbidden - end - - def make_collection - raise Forbidden - end - - def ==(other) - path == other.path - end - - def name - ::File.basename(path) - end - - def display_name - ::File.basename(path.to_s) - end - - def child(name, option={}) - new_path = path.dup - new_path = '/' + new_path unless new_path[0,1] == '/' - new_path.slice!(-1) if new_path[-1,1] == '/' - name = '/' + name unless name[-1,1] == '/' - new_path = "#{new_path}#{name}" - new_public = public_path.dup - new_public = '/' + new_public unless new_public[0,1] == '/' - new_public.slice!(-1) if new_public[-1,1] == '/' - new_public = "#{new_public}#{name}" - if(key = @root_paths.find{|x| new_path =~ /^#{Regexp.escape(x.downcase)}\/?/}) - @mappings[key][:resource_class].new(new_public, new_path.gsub(key, ''), request, response, {:root_uri_path => key, :user => @user}.merge(options).merge(@mappings[key])) - else - self.class.new(new_public, new_path, request, response, {:user => @user}.merge(options)) - end - end - - def descendants - list = [] - children.each do |child| - list << child - list.concat(child.descendants) - end - list - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/lock.rb b/lib/redmine_dmsf/vendor/dav4rack/lock.rb deleted file mode 100644 index e37c0d06..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/lock.rb +++ /dev/null @@ -1,40 +0,0 @@ -module DAV4Rack - class Lock - - def initialize(args={}) - @args = args - @store = nil - @args[:created_at] = Time.now - @args[:updated_at] = Time.now - end - - def store - @store - end - - def store=(s) - raise TypeError.new 'Expecting LockStore' unless s.respond_to? :remove - @store = s - end - - def destroy - if(@store) - @store.remove(self) - end - end - - def remaining_timeout - @args[:timeout].to_i - (Time.now.to_i - @args[:created_at].to_i) - end - - def method_missing(*args) - if(@args.has_key?(args.first.to_sym)) - @args[args.first.to_sym] - elsif(args.first.to_s[-1,1] == '=') - @args[args.first.to_s[0, args.first.to_s.length - 1].to_sym] = args[1] - else - raise NoMethodError.new "Undefined method #{args.first} for #{self}" - end - end - end -end \ No newline at end of file diff --git a/lib/redmine_dmsf/vendor/dav4rack/lock_store.rb b/lib/redmine_dmsf/vendor/dav4rack/lock_store.rb deleted file mode 100644 index 76f7e82e..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/lock_store.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'dav4rack/lock' -module DAV4Rack - class LockStore - class << self - def create - @locks_by_path = {} - @locks_by_token = {} - end - def add(lock) - @locks_by_path[lock.path] = lock - @locks_by_token[lock.token] = lock - end - - def remove(lock) - @locks_by_path.delete(lock.path) - @locks_by_token.delete(lock.token) - end - - def find_by_path(path) - @locks_by_path.map do |lpath, lock| - lpath == path && lock.remaining_timeout > 0 ? lock : nil - end.compact.first - end - - def find_by_token(token) - @locks_by_token.map do |ltoken, lock| - ltoken == token && lock.remaining_timeout > 0 ? lock : nil - end.compact.first - end - - def explicit_locks(path) - @locks_by_path.map do |lpath, lock| - lpath == path && lock.remaining_timeout > 0 ? lock : nil - end.compact - end - - def implicit_locks(path) - @locks_by_path.map do |lpath, lock| - lpath =~ /^#{Regexp.escape(path)}/ && lock.remaining_timeout > 0 && lock.depth > 0 ? lock : nil - end.compact - end - - def explicitly_locked?(path) - self.explicit_locks(path).size > 0 - end - - def implicitly_locked?(path) - self.implicit_locks(path).size > 0 - end - - def generate(path, user, token) - l = Lock.new(:path => path, :user => user, :token => token) - l.store = self - add(l) - l - end - end - end -end - -DAV4Rack::LockStore.create \ No newline at end of file diff --git a/lib/redmine_dmsf/vendor/dav4rack/logger.rb b/lib/redmine_dmsf/vendor/dav4rack/logger.rb deleted file mode 100644 index 64f708be..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/logger.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'logger' - -module DAV4Rack - # This is a simple wrapper for the Logger class. It allows easy access - # to log messages from the library. - class Logger - class << self - # args:: Arguments for Logger -> [path, level] (level is optional) or a Logger instance - # Set the path to the log file. - def set(*args) - if(%w(info debug warn fatal).all?{|meth| args.first.respond_to?(meth)}) - @@logger = args.first - elsif(args.first.respond_to?(:to_s) && !args.first.to_s.empty?) - @@logger = ::Logger.new(args.first.to_s, 'weekly') - elsif(args.first) - raise 'Invalid type specified for logger' - end - if(args.size > 1) - @@logger.level = args[1] - end - end - - def method_missing(*args) - if(defined? @@logger) - @@logger.send *args - end - end - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/remote_file.rb b/lib/redmine_dmsf/vendor/dav4rack/remote_file.rb deleted file mode 100644 index 7abf5753..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/remote_file.rb +++ /dev/null @@ -1,148 +0,0 @@ -require 'net/http' -require 'uri' -require 'digest/sha1' -require 'rack/file' - -module DAV4Rack - - class RemoteFile < Rack::File - - attr_accessor :path - - alias :to_path :path - - # path:: path to file (Actual path, preferably a URL since this is a *REMOTE* file) - # args:: Hash of arguments: - # :size -> Integer - number of bytes - # :mime_type -> String - mime type - # :last_modified -> String/Time - Time of last modification - # :sendfile -> True or String to define sendfile header variation - # :cache_directory -> Where to store cached files - # :cache_ref -> Reference to be used for cache file name (useful for changing URLs like S3) - # :sendfile_prefix -> String directory prefix. Eg: 'webdav' will result in: /wedav/#{path.sub('http://', '')} - # :sendfile_fail_gracefully -> Boolean if true will simply proxy if unable to determine proper sendfile - def initialize(path, args={}) - @path = path - @args = args - @heads = {} - @cache_file = args[:cache_directory] ? cache_file_path : nil - @redefine_prefix = nil - if(@cache_file && File.exists?(@cache_file)) - @root = '' - @path_info = @cache_file - @path = @path_info - elsif(args[:sendfile]) - @redefine_prefix = 'sendfile' - @sendfile_header = args[:sendfile].is_a?(String) ? args[:sendfile] : nil - else - setup_remote - end - do_redefines(@redefine_prefix) if @redefine_prefix - end - - # env:: Environment variable hash - # Process the call - def call(env) - serving(env) - end - - # env:: Environment variable hash - # Return an empty result with the proper header information - def sendfile_serving(env) - header = @sendfile_header || env['sendfile.type'] || env['HTTP_X_SENDFILE_TYPE'] - unless(header) - raise 'Failed to determine proper sendfile header value' unless @args[:sendfile_fail_gracefully] - setup_remote - do_redefines('remote') - call(env) - end - prefix = (@args[:sendfile_prefix] || env['HTTP_X_ACCEL_REMOTE_MAPPING']).to_s.sub(/^\//, '').sub(/\/$/, '') - [200, { - "Last-Modified" => last_modified, - "Content-Type" => content_type, - "Content-Length" => size, - "Redirect-URL" => @path, - "Redirect-Host" => @path.scan(%r{^https?://([^/\?]+)}).first.first, - header => "/#{prefix}" - }, - ['']] - end - - # env:: Environment variable hash - # Return self to be processed - def remote_serving(e) - [200, { - "Last-Modified" => last_modified, - "Content-Type" => content_type, - "Content-Length" => size - }, self] - end - - # Get the remote file - def remote_each - if(@store) - yield @store - else - @con.request_get(@call_path) do |res| - res.read_body(@store) do |part| - @cf.write part if @cf - yield part - end - end - end - end - - # Size based on remote headers or given size - def size - @heads['content-length'] || @size - end - - private - - # Content type based on provided or remote headers - def content_type - @mime_type || @heads['content-type'] - end - - # Last modified type based on provided, remote headers or current time - def last_modified - @heads['last-modified'] || @modified || Time.now.httpdate - end - - # Builds the path for the cached file - def cache_file_path - raise IOError.new 'Write permission is required for cache directory' unless File.writable?(@args[:cache_directory]) - "#{@args[:cache_directory]}/#{Digest::SHA1.hexdigest((@args[:cache_ref] || @path).to_s + size.to_s + last_modified.to_s)}.cache" - end - - # prefix:: prefix of methods to be redefined - # Redefine methods to do what we want in the proper situation - def do_redefines(prefix) - self.public_methods.each do |method| - m = method.to_s.dup - next unless m.slice!(0, prefix.to_s.length + 1) == "#{prefix}_" - self.class.class_eval "undef :'#{m}'" - self.class.class_eval "alias :'#{m}' :'#{method}'" - end - end - - # Sets up all the requirements for proxying a remote file - def setup_remote - if(@cache_file) - begin - @cf = File.open(@cache_file, 'w+') - rescue - @cf = nil - end - end - @uri = URI.parse(@path) - @con = Net::HTTP.new(@uri.host, @uri.port) - @call_path = @uri.path + (@uri.query ? "?#{@uri.query}" : '') - res = @con.request_get(@call_path) - @heads = res.to_hash - res.value - @store = nil - @redefine_prefix = 'remote' - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/resource.rb b/lib/redmine_dmsf/vendor/dav4rack/resource.rb deleted file mode 100644 index 4f3e57ff..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/resource.rb +++ /dev/null @@ -1,469 +0,0 @@ -require 'uuidtools' -require 'dav4rack/http_status' - -module DAV4Rack - - class LockFailure < RuntimeError - attr_reader :path_status - def initialize(*args) - super(*args) - @path_status = {} - end - - def add_failure(path, status) - @path_status[path] = status - end - end - - class Resource - attr_reader :path, :options, :public_path, :request, - :response, :propstat_relative_path, :root_xml_attributes - attr_accessor :user - @@blocks = {} - - class << self - - # This lets us define a bunch of before and after blocks that are - # either called before all methods on the resource, or only specific - # methods on the resource - def method_missing(*args, &block) - class_sym = self.name.to_sym - @@blocks[class_sym] ||= {:before => {}, :after => {}} - m = args.shift - parts = m.to_s.split('_') - type = parts.shift.to_s.to_sym - method = parts.empty? ? nil : parts.join('_').to_sym - if(@@blocks[class_sym][type] && block_given?) - if(method) - @@blocks[class_sym][type][method] ||= [] - @@blocks[class_sym][type][method] << block - else - @@blocks[class_sym][type][:'__all__'] ||= [] - @@blocks[class_sym][type][:'__all__'] << block - end - else - raise NoMethodError.new("Undefined method #{m} for class #{self}") - end - end - - end - - include DAV4Rack::HTTPStatus - - # public_path:: Path received via request - # path:: Internal resource path (Only different from public path when using root_uri's for webdav) - # request:: Rack::Request - # options:: Any options provided for this resource - # Creates a new instance of the resource. - # NOTE: path and public_path will only differ if the root_uri has been set for the resource. The - # controller will strip out the starting path so the resource can easily determine what - # it is working on. For example: - # request -> /my/webdav/directory/actual/path - # public_path -> /my/webdav/directory/actual/path - # path -> /actual/path - # NOTE: Customized Resources should not use initialize for setup. Instead - # use the #setup method - def initialize(public_path, path, request, response, options) - @skip_alias = [ - :authenticate, :authentication_error_msg, - :authentication_realm, :path, :options, - :public_path, :request, :response, :user, - :user=, :setup - ] - @public_path = public_path.dup - @path = path.dup - @propstat_relative_path = !!options.delete(:propstat_relative_path) - @root_xml_attributes = options.delete(:root_xml_attributes) || {} - @request = request - @response = response - unless(options.has_key?(:lock_class)) - require 'dav4rack/lock_store' - @lock_class = LockStore - else - @lock_class = options[:lock_class] - raise NameError.new("Unknown lock type constant provided: #{@lock_class}") unless @lock_class.nil? || defined?(@lock_class) - end - @options = options.dup - @max_timeout = options[:max_timeout] || 86400 - @default_timeout = options[:default_timeout] || 60 - @user = @options[:user] || request.ip - setup if respond_to?(:setup) - public_methods(false).each do |method| - next if @skip_alias.include?(method.to_sym) || method[0,4] == 'DAV_' || method[0,5] == '_DAV_' - self.class.class_eval "alias :'_DAV_#{method}' :'#{method}'" - self.class.class_eval "undef :'#{method}'" - end - @runner = lambda do |class_sym, kind, method_name| - [:'__all__', method_name.to_sym].each do |sym| - if(@@blocks[class_sym] && @@blocks[class_sym][kind] && @@blocks[class_sym][kind][sym]) - @@blocks[class_sym][kind][sym].each do |b| - args = [self, sym == :'__all__' ? method_name : nil].compact - b.call(*args) - end - end - end - end - end - - # This allows us to call before and after blocks - def method_missing(*args) - result = nil - orig = args.shift - class_sym = self.class.name.to_sym - m = orig.to_s[0,5] == '_DAV_' ? orig : "_DAV_#{orig}" # If hell is doing the same thing over and over and expecting a different result this is a hell preventer - raise NoMethodError.new("Undefined method: #{orig} for class #{self}.") unless respond_to?(m) - @runner.call(class_sym, :before, orig) - result = send m, *args - @runner.call(class_sym, :after, orig) - result - end - - # If this is a collection, return the child resources. - def children - NotImplemented - end - - # Is this resource a collection? - def collection? - NotImplemented - end - - # Does this resource exist? - def exist? - NotImplemented - end - - # Does the parent resource exist? - def parent_exists? - parent.exist? - end - - # Return the creation time. - def creation_date - raise NotImplemented - end - - # Return the time of last modification. - def last_modified - raise NotImplemented - end - - # Set the time of last modification. - def last_modified=(time) - # Is this correct? - raise NotImplemented - end - - # Return an Etag, an unique hash value for this resource. - def etag - raise NotImplemented - end - - # Return the resource type. Generally only used to specify - # resource is a collection. - def resource_type - :collection if collection? - end - - # Return the mime type of this resource. - def content_type - raise NotImplemented - end - - # Return the size in bytes for this resource. - def content_length - raise NotImplemented - end - - # HTTP GET request. - # - # Write the content of the resource to the response.body. - def get(request, response) - NotImplemented - end - - # HTTP PUT request. - # - # Save the content of the request.body. - def put(request, response) - NotImplemented - end - - # HTTP POST request. - # - # Usually forbidden. - def post(request, response) - NotImplemented - end - - # HTTP DELETE request. - # - # Delete this resource. - def delete - NotImplemented - end - - # HTTP COPY request. - # - # Copy this resource to given destination resource. - def copy(dest, overwrite=false) - NotImplemented - end - - # HTTP MOVE request. - # - # Move this resource to given destination resource. - def move(dest, overwrite=false) - NotImplemented - end - - # args:: Hash of lock arguments - # Request for a lock on the given resource. A valid lock should lock - # all descendents. Failures should be noted and returned as an exception - # using LockFailure. - # Valid args keys: :timeout -> requested timeout - # :depth -> lock depth - # :scope -> lock scope - # :type -> lock type - # :owner -> lock owner - # Should return a tuple: [lock_time, locktoken] where lock_time is the - # given timeout - # NOTE: See section 9.10 of RFC 4918 for guidance about - # how locks should be generated and the expected responses - # (http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10) - - def lock(args) - unless(@lock_class) - NotImplemented - else - unless(parent_exists?) - Conflict - else - lock_check(args[:scope]) - lock = @lock_class.explicit_locks(@path).find{|l| l.scope == args[:scope] && l.kind == args[:type] && l.user == @user} - unless(lock) - token = UUIDTools::UUID.random_create.to_s - lock = @lock_class.generate(@path, @user, token) - lock.scope = args[:scope] - lock.kind = args[:type] - lock.owner = args[:owner] - lock.depth = args[:depth].is_a?(Symbol) ? args[:depth] : args[:depth].to_i - if(args[:timeout]) - lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout - else - lock.timeout = @default_timeout - end - lock.save if lock.respond_to? :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 - end - - # lock_scope:: scope of lock - # Check if resource is locked. Raise DAV4Rack::LockFailure if locks are in place. - def lock_check(lock_scope=nil) - return unless @lock_class - if(@lock_class.explicitly_locked?(@path)) - raise Locked if @lock_class.explicit_locks(@path).find_all{|l|l.scope == 'exclusive' && l.user != @user}.size > 0 - elsif(@lock_class.implicitly_locked?(@path)) - if(lock_scope.to_s == 'exclusive') - locks = @lock_class.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 = @lock_class.implict_locks(@path).find_all{|l| l.scope == 'exclusive' && l.user != @user} - 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 - - # token:: Lock token - # Remove the given lock - def unlock(token) - unless(@lock_class) - NotImplemented - else - token = token.slice(1, token.length - 2) - if(token.nil? || token.empty?) - BadRequest - else - lock = @lock_class.find_by_token(token) - if(lock.nil? || lock.user != @user) - Forbidden - elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/) - Conflict - else - lock.destroy - NoContent - end - end - end - end - - - # Create this resource as collection. - def make_collection - NotImplemented - end - - # other:: Resource - # Returns if current resource is equal to other resource - def ==(other) - path == other.path - end - - # Name of the resource - def name - File.basename(path) - end - - # Name of the resource to be displayed to the client - def display_name - name - end - - # Available properties - def property_names - %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength) - end - - # name:: String - Property name - # Returns the value of the given property - def get_property(name) - case name - when 'resourcetype' then resource_type - when 'displayname' then display_name - when 'creationdate' then use_ms_compat_creationdate? ? creation_date.httpdate : creation_date.xmlschema - when 'getcontentlength' then content_length.to_s - when 'getcontenttype' then content_type - when 'getetag' then etag - when 'getlastmodified' then last_modified.httpdate - end - end - - # name:: String - Property name - # value:: New value - # Set the property to the given value - def set_property(name, value) - case name - when 'resourcetype' then self.resource_type = value - when 'getcontenttype' then self.content_type = value - when 'getetag' then self.etag = value - when 'getlastmodified' then self.last_modified = Time.httpdate(value) - end - end - - # name:: Property name - # Remove the property from the resource - def remove_property(name) - Forbidden - end - - # name:: Name of child - # Create a new child with the given name - # NOTE:: Include trailing '/' if child is collection - def child(name) - new_public = public_path.dup - new_public = new_public + '/' unless new_public[-1,1] == '/' - new_public = '/' + new_public unless new_public[0,1] == '/' - new_path = path.dup - new_path = new_path + '/' unless new_path[-1,1] == '/' - new_path = '/' + new_path unless new_path[0,1] == '/' - self.class.new("#{new_public}#{name}", "#{new_path}#{name}", request, response, options.merge(:user => @user)) - end - - # Return parent of this resource - def parent - unless(@path.to_s.empty?) - self.class.new( - File.split(@public_path).first, - File.split(@path).first, - @request, - @response, - @options.merge( - :user => @user - ) - ) - end - end - - # Return list of descendants - def descendants - list = [] - children.each do |child| - list << child - list.concat(child.descendants) - end - list - end - - # Index page template for GETs on collection - def index_page - '
| Name | -Size | Type | -Last Modified |
|---|