dav4rack 0.2.10 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -0
- data/bin/dav4rack +4 -1
- data/lib/dav4rack/controller.rb +65 -30
- data/lib/dav4rack/file_resource.rb +93 -20
- data/lib/dav4rack/handler.rb +2 -1
- data/lib/dav4rack/resource.rb +45 -17
- data/lib/dav4rack/version.rb +1 -1
- data/spec/handler_spec.rb +15 -29
- metadata +42 -75
data/README.rdoc
CHANGED
@@ -311,6 +311,8 @@ A big thanks to everyone contributing to help make this project better.
|
|
311
311
|
* {doxavore}[https://github.com/doxavore]
|
312
312
|
* {spicyj}[https://github.com/spicyj]
|
313
313
|
* {TurchenkoAlex}[https://github.com/TurchenkoAlex]
|
314
|
+
* {exabugs}[https://github.com/exabugs]
|
315
|
+
* {inferiorhumanorgans}[https://github.com/inferiorhumanorgans]
|
314
316
|
|
315
317
|
== License
|
316
318
|
|
data/bin/dav4rack
CHANGED
@@ -27,7 +27,8 @@ opts = GetoptLong.new(
|
|
27
27
|
['--version', '-v', GetoptLong::NO_ARGUMENT],
|
28
28
|
['--root', '-r', GetoptLong::REQUIRED_ARGUMENT],
|
29
29
|
['--log', '-l', GetoptLong::OPTIONAL_ARGUMENT],
|
30
|
-
['--verbosity', '-V', GetoptLong::REQUIRED_ARGUMENT]
|
30
|
+
['--verbosity', '-V', GetoptLong::REQUIRED_ARGUMENT],
|
31
|
+
['--pretty-xml', '-P', GetoptLong::REQUIRED_ARGUMENT]
|
31
32
|
)
|
32
33
|
|
33
34
|
credentials = {}
|
@@ -47,6 +48,8 @@ opts.each do |opt,arg|
|
|
47
48
|
puts "ERROR: Path provided is not a valid directory (#{arg})"
|
48
49
|
exit(-1)
|
49
50
|
end
|
51
|
+
when '--pretty-xml'
|
52
|
+
credentials[:pretty_xml] = true
|
50
53
|
when '--version'
|
51
54
|
print_version_info
|
52
55
|
exit(0)
|
data/lib/dav4rack/controller.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module DAV4Rack
|
2
4
|
|
3
5
|
class Controller
|
@@ -11,7 +13,7 @@ module DAV4Rack
|
|
11
13
|
# Create a new Controller.
|
12
14
|
# NOTE: options will be passed to Resource
|
13
15
|
def initialize(request, response, options={})
|
14
|
-
raise Forbidden if request.path_info.include?('
|
16
|
+
raise Forbidden if request.path_info.include?('..')
|
15
17
|
@request = request
|
16
18
|
@response = response
|
17
19
|
@options = options
|
@@ -20,18 +22,18 @@ module DAV4Rack
|
|
20
22
|
|
21
23
|
# s:: string
|
22
24
|
# Escape URL string
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def url_format(resource)
|
26
|
+
ret = URI.escape(resource.public_path)
|
27
|
+
if resource.collection? and ret[-1,1] != '/'
|
28
|
+
ret += '/'
|
29
|
+
end
|
30
|
+
ret
|
27
31
|
end
|
28
32
|
|
29
33
|
# s:: string
|
30
34
|
# Unescape URL string
|
31
35
|
def url_unescape(s)
|
32
|
-
|
33
|
-
[$1.delete('%')].pack('H*')
|
34
|
-
end
|
36
|
+
URI.unescape(s)
|
35
37
|
end
|
36
38
|
|
37
39
|
# Return response to OPTIONS
|
@@ -79,7 +81,7 @@ module DAV4Rack
|
|
79
81
|
else
|
80
82
|
resource.lock_check
|
81
83
|
status = resource.put(request, response)
|
82
|
-
response['Location'] = "#{scheme}://#{host}:#{port}#{resource
|
84
|
+
response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created
|
83
85
|
response.body = response['Location']
|
84
86
|
status
|
85
87
|
end
|
@@ -104,11 +106,17 @@ module DAV4Rack
|
|
104
106
|
def mkcol
|
105
107
|
resource.lock_check
|
106
108
|
status = resource.make_collection
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
xml.
|
109
|
+
gen_url = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created
|
110
|
+
if(resource.use_compat_mkcol_response?)
|
111
|
+
multistatus do |xml|
|
112
|
+
xml.response do
|
113
|
+
xml.href gen_url
|
114
|
+
xml.status "#{http_version} #{status.status_line}"
|
115
|
+
end
|
111
116
|
end
|
117
|
+
else
|
118
|
+
response['Location'] = gen_url
|
119
|
+
status
|
112
120
|
end
|
113
121
|
end
|
114
122
|
|
@@ -132,6 +140,7 @@ module DAV4Rack
|
|
132
140
|
elsif(destination == resource.public_path)
|
133
141
|
Forbidden
|
134
142
|
else
|
143
|
+
collection = resource.collection?
|
135
144
|
dest = resource_class.new(destination, clean_path(destination), @request, @response, @options.merge(:user => resource.user))
|
136
145
|
status = nil
|
137
146
|
if(args.include?(:copy))
|
@@ -140,12 +149,17 @@ module DAV4Rack
|
|
140
149
|
return Conflict unless depth.is_a?(Symbol) || depth > 1
|
141
150
|
status = resource.move(dest, overwrite)
|
142
151
|
end
|
143
|
-
response['Location'] = "#{scheme}://#{host}:#{port}#{dest
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
xml.
|
152
|
+
response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(dest)}" if status == Created
|
153
|
+
# RFC 2518
|
154
|
+
if collection
|
155
|
+
multistatus do |xml|
|
156
|
+
xml.response do
|
157
|
+
xml.href "#{scheme}://#{host}:#{port}#{url_format(status == Created ? dest : resource)}"
|
158
|
+
xml.status "#{http_version} #{status.status_line}"
|
159
|
+
end
|
148
160
|
end
|
161
|
+
else
|
162
|
+
status
|
149
163
|
end
|
150
164
|
end
|
151
165
|
end
|
@@ -159,13 +173,26 @@ module DAV4Rack
|
|
159
173
|
unless(request_document.xpath("//#{ns}propfind/#{ns}allprop").empty?)
|
160
174
|
names = resource.property_names
|
161
175
|
else
|
162
|
-
names =
|
176
|
+
names = (
|
177
|
+
ns.empty? ? request_document.remove_namespaces! : request_document
|
178
|
+
).xpath(
|
179
|
+
"//#{ns}propfind/#{ns}prop"
|
180
|
+
).children.find_all{ |item|
|
181
|
+
item.element? && item.name.start_with?(ns)
|
182
|
+
}.map{ |item|
|
183
|
+
item.name.sub("#{ns}::", '')
|
184
|
+
}
|
185
|
+
raise BadRequest if names.empty?
|
163
186
|
names = resource.property_names if names.empty?
|
164
187
|
end
|
165
188
|
multistatus do |xml|
|
166
189
|
find_resources.each do |resource|
|
167
190
|
xml.response do
|
168
|
-
|
191
|
+
unless(resource.propstat_relative_path)
|
192
|
+
xml.href "#{scheme}://#{host}:#{port}#{url_format(resource)}"
|
193
|
+
else
|
194
|
+
xml.href url_format(resource)
|
195
|
+
end
|
169
196
|
propstats(xml, get_properties(resource, names))
|
170
197
|
end
|
171
198
|
end
|
@@ -184,7 +211,7 @@ module DAV4Rack
|
|
184
211
|
multistatus do |xml|
|
185
212
|
find_resources.each do |resource|
|
186
213
|
xml.response do
|
187
|
-
xml.href "#{scheme}://#{host}:#{port}#{
|
214
|
+
xml.href "#{scheme}://#{host}:#{port}#{url_format(resource)}"
|
188
215
|
propstats(xml, set_properties(resource, prop_set))
|
189
216
|
end
|
190
217
|
end
|
@@ -386,9 +413,9 @@ module DAV4Rack
|
|
386
413
|
|
387
414
|
# pattern:: XPath pattern
|
388
415
|
# Search XML document for given XPath
|
416
|
+
# TODO: Stripping namespaces not so great
|
389
417
|
def request_match(pattern)
|
390
|
-
|
391
|
-
request_document.xpath(pattern, request_document.root.namespaces)
|
418
|
+
request_document.remove_namespaces!.xpath(pattern, request_document.root.namespaces)
|
392
419
|
end
|
393
420
|
|
394
421
|
# root_type:: Root tag name
|
@@ -396,14 +423,20 @@ module DAV4Rack
|
|
396
423
|
def render_xml(root_type)
|
397
424
|
raise ArgumentError.new 'Expecting block' unless block_given?
|
398
425
|
doc = Nokogiri::XML::Builder.new do |xml_base|
|
399
|
-
xml_base.send(root_type.to_s, 'xmlns:D' => 'DAV:') do
|
426
|
+
xml_base.send(root_type.to_s, {'xmlns:D' => 'DAV:'}.merge(resource.root_xml_attributes)) do
|
400
427
|
xml_base.parent.namespace = xml_base.parent.namespace_definitions.first
|
401
428
|
xml = xml_base['D']
|
402
429
|
yield xml
|
403
430
|
end
|
404
431
|
end
|
405
|
-
|
406
|
-
|
432
|
+
|
433
|
+
if(@options[:pretty_xml])
|
434
|
+
response.body = doc.to_xml
|
435
|
+
else
|
436
|
+
response.body = doc.to_xml(
|
437
|
+
:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML
|
438
|
+
)
|
439
|
+
end
|
407
440
|
response["Content-Type"] = 'text/xml; charset="utf-8"'
|
408
441
|
response["Content-Length"] = response.body.size.to_s
|
409
442
|
end
|
@@ -422,7 +455,7 @@ module DAV4Rack
|
|
422
455
|
def response_errors(xml, errors)
|
423
456
|
for path, status in errors
|
424
457
|
xml.response do
|
425
|
-
xml.href "#{scheme}://#{host}:#{port}#{path}"
|
458
|
+
xml.href "#{scheme}://#{host}:#{port}#{URI.escape(path)}"
|
426
459
|
xml.status "#{http_version} #{status.status_line}"
|
427
460
|
end
|
428
461
|
end
|
@@ -440,7 +473,7 @@ module DAV4Rack
|
|
440
473
|
rescue Unauthorized => u
|
441
474
|
raise u
|
442
475
|
rescue Status
|
443
|
-
stats[
|
476
|
+
stats[$!.class] << name
|
444
477
|
end
|
445
478
|
end
|
446
479
|
stats
|
@@ -457,7 +490,7 @@ module DAV4Rack
|
|
457
490
|
rescue Unauthorized => u
|
458
491
|
raise u
|
459
492
|
rescue Status
|
460
|
-
stats[
|
493
|
+
stats[$!.class] << name
|
461
494
|
end
|
462
495
|
end
|
463
496
|
stats
|
@@ -472,7 +505,9 @@ module DAV4Rack
|
|
472
505
|
xml.propstat do
|
473
506
|
xml.prop do
|
474
507
|
for name, value in props
|
475
|
-
if(value.is_a?(Nokogiri::XML::
|
508
|
+
if(value.is_a?(Nokogiri::XML::DocumentFragment))
|
509
|
+
xml.__send__ :insert, value
|
510
|
+
elsif(value.is_a?(Nokogiri::XML::Node))
|
476
511
|
xml.send(name) do
|
477
512
|
xml_convert(xml, value)
|
478
513
|
end
|
@@ -105,41 +105,71 @@ module DAV4Rack
|
|
105
105
|
# HTTP COPY request.
|
106
106
|
#
|
107
107
|
# Copy this resource to given destination resource.
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
108
|
+
# Copy this resource to given destination resource.
|
109
|
+
def copy(dest, overwrite)
|
110
|
+
if(collection?)
|
111
|
+
if(dest.exist?)
|
112
|
+
if(dest.collection? && overwrite)
|
113
|
+
FileUtils.cp_r(file_path, dest.send(:file_path))
|
114
|
+
Created
|
115
|
+
else
|
116
|
+
if(overwrite)
|
117
|
+
FileUtils.rm(dest.send(:file_path))
|
118
|
+
FileUtils.cp_r(file_path, dest.send(:file_path))
|
119
|
+
NoContent
|
120
|
+
else
|
121
|
+
PreconditionFailed
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
FileUtils.cp_r(file_path, dest.send(:file_path))
|
126
|
+
Created
|
127
|
+
end
|
115
128
|
else
|
116
|
-
|
117
|
-
if(exists && !overwrite)
|
129
|
+
if(dest.exist? && !overwrite)
|
118
130
|
PreconditionFailed
|
119
131
|
else
|
120
|
-
|
121
|
-
dest.
|
132
|
+
if(File.directory?(File.dirname(dest.send(:file_path))))
|
133
|
+
new = !dest.exist?
|
134
|
+
if(dest.collection? && dest.exist?)
|
135
|
+
FileUtils.rm_rf(dest.send(:file_path))
|
136
|
+
end
|
137
|
+
FileUtils.cp(file_path, dest.send(:file_path).sub(/\/$/, ''))
|
138
|
+
new ? Created : NoContent
|
139
|
+
else
|
140
|
+
Conflict
|
122
141
|
end
|
123
|
-
exists ? NoContent : Created
|
124
142
|
end
|
125
143
|
end
|
126
144
|
end
|
127
|
-
|
145
|
+
|
128
146
|
# HTTP MOVE request.
|
129
147
|
#
|
130
148
|
# Move this resource to given destination resource.
|
131
149
|
def move(*args)
|
132
|
-
copy(*args)
|
133
|
-
delete
|
134
|
-
|
150
|
+
result = copy(*args)
|
151
|
+
delete if [Created, NoContent].include?(result)
|
152
|
+
result
|
135
153
|
end
|
136
154
|
|
137
155
|
# HTTP MKCOL request.
|
138
156
|
#
|
139
157
|
# Create this resource as collection.
|
140
158
|
def make_collection
|
141
|
-
|
142
|
-
|
159
|
+
if(request.body.read.to_s == '')
|
160
|
+
if(File.directory?(file_path))
|
161
|
+
MethodNotAllowed
|
162
|
+
else
|
163
|
+
if(File.directory?(File.dirname(file_path)))
|
164
|
+
Dir.mkdir(file_path)
|
165
|
+
Created
|
166
|
+
else
|
167
|
+
Conflict
|
168
|
+
end
|
169
|
+
end
|
170
|
+
else
|
171
|
+
UnsupportedMediaType
|
172
|
+
end
|
143
173
|
end
|
144
174
|
|
145
175
|
# Write to this resource from given IO.
|
@@ -157,7 +187,50 @@ module DAV4Rack
|
|
157
187
|
File.unlink(tempfile) rescue nil
|
158
188
|
end
|
159
189
|
|
160
|
-
|
190
|
+
# name:: String - Property name
|
191
|
+
# Returns the value of the given property
|
192
|
+
def get_property(name)
|
193
|
+
super || custom_props(name)
|
194
|
+
end
|
195
|
+
|
196
|
+
# name:: String - Property name
|
197
|
+
# value:: New value
|
198
|
+
# Set the property to the given value
|
199
|
+
def set_property(name, value)
|
200
|
+
super || set_custom_props(name,value)
|
201
|
+
end
|
202
|
+
|
203
|
+
protected
|
204
|
+
|
205
|
+
def set_custom_props(key,val)
|
206
|
+
prop_hash[key.to_sym] = val
|
207
|
+
File.open(prop_path, 'w') do |file|
|
208
|
+
file.write(YAML.dump(prop_hash))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def custom_props(key)
|
213
|
+
prop_hash[key.to_sym]
|
214
|
+
end
|
215
|
+
|
216
|
+
def prop_path
|
217
|
+
path = File.join(root, '.props', File.dirname(file_path), File.basename(file_path))
|
218
|
+
unless(File.directory?(File.dirname(path)))
|
219
|
+
FileUtils.mkdir_p(File.dirname(path))
|
220
|
+
end
|
221
|
+
path
|
222
|
+
end
|
223
|
+
|
224
|
+
def prop_hash
|
225
|
+
unless(@_prop_hash)
|
226
|
+
if(File.exists?(prop_path))
|
227
|
+
@_prop_hash = YAML.load(File.read(prop_path))
|
228
|
+
else
|
229
|
+
@_prop_hash = {}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
@_prop_hash
|
233
|
+
end
|
161
234
|
|
162
235
|
def authenticate(user, pass)
|
163
236
|
if(options[:username])
|
@@ -172,7 +245,7 @@ module DAV4Rack
|
|
172
245
|
end
|
173
246
|
|
174
247
|
def file_path
|
175
|
-
root
|
248
|
+
File.join(root, path)
|
176
249
|
end
|
177
250
|
|
178
251
|
def stat
|
data/lib/dav4rack/handler.rb
CHANGED
@@ -24,7 +24,8 @@ module DAV4Rack
|
|
24
24
|
|
25
25
|
controller = nil
|
26
26
|
begin
|
27
|
-
|
27
|
+
controller_class = @options[:controller_class] || Controller
|
28
|
+
controller = controller_class.new(request, response, @options.dup)
|
28
29
|
controller.authenticate
|
29
30
|
res = controller.send(request.request_method.downcase)
|
30
31
|
response.status = res.code if res.respond_to?(:code)
|
data/lib/dav4rack/resource.rb
CHANGED
@@ -16,7 +16,8 @@ module DAV4Rack
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class Resource
|
19
|
-
attr_reader :path, :options, :public_path, :request,
|
19
|
+
attr_reader :path, :options, :public_path, :request,
|
20
|
+
:response, :propstat_relative_path, :root_xml_attributes
|
20
21
|
attr_accessor :user
|
21
22
|
@@blocks = {}
|
22
23
|
|
@@ -71,6 +72,8 @@ module DAV4Rack
|
|
71
72
|
]
|
72
73
|
@public_path = public_path.dup
|
73
74
|
@path = path.dup
|
75
|
+
@propstat_relative_path = !!options.delete(:propstat_relative_path)
|
76
|
+
@root_xml_attributes = options.delete(:root_xml_attributes) || {}
|
74
77
|
@request = request
|
75
78
|
@response = response
|
76
79
|
unless(options.has_key?(:lock_class))
|
@@ -137,22 +140,23 @@ module DAV4Rack
|
|
137
140
|
|
138
141
|
# Return the creation time.
|
139
142
|
def creation_date
|
140
|
-
NotImplemented
|
143
|
+
raise NotImplemented
|
141
144
|
end
|
142
145
|
|
143
146
|
# Return the time of last modification.
|
144
147
|
def last_modified
|
145
|
-
NotImplemented
|
148
|
+
raise NotImplemented
|
146
149
|
end
|
147
150
|
|
148
151
|
# Set the time of last modification.
|
149
152
|
def last_modified=(time)
|
150
|
-
|
153
|
+
# Is this correct?
|
154
|
+
raise NotImplemented
|
151
155
|
end
|
152
156
|
|
153
157
|
# Return an Etag, an unique hash value for this resource.
|
154
158
|
def etag
|
155
|
-
NotImplemented
|
159
|
+
raise NotImplemented
|
156
160
|
end
|
157
161
|
|
158
162
|
# Return the resource type. Generally only used to specify
|
@@ -163,12 +167,12 @@ module DAV4Rack
|
|
163
167
|
|
164
168
|
# Return the mime type of this resource.
|
165
169
|
def content_type
|
166
|
-
NotImplemented
|
170
|
+
raise NotImplemented
|
167
171
|
end
|
168
172
|
|
169
173
|
# Return the size in bytes for this resource.
|
170
174
|
def content_length
|
171
|
-
NotImplemented
|
175
|
+
raise NotImplemented
|
172
176
|
end
|
173
177
|
|
174
178
|
# HTTP GET request.
|
@@ -347,7 +351,7 @@ module DAV4Rack
|
|
347
351
|
case name
|
348
352
|
when 'resourcetype' then resource_type
|
349
353
|
when 'displayname' then display_name
|
350
|
-
when 'creationdate' then creation_date.xmlschema
|
354
|
+
when 'creationdate' then use_ms_compat_creationdate? ? creation_date.httpdate : creation_date.xmlschema
|
351
355
|
when 'getcontentlength' then content_length.to_s
|
352
356
|
when 'getcontenttype' then content_type
|
353
357
|
when 'getetag' then etag
|
@@ -365,8 +369,6 @@ module DAV4Rack
|
|
365
369
|
when 'getetag' then self.etag = value
|
366
370
|
when 'getlastmodified' then self.last_modified = Time.httpdate(value)
|
367
371
|
end
|
368
|
-
rescue ArgumentError
|
369
|
-
Conflict
|
370
372
|
end
|
371
373
|
|
372
374
|
# name:: Property name
|
@@ -390,9 +392,17 @@ module DAV4Rack
|
|
390
392
|
|
391
393
|
# Return parent of this resource
|
392
394
|
def parent
|
393
|
-
|
394
|
-
|
395
|
-
|
395
|
+
unless(@path.to_s.empty?)
|
396
|
+
self.class.new(
|
397
|
+
File.split(@public_path).first,
|
398
|
+
File.split(@path).first,
|
399
|
+
@request,
|
400
|
+
@response,
|
401
|
+
@options.merge(
|
402
|
+
:user => @user
|
403
|
+
)
|
404
|
+
)
|
405
|
+
end
|
396
406
|
end
|
397
407
|
|
398
408
|
# Return list of descendants
|
@@ -405,8 +415,6 @@ module DAV4Rack
|
|
405
415
|
list
|
406
416
|
end
|
407
417
|
|
408
|
-
protected
|
409
|
-
|
410
418
|
# Index page template for GETs on collection
|
411
419
|
def index_page
|
412
420
|
'<html><head> <title>%s</title>
|
@@ -425,10 +433,30 @@ module DAV4Rack
|
|
425
433
|
%r{cyberduck}i,
|
426
434
|
%r{konqueror}i
|
427
435
|
].any? do |regexp|
|
428
|
-
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']) =~ regexp
|
436
|
+
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp
|
429
437
|
end
|
430
438
|
end
|
431
|
-
|
439
|
+
|
440
|
+
def use_compat_mkcol_response?
|
441
|
+
@options[:compat_mkcol] || @options[:compat_all]
|
442
|
+
end
|
443
|
+
|
444
|
+
# Returns true if using an MS client
|
445
|
+
def use_ms_compat_creationdate?
|
446
|
+
if(@options[:compat_ms_mangled_creationdate] || @options[:compat_all])
|
447
|
+
is_ms_client?
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
# Basic user agent testing for MS authored client
|
452
|
+
def is_ms_client?
|
453
|
+
[%r{microsoft-webdav}i, %r{microsoft office}i].any? do |regexp|
|
454
|
+
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
protected
|
459
|
+
|
432
460
|
# Returns authentication credentials if available in form of [username,password]
|
433
461
|
# TODO: Add support for digest
|
434
462
|
def auth_credentials
|
data/lib/dav4rack/version.rb
CHANGED
data/spec/handler_spec.rb
CHANGED
@@ -4,6 +4,7 @@ require 'rubygems'
|
|
4
4
|
require 'dav4rack'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'nokogiri'
|
7
|
+
require 'rspec'
|
7
8
|
|
8
9
|
describe DAV4Rack::Handler do
|
9
10
|
DOC_ROOT = File.expand_path(File.dirname(__FILE__) + '/htdocs')
|
@@ -48,9 +49,7 @@ describe DAV4Rack::Handler do
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def url_escape(string)
|
51
|
-
|
52
|
-
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
53
|
-
end.tr(' ', '+')
|
52
|
+
URI.escape(string)
|
54
53
|
end
|
55
54
|
|
56
55
|
def response_xml
|
@@ -136,8 +135,7 @@ describe DAV4Rack::Handler do
|
|
136
135
|
end
|
137
136
|
|
138
137
|
it 'should delete recursively' do
|
139
|
-
mkcol('/folder')
|
140
|
-
multi_status_created.should eq true
|
138
|
+
mkcol('/folder').should be_created
|
141
139
|
put('/folder/a', :input => 'body').should be_created
|
142
140
|
put('/folder/b', :input => 'body').should be_created
|
143
141
|
|
@@ -159,15 +157,13 @@ describe DAV4Rack::Handler do
|
|
159
157
|
|
160
158
|
it 'should copy a single resource' do
|
161
159
|
put('/test', :input => 'body').should be_created
|
162
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy')
|
163
|
-
multi_status_no_content.should eq true
|
160
|
+
copy('/test', 'HTTP_DESTINATION' => '/copy').should be_created
|
164
161
|
get('/copy').body.should == 'body'
|
165
162
|
end
|
166
163
|
|
167
164
|
it 'should copy a resource with escaped characters' do
|
168
165
|
put(url_escape('/a b'), :input => 'body').should be_created
|
169
|
-
copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c'))
|
170
|
-
multi_status_no_content.should eq true
|
166
|
+
copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c')).should be_created
|
171
167
|
get(url_escape('/a c')).should be_ok
|
172
168
|
response.body.should == 'body'
|
173
169
|
end
|
@@ -175,39 +171,32 @@ describe DAV4Rack::Handler do
|
|
175
171
|
it 'should deny a copy without overwrite' do
|
176
172
|
put('/test', :input => 'body').should be_created
|
177
173
|
put('/copy', :input => 'copy').should be_created
|
178
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F')
|
179
|
-
|
180
|
-
multistatus_response('/D:href').first.text.should =~ /http:\/\/localhost(:\d+)?\/test/
|
181
|
-
multistatus_response('/D:status').first.text.should match(/412 Precondition Failed/)
|
182
|
-
|
174
|
+
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F').should be_precondition_failed
|
183
175
|
get('/copy').body.should == 'copy'
|
184
176
|
end
|
185
177
|
|
186
178
|
it 'should allow a copy with overwrite' do
|
187
179
|
put('/test', :input => 'body').should be_created
|
188
180
|
put('/copy', :input => 'copy').should be_created
|
189
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T')
|
190
|
-
multi_status_no_content.should eq true
|
181
|
+
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T').should be_no_content
|
191
182
|
get('/copy').body.should == 'body'
|
192
183
|
end
|
193
184
|
|
194
185
|
it 'should copy a collection' do
|
195
|
-
mkcol('/folder')
|
196
|
-
multi_status_created.should eq true
|
186
|
+
mkcol('/folder').should be_created
|
197
187
|
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
198
|
-
|
188
|
+
multi_status_created.should eq true
|
199
189
|
propfind('/copy', :input => propfind_xml(:resourcetype))
|
200
190
|
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
201
191
|
end
|
202
192
|
|
203
193
|
it 'should copy a collection resursively' do
|
204
|
-
mkcol('/folder')
|
205
|
-
multi_status_created.should eq true
|
194
|
+
mkcol('/folder').should be_created
|
206
195
|
put('/folder/a', :input => 'A').should be_created
|
207
196
|
put('/folder/b', :input => 'B').should be_created
|
208
197
|
|
209
198
|
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
210
|
-
|
199
|
+
multi_status_created.should eq true
|
211
200
|
propfind('/copy', :input => propfind_xml(:resourcetype))
|
212
201
|
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
213
202
|
get('/copy/a').body.should == 'A'
|
@@ -215,13 +204,12 @@ describe DAV4Rack::Handler do
|
|
215
204
|
end
|
216
205
|
|
217
206
|
it 'should move a collection recursively' do
|
218
|
-
mkcol('/folder')
|
219
|
-
multi_status_created.should eq true
|
207
|
+
mkcol('/folder').should be_created
|
220
208
|
put('/folder/a', :input => 'A').should be_created
|
221
209
|
put('/folder/b', :input => 'B').should be_created
|
222
210
|
|
223
211
|
move('/folder', 'HTTP_DESTINATION' => '/move')
|
224
|
-
|
212
|
+
multi_status_created.should eq true
|
225
213
|
propfind('/move', :input => propfind_xml(:resourcetype))
|
226
214
|
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
227
215
|
|
@@ -232,15 +220,13 @@ describe DAV4Rack::Handler do
|
|
232
220
|
end
|
233
221
|
|
234
222
|
it 'should create a collection' do
|
235
|
-
mkcol('/folder')
|
236
|
-
multi_status_created.should eq true
|
223
|
+
mkcol('/folder').should be_created
|
237
224
|
propfind('/folder', :input => propfind_xml(:resourcetype))
|
238
225
|
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
239
226
|
end
|
240
227
|
|
241
228
|
it 'should return full urls after creating a collection' do
|
242
|
-
mkcol('/folder')
|
243
|
-
multi_status_created.should eq true
|
229
|
+
mkcol('/folder').should be_created
|
244
230
|
propfind('/folder', :input => propfind_xml(:resourcetype))
|
245
231
|
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
246
232
|
multistatus_response('/D:href').first.text.should =~ /http:\/\/localhost(:\d+)?\/folder/
|
metadata
CHANGED
@@ -1,80 +1,57 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: dav4rack
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.11
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 10
|
10
|
-
version: 0.2.10
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Chris Roberts
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-09-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: nokogiri
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &15276900 !ruby/object:Gem::Requirement
|
25
17
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 3
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 4
|
33
|
-
- 2
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
34
21
|
version: 1.4.2
|
35
22
|
type: :runtime
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: uuidtools
|
39
23
|
prerelease: false
|
40
|
-
|
24
|
+
version_requirements: *15276900
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: uuidtools
|
27
|
+
requirement: &15293080 !ruby/object:Gem::Requirement
|
41
28
|
none: false
|
42
|
-
requirements:
|
29
|
+
requirements:
|
43
30
|
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
hash: 9
|
46
|
-
segments:
|
47
|
-
- 2
|
48
|
-
- 1
|
49
|
-
- 1
|
31
|
+
- !ruby/object:Gem::Version
|
50
32
|
version: 2.1.1
|
51
33
|
type: :runtime
|
52
|
-
version_requirements: *id002
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
|
-
name: rack
|
55
34
|
prerelease: false
|
56
|
-
|
35
|
+
version_requirements: *15293080
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rack
|
38
|
+
requirement: &15292420 !ruby/object:Gem::Requirement
|
57
39
|
none: false
|
58
|
-
requirements:
|
59
|
-
- -
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
hash: 19
|
62
|
-
segments:
|
63
|
-
- 1
|
64
|
-
- 1
|
65
|
-
- 0
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
66
43
|
version: 1.1.0
|
67
44
|
type: :runtime
|
68
|
-
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *15292420
|
69
47
|
description: WebDAV handler for Rack
|
70
48
|
email: chrisroberts.code@gmail.com
|
71
|
-
executables:
|
49
|
+
executables:
|
72
50
|
- dav4rack
|
73
51
|
extensions: []
|
74
|
-
|
75
|
-
extra_rdoc_files:
|
52
|
+
extra_rdoc_files:
|
76
53
|
- README.rdoc
|
77
|
-
files:
|
54
|
+
files:
|
78
55
|
- .gitignore
|
79
56
|
- LICENSE
|
80
57
|
- dav4rack.gemspec
|
@@ -95,39 +72,29 @@ files:
|
|
95
72
|
- bin/dav4rack
|
96
73
|
- spec/handler_spec.rb
|
97
74
|
- README.rdoc
|
98
|
-
has_rdoc: true
|
99
75
|
homepage: http://github.com/chrisroberts/dav4rack
|
100
76
|
licenses: []
|
101
|
-
|
102
77
|
post_install_message:
|
103
78
|
rdoc_options: []
|
104
|
-
|
105
|
-
require_paths:
|
79
|
+
require_paths:
|
106
80
|
- lib
|
107
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
82
|
none: false
|
109
|
-
requirements:
|
110
|
-
- -
|
111
|
-
- !ruby/object:Gem::Version
|
112
|
-
|
113
|
-
|
114
|
-
- 0
|
115
|
-
version: "0"
|
116
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
88
|
none: false
|
118
|
-
requirements:
|
119
|
-
- -
|
120
|
-
- !ruby/object:Gem::Version
|
121
|
-
|
122
|
-
segments:
|
123
|
-
- 0
|
124
|
-
version: "0"
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
125
93
|
requirements: []
|
126
|
-
|
127
94
|
rubyforge_project:
|
128
|
-
rubygems_version: 1.
|
95
|
+
rubygems_version: 1.8.17
|
129
96
|
signing_key:
|
130
97
|
specification_version: 3
|
131
98
|
summary: WebDAV handler for Rack
|
132
99
|
test_files: []
|
133
|
-
|
100
|
+
has_rdoc: true
|