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.
- checksums.yaml +4 -4
- data/bin/byebug +17 -0
- data/bin/coderay +17 -0
- data/bin/htmldiff +17 -0
- data/bin/ldiff +17 -0
- data/bin/nokogiri +17 -0
- data/bin/pry +17 -0
- data/bin/puma +17 -0
- data/bin/pumactl +17 -0
- data/bin/rack-webdav +23 -109
- data/bin/rackup +17 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/bin/rubocop +17 -0
- data/bin/ruby-parse +17 -0
- data/bin/ruby-rewrite +17 -0
- data/bin/unicorn +17 -0
- data/bin/unicorn_rails +17 -0
- data/lib/rack-webdav/controller.rb +447 -480
- data/lib/rack-webdav/file_resource.rb +247 -0
- data/lib/rack-webdav/handler.rb +27 -50
- data/lib/rack-webdav/http_status.rb +10 -10
- data/lib/rack-webdav/resource.rb +73 -368
- data/lib/rack-webdav/string.rb +28 -0
- data/lib/rack-webdav/version.rb +10 -11
- data/lib/rack-webdav.rb +5 -1
- data/rack-webdav.gemspec +3 -0
- metadata +62 -9
- data/lib/rack-webdav/file.rb +0 -37
- data/lib/rack-webdav/interceptor.rb +0 -22
- data/lib/rack-webdav/interceptor_resource.rb +0 -119
- data/lib/rack-webdav/lock.rb +0 -40
- data/lib/rack-webdav/logger.rb +0 -30
- data/lib/rack-webdav/remote_file.rb +0 -148
- data/lib/rack-webdav/resources/file_resource.rb +0 -384
data/lib/rack-webdav/resource.rb
CHANGED
|
@@ -1,427 +1,181 @@
|
|
|
1
|
-
require 'uuidtools'
|
|
2
|
-
require 'rack-webdav/http_status'
|
|
3
|
-
|
|
4
1
|
module RackWebDAV
|
|
5
|
-
|
|
6
|
-
class LockFailure < RuntimeError
|
|
7
|
-
attr_reader :path_status
|
|
8
|
-
def initialize(*args)
|
|
9
|
-
super(*args)
|
|
10
|
-
@path_status = {}
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def add_failure(path, status)
|
|
14
|
-
@path_status[path] = status
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
2
|
+
|
|
18
3
|
class Resource
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class << self
|
|
25
|
-
|
|
26
|
-
# This lets us define a bunch of before and after blocks that are
|
|
27
|
-
# either called before all methods on the resource, or only specific
|
|
28
|
-
# methods on the resource
|
|
29
|
-
def method_missing(*args, &block)
|
|
30
|
-
class_sym = self.name.to_sym
|
|
31
|
-
@@blocks[class_sym] ||= {:before => {}, :after => {}}
|
|
32
|
-
m = args.shift
|
|
33
|
-
parts = m.to_s.split('_')
|
|
34
|
-
type = parts.shift.to_s.to_sym
|
|
35
|
-
method = parts.empty? ? nil : parts.join('_').to_sym
|
|
36
|
-
if(@@blocks[class_sym][type] && block_given?)
|
|
37
|
-
if(method)
|
|
38
|
-
@@blocks[class_sym][type][method] ||= []
|
|
39
|
-
@@blocks[class_sym][type][method] << block
|
|
40
|
-
else
|
|
41
|
-
@@blocks[class_sym][type][:'__all__'] ||= []
|
|
42
|
-
@@blocks[class_sym][type][:'__all__'] << block
|
|
43
|
-
end
|
|
44
|
-
else
|
|
45
|
-
raise NoMethodError.new("Undefined method #{m} for class #{self}")
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
include RackWebDAV::HTTPStatus
|
|
52
|
-
|
|
53
|
-
# public_path:: Path received via request
|
|
54
|
-
# path:: Internal resource path (Only different from public path when using root_uri's for webdav)
|
|
55
|
-
# request:: Rack::Request
|
|
56
|
-
# options:: Any options provided for this resource
|
|
57
|
-
# Creates a new instance of the resource.
|
|
58
|
-
# NOTE: path and public_path will only differ if the root_uri has been set for the resource. The
|
|
59
|
-
# controller will strip out the starting path so the resource can easily determine what
|
|
60
|
-
# it is working on. For example:
|
|
61
|
-
# request -> /my/webdav/directory/actual/path
|
|
62
|
-
# public_path -> /my/webdav/directory/actual/path
|
|
63
|
-
# path -> /actual/path
|
|
64
|
-
# NOTE: Customized Resources should not use initialize for setup. Instead
|
|
65
|
-
# use the #setup method
|
|
66
|
-
def initialize(public_path, path, request, response, options)
|
|
67
|
-
@skip_alias = [
|
|
68
|
-
:authenticate, :authentication_error_msg,
|
|
69
|
-
:authentication_realm, :path, :options,
|
|
70
|
-
:public_path, :request, :response, :user,
|
|
71
|
-
:user=, :setup
|
|
72
|
-
]
|
|
73
|
-
@public_path = public_path.dup
|
|
74
|
-
@path = path.dup
|
|
75
|
-
@propstat_relative_path = !!options.delete(:propstat_relative_path)
|
|
76
|
-
@root_xml_attributes = options.delete(:root_xml_attributes) || {}
|
|
4
|
+
|
|
5
|
+
attr_reader :path, :options
|
|
6
|
+
|
|
7
|
+
def initialize(path, request, response, options)
|
|
8
|
+
@path = path
|
|
77
9
|
@request = request
|
|
78
10
|
@response = response
|
|
79
|
-
|
|
80
|
-
require 'rack-webdav/lock_store'
|
|
81
|
-
@lock_class = LockStore
|
|
82
|
-
else
|
|
83
|
-
@lock_class = options[:lock_class]
|
|
84
|
-
raise NameError.new("Unknown lock type constant provided: #{@lock_class}") unless @lock_class.nil? || defined?(@lock_class)
|
|
85
|
-
end
|
|
86
|
-
@options = options.dup
|
|
87
|
-
@max_timeout = options[:max_timeout] || 86400
|
|
88
|
-
@default_timeout = options[:default_timeout] || 60
|
|
89
|
-
@user = @options[:user] || request.ip
|
|
90
|
-
setup if respond_to?(:setup)
|
|
91
|
-
public_methods(false).each do |method|
|
|
92
|
-
next if @skip_alias.include?(method.to_sym) || method[0,4] == 'DAV_' || method[0,5] == '_DAV_'
|
|
93
|
-
self.class.class_eval "alias :'_DAV_#{method}' :'#{method}'"
|
|
94
|
-
self.class.class_eval "undef :'#{method}'"
|
|
95
|
-
end
|
|
96
|
-
@runner = lambda do |class_sym, kind, method_name|
|
|
97
|
-
[:'__all__', method_name.to_sym].each do |sym|
|
|
98
|
-
if(@@blocks[class_sym] && @@blocks[class_sym][kind] && @@blocks[class_sym][kind][sym])
|
|
99
|
-
@@blocks[class_sym][kind][sym].each do |b|
|
|
100
|
-
args = [self, sym == :'__all__' ? method_name : nil].compact
|
|
101
|
-
b.call(*args)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# This allows us to call before and after blocks
|
|
109
|
-
def method_missing(*args)
|
|
110
|
-
result = nil
|
|
111
|
-
orig = args.shift
|
|
112
|
-
class_sym = self.class.name.to_sym
|
|
113
|
-
m = orig.to_s[0,5] == '_DAV_' ? orig : "_DAV_#{orig}" # If hell is doing the same thing over and over and expecting a different result this is a hell preventer
|
|
114
|
-
raise NoMethodError.new("Undefined method: #{orig} for class #{self}.") unless respond_to?(m)
|
|
115
|
-
@runner.call(class_sym, :before, orig)
|
|
116
|
-
result = send m, *args
|
|
117
|
-
@runner.call(class_sym, :after, orig)
|
|
118
|
-
result
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Returns if resource supports locking
|
|
122
|
-
def supports_locking?
|
|
123
|
-
false #true
|
|
11
|
+
@options = options
|
|
124
12
|
end
|
|
125
13
|
|
|
126
14
|
# If this is a collection, return the child resources.
|
|
127
15
|
def children
|
|
128
|
-
|
|
16
|
+
raise NotImplementedError
|
|
129
17
|
end
|
|
130
18
|
|
|
131
19
|
# Is this resource a collection?
|
|
132
20
|
def collection?
|
|
133
|
-
|
|
21
|
+
raise NotImplementedError
|
|
134
22
|
end
|
|
135
23
|
|
|
136
|
-
# Does this
|
|
24
|
+
# Does this recource exist?
|
|
137
25
|
def exist?
|
|
138
|
-
|
|
26
|
+
raise NotImplementedError
|
|
139
27
|
end
|
|
140
|
-
|
|
141
|
-
# Does the parent resource exist?
|
|
142
|
-
def parent_exists?
|
|
143
|
-
parent.exist?
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Is the parent resource a collection?
|
|
147
|
-
def parent_collection?
|
|
148
|
-
parent.collection?
|
|
149
|
-
end
|
|
150
|
-
|
|
28
|
+
|
|
151
29
|
# Return the creation time.
|
|
152
30
|
def creation_date
|
|
153
|
-
raise
|
|
31
|
+
raise NotImplementedError
|
|
154
32
|
end
|
|
155
33
|
|
|
156
34
|
# Return the time of last modification.
|
|
157
35
|
def last_modified
|
|
158
|
-
raise
|
|
36
|
+
raise NotImplementedError
|
|
159
37
|
end
|
|
160
|
-
|
|
38
|
+
|
|
161
39
|
# Set the time of last modification.
|
|
162
40
|
def last_modified=(time)
|
|
163
|
-
|
|
164
|
-
raise NotImplemented
|
|
41
|
+
raise NotImplementedError
|
|
165
42
|
end
|
|
166
43
|
|
|
167
44
|
# Return an Etag, an unique hash value for this resource.
|
|
168
45
|
def etag
|
|
169
|
-
raise
|
|
46
|
+
raise NotImplementedError
|
|
170
47
|
end
|
|
171
48
|
|
|
172
|
-
# Return the resource type.
|
|
173
|
-
#
|
|
49
|
+
# Return the resource type.
|
|
50
|
+
#
|
|
51
|
+
# If this is a collection, return a collection element
|
|
174
52
|
def resource_type
|
|
175
|
-
|
|
53
|
+
if collection?
|
|
54
|
+
Nokogiri::XML::fragment('<D:collection xmlns:D="DAV:"/>').children.first
|
|
55
|
+
end
|
|
176
56
|
end
|
|
177
57
|
|
|
178
58
|
# Return the mime type of this resource.
|
|
179
59
|
def content_type
|
|
180
|
-
raise
|
|
60
|
+
raise NotImplementedError
|
|
181
61
|
end
|
|
182
62
|
|
|
183
63
|
# Return the size in bytes for this resource.
|
|
184
64
|
def content_length
|
|
185
|
-
raise
|
|
65
|
+
raise NotImplementedError
|
|
186
66
|
end
|
|
187
67
|
|
|
188
68
|
# HTTP GET request.
|
|
189
69
|
#
|
|
190
70
|
# Write the content of the resource to the response.body.
|
|
191
|
-
def get
|
|
192
|
-
|
|
71
|
+
def get
|
|
72
|
+
raise NotImplementedError
|
|
193
73
|
end
|
|
194
74
|
|
|
195
75
|
# HTTP PUT request.
|
|
196
76
|
#
|
|
197
77
|
# Save the content of the request.body.
|
|
198
|
-
def put
|
|
199
|
-
|
|
78
|
+
def put
|
|
79
|
+
raise NotImplementedError
|
|
200
80
|
end
|
|
201
|
-
|
|
81
|
+
|
|
202
82
|
# HTTP POST request.
|
|
203
83
|
#
|
|
204
84
|
# Usually forbidden.
|
|
205
|
-
def post
|
|
206
|
-
|
|
85
|
+
def post
|
|
86
|
+
raise NotImplementedError
|
|
207
87
|
end
|
|
208
|
-
|
|
88
|
+
|
|
209
89
|
# HTTP DELETE request.
|
|
210
90
|
#
|
|
211
91
|
# Delete this resource.
|
|
212
92
|
def delete
|
|
213
|
-
|
|
93
|
+
raise NotImplementedError
|
|
214
94
|
end
|
|
215
|
-
|
|
95
|
+
|
|
216
96
|
# HTTP COPY request.
|
|
217
97
|
#
|
|
218
98
|
# Copy this resource to given destination resource.
|
|
219
|
-
def copy(dest
|
|
220
|
-
|
|
99
|
+
def copy(dest)
|
|
100
|
+
raise NotImplementedError
|
|
221
101
|
end
|
|
222
|
-
|
|
102
|
+
|
|
223
103
|
# HTTP MOVE request.
|
|
224
104
|
#
|
|
225
105
|
# Move this resource to given destination resource.
|
|
226
|
-
def move(dest
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
# args:: Hash of lock arguments
|
|
231
|
-
# Request for a lock on the given resource. A valid lock should lock
|
|
232
|
-
# all descendents. Failures should be noted and returned as an exception
|
|
233
|
-
# using LockFailure.
|
|
234
|
-
# Valid args keys: :timeout -> requested timeout
|
|
235
|
-
# :depth -> lock depth
|
|
236
|
-
# :scope -> lock scope
|
|
237
|
-
# :type -> lock type
|
|
238
|
-
# :owner -> lock owner
|
|
239
|
-
# Should return a tuple: [lock_time, locktoken] where lock_time is the
|
|
240
|
-
# given timeout
|
|
241
|
-
# NOTE: See section 9.10 of RFC 4918 for guidance about
|
|
242
|
-
# how locks should be generated and the expected responses
|
|
243
|
-
# (http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10)
|
|
244
|
-
|
|
245
|
-
def lock(args)
|
|
246
|
-
unless(@lock_class)
|
|
247
|
-
NotImplemented
|
|
248
|
-
else
|
|
249
|
-
unless(parent_exists?)
|
|
250
|
-
Conflict
|
|
251
|
-
else
|
|
252
|
-
lock_check(args[:scope])
|
|
253
|
-
lock = @lock_class.explicit_locks(@path).find{|l| l.scope == args[:scope] && l.kind == args[:type] && l.user == @user}
|
|
254
|
-
unless(lock)
|
|
255
|
-
token = UUIDTools::UUID.random_create.to_s
|
|
256
|
-
lock = @lock_class.generate(@path, @user, token)
|
|
257
|
-
lock.scope = args[:scope]
|
|
258
|
-
lock.kind = args[:type]
|
|
259
|
-
lock.owner = args[:owner]
|
|
260
|
-
lock.depth = args[:depth].is_a?(Symbol) ? args[:depth] : args[:depth].to_i
|
|
261
|
-
if(args[:timeout])
|
|
262
|
-
lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout
|
|
263
|
-
else
|
|
264
|
-
lock.timeout = @default_timeout
|
|
265
|
-
end
|
|
266
|
-
lock.save if lock.respond_to? :save
|
|
267
|
-
end
|
|
268
|
-
begin
|
|
269
|
-
lock_check(args[:type])
|
|
270
|
-
rescue RackWebDAV::LockFailure => lock_failure
|
|
271
|
-
lock.destroy
|
|
272
|
-
raise lock_failure
|
|
273
|
-
rescue HTTPStatus::Status => status
|
|
274
|
-
status
|
|
275
|
-
end
|
|
276
|
-
[lock.remaining_timeout, lock.token]
|
|
277
|
-
end
|
|
278
|
-
end
|
|
106
|
+
def move(dest)
|
|
107
|
+
copy(dest)
|
|
108
|
+
delete
|
|
279
109
|
end
|
|
280
110
|
|
|
281
|
-
#
|
|
282
|
-
#
|
|
283
|
-
def lock_check(lock_scope=nil)
|
|
284
|
-
return unless @lock_class
|
|
285
|
-
if(@lock_class.explicitly_locked?(@path))
|
|
286
|
-
raise Locked if @lock_class.explicit_locks(@path).find_all{|l|l.scope == 'exclusive' && l.user != @user}.size > 0
|
|
287
|
-
elsif(@lock_class.implicitly_locked?(@path))
|
|
288
|
-
if(lock_scope.to_s == 'exclusive')
|
|
289
|
-
locks = @lock_class.implicit_locks(@path)
|
|
290
|
-
failure = RackWebDAV::LockFailure.new("Failed to lock: #{@path}")
|
|
291
|
-
locks.each do |lock|
|
|
292
|
-
failure.add_failure(@path, Locked)
|
|
293
|
-
end
|
|
294
|
-
raise failure
|
|
295
|
-
else
|
|
296
|
-
locks = @lock_class.implict_locks(@path).find_all{|l| l.scope == 'exclusive' && l.user != @user}
|
|
297
|
-
if(locks.size > 0)
|
|
298
|
-
failure = LockFailure.new("Failed to lock: #{@path}")
|
|
299
|
-
locks.each do |lock|
|
|
300
|
-
failure.add_failure(@path, Locked)
|
|
301
|
-
end
|
|
302
|
-
raise failure
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
# token:: Lock token
|
|
309
|
-
# Remove the given lock
|
|
310
|
-
def unlock(token)
|
|
311
|
-
unless(@lock_class)
|
|
312
|
-
NotImplemented
|
|
313
|
-
else
|
|
314
|
-
token = token.slice(1, token.length - 2)
|
|
315
|
-
if(token.nil? || token.empty?)
|
|
316
|
-
BadRequest
|
|
317
|
-
else
|
|
318
|
-
lock = @lock_class.find_by_token(token)
|
|
319
|
-
if(lock.nil? || lock.user != @user)
|
|
320
|
-
Forbidden
|
|
321
|
-
elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/)
|
|
322
|
-
Conflict
|
|
323
|
-
else
|
|
324
|
-
lock.destroy
|
|
325
|
-
NoContent
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
|
|
111
|
+
# HTTP MKCOL request.
|
|
112
|
+
#
|
|
332
113
|
# Create this resource as collection.
|
|
333
114
|
def make_collection
|
|
334
|
-
|
|
115
|
+
raise NotImplementedError
|
|
335
116
|
end
|
|
336
117
|
|
|
337
|
-
# other:: Resource
|
|
338
|
-
# Returns if current resource is equal to other resource
|
|
339
118
|
def ==(other)
|
|
340
119
|
path == other.path
|
|
341
120
|
end
|
|
342
121
|
|
|
343
|
-
# Name of the resource
|
|
344
122
|
def name
|
|
345
123
|
File.basename(path)
|
|
346
124
|
end
|
|
347
125
|
|
|
348
|
-
# Name of the resource to be displayed to the client
|
|
349
126
|
def display_name
|
|
350
127
|
name
|
|
351
128
|
end
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
129
|
+
|
|
130
|
+
def child(name, option={})
|
|
131
|
+
self.class.new(path + '/' + name, @request, @response, options)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def lockable?
|
|
135
|
+
self.respond_to?(:lock) && self.respond_to?(:unlock)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def property_names
|
|
139
|
+
%w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength)
|
|
358
140
|
end
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
def get_property(element)
|
|
363
|
-
raise NotImplemented if (element[:ns_href] != 'DAV:')
|
|
364
|
-
case element[:name]
|
|
141
|
+
|
|
142
|
+
def get_property(name)
|
|
143
|
+
case name
|
|
365
144
|
when 'resourcetype' then resource_type
|
|
366
145
|
when 'displayname' then display_name
|
|
367
|
-
when 'creationdate' then
|
|
146
|
+
when 'creationdate' then creation_date.xmlschema
|
|
368
147
|
when 'getcontentlength' then content_length.to_s
|
|
369
148
|
when 'getcontenttype' then content_type
|
|
370
149
|
when 'getetag' then etag
|
|
371
150
|
when 'getlastmodified' then last_modified.httpdate
|
|
372
|
-
else
|
|
151
|
+
else self.get_custom_property(name) if self.respond_to?(:get_custom_property)
|
|
373
152
|
end
|
|
374
153
|
end
|
|
375
154
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
# Set the property to the given value
|
|
379
|
-
def set_property(element, value)
|
|
380
|
-
raise NotImplemented if (element[:ns_href] != 'DAV:')
|
|
381
|
-
case element[:name]
|
|
155
|
+
def set_property(name, value)
|
|
156
|
+
case name
|
|
382
157
|
when 'resourcetype' then self.resource_type = value
|
|
383
158
|
when 'getcontenttype' then self.content_type = value
|
|
384
159
|
when 'getetag' then self.etag = value
|
|
385
160
|
when 'getlastmodified' then self.last_modified = Time.httpdate(value)
|
|
386
|
-
else
|
|
161
|
+
else self.set_custom_property(name, value) if self.respond_to?(:set_custom_property)
|
|
387
162
|
end
|
|
163
|
+
rescue Errno::EOPNOTSUPP
|
|
164
|
+
# nothing done
|
|
165
|
+
rescue ArgumentError
|
|
166
|
+
raise HTTPStatus::Conflict
|
|
388
167
|
end
|
|
389
168
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
def remove_property(element)
|
|
393
|
-
Forbidden
|
|
169
|
+
def remove_property(name)
|
|
170
|
+
raise HTTPStatus::Forbidden if property_names.include?(name)
|
|
394
171
|
end
|
|
395
172
|
|
|
396
|
-
# name:: Name of child
|
|
397
|
-
# Create a new child with the given name
|
|
398
|
-
# NOTE:: Include trailing '/' if child is collection
|
|
399
|
-
def child(name)
|
|
400
|
-
new_public = public_path.dup
|
|
401
|
-
new_public = new_public + '/' unless new_public[-1,1] == '/'
|
|
402
|
-
new_public = '/' + new_public unless new_public[0,1] == '/'
|
|
403
|
-
new_path = path.dup
|
|
404
|
-
new_path = new_path + '/' unless new_path[-1,1] == '/'
|
|
405
|
-
new_path = '/' + new_path unless new_path[0,1] == '/'
|
|
406
|
-
self.class.new("#{new_public}#{name}", "#{new_path}#{name}", request, response, options.merge(:user => @user))
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
# Return parent of this resource
|
|
410
173
|
def parent
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
File.split(@path).first,
|
|
415
|
-
@request,
|
|
416
|
-
@response,
|
|
417
|
-
@options.merge(
|
|
418
|
-
:user => @user
|
|
419
|
-
)
|
|
420
|
-
)
|
|
421
|
-
end
|
|
174
|
+
elements = @path.scan(/[^\/]+/)
|
|
175
|
+
return nil if elements.empty?
|
|
176
|
+
self.class.new('/' + elements[0..-2].to_a.join('/'), @options)
|
|
422
177
|
end
|
|
423
|
-
|
|
424
|
-
# Return list of descendants
|
|
178
|
+
|
|
425
179
|
def descendants
|
|
426
180
|
list = []
|
|
427
181
|
children.each do |child|
|
|
@@ -430,55 +184,6 @@ module RackWebDAV
|
|
|
430
184
|
end
|
|
431
185
|
list
|
|
432
186
|
end
|
|
433
|
-
|
|
434
|
-
# Index page template for GETs on collection
|
|
435
|
-
def index_page
|
|
436
|
-
'<html><head> <title>%s</title>
|
|
437
|
-
<meta http-equiv="content-type" content="text/html; charset=utf-8" /></head>
|
|
438
|
-
<body> <h1>%s</h1> <hr /> <table> <tr> <th class="name">Name</th>
|
|
439
|
-
<th class="size">Size</th> <th class="type">Type</th>
|
|
440
|
-
<th class="mtime">Last Modified</th> </tr> %s </table> <hr /> </body></html>'
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
# Does client allow GET redirection
|
|
444
|
-
# TODO: Get a comprehensive list in here.
|
|
445
|
-
# TODO: Allow this to be dynamic so users can add regexes to match if they know of a client
|
|
446
|
-
# that can be supported that is not listed.
|
|
447
|
-
def allows_redirect?
|
|
448
|
-
[
|
|
449
|
-
%r{cyberduck}i,
|
|
450
|
-
%r{konqueror}i
|
|
451
|
-
].any? do |regexp|
|
|
452
|
-
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
def use_compat_mkcol_response?
|
|
457
|
-
@options[:compat_mkcol] || @options[:compat_all]
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
# Returns true if using an MS client
|
|
461
|
-
def use_ms_compat_creationdate?
|
|
462
|
-
if(@options[:compat_ms_mangled_creationdate] || @options[:compat_all])
|
|
463
|
-
is_ms_client?
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
# Basic user agent testing for MS authored client
|
|
468
|
-
def is_ms_client?
|
|
469
|
-
[%r{microsoft-webdav}i, %r{microsoft office}i].any? do |regexp|
|
|
470
|
-
(request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp
|
|
471
|
-
end
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
protected
|
|
475
|
-
|
|
476
|
-
# Returns authentication credentials if available in form of [username,password]
|
|
477
|
-
# TODO: Add support for digest
|
|
478
|
-
def auth_credentials
|
|
479
|
-
auth = Rack::Auth::Basic::Request.new(request.env)
|
|
480
|
-
auth.provided? && auth.basic? ? auth.credentials : [nil,nil]
|
|
481
|
-
end
|
|
482
187
|
|
|
483
188
|
end
|
|
484
189
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class String
|
|
2
|
+
|
|
3
|
+
if RUBY_VERSION >= "1.9"
|
|
4
|
+
def force_valid_encoding
|
|
5
|
+
find_encoding(Encoding.list.to_enum)
|
|
6
|
+
end
|
|
7
|
+
else
|
|
8
|
+
def force_valid_encoding
|
|
9
|
+
self
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def find_encoding(encodings)
|
|
16
|
+
if valid_encoding?
|
|
17
|
+
self
|
|
18
|
+
else
|
|
19
|
+
force_next_encoding(encodings)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def force_next_encoding(encodings)
|
|
24
|
+
force_encoding(encodings.next)
|
|
25
|
+
find_encoding(encodings)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
data/lib/rack-webdav/version.rb
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
module RackWebDAV
|
|
2
|
-
class Version
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
# Holds information about library version.
|
|
4
|
+
module Version
|
|
5
|
+
MAJOR = 0
|
|
6
|
+
MINOR = 4
|
|
7
|
+
PATCH = 4
|
|
8
|
+
BUILD = nil
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
version = version.split('.')
|
|
8
|
-
@major, @minor, @tiny = [version[0].to_i, version[1].to_i, version[2].to_i]
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def to_s
|
|
12
|
-
"#{@major}.#{@minor}.#{@tiny}"
|
|
13
|
-
end
|
|
10
|
+
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join(".")
|
|
14
11
|
end
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
# The current library version.
|
|
14
|
+
VERSION = Version::STRING
|
|
15
|
+
|
|
17
16
|
end
|
data/lib/rack-webdav.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
require 'rubygems'
|
|
1
3
|
require 'time'
|
|
2
4
|
require 'uri'
|
|
5
|
+
require 'rexml/document'
|
|
3
6
|
require 'nokogiri'
|
|
7
|
+
require 'webrick/httputils'
|
|
4
8
|
|
|
5
9
|
require 'rack'
|
|
6
|
-
require 'rack-webdav/utils'
|
|
7
10
|
require 'rack-webdav/http_status'
|
|
8
11
|
require 'rack-webdav/resource'
|
|
12
|
+
require 'rack-webdav/file_resource'
|
|
9
13
|
require 'rack-webdav/handler'
|
|
10
14
|
require 'rack-webdav/controller'
|
|
11
15
|
|
data/rack-webdav.gemspec
CHANGED
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
s.add_dependency 'nokogiri', '>= 1.4.2'
|
|
24
24
|
s.add_dependency 'uuidtools', '~> 2.1.1'
|
|
25
25
|
s.add_dependency 'rack', '>= 1.1.0'
|
|
26
|
+
s.add_dependency("ffi-xattr", "~> 0.1")
|
|
26
27
|
|
|
27
28
|
s.add_development_dependency 'rspec'
|
|
28
29
|
|
|
@@ -32,4 +33,6 @@ Gem::Specification.new do |s|
|
|
|
32
33
|
s.add_development_dependency 'awesome_print'
|
|
33
34
|
s.add_development_dependency 'hirb'
|
|
34
35
|
s.add_development_dependency 'hirb-unicode'
|
|
36
|
+
s.add_development_dependency 'rubocop'
|
|
37
|
+
s.add_development_dependency 'rack-timeout'
|
|
35
38
|
end
|