icfs 0.1.0

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.
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