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