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