rcs-common 9.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +49 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +1 -0
  5. data/Rakefile +27 -0
  6. data/lib/rcs-common.rb +21 -0
  7. data/lib/rcs-common/binary.rb +64 -0
  8. data/lib/rcs-common/cgi.rb +7 -0
  9. data/lib/rcs-common/component.rb +87 -0
  10. data/lib/rcs-common/crypt.rb +71 -0
  11. data/lib/rcs-common/deploy.rb +96 -0
  12. data/lib/rcs-common/diagnosticable.rb +136 -0
  13. data/lib/rcs-common/evidence.rb +261 -0
  14. data/lib/rcs-common/evidence/addressbook.rb +173 -0
  15. data/lib/rcs-common/evidence/application.rb +59 -0
  16. data/lib/rcs-common/evidence/calendar.rb +62 -0
  17. data/lib/rcs-common/evidence/call.rb +185 -0
  18. data/lib/rcs-common/evidence/camera.rb +25 -0
  19. data/lib/rcs-common/evidence/chat.rb +272 -0
  20. data/lib/rcs-common/evidence/clibpoard.rb +58 -0
  21. data/lib/rcs-common/evidence/command.rb +50 -0
  22. data/lib/rcs-common/evidence/common.rb +78 -0
  23. data/lib/rcs-common/evidence/content/camera/001.jpg +0 -0
  24. data/lib/rcs-common/evidence/content/coin/wallet_bit.dat +0 -0
  25. data/lib/rcs-common/evidence/content/coin/wallet_lite.dat +0 -0
  26. data/lib/rcs-common/evidence/content/file/Einstein.docx +0 -0
  27. data/lib/rcs-common/evidence/content/file/arabic.docx +0 -0
  28. data/lib/rcs-common/evidence/content/mouse/001.jpg +0 -0
  29. data/lib/rcs-common/evidence/content/mouse/002.jpg +0 -0
  30. data/lib/rcs-common/evidence/content/mouse/003.jpg +0 -0
  31. data/lib/rcs-common/evidence/content/mouse/004.jpg +0 -0
  32. data/lib/rcs-common/evidence/content/print/001.jpg +0 -0
  33. data/lib/rcs-common/evidence/content/screenshot/001.jpg +0 -0
  34. data/lib/rcs-common/evidence/content/screenshot/002.jpg +0 -0
  35. data/lib/rcs-common/evidence/content/screenshot/003.jpg +0 -0
  36. data/lib/rcs-common/evidence/content/url/001.jpg +0 -0
  37. data/lib/rcs-common/evidence/content/url/002.jpg +0 -0
  38. data/lib/rcs-common/evidence/content/url/003.jpg +0 -0
  39. data/lib/rcs-common/evidence/device.rb +23 -0
  40. data/lib/rcs-common/evidence/download.rb +54 -0
  41. data/lib/rcs-common/evidence/exec.rb +0 -0
  42. data/lib/rcs-common/evidence/file.rb +129 -0
  43. data/lib/rcs-common/evidence/filesystem.rb +71 -0
  44. data/lib/rcs-common/evidence/info.rb +24 -0
  45. data/lib/rcs-common/evidence/keylog.rb +84 -0
  46. data/lib/rcs-common/evidence/mail.rb +237 -0
  47. data/lib/rcs-common/evidence/mic.rb +39 -0
  48. data/lib/rcs-common/evidence/mms.rb +36 -0
  49. data/lib/rcs-common/evidence/money.rb +676 -0
  50. data/lib/rcs-common/evidence/mouse.rb +62 -0
  51. data/lib/rcs-common/evidence/password.rb +60 -0
  52. data/lib/rcs-common/evidence/photo.rb +80 -0
  53. data/lib/rcs-common/evidence/position.rb +303 -0
  54. data/lib/rcs-common/evidence/print.rb +50 -0
  55. data/lib/rcs-common/evidence/screenshot.rb +53 -0
  56. data/lib/rcs-common/evidence/sms.rb +91 -0
  57. data/lib/rcs-common/evidence/url.rb +133 -0
  58. data/lib/rcs-common/fixnum.rb +48 -0
  59. data/lib/rcs-common/gridfs.rb +294 -0
  60. data/lib/rcs-common/heartbeat.rb +96 -0
  61. data/lib/rcs-common/keywords.rb +50 -0
  62. data/lib/rcs-common/mime.rb +65 -0
  63. data/lib/rcs-common/mongoid.rb +19 -0
  64. data/lib/rcs-common/pascalize.rb +62 -0
  65. data/lib/rcs-common/path_utils.rb +67 -0
  66. data/lib/rcs-common/resolver.rb +40 -0
  67. data/lib/rcs-common/rest.rb +17 -0
  68. data/lib/rcs-common/sanitize.rb +42 -0
  69. data/lib/rcs-common/serializer.rb +404 -0
  70. data/lib/rcs-common/signature.rb +141 -0
  71. data/lib/rcs-common/stats.rb +94 -0
  72. data/lib/rcs-common/symbolize.rb +10 -0
  73. data/lib/rcs-common/systemstatus.rb +136 -0
  74. data/lib/rcs-common/temporary.rb +13 -0
  75. data/lib/rcs-common/time.rb +24 -0
  76. data/lib/rcs-common/trace.rb +138 -0
  77. data/lib/rcs-common/trace.yaml +42 -0
  78. data/lib/rcs-common/updater/client.rb +354 -0
  79. data/lib/rcs-common/updater/dsl.rb +178 -0
  80. data/lib/rcs-common/updater/payload.rb +79 -0
  81. data/lib/rcs-common/updater/server.rb +126 -0
  82. data/lib/rcs-common/updater/shared_key.rb +55 -0
  83. data/lib/rcs-common/updater/tmp_dir.rb +13 -0
  84. data/lib/rcs-common/utf16le.rb +83 -0
  85. data/lib/rcs-common/version.rb +5 -0
  86. data/lib/rcs-common/winfirewall.rb +235 -0
  87. data/rcs-common.gemspec +64 -0
  88. data/spec/gridfs_spec.rb +637 -0
  89. data/spec/mongoid.yaml +6 -0
  90. data/spec/signature_spec.rb +105 -0
  91. data/spec/spec_helper.rb +22 -0
  92. data/spec/updater_spec.rb +80 -0
  93. data/tasks/deploy.rake +21 -0
  94. data/tasks/protect.rake +90 -0
  95. data/test/helper.rb +17 -0
  96. data/test/test_binary.rb +107 -0
  97. data/test/test_cgi.rb +14 -0
  98. data/test/test_crypt.rb +125 -0
  99. data/test/test_evidence.rb +52 -0
  100. data/test/test_evidence_manager.rb +119 -0
  101. data/test/test_fixnum.rb +35 -0
  102. data/test/test_keywords.rb +137 -0
  103. data/test/test_mime.rb +49 -0
  104. data/test/test_pascalize.rb +100 -0
  105. data/test/test_path_utils.rb +24 -0
  106. data/test/test_rcs-common.rb +7 -0
  107. data/test/test_sanitize.rb +40 -0
  108. data/test/test_serialization.rb +20 -0
  109. data/test/test_stats.rb +90 -0
  110. data/test/test_symbolize.rb +20 -0
  111. data/test/test_systemstatus.rb +35 -0
  112. data/test/test_time.rb +56 -0
  113. data/test/test_trace.rb +25 -0
  114. data/test/test_utf16le.rb +71 -0
  115. data/test/test_winfirewall.rb +68 -0
  116. metadata +423 -0
