mpw 3.2.1 → 4.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/cli.rb CHANGED
@@ -17,10 +17,12 @@
17
17
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
 
19
19
  require 'readline'
20
+ require 'locale'
20
21
  require 'i18n'
21
22
  require 'colorize'
22
23
  require 'highline/import'
23
24
  require 'clipboard'
25
+ require 'tmpdir'
24
26
  require 'mpw/item'
25
27
  require 'mpw/mpw'
26
28
 
@@ -30,33 +32,32 @@ class Cli
30
32
  # Constructor
31
33
  # @args: config -> the config
32
34
  # sync -> boolean for sync or not
33
- # clipboard -> enable clopboard
34
- # otp -> enable otp
35
- def initialize(config, clipboard=true, sync=true, otp=false)
35
+ def initialize(config, sync=true)
36
36
  @config = config
37
- @clipboard = clipboard
38
37
  @sync = sync
39
- @otp = otp
38
+ end
39
+
40
+ # Change a parameter int the config after init
41
+ # @args: options -> param to change
42
+ def set_config(options)
43
+ @config.setup(options)
44
+
45
+ puts "#{I18n.t('form.set_config.valid')}".green
46
+ rescue Exception => e
47
+ puts "#{I18n.t('display.error')} #15: #{e}".red
48
+ exit 2
40
49
  end
41
50
 
42
51
  # Create a new config file
43
- # @args: lang -> the software language
44
- def setup(lang)
45
- puts I18n.t('form.setup_config.title')
46
- puts '--------------------'
47
- language = ask(I18n.t('form.setup_config.lang', lang: lang)).to_s
48
- key = ask(I18n.t('form.setup_config.gpg_key')).to_s
49
- wallet_dir = ask(I18n.t('form.setup_config.wallet_dir', home: "#{@config.config_dir}")).to_s
50
- gpg_exe = ask(I18n.t('form.setup_config.gpg_exe')).to_s
51
-
52
- if language.nil? or language.empty?
53
- language = lang
54
- end
55
- I18n.locale = language.to_sym
52
+ # @args: options -> set param
53
+ def setup(options)
54
+ options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
56
55
 
57
- @config.setup(key, lang, wallet_dir, gpg_exe)
56
+ I18n.locale = options[:lang].to_sym
58
57
 
59
- raise I18n.t('error.config.check') if not @config.is_valid?
58
+ @config.setup(options)
59
+
60
+ load_config
60
61
 
61
62
  puts "#{I18n.t('form.setup_config.valid')}".green
62
63
  rescue Exception => e
@@ -65,16 +66,10 @@ class Cli
65
66
  end
66
67
 
67
68
  # Setup a new GPG key
68
- def setup_gpg_key
69
- puts I18n.t('form.setup_gpg_key.title')
70
- puts '--------------------'
71
- ask = ask(I18n.t('form.setup_gpg_key.ask')).to_s
72
-
73
- if not ['Y', 'y', 'O', 'o'].include?(ask)
74
- raise I18n.t('form.setup_gpg_key.no_create')
75
- end
69
+ # @args: gpg_key -> the key name
70
+ def setup_gpg_key(gpg_key)
71
+ return if @config.check_gpg_key?
76
72
 
77
- name = ask(I18n.t('form.setup_gpg_key.name')).to_s
78
73
  password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
79
74
  confirm = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
80
75
 
@@ -82,16 +77,11 @@ class Cli
82
77
  raise I18n.t('form.setup_gpg_key.error_password')
83
78
  end
84
79
 
85
- length = ask(I18n.t('form.setup_gpg_key.length')).to_s
86
- expire = ask(I18n.t('form.setup_gpg_key.expire')).to_s
87
- password = password.to_s
88
-
89
- length = length.nil? or length.empty? ? 2048 : length.to_i
90
- expire = expire.nil? or expire.empty? ? 0 : expire.to_i
80
+ @password = password.to_s
91
81
 
92
82
  puts I18n.t('form.setup_gpg_key.wait')
93
83
 
94
- @config.setup_gpg_key(password, name, length, expire)
84
+ @config.setup_gpg_key(@password, gpg_key)
95
85
 
96
86
  puts "#{I18n.t('form.setup_gpg_key.valid')}".green
97
87
  rescue Exception => e
@@ -100,23 +90,17 @@ class Cli
100
90
  end
101
91
 
102
92
  # Setup wallet config for sync
