rack_dav 0.4.0 → 0.4.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.
- 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.
|