dav4rack 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +15 -4
- data/lib/dav4rack/controller.rb +16 -9
- data/lib/dav4rack/file_resource.rb +19 -29
- data/lib/dav4rack/interceptor.rb +3 -1
- data/lib/dav4rack/resource.rb +3 -3
- data/lib/dav4rack/version.rb +1 -1
- data/spec/handler_spec.rb +123 -85
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -21,6 +21,10 @@ a pull request with your modifications. If you are just here to use the library,
|
|
21
21
|
=== Via RubyGems
|
22
22
|
|
23
23
|
gem install dav4rack
|
24
|
+
|
25
|
+
== Documentation
|
26
|
+
|
27
|
+
* {DAV4Rack documentation}[http://chrisroberts.github.com/dav4rack]
|
24
28
|
|
25
29
|
== Quickstart
|
26
30
|
|
@@ -90,9 +94,9 @@ lets continue with the last example but this time include the interceptor:
|
|
90
94
|
end
|
91
95
|
map '/' do
|
92
96
|
use DAV4Rack::Interceptor, :mappings => {
|
93
|
-
|
94
|
-
|
95
|
-
|
97
|
+
'/webdav/share/' => {:resource_class => FileResource, :custom => 'option'},
|
98
|
+
'/webdav/share2/' => {:resource_class => CustomResource}
|
99
|
+
}
|
96
100
|
use Rails::Rack::Static
|
97
101
|
run ActionController::Dispatcher.new
|
98
102
|
end
|
@@ -202,7 +206,7 @@ And if you don't have the X-Sendfile-Type header set, you can fix that by changi
|
|
202
206
|
|
203
207
|
response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => 'X-Accel-Redirect', :sendfile_prefix => 'webdav_redirect')
|
204
208
|
|
205
|
-
And if you have none of the above because your server hasn't been configured for sendfile support,
|
209
|
+
And if you have none of the above because your server hasn't been configured for sendfile support, you're out of luck until it's configured.
|
206
210
|
|
207
211
|
== Authentication
|
208
212
|
|
@@ -295,6 +299,13 @@ without getting stuck in a loop.
|
|
295
299
|
|
296
300
|
Please use the issues at github: http://github.com/chrisroberts/dav4rack/issues
|
297
301
|
|
302
|
+
== Contributors
|
303
|
+
|
304
|
+
A big thanks to everyone contributing to help make this project better.
|
305
|
+
|
306
|
+
* {clyfe}[http://github.com/clyfe]
|
307
|
+
* {antiloopgmbh}[http://github.com/antiloopgmbh]
|
308
|
+
|
298
309
|
== License
|
299
310
|
|
300
311
|
Just like RackDAV before it, this software is distributed under the MIT license.
|
data/lib/dav4rack/controller.rb
CHANGED
@@ -39,7 +39,7 @@
|
|
39
39
|
response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
|
40
40
|
response["Dav"] = "2"
|
41
41
|
response["Ms-Author-Via"] = "DAV"
|
42
|
-
|
42
|
+
OK
|
43
43
|
end
|
44
44
|
|
45
45
|
# Return response to HEAD
|
@@ -48,7 +48,7 @@
|
|
48
48
|
response['Etag'] = resource.etag
|
49
49
|
response['Content-Type'] = resource.content_type
|
50
50
|
response['Last-Modified'] = resource.last_modified.httpdate
|
51
|
-
|
51
|
+
OK
|
52
52
|
end
|
53
53
|
|
54
54
|
# Return response to GET
|
@@ -127,8 +127,8 @@
|
|
127
127
|
response['Location'] = "#{scheme}://#{host}:#{port}#{dest.public_path}" if status == Created
|
128
128
|
multistatus do |xml|
|
129
129
|
xml.response do
|
130
|
-
xml.href "#{scheme}://#{host}:#{port}#{dest.public_path}"
|
131
|
-
xml.status
|
130
|
+
xml.href "#{scheme}://#{host}:#{port}#{status == Created ? dest.public_path : resource.public_path}"
|
131
|
+
xml.status "#{http_version} #{status.status_line}"
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
@@ -202,6 +202,9 @@
|
|
202
202
|
xml.locktoken do
|
203
203
|
xml.href locktoken
|
204
204
|
end
|
205
|
+
if(asked[:owner])
|
206
|
+
xml.owner asked[:owner]
|
207
|
+
end
|
205
208
|
end
|
206
209
|
end
|
207
210
|
end
|
@@ -228,14 +231,18 @@
|
|
228
231
|
# has defined an #authenticate method
|
229
232
|
def authenticate
|
230
233
|
authed = true
|
231
|
-
if(resource.respond_to?(:authenticate))
|
234
|
+
if(resource.respond_to?(:authenticate, true))
|
232
235
|
authed = false
|
236
|
+
uname = nil
|
237
|
+
password = nil
|
233
238
|
if(request.env['HTTP_AUTHORIZATION'])
|
234
239
|
auth = Rack::Auth::Basic::Request.new(request.env)
|
235
240
|
if(auth.basic? && auth.credentials)
|
236
|
-
|
241
|
+
uname = auth.credentials[0]
|
242
|
+
password = auth.credentials[1]
|
237
243
|
end
|
238
244
|
end
|
245
|
+
authed = resource.send(:authenticate, uname, password)
|
239
246
|
end
|
240
247
|
raise Unauthorized unless authed
|
241
248
|
end
|
@@ -320,17 +327,17 @@
|
|
320
327
|
end
|
321
328
|
|
322
329
|
# Find resources at depth requested
|
323
|
-
def find_resources
|
330
|
+
def find_resources(with_current_resource=true)
|
324
331
|
ary = nil
|
325
332
|
case depth
|
326
333
|
when 0
|
327
|
-
ary = [
|
334
|
+
ary = []
|
328
335
|
when 1
|
329
336
|
ary = resource.children
|
330
337
|
else
|
331
338
|
ary = resource.descendants
|
332
339
|
end
|
333
|
-
|
340
|
+
with_current_resource ? [resource] + ary : ary
|
334
341
|
end
|
335
342
|
|
336
343
|
# XML parsed request
|
@@ -6,10 +6,6 @@ module DAV4Rack
|
|
6
6
|
|
7
7
|
include WEBrick::HTTPUtils
|
8
8
|
|
9
|
-
before do |resource, method_name|
|
10
|
-
resource.send(:check_authentication)
|
11
|
-
end
|
12
|
-
|
13
9
|
# If this is a collection, return the child resources.
|
14
10
|
def children
|
15
11
|
Dir[file_path + '/*'].map do |path|
|
@@ -84,7 +80,7 @@ module DAV4Rack
|
|
84
80
|
# Save the content of the request.body.
|
85
81
|
def put(request, response)
|
86
82
|
write(request.body)
|
87
|
-
|
83
|
+
OK
|
88
84
|
end
|
89
85
|
|
90
86
|
# HTTP POST request.
|
@@ -99,7 +95,7 @@ module DAV4Rack
|
|
99
95
|
# Delete this resource.
|
100
96
|
def delete
|
101
97
|
if stat.directory?
|
102
|
-
|
98
|
+
FileUtils.rm_rf(file_path)
|
103
99
|
else
|
104
100
|
File.unlink(file_path)
|
105
101
|
end
|
@@ -109,22 +105,31 @@ module DAV4Rack
|
|
109
105
|
# HTTP COPY request.
|
110
106
|
#
|
111
107
|
# Copy this resource to given destination resource.
|
112
|
-
def copy(dest)
|
113
|
-
if
|
108
|
+
def copy(dest, overwrite = false)
|
109
|
+
if(dest.path == path)
|
110
|
+
Conflict
|
111
|
+
elsif(stat.directory?)
|
114
112
|
dest.make_collection
|
113
|
+
FileUtils.cp_r("#{file_path}/.", "#{dest.send(:file_path)}/")
|
114
|
+
OK
|
115
115
|
else
|
116
|
-
|
117
|
-
|
116
|
+
exists = File.exists?(file_path)
|
117
|
+
if(exists && !overwrite)
|
118
|
+
PreconditionFailed
|
119
|
+
else
|
120
|
+
open(file_path, "rb") do |file|
|
121
|
+
dest.write(file)
|
122
|
+
end
|
123
|
+
exists ? NoContent : Created
|
118
124
|
end
|
119
125
|
end
|
120
|
-
OK
|
121
126
|
end
|
122
127
|
|
123
128
|
# HTTP MOVE request.
|
124
129
|
#
|
125
130
|
# Move this resource to given destination resource.
|
126
|
-
def move(
|
127
|
-
copy(
|
131
|
+
def move(*args)
|
132
|
+
copy(*args)
|
128
133
|
delete
|
129
134
|
OK
|
130
135
|
end
|
@@ -134,7 +139,7 @@ module DAV4Rack
|
|
134
139
|
# Create this resource as collection.
|
135
140
|
def make_collection
|
136
141
|
Dir.mkdir(file_path)
|
137
|
-
|
142
|
+
Created
|
138
143
|
end
|
139
144
|
|
140
145
|
# Write to this resource from given IO.
|
@@ -154,21 +159,6 @@ module DAV4Rack
|
|
154
159
|
|
155
160
|
private
|
156
161
|
|
157
|
-
def check_authentication
|
158
|
-
valid = true
|
159
|
-
if(options[:username])
|
160
|
-
valid = false
|
161
|
-
if(request.env['HTTP_AUTHORIZATION'])
|
162
|
-
auth = Rack::Auth::Basic::Request.new(request.env)
|
163
|
-
if(auth.basic? && auth.credentials)
|
164
|
-
valid = options[:username] == auth.credentials[0] && options[:password] == auth.credentials[1]
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
raise Unauthorized unless valid
|
169
|
-
valid
|
170
|
-
end
|
171
|
-
|
172
162
|
def authenticate(user, pass)
|
173
163
|
if(options[:username])
|
174
164
|
options[:username] == user && options[:password] == pass
|
data/lib/dav4rack/interceptor.rb
CHANGED
@@ -5,13 +5,15 @@ module DAV4Rack
|
|
5
5
|
@roots = args[:mappings].keys
|
6
6
|
@args = args
|
7
7
|
@app = app
|
8
|
+
@intercept_methods = %w(OPTIONS PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
|
9
|
+
@intercept_methods -= args[:ignore_methods] if args[:ignore_methods]
|
8
10
|
end
|
9
11
|
|
10
12
|
def call(env)
|
11
13
|
path = env['PATH_INFO'].downcase
|
12
14
|
method = env['REQUEST_METHOD'].upcase
|
13
15
|
app = nil
|
14
|
-
if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? &&
|
16
|
+
if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? && @intercept_methods.include?(method))
|
15
17
|
app = DAV4Rack::Handler.new(:resource_class => InterceptorResource, :mappings => @args[:mappings], :log_to => @args[:log_to])
|
16
18
|
end
|
17
19
|
app ? app.call(env) : @app.call(env)
|
data/lib/dav4rack/resource.rb
CHANGED
@@ -196,14 +196,14 @@ module DAV4Rack
|
|
196
196
|
# HTTP COPY request.
|
197
197
|
#
|
198
198
|
# Copy this resource to given destination resource.
|
199
|
-
def copy(dest)
|
199
|
+
def copy(dest, overwrite=false)
|
200
200
|
raise NotImplementedError
|
201
201
|
end
|
202
202
|
|
203
203
|
# HTTP MOVE request.
|
204
204
|
#
|
205
205
|
# Move this resource to given destination resource.
|
206
|
-
def move(dest)
|
206
|
+
def move(dest, overwrite=false)
|
207
207
|
raise NotImplemented
|
208
208
|
end
|
209
209
|
|
@@ -287,7 +287,6 @@ module DAV4Rack
|
|
287
287
|
raise Forbidden unless lock && lock.user == @user
|
288
288
|
raise Conflict unless lock.path =~ /^#{Regexp.escape(@path)}.*$/
|
289
289
|
lock.destroy
|
290
|
-
delete if name[0,1] == '.' && @options[:delete_dotfiles]
|
291
290
|
NoContent
|
292
291
|
end
|
293
292
|
|
@@ -394,6 +393,7 @@ module DAV4Rack
|
|
394
393
|
end
|
395
394
|
|
396
395
|
# Does client allow GET redirection
|
396
|
+
# TODO: Get a comprehensive list in here. Especially now that trasmit added support
|
397
397
|
def allows_redirect?
|
398
398
|
%w(cyberduck konqueror).any?{|x| (request.respond_to?(:user_agent) ? request.user_agent.to_s.downcase : request.env['HTTP_USER_AGENT'].to_s.downcase) =~ /#{Regexp.escape(x)}/}
|
399
399
|
end
|
data/lib/dav4rack/version.rb
CHANGED
data/spec/handler_spec.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
4
|
+
require 'dav4rack'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'nokogiri'
|
6
7
|
|
7
|
-
describe
|
8
|
+
describe DAV4Rack::Handler do
|
8
9
|
DOC_ROOT = File.expand_path(File.dirname(__FILE__) + '/htdocs')
|
9
10
|
METHODS = %w(GET PUT POST DELETE PROPFIND PROPPATCH MKCOL COPY MOVE OPTIONS HEAD LOCK UNLOCK)
|
10
11
|
|
11
12
|
before do
|
12
13
|
FileUtils.mkdir(DOC_ROOT) unless File.exists?(DOC_ROOT)
|
13
|
-
@controller =
|
14
|
+
@controller = DAV4Rack::Handler.new(:root => DOC_ROOT)
|
14
15
|
end
|
15
16
|
|
16
17
|
after do
|
@@ -22,7 +23,7 @@ describe RackDAV::Handler do
|
|
22
23
|
def request(method, uri, options={})
|
23
24
|
options = {
|
24
25
|
'HTTP_HOST' => 'localhost',
|
25
|
-
'REMOTE_USER' => '
|
26
|
+
'REMOTE_USER' => 'user'
|
26
27
|
}.merge(options)
|
27
28
|
request = Rack::MockRequest.new(@controller)
|
28
29
|
@response = request.request(method, uri, options)
|
@@ -34,13 +35,16 @@ describe RackDAV::Handler do
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
def render
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
def render(root_type)
|
39
|
+
raise ArgumentError.new 'Expecting block' unless block_given?
|
40
|
+
doc = Nokogiri::XML::Builder.new do |xml_base|
|
41
|
+
xml_base.send(root_type.to_s, 'xmlns:D' => 'D:') do
|
42
|
+
xml_base.parent.namespace = xml_base.parent.namespace_definitions.first
|
43
|
+
xml = xml_base['D']
|
44
|
+
yield xml
|
45
|
+
end
|
42
46
|
end
|
43
|
-
|
47
|
+
doc.to_xml
|
44
48
|
end
|
45
49
|
|
46
50
|
def url_escape(string)
|
@@ -50,22 +54,35 @@ describe RackDAV::Handler do
|
|
50
54
|
end
|
51
55
|
|
52
56
|
def response_xml
|
53
|
-
|
57
|
+
Nokogiri.XML(@response.body)
|
54
58
|
end
|
55
59
|
|
56
60
|
def multistatus_response(pattern)
|
57
61
|
@response.should be_multi_status
|
58
|
-
|
59
|
-
|
62
|
+
response_xml.xpath('//D:multistatus/D:response', response_xml.root.namespaces).should_not be_empty
|
63
|
+
response_xml.xpath("//D:multistatus/D:response#{pattern}", response_xml.root.namespaces)
|
60
64
|
end
|
61
65
|
|
66
|
+
def multi_status_created
|
67
|
+
response_xml.xpath('//D:multistatus/D:response/D:status').should_not be_empty
|
68
|
+
response_xml.xpath('//D:multistatus/D:response/D:status').text.should =~ /Created/
|
69
|
+
end
|
70
|
+
|
71
|
+
def multi_status_ok
|
72
|
+
response_xml.xpath('//D:multistatus/D:response/D:status').should_not be_empty
|
73
|
+
response_xml.xpath('//D:multistatus/D:response/D:status').text.should =~ /OK/
|
74
|
+
end
|
75
|
+
|
76
|
+
def multi_status_no_content
|
77
|
+
response_xml.xpath('//D:multistatus/D:response/D:status').should_not be_empty
|
78
|
+
response_xml.xpath('//D:multistatus/D:response/D:status').text.should =~ /No Content/
|
79
|
+
end
|
80
|
+
|
62
81
|
def propfind_xml(*props)
|
63
|
-
render do |xml|
|
64
|
-
xml.
|
65
|
-
|
66
|
-
|
67
|
-
xml.tag! prop
|
68
|
-
end
|
82
|
+
render(:propfind) do |xml|
|
83
|
+
xml.prop do
|
84
|
+
props.each do |prop|
|
85
|
+
xml.send(prop.to_sym)
|
69
86
|
end
|
70
87
|
end
|
71
88
|
end
|
@@ -80,7 +97,8 @@ describe RackDAV::Handler do
|
|
80
97
|
end
|
81
98
|
|
82
99
|
it 'should return headers' do
|
83
|
-
put('/test.html', :input => '<html/>')
|
100
|
+
put('/test.html', :input => '<html/>')
|
101
|
+
multi_status_ok.should eq true
|
84
102
|
head('/test.html').should be_ok
|
85
103
|
|
86
104
|
response.headers['etag'].should_not be_nil
|
@@ -97,25 +115,31 @@ describe RackDAV::Handler do
|
|
97
115
|
end
|
98
116
|
|
99
117
|
it 'should create a resource and allow its retrieval' do
|
100
|
-
put('/test', :input => 'body')
|
118
|
+
put('/test', :input => 'body')
|
119
|
+
multi_status_ok.should eq true
|
101
120
|
get('/test').should be_ok
|
102
121
|
response.body.should == 'body'
|
103
122
|
end
|
104
123
|
it 'should create and find a url with escaped characters' do
|
105
|
-
put(url_escape('/a b'), :input => 'body')
|
124
|
+
put(url_escape('/a b'), :input => 'body')
|
125
|
+
multi_status_ok.should eq true
|
106
126
|
get(url_escape('/a b')).should be_ok
|
107
127
|
response.body.should == 'body'
|
108
128
|
end
|
109
129
|
|
110
130
|
it 'should delete a single resource' do
|
111
|
-
put('/test', :input => 'body')
|
131
|
+
put('/test', :input => 'body')
|
132
|
+
multi_status_ok.should eq true
|
112
133
|
delete('/test').should be_no_content
|
113
134
|
end
|
114
135
|
|
115
136
|
it 'should delete recursively' do
|
116
|
-
mkcol('/folder')
|
117
|
-
|
118
|
-
put('/folder/
|
137
|
+
mkcol('/folder')
|
138
|
+
multi_status_created.should eq true
|
139
|
+
put('/folder/a', :input => 'body')
|
140
|
+
multi_status_ok.should eq true
|
141
|
+
put('/folder/b', :input => 'body')
|
142
|
+
multi_status_ok.should eq true
|
119
143
|
|
120
144
|
delete('/folder').should be_no_content
|
121
145
|
get('/folder').should be_not_found
|
@@ -124,79 +148,94 @@ describe RackDAV::Handler do
|
|
124
148
|
end
|
125
149
|
|
126
150
|
it 'should not allow copy to another domain' do
|
127
|
-
put('/test', :input => 'body')
|
151
|
+
put('/test', :input => 'body')
|
152
|
+
multi_status_ok.should eq true
|
128
153
|
copy('http://localhost/', 'HTTP_DESTINATION' => 'http://another/').should be_bad_gateway
|
129
154
|
end
|
130
155
|
|
131
156
|
it 'should not allow copy to the same resource' do
|
132
|
-
put('/test', :input => 'body')
|
157
|
+
put('/test', :input => 'body')
|
158
|
+
multi_status_ok.should eq true
|
133
159
|
copy('/test', 'HTTP_DESTINATION' => '/test').should be_forbidden
|
134
160
|
end
|
135
161
|
|
136
|
-
it 'should not allow an invalid destination uri' do
|
137
|
-
put('/test', :input => 'body').should be_ok
|
138
|
-
copy('/test', 'HTTP_DESTINATION' => '%').should be_bad_request
|
139
|
-
end
|
140
|
-
|
141
162
|
it 'should copy a single resource' do
|
142
|
-
put('/test', :input => 'body')
|
143
|
-
|
163
|
+
put('/test', :input => 'body')
|
164
|
+
multi_status_ok.should eq true
|
165
|
+
copy('/test', 'HTTP_DESTINATION' => '/copy')
|
166
|
+
multi_status_no_content.should eq true
|
144
167
|
get('/copy').body.should == 'body'
|
145
168
|
end
|
146
169
|
|
147
170
|
it 'should copy a resource with escaped characters' do
|
148
|
-
put(url_escape('/a b'), :input => 'body')
|
149
|
-
|
171
|
+
put(url_escape('/a b'), :input => 'body')
|
172
|
+
multi_status_ok.should eq true
|
173
|
+
copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c'))
|
174
|
+
multi_status_no_content.should eq true
|
150
175
|
get(url_escape('/a c')).should be_ok
|
151
176
|
response.body.should == 'body'
|
152
177
|
end
|
153
178
|
|
154
179
|
it 'should deny a copy without overwrite' do
|
155
|
-
put('/test', :input => 'body')
|
156
|
-
|
180
|
+
put('/test', :input => 'body')
|
181
|
+
multi_status_ok.should eq true
|
182
|
+
put('/copy', :input => 'copy')
|
183
|
+
multi_status_ok.should eq true
|
157
184
|
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F')
|
158
185
|
|
159
|
-
multistatus_response('/href').first.text.should
|
160
|
-
multistatus_response('/status').first.text.should match(/412 Precondition Failed/)
|
186
|
+
multistatus_response('/D:href').first.text.should =~ /http:\/\/localhost(:\d+)?\/test/
|
187
|
+
multistatus_response('/D:status').first.text.should match(/412 Precondition Failed/)
|
161
188
|
|
162
189
|
get('/copy').body.should == 'copy'
|
163
190
|
end
|
164
191
|
|
165
192
|
it 'should allow a copy with overwrite' do
|
166
|
-
put('/test', :input => 'body')
|
167
|
-
|
168
|
-
|
193
|
+
put('/test', :input => 'body')
|
194
|
+
multi_status_ok.should eq true
|
195
|
+
put('/copy', :input => 'copy')
|
196
|
+
multi_status_ok.should eq true
|
197
|
+
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T')
|
198
|
+
multi_status_no_content.should eq true
|
169
199
|
get('/copy').body.should == 'body'
|
170
200
|
end
|
171
201
|
|
172
|
-
it 'should copy a collection' do
|
173
|
-
mkcol('/folder')
|
174
|
-
|
202
|
+
it 'should copy a collection' do
|
203
|
+
mkcol('/folder')
|
204
|
+
multi_status_created.should eq true
|
205
|
+
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
206
|
+
multi_status_ok.should eq true
|
175
207
|
propfind('/copy', :input => propfind_xml(:resourcetype))
|
176
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
208
|
+
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
177
209
|
end
|
178
210
|
|
179
211
|
it 'should copy a collection resursively' do
|
180
|
-
mkcol('/folder')
|
181
|
-
|
182
|
-
put('/folder/
|
212
|
+
mkcol('/folder')
|
213
|
+
multi_status_created.should eq true
|
214
|
+
put('/folder/a', :input => 'A')
|
215
|
+
multi_status_ok.should eq true
|
216
|
+
put('/folder/b', :input => 'B')
|
217
|
+
multi_status_ok.should eq true
|
183
218
|
|
184
|
-
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
219
|
+
copy('/folder', 'HTTP_DESTINATION' => '/copy')
|
220
|
+
multi_status_ok.should eq true
|
185
221
|
propfind('/copy', :input => propfind_xml(:resourcetype))
|
186
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
187
|
-
|
222
|
+
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
188
223
|
get('/copy/a').body.should == 'A'
|
189
224
|
get('/copy/b').body.should == 'B'
|
190
225
|
end
|
191
226
|
|
192
227
|
it 'should move a collection recursively' do
|
193
|
-
mkcol('/folder')
|
194
|
-
|
195
|
-
put('/folder/
|
228
|
+
mkcol('/folder')
|
229
|
+
multi_status_created.should eq true
|
230
|
+
put('/folder/a', :input => 'A')
|
231
|
+
multi_status_ok.should eq true
|
232
|
+
put('/folder/b', :input => 'B')
|
233
|
+
multi_status_ok.should eq true
|
196
234
|
|
197
|
-
move('/folder', 'HTTP_DESTINATION' => '/move')
|
235
|
+
move('/folder', 'HTTP_DESTINATION' => '/move')
|
236
|
+
multi_status_ok.should eq true
|
198
237
|
propfind('/move', :input => propfind_xml(:resourcetype))
|
199
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
238
|
+
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
200
239
|
|
201
240
|
get('/move/a').body.should == 'A'
|
202
241
|
get('/move/b').body.should == 'B'
|
@@ -205,9 +244,10 @@ describe RackDAV::Handler do
|
|
205
244
|
end
|
206
245
|
|
207
246
|
it 'should create a collection' do
|
208
|
-
mkcol('/folder')
|
247
|
+
mkcol('/folder')
|
248
|
+
multi_status_created.should eq true
|
209
249
|
propfind('/folder', :input => propfind_xml(:resourcetype))
|
210
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
250
|
+
multistatus_response('/D:propstat/D:prop/D:resourcetype/D:collection').should_not be_empty
|
211
251
|
end
|
212
252
|
|
213
253
|
it 'should not find properties for nonexistent resources' do
|
@@ -215,56 +255,54 @@ describe RackDAV::Handler do
|
|
215
255
|
end
|
216
256
|
|
217
257
|
it 'should find all properties' do
|
218
|
-
xml = render do |xml|
|
219
|
-
xml.
|
220
|
-
xml.allprop
|
221
|
-
end
|
258
|
+
xml = render(:propfind) do |xml|
|
259
|
+
xml.allprop
|
222
260
|
end
|
223
261
|
|
224
262
|
propfind('http://localhost/', :input => xml)
|
225
263
|
|
226
|
-
multistatus_response('/href').first.text.strip.should
|
264
|
+
multistatus_response('/D:href').first.text.strip.should =~ /http:\/\/localhost(:\d+)?\//
|
227
265
|
|
228
266
|
props = %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength)
|
229
267
|
props.each do |prop|
|
230
|
-
multistatus_response(
|
268
|
+
multistatus_response("/D:propstat/D:prop/D:#{prop}").should_not be_empty
|
231
269
|
end
|
232
270
|
end
|
233
271
|
|
234
272
|
it 'should find named properties' do
|
235
|
-
put('/test.html', :input => '<html/>')
|
273
|
+
put('/test.html', :input => '<html/>')
|
274
|
+
multi_status_ok.should eq true
|
236
275
|
propfind('/test.html', :input => propfind_xml(:getcontenttype, :getcontentlength))
|
237
276
|
|
238
|
-
multistatus_response('/propstat/prop/getcontenttype').first.text.should == 'text/html'
|
239
|
-
multistatus_response('/propstat/prop/getcontentlength').first.text.should == '7'
|
277
|
+
multistatus_response('/D:propstat/D:prop/D:getcontenttype').first.text.should == 'text/html'
|
278
|
+
multistatus_response('/D:propstat/D:prop/D:getcontentlength').first.text.should == '7'
|
240
279
|
end
|
241
280
|
|
242
281
|
it 'should lock a resource' do
|
243
|
-
put('/test', :input => 'body')
|
282
|
+
put('/test', :input => 'body')
|
283
|
+
multi_status_ok.should eq true
|
244
284
|
|
245
|
-
xml = render do |xml|
|
246
|
-
xml.
|
247
|
-
|
248
|
-
|
249
|
-
xml.owner { xml.href "http://test.de/" }
|
250
|
-
end
|
285
|
+
xml = render(:lockinfo) do |xml|
|
286
|
+
xml.lockscope { xml.exclusive }
|
287
|
+
xml.locktype { xml.write }
|
288
|
+
xml.owner { xml.href "http://test.de/" }
|
251
289
|
end
|
252
290
|
|
253
291
|
lock('/test', :input => xml)
|
254
292
|
|
255
293
|
response.should be_ok
|
256
|
-
|
294
|
+
|
257
295
|
match = lambda do |pattern|
|
258
|
-
|
296
|
+
response_xml.xpath "/D:prop/D:lockdiscovery/D:activelock#{pattern}"
|
259
297
|
end
|
260
298
|
|
261
299
|
match[''].should_not be_empty
|
262
300
|
|
263
|
-
match['/locktype'].should_not be_empty
|
264
|
-
match['/lockscope'].should_not be_empty
|
265
|
-
match['/depth'].should_not be_empty
|
266
|
-
match['/
|
267
|
-
match['/
|
268
|
-
match['/
|
301
|
+
match['/D:locktype'].should_not be_empty
|
302
|
+
match['/D:lockscope'].should_not be_empty
|
303
|
+
match['/D:depth'].should_not be_empty
|
304
|
+
match['/D:timeout'].should_not be_empty
|
305
|
+
match['/D:locktoken'].should_not be_empty
|
306
|
+
match['/D:owner'].should_not be_empty
|
269
307
|
end
|
270
308
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dav4rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 5
|
10
|
+
version: 0.1.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chris Roberts
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-04 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|