Dav4rack sources replaces with correspondig gem
This commit is contained in:
parent
58eda96713
commit
4350868cef
1
Gemfile
1
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'
|
||||
|
||||
9
lib/redmine_dmsf/vendor/dav4rack.rb
vendored
9
lib/redmine_dmsf/vendor/dav4rack.rb
vendored
@ -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'
|
||||
545
lib/redmine_dmsf/vendor/dav4rack/controller.rb
vendored
545
lib/redmine_dmsf/vendor/dav4rack/controller.rb
vendored
@ -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
|
||||
37
lib/redmine_dmsf/vendor/dav4rack/file.rb
vendored
37
lib/redmine_dmsf/vendor/dav4rack/file.rb
vendored
@ -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
|
||||
257
lib/redmine_dmsf/vendor/dav4rack/file_resource.rb
vendored
257
lib/redmine_dmsf/vendor/dav4rack/file_resource.rb
vendored
@ -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
|
||||
64
lib/redmine_dmsf/vendor/dav4rack/handler.rb
vendored
64
lib/redmine_dmsf/vendor/dav4rack/handler.rb
vendored
@ -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
|
||||
108
lib/redmine_dmsf/vendor/dav4rack/http_status.rb
vendored
108
lib/redmine_dmsf/vendor/dav4rack/http_status.rb
vendored
@ -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
|
||||
22
lib/redmine_dmsf/vendor/dav4rack/interceptor.rb
vendored
22
lib/redmine_dmsf/vendor/dav4rack/interceptor.rb
vendored
@ -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
|
||||
@ -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
|
||||
40
lib/redmine_dmsf/vendor/dav4rack/lock.rb
vendored
40
lib/redmine_dmsf/vendor/dav4rack/lock.rb
vendored
@ -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
|
||||
61
lib/redmine_dmsf/vendor/dav4rack/lock_store.rb
vendored
61
lib/redmine_dmsf/vendor/dav4rack/lock_store.rb
vendored
@ -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
|
||||
30
lib/redmine_dmsf/vendor/dav4rack/logger.rb
vendored
30
lib/redmine_dmsf/vendor/dav4rack/logger.rb
vendored
@ -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
|
||||
148
lib/redmine_dmsf/vendor/dav4rack/remote_file.rb
vendored
148
lib/redmine_dmsf/vendor/dav4rack/remote_file.rb
vendored
@ -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
|
||||
469
lib/redmine_dmsf/vendor/dav4rack/resource.rb
vendored
469
lib/redmine_dmsf/vendor/dav4rack/resource.rb
vendored
@ -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
|
||||
'<html><head> <title>%s</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" /></head>
|
||||
<body> <h1>%s</h1> <hr /> <table> <tr> <th class="name">Name</th>
|
||||
<th class="size">Size</th> <th class="type">Type</th>
|
||||
<th class="mtime">Last Modified</th> </tr> %s </table> <hr /> </body></html>'
|
||||
end
|
||||
|
||||
# Does client allow GET redirection
|
||||
# TODO: Get a comprehensive list in here.
|
||||
# TODO: Allow this to be dynamic so users can add regexes to match if they know of a client
|
||||
# that can be supported that is not listed.
|
||||
def allows_redirect?
|
||||
[
|
||||
%r{cyberduck}i,
|
||||
%r{konqueror}i
|
||||
].any? do |regexp|
|
||||
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp
|
||||
end
|
||||
end
|
||||
|
||||
def use_compat_mkcol_response?
|
||||
@options[:compat_mkcol] || @options[:compat_all]
|
||||
end
|
||||
|
||||
# Returns true if using an MS client
|
||||
def use_ms_compat_creationdate?
|
||||
if(@options[:compat_ms_mangled_creationdate] || @options[:compat_all])
|
||||
is_ms_client?
|
||||
end
|
||||
end
|
||||
|
||||
# Basic user agent testing for MS authored client
|
||||
def is_ms_client?
|
||||
[%r{microsoft-webdav}i, %r{microsoft office}i].any? do |regexp|
|
||||
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Returns authentication credentials if available in form of [username,password]
|
||||
# TODO: Add support for digest
|
||||
def auth_credentials
|
||||
auth = Rack::Auth::Basic::Request.new(request.env)
|
||||
auth.provided? && auth.basic? ? auth.credentials : [nil,nil]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
17
lib/redmine_dmsf/vendor/dav4rack/version.rb
vendored
17
lib/redmine_dmsf/vendor/dav4rack/version.rb
vendored
@ -1,17 +0,0 @@
|
||||
module DAV4Rack
|
||||
class Version
|
||||
|
||||
attr_reader :major, :minor, :tiny
|
||||
|
||||
def initialize(version)
|
||||
version = version.split('.')
|
||||
@major, @minor, @tiny = [version[0].to_i, version[1].to_i, version[2].to_i]
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{@major}.#{@minor}.#{@tiny}"
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = Version.new('0.2.11')
|
||||
end
|
||||
@ -1,6 +1,7 @@
|
||||
# Redmine plugin for Document Management System "Features"
|
||||
#
|
||||
# Copyright (C) 2012 Daniel Munn <dan.munn@munnster.co.uk>
|
||||
# Copyright (C) 2012 Daniel Munn <dan.munn@munnster.co.uk>
|
||||
# Copyright (C) 2011-14 Karel Picman <karel.picman@kontron.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@ -20,12 +21,12 @@ module RedmineDmsf
|
||||
module Webdav
|
||||
class Controller < DAV4Rack::Controller
|
||||
|
||||
#Overload default options
|
||||
# Overload default options
|
||||
def options
|
||||
raise NotFound unless resource.exist?
|
||||
response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
|
||||
response["Dav"] = "1,2,3"
|
||||
response["Ms-Author-Via"] = "DAV"
|
||||
response['Allow'] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
|
||||
response['Dav'] = '1,2,3'
|
||||
response['Ms-Author-Via'] = 'DAV'
|
||||
OK
|
||||
end
|
||||
|
||||
@ -35,7 +36,7 @@ module RedmineDmsf
|
||||
begin
|
||||
request.env['Timeout'] = request.env['HTTP_TIMEOUT'].split('-',2).join(',') unless request.env['HTTP_TIMEOUT'].nil?
|
||||
rescue
|
||||
#Nothing here
|
||||
# Nothing here
|
||||
end
|
||||
|
||||
request_document.remove_namespaces! if ns.empty?
|
||||
@ -46,7 +47,7 @@ module RedmineDmsf
|
||||
end
|
||||
|
||||
|
||||
#Overload the default propfind function with this
|
||||
# Overload the default propfind function with this
|
||||
def propfind
|
||||
unless(resource.exist?)
|
||||
NotFound
|
||||
@ -94,10 +95,22 @@ module RedmineDmsf
|
||||
end
|
||||
end
|
||||
response.body = doc.to_xml
|
||||
response["Content-Type"] = 'application/xml; charset="utf-8"'
|
||||
response["Content-Length"] = response.body.bytesize.to_s
|
||||
response['Content-Type'] = 'application/xml; charset="utf-8"'
|
||||
response['Content-Length'] = response.body.bytesize.to_s
|
||||
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
|
||||
|
||||
private
|
||||
def ns(opt_head = '')
|
||||
@ -111,4 +124,4 @@ module RedmineDmsf
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user