ServeWebdav 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 / 2006-11-13
2
+
3
+ * Initial release
4
+
data/Manifest.txt ADDED
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/serve_webdav.rb
6
+ lib/serve_webdav/errors.rb
7
+ lib/serve_webdav/raw_data_patch.rb
8
+ lib/serve_webdav/resource.rb
9
+ lib/serve_webdav/serve_webdav.rb
10
+ test/test_serve_webdav.rb
data/README.txt ADDED
@@ -0,0 +1,49 @@
1
+ ServeWebdav
2
+ by Stuart Eccles and Adocca AB
3
+ http://www.adocca.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ Based on the RailsDav plugin by Stuart Eccles, but somewhat altered. Provides a webdav interface
8
+ for rails applications.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ TODO write better docs
13
+
14
+ == SYNOPSYS:
15
+
16
+ TODO write better docs...
17
+
18
+ == REQUIREMENTS:
19
+
20
+ * Rails 1.1.6
21
+
22
+ == INSTALL:
23
+
24
+ * gem install serve_webdav
25
+
26
+ == LICENSE:
27
+
28
+ (The MIT License)
29
+
30
+ Copyright (c) 2006 Stuart Eccles & Adocca AB
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining
33
+ a copy of this software and associated documentation files (the
34
+ 'Software'), to deal in the Software without restriction, including
35
+ without limitation the rights to use, copy, modify, merge, publish,
36
+ distribute, sublicense, and/or sell copies of the Software, and to
37
+ permit persons to whom the Software is furnished to do so, subject to
38
+ the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be
41
+ included in all copies or substantial portions of the Software.
42
+
43
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
44
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
45
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
46
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
47
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
48
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
49
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ require './lib/serve_webdav.rb'
7
+
8
+
9
+ Hoe.new('ServeWebdav', ServeWebdav::VERSION) do |p|
10
+ p.rubyforge_name = 'adocca_plugins'
11
+ p.summary = 'Webdav protocol abstraction for rails controllers'
12
+ # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
+ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
+ end
16
+
17
+ # vim: syntax=Ruby
@@ -0,0 +1,68 @@
1
+ require 'active_support'
2
+
3
+ module WebDavErrors
4
+
5
+ class BaseError < Exception
6
+ end
7
+
8
+ class LockedError < WebDavErrors::BaseError
9
+ @@http_status = 423
10
+ cattr_accessor :http_status
11
+ end
12
+
13
+ class InsufficientStorageError < WebDavErrors::BaseError
14
+ @@http_status = 507
15
+ cattr_accessor :http_status
16
+ end
17
+
18
+ class ConflictError < WebDavErrors::BaseError
19
+ @@http_status = 405
20
+ cattr_accessor :http_status
21
+ end
22
+
23
+ class ForbiddenError < WebDavErrors::BaseError
24
+ @@http_status = 403
25
+ cattr_accessor :http_status
26
+ end
27
+
28
+ class BadGatewayError < WebDavErrors::BaseError
29
+ @@http_status = 502
30
+ cattr_accessor :http_status
31
+ end
32
+
33
+ class PreconditionFailsError < WebDavErrors::BaseError
34
+ @@http_status = 412
35
+ cattr_accessor :http_status
36
+ end
37
+
38
+ class NotFoundError < WebDavErrors::BaseError
39
+ @@http_status = 404
40
+ cattr_accessor :http_status
41
+ end
42
+
43
+ class UnSupportedTypeError < WebDavErrors::BaseError
44
+ @@http_status = 415
45
+ cattr_accessor :http_status
46
+ end
47
+
48
+ class UnknownWebDavMethodError < WebDavErrors::BaseError
49
+ @@http_status = 405
50
+ cattr_accessor :http_status
51
+ end
52
+
53
+ class ConflictWebDavError < WebDavErrors::BaseError
54
+ @@http_status = 409
55
+ cattr_accessor :http_status
56
+ end
57
+
58
+ class BadRequestBodyError < WebDavErrors::BaseError
59
+ @@http_status = 400
60
+ cattr_accessor :http_status
61
+ end
62
+
63
+ class EmptyPutError < WebDavErrors::BaseError
64
+ @@http_status = 200
65
+ cattr_accessor :http_status
66
+ end
67
+ end
68
+
@@ -0,0 +1,77 @@
1
+ class CGI #:nodoc:
2
+ # Add @request.env['RAW_POST_DATA'] for the vegans.
3
+ module QueryExtension
4
+
5
+ private
6
+
7
+ def read_query_params(method)
8
+ case method
9
+ when :get
10
+ read_params_from_query
11
+ when :post, :proppatch, :propfind
12
+ read_params_from_post
13
+ when :put
14
+ read_params_from_post
15
+ ""
16
+ when :cmd
17
+ read_from_cmdline
18
+ else # when :head, :delete, :options
19
+ read_params_from_query
20
+ end
21
+ end
22
+ end # module QueryExtension
23
+ end
24
+
25
+ class CGIMethods
26
+ def self.typecast_xml_value(value)
27
+ case value
28
+ when Hash
29
+ if value.has_key?("__content__")
30
+ content = translate_xml_entities(value["__content__"])
31
+ case value["type"]
32
+ when "integer" then content.to_i
33
+ when "boolean" then content == "true"
34
+ when "datetime" then Time.parse(content)
35
+ when "date" then Date.parse(content)
36
+ else content
37
+ end
38
+ else
39
+ value.empty? ? nil : value.inject({}) do |h,(k,v)|
40
+ h[k] = typecast_xml_value(v)
41
+ h
42
+ end
43
+ end
44
+ when Array
45
+ value.map! { |i| typecast_xml_value(i) }
46
+ case value.length
47
+ when 0 then nil
48
+ when 1 then value.first
49
+ else value
50
+ end
51
+ when String
52
+ value
53
+ else
54
+ raise "can't typecast #{value.inspect}"
55
+ end
56
+ end
57
+ end
58
+
59
+ class CGIMethods
60
+ def CGIMethods.parse_request_parameters(params)
61
+ parsed_params = {}
62
+
63
+ for key, value in params
64
+ value = [value] if key =~ /.*\[\]$/
65
+ next if key.nil?
66
+ if !key.nil? && !key.include?('[')
67
+ # much faster to test for the most common case first (GET)
68
+ # and avoid the call to build_deep_hash
69
+ parsed_params[key] = get_typed_value(value[0])
70
+ else
71
+ build_deep_hash(get_typed_value(value[0]), parsed_params, get_levels(key))
72
+ end
73
+ end
74
+
75
+ parsed_params
76
+ end
77
+ end
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2006 Stuart Eccles
2
+ # Released under the MIT License. See the LICENSE file for more details.
3
+ class WebDavResource
4
+
5
+ def properties
6
+ Array.new
7
+ end
8
+
9
+ def delete!
10
+
11
+ end
12
+
13
+ def move! (dest_path, depth)
14
+
15
+ end
16
+
17
+ def copy! (dest_path, depth)
18
+
19
+ end
20
+
21
+ def status
22
+ gen_status(200, "OK").to_s
23
+ end
24
+
25
+ def collection?
26
+ return false
27
+ end
28
+
29
+ def children
30
+ return []
31
+ end
32
+
33
+ def get_displayname
34
+ CGI::escape(self.displayname).gsub(/\+/, '%20')
35
+ end
36
+
37
+ def get_href
38
+ self.href.gsub(/\+/, '%20')
39
+ end
40
+
41
+ def get_properties
42
+ hsh = {}
43
+ self.properties.each do|meth|
44
+ if self.respond_to?('get_'+meth.to_s)
45
+ hsh[meth] = self.send(('get_'+meth.to_s).to_sym)
46
+ else
47
+ hsh[meth] = self.send(meth)
48
+ end
49
+ end
50
+ hsh
51
+ end
52
+
53
+ protected
54
+ def gen_element(elem, text = nil, attrib = {})
55
+ e = REXML::Element.new elem
56
+ text and e.text = text
57
+ attrib.each {|k, v| e.attributes[k] = v }
58
+ e
59
+ end
60
+
61
+ def gen_status(status_code, reason_phrase)
62
+ "HTTP/1.1 #{status_code} #{reason_phrase}"
63
+ end
64
+
65
+ end
66
+
@@ -0,0 +1,419 @@
1
+ require 'action_controller'
2
+ module ServeWebdav
3
+
4
+ def self.append_features(base)
5
+ super
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def serve_webdav
11
+ class_eval do
12
+ extend ServeWebdav::SingletonMethods
13
+ end
14
+ include ServeWebdav::InstanceMethods
15
+ end
16
+ end
17
+
18
+ module SingletonMethods
19
+
20
+ def set_max_propfind_depth( value=nil)
21
+ @@max_propfind_depth = value
22
+ end
23
+
24
+ def get_max_propfind_depth
25
+ @@max_propfind_depth
26
+ end
27
+
28
+ def get_propfind_xml
29
+ <<EOPROPFIND_XML
30
+ xml.D(:multistatus, {"xmlns:D" => "DAV:"}) do
31
+ @resources.each do |resource|
32
+ xml.D :response do
33
+ xml.D :href, resource.get_href
34
+ xml.D :propstat do
35
+ xml.D :prop do
36
+ resource.get_properties.each do |property, value|
37
+ xml.D(property, value)
38
+ end
39
+ xml.D :resourcetype do
40
+ xml.D :collection if resource.collection?
41
+ end
42
+ end
43
+ xml.D :status, resource.status
44
+ end
45
+ end
46
+ end
47
+ end
48
+ EOPROPFIND_XML
49
+ end
50
+ def get_proppatch_xml
51
+ <<EOPROPPATCH_XML
52
+ xml.D(:multistatus, {"xmlns:D" => "DAV:"}) do
53
+ xml.D :response do
54
+ xml.D :href, @resource.get_href
55
+ for remove_property in @remove_properties
56
+ xml.D :propstat do
57
+ xml.D :prop do
58
+ xml.tag! remove_property.name.to_sym, remove_property.attributes
59
+ end
60
+ if @resource.respond_to?(("remove_" + remove_property.name).to_sym)
61
+ xml.D(:status, @resource.__send__(("remove_" + remove_property.name).to_sym))
62
+ else
63
+ xml.D :status, "HTTP/1.1 200 OK"
64
+ end
65
+ end
66
+ end
67
+ for set_property in @set_properties
68
+ xml.D :propstat do
69
+ xml.D :prop do
70
+ xml.D set_property.name.to_sym, set_property.attributes
71
+ end
72
+ if @resource.respond_to?(("set_" + set_property.name).to_sym)
73
+ xml.D(:status, @resource.__send__(("set_" + set_property.name).to_sym))
74
+ else
75
+ xml.D :status, "HTTP/1.1 200 OK"
76
+ end
77
+ end
78
+ end
79
+ xml.D :responsedescription
80
+ end
81
+ end
82
+ EOPROPPATCH_XML
83
+ end
84
+
85
+ private
86
+ def define_attr_method(name, value=nil, &block)
87
+ sing = class << self; self; end
88
+ #sing.send :alias_method, "original_#{name}", name
89
+ if block_given?
90
+ sing.send :define_method, name, &block
91
+ else
92
+ # use eval instead of a block to work around a memory leak in dev
93
+ # mode in fcgi
94
+ sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
95
+ end
96
+ end
97
+ end # SingletonMethods
98
+
99
+ module InstanceMethods
100
+ def index
101
+ webdav
102
+ end
103
+
104
+ def webdav
105
+ #we can get the method (as webdav has a lot of new methods) from the request
106
+ webdav_method = request.method.to_s
107
+ @path_info = convert_path_info(@request, params[:path_info].to_s)
108
+
109
+ begin
110
+ #going to call the method for this webdav method
111
+ if respond_to?("webdav_#{webdav_method}", true)
112
+ __send__("webdav_#{webdav_method}")
113
+ else
114
+ raise WebDavErrors::UnknownWebDavMethodError
115
+ end
116
+
117
+ rescue WebDavErrors::BaseError => webdav_error
118
+ logger.debug("ERROR : #{webdav_error.to_s}")
119
+ logger.debug("STATUS : #{webdav_error.http_status}")
120
+ render :nothing => true, :status => webdav_error.http_status and return
121
+ end
122
+ end
123
+
124
+ def webdav_options()
125
+ @response.headers['DAV'] = "1,2"
126
+ @response.headers['MS-Author-Via'] = "DAV"
127
+ @response.headers["Allow"] = "COPY,DELETE,GET,HEAD,MKCOL,MOVE,OPTIONS,POST,PROPFIND,PROPPATCH,PUT"
128
+ render :nothing => true, :status => 200 and return
129
+ end
130
+
131
+ def webdav_lock()
132
+ #TODO implementation for now return a 200 OK
133
+ render :nothing => true, :status => 200 and return
134
+ end
135
+
136
+ def webdav_unlock()
137
+ #TODO implementation for now return a 200 OK
138
+ render :nothing => true, :status => 200 and return
139
+ end
140
+
141
+ def webdav_propfind()
142
+ logger.info("PATH INFO: " + @path_info)
143
+ @depth = get_depth
144
+
145
+ #default a big depth if infinity
146
+ @depth = 500 if @depth.nil?
147
+
148
+
149
+ unless request.raw_post.blank?
150
+ begin
151
+ req_doc = REXML::Document.new @request.raw_post
152
+ rescue REXML::ParseException
153
+ raise WebDavErrors::BadRequestBodyError
154
+ end
155
+ end
156
+
157
+ @resources = get_dav_resource_props(@path_info)
158
+
159
+ raise WebDavErrors::NotFoundError if @resources.blank?
160
+
161
+ response.headers["Content-Type"] = 'text/xml; charset="utf-8"'
162
+ #render the Multistatus XML
163
+ render :inline => self.class.get_propfind_xml, :type => :rxml, :status => 207 and return
164
+ end
165
+
166
+ def webdav_proppatch()
167
+
168
+ @resource = get_resource_for_path(@path_info)
169
+
170
+ ns = {""=>"DAV:"}
171
+ begin
172
+ req_doc = REXML::Document.new request.raw_post
173
+ rescue REXML::ParseException
174
+ raise WebDavErrors::BadRequestBodyError
175
+ end
176
+
177
+ @remove_properties = []
178
+ @set_properties = []
179
+ REXML::XPath.each(req_doc, "/propertyupdate/remove/prop/*", ns){|e|
180
+ @remove_properties << e
181
+ }
182
+ REXML::XPath.each(req_doc, "/propertyupdate/set/prop/*", ns){|e|
183
+ @set_properties << e
184
+ }
185
+
186
+ response.headers["Content-Type"] = 'text/xml; charset="utf-8"'
187
+
188
+ #render the Multistatus XML
189
+ render :inline => self.class.get_proppatch_xml, :type => :rxml, :status => 207 # and return
190
+ end
191
+
192
+ def webdav_mkcol
193
+ # need to check the content-type header to not allow invalid content types
194
+ mkcol_ct = request.env['CONTENT_TYPE']
195
+
196
+ logger.info(request.inspect)
197
+
198
+ if (!mkcol_ct.blank? && (mkcol_ct != "httpd/unix-directory")) || !request.raw_post.nil?
199
+ raise WebDavErrors::UnSupportedTypeError
200
+ end
201
+
202
+
203
+ raise WebDavErrors::UnknownWebDavMethodError if file_exist?(@path_info)
204
+
205
+ mkcol_for_path(@path_info)
206
+
207
+ render :nothing => true, :status => 201
208
+ end
209
+
210
+ def webdav_delete()
211
+ resource = get_resource_for_path(@path_info)
212
+ unless resource.nil?
213
+ logger.debug('RESOURCE IS' + resource.inspect)
214
+ resource.delete!
215
+ else
216
+ # Delete on a non-existant resource
217
+ raise WebDavErrors::NotFoundError
218
+ end
219
+
220
+ render :nothing => true, :status => 204
221
+ end
222
+
223
+ def webdav_put()
224
+ write_content_to_path(@path_info, request.raw_post)
225
+
226
+ render :nothing => true, :status => 201 and return
227
+ end
228
+
229
+ def webdav_copy()
230
+ @depth = get_depth
231
+
232
+ #Check the destination URI
233
+ begin
234
+ dest_uri = get_destination_uri
235
+ dest_path = convert_path_info(@request, get_dest_path)
236
+ # Bad Gateway it if the servers arnt the same
237
+ raise WebDavErrors::BadGatewayError unless request.host_with_port == "#{dest_uri.host}:#{dest_uri.port}"
238
+ rescue URI::InvalidURIError
239
+ raise WebDavErrors::BadGatewayError
240
+ end
241
+
242
+ resource = get_resource_for_path(@path_info)
243
+
244
+ # Not found if the source doesnt exist
245
+ raise WebDavErrors::NotFoundError if resource.nil?
246
+
247
+ file_exist = file_exist?(dest_path)
248
+
249
+ raise WebDavErrors::PreconditionFailsError if file_exist && !get_overwrite
250
+
251
+ copy_to_path(resource, dest_path, @depth)
252
+
253
+ file_exist ? render(:nothing => true, :status => 204) : render(:nothing => true, :status => 201)
254
+ end
255
+
256
+ def webdav_move()
257
+ @depth = get_depth
258
+
259
+ #Check the destination URI
260
+ begin
261
+ dest_uri = get_destination_uri
262
+ dest_path = convert_path_info(@request, get_dest_path)
263
+ #Bad Gateway it if the servers arnt the same
264
+ raise WebDavErrors::BadGatewayError unless request.host_with_port == "#{dest_uri.host}:#{dest_uri.port}"
265
+ rescue URI::InvalidURIError => e
266
+ logger.debug("INVALID URI: " + e.to_s)
267
+ logger.debug(request.env['HTTP_DESTINATION'])
268
+ raise WebDavErrors::BadGatewayError
269
+ end
270
+
271
+ resource = get_resource_for_path(@path_info)
272
+
273
+ #Not found if the source doesnt exist
274
+ raise WebDavErrors::NotFoundError if resource.nil?
275
+
276
+ file_exist = file_exist?(dest_path)
277
+
278
+ raise WebDavErrors::PreconditionFailsError if file_exist && !get_overwrite
279
+
280
+ move_to_path(resource, dest_path, @depth)
281
+ file_exist ? render(:nothing => true, :status => 204) : render(:nothing => true, :status => 201)
282
+ end
283
+
284
+ def webdav_get
285
+ resource = get_resource_for_path(@path_info)
286
+ send_data resource.data, :filename => resource.get_displayname
287
+ end
288
+
289
+ def webdav_head
290
+ resource = get_resource_for_path(@path_info)
291
+ raise WebDavErrors::NotFoundError if resource.blank?
292
+ @response.headers["Last-Modified"] = resource.getlastmodified
293
+ render(:nothing => true, :status => 200) and return
294
+ end
295
+
296
+ protected
297
+ ##############################################################################################################
298
+ #To be overidden by implementing controller
299
+ def mkcol_for_path(path)
300
+ raise "implement me"
301
+ end
302
+
303
+ def write_content_to_path(path, content)
304
+ raise "implement me"
305
+ end
306
+
307
+ def file_exist?(dest_path)
308
+ raise "implement me"
309
+ end
310
+
311
+ def copy_to_path(resource, dest_path, depth)
312
+ raise "implement me"
313
+ end
314
+
315
+ def move_to_path(resource, dest_path, depth)
316
+ raise "implement me"
317
+ end
318
+
319
+ def get_resource_for_path(file_path)
320
+ raise "implement me" # WebDavResource.new
321
+ end
322
+ ##############################################################################################################
323
+
324
+ def get_depth
325
+ depth_head = request.env['HTTP_DEPTH']
326
+
327
+ if (depth_head.nil?)
328
+ depth = 1
329
+ else
330
+ depth = (depth_head == "infinity") ? nil : depth_head.to_i
331
+ end
332
+ return depth
333
+ end
334
+
335
+ def get_destination_uri
336
+ URI.parse(request.env['HTTP_DESTINATION'])
337
+ end
338
+
339
+ def get_dest_path
340
+ if /^#{Regexp.escape(url_for(:only_path => true, :path_info => ""))}/ =~ get_destination_uri.path
341
+ return $'
342
+ else
343
+ raise WebDavErrors::ForbiddenError
344
+ end
345
+ end
346
+
347
+ def get_overwrite
348
+ ov = request.env['HTTP_OVERWRITE']
349
+ return (!ov.blank? and ov == 'T')
350
+ end
351
+
352
+ def get_dav_resource_props(path)
353
+ ret_set = Array.new
354
+ @depth = self.class.get_max_propfind_depth if @depth > self.class.get_max_propfind_depth
355
+ @depth -= 1
356
+ resource = get_resource_for_path(path)
357
+ ret_set << resource
358
+ ret_set.concat resource.children unless @depth < 0
359
+
360
+ return ret_set
361
+ end
362
+
363
+ def href_for_path(path)
364
+ unless path.nil?
365
+ new_path = path.clone
366
+ new_path = new_path[1..-1] if (new_path.first == "/")
367
+ url_for(:only_path => true, :path_info => new_path)
368
+ else
369
+ url_for(:only_path => true)
370
+ end
371
+ end
372
+
373
+ def basic_auth_required(realm='Web Password', error_message="Could not authenticate you")
374
+ username, passwd = get_auth_data
375
+ # check if authorized
376
+ # try to get user
377
+ unless yield username, passwd
378
+ # the user does not exist or the password was wrong
379
+ headers["Status"] = "Unauthorized"
380
+ headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
381
+ render :nothing => true, :status => 401
382
+ end
383
+ end
384
+
385
+ def get_auth_data
386
+ user, pass = '', ''
387
+ # extract authorisation credentials
388
+ if request.env.has_key? 'X-HTTP_AUTHORIZATION'
389
+ # try to get it where mod_rewrite might have put it
390
+ authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split
391
+ elsif request.env.has_key? 'HTTP_AUTHORIZATION'
392
+ # this is the regular location
393
+ authdata = request.env['HTTP_AUTHORIZATION'].to_s.split
394
+ end
395
+
396
+ # at the moment we only support basic authentication
397
+ if authdata and authdata[0] == 'Basic'
398
+ user, pass = Base64.decode64(authdata[1]).split(':')[0..1]
399
+ end
400
+ return [user, pass]
401
+ end
402
+
403
+ def convert_path_info(req, path)
404
+ logger.info("PATH; " + path)
405
+ if req.env["HTTP_USER_AGENT"].match(/Microsoft|Windows/)
406
+ logger.info("CONVERTED: " + Iconv.iconv('UTF-8', 'latin1', CGI::unescape(path)).first)
407
+ Iconv.iconv('UTF-8', 'latin1', CGI::unescape(path)).first
408
+ elsif req.env["HTTP_USER_AGENT"].match(/cadaver/)
409
+ CGI::unescape(CGI::unescape(path))
410
+ elsif req.env["HTTP_USER_AGENT"].match(/Darwin|Macintosh/)
411
+ Unicode::normalize_KC(CGI::unescape(path))
412
+ else
413
+ CGI::unescape(path)
414
+ end
415
+ end
416
+ end
417
+ end
418
+
419
+ ActionController::Base.send(:include, ServeWebdav)
@@ -0,0 +1,14 @@
1
+ # = About lib/serve_webdav.rb
2
+ $:.unshift File.expand_path(File.dirname(__FILE__))
3
+
4
+ # Blah blah
5
+
6
+ module ServeWebdav
7
+ VERSION = '0.0.1'
8
+ end
9
+
10
+ require 'serve_webdav/errors'
11
+ require 'serve_webdav/resource'
12
+ require 'serve_webdav/raw_data_patch'
13
+ require 'serve_webdav/serve_webdav'
14
+
File without changes
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: ServeWebdav
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2006-11-13 00:00:00 +01:00
8
+ summary: Webdav protocol abstraction for rails controllers
9
+ require_paths:
10
+ - lib
11
+ email: ryand-ruby@zenspider.com
12
+ homepage: http://www.zenspider.com/ZSS/Products/ServeWebdav/
13
+ rubyforge_project: adocca_plugins
14
+ description: Ryan Davis is too lazy to write a description
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Ryan Davis
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - lib/serve_webdav.rb
37
+ - lib/serve_webdav/errors.rb
38
+ - lib/serve_webdav/raw_data_patch.rb
39
+ - lib/serve_webdav/resource.rb
40
+ - lib/serve_webdav/serve_webdav.rb
41
+ - test/test_serve_webdav.rb
42
+ test_files:
43
+ - test/test_serve_webdav.rb
44
+ rdoc_options: []
45
+
46
+ extra_rdoc_files: []
47
+
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ requirements: []
53
+
54
+ dependencies:
55
+ - !ruby/object:Gem::Dependency
56
+ name: hoe
57
+ version_requirement:
58
+ version_requirements: !ruby/object:Gem::Version::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.1.4
63
+ version: