rack-webdav 0.4.3 → 0.4.4

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.
@@ -1,384 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'pstore'
4
- require 'webrick/httputils'
5
- require 'rack-webdav/file_resource_lock'
6
-
7
- module RackWebDAV
8
-
9
- class FileResource < Resource
10
-
11
- include WEBrick::HTTPUtils
12
- include RackWebDAV::Utils
13
-
14
- # If this is a collection, return the child resources.
15
- def children
16
- Dir[file_path + '/*'].map do |path|
17
- child ::File.basename(path)
18
- end
19
- end
20
-
21
- # Is this resource a collection?
22
- def collection?
23
- ::File.directory?(file_path)
24
- end
25
-
26
- # Does this recource exist?
27
- def exist?
28
- ::File.exist?(file_path)
29
- end
30
-
31
- # Return the creation time.
32
- def creation_date
33
- stat.ctime
34
- end
35
-
36
- # Return the time of last modification.
37
- def last_modified
38
- stat.mtime
39
- end
40
-
41
- # Set the time of last modification.
42
- def last_modified=(time)
43
- ::File.utime(Time.now, time, file_path)
44
- end
45
-
46
- # Return an Etag, an unique hash value for this resource.
47
- def etag
48
- sprintf('%x-%x-%x', stat.ino, stat.size, stat.mtime.to_i)
49
- end
50
-
51
- # Return the mime type of this resource.
52
- def content_type
53
- if stat.directory?
54
- "text/html"
55
- else
56
- mime_type(file_path, DefaultMimeTypes)
57
- end
58
- end
59
-
60
- # Return the size in bytes for this resource.
61
- def content_length
62
- stat.size
63
- end
64
-
65
- # HTTP GET request.
66
- #
67
- # Write the content of the resource to the response.body.
68
- def get(request, response)
69
- raise NotFound unless exist?
70
- if stat.directory?
71
- response.body = ""
72
- Rack::Directory.new(root).call(request.env)[2].each do |line|
73
- response.body << line
74
- end
75
- response['Content-Length'] = response.body.bytesize.to_s
76
- else
77
- file = Rack::File.new(root)
78
- status, headers, body = file.call(request.env)
79
-
80
- if body.respond_to?(:to_path)
81
- response.body = ::File.open(body.to_path, 'rb').read
82
- response['Content-Length'] = response.body.bytesize.to_s
83
- else
84
- response.body = ""
85
- total_bytes = 0
86
- body.each { |part|
87
- response.body += part
88
- total_bytes += part.bytesize
89
- }
90
- response['Content-Length'] = total_bytes
91
- end
92
- end
93
- OK
94
- end
95
-
96
- # HTTP PUT request.
97
- #
98
- # Save the content of the request.body.
99
- def put(request, response)
100
- write(request.body)
101
- Created
102
- end
103
-
104
- # HTTP POST request.
105
- #
106
- # Usually forbidden.
107
- def post(request, response)
108
- raise HTTPStatus::Forbidden
109
- end
110
-
111
- # HTTP DELETE request.
112
- #
113
- # Delete this resource.
114
- def delete
115
- if stat.directory?
116
- FileUtils.rm_rf(file_path)
117
- else
118
- ::File.unlink(file_path)
119
- end
120
- ::File.unlink(prop_path) if ::File.exists?(prop_path)
121
- @_prop_hash = nil
122
- NoContent
123
- end
124
-
125
- # HTTP COPY request.
126
- #
127
- # Copy this resource to given destination resource.
128
- # Copy this resource to given destination resource.
129
- def copy(dest, overwrite)
130
- if(collection?)
131
- if(dest.exist?)
132
- if(dest.collection? && overwrite)
133
- FileUtils.cp_r(file_path, dest.send(:file_path))
134
- Created
135
- else
136
- if(overwrite)
137
- FileUtils.rm(dest.send(:file_path))
138
- FileUtils.cp_r(file_path, dest.send(:file_path))
139
- NoContent
140
- else
141
- PreconditionFailed
142
- end
143
- end
144
- else
145
- FileUtils.cp_r(file_path, dest.send(:file_path))
146
- Created
147
- end
148
- else
149
- if(dest.exist? && !overwrite)
150
- PreconditionFailed
151
- else
152
- if(::File.directory?(::File.dirname(dest.send(:file_path))))
153
- new = !dest.exist?
154
- if(dest.collection? && dest.exist?)
155
- FileUtils.rm_rf(dest.send(:file_path))
156
- end
157
- FileUtils.cp(file_path, dest.send(:file_path).sub(/\/$/, ''))
158
- FileUtils.cp(prop_path, dest.prop_path) if ::File.exist? prop_path
159
- new ? Created : NoContent
160
- else
161
- Conflict
162
- end
163
- end
164
- end
165
- end
166
-
167
- # HTTP MOVE request.
168
- #
169
- # Move this resource to given destination resource.
170
- def move(*args)
171
- result = copy(*args)
172
- delete if [Created, NoContent].include?(result)
173
- result
174
- end
175
-
176
- # HTTP MKCOL request.
177
- #
178
- # Create this resource as collection.
179
- def make_collection
180
- if(request.body.read.to_s == '')
181
- if(::File.directory?(file_path))
182
- MethodNotAllowed
183
- else
184
- if(::File.directory?(::File.dirname(file_path)) && !::File.exists?(file_path) && !::File.exists?(file_path.gsub(/\/$/, '')))
185
- Dir.mkdir(file_path)
186
- Created
187
- else
188
- Conflict
189
- end
190
- end
191
- else
192
- UnsupportedMediaType
193
- end
194
- end
195
-
196
- # Write to this resource from given IO.
197
- def write(io)
198
- tempfile = "#{file_path}.#{Process.pid}.#{object_id}"
199
- open(tempfile, "wb") do |file|
200
- while part = io.read(8192)
201
- file << part
202
- end
203
- end
204
- ::File.rename(tempfile, file_path)
205
- ensure
206
- ::File.unlink(tempfile) rescue nil
207
- end
208
-
209
- # name:: String - Property name
210
- # Returns the value of the given property
211
- def get_property(name)
212
- begin
213
- super
214
- rescue NotImplemented
215
- custom_props(name)
216
- end
217
- end
218
-
219
- # name:: String - Property name
220
- # value:: New value
221
- # Set the property to the given value
222
- def set_property(name, value)
223
- begin
224
- super
225
- rescue NotImplemented
226
- set_custom_props(name,value)
227
- end
228
- end
229
-
230
- def remove_property(element)
231
- prop_hash.transaction do
232
- prop_hash.delete(to_element_key(element))
233
- prop_hash.commit
234
- end
235
- val = prop_hash.transaction{ prop_hash[to_element_key(element)] }
236
- end
237
-
238
- def lock(args)
239
- unless(parent_exists?)
240
- Conflict
241
- else
242
- lock_check(args[:type])
243
- lock = FileResourceLock.explicit_locks(@path, root, :scope => args[:scope], :kind => args[:type], :user => @user)
244
- unless(lock)
245
- token = UUIDTools::UUID.random_create.to_s
246
- lock = FileResourceLock.generate(@path, @user, token, root)
247
- lock.scope = args[:scope]
248
- lock.kind = args[:type]
249
- lock.owner = args[:owner]
250
- lock.depth = args[:depth]
251
- if(args[:timeout])
252
- lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout
253
- else
254
- lock.timeout = @default_timeout
255
- end
256
- lock.save
257
- end
258
- begin
259
- lock_check(args[:type])
260
- rescue RackWebDAV::LockFailure => lock_failure
261
- lock.destroy
262
- raise lock_failure
263
- rescue HTTPStatus::Status => status
264
- status
265
- end
266
- [lock.remaining_timeout, lock.token]
267
- end
268
- end
269
-
270
- def unlock(token)
271
- token = token.slice(1, token.length - 2)
272
- if(token.nil? || token.empty?)
273
- BadRequest
274
- else
275
- lock = FileResourceLock.find_by_token(token, root)
276
- if lock.nil?
277
- Forbidden
278
- elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/)
279
- Conflict
280
- else
281
- lock.destroy
282
- NoContent
283
- end
284
- end
285
- end
286
-
287
- protected
288
-
289
- def lock_check(lock_type=nil)
290
- if(FileResourceLock.explicitly_locked?(@path, root))
291
- raise Locked if lock_type && lock_type == 'exclusive'
292
- #raise Locked if FileResourceLock.explicit_locks(@path, root).find(:all, :conditions => ["scope = 'exclusive' AND user_id != ?", @user.id]).size > 0
293
- elsif(FileResourceLock.implicitly_locked?(@path, root))
294
- if(lock_type.to_s == 'exclusive')
295
- locks = FileResourceLock.implicit_locks(@path)
296
- failure = RackWebDAV::LockFailure.new("Failed to lock: #{@path}")
297
- locks.each do |lock|
298
- failure.add_failure(@path, Locked)
299
- end
300
- raise failure
301
- else
302
- if @user.respond_to?(:id)
303
- locks = FileResourceLock.implict_locks(@path).find(:all, :conditions => ["scope = 'exclusive' AND user_id != ?", @user.id])
304
- else
305
- locks = []
306
- end
307
-
308
- if(locks.size > 0)
309
- failure = LockFailure.new("Failed to lock: #{@path}")
310
- locks.each do |lock|
311
- failure.add_failure(@path, Locked)
312
- end
313
- raise failure
314
- end
315
- end
316
- end
317
- end
318
-
319
- def set_custom_props(element, val)
320
- prop_hash.transaction do
321
- prop_hash[to_element_key(element)] = val
322
- prop_hash.commit
323
- end
324
- end
325
-
326
- def custom_props(element)
327
- val = prop_hash.transaction(true) do
328
- prop_hash[to_element_key(element)]
329
- end
330
- raise NotFound unless val
331
- val
332
- end
333
-
334
- def store_directory
335
- path = ::File.join(root, '.attrib_store')
336
- unless(::File.directory?(::File.dirname(path)))
337
- FileUtils.mkdir_p(::File.dirname(path))
338
- end
339
- path
340
- end
341
-
342
- def prop_path
343
- path = ::File.join(store_directory, "#{::File.join(::File.dirname(file_path), ::File.basename(file_path)).gsub('/', '_')}.pstore")
344
- unless(::File.directory?(::File.dirname(path)))
345
- FileUtils.mkdir_p(::File.dirname(path))
346
- end
347
- path
348
- end
349
-
350
- def lock_path
351
- path = ::File.join(store_directory, 'locks.pstore')
352
- unless(::File.directory?(::File.dirname(path)))
353
- FileUtils.mkdir_p(::File.dirname(path))
354
- end
355
- path
356
- end
357
-
358
- def prop_hash
359
- @_prop_hash ||= IS_18 ? PStore.new(prop_path) : PStore.new(prop_path, true)
360
- end
361
-
362
- def authenticate(user, pass)
363
- if(options[:username])
364
- options[:username] == user && options[:password] == pass
365
- else
366
- true
367
- end
368
- end
369
-
370
- def root
371
- @options[:root]
372
- end
373
-
374
- def file_path
375
- ::File.join(root, path)
376
- end
377
-
378
- def stat
379
- @stat ||= ::File.stat(file_path)
380
- end
381
-
382
- end
383
-
384
- end