rack_dav 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +24 -17
- data/README.md +6 -2
- data/lib/rack_dav/controller.rb +81 -22
- data/lib/rack_dav/file_resource.rb +31 -0
- data/lib/rack_dav/resource.rb +3 -1
- data/lib/rack_dav/version.rb +1 -1
- data/rack_dav.gemspec +1 -0
- data/spec/controller_spec.rb +119 -40
- metadata +29 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 849a36645773dd942cbf87d7e41a685ff5814157
|
4
|
+
data.tar.gz: 5269572bc9f41e2c6fa73eb23f1e31a7ac17aa58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6e6183c272dbd1c9c292c2de86fda4afef9282db0e190cc9fa31f1d7d0af7d3d175e917442f83d11c53231bf07f676fa4990bf722781a385f29d2dfcbdab299
|
7
|
+
data.tar.gz: c591239bce9cff7162639b6bf1b24b28b4e1d193a87eafda61c1aa0477085f491683270622cdd78eaeaa9855fb2c1a039b0816bfa0da7b66e986b11c0c15b25c
|
data/Gemfile.lock
CHANGED
@@ -1,26 +1,33 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rack_dav (0.
|
5
|
-
|
6
|
-
|
4
|
+
rack_dav (0.4.0)
|
5
|
+
ffi-xattr (~> 0.1)
|
6
|
+
nokogiri (~> 1.5)
|
7
|
+
rack (~> 1.4)
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: http://rubygems.org/
|
10
11
|
specs:
|
11
|
-
diff-lcs (1.2.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
diff-lcs (1.2.5)
|
13
|
+
ffi (1.9.10)
|
14
|
+
ffi (1.9.10-java)
|
15
|
+
ffi-xattr (0.1.2)
|
16
|
+
ffi
|
17
|
+
mini_portile (0.6.2)
|
18
|
+
nokogiri (1.6.6.2)
|
19
|
+
mini_portile (~> 0.6.0)
|
20
|
+
nokogiri (1.6.6.2-java)
|
21
|
+
rack (1.6.4)
|
22
|
+
rake (0.9.6)
|
23
|
+
rspec (2.99.0)
|
24
|
+
rspec-core (~> 2.99.0)
|
25
|
+
rspec-expectations (~> 2.99.0)
|
26
|
+
rspec-mocks (~> 2.99.0)
|
27
|
+
rspec-core (2.99.2)
|
28
|
+
rspec-expectations (2.99.2)
|
22
29
|
diff-lcs (>= 1.1.3, < 2.0)
|
23
|
-
rspec-mocks (2.
|
30
|
+
rspec-mocks (2.99.3)
|
24
31
|
|
25
32
|
PLATFORMS
|
26
33
|
java
|
@@ -28,5 +35,5 @@ PLATFORMS
|
|
28
35
|
|
29
36
|
DEPENDENCIES
|
30
37
|
rack_dav!
|
31
|
-
rake (
|
32
|
-
rspec (
|
38
|
+
rake (~> 0.9)
|
39
|
+
rspec (~> 2.11)
|
data/README.md
CHANGED
@@ -29,9 +29,9 @@ script looks like this:
|
|
29
29
|
|
30
30
|
require 'rubygems'
|
31
31
|
require 'rack_dav'
|
32
|
-
|
32
|
+
|
33
33
|
use Rack::CommonLogger
|
34
|
-
|
34
|
+
|
35
35
|
run RackDAV::Handler.new(:root => '/path/to/docs')
|
36
36
|
|
37
37
|
## Implementing your own WebDAV resource
|
@@ -85,6 +85,10 @@ to retrieve and change the resources:
|
|
85
85
|
|
86
86
|
* __make\_collection__: Create this resource as collection.
|
87
87
|
|
88
|
+
* __set_custom_property(name, value)__: Set a custom property on the resource. If the value is nil, delete the custom property.
|
89
|
+
|
90
|
+
* __get_custom_property(name)__: Return the value of the named custom property.
|
91
|
+
|
88
92
|
* __lock(locktoken, timeout, lockscope=nil, locktype=nil, owner=nil)__: Lock this resource.
|
89
93
|
If scope, type and owner are nil, refresh the given lock.
|
90
94
|
|
data/lib/rack_dav/controller.rb
CHANGED
@@ -61,6 +61,7 @@ module RackDAV
|
|
61
61
|
map_exceptions do
|
62
62
|
resource.put
|
63
63
|
end
|
64
|
+
response.status = Created
|
64
65
|
end
|
65
66
|
|
66
67
|
def post
|
@@ -70,6 +71,8 @@ module RackDAV
|
|
70
71
|
end
|
71
72
|
|
72
73
|
def delete
|
74
|
+
raise NotFound if not resource.exist?
|
75
|
+
|
73
76
|
delete_recursive(resource, errors = [])
|
74
77
|
|
75
78
|
if errors.empty?
|
@@ -82,6 +85,10 @@ module RackDAV
|
|
82
85
|
end
|
83
86
|
|
84
87
|
def mkcol
|
88
|
+
# Reject message bodies - RFC2518:8.3.1
|
89
|
+
body = @request.body.read(8)
|
90
|
+
fail UnsupportedMediaType if !body.nil? && body.length > 0
|
91
|
+
|
85
92
|
map_exceptions do
|
86
93
|
resource.make_collection
|
87
94
|
end
|
@@ -98,6 +105,8 @@ module RackDAV
|
|
98
105
|
raise Forbidden if destination == resource.path
|
99
106
|
|
100
107
|
dest = resource_class.new(destination, @request, @response, @options)
|
108
|
+
raise PreconditionFailed if dest.exist? && !overwrite
|
109
|
+
|
101
110
|
dest = dest.child(resource.name) if dest.collection?
|
102
111
|
|
103
112
|
dest_existed = dest.exist?
|
@@ -125,9 +134,10 @@ module RackDAV
|
|
125
134
|
raise Forbidden if destination == resource.path
|
126
135
|
|
127
136
|
dest = resource_class.new(destination, @request, @response, @options)
|
128
|
-
|
137
|
+
raise PreconditionFailed if dest.exist? && !overwrite
|
129
138
|
|
130
139
|
dest_existed = dest.exist?
|
140
|
+
dest = dest.child(resource.name) if dest.collection?
|
131
141
|
|
132
142
|
raise Conflict if depth <= 1
|
133
143
|
|
@@ -149,11 +159,26 @@ module RackDAV
|
|
149
159
|
raise NotFound if not resource.exist?
|
150
160
|
|
151
161
|
if not request_match("/d:propfind/d:allprop").empty?
|
152
|
-
|
162
|
+
nodes = all_prop_nodes
|
153
163
|
else
|
154
|
-
|
155
|
-
|
156
|
-
|
164
|
+
nodes = request_match("/d:propfind/d:prop/*")
|
165
|
+
nodes = all_prop_nodes if nodes.empty?
|
166
|
+
end
|
167
|
+
|
168
|
+
nodes.each do |n|
|
169
|
+
# Don't allow empty namespace declarations
|
170
|
+
# See litmus props test 3
|
171
|
+
raise BadRequest if n.namespace.nil? && n.namespace_definitions.empty?
|
172
|
+
|
173
|
+
# Set a blank namespace if one is included in the request
|
174
|
+
# See litmus props test 16
|
175
|
+
# <propfind xmlns="DAV:"><prop><nonamespace xmlns=""/></prop></propfind>
|
176
|
+
if n.namespace.nil?
|
177
|
+
nd = n.namespace_definitions.first
|
178
|
+
if nd.prefix.nil? && nd.href.empty?
|
179
|
+
n.add_namespace(nil, '')
|
180
|
+
end
|
181
|
+
end
|
157
182
|
end
|
158
183
|
|
159
184
|
multistatus do |xml|
|
@@ -161,7 +186,7 @@ module RackDAV
|
|
161
186
|
resource.path.gsub!(/\/\//, '/')
|
162
187
|
xml.response do
|
163
188
|
xml.href "http://#{host}#{url_escape resource.path}"
|
164
|
-
propstats xml, get_properties(resource,
|
189
|
+
propstats xml, get_properties(resource, nodes)
|
165
190
|
end
|
166
191
|
end
|
167
192
|
end
|
@@ -170,19 +195,28 @@ module RackDAV
|
|
170
195
|
def proppatch
|
171
196
|
raise NotFound if not resource.exist?
|
172
197
|
|
173
|
-
|
174
|
-
|
198
|
+
nodes = request_match("/d:propertyupdate[d:remove/d:prop/* or d:set/d:prop/*]//d:prop/*")
|
199
|
+
|
200
|
+
# Set a blank namespace if one is included in the request
|
201
|
+
# See litmus props test 15
|
202
|
+
# <propertyupdate xmlns="DAV:"><set>
|
203
|
+
# <prop><nonamespace xmlns="">randomvalue</nonamespace></prop>
|
204
|
+
# </set></propertyupdate>
|
205
|
+
nodes.each do |n|
|
206
|
+
nd = n.namespace_definitions.first
|
207
|
+
if !nd.nil? && nd.prefix.nil? && nd.href.empty?
|
208
|
+
n.add_namespace(nil, '')
|
209
|
+
end
|
210
|
+
end
|
175
211
|
|
176
212
|
multistatus do |xml|
|
177
213
|
for resource in find_resources
|
178
214
|
xml.response do
|
179
215
|
xml.href "http://#{host}#{resource.path}"
|
180
|
-
propstats xml, set_properties(resource,
|
216
|
+
propstats xml, set_properties(resource, nodes)
|
181
217
|
end
|
182
218
|
end
|
183
219
|
end
|
184
|
-
|
185
|
-
resource.save
|
186
220
|
end
|
187
221
|
|
188
222
|
def lock
|
@@ -310,6 +344,22 @@ module RackDAV
|
|
310
344
|
request_document.xpath(pattern, 'd' => 'DAV:')
|
311
345
|
end
|
312
346
|
|
347
|
+
def qualified_node_name(node)
|
348
|
+
node.namespace.nil? || node.namespace.prefix.nil? ? node.name : "#{node.namespace.prefix}:#{node.name}"
|
349
|
+
end
|
350
|
+
|
351
|
+
def qualified_property_name(node)
|
352
|
+
node.namespace.nil? || node.namespace.href == 'DAV:' ? node.name : "{#{node.namespace.href}}#{node.name}"
|
353
|
+
end
|
354
|
+
|
355
|
+
def all_prop_nodes
|
356
|
+
resource.property_names.map do |n|
|
357
|
+
node = Nokogiri::XML::Element.new(n, request_document)
|
358
|
+
node.add_namespace(nil, 'DAV:')
|
359
|
+
node
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
313
363
|
# Quick and dirty parsing of the WEBDAV Timeout header.
|
314
364
|
# Refuses infinity, rejects anything but Second- timeouts
|
315
365
|
#
|
@@ -371,29 +421,29 @@ module RackDAV
|
|
371
421
|
end
|
372
422
|
end
|
373
423
|
|
374
|
-
def get_properties(resource,
|
424
|
+
def get_properties(resource, nodes)
|
375
425
|
stats = Hash.new { |h, k| h[k] = [] }
|
376
|
-
for
|
426
|
+
for node in nodes
|
377
427
|
begin
|
378
428
|
map_exceptions do
|
379
|
-
stats[OK] << [
|
429
|
+
stats[OK] << [node, resource.get_property(qualified_property_name(node))]
|
380
430
|
end
|
381
431
|
rescue Status
|
382
|
-
stats[$!] <<
|
432
|
+
stats[$!] << node
|
383
433
|
end
|
384
434
|
end
|
385
435
|
stats
|
386
436
|
end
|
387
437
|
|
388
|
-
def set_properties(resource,
|
438
|
+
def set_properties(resource, nodes)
|
389
439
|
stats = Hash.new { |h, k| h[k] = [] }
|
390
|
-
for
|
440
|
+
for node in nodes
|
391
441
|
begin
|
392
442
|
map_exceptions do
|
393
|
-
stats[OK] << [
|
443
|
+
stats[OK] << [node, resource.set_property(qualified_property_name(node), node.text)]
|
394
444
|
end
|
395
445
|
rescue Status
|
396
|
-
stats[$!] <<
|
446
|
+
stats[$!] << node
|
397
447
|
end
|
398
448
|
end
|
399
449
|
stats
|
@@ -404,13 +454,22 @@ module RackDAV
|
|
404
454
|
for status, props in stats
|
405
455
|
xml.propstat do
|
406
456
|
xml.prop do
|
407
|
-
for
|
457
|
+
for node, value in props
|
408
458
|
if value.is_a?(Nokogiri::XML::Node)
|
409
|
-
xml.send(
|
459
|
+
xml.send(qualified_node_name(node).to_sym) do
|
410
460
|
rexml_convert(xml, value)
|
411
461
|
end
|
412
462
|
else
|
413
|
-
|
463
|
+
attrs = {}
|
464
|
+
unless node.namespace.nil?
|
465
|
+
unless node.namespace.prefix.nil?
|
466
|
+
attrs = { "xmlns:#{node.namespace.prefix}" => node.namespace.href }
|
467
|
+
else
|
468
|
+
attrs = { 'xmlns' => node.namespace.href }
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
xml.send(qualified_node_name(node).to_sym, value, attrs)
|
414
473
|
end
|
415
474
|
end
|
416
475
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'digest'
|
2
|
+
require 'ffi-xattr'
|
2
3
|
|
3
4
|
module RackDAV
|
4
5
|
|
@@ -65,6 +66,28 @@ module RackDAV
|
|
65
66
|
stat.size
|
66
67
|
end
|
67
68
|
|
69
|
+
def set_custom_property(name, value)
|
70
|
+
if value.nil? || value.empty?
|
71
|
+
begin
|
72
|
+
xattr.remove("rack_dav:#{name}")
|
73
|
+
rescue Errno::ENOATTR
|
74
|
+
# If the attribute being deleted doesn't exist, just do nothing
|
75
|
+
end
|
76
|
+
else
|
77
|
+
xattr["rack_dav:#{name}"] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_custom_property(name)
|
82
|
+
value = xattr["rack_dav:#{name}"]
|
83
|
+
raise HTTPStatus::NotFound if value.nil?
|
84
|
+
value
|
85
|
+
end
|
86
|
+
|
87
|
+
def list_custom_properties
|
88
|
+
xattr.list.select { |a| a.start_with?('rack_dav') }.map { |a| a.sub(/^rack_dav:/, '') }
|
89
|
+
end
|
90
|
+
|
68
91
|
# HTTP GET request.
|
69
92
|
#
|
70
93
|
# Write the content of the resource to the response.body.
|
@@ -119,6 +142,10 @@ module RackDAV
|
|
119
142
|
open(file_path, "rb") do |file|
|
120
143
|
dest.write(file)
|
121
144
|
end
|
145
|
+
|
146
|
+
list_custom_properties.each do |prop|
|
147
|
+
dest.set_custom_property(prop, get_custom_property(prop))
|
148
|
+
end
|
122
149
|
end
|
123
150
|
end
|
124
151
|
|
@@ -167,6 +194,10 @@ module RackDAV
|
|
167
194
|
@stat ||= File.stat(file_path)
|
168
195
|
end
|
169
196
|
|
197
|
+
def xattr
|
198
|
+
@xattr ||= Xattr.new(file_path)
|
199
|
+
end
|
200
|
+
|
170
201
|
def content_md5_pass?(env)
|
171
202
|
expected = env['HTTP_CONTENT_MD5'] or return true
|
172
203
|
|
data/lib/rack_dav/resource.rb
CHANGED
@@ -148,6 +148,7 @@ module RackDAV
|
|
148
148
|
when 'getcontenttype' then content_type
|
149
149
|
when 'getetag' then etag
|
150
150
|
when 'getlastmodified' then last_modified.httpdate
|
151
|
+
else self.get_custom_property(name) if self.respond_to?(:get_custom_property)
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
@@ -157,13 +158,14 @@ module RackDAV
|
|
157
158
|
when 'getcontenttype' then self.content_type = value
|
158
159
|
when 'getetag' then self.etag = value
|
159
160
|
when 'getlastmodified' then self.last_modified = Time.httpdate(value)
|
161
|
+
else self.set_custom_property(name, value) if self.respond_to?(:set_custom_property)
|
160
162
|
end
|
161
163
|
rescue ArgumentError
|
162
164
|
raise HTTPStatus::Conflict
|
163
165
|
end
|
164
166
|
|
165
167
|
def remove_property(name)
|
166
|
-
raise HTTPStatus::Forbidden
|
168
|
+
raise HTTPStatus::Forbidden if property_names.include?(name)
|
167
169
|
end
|
168
170
|
|
169
171
|
def parent
|
data/lib/rack_dav/version.rb
CHANGED
data/rack_dav.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_dependency("rack", "~> 1.4")
|
22
22
|
s.add_dependency('nokogiri', "~> 1.5")
|
23
|
+
s.add_dependency("ffi-xattr", "~> 0.1")
|
23
24
|
s.add_development_dependency("rspec", "~> 2.11")
|
24
25
|
s.add_development_dependency("rake","~> 0.9")
|
25
26
|
end
|
data/spec/controller_spec.rb
CHANGED
@@ -26,6 +26,12 @@ class Rack::MockResponse
|
|
26
26
|
|
27
27
|
end
|
28
28
|
|
29
|
+
if ENV['TRAVIS']
|
30
|
+
RSpec.configure do |c|
|
31
|
+
c.filter_run_excluding :has_xattr_support => true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
29
35
|
describe RackDAV::Handler do
|
30
36
|
|
31
37
|
DOC_ROOT = File.expand_path(File.dirname(__FILE__) + '/htdocs')
|
@@ -66,7 +72,7 @@ describe RackDAV::Handler do
|
|
66
72
|
|
67
73
|
describe "LOCK" do
|
68
74
|
before(:each) do
|
69
|
-
put("/test", :input => "body").should
|
75
|
+
put("/test", :input => "body").should be_created
|
70
76
|
lock("/test", :input => File.read(fixture("requests/lock.xml")))
|
71
77
|
end
|
72
78
|
|
@@ -78,7 +84,7 @@ describe RackDAV::Handler do
|
|
78
84
|
it "sets a compliant rack response" do
|
79
85
|
body = response.original_response.body
|
80
86
|
body.should be_a(Array)
|
81
|
-
body.
|
87
|
+
expect(body.size).to eq(1)
|
82
88
|
end
|
83
89
|
|
84
90
|
it "prints the lockdiscovery" do
|
@@ -128,7 +134,7 @@ describe RackDAV::Handler do
|
|
128
134
|
|
129
135
|
describe "UNLOCK" do
|
130
136
|
before(:each) do
|
131
|
-
put("/test", :input => "body").should
|
137
|
+
put("/test", :input => "body").should be_created
|
132
138
|
lock("/test", :input => File.read(fixture("requests/lock.xml"))).should be_ok
|
133
139
|
end
|
134
140
|
|
@@ -176,12 +182,12 @@ describe RackDAV::Handler do
|
|
176
182
|
|
177
183
|
describe "uri escaping" do
|
178
184
|
it "allows url escaped utf-8" do
|
179
|
-
put('/D%C3%B6ner').should
|
185
|
+
put('/D%C3%B6ner').should be_created
|
180
186
|
get('/D%C3%B6ner').should be_ok
|
181
187
|
end
|
182
188
|
|
183
189
|
it "allows url escaped iso-8859" do
|
184
|
-
put('/D%F6ner').should
|
190
|
+
put('/D%F6ner').should be_created
|
185
191
|
get('/D%F6ner').should be_ok
|
186
192
|
end
|
187
193
|
end
|
@@ -222,7 +228,7 @@ describe RackDAV::Handler do
|
|
222
228
|
end
|
223
229
|
|
224
230
|
it 'should be successful' do
|
225
|
-
response.should
|
231
|
+
response.should be_created
|
226
232
|
end
|
227
233
|
|
228
234
|
it 'should create the resource' do
|
@@ -233,7 +239,7 @@ describe RackDAV::Handler do
|
|
233
239
|
end
|
234
240
|
|
235
241
|
it 'should return headers' do
|
236
|
-
put('/test.html', :input => '<html/>').should
|
242
|
+
put('/test.html', :input => '<html/>').should be_created
|
237
243
|
head('/test.html').should be_ok
|
238
244
|
|
239
245
|
response.headers['etag'].should_not be_nil
|
@@ -251,25 +257,25 @@ describe RackDAV::Handler do
|
|
251
257
|
end
|
252
258
|
|
253
259
|
it 'should create a resource and allow its retrieval' do
|
254
|
-
put('/test', :input => 'body').should
|
260
|
+
put('/test', :input => 'body').should be_created
|
255
261
|
get('/test').should be_ok
|
256
262
|
response.body.should == 'body'
|
257
263
|
end
|
258
264
|
it 'should create and find a url with escaped characters' do
|
259
|
-
put(url_escape('/a b'), :input => 'body').should
|
265
|
+
put(url_escape('/a b'), :input => 'body').should be_created
|
260
266
|
get(url_escape('/a b')).should be_ok
|
261
267
|
response.body.should == 'body'
|
262
268
|
end
|
263
269
|
|
264
270
|
it 'should delete a single resource' do
|
265
|
-
put('/test', :input => 'body').should
|
271
|
+
put('/test', :input => 'body').should be_created
|
266
272
|
delete('/test').should be_no_content
|
267
273
|
end
|
268
274
|
|
269
275
|
it 'should delete recursively' do
|
270
276
|
mkcol('/folder').should be_created
|
271
|
-
put('/folder/a', :input => 'body').should
|
272
|
-
put('/folder/b', :input => 'body').should
|
277
|
+
put('/folder/a', :input => 'body').should be_created
|
278
|
+
put('/folder/b', :input => 'body').should be_created
|
273
279
|
|
274
280
|
delete('/folder').should be_no_content
|
275
281
|
get('/folder').should be_not_found
|
@@ -277,52 +283,59 @@ describe RackDAV::Handler do
|
|
277
283
|
get('/folder/b').should be_not_found
|
278
284
|
end
|
279
285
|
|
286
|
+
it 'should return not found when deleting a non-existent resource' do
|
287
|
+
delete('/not_found').should be_not_found
|
288
|
+
end
|
289
|
+
|
280
290
|
it 'should not allow copy to another domain' do
|
281
|
-
put('/test', :input => 'body').should
|
291
|
+
put('/test', :input => 'body').should be_created
|
282
292
|
copy('http://localhost/', 'HTTP_DESTINATION' => 'http://another/').should be_bad_gateway
|
283
293
|
end
|
284
294
|
|
285
295
|
it 'should not allow copy to the same resource' do
|
286
|
-
put('/test', :input => 'body').should
|
296
|
+
put('/test', :input => 'body').should be_created
|
287
297
|
copy('/test', 'HTTP_DESTINATION' => '/test').should be_forbidden
|
288
298
|
end
|
289
299
|
|
290
300
|
it 'should not allow an invalid destination uri' do
|
291
|
-
put('/test', :input => 'body').should
|
301
|
+
put('/test', :input => 'body').should be_created
|
292
302
|
copy('/test', 'HTTP_DESTINATION' => '%').should be_bad_request
|
293
303
|
end
|
294
304
|
|
295
305
|
it 'should copy a single resource' do
|
296
|
-
put('/test', :input => 'body').should
|
306
|
+
put('/test', :input => 'body').should be_created
|
297
307
|
copy('/test', 'HTTP_DESTINATION' => '/copy').should be_created
|
298
308
|
get('/copy').body.should == 'body'
|
299
309
|
end
|
300
310
|
|
301
311
|
it 'should copy a resource with escaped characters' do
|
302
|
-
put(url_escape('/a b'), :input => 'body').should
|
312
|
+
put(url_escape('/a b'), :input => 'body').should be_created
|
303
313
|
copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c')).should be_created
|
304
314
|
get(url_escape('/a c')).should be_ok
|
305
315
|
response.body.should == 'body'
|
306
316
|
end
|
307
317
|
|
308
318
|
it 'should deny a copy without overwrite' do
|
309
|
-
put('/test', :input => 'body').should
|
310
|
-
put('/copy', :input => 'copy').should
|
311
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F')
|
312
|
-
|
313
|
-
multistatus_response('/d:href').first.text.should == 'http://localhost/test'
|
314
|
-
multistatus_response('/d:status').first.text.should match(/412 Precondition Failed/)
|
319
|
+
put('/test', :input => 'body').should be_created
|
320
|
+
put('/copy', :input => 'copy').should be_created
|
321
|
+
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F').should be_precondition_failed
|
315
322
|
|
316
323
|
get('/copy').body.should == 'copy'
|
317
324
|
end
|
318
325
|
|
319
326
|
it 'should allow a copy with overwrite' do
|
320
|
-
put('/test', :input => 'body').should
|
321
|
-
put('/copy', :input => 'copy').should
|
327
|
+
put('/test', :input => 'body').should be_created
|
328
|
+
put('/copy', :input => 'copy').should be_created
|
322
329
|
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T').should be_no_content
|
323
330
|
get('/copy').body.should == 'body'
|
324
331
|
end
|
325
332
|
|
333
|
+
it 'should deny a move to an existing resource without overwrite' do
|
334
|
+
put('/test', :input => 'body').should be_created
|
335
|
+
put('/copy', :input => 'copy').should be_created
|
336
|
+
move('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F').should be_precondition_failed
|
337
|
+
end
|
338
|
+
|
326
339
|
it 'should copy a collection' do
|
327
340
|
mkcol('/folder').should be_created
|
328
341
|
copy('/folder', 'HTTP_DESTINATION' => '/copy').should be_created
|
@@ -332,8 +345,8 @@ describe RackDAV::Handler do
|
|
332
345
|
|
333
346
|
it 'should copy a collection resursively' do
|
334
347
|
mkcol('/folder').should be_created
|
335
|
-
put('/folder/a', :input => 'A').should
|
336
|
-
put('/folder/b', :input => 'B').should
|
348
|
+
put('/folder/a', :input => 'A').should be_created
|
349
|
+
put('/folder/b', :input => 'B').should be_created
|
337
350
|
|
338
351
|
copy('/folder', 'HTTP_DESTINATION' => '/copy').should be_created
|
339
352
|
propfind('/copy', :input => propfind_xml(:resourcetype))
|
@@ -345,8 +358,8 @@ describe RackDAV::Handler do
|
|
345
358
|
|
346
359
|
it 'should move a collection recursively' do
|
347
360
|
mkcol('/folder').should be_created
|
348
|
-
put('/folder/a', :input => 'A').should
|
349
|
-
put('/folder/b', :input => 'B').should
|
361
|
+
put('/folder/a', :input => 'A').should be_created
|
362
|
+
put('/folder/b', :input => 'B').should be_created
|
350
363
|
|
351
364
|
move('/folder', 'HTTP_DESTINATION' => '/move').should be_created
|
352
365
|
propfind('/move', :input => propfind_xml(:resourcetype))
|
@@ -358,19 +371,30 @@ describe RackDAV::Handler do
|
|
358
371
|
get('/folder/b').should be_not_found
|
359
372
|
end
|
360
373
|
|
374
|
+
it 'should not move a collection onto an existing collection without overwrite' do
|
375
|
+
mkcol('/folder').should be_created
|
376
|
+
mkcol('/dest').should be_created
|
377
|
+
|
378
|
+
move('/folder', 'HTTP_DESTINATION' => '/dest', 'HTTP_OVERWRITE' => 'F').should be_precondition_failed
|
379
|
+
end
|
380
|
+
|
361
381
|
it 'should create a collection' do
|
362
382
|
mkcol('/folder').should be_created
|
363
383
|
propfind('/folder', :input => propfind_xml(:resourcetype))
|
364
384
|
multistatus_response('/d:propstat/d:prop/d:resourcetype/d:collection').should_not be_empty
|
365
385
|
end
|
366
386
|
|
387
|
+
it 'should not create a collection with a body' do
|
388
|
+
mkcol('/folder', :input => 'body').should be_unsupported_media_type
|
389
|
+
end
|
390
|
+
|
367
391
|
it 'should not find properties for nonexistent resources' do
|
368
392
|
propfind('/non').should be_not_found
|
369
393
|
end
|
370
394
|
|
371
395
|
it 'should find all properties' do
|
372
396
|
xml = render do |xml|
|
373
|
-
xml.propfind('xmlns
|
397
|
+
xml.propfind('xmlns' => "DAV:") do
|
374
398
|
xml.allprop
|
375
399
|
end
|
376
400
|
end
|
@@ -386,15 +410,53 @@ describe RackDAV::Handler do
|
|
386
410
|
end
|
387
411
|
|
388
412
|
it 'should find named properties' do
|
389
|
-
put('/test.html', :input => '<html/>').should
|
413
|
+
put('/test.html', :input => '<html/>').should be_created
|
390
414
|
propfind('/test.html', :input => propfind_xml(:getcontenttype, :getcontentlength))
|
391
415
|
|
392
416
|
multistatus_response('/d:propstat/d:prop/d:getcontenttype').first.text.should == 'text/html'
|
393
417
|
multistatus_response('/d:propstat/d:prop/d:getcontentlength').first.text.should == '7'
|
394
418
|
end
|
395
419
|
|
420
|
+
it 'should set custom properties in the dav namespace', :has_xattr_support => true do
|
421
|
+
put('/prop', :input => 'A').should be_created
|
422
|
+
proppatch('/prop', :input => propset_xml([:foo, 'testing']))
|
423
|
+
multistatus_response('/d:propstat/d:prop/d:foo').should_not be_empty
|
424
|
+
|
425
|
+
propfind('/prop', :input => propfind_xml(:foo))
|
426
|
+
multistatus_response('/d:propstat/d:prop/d:foo').first.text.should == 'testing'
|
427
|
+
end
|
428
|
+
|
429
|
+
it 'should set custom properties in custom namespaces', :has_xattr_support => true do
|
430
|
+
xmlns = { 'xmlns:s' => 'SPEC:' }
|
431
|
+
put('/prop', :input => 'A').should be_created
|
432
|
+
proppatch('/prop', :input => propset_xml(['s:foo'.to_sym, 'testing', xmlns]))
|
433
|
+
multistatus_response('/d:propstat/d:prop/s:foo', xmlns).should_not be_empty
|
434
|
+
|
435
|
+
propfind('/prop', :input => propfind_xml(['s:foo'.to_sym, xmlns]))
|
436
|
+
multistatus_response('/d:propstat/d:prop/s:foo', xmlns).first.text.should == 'testing'
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'should copy custom properties', :has_xattr_support => true do
|
440
|
+
xmlns = { 'xmlns:s' => 'SPEC:' }
|
441
|
+
put('/prop', :input => 'A').should be_created
|
442
|
+
proppatch('/prop', :input => propset_xml(['s:foo'.to_sym, 'testing', xmlns]))
|
443
|
+
multistatus_response('/d:propstat/d:prop/s:foo', xmlns).should_not be_empty
|
444
|
+
|
445
|
+
copy('/prop', 'HTTP_DESTINATION' => '/propcopy').should be_created
|
446
|
+
propfind('/propcopy', :input => propfind_xml(['s:foo'.to_sym, xmlns]))
|
447
|
+
multistatus_response('/d:propstat/d:prop/s:foo', xmlns).first.text.should == 'testing'
|
448
|
+
end
|
449
|
+
|
450
|
+
it 'should not set properties for a non-existent resource' do
|
451
|
+
proppatch('/not_found', :input => propset_xml([:foo, 'testing'])).should be_not_found
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'should not return properties for non-existent resource' do
|
455
|
+
propfind('/prop', :input => propfind_xml(:foo)).should be_not_found
|
456
|
+
end
|
457
|
+
|
396
458
|
it 'should return the correct charset (utf-8)' do
|
397
|
-
put('/test.html', :input => '<html/>').should
|
459
|
+
put('/test.html', :input => '<html/>').should be_created
|
398
460
|
propfind('/test.html', :input => propfind_xml(:getcontenttype, :getcontentlength))
|
399
461
|
|
400
462
|
charset = @response.media_type_params['charset']
|
@@ -402,7 +464,7 @@ describe RackDAV::Handler do
|
|
402
464
|
end
|
403
465
|
|
404
466
|
it 'should not support LOCK' do
|
405
|
-
put('/test', :input => 'body').should
|
467
|
+
put('/test', :input => 'body').should be_created
|
406
468
|
|
407
469
|
xml = render do |xml|
|
408
470
|
xml.lockinfo('xmlns:d' => "DAV:") do
|
@@ -416,7 +478,7 @@ describe RackDAV::Handler do
|
|
416
478
|
end
|
417
479
|
|
418
480
|
it 'should not support UNLOCK' do
|
419
|
-
put('/test', :input => 'body').should
|
481
|
+
put('/test', :input => 'body').should be_created
|
420
482
|
unlock('/test', :input => '').should be_method_not_allowed
|
421
483
|
end
|
422
484
|
|
@@ -477,22 +539,39 @@ describe RackDAV::Handler do
|
|
477
539
|
match['/d:locktoken/d:href'].first.text.should == token
|
478
540
|
end
|
479
541
|
|
480
|
-
def multistatus_response(pattern)
|
542
|
+
def multistatus_response(pattern, ns=nil)
|
543
|
+
xmlns = { 'd' => 'DAV:' }
|
544
|
+
xmlns.merge!(ns) unless ns.nil?
|
545
|
+
|
481
546
|
@response.should be_multi_status
|
482
|
-
response_xml.xpath("/d:multistatus/d:response",
|
483
|
-
response_xml.xpath("/d:multistatus/d:response" + pattern,
|
547
|
+
response_xml.xpath("/d:multistatus/d:response", xmlns).should_not be_empty
|
548
|
+
response_xml.xpath("/d:multistatus/d:response" + pattern, xmlns)
|
484
549
|
end
|
485
550
|
|
486
551
|
def propfind_xml(*props)
|
487
552
|
render do |xml|
|
488
|
-
xml.propfind('xmlns
|
553
|
+
xml.propfind('xmlns' => "DAV:") do
|
489
554
|
xml.prop do
|
490
|
-
props.each do |prop|
|
491
|
-
|
555
|
+
props.each do |prop, attrs|
|
556
|
+
xml.send(prop.to_sym, attrs)
|
492
557
|
end
|
493
558
|
end
|
494
559
|
end
|
495
560
|
end
|
496
561
|
end
|
497
562
|
|
563
|
+
def propset_xml(*props)
|
564
|
+
render do |xml|
|
565
|
+
xml.propertyupdate('xmlns' => 'DAV:') do
|
566
|
+
xml.set do
|
567
|
+
xml.prop do
|
568
|
+
props.each do |prop, value, attrs|
|
569
|
+
attrs = {} if attrs.nil?
|
570
|
+
xml.send(prop.to_sym, value, attrs)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
498
577
|
end
|
metadata
CHANGED
@@ -1,69 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack_dav
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthias Georgi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.4'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nokogiri
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.5'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ffi-xattr
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.1'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- -
|
59
|
+
- - ~>
|
46
60
|
- !ruby/object:Gem::Version
|
47
61
|
version: '2.11'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- -
|
66
|
+
- - ~>
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '2.11'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- -
|
73
|
+
- - ~>
|
60
74
|
- !ruby/object:Gem::Version
|
61
75
|
version: '0.9'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- -
|
80
|
+
- - ~>
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0.9'
|
69
83
|
description: WebDAV handler for Rack.
|
@@ -74,8 +88,8 @@ extensions: []
|
|
74
88
|
extra_rdoc_files:
|
75
89
|
- README.md
|
76
90
|
files:
|
77
|
-
-
|
78
|
-
-
|
91
|
+
- .gitignore
|
92
|
+
- .travis.yml
|
79
93
|
- CHANGELOG.md
|
80
94
|
- Gemfile
|
81
95
|
- Gemfile.lock
|
@@ -110,17 +124,17 @@ require_paths:
|
|
110
124
|
- lib
|
111
125
|
required_ruby_version: !ruby/object:Gem::Requirement
|
112
126
|
requirements:
|
113
|
-
- -
|
127
|
+
- - '>='
|
114
128
|
- !ruby/object:Gem::Version
|
115
129
|
version: '0'
|
116
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
131
|
requirements:
|
118
|
-
- -
|
132
|
+
- - '>='
|
119
133
|
- !ruby/object:Gem::Version
|
120
134
|
version: '0'
|
121
135
|
requirements: []
|
122
136
|
rubyforge_project:
|
123
|
-
rubygems_version: 2.
|
137
|
+
rubygems_version: 2.0.14
|
124
138
|
signing_key:
|
125
139
|
specification_version: 4
|
126
140
|
summary: WebDAV handler for Rack.
|