mpw 3.2.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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