@@ -0,0 +1,64 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rcs-common/version"
4
+
5
+ gemspec = Gem::Specification.new do |s|
6
+ s.name = "rcs-common"
7
+ s.version = RCS::Common::VERSION
8
+ s.authors = ["alor", "daniele"]
9
+ s.email = ["alor@hackingteam.it", "daniele@hackingteam.it"]
10
+ s.homepage = ""
11
+ s.summary = %q{rcs-common}
12
+ s.description = %q{Common components for the RCS Backend}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency("log4r", ">= 1.1.9")
20
+ s.add_dependency('eventmachine', '= 1.0.3')
21
+ s.add_dependency('mongoid', '= 4.0.1')
22
+ s.add_dependency('mime-types')
23
+ s.add_dependency('sys-filesystem')
24
+ s.add_dependency('sys-cpu')
25
+ s.add_dependency('ffi')
26
+ s.add_dependency('mail')
27
+ s.add_dependency('sbdb')
28
+ s.add_dependency('yajl-ruby')
29
+ s.add_dependency('em-http-server')
30
+
31
+ s.add_development_dependency("bundler")
32
+ s.add_development_dependency('rake')
33
+ s.add_development_dependency('test-unit')
34
+ s.add_development_dependency('simplecov')
35
+ s.add_development_dependency('rspec')
36
+ s.add_development_dependency('pry')
37
+ end
38
+
39
+ if ENV['PROTECTED']
40
+ files = gemspec.files.dup
41
+
42
+ gemspec.instance_variable_set(:'@test_files', [])
43
+ gemspec.instance_variable_set(:'@files', [])
44
+
45
+ exclusions = [
46
+ /.gitignore/,
47
+ /.ruby-version/,
48
+ /Rakefile/,
49
+ /lib\/rcs-common\/evidence\/content\//,
50
+ /^test\//,
51
+ /^tasks\//,
52
+ /^spec\//
53
+ ]
54
+
55
+ files.reject! do |path|
56
+ exclusions.find { |regexp| path =~ regexp }
57
+ end
58
+
59
+ files.concat(Dir["lib/rgloader/**/*"])
60
+
61
+ gemspec.instance_variable_set(:'@files', files)
62
+ end
63
+
64
+ gemspec
@@ -0,0 +1,637 @@
1
+ require 'spec_helper'
2
+ require 'rcs-common/gridfs'
3
+
4
+ module RCS::Common::GridFS
5
+
6
+ describe 'compatibility' do
7
+
8
+ begin
9
+ # Note: use the modified version of mongo-ruby-driver (with append support)
10
+ # path = File.expand_path('~/.rvm/gems/ruby-2.0.0-p451@rcs-project/bundler/gems/mongo-ruby-driver-b1d59bfe1700/lib')
11
+ # $LOAD_PATH << path if File.exist?(path)
12
+
13
+ require 'mongo'
14
+ rescue LoadError
15
+ $mongo_gem_missing = true
16
+ end
17
+
18
+ break if $mongo_gem_missing
19
+
20
+ let(:bucket) { Bucket.new }
21
+
22
+ let(:db_name) { bucket.session.options[:database] }
23
+
24
+ let(:mongo_client_db) { Mongo::MongoClient.new[db_name] }
25
+
26
+ let(:grid) { Mongo::Grid.new(mongo_client_db)}
27
+
28
+ let(:grid_filesystem) { Mongo::GridFileSystem.new(mongo_client_db) }
29
+
30
+ let(:chunk_size) { Bucket::DEFAULT_CHUNK_SIZE }
31
+
32
+ it 'is compatibile with mongo grid#put' do
33
+ data = 'foo bar'
34
+ id = grid.put(data)
35
+ expect(bucket.get(id.to_s).read).to eq(data)
36
+
37
+ data = ('bar'*chunk_size)+'foo'
38
+ id = grid.put(data)
39
+ expect(bucket.get(id.to_s).read).to eq(data)
40
+ end
41
+
42
+ it 'is compatibile with mongo grid#get' do
43
+ data = 'foo bar'
44
+ id = bucket.put(data)
45
+ id = ::BSON::ObjectId.from_string(id.to_s)
46
+ expect(grid.get(id).read).to eq(data)
47
+
48
+ data = ('bar'*chunk_size)+'foo'
49
+ id = bucket.put(data)
50
+ id = ::BSON::ObjectId.from_string(id.to_s)
51
+ expect(grid.get(id).read).to eq(data)
52
+ end
53
+
54
+ it 'is compatibile with mongo file#read' do
55
+ data = ('bar'*chunk_size*5)+'foo'
56
+
57
+ id = grid.put(data)
58
+
59
+ bucket_file = bucket.get(id.to_s)
60
+ grid_file = grid.get(id)
61
+
62
+ loop do
63
+ bytes = rand(0..1000)
64
+ buff1 = grid_file.read(bytes)
65
+ buff2 = bucket_file.read(bytes)
66
+ expect(buff1).to eq(buff2)
67
+ break if buff1 == '' or buff2 == ''
68
+ end
69
+ end
70
+
71
+ it 'is compatibile with mongo-ruby-driver append' do
72
+ data = "foo"*chunk_size
73
+
74
+ id = bucket.put('a')
75
+ id2 = ::BSON::ObjectId.new
76
+ fs = grid_filesystem.open(id2, 'w') { |f| f.write('a') }
77
+
78
+ index, seek = 0, (chunk_size / 2 - 10)
79
+
80
+ loop {
81
+ cnt = data[index..index+seek-1]
82
+ break if cnt.nil? or cnt.size == 0
83
+ index += seek
84
+
85
+ bucket.append(id, cnt)
86
+ grid_filesystem.open(id2, 'a') { |f| f.write(cnt) }
87
+
88
+ expect(bucket.content(id)).to eq('a'+data[0..index - 1])
89
+ expect(grid_filesystem.open(id2, 'r') { |f| f.read }).to eq('a'+data[0..index - 1])
90
+ }
91
+ end
92
+ end
93
+
94
+ # Keep it at least >= 3 in these tests
95
+ $chunk_size = 5
96
+
97
+ describe File do
98
+
99
+ before do
100
+ Bucket.__send__(:remove_const, :DEFAULT_CHUNK_SIZE)
101
+ Bucket.const_set(:DEFAULT_CHUNK_SIZE, $chunk_size)
102
+ end
103
+
104
+ let(:bucket) { Bucket.new }
105
+
106
+ let(:content) { ('a'*$chunk_size)+'b' }
107
+
108
+ let(:file) { bucket.get(bucket.put(content)) }
109
+
110
+ describe '#eof?' do
111
+
112
+ it 'is false when there are bytes to read' do
113
+ expect(file.eof?).to be_falsey
114
+ end
115
+
116
+ context 'when file#read has been called' do
117
+
118
+ before { file.read }
119
+
120
+ it 'is true' do
121
+ expect(file.eof?).to be_truthy
122
+ end
123
+ end
124
+
125
+ context 'when the file has been read sequentially' do
126
+
127
+ let(:content) { ('a'*$chunk_size)+('b'*$chunk_size)+'c' }
128
+
129
+ let(:file) { bucket.get(bucket.put(content)) }
130
+
131
+ it 'is false until the are no more bytes to read' do
132
+ file.read(1)
133
+ expect(file.eof?).to be_falsey
134
+ file.read($chunk_size**2)
135
+ expect(file.eof?).to be_truthy
136
+
137
+ file.rewind
138
+ file.read(1)
139
+ expect(file.eof?).to be_falsey
140
+ file.read($chunk_size*2)
141
+ expect(file.eof?).to be_truthy
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '#read' do
147
+
148
+ it 'reads the entire file without parameters' do
149
+ expect(file.read).to eq(content)
150
+ end
151
+
152
+ context 'when the content is binary' do
153
+
154
+ let(:content_size) { $chunk_size*20+$chunk_size/2 }
155
+
156
+ let(:content) do
157
+ content = ""
158
+ content_size.times { content << rand(0..255).chr }
159
+ content
160
+ end
161
+
162
+ let(:file) { bucket.get(bucket.put(content)) }
163
+
164
+ def print_bytes(str)
165
+ str2 = []
166
+ str.bytes.each { |b| str2 << b.to_i }
167
+ str2.join(", ")
168
+ end
169
+
170
+ it 'reads the file sequentially (binary)' do
171
+ readed = ""
172
+ loop {
173
+ break if file.eof?;
174
+ readed << file.read($chunk_size/2)
175
+ }
176
+ expect(readed).to eq(content)
177
+ end
178
+ end
179
+
180
+ it 'reads the file sequentially' do
181
+ expect(file.read($chunk_size)).to eq('a'*$chunk_size)
182
+ expect(file.read).to eq('b')
183
+
184
+ file.rewind
185
+ expect(file.read($chunk_size)).to eq('a'*$chunk_size)
186
+ expect(file.read($chunk_size)).to eq('b')
187
+
188
+ file.rewind
189
+ expect(file.read($chunk_size-1)).to eq(('a'*($chunk_size-1)))
190
+ expect(file.read).to eq('ab')
191
+ expect(file.read).to be_empty
192
+
193
+ file.rewind
194
+ $chunk_size.times { expect(file.read(1)).to eq('a') }
195
+ expect(file.read(1)).to eq('b')
196
+ expect(file.read(1)).to be_empty
197
+
198
+ file.rewind
199
+ expect(file.read($chunk_size**2)).to eq(content)
200
+ end
201
+ end
202
+ end
203
+
204
+ describe Bucket do
205
+
206
+ before do
207
+ described_class.__send__(:remove_const, :DEFAULT_CHUNK_SIZE)
208
+ described_class.const_set(:DEFAULT_CHUNK_SIZE, $chunk_size)
209
+ end
210
+
211
+ it "deal with different encodings" do
212
+ bucket = Bucket.new
213
+
214
+ non_utf8_string = "- Men\xFC -"
215
+ expect(non_utf8_string.valid_encoding?).to be_falsey
216
+ id = bucket.put non_utf8_string
217
+ file = bucket.get(id)
218
+ expect(file.read.encoding.to_s).to eq('ASCII-8BIT')
219
+
220
+ utf8_string = "ciao"
221
+ id = bucket.put utf8_string
222
+ file = bucket.get(id)
223
+ expect(file.read.encoding.to_s).to eq('UTF-8')
224
+ end
225
+
226
+ describe '#append' do
227
+
228
+ let(:bucket) { Bucket.new }
229
+
230
+ it 'appends the given data to the file' do
231
+ content = 'a'*$chunk_size
232
+ file_id = bucket.put(content)
233
+
234
+ content << 'b'
235
+ bucket.append(file_id, 'b')
236
+ expect(bucket.content(file_id)).to eq(content)
237
+
238
+ content << 'c'*$chunk_size
239
+ bucket.append(file_id, 'c'*$chunk_size)
240
+ expect(bucket.content(file_id)).to eq(content)
241
+
242
+ content << 'd'*($chunk_size-1)
243
+ bucket.append(file_id, 'd'*($chunk_size-1))
244
+ expect(bucket.content(file_id)).to eq(content)
245
+ end
246
+
247
+ it 'updates the file length' do
248
+ content = 'a'*$chunk_size
249
+ file_id = bucket.put(content[1])
250
+ expect(bucket.get(file_id).length).to eq(1)
251
+ bucket.append(file_id, content[1..-1])
252
+ expect(bucket.get(file_id).length).to eq(content.bytesize)
253
+
254
+ content = 'a'*$chunk_size * 2
255
+ file_id = bucket.put(content[0..$chunk_size-1])
256
+ expect(bucket.get(file_id).length).to eq($chunk_size)
257
+ bucket.append(file_id, content[$chunk_size..-1])
258
+ expect(bucket.get(file_id).length).to eq($chunk_size * 2)
259
+ end
260
+
261
+ it 'updates the md5 (by default)' do
262
+ content = 'a'*$chunk_size
263
+ file_id = bucket.put(content[1])
264
+ expect(bucket.get(file_id).md5).to eq(Digest::MD5.hexdigest('a'))
265
+ bucket.append(file_id, content[1..-1])
266
+ expect(bucket.get(file_id).md5).to eq(Digest::MD5.hexdigest(content))
267
+ end
268
+
269
+ context 'when the file is missing' do
270
+
271
+ it 'raises an error' do
272
+ fake_id = ::BSON::ObjectId.new
273
+ expect { bucket.append(fake_id, 'foo') }.to raise_error(/not found/)
274
+ end
275
+
276
+ context 'when option :create is passed' do
277
+
278
+ it 'creates the file' do
279
+ fake_id = ::BSON::ObjectId.new
280
+ expect { bucket.append(fake_id, 'foo', create: true) }.not_to raise_error
281
+
282
+ fake_id = ::BSON::ObjectId.new
283
+ file_id, length = bucket.append(fake_id, 'foo', create: {filename: 'bar'})
284
+ file = bucket.get(file_id)
285
+ expect(file.filename).to eq('bar')
286
+ expect(file.content).to eq('foo')
287
+ end
288
+ end
289
+ end
290
+
291
+ context "when {md5: false} is given as options" do
292
+
293
+ it 'sets the md5 attribute to nil' do
294
+ content = 'a'*$chunk_size
295
+ file_id = bucket.put(content[1])
296
+ expect(bucket.get(file_id).md5).to eq(Digest::MD5.hexdigest('a'))
297
+ bucket.append(file_id, content[1..-1], md5: false)
298
+ expect(bucket.get(file_id).md5).to be_nil
299
+ end
300
+ end
301
+ end
302
+
303
+ describe '#delete' do
304
+
305
+ let(:bucket) { Bucket.new }
306
+
307
+ let(:content) { ('a'*$chunk_size)+'b' }
308
+
309
+ it 'returns nil when the file exists' do
310
+ id = bucket.put(content)
311
+ expect(bucket.delete(id)).to be_nil
312
+ end
313
+
314
+ it 'returns nil when the file is missing' do
315
+ id = ::BSON::ObjectId.new
316
+ expect(bucket.delete(id)).to be_nil
317
+ end
318
+
319
+ it 'removes the file and its chunks' do
320
+ id = bucket.put(content)
321
+ expect(id).not_to be_nil
322
+ bucket.delete(id)
323
+ expect(bucket.get(id)).to be_nil
324
+ expect(bucket.files_collection.find(_id: id).count).to be_zero
325
+ expect(bucket.chunks_collection.find(files_id: id).count).to be_zero
326
+ end
327
+
328
+ it 'does not deletes other files' do
329
+ id = bucket.put(content)
330
+ id2 = bucket.put(content+'c')
331
+
332
+ bucket.delete(id)
333
+
334
+ expect(bucket.get(id)).to be_nil
335
+ expect(bucket.get(id2).read).to eq(content+'c')
336
+ end
337
+ end
338
+
339
+ describe '#drop' do
340
+
341
+ it 'removes the collections' do
342
+ bucket = Bucket.new(nil, lazy: false)
343
+ bucket.drop
344
+ expect(bucket.session.collections).to be_empty
345
+ end
346
+
347
+ context 'when the collections are missing' do
348
+
349
+ let(:bucket) do
350
+ bucket = Bucket.new
351
+ end
352
+
353
+ context '#get' do
354
+
355
+ it 'returns nil (without errors)' do
356
+ expect(bucket.get(::BSON::ObjectId.new)).to be_nil
357
+ end
358
+ end
359
+
360
+ context '#put' do
361
+
362
+ it 'creates the collections and the indexes' do
363
+ bucket.put 'foo bar'
364
+ expect(bucket.session['fs.chunks'].indexes.count).to eq(2)
365
+ expect(bucket.session['fs.files'].indexes.count).to eq(2)
366
+ end
367
+ end
368
+ end
369
+ end
370
+
371
+ describe '#initialize' do
372
+
373
+ it 'creates the requied collections with the default name' do
374
+ session = Bucket.new(nil, lazy: false).session
375
+ names = session.collections.map(&:name).sort
376
+ expect(names).to eq(['fs.chunks', 'fs.files'])
377
+ end
378
+
379
+ it 'creates the requied collections with the given prefix' do
380
+ session = Bucket.new('foo.bar', lazy: false).session
381
+ names = session.collections.map(&:name).sort
382
+ expect(names).to eq(['foo.bar.chunks', 'foo.bar.files'])
383
+ end
384
+
385
+ it 'creates the indexes' do
386
+ session = Bucket.new(nil, lazy: false).session
387
+
388
+ db_name = session.options[:database]
389
+
390
+ chunks_indexes = session['fs.chunks'].indexes
391
+ files_indexes = session['fs.files'].indexes
392
+
393
+ expect(chunks_indexes.count).to eq(2)
394
+ expect(chunks_indexes.to_a[1]).to eq("v"=>1, "key"=>{"files_id"=>1, "n"=>1}, "unique"=>true, "ns"=>"#{db_name}.fs.chunks", "name"=>"files_id_1_n_1")
395
+
396
+ expect(files_indexes.count).to eq(2)
397
+ expect(files_indexes.to_a[1]).to eq("v"=>1, "key"=>{"filename"=>1}, "ns"=>"#{db_name}.fs.files", "background"=>true, "name"=>"filename_1")
398
+ end
399
+
400
+ context 'when nil is passed as name' do
401
+
402
+ it 'creates the requied collections with the default name' do
403
+ session = Bucket.new(nil, lazy: false).session
404
+ names = session.collections.map(&:name).sort
405
+ expect(names).to eq(['fs.chunks', 'fs.files'])
406
+ end
407
+ end
408
+
409
+ context 'when a blank string is passed as name' do
410
+
411
+ it 'creates the requied collections with the default name' do
412
+ session = Bucket.new(' ', lazy: false).session
413
+ names = session.collections.map(&:name).sort
414
+ expect(names).to eq(['fs.chunks', 'fs.files'])
415
+ end
416
+ end
417
+
418
+ context 'when lazy' do
419
+
420
+ it 'does not create the collections' do
421
+ session = Bucket.new('fs', lazy: true).session
422
+ expect(session.collections).to be_empty
423
+ end
424
+ end
425
+ end
426
+
427
+ describe '#md5' do
428
+
429
+ let(:bucket) { Bucket.new }
430
+
431
+ let(:content) { "foo bar" }
432
+
433
+ let(:md5) { Digest::MD5.hexdigest(content) }
434
+
435
+ it 'gets the md5 of a stored file' do
436
+ id = bucket.put(content)
437
+ expect(bucket.md5(id)).to eq(md5)
438
+ end
439
+
440
+ it 'does not read the md5 attribute on the file document' do
441
+ id = bucket.put(content)
442
+ bucket.files_collection.find(_id: id).update('$set' => {md5: 'foo'})
443
+
444
+ expect(bucket.md5(id)).to eq(md5)
445
+ expect(bucket.get(id).md5).to eq('foo')
446
+ end
447
+ end
448
+
449
+ describe '#get' do
450
+
451
+ let(:bucket) { Bucket.new }
452
+
453
+ let(:content) { "foo bar" }
454
+
455
+ it 'retuns a object with a read method' do
456
+ id = bucket.put(content)
457
+ expect(bucket.get(id)).to respond_to(:read)
458
+ end
459
+
460
+ it 'returns nil when nothing is found' do
461
+ id = ::BSON::ObjectId.new
462
+ expect(bucket.get(id)).to be_nil
463
+ end
464
+
465
+ it 'works if the given id is a string' do
466
+ id = bucket.put(content)
467
+ expect(bucket.get(id.to_s)).not_to be_nil
468
+ end
469
+
470
+ it 'works if the given id is a (Moped::)BSON::ObjectId' do
471
+ id = bucket.put(content)
472
+ id = BSON ? ::BSON::ObjectId.from_string(id.to_s) : ::BSON::ObjectId.from_string(id.to_s)
473
+ expect(bucket.get(id)).not_to be_nil
474
+ end
475
+
476
+ it 'does not works with filenames (raise an error)' do
477
+ id = bucket.put(content, filename: 'foo.bar')
478
+ expect{ bucket.get('foo.bar') }.to raise_error(BSON::ObjectId::Invalid)
479
+ end
480
+ end
481
+
482
+ describe '#put' do
483
+
484
+ let(:bucket) { Bucket.new }
485
+
486
+ let(:now) { Time.now.utc }
487
+
488
+ context 'the file size is 7 bytes (< DEFAULT_CHUNK_SIZE)' do
489
+
490
+ let(:content) { "foo bar" }
491
+
492
+ it 'returns nil when nil (or empty string) is passed as content' do
493
+ expect(bucket.put(nil)).to be_nil
494
+ expect(bucket.files_collection.find.count).to be_zero
495
+ expect(bucket.chunks_collection.find.count).to be_zero
496
+ end
497
+
498
+ it 'stores a file with the given (valid) attributes' do
499
+ id = bucket.put(content, filename: 'prova.txt', upload_date: now, metadata: {a: 1})
500
+ file = bucket.get(id)
501
+
502
+ expect(file.read).to eq(content)
503
+ expect(file.filename).to eq('prova.txt')
504
+ expect(file.upload_date.to_i).to eq(now.to_i)
505
+ expect(file.metadata).to eq({'a' => 1})
506
+ end
507
+
508
+ it 'raise an error with invalid attributes' do
509
+ id = bucket.put(content, foo: 'bar', content_type: 'text')
510
+ file = bucket.get(id)
511
+
512
+ expect{ file.foo }.to raise_error(NoMethodError)
513
+ expect(file.content_type).to eq('text')
514
+ end
515
+
516
+ it 'forces the aliases attribute to be an array' do
517
+ id = bucket.put(content, aliases: 'bar')
518
+ file = bucket.get(id)
519
+
520
+ expect(file.aliases).to eq(['bar'])
521
+
522
+ id = bucket.put(content, aliases: ['bar', 'foo'])
523
+ file = bucket.get(id)
524
+
525
+ expect(file.aliases).to eq(['bar', 'foo'])
526
+ end
527
+
528
+
529
+ it 'forces the metadata attribute to be an hash when its empty' do
530
+ id = bucket.put(content, metadata: '')
531
+ file = bucket.get(id)
532
+
533
+ expect(file.metadata).to eq({})
534
+
535
+ id = bucket.put(content, metadata: nil)
536
+ file = bucket.get(id)
537
+
538
+ expect(file.metadata).to eq({})
539
+ end
540
+
541
+ it 'does not overwrite calculated attributes' do
542
+ id = bucket.put(content, md5: 'foo', chunk_size: 'foo', chunkSize: 'foo', length: 'foo')
543
+ file = bucket.get(id)
544
+
545
+ expect(file.md5).to eq(Digest::MD5.hexdigest(content))
546
+ expect(file.length).to eq(content.bytesize)
547
+ expect(file.chunk_size).to eq($chunk_size)
548
+ expect(file.chunkSize).to eq($chunk_size)
549
+ end
550
+
551
+ it 'uses default values for some attributes' do
552
+ id = bucket.put(content, filename: 'prova.txt')
553
+ file = bucket.get(id)
554
+
555
+ expect(file.content_type).to eq('application/octet-stream')
556
+ expect(file.aliases).to eq([])
557
+ expect(file.metadata).to eq({})
558
+ end
559
+ end
560
+
561
+ context 'the file size is DEFAULT_CHUNK_SIZE bytes long' do
562
+
563
+ let(:content) { 'a'*$chunk_size }
564
+
565
+ it 'uses only a chunk' do
566
+ id = bucket.put(content)
567
+ expect(bucket.chunks_collection.find(files_id: id).count).to eq(1)
568
+ end
569
+
570
+ it 'the chunk number is zero' do
571
+ id = bucket.put(content)
572
+ expect(bucket.chunks_collection.find(files_id: id).first['n']).to eq(0)
573
+ end
574
+
575
+ it 'stores the files correctly' do
576
+ id = bucket.put(content)
577
+ file = bucket.get(id)
578
+
579
+ expect(file.read).to eq(content)
580
+ expect(file.md5).to eq(bucket.md5(id))
581
+ expect(file.length).to eq($chunk_size)
582
+ end
583
+ end
584
+
585
+ context 'the file size is DEFAULT_CHUNK_SIZE + 1 bytes long' do
586
+
587
+ let(:content) { ('a'*$chunk_size)+'b' }
588
+
589
+ it 'uses 2 chunks' do
590
+ id = bucket.put(content)
591
+ expect(bucket.chunks_collection.find(files_id: id).count).to eq(2)
592
+ end
593
+
594
+ it 'numbers the chunks correctly' do
595
+ id = bucket.put(content)
596
+ numbers = bucket.chunks_collection.find(files_id: id).map { |doc| doc['n'] }
597
+ expect(numbers).to eq([0, 1])
598
+ end
599
+
600
+ it 'stores the files correctly' do
601
+ id = bucket.put(content)
602
+ file = bucket.get(id)
603
+
604
+ expect(file.read).to eq(content)
605
+ expect(file.md5).to eq(bucket.md5(id))
606
+ expect(file.length).to eq($chunk_size + 1)
607
+ end
608
+ end
609
+
610
+
611
+ context 'the file size is DEFAULT_CHUNK_SIZE * 2 bytes long' do
612
+
613
+ let(:content) { 'a'*$chunk_size*2 }
614
+
615
+ it 'uses 2 chunks' do
616
+ id = bucket.put(content)
617
+ expect(bucket.chunks_collection.find(files_id: id).count).to eq(2)
618
+ end
619
+
620
+ it 'numbers the chunks correctly' do
621
+ id = bucket.put(content)
622
+ numbers = bucket.chunks_collection.find(files_id: id).map { |doc| doc['n'] }
623
+ expect(numbers).to eq([0, 1])
624
+ end
625
+
626
+ it 'stores the files correctly' do
627
+ id = bucket.put(content)
628
+ file = bucket.get(id)
629
+
630
+ expect(file.read).to eq(content)
631
+ expect(file.md5).to eq(bucket.md5(id))
632
+ expect(file.length).to eq($chunk_size*2)
633
+ end
634
+ end
635
+ end
636
+ end
637
+ end