103
- def setup_wallet_config
104
- config = {}
105
- config['sync'] = {}
106
-
107
- puts I18n.t('form.setup_wallet.title')
108
- puts '--------------------'
109
- config['sync']['type'] = ask(I18n.t('form.setup_wallet.sync_type')).to_s
110
-
111
- if ['ftp', 'ssh'].include?(config['sync']['type'].downcase)
112
- config['sync']['host'] = ask(I18n.t('form.setup_wallet.sync_host')).to_s
113
- config['sync']['port'] = ask(I18n.t('form.setup_wallet.sync_port')).to_s
114
- config['sync']['user'] = ask(I18n.t('form.setup_wallet.sync_user')).to_s
115
- config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
116
- config['sync']['path'] = ask(I18n.t('form.setup_wallet.sync_path')).to_s
93
+ # @args: options -> value to change
94
+ def setup_wallet_config(options={})
95
+ if not options[:password].nil?
96
+ options[:password] = ask(I18n.t('form.setup_wallet.password')) {|q| q.echo = false}
117
97
  end
118
98
 
119
- @mpw.set_config(config)
99
+ #wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
100
+
101
+ @mpw = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe)
102
+ @mpw.read_data
103
+ @mpw.set_config(options)
120
104
  @mpw.write_data
121
105
 
122
106
  puts "#{I18n.t('form.setup_wallet.valid')}".green
@@ -125,11 +109,25 @@ class Cli
125
109
  exit 2
126
110
  end
127
111
 
112
+ # List gpg keys in wallet
113
+ def list_keys
114
+ table_list('keys', @mpw.list_keys)
115
+ end
116
+
117
+ # Load config
118
+ def load_config
119
+ @config.load_config
120
+
121
+ rescue Exception => e
122
+ puts "#{I18n.t('display.error')} #10: #{e}".red
123
+ exit 2
124
+ end
125
+
128
126
  # Request the GPG password and decrypt the file
129
127
  def decrypt
130
128
  if not defined?(@mpw)
131
129
  @password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
132
- @mpw = MPW.new(@config.key, @wallet_file, @password, @config.gpg_exe)
130
+ @mpw = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe)
133
131
  end
134
132
 
135
133
  @mpw.read_data
@@ -139,97 +137,143 @@ class Cli
139
137
  exit 2
140
138
  end
141
139
 
142
- # Display the query's result
143
- # @args: search -> the string to search
144
- # protocol -> search from a particular protocol
145
- def display(options={})
146
- result = @mpw.list(options)
140
+ # Format list on a table
141
+ def table_list(title, list)
142
+ i = 1
143
+ length = 0
147
144
 
148
- case result.length
149
- when 0
150
- puts I18n.t('display.nothing')
145
+ list.each do |item|
146
+ length = item.length if length < item.length
147
+ end
148
+ length += 7
149
+
150
+ puts "\n#{I18n.t("display.#{title}")}".red
151
+ print ' '
152
+ length.times { print '=' }
153
+ print "\n"
154
+
155
+ list.each do |item|
156
+ print " #{i}".cyan
157
+ (3 - i.to_s.length).times { print ' ' }
158
+ puts "| #{item}"
159
+ i += 1
160
+ end
151
161
 
152
- when 1
153
- display_item(result.first)
162
+ print "\n"
163
+ end
154
164
 
