dav4rack 0.2.7 → 0.2.8
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 +1 -0
- data/lib/dav4rack/controller.rb +122 -94
- data/lib/dav4rack/logger.rb +5 -3
- data/lib/dav4rack/resource.rb +66 -48
- data/lib/dav4rack/version.rb +1 -1
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -308,6 +308,7 @@ A big thanks to everyone contributing to help make this project better.
|
|
308
308
|
* {teefax}[http://github.com/teefax]
|
309
309
|
* {buffym}[https://github.com/buffym]
|
310
310
|
* {jbangert}[https://github.com/jbangert]
|
311
|
+
* {doxavore}[https://github.com/doxavore]
|
311
312
|
|
312
313
|
== License
|
313
314
|
|
data/lib/dav4rack/controller.rb
CHANGED
@@ -44,35 +44,45 @@ module DAV4Rack
|
|
44
44
|
|
45
45
|
# Return response to HEAD
|
46
46
|
def head
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
if(resource.exist?)
|
48
|
+
response['Etag'] = resource.etag
|
49
|
+
response['Content-Type'] = resource.content_type
|
50
|
+
response['Last-Modified'] = resource.last_modified.httpdate
|
51
|
+
OK
|
52
|
+
else
|
53
|
+
NotFound
|
54
|
+
end
|
52
55
|
end
|
53
56
|
|
54
57
|
# Return response to GET
|
55
58
|
def get
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
if(resource.exist?)
|
60
|
+
res = resource.get(request, response)
|
61
|
+
if(res == OK && !resource.collection?)
|
62
|
+
response['Etag'] = resource.etag
|
63
|
+
response['Content-Type'] = resource.content_type
|
64
|
+
response['Content-Length'] = resource.content_length.to_s
|
65
|
+
response['Last-Modified'] = resource.last_modified.httpdate
|
66
|
+
end
|
67
|
+
res
|
68
|
+
else
|
69
|
+
NotFound
|
63
70
|
end
|
64
|
-
res
|
65
71
|
end
|
66
72
|
|
67
73
|
# Return response to PUT
|
68
74
|
def put
|
69
|
-
|
70
|
-
|
71
|
-
resource.
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
75
|
+
if(resource.collection?)
|
76
|
+
Forbidden
|
77
|
+
elsif(!resource.parent_exists? || !resource.parent.collection?)
|
78
|
+
Conflict
|
79
|
+
else
|
80
|
+
resource.lock_check
|
81
|
+
status = resource.put(request, response)
|
82
|
+
response['Location'] = "#{scheme}://#{host}:#{port}#{resource.public_path}" if status == Created
|
83
|
+
response.body = response['Location']
|
84
|
+
status
|
85
|
+
end
|
76
86
|
end
|
77
87
|
|
78
88
|
# Return response to POST
|
@@ -82,9 +92,12 @@ module DAV4Rack
|
|
82
92
|
|
83
93
|
# Return response to DELETE
|
84
94
|
def delete
|
85
|
-
|
86
|
-
|
87
|
-
|
95
|
+
if(resource.exist?)
|
96
|
+
resource.lock_check
|
97
|
+
resource.delete
|
98
|
+
else
|
99
|
+
NotFound
|
100
|
+
end
|
88
101
|
end
|
89
102
|
|
90
103
|
# Return response to MKCOL
|
@@ -108,43 +121,53 @@ module DAV4Rack
|
|
108
121
|
# Move Resource to new location. If :copy is provided,
|
109
122
|
# Resource will be copied (implementation ease)
|
110
123
|
def move(*args)
|
111
|
-
|
112
|
-
|
113
|
-
destination = url_unescape(env['HTTP_DESTINATION'].sub(%r{https?://([^/]+)}, ''))
|
114
|
-
dest_host = $1
|
115
|
-
raise BadGateway if dest_host and dest_host.gsub(/:\d{2,5}$/,'') != request.host
|
116
|
-
raise Forbidden if destination == resource.public_path
|
117
|
-
dest = resource_class.new(destination, clean_path(destination), @request, @response, @options.merge(:user => resource.user))
|
118
|
-
status = nil
|
119
|
-
if(args.include?(:copy))
|
120
|
-
status = resource.copy(dest, overwrite)
|
124
|
+
unless(resource.exist?)
|
125
|
+
NotFound
|
121
126
|
else
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
127
|
+
resource.lock_check unless args.include?(:copy)
|
128
|
+
destination = url_unescape(env['HTTP_DESTINATION'].sub(%r{https?://([^/]+)}, ''))
|
129
|
+
dest_host = $1
|
130
|
+
if(dest_host && dest_host.gsub(/:\d{2,5}$/, '') != request.host)
|
131
|
+
BadGateway
|
132
|
+
elsif(destination == resource.public_path)
|
133
|
+
Forbidden
|
134
|
+
else
|
135
|
+
dest = resource_class.new(destination, clean_path(destination), @request, @response, @options.merge(:user => resource.user))
|
136
|
+
status = nil
|
137
|
+
if(args.include?(:copy))
|
138
|
+
status = resource.copy(dest, overwrite)
|
139
|
+
else
|
140
|
+
return Conflict unless depth.is_a?(Symbol) || depth > 1
|
141
|
+
status = resource.move(dest, overwrite)
|
142
|
+
end
|
143
|
+
response['Location'] = "#{scheme}://#{host}:#{port}#{dest.public_path}" if status == Created
|
144
|
+
multistatus do |xml|
|
145
|
+
xml.response do
|
146
|
+
xml.href "#{scheme}://#{host}:#{port}#{status == Created ? dest.public_path : resource.public_path}"
|
147
|
+
xml.status "#{http_version} #{status.status_line}"
|
148
|
+
end
|
149
|
+
end
|
130
150
|
end
|
131
151
|
end
|
132
152
|
end
|
133
153
|
|
134
154
|
# Return respoonse to PROPFIND
|
135
155
|
def propfind
|
136
|
-
|
137
|
-
|
138
|
-
names = resource.property_names
|
156
|
+
unless(resource.exist?)
|
157
|
+
NotFound
|
139
158
|
else
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
159
|
+
unless(request_document.xpath("//#{ns}propfind/#{ns}allprop").empty?)
|
160
|
+
names = resource.property_names
|
161
|
+
else
|
162
|
+
names = request_document.xpath("//#{ns}propfind/#{ns}prop").children.find_all{|n|n.element?}.map{|n|n.name}
|
163
|
+
names = resource.property_names if names.empty?
|
164
|
+
end
|
165
|
+
multistatus do |xml|
|
166
|
+
find_resources.each do |resource|
|
167
|
+
xml.response do
|
168
|
+
xml.href "#{scheme}://#{host}:#{port}#{url_escape(resource.public_path)}"
|
169
|
+
propstats(xml, get_properties(resource, names))
|
170
|
+
end
|
148
171
|
end
|
149
172
|
end
|
150
173
|
end
|
@@ -152,15 +175,18 @@ module DAV4Rack
|
|
152
175
|
|
153
176
|
# Return response to PROPPATCH
|
154
177
|
def proppatch
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
178
|
+
unless(resource.exist?)
|
179
|
+
NotFound
|
180
|
+
else
|
181
|
+
resource.lock_check
|
182
|
+
prop_rem = request_match('/propertyupdate/remove/prop').children.map{|n| [n.name] }
|
183
|
+
prop_set = request_match('/propertyupdate/set/prop').children.map{|n| [n.name, n.text] }
|
184
|
+
multistatus do |xml|
|
185
|
+
find_resources.each do |resource|
|
186
|
+
xml.response do
|
187
|
+
xml.href "#{scheme}://#{host}:#{port}#{url_escape(resource.public_path)}"
|
188
|
+
propstats(xml, set_properties(resource, prop_set))
|
189
|
+
end
|
164
190
|
end
|
165
191
|
end
|
166
192
|
end
|
@@ -171,48 +197,50 @@ module DAV4Rack
|
|
171
197
|
# NOTE: This will pass an argument hash to Resource#lock and
|
172
198
|
# wait for a success/failure response.
|
173
199
|
def lock
|
174
|
-
raise NotFound unless resource.exist?
|
175
200
|
lockinfo = request_document.xpath("//#{ns}lockinfo")
|
176
201
|
asked = {}
|
177
202
|
asked[:timeout] = request.env['Timeout'].split(',').map{|x|x.strip} if request.env['Timeout']
|
178
203
|
asked[:depth] = depth
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
204
|
+
unless([0, :infinity].include?(asked[:depth]))
|
205
|
+
BadRequest
|
206
|
+
else
|
207
|
+
asked[:scope] = lockinfo.xpath("//#{ns}lockscope").children.find_all{|n|n.element?}.map{|n|n.name}.first
|
208
|
+
asked[:type] = lockinfo.xpath("#{ns}locktype").children.find_all{|n|n.element?}.map{|n|n.name}.first
|
209
|
+
asked[:owner] = lockinfo.xpath("//#{ns}owner/#{ns}href").children.map{|n|n.text}.first
|
210
|
+
begin
|
211
|
+
lock_time, locktoken = resource.lock(asked)
|
212
|
+
render_xml(:prop) do |xml|
|
213
|
+
xml.lockdiscovery do
|
214
|
+
xml.activelock do
|
215
|
+
if(asked[:scope])
|
216
|
+
xml.lockscope do
|
217
|
+
xml.send(asked[:scope])
|
218
|
+
end
|
191
219
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
220
|
+
if(asked[:type])
|
221
|
+
xml.locktype do
|
222
|
+
xml.send(asked[:type])
|
223
|
+
end
|
224
|
+
end
|
225
|
+
xml.depth asked[:depth].to_s
|
226
|
+
xml.timeout lock_time ? "Second-#{lock_time}" : 'infinity'
|
227
|
+
xml.locktoken do
|
228
|
+
xml.href locktoken
|
229
|
+
end
|
230
|
+
if(asked[:owner])
|
231
|
+
xml.owner asked[:owner]
|
196
232
|
end
|
197
|
-
end
|
198
|
-
xml.depth asked[:depth].to_s
|
199
|
-
xml.timeout lock_time ? "Second-#{lock_time}" : 'infinity'
|
200
|
-
xml.locktoken do
|
201
|
-
xml.href locktoken
|
202
|
-
end
|
203
|
-
if(asked[:owner])
|
204
|
-
xml.owner asked[:owner]
|
205
233
|
end
|
206
234
|
end
|
207
235
|
end
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
236
|
+
response.status = resource.exist? ? OK : Created
|
237
|
+
rescue LockFailure => e
|
238
|
+
multistatus do |xml|
|
239
|
+
e.path_status.each_pair do |path, status|
|
240
|
+
xml.response do
|
241
|
+
xml.href path
|
242
|
+
xml.status "#{http_version} #{status.status_line}"
|
243
|
+
end
|
216
244
|
end
|
217
245
|
end
|
218
246
|
end
|
data/lib/dav4rack/logger.rb
CHANGED
@@ -8,10 +8,12 @@ module DAV4Rack
|
|
8
8
|
# args:: Arguments for Logger -> [path, level] (level is optional) or a Logger instance
|
9
9
|
# Set the path to the log file.
|
10
10
|
def set(*args)
|
11
|
-
if(args.first.
|
11
|
+
if(%w(info debug warn fatal).all?{|meth| args.first.respond_to?(meth)})
|
12
12
|
@@logger = args.first
|
13
|
-
|
14
|
-
@@logger = ::Logger.new(args.first, 'weekly')
|
13
|
+
elsif(args.first.respond_to?(:to_s) && !args.first.to_s.empty?)
|
14
|
+
@@logger = ::Logger.new(args.first.to_s, 'weekly')
|
15
|
+
elsif(args.first)
|
16
|
+
raise 'Invalid type specified for logger'
|
15
17
|
end
|
16
18
|
if(args.size > 1)
|
17
19
|
@@logger.level = args[1]
|
data/lib/dav4rack/resource.rb
CHANGED
@@ -117,17 +117,17 @@ module DAV4Rack
|
|
117
117
|
|
118
118
|
# If this is a collection, return the child resources.
|
119
119
|
def children
|
120
|
-
|
120
|
+
NotImplemented
|
121
121
|
end
|
122
122
|
|
123
123
|
# Is this resource a collection?
|
124
124
|
def collection?
|
125
|
-
|
125
|
+
NotImplemented
|
126
126
|
end
|
127
127
|
|
128
128
|
# Does this resource exist?
|
129
129
|
def exist?
|
130
|
-
|
130
|
+
NotImplemented
|
131
131
|
end
|
132
132
|
|
133
133
|
# Does the parent resource exist?
|
@@ -137,22 +137,22 @@ module DAV4Rack
|
|
137
137
|
|
138
138
|
# Return the creation time.
|
139
139
|
def creation_date
|
140
|
-
|
140
|
+
NotImplemented
|
141
141
|
end
|
142
142
|
|
143
143
|
# Return the time of last modification.
|
144
144
|
def last_modified
|
145
|
-
|
145
|
+
NotImplemented
|
146
146
|
end
|
147
147
|
|
148
148
|
# Set the time of last modification.
|
149
149
|
def last_modified=(time)
|
150
|
-
|
150
|
+
NotImplemented
|
151
151
|
end
|
152
152
|
|
153
153
|
# Return an Etag, an unique hash value for this resource.
|
154
154
|
def etag
|
155
|
-
|
155
|
+
NotImplemented
|
156
156
|
end
|
157
157
|
|
158
158
|
# Return the resource type. Generally only used to specify
|
@@ -163,54 +163,54 @@ module DAV4Rack
|
|
163
163
|
|
164
164
|
# Return the mime type of this resource.
|
165
165
|
def content_type
|
166
|
-
|
166
|
+
NotImplemented
|
167
167
|
end
|
168
168
|
|
169
169
|
# Return the size in bytes for this resource.
|
170
170
|
def content_length
|
171
|
-
|
171
|
+
NotImplemented
|
172
172
|
end
|
173
173
|
|
174
174
|
# HTTP GET request.
|
175
175
|
#
|
176
176
|
# Write the content of the resource to the response.body.
|
177
177
|
def get(request, response)
|
178
|
-
|
178
|
+
NotImplemented
|
179
179
|
end
|
180
180
|
|
181
181
|
# HTTP PUT request.
|
182
182
|
#
|
183
183
|
# Save the content of the request.body.
|
184
184
|
def put(request, response)
|
185
|
-
|
185
|
+
NotImplemented
|
186
186
|
end
|
187
187
|
|
188
188
|
# HTTP POST request.
|
189
189
|
#
|
190
190
|
# Usually forbidden.
|
191
191
|
def post(request, response)
|
192
|
-
|
192
|
+
NotImplemented
|
193
193
|
end
|
194
194
|
|
195
195
|
# HTTP DELETE request.
|
196
196
|
#
|
197
197
|
# Delete this resource.
|
198
198
|
def delete
|
199
|
-
|
199
|
+
NotImplemented
|
200
200
|
end
|
201
201
|
|
202
202
|
# HTTP COPY request.
|
203
203
|
#
|
204
204
|
# Copy this resource to given destination resource.
|
205
205
|
def copy(dest, overwrite=false)
|
206
|
-
|
206
|
+
NotImplemented
|
207
207
|
end
|
208
208
|
|
209
209
|
# HTTP MOVE request.
|
210
210
|
#
|
211
211
|
# Move this resource to given destination resource.
|
212
212
|
def move(dest, overwrite=false)
|
213
|
-
|
213
|
+
NotImplemented
|
214
214
|
end
|
215
215
|
|
216
216
|
# args:: Hash of lock arguments
|
@@ -229,31 +229,39 @@ module DAV4Rack
|
|
229
229
|
# (http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10)
|
230
230
|
|
231
231
|
def lock(args)
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
token = UUIDTools::UUID.random_create.to_s
|
238
|
-
lock = @lock_class.generate(@path, @user, token)
|
239
|
-
lock.scope = args[:scope]
|
240
|
-
lock.kind = args[:type]
|
241
|
-
lock.owner = args[:owner]
|
242
|
-
lock.depth = args[:depth].is_a?(Symbol) ? args[:depth] : args[:depth].to_i
|
243
|
-
if(args[:timeout])
|
244
|
-
lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout
|
232
|
+
unless(@lock_class)
|
233
|
+
NotImplemented
|
234
|
+
else
|
235
|
+
unless(parent_exists?)
|
236
|
+
Conflict
|
245
237
|
else
|
246
|
-
|
238
|
+
lock_check(args[:scope])
|
239
|
+
lock = @lock_class.explicit_locks(@path).find{|l| l.scope == args[:scope] && l.kind == args[:type] && l.user == @user}
|
240
|
+
unless(lock)
|
241
|
+
token = UUIDTools::UUID.random_create.to_s
|
242
|
+
lock = @lock_class.generate(@path, @user, token)
|
243
|
+
lock.scope = args[:scope]
|
244
|
+
lock.kind = args[:type]
|
245
|
+
lock.owner = args[:owner]
|
246
|
+
lock.depth = args[:depth].is_a?(Symbol) ? args[:depth] : args[:depth].to_i
|
247
|
+
if(args[:timeout])
|
248
|
+
lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout
|
249
|
+
else
|
250
|
+
lock.timeout = @default_timeout
|
251
|
+
end
|
252
|
+
lock.save if lock.respond_to? :save
|
253
|
+
end
|
254
|
+
begin
|
255
|
+
lock_check(args[:type])
|
256
|
+
rescue DAV4Rack::LockFailure => lock_failure
|
257
|
+
lock.destroy
|
258
|
+
raise lock_failure
|
259
|
+
rescue HTTPStatus::Status => status
|
260
|
+
status
|
261
|
+
end
|
262
|
+
[lock.remaining_timeout, lock.token]
|
247
263
|
end
|
248
|
-
lock.save if lock.respond_to? :save
|
249
|
-
end
|
250
|
-
begin
|
251
|
-
lock_check(args[:type])
|
252
|
-
rescue DAV4Rack::LockFailure => e
|
253
|
-
lock.destroy
|
254
|
-
raise e
|
255
264
|
end
|
256
|
-
[lock.remaining_timeout, lock.token]
|
257
265
|
end
|
258
266
|
|
259
267
|
# lock_scope:: scope of lock
|
@@ -286,20 +294,30 @@ module DAV4Rack
|
|
286
294
|
# token:: Lock token
|
287
295
|
# Remove the given lock
|
288
296
|
def unlock(token)
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
+
unless(@lock_class)
|
298
|
+
NotImplemented
|
299
|
+
else
|
300
|
+
token = token.slice(1, token.length - 2)
|
301
|
+
if(token.nil? || token.empty?)
|
302
|
+
BadRequest
|
303
|
+
else
|
304
|
+
lock = @lock_class.find_by_token(token)
|
305
|
+
if(lock.nil? || lock.user != @user)
|
306
|
+
Forbidden
|
307
|
+
elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/)
|
308
|
+
Conflict
|
309
|
+
else
|
310
|
+
lock.destroy
|
311
|
+
NoContent
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
297
315
|
end
|
298
316
|
|
299
317
|
|
300
318
|
# Create this resource as collection.
|
301
319
|
def make_collection
|
302
|
-
|
320
|
+
NotImplemented
|
303
321
|
end
|
304
322
|
|
305
323
|
# other:: Resource
|
@@ -348,13 +366,13 @@ module DAV4Rack
|
|
348
366
|
when 'getlastmodified' then self.last_modified = Time.httpdate(value)
|
349
367
|
end
|
350
368
|
rescue ArgumentError
|
351
|
-
|
369
|
+
Conflict
|
352
370
|
end
|
353
371
|
|
354
372
|
# name:: Property name
|
355
373
|
# Remove the property from the resource
|
356
374
|
def remove_property(name)
|
357
|
-
|
375
|
+
Forbidden
|
358
376
|
end
|
359
377
|
|
360
378
|
# name:: Name of child
|
data/lib/dav4rack/version.rb
CHANGED
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: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 8
|
10
|
+
version: 0.2.8
|
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: 2011-08-
|
18
|
+
date: 2011-08-26 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|