dav4rack 0.2.11 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,341 @@
1
+ # coding: utf-8
2
+ require 'mime/types'
3
+
4
+ module DAV4Rack
5
+
6
+ class MongoResource < DAV4Rack::Resource
7
+
8
+ # @@logger = Rails.logger
9
+
10
+ def initialize(public_path, path, request, response, options)
11
+ # 'ASCII-8BIT'で渡される場合があるので'UTF-8'を指定しておく
12
+ _force_encoding!(public_path)
13
+ _force_encoding!(path)
14
+ super(public_path, path, request, response, options)
15
+ @filesystem = Mongo::GridFileSystem.new(Mongoid.database)
16
+ @collection = Mongoid.database.collection('fs.files')
17
+ if options[:bson]
18
+ @bson = options[:bson]
19
+ elsif path.length <= 1
20
+ # ルートの場合 (''の場合と'/'の場合がある)
21
+ @bson = {'filename' => root + '/'}
22
+ else
23
+ # ファイルかディレクトリが、パラメータだけでは判断できない。ので \/? が必要。
24
+ # だから、ディレクトリと同名のファイルは、作成できない。
25
+ @bson = @collection.find_one({:filename => /^#{Regexp.escape(file_path)}\/?$/}) rescue nil
26
+ end
27
+ end
28
+
29
+ def child(bson)
30
+ path = remove(bson['filename'], root)
31
+ public_path = @options[:root_uri_path] + path
32
+ @options[:bson] = bson
33
+ self.class.new(public_path, path, @request, @response, @options)
34
+ end
35
+
36
+ # If this is a collection, return the child resources.
37
+ def children
38
+ # Dir[file_path + '/*'].map do |path|
39
+ # child File.basename(path)
40
+ # end
41
+ @collection.find({:filename => /^#{Regexp.escape(@bson['filename'])}[^\/]+\/?$/}).map do |bson|
42
+ child bson
43
+ end
44
+ end
45
+
46
+ # Is this resource a collection?
47
+ def collection?
48
+ # File.directory?(file_path)
49
+ @bson && _collection?(@bson['filename'])
50
+ end
51
+
52
+ # Does this recource exist?
53
+ def exist?
54
+ # File.exist?(file_path)
55
+ @bson
56
+ end
57
+
58
+ # Return the creation time.
59
+ def creation_date
60
+ # stat.ctime
61
+ @bson['uploadDate'] || Date.new
62
+ end
63
+
64
+ # Return the time of last modification.
65
+ def last_modified
66
+ # stat.mtime
67
+ @bson['uploadDate'] || Date.new
68
+ end
69
+
70
+ # Set the time of last modification.
71
+ def last_modified=(time)
72
+ # File.utime(Time.now, time, file_path)
73
+ end
74
+
75
+ # Return an Etag, an unique hash value for this resource.
76
+ def etag
77
+ # sprintf('%x-%x-%x', stat.ino, stat.size, stat.mtime.to_i)
78
+ @bson['_id'].to_s
79
+ end
80
+
81
+ # Return the mime type of this resource.
82
+ def content_type
83
+ # if stat.directory?
84
+ # "text/html"
85
+ # else
86
+ # mime_type(file_path, DefaultMimeTypes)
87
+ # end
88
+ @bson['contentType'] || "text/html"
89
+ end
90
+
91
+ # Return the size in bytes for this resource.
92
+ def content_length
93
+ # stat.size
94
+ @bson['length'] || 0
95
+ end
96
+
97
+ # HTTP GET request.
98
+ #
99
+ # Write the content of the resource to the response.body.
100
+ def get(request, response)
101
+ raise NotFound unless exist?
102
+ # if stat.directory?
103
+ # response.body = ""
104
+ # Rack::Directory.new(root).call(request.env)[2].each do |line|
105
+ # response.body << line
106
+ # end
107
+ # response['Content-Length'] = response.body.size.to_s
108
+ # else
109
+ # file = Rack::File.new(root)
110
+ # response.body = file
111
+ # end
112
+ if collection?
113
+ response.body = "<html>"
114
+ response.body << "<h2>" + file_path.html_safe + "</h2>"
115
+ children.each do |child|
116
+ name = child.file_path.html_safe
117
+ path = child.public_path
118
+ response.body << "<a href='" + path + "'>" + name + "</a>"
119
+ response.body << "</br>"
120
+ end
121
+ response.body << "</html>"
122
+ response['Content-Length'] = response.body.size.to_s
123
+ response['Content-Type'] = 'text/html'
124
+ else
125
+ @filesystem.open(file_path, 'r') do |f|
126
+ response.body = f
127
+ response['Content-Type'] = @bson['contentType']
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ # HTTP PUT request.
134
+ #
135
+ # Save the content of the request.body.
136
+ def put(request, response)
137
+ write(request.body)
138
+ Created
139
+ end
140
+
141
+ # HTTP POST request.
142
+ #
143
+ # Usually forbidden.
144
+ def post(request, response)
145
+ raise HTTPStatus::Forbidden
146
+ end
147
+
148
+ # HTTP DELETE request.
149
+ #
150
+ # Delete this resource.
151
+ def delete
152
+ # if stat.directory?
153
+ # FileUtils.rm_rf(file_path)
154
+ # else
155
+ # File.unlink(file_path)
156
+ # end
157
+ if collection?
158
+ @collection.find({:filename => /^#{Regexp.escape(@bson['filename'])}/}).each do |bson|
159
+ @collection.remove(bson)
160
+ end
161
+ else
162
+ @collection.remove(@bson)
163
+ end
164
+ NoContent
165
+ end
166
+
167
+ # HTTP COPY request.
168
+ #
169
+ # Copy this resource to given destination resource.
170
+ def copy(dest, overwrite = false)
171
+ # if(dest.path == path)
172
+ # Conflict
173
+ # elsif(stat.directory?)
174
+ # dest.make_collection
175
+ # FileUtils.cp_r("#{file_path}/.", "#{dest.send(:file_path)}/")
176
+ # OK
177
+ # else
178
+ # exists = File.exists?(file_path)
179
+ # if(exists && !overwrite)
180
+ # PreconditionFailed
181
+ # else
182
+ # open(file_path, "rb") do |file|
183
+ # dest.write(file)
184
+ # end
185
+ # exists ? NoContent : Created
186
+ # end
187
+ # end
188
+
189
+ # ディレクトリなら末尾に「/」をつける。
190
+ # (dstにもともと「/」が付いているかどうか、クライアントに依存している)
191
+ # CarotDAV : 「/」が付いていない
192
+ # TeamFile : 「/」が付いている
193
+ dest.collection! if collection?
194
+
195
+ src = @bson['filename']
196
+ dst = dest.file_path
197
+ exists = nil
198
+
199
+ @collection.find({:filename => /^#{Regexp.escape(src)}/}).each do |bson|
200
+ src_name = bson['filename']
201
+ dst_name = dst + src_name.slice(src.length, src_name.length)
202
+
203
+ exists = @collection.find_one({:filename => dst_name}) rescue nil
204
+
205
+ return PreconditionFailed if (exists && !overwrite && !collection?)
206
+
207
+ @filesystem.open(src_name, "r") do |src|
208
+ @filesystem.open(dst_name, "w") do |dst|
209
+ dst.write(src) if src.file_length > 0
210
+ end
211
+ end
212
+
213
+ @collection.remove(exists) if exists
214
+ end
215
+
216
+ collection? ? Created : (exists ? NoContent : Created)
217
+ end
218
+
219
+ # HTTP MOVE request.
220
+ #
221
+ # Move this resource to given destination resource.
222
+ def move(dest, overwrite = false)
223
+
224
+ # ディレクトリなら末尾に「/」をつける。
225
+ # (dstにもともと「/」が付いているかどうか、クライアントに依存している)
226
+ # CarotDAV : 「/」が付いていない
227
+ # TeamFile : 「/」が付いている
228
+ dest.collection! if collection?
229
+
230
+ src = @bson['filename']
231
+ dst = dest.file_path
232
+ exists = nil
233
+
234
+ @collection.find({:filename => /^#{Regexp.escape(src)}/}).each do |bson|
235
+ src_name = bson['filename']
236
+ dst_name = dst + src_name.slice(src.length, src_name.length)
237
+
238
+ exists = @collection.find_one({:filename => dst_name}) rescue nil
239
+
240
+ # http://mongoid.org/docs/persistence/atomic.html
241
+ # http://rubydoc.info/github/mongoid/mongoid/master/Mongoid/Collection#update-instance_method
242
+ @collection.update({'_id' => bson['_id']}, {'$set' => {'filename' => dst_name}}, :safe => true)
243
+
244
+ @collection.remove(exists) if exists
245
+ end
246
+
247
+ collection? ? Created : (exists ? NoContent : Created)
248
+ end
249
+
250
+ # HTTP MKCOL request.
251
+ #
252
+ # Create this resource as collection.
253
+ def make_collection
254
+ # Dir.mkdir(file_path)
255
+ # Created
256
+
257
+ # ディレクトリなら末尾に「/」をつける。
258
+ # (dstにもともと「/」が付いているかどうか、クライアントに依存している)
259
+ # CarotDAV : 「/」が付いていない
260
+ # TeamFile : 「/」が付いている
261
+ collection!
262
+
263
+ bson = @collection.find_one({:filename => file_path}) rescue nil
264
+
265
+ # 0バイトのファイルを作成しディレクトリの代わりとする
266
+ @filesystem.open(file_path, "w") { |f| } if !bson
267
+
268
+ # @@logger.error('make_collection : ' + file_path)
269
+
270
+ Created
271
+ end
272
+
273
+ # Write to this resource from given IO.
274
+ def write(io)
275
+ # tempfile = "#{file_path}.#{Process.pid}.#{object_id}"
276
+ # open(tempfile, "wb") do |file|
277
+ # while part = io.read(8192)
278
+ # file << part
279
+ # end
280
+ # end
281
+ # File.rename(tempfile, file_path)
282
+ # ensure
283
+ # File.unlink(tempfile) rescue nil
284
+
285
+ # 同名のファイルができないように
286
+ bson = @collection.find_one({:filename => file_path}) rescue nil
287
+
288
+ @filesystem.open(file_path, "w", :content_type => _content_type(file_path)) { |f| f.write(io) }
289
+
290
+ # 同名のファイルができないように
291
+ @collection.remove(bson) if bson
292
+
293
+ end
294
+
295
+ protected
296
+
297
+ def file_path
298
+ root + path
299
+ end
300
+
301
+ # ファイル名の末尾に「/」を付加してディレクトリ(コレクション)とする
302
+ def collection!
303
+ path << '/' if !_collection?(path)
304
+ end
305
+
306
+ private
307
+
308
+ def _content_type(filename)
309
+ MIME::Types.type_for(filename).first.to_s || 'text/html'
310
+ end
311
+
312
+ def authenticate(user, pass)
313
+ if(options[:username])
314
+ options[:username] == user && options[:password] == pass
315
+ else
316
+ true
317
+ end
318
+ end
319
+
320
+ # path1の先頭からpath2を取り除く
321
+ def remove(path1, path2)
322
+ path1.slice(path2.length, path1.length)
323
+ end
324
+
325
+ def root
326
+ @options[:root]
327
+ end
328
+
329
+ # ファイル名の末尾が「/」のファイルをディレクトリ(コレクション)とする
330
+ def _collection?(path)
331
+ path && path[-1].chr == '/'
332
+ end
333
+
334
+ # 'ASCII-8BIT'で渡される場合があるので'UTF-8'を指定しておく
335
+ def _force_encoding!(str)
336
+ str.force_encoding('UTF-8')
337
+ end
338
+
339
+ end
340
+
341
+ end
@@ -0,0 +1,40 @@
1
+ require 'ostruct'
2
+
3
+ module DAV4Rack
4
+
5
+ # Simple wrapper for formatted elements
6
+ class DAVElement < OpenStruct
7
+ def [](key)
8
+ self.send(key)
9
+ end
10
+ end
11
+
12
+ module Utils
13
+ def to_element_hash(element)
14
+ ns = element.namespace
15
+ DAVElement.new(
16
+ :namespace => ns,
17
+ :name => element.name,
18
+ :ns_href => (ns.href if ns),
19
+ :children => element.children.collect{|e|
20
+ to_element_hash(e) if e.element?
21
+ }.compact,
22
+ :attributes => attributes_hash(element)
23
+ )
24
+ end
25
+
26
+ def to_element_key(element)
27
+ ns = element.namespace
28
+ "#{ns.href if ns}!!#{element.name}"
29
+ end
30
+
31
+ private
32
+ def attributes_hash(node)
33
+ node.attributes.inject({}) do |ret, (key,attr)|
34
+ ret[attr.name] = attr.value
35
+ ret
36
+ end
37
+ end
38
+ end
39
+
40
+ end
@@ -13,5 +13,5 @@ module DAV4Rack
13
13
  end
14
14
  end
15
15
 
16
- VERSION = Version.new('0.2.11')
16
+ VERSION = Version.new('0.3.0')
17
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dav4rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.11
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-06 00:00:00.000000000 Z
12
+ date: 2012-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &15276900 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 1.4.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *15276900
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.2
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: uuidtools
27
- requirement: &15293080 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 2.1.1
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *15293080
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.1.1
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rack
38
- requirement: &15292420 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,7 +53,12 @@ dependencies:
43
53
  version: 1.1.0
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *15292420
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.0
47
62
  description: WebDAV handler for Rack
48
63
  email: chrisroberts.code@gmail.com
49
64
  executables:
@@ -52,26 +67,28 @@ extensions: []
52
67
  extra_rdoc_files:
53
68
  - README.rdoc
54
69
  files:
55
- - .gitignore
56
- - LICENSE
57
70
  - dav4rack.gemspec
71
+ - README.rdoc
72
+ - CHANGELOG.rdoc
73
+ - LICENSE
74
+ - bin/dav4rack
58
75
  - lib/dav4rack.rb
59
- - lib/dav4rack/file_resource.rb
60
- - lib/dav4rack/handler.rb
61
- - lib/dav4rack/controller.rb
76
+ - lib/dav4rack/lock_store.rb
77
+ - lib/dav4rack/logger.rb
62
78
  - lib/dav4rack/http_status.rb
79
+ - lib/dav4rack/version.rb
80
+ - lib/dav4rack/remote_file.rb
81
+ - lib/dav4rack/controller.rb
63
82
  - lib/dav4rack/resource.rb
64
- - lib/dav4rack/interceptor.rb
83
+ - lib/dav4rack/lock.rb
65
84
  - lib/dav4rack/interceptor_resource.rb
66
- - lib/dav4rack/remote_file.rb
85
+ - lib/dav4rack/interceptor.rb
67
86
  - lib/dav4rack/file.rb
68
- - lib/dav4rack/lock.rb
69
- - lib/dav4rack/lock_store.rb
70
- - lib/dav4rack/logger.rb
71
- - lib/dav4rack/version.rb
72
- - bin/dav4rack
73
- - spec/handler_spec.rb
74
- - README.rdoc
87
+ - lib/dav4rack/file_resource_lock.rb
88
+ - lib/dav4rack/utils.rb
89
+ - lib/dav4rack/resources/mongo_resource.rb
90
+ - lib/dav4rack/resources/file_resource.rb
91
+ - lib/dav4rack/handler.rb
75
92
  homepage: http://github.com/chrisroberts/dav4rack
76
93
  licenses: []
77
94
  post_install_message:
@@ -92,9 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
109
  version: '0'
93
110
  requirements: []
94
111
  rubyforge_project:
95
- rubygems_version: 1.8.17
112
+ rubygems_version: 1.8.24
96
113
  signing_key:
97
114
  specification_version: 3
98
115
  summary: WebDAV handler for Rack
99
116
  test_files: []
100
- has_rdoc: true