icfs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/icfs/store.rb ADDED
@@ -0,0 +1,278 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ #
13
+ module ICFS
14
+
15
+ ##########################################################################
16
+ # Permanent store for items
17
+ #
18
+ # Provides storage for:
19
+ # * Case
20
+ # * Log
21
+ # * Entry
22
+ # * Attached files
23
+ # * Action
24
+ # * Indexes
25
+ #
26
+ # @abstract
27
+ #
28
+ class Store
29
+
30
+ ###############################################
31
+ # Read a case
32
+ #
33
+ # @param cid [String] caseid
34
+ # @param lnum [Integer] Log number
35
+ # @return [String] The JSON encoded item
36
+ #
37
+ def case_read(cid, lnum); _read(_case(cid, lnum)); end
38
+
39
+
40
+ ###############################################
41
+ # Write a case
42
+ #
43
+ # @param cid [String] caseid
44
+ # @param lnum [Integer] Log number
45
+ # @param item [String] the JSON encoded item
46
+ #
47
+ def case_write(cid, lnum, item); _write(_case(cid, lnum), item); end
48
+
49
+
50
+ ###############################################
51
+ # Read a log
52
+ #
53
+ # @param cid [String] caseid
54
+ # @param lnum [Integer] Log number
55
+ # @return [String] The JSON encoded item
56
+ #
57
+ def log_read(cid, lnum); _read(_log(cid, lnum)); end
58
+
59
+
60
+ ###############################################
61
+ # Write a log
62
+ #
63
+ # @param cid [String] caseid
64
+ # @param lnum [Integer] Log number
65
+ # @param item [String] the JSON encoded item
66
+ #
67
+ def log_write(cid, lnum, item); _write(_log(cid, lnum), item); end
68
+
69
+
70
+ ###############################################
71
+ # Read an entry
72
+ #
73
+ # @param cid [String] caseid
74
+ # @param enum [Integer] Entry number
75
+ # @param lnum [Integer] Log number
76
+ # @return [String] The JSON encoded item
77
+ #
78
+ def entry_read(cid, enum, lnum); _read(_entry(cid, enum, lnum)); end
79
+
80
+
81
+ ###############################################
82
+ # Write an entry
83
+ #
84
+ # @param cid [String] caseid
85
+ # @param enum [Integer] Entry number
86
+ # @param lnum [Integer] Log number
87
+ # @param item [String] the JSON encoded item
88
+ #
89
+ def entry_write(cid, enum, lnum, item)
90
+ _write(_entry(cid, enum, lnum), item)
91
+ end
92
+
93
+
94
+ ###############################################
95
+ # Read a file
96
+ #
97
+ # @param cid [String] caseid
98
+ # @param enum [Integer] Entry number
99
+ # @param lnum [Integer] Log number
100
+ # @param fnum [Integer] File number
101
+ # @return [File,Tempfile] Read only copy of the file
102
+ #
103
+ def file_read(cid, enum, lnum, fnum); raise NotImplementedError; end
104
+
105
+
106
+ ###############################################
107
+ # Write a file
108
+ #
109
+ # @param cid [String] caseid
110
+ # @param enum [Integer] Entry number
111
+ # @param lnum [Integer] Log number
112
+ # @param fnum [Integer] File number
113
+ # @param tmpf [Tempfile] A Tempfile obtained from #tempfile
114
+ #
115
+ def file_write(cid, enum, lnum, fnum, tmpf); raise NotImplementedError; end
116
+
117
+
118
+ ###############################################
119
+ # Read an action
120
+ #
121
+ # @param cid [String] caseid
122
+ # @param anum [Integer] Action number
123
+ # @param lnum [Integer] Log number
124
+ # @return [String] The JSON encoded item
125
+ #
126
+ def action_read(cid, anum, lnum)
127
+ _read(_action(cid, anum, lnum))
128
+ end
129
+
130
+
131
+ ###############################################
132
+ # Write an action
133
+ #
134
+ # @param cid [String] caseid
135
+ # @param anum [Integer] Action number
136
+ # @param lnum [Integer] Log number
137
+ # @param item [String] the JSON encoded item
138
+ #
139
+ def action_write(cid, anum, lnum, item)
140
+ _write(_action(cid, anum, lnum), item)
141
+ end
142
+
143
+
144
+ ###############################################
145
+ # Read an Index
146
+ #
147
+ # @param cid [String] caseid
148
+ # @param xnum [Integer] Index number
149
+ # @param lnum [Integer] Log number
150
+ # @return [String] the JSON encoded item
151
+ #
152
+ def index_read(cid, xnum, lnum)
153
+ _read(_index(cid, xnum, lnum))
154
+ end
155
+
156
+
157
+ ###############################################
158
+ # Write an Index
159
+ #
160
+ # @param cid [String] caseid
161
+ # @param xnum [Integer] Index number
162
+ # @param lnum [Integer] Log number
163
+ # @param item [String] the JSON encoded item
164
+ #
165
+ def index_write(cid, xnum, lnum, item)
166
+ _write(_index(cid, xnum, lnum), item)
167
+ end
168
+
169
+
170
+ ###############################################
171
+ # Get a Tempfile to use to write files
172
+ #
173
+ # @return [Tempfile] a Tempfile which can be written and passed to
174
+ # #file_write
175
+ #
176
+ def tempfile; raise NotImplementedError; end
177
+
178
+
179
+ private
180
+
181
+
182
+ ###############################################
183
+ # Read an item
184
+ #
185
+ def _read(path); raise NotImplementedError; end
186
+
187
+
188
+ ###############################################
189
+ # Write an item
190
+ #
191
+ def _write(path, item); raise NotImplementedError; end
192
+
193
+
194
+ ###############################################
195
+ # Path for case
196
+ #
197
+ def _case(cid, lnum)
198
+ [
199
+ @base,
200
+ cid,
201
+ 'c'.freeze,
202
+ '%d.json'.freeze % lnum
203
+ ].join('/'.freeze)
204
+ end
205
+
206
+
207
+ ###############################################
208
+ # Path for log
209
+ #
210
+ def _log(cid, lnum)
211
+ [
212
+ @base,
213
+ cid,
214
+ 'l'.freeze,
215
+ '%d.json'.freeze % lnum
216
+ ].join('/'.freeze)
217
+ end
218
+
219
+
220
+ ###############################################
221
+ # Path for entry
222
+ #
223
+ def _entry(cid, enum, lnum)
224
+ [
225
+ @base,
226
+ cid,
227
+ 'e'.freeze,
228
+ enum.to_s,
229
+ '%d.json'.freeze % lnum
230
+ ].join('/'.freeze)
231
+ end
232
+
233
+
234
+ ###############################################
235
+ # Path for file
236
+ #
237
+ def _file(cid, enum, lnum, fnum)
238
+ [
239
+ @base,
240
+ cid,
241
+ 'e'.freeze,
242
+ enum.to_s,
243
+ '%d-%d.bin'.freeze % [lnum, fnum]
244
+ ].join('/'.freeze)
245
+ end
246
+
247
+
248
+ ###############################################
249
+ # Filename for action
250
+ #
251
+ def _action(cid, anum, lnum)
252
+ [
253
+ @base,
254
+ cid,
255
+ 'a'.freeze,
256
+ anum.to_s,
257
+ lnum.to_s + '.json'.freeze
258
+ ].join('/'.freeze)
259
+ end
260
+
261
+
262
+ ###############################################
263
+ # Filename for index
264
+ #
265
+ def _index(cid, xnum, lnum)
266
+ [
267
+ @base,
268
+ cid,
269
+ 'i'.freeze,
270
+ xnum.to_s,
271
+ lnum.to_s + '.json'.freeze
272
+ ].join('/'.freeze)
273
+ end
274
+
275
+
276
+ end # class ICFS::Store
277
+
278
+ end # module ICFS
@@ -0,0 +1,98 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ require 'fileutils'
13
+ require 'tempfile'
14
+
15
+ module ICFS
16
+
17
+ ##########################################################################
18
+ # Permanent store for items using the filesystem
19
+ #
20
+ # @deprecated Using a filesystem for a production system is a horrible idea.
21
+ # This is provided as an example and should be used for development use
22
+ # only.
23
+ #
24
+ class StoreFs < Store
25
+
26
+ ###############################################
27
+ # New instance
28
+ #
29
+ # @param base [String] the base directory
30
+ #
31
+ def initialize(base)
32
+ @base = base.freeze
33
+ end
34
+
35
+
36
+ ###############################################
37
+ # (see Store#file_read)
38
+ #
39
+ def file_read(cid, enum, lnum, fnum)
40
+ File.open(_file(cid, enum, lnum, fnum), 'rb')
41
+ rescue Errno::ENOENT
42
+ return nil
43
+ end
44
+
45
+
46
+ ###############################################
47
+ # (see Store#file_write)
48
+ #
49
+ def file_write(cid, enum, lnum, fnum, tmpf)
50
+ fn = _file(cid, enum, lnum, fnum)
51
+ FileUtils.ln(tmpf.path, fn, force: true)
52
+ tmpf.close!
53
+ rescue Errno::ENOENT
54
+ FileUtils.mkdir_p(File.dirname(fn))
55
+ FileUtils.ln(tmpf.path, fn, force: true)
56
+ tmpf.close!
57
+ end
58
+
59
+
60
+ ###############################################
61
+ # (see Store#tempfile)
62
+ #
63
+ def tempfile
64
+ Tempfile.new('tmp'.freeze, @base, :encoding => 'ascii-8bit'.freeze)
65
+ end
66
+
67
+
68
+ private
69
+
70
+
71
+ ###############################################
72
+ # Read an item
73
+ #
74
+ def _read(fnam)
75
+ return File.read(fnam, encoding: 'utf-8'.freeze)
76
+ rescue Errno::ENOENT
77
+ return nil
78
+ end
79
+
80
+
81
+ ###############################################
82
+ # Write an item
83
+ #
84
+ def _write(fnam, item)
85
+ File.open(fnam, 'w'.freeze, encoding: 'utf-8'.freeze) do |fi|
86
+ fi.write(item)
87
+ end
88
+ rescue Errno::ENOENT
89
+ FileUtils.mkdir_p(File.dirname(fnam))
90
+ File.open(fnam, 'w'.freeze, encoding: 'utf-8'.freeze) do |fi|
91
+ fi.write(item)
92
+ end
93
+ end
94
+
95
+
96
+ end # class StoreFs
97
+
98
+ end # module ICFS
@@ -0,0 +1,97 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ require 'aws-sdk-s3'
13
+ require 'tempfile'
14
+
15
+ module ICFS
16
+
17
+ ##########################################################################
18
+ # Permanent store for items using AWS S3
19
+ #
20
+ class StoreS3 < Store
21
+
22
+
23
+ ###############################################
24
+ # New store
25
+ #
26
+ # @param client [Aws::S3::Client] The configured S3 client
27
+ # @param bucket [String] The bucket name
28
+ # @param prefix [String] Prefix to use for object keys
29
+ #
30
+ def initialize(client, bucket, prefix=nil)
31
+ @s3 = client
32
+ @bck = bucket
33
+ @base = prefix || ''.freeze
34
+ end # def initialize()
35
+
36
+
37
+ ###############################################
38
+ # (see Store#file_read)
39
+ #
40
+ def file_read(cid, enum, lnum, fnum)
41
+ tmp = tempfile
42
+ key = _file(cid, enum, lnum, fnum)
43
+ @s3.get_object( bucket: @bck, key: key, response_target: tmp)
44
+ tmp.rewind
45
+ return tmp
46
+ rescue Aws::S3::Errors::NoSuchKey
47
+ return nil
48
+ end
49
+
50
+
51
+ ###############################################
52
+ # (see Store#file_write)
53
+ #
54
+ def file_write(cid, enum, lnum, fnum, tmpf)
55
+ key = _file(cid, enum, lnum, fnum)
56
+ tmpf.rewind
57
+ @s3.put_object( bucket: @bck, key: key, body: tmpf )
58
+
59
+ if tmpf.respond_to?( :close! )
60
+ tmpf.close!
61
+ else
62
+ tmpf.close
63
+ end
64
+ end
65
+
66
+
67
+ ###############################################
68
+ # (see Store#tempfile)
69
+ #
70
+ def tempfile
71
+ Tempfile.new('tmp'.freeze, encoding: 'ascii-8bit'.freeze)
72
+ end
73
+
74
+
75
+ private
76
+
77
+
78
+ ###############################################
79
+ # (see Store#_read)
80
+ #
81
+ def _read(path)
82
+ @s3.get_object( bucket: @bck, key: path).body.read
83
+ rescue Aws::S3::Errors::NoSuchKey
84
+ return nil
85
+ end # def _read()
86
+
87
+
88
+ ###############################################
89
+ # (see Store#_write)
90
+ #
91
+ def _write(path, item)
92
+ @s3.put_object( bucket: @bck, key: path, body: item )
93
+ end
94
+
95
+ end # class ICFS::Store
96
+
97
+ end # module ICFS