From c76556c50795961e71f624132c76fb624b397164 Mon Sep 17 00:00:00 2001 From: Daniel Munn Date: Thu, 14 Jun 2012 22:35:02 +0100 Subject: [PATCH] Parse fix applied for webdav PUT method, where .Net / Office over WebDav pushes content with text/xml mime type. Rails stack automatically will try to parse XML, however unfortunately as content is not XML, it ends up failing. "init.rb" has a hook adding RedmineDmsf::NoParse into Rails middleware a step before content parsing occurs, and will rewrite content type on put method, within specified target area (identified in init.rb). Controller has been added to override options function (maybe removed later on). --- config/routes.rb | 1 + init.rb | 30 ++++++++----- lib/redmine_dmsf.rb | 1 + lib/redmine_dmsf/no_parse.rb | 25 +++++++++++ lib/redmine_dmsf/webdav.rb | 4 +- lib/redmine_dmsf/webdav/base_resource.rb | 1 + lib/redmine_dmsf/webdav/controller.rb | 49 +++++++++++++++++++++ lib/redmine_dmsf/webdav/dmsf_resource.rb | 1 - lib/redmine_dmsf/webdav/project_resource.rb | 8 ++++ lib/redmine_dmsf/webdav/resource_proxy.rb | 2 +- 10 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 lib/redmine_dmsf/no_parse.rb create mode 100644 lib/redmine_dmsf/webdav/controller.rb diff --git a/config/routes.rb b/config/routes.rb index a43cd4c9..3d27b11b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -92,5 +92,6 @@ RedmineApp::Application.routes.draw do # :root => Rails.root.to_s, :root_uri_path => "/dmsf/webdav", :resource_class => RedmineDmsf::Webdav::ResourceProxy, + :controller_class => RedmineDmsf::Webdav::Controller ), :at => "/dmsf/webdav" end diff --git a/init.rb b/init.rb index 6c2f57ca..d9b385e8 100644 --- a/init.rb +++ b/init.rb @@ -17,19 +17,29 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require 'redmine' -#require 'dispatcher' - -#Dispatcher.to_prepare :redmine_dmsf do +require 'redmine_dmsf' + +# +# This may need to be configurable +# +Rails.configuration.middleware.insert_before(ActionDispatch::ParamsParser, + RedmineDmsf::NoParse, :urls => ['/dmsf/webdav']) + + + Rails.configuration.to_prepare do - unless ProjectsHelper.included_modules.include?(ProjectTabsExtended) - ProjectsHelper.send(:include, ProjectTabsExtended) - end + unless ProjectsHelper.included_modules.include?(ProjectTabsExtended) + ProjectsHelper.send(:include, ProjectTabsExtended) + end - unless CustomFieldsHelper.included_modules.include?(CustomFieldsHelper) - CustomFieldsHelper.send(:include, RedmineDmsf::Patches::CustomFieldsHelper) - end + unless CustomFieldsHelper.included_modules.include?(CustomFieldsHelper) + CustomFieldsHelper.send(:include, RedmineDmsf::Patches::CustomFieldsHelper) + end + + Project.send(:include, RedmineDmsf::Patches::ProjectPatch) + + #ActiveSupport::XmlMini.backend = 'Nokogiri' - Project.send(:include, RedmineDmsf::Patches::ProjectPatch) end Redmine::Plugin.register :redmine_dmsf do diff --git a/lib/redmine_dmsf.rb b/lib/redmine_dmsf.rb index 99a6fcb5..7e3e9c12 100644 --- a/lib/redmine_dmsf.rb +++ b/lib/redmine_dmsf.rb @@ -2,6 +2,7 @@ require 'redmine_dmsf/patches/custom_fields_helper' require 'redmine_dmsf/patches/acts_as_customizable' require 'redmine_dmsf/patches/project_patch' require 'redmine_dmsf/webdav' +require 'redmine_dmsf/no_parse' module RedmineDmsf end diff --git a/lib/redmine_dmsf/no_parse.rb b/lib/redmine_dmsf/no_parse.rb new file mode 100644 index 00000000..3dc99238 --- /dev/null +++ b/lib/redmine_dmsf/no_parse.rb @@ -0,0 +1,25 @@ +module RedmineDmsf + class NoParse + def initialize(app, options={}) + @app = app + @urls = options[:urls] + end + + def call(env) + if env['REQUEST_METHOD'] == "PUT" && env.has_key?('CONTENT_TYPE') then + if (@urls.dup.delete_if {|x| !env['PATH_INFO'].starts_with? x}.length > 0) then + logger "RedmineDmsf::NoParse prevented mime parsing for PUT #{env['PATH_INFO']}" + env['CONTENT_TYPE'] = 'text/plain' + end + end + @app.call(env) + end + + private + + def logger(env) + env['action_dispatch.logger'] || Logger.new($stdout) + end + + end +end diff --git a/lib/redmine_dmsf/webdav.rb b/lib/redmine_dmsf/webdav.rb index 05984b5b..93e8f55a 100644 --- a/lib/redmine_dmsf/webdav.rb +++ b/lib/redmine_dmsf/webdav.rb @@ -1,7 +1,7 @@ +# Load up classes that make up our webdav solution ontop +# of DAV4Rack require 'redmine_dmsf/webdav/resource_proxy' - require 'redmine_dmsf/webdav/base_resource' - require 'redmine_dmsf/webdav/index_resource' require 'redmine_dmsf/webdav/project_resource' require 'redmine_dmsf/webdav/dmsf_resource' diff --git a/lib/redmine_dmsf/webdav/base_resource.rb b/lib/redmine_dmsf/webdav/base_resource.rb index d1077ea6..7484b088 100644 --- a/lib/redmine_dmsf/webdav/base_resource.rb +++ b/lib/redmine_dmsf/webdav/base_resource.rb @@ -51,6 +51,7 @@ module RedmineDmsf def parent p = @__proxy.parent + return nil if p.nil? return p.resource.nil? ? p : p.resource end diff --git a/lib/redmine_dmsf/webdav/controller.rb b/lib/redmine_dmsf/webdav/controller.rb new file mode 100644 index 00000000..01c18e5a --- /dev/null +++ b/lib/redmine_dmsf/webdav/controller.rb @@ -0,0 +1,49 @@ +module RedmineDmsf + module Webdav + class Controller < DAV4Rack::Controller + + #Overload default options + def options + 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 + + #Overload the default propfind function with this + 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}::", '') + } + 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 + + end + end +end diff --git a/lib/redmine_dmsf/webdav/dmsf_resource.rb b/lib/redmine_dmsf/webdav/dmsf_resource.rb index c84855fb..9857d6df 100644 --- a/lib/redmine_dmsf/webdav/dmsf_resource.rb +++ b/lib/redmine_dmsf/webdav/dmsf_resource.rb @@ -167,7 +167,6 @@ module RedmineDmsf # # Create a DmsfFolder at location requested, only if parent is a folder (or root) def make_collection - debugger if (request.body.read.to_s == '') return MethodNotAllowed if exist? #If we already exist, why waste the time trying to save? parent_folder = nil diff --git a/lib/redmine_dmsf/webdav/project_resource.rb b/lib/redmine_dmsf/webdav/project_resource.rb index 28c7730f..f30a6a16 100644 --- a/lib/redmine_dmsf/webdav/project_resource.rb +++ b/lib/redmine_dmsf/webdav/project_resource.rb @@ -9,6 +9,7 @@ module RedmineDmsf def children #caching for repeat usage return @children unless @children.nil? + return [] if project.nil? || project.id.nil? @children = [] DmsfFolder.project_root_folders(project).map do |p| @children.push child(p.title, p) @@ -65,6 +66,13 @@ module RedmineDmsf OK end + def folder + nil + end + def file + nil + end + end end end diff --git a/lib/redmine_dmsf/webdav/resource_proxy.rb b/lib/redmine_dmsf/webdav/resource_proxy.rb index dcba5ec7..1dbbf884 100644 --- a/lib/redmine_dmsf/webdav/resource_proxy.rb +++ b/lib/redmine_dmsf/webdav/resource_proxy.rb @@ -30,7 +30,7 @@ module RedmineDmsf # going to fork it to ensure compliance, checking the request method in the authentication # seems the next best step, if the request method is OPTIONS return true, controller will simply # call the options method within, which accesses nothing, just returns headers about dav env. - return false if (@request.request_method.downcase == "options") + return true if ( @request.request_method.downcase == "options" && ( path == "/" || path.empty? ) ) User.current = User.try_to_login(username, password) || nil return !User.current.anonymous? unless User.current.nil? false