ServeWebdav 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: