mpw 2.0.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/mpw/item.rb ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/ruby
2
+ # author: nishiki
3
+ # mail: nishiki@yaegashi.fr
4
+
5
+ require 'rubygems'
6
+ require 'i18n'
7
+
8
+ module MPW
9
+ class Item
10
+
11
+ attr_accessor :error_msg
12
+
13
+ attr_accessor :id
14
+ attr_accessor :name
15
+ attr_accessor :group
16
+ attr_accessor :host
17
+ attr_accessor :protocol
18
+ attr_accessor :user
19
+ attr_accessor :password
20
+ attr_accessor :port
21
+ attr_accessor :comment
22
+ attr_accessor :last_edit
23
+ attr_accessor :last_sync
24
+ attr_accessor :created
25
+
26
+ # Constructor
27
+ # Create a new item
28
+ # @args: options -> a hash of parameter
29
+ # raise an error if the hash hasn't the key name
30
+ def initialize(options={})
31
+ if not options.has_key?(:name) or options[:name].to_s.empty?
32
+ @error_msg = I18n.t('error.update.name_empty')
33
+ raise @error_msg
34
+ end
35
+
36
+ if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
37
+ @id = generate_id
38
+ @created = Time.now.to_i
39
+ else
40
+ @id = options[:id]
41
+ @created = options[:created]
42
+ @last_edit = options[:last_edit]
43
+ options[:no_update_last_edit] = true
44
+ end
45
+
46
+ update(options)
47
+ end
48
+
49
+ # Update the item
50
+ # @args: options -> a hash of parameter
51
+ # @rtrn: true if the item is update
52
+ def update(options={})
53
+ if options.has_key?(:name) and options[:name].to_s.empty?
54
+ @error_msg = I18n.t('error.update.name_empty')
55
+ return false
56
+ end
57
+
58
+ @name = options[:name] if options.has_key?(:name)
59
+ @group = options[:group] if options.has_key?(:group)
60
+ @host = options[:host] if options.has_key?(:host)
61
+ @protocol = options[:protocol] if options.has_key?(:protocol)
62
+ @user = options[:user] if options.has_key?(:user)
63
+ @password = options[:password] if options.has_key?(:password)
64
+ @port = options[:port].to_i if options.has_key?(:port) and not options[:port].to_s.empty?
65
+ @comment = options[:comment] if options.has_key?(:comment)
66
+ @last_edit = Time.now.to_i if not options.has_key?(:no_update_last_edit)
67
+
68
+ return true
69
+ end
70
+
71
+ # Update last_sync
72
+ def set_last_sync
73
+ @last_sync = Time.now.to_i
74
+ end
75
+
76
+ # Delete all data
77
+ # @rtrn: true
78
+ def delete
79
+ @id = nil
80
+ @name = nil
81
+ @group = nil
82
+ @host = nil
83
+ @protocol = nil
84
+ @user = nil
85
+ @password = nil
86
+ @port = nil
87
+ @comment = nil
88
+ @created = nil
89
+ @last_edit = nil
90
+ @last_sync = nil
91
+
92
+ return true
93
+ end
94
+
95
+ def empty?
96
+ return @name.to_s.empty?
97
+ end
98
+
99
+ def nil?
100
+ return false
101
+ end
102
+
103
+ # Generate an random id
104
+ private
105
+ def generate_id
106
+ return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
107
+ end
108
+ end
109
+ end
data/lib/mpw/mpw.rb ADDED
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/ruby
2
+ # author: nishiki
3
+ # mail: nishiki@yaegashi.fr
4
+
5
+ require 'rubygems'
6
+ require 'gpgme'
7
+ require 'csv'
8
+ require 'i18n'
9
+ require 'fileutils'
10
+ require 'yaml'
11
+ require 'mpw/item'
12
+
13
+ module MPW
14
+ class MPW
15
+
16
+ attr_accessor :error_msg
17
+
18
+ # Constructor
19
+ def initialize(file_gpg, key, share_keys='')
20
+ @error_msg = nil
21
+ @file_gpg = file_gpg
22
+ @key = key
23
+ @share_keys = share_keys
24
+ @data = []
25
+ end
26
+
27
+ # Decrypt a gpg file
28
+ # @args: password -> the GPG key password
29
+ # @rtrn: true if data has been decrypted
30
+ def decrypt(password=nil)
31
+ @data = []
32
+
33
+ if File.exist?(@file_gpg) and not File.zero?(@file_gpg)
34
+ crypto = GPGME::Crypto.new(armor: true)
35
+ data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
36
+
37
+ if not data_decrypt.to_s.empty?
38
+ YAML.load(data_decrypt).each_value do |d|
39
+ @data.push(Item.new(id: d['id'],
40
+ name: d['name'],
41
+ group: d['group'],
42
+ host: d['host'],
43
+ protocol: d['protocol'],
44
+ user: d['user'],
45
+ password: d['password'],
46
+ port: d['port'],
47
+ comment: d['comment'],
48
+ last_edit: d['last_edit'],
49
+ created: d['created'],
50
+ )
51
+ )
52
+ end
53
+ end
54
+ end
55
+
56
+ return true
57
+ rescue Exception => e
58
+ @error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
59
+ return false
60
+ end
61
+
62
+ # Encrypt a file
63
+ # @rtrn: true if the file has been encrypted
64
+ def encrypt
65
+ FileUtils.cp(@file_gpg, "#{@file_gpg}.bk") if File.exist?(@file_gpg)
66
+
67
+ data_to_encrypt = {}
68
+
69
+ @data.each do |item|
70
+ next if item.empty?
71
+
72
+ data_to_encrypt.merge!(item.id => {'id' => item.id,
73
+ 'name' => item.name,
74
+ 'group' => item.group,
75
+ 'host' => item.host,
76
+ 'protocol' => item.protocol,
77
+ 'user' => item.user,
78
+ 'password' => item.password,
79
+ 'port' => item.port,
80
+ 'comment' => item.comment,
81
+ 'last_edit' => item.last_edit,
82
+ 'created' => item.created,
83
+ }
84
+ )
85
+ end
86
+
87
+ recipients = []
88
+ recipients.push(@key)
89
+ if not @share_keys.nil?
90
+ @share_keys.split.each { |k| recipients.push(k) }
91
+ end
92
+
93
+ crypto = GPGME::Crypto.new(armor: true)
94
+ file_gpg = File.open(@file_gpg, 'w+')
95
+ crypto.encrypt(data_to_encrypt.to_yaml, recipients: recipients, output: file_gpg)
96
+ file_gpg.close
97
+
98
+ FileUtils.rm("#{@file_gpg}.bk") if File.exist?("#{@file_gpg}.bk")
99
+ return true
100
+ rescue Exception => e
101
+ @error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
102
+ FileUtils.mv("#{@file_gpg}.bk", @file_gpg) if File.exist?("#{@file_gpg}.bk")
103
+ return false
104
+ end
105
+
106
+ # Add a new item
107
+ # @args: item -> Object MPW::Item
108
+ # @rtrn: true if add item
109
+ def add(item)
110
+ if not item.instance_of?(Item)
111
+ @error_msg = I18n.t('error.bad_class')
112
+ return false
113
+ elsif item.empty?
114
+ @error_msg = I18n.t('error.add.empty')
115
+ return false
116
+ else
117
+ @data.push(item)
118
+ return true
119
+ end
120
+ end
121
+
122
+ # Search in some csv data
123
+ # @args: options -> a hash with paramaters
124
+ # @rtrn: a list with the resultat of the search
125
+ def list(options={})
126
+ result = []
127
+
128
+ search = options[:search].to_s.downcase
129
+ group = options[:group].to_s.downcase
130
+ protocol = options[:protocol].to_s.downcase
131
+
132
+ @data.each do |item|
133
+ next if item.empty?
134
+
135
+ next if not group.empty? and not group.eql?(item.group.downcase)
136
+ next if not protocol.empty? and not protocol.eql?(item.protocol.downcase)
137
+
138
+ name = item.name.to_s.downcase
139
+ host = item.host.to_s.downcase
140
+ comment = item.comment.to_s.downcase
141
+
142
+ if not name =~ /^.*#{search}.*$/ and not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/
143
+ next
144
+ end
145
+
146
+ result.push(item)
147
+ end
148
+
149
+ return result
150
+ end
151
+
152
+ # Search in some csv data
153
+ # @args: id -> the id item
154
+ # @rtrn: a row with the result of the search
155
+ def search_by_id(id)
156
+ @data.each do |item|
157
+ return item if item.id == id
158
+ end
159
+
160
+ return nil
161
+ end
162
+
163
+ # Export to csv
164
+ # @args: file -> file where you export the data
165
+ # type -> udata type
166
+ # @rtrn: true if export work
167
+ def export(file, type=:yaml)
168
+ case type
169
+ when :csv
170
+ CSV.open(file, 'w', write_headers: true,
171
+ headers: ['name', 'group', 'protocol', 'host', 'user', 'password', 'port', 'comment']) do |csv|
172
+ @data.each do |item|
173
+ csv << [item.name, item.group, item.protocol, item.host, item.user, item.password, item.port, item.comment]
174
+ end
175
+ end
176
+
177
+ when :yaml
178
+ data = {}
179
+ @data.each do |item|
180
+ data.merge!(item.id => {'id' => item.id,
181
+ 'name' => item.name,
182
+ 'group' => item.group,
183
+ 'host' => item.host,
184
+ 'protocol' => item.protocol,
185
+ 'user' => item.user,
186
+ 'password' => item.password,
187
+ 'port' => item.port,
188
+ 'comment' => item.comment,
189
+ 'last_edit' => item.last_edit,
190
+ 'created' => item.created,
191
+ }
192
+ )
193
+ end
194
+
195
+ File.open(file, 'w') {|f| f << data.to_yaml}
196
+
197
+ else
198
+ @error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
199
+ return false
200
+ end
201
+
202
+ return true
203
+ rescue Exception => e
204
+ @error_msg = "#{I18n.t('error.export.write', file: file)}\n#{e}"
205
+ return false
206
+ end
207
+
208
+ # Import to csv
209
+ # @args: file -> path to file import
210
+ # type -> udata type
211
+ # @rtrn: true if the import work
212
+ def import(file, type=:yaml)
213
+ case type
214
+ when :csv
215
+ CSV.foreach(file, {headers: true}) do |row|
216
+ item = Item.new(name: row['name'],
217
+ group: row['group'],
218
+ host: row['host'],
219
+ protocol: row['protocol'],
220
+ user: row['user'],
221
+ password: row['password'],
222
+ port: row['port'],
223
+ comment: row['comment'],
224
+ )
225
+
226
+ return false if item.empty?
227
+
228
+ @data.push(item)
229
+ end
230
+
231
+ when :yaml
232
+ YAML::load_file(file).each_value do |row|
233
+ item = Item.new(name: row['name'],
234
+ group: row['group'],
235
+ host: row['host'],
236
+ protocol: row['protocol'],
237
+ user: row['user'],
238
+ password: row['password'],
239
+ port: row['port'],
240
+ comment: row['comment'],
241
+ )
242
+
243
+ return false if item.empty?
244
+
245
+ @data.push(item)
246
+ end
247
+
248
+ else
249
+ @error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
250
+ return false
251
+ end
252
+
253
+ return true
254
+ rescue Exception => e
255
+ @error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
256
+ return false
257
+ end
258
+
259
+ # Return a preview import
260
+ # @args: file -> path to file import
261
+ # @rtrn: a hash with the items to import, if there is an error return false
262
+ def import_preview(file, type=:yaml)
263
+ data = []
264
+
265
+ case type
266
+ when :csv
267
+ CSV.foreach(file, {headers: true}) do |row|
268
+ item = Item.new(name: row['name'],
269
+ group: row['group'],
270
+ host: row['host'],
271
+ protocol: row['protocol'],
272
+ user: row['user'],
273
+ password: row['password'],
274
+ port: row['port'],
275
+ comment: row['comment'],
276
+ )
277
+
278
+ return false if item.empty?
279
+
280
+ data.push(item)
281
+ end
282
+
283
+ when :yaml
284
+ YAML::load_file(file).each_value do |row|
285
+ item = Item.new(name: row['name'],
286
+ group: row['group'],
287
+ host: row['host'],
288
+ protocol: row['protocol'],
289
+ user: row['user'],
290
+ password: row['password'],
291
+ port: row['port'],
292
+ comment: row['comment'],
293
+ )
294
+
295
+ return false if item.empty?
296
+
297
+ data.push(item)
298
+ end
299
+
300
+ else
301
+ @error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
302
+ return false
303
+ end
304
+
305
+ return data
306
+ rescue Exception => e
307
+ @error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
308
+ return false
309
+ end
310
+
311
+ # Generate a random password
312
+ # @args: length -> the length password
313
+ # @rtrn: a random string
314
+ def self.password(length=8)
315
+ if length.to_i <= 0
316
+ length = 8
317
+ else
318
+ length = length.to_i
319
+ end
320
+
321
+ result = ''
322
+ while length > 62 do
323
+ result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
324
+ length -= 62
325
+ end
326
+ result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
327
+
328
+ return result
329
+ end
330
+
331
+ end
332
+ end