155
- else
156
- group = nil
157
- i = 1
165
+ # Format items on a table
166
+ def table_items(items=[])
167
+ group = '.'
168
+ i = 1
169
+ length_total = 10
170
+ data = { id: { length: 3, color: 'cyan' },
171
+ host: { length: 9, color: 'yellow' },
172
+ user: { length: 7, color: 'green' },
173
+ protocol: { length: 9, color: 'white' },
174
+ port: { length: 5, color: 'white' },
175
+ otp: { length: 4, color: 'white' },
176
+ comment: { length: 14, color: 'magenta' },
177
+ }
178
+
179
+ items.each do |item|
180
+ data.each do |k, v|
181
+ next if k == :id or k == :otp
182
+
183
+ v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
184
+ end
185
+ end
186
+ data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
187
+
188
+ data.each_value { |v| length_total += v[:length] }
189
+ items.sort! { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
158
190
 
159
- result.sort! { |a,b| a.group.downcase <=> b.group.downcase }
191
+ items.each do |item|
192
+ if group != item.group
193
+ group = item.group
160
194
 
161
- result.each do |item|
162
- if group != item.group
163
- group = item.group
195
+ if group.to_s.empty?
196
+ puts "\n#{I18n.t('display.no_group')}".red
197
+ else
198
+ puts "\n#{group}".red
199
+ end
164
200
 
165
- if group.empty?
166
- puts I18n.t('display.no_group').yellow
201
+ print ' '
202
+ length_total.times { print '=' }
203
+ print "\n "
204
+ data.each do |k, v|
205
+ case k
206
+ when :id
207
+ print ' ID'
208
+ when :otp
209
+ print '| OTP'
167
210
  else
168
- puts "\n#{group}".yellow
211
+ print "| #{k.to_s.capitalize}"
169
212
  end
170
- end
171
213
 
172
- print " |_ ".yellow
173
- print "#{i}: ".cyan
174
- print item.name
175
- print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
214
+ (v[:length] - k.to_s.length).times { print ' ' }
215
+ end
216
+ print "\n "
217
+ length_total.times { print '=' }
176
218
  print "\n"
177
-
178
- i += 1
179
219
  end
180
220
 
181
- print "\n"
182
- choice = ask(I18n.t('form.select')).to_i
221
+ print " #{i}".send(data[:id][:color])
222
+ (data[:id][:length] - i.to_s.length).times { print ' ' }
223
+ data.each do |k, v|
224
+ next if k == :id
183
225
 
184
- if choice >= 1 and choice < i
185
- display_item(result[choice-1])
186
- else
187
- puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
226
+ if k == :otp
227
+ print '| '
228
+ if item.otp; print ' X ' else 4.times { print ' ' } end
229
+
230
+ next
231
+ end
232
+
233
+ print '| '
234
+ print "#{item.send(k.to_s)}".send(v[:color])
235
+ (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
188
236
  end
237
+ print "\n"
238
+
239
+ i += 1
189
240
  end
241
+
242
+ print "\n"
190
243
  end
191
244
 
192
- # Display an item in the default format
193
- # @args: item -> an array with the item information
194
- def display_item(item)
195
- puts '--------------------'.cyan
196
- print 'Id: '.cyan
197
- puts item.id
198
- print "#{I18n.t('display.name')}: ".cyan
199
- puts item.name
200
- print "#{I18n.t('display.group')}: ".cyan
201
- puts item.group
202
- print "#{I18n.t('display.server')}: ".cyan
203
- puts item.host
204
- print "#{I18n.t('display.protocol')}: ".cyan
205
- puts item.protocol
206
- print "#{I18n.t('display.login')}: ".cyan
207
- puts item.user
208
-
209
- if @clipboard
210
- print "#{I18n.t('display.password')}: ".cyan
211
- puts '***********'
212
- else
213
- print "#{I18n.t('display.password')}: ".cyan
214
- puts @mpw.get_password(item.id)
245
+ # Display the query's result
246
+ # @args: options -> the option to search
247
+ def list(options={})
248
+ result = @mpw.list(options)
215
249
 
216
- if @mpw.get_otp_code(item.id) > 0
217
- print "#{I18n.t('display.otp_code')}: ".cyan
218
- puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
219
- end
250
+ if result.length == 0
251
+ puts I18n.t('display.nothing')
252
+ else
253
+ table_items(result)
220
254
  end
255
+ end
221
256
 
222
- print "#{I18n.t('display.port')}: ".cyan
223
- puts item.port
224
- print "#{I18n.t('display.comment')}: ".cyan
225
- puts item.comment
257
+ # Get an item when multiple choice
258
+ # @args: items -> array of items
259
+ # @rtrn: item
260
+ def get_item(items)
261
+ return items[0] if items.length == 1
226
262
 
227
- clipboard(item) if @clipboard
263
+ items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
264
+ choice = ask(I18n.t('form.select')).to_i
265
+
266
+ if choice >= 1 and choice <= items.length
267
+ return items[choice-1]
268
+ else
269
+ return nil
270
+ end
228
271
  end
229
272
 
230
273
  # Copy in clipboard the login and password
231
274
  # @args: item -> the item
232
- def clipboard(item)
275
+ # clipboard -> enable clipboard
276
+ def clipboard(item, clipboard=true)
233
277
  pid = nil
234
278
 
235
279
  # Security: force quit after 90s
@@ -246,21 +290,33 @@ class Cli
246
290
  break
247
291
 
248
292
  when 'l', 'login'
249
- Clipboard.copy(item.user)
250
- puts I18n.t('form.clipboard.login').green
293
+ if clipboard
294
+ Clipboard.copy(item.user)
295
+ puts I18n.t('form.clipboard.login').green
296
+ else
297
+ puts item.user
298
+ end
251
299
 
252
300
  when 'p', 'password'
253
- Clipboard.copy(@mpw.get_password(item.id))
254
- puts I18n.t('form.clipboard.password').yellow
301
+ if clipboard
302
+ Clipboard.copy(@mpw.get_password(item.id))
303
+ puts I18n.t('form.clipboard.password').yellow
255
304
 
256
- Thread.new do
257
- sleep 30
305
+ Thread.new do
306
+ sleep 30
258
307
 
259
- Clipboard.clear
308
+ Clipboard.clear
309
+ end
310
+ else
311
+ puts @mpw.get_password(item.id)
260
312
  end
261
313
 
262
314
  when 'o', 'otp'
263
- Clipboard.copy(@mpw.get_otp_code(item.id))
315
+ if clipboard
316
+ Clipboard.copy(@mpw.get_otp_code(item.id))
317
+ else
318
+ puts @mpw.get_otp_code(item.id)
319
+ end
264
320
  puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
265
321
 
266
322
  else
@@ -268,6 +324,7 @@ class Cli
268
324
  puts I18n.t('form.clipboard.help.login')
269
325
  puts I18n.t('form.clipboard.help.password')
270
326
  puts I18n.t('form.clipboard.help.otp_code')
327
+ puts I18n.t('form.clipboard.help.quit')
271
328
  next
272
329
  end
273
330
  end
@@ -277,34 +334,30 @@ class Cli
277
334
  Clipboard.clear
278
335
  end
279
336
 
337
+ # List all wallets
338
+ def list_wallet
339
+ wallets = []
340
+ Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f|
341
+ wallet = File.basename(f, '.mpw')
342
+ wallet += ' *'.green if wallet == @config.default_wallet
343
+ wallets << wallet
344
+ end
345
+
346
+ table_list('wallets', wallets)
347
+ end
348
+
280
349
  # Display the wallet
281
350
  # @args: wallet -> the wallet name
282
351
  def get_wallet(wallet=nil)
283
352
  if wallet.to_s.empty?
284
353
  wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
285
354
 
286
- case wallets.length
287
- when 0
288
- puts I18n.t('display.nothing')
289
- when 1
355
+ if wallets.length == 1
290
356
  @wallet_file = wallets[0]
357
+ elsif not @config.default_wallet.to_s.empty?
358
+ @wallet_file = "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
291
359
  else
292
- i = 1
293
- wallets.each do |wallet|
294
- print "#{i}: ".cyan
295
- puts File.basename(wallet, '.mpw')
296
-
297
- i += 1
298
- end
299
-
300
- choice = ask(I18n.t('form.select')).to_i
301
-
302
- if choice >= 1 and choice < i
303
- @wallet_file = wallets[choice-1]
304
- else
305
- puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
306
- exit 2
307
- end
360
+ @wallet_file = "#{@config.wallet_dir}/default.mpw"
308
361
  end
309
362
  else
310
363
  @wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
@@ -312,10 +365,9 @@ class Cli
312
365
  end
313
366
 
314
367
  # Add a new public key
315
- # args: key -> the key name to add
316
- # file -> gpg public file to import
317
- def add_key(key, file=nil)
318
- @mpw.add_key(key, file)
368
+ # args: key -> the key name or key file to add
369
+ def add_key(key)
370
+ @mpw.add_key(key)
319
371
  @mpw.write_data
320
372
  @mpw.sync(true) if @sync
321
373
 
@@ -336,111 +388,154 @@ class Cli
336
388
  puts "#{I18n.t('display.error')} #15: #{e}".red
337
389
  end
338
390
 
339
- # Form to add a new item
340
- def add
341
- options = {}
342
-
343
- puts I18n.t('form.add_item.title')
344
- puts '--------------------'
345
- options[:name] = ask(I18n.t('form.add_item.name')).to_s
346
- options[:group] = ask(I18n.t('form.add_item.group')).to_s
347
- options[:host] = ask(I18n.t('form.add_item.server')).to_s
348
- options[:protocol] = ask(I18n.t('form.add_item.protocol')).to_s
349
- options[:user] = ask(I18n.t('form.add_item.login')).to_s
350
- password = ask(I18n.t('form.add_item.password')).to_s
351
- options[:port] = ask(I18n.t('form.add_item.port')).to_s
352
- options[:comment] = ask(I18n.t('form.add_item.comment')).to_s
353
-
354
- if @otp
355
- otp_key = ask(I18n.t('form.add_item.otp_key')).to_s
391
+ # Text editor interface
392
+ # @args: template -> template name
393
+ # item -> the item to edit
394
+ # password -> disable field password
395
+ def text_editor(template_name, item=nil, password=false)
396
+ editor = ENV['EDITOR'] || 'nano'
397
+ options = {}
398
+ opts = {}
399
+ template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
400
+ template = ERB.new(IO.read(template_file))
401
+
402
+ Dir.mktmpdir do |dir|
403
+ tmp_file = "#{dir}/#{template_name}.yml"
404
+
405
+ File.open(tmp_file, 'w') do |f|
406
+ f << template.result(binding)
407
+ end
408
+
409
+ system("#{editor} #{tmp_file}")
410
+
411
+ opts = YAML::load_file(tmp_file)
412
+ end
413
+
414
+ opts.delete_if { |k,v| v.to_s.empty? }
415
+
416
+ opts.each do |k,v|
417
+ options[k.to_sym] = v
356
418
  end
357
419
 
358
- item = Item.new(options)
420
+ return options
421
+ end
359
422
 
423
+ # Form to add a new item
424
+ # @args: password -> generate a random password
425
+ def add(password=false)
426
+ options = text_editor('add_form', nil, password)
427
+ item = Item.new(options)
428
+ options[:password] = MPW::password(@config.password) if password
429
+
360
430
  @mpw.add(item)
361
- @mpw.set_password(item.id, password)
362
- @mpw.set_otp_key(item.id, otp_key)
431
+ @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
432
+ @mpw.set_otp_key(item.id, options[:otp_key]) if options.has_key?(:otp_key)
363
433
  @mpw.write_data
364
434
  @mpw.sync(true) if @sync
365
435
 
366
436
  puts "#{I18n.t('form.add_item.valid')}".green
437
+ rescue Exception => e
438
+ puts "#{I18n.t('display.error')} #13: #{e}".red
367
439
  end
368
440
 
369
441
  # Update an item
370
- # @args: id -> the item's id
371
- def update(id)
372
- item = @mpw.search_by_id(id)
373
-
374
- if not item.nil?
375
- options = {}
376
-
377
- puts I18n.t('form.update_item.title')
378
- puts '--------------------'
379
- options[:name] = ask(I18n.t('form.update_item.name' , name: item.name)).to_s
380
- options[:group] = ask(I18n.t('form.update_item.group' , group: item.group)).to_s
381
- options[:host] = ask(I18n.t('form.update_item.server' , server: item.host)).to_s
382
- options[:protocol] = ask(I18n.t('form.update_item.protocol', protocol: item.protocol)).to_s
383
- options[:user] = ask(I18n.t('form.update_item.login' , login: item.user)).to_s
384
- password = ask(I18n.t('form.update_item.password')).to_s
385
- options[:port] = ask(I18n.t('form.update_item.port' , port: item.port)).to_s
386
- options[:comment] = ask(I18n.t('form.update_item.comment' , comment: item.comment)).to_s
387
-
388
- if @otp
389
- otp_key = ask(I18n.t('form.update_item.otp_key')).to_s
390
- end
442
+ # @args: password -> generate a random password
443
+ # options -> the option to search
444
+ def update(password=false, options={})
445
+ items = @mpw.list(options)
446
+
447
+ if items.length == 0
448
+ puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
449
+ else
450
+ table_items(items) if items.length > 1
391
451
 
392
- options.delete_if { |k,v| v.empty? }
452
+ item = get_item(items)
453
+ options = text_editor('update_form', item, password)
454
+ options[:password] = MPW::password(@config.password) if password
393
455
 
394
456
  item.update(options)
395
- @mpw.set_password(item.id, password) if not password.empty?
396
- @mpw.set_otp_key(item.id, otp_key) if not otp_key.to_s.empty?
457
+ @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
458
+ @mpw.set_otp_key(item.id, options[:otp_key]) if options.has_key?(:otp_key)
397
459
  @mpw.write_data
398
460
  @mpw.sync(true) if @sync
399
461
 
400
462
  puts "#{I18n.t('form.update_item.valid')}".green
401
- else
402
- puts I18n.t('display.nothing')
403
463
  end
404
464
  rescue Exception => e
405
465
  puts "#{I18n.t('display.error')} #14: #{e}".red
406
466
  end
407
467
 
408
468
  # Remove an item
409
- # @args: id -> the item's id
410
- # force -> no resquest a validation
411
- def delete(id, force=false)
412
- @clipboard = false
413
- item = @mpw.search_by_id(id)
414
-
415
- if item.nil?
416
- puts I18n.t('form.delete_item.not_valid', id: id)
417
- return
418
- end
419
-
420
- if not force
421
- display_item(item)
469
+ # @args: options -> the option to search
470
+ def delete(options={})
471
+ items = @mpw.list(options)
472
+
473
+ if items.length == 0
474
+ puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
475
+ else
476
+ table_items(items)
422
477
 
423
- confirm = ask("#{I18n.t('form.delete_item.ask', id: id)} (y/N) ").to_s
478
+ item = get_item(items)
479
+ confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
480
+
424
481
  if not confirm =~ /^(y|yes|YES|Yes|Y)$/
425
- return
482
+ return false
426
483
  end
484
+
485
+ item.delete
486
+ @mpw.write_data
487
+ @mpw.sync(true) if @sync
488
+
489
+ puts "#{I18n.t('form.delete_item.valid')}".green
427
490
  end
491
+ rescue Exception => e
492
+ puts "#{I18n.t('display.error')} #16: #{e}".red
493
+ end
428
494
 
429
- item.delete
430
- @mpw.write_data
431
- @mpw.sync(true) if @sync
495
+ # Copy a password, otp, login
496
+ # @args: clipboard -> enable clipboard
497
+ # options -> the option to search
498
+ def copy(clipboard=true, options={})
499
+ items = @mpw.list(options)
500
+
501
+ if items.length == 0
502
+ puts I18n.t('display.nothing')
503
+ else
504
+ table_items(items)
432
505
 
433
- puts "#{I18n.t('form.delete_item.valid', id: id)}".green
506
+ item = get_item(items)
507
+ clipboard(item, clipboard)
508
+ end
434
509
  rescue Exception => e
435
- puts "#{I18n.t('display.error')} #16: #{e}".red
510
+ puts "#{I18n.t('display.error')} #14: #{e}".red
436
511
  end
437
512
 
438
513
  # Export the items in a CSV file
439
514
  # @args: file -> the destination file
440
- def export(file)
441
- @mpw.export(file)
515
+ # options -> option to search
516
+ def export(file, options)
517
+ file = 'export-mpw.yml' if file.to_s.empty?
518
+ items = @mpw.list(options)
519
+ data = {}
520
+
521
+ items.each do |item|
522
+ data.merge!(item.id => { 'host' => item.host,
523
+ 'user' => item.user,
524
+ 'group' => item.group,
525
+ 'password' => @mpw.get_password(item.id),
526
+ 'protocol' => item.protocol,
527
+ 'port' => item.port,
528
+ 'otp_key' => @mpw.get_otp_key(item.id),
529
+ 'comment' => item.comment,
530
+ 'last_edit' => item.last_edit,
531
+ 'created' => item.created,
532
+ }
533
+ )
534
+ end
442
535
 
443
- puts "#{I18n.t('export.export.valid', file)}".green
536
+ File.open(file, 'w') {|f| f << data.to_yaml}
537
+
538
+ puts "#{I18n.t('form.export.valid', file: file)}".green
444
539
  rescue Exception => e
445
540
  puts "#{I18n.t('display.error')} #17: #{e}".red
446
541
  end
@@ -448,7 +543,25 @@ class Cli
448
543
  # Import items from a YAML file
449
544
  # @args: file -> the import file
450
545
  def import(file)
451
- @mpw.import(file)
546
+ raise I18n.t('form.import.file_empty') if file.to_s.empty?
547
+ raise I18n.t('form.import.file_not_exist') if not File.exist?(file)
548
+
549
+ YAML::load_file(file).each_value do |row|
550
+ item = Item.new(group: row['group'],
551
+ host: row['host'],
552
+ protocol: row['protocol'],
553
+ user: row['user'],
554
+ port: row['port'],
555
+ comment: row['comment'],
556
+ )
557
+
558
+ next if item.empty?
559
+
560
+ @mpw.add(item)
561
+ @mpw.set_password(item.id, row['password']) if not row['password'].to_s.empty?
562
+ @mpw.set_otp_key(item.id, row['otp_key']) if not row['otp_key'].to_s.empty?
563
+ end
564
+
452
565
  @mpw.write_data
453
566
 
454
567
  puts "#{I18n.t('form.import.valid')}".green