From 9ad8eba901c97637d0cb0e1d2eb3a6c067f04b96 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 3 May 2017 19:54:21 +0200
Subject: [PATCH 01/58] fix: remove unused method

---
 bin/mpw-config | 1 -
 1 file changed, 1 deletion(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 3f049fa..bec687d 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -109,7 +109,6 @@ if options.key?(:init)
   cli.load_config
   cli.get_wallet
   cli.setup_gpg_key(values[:gpg_key])
-  cli.setup_wallet_config
 else
   cli.load_config
   if values.empty?

From 96380d3d934eba25747713c2bbbbf068b060ed76 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 3 May 2017 20:39:32 +0200
Subject: [PATCH 02/58] update version 4.1.1

---
 CHANGELOG.md | 4 ++++
 README.md    | 2 +-
 VERSION      | 2 +-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f52b5f..1fb8d79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
 # CHANGELOG
+## v4.1.1
+
+ * fix bug in init
+
 ## v4.1.0
 
  * feat: add options to update or add an item in command line
diff --git a/README.md b/README.md
index 60a69d8..afb4ade 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # MPW: Manage your passwords!
-[![Version](https://img.shields.io/badge/latest_version-4.1.0-green.svg)](https://github.com/nishiki/manage-password/releases)
+[![Version](https://img.shields.io/badge/latest_version-4.1.1-green.svg)](https://github.com/nishiki/manage-password/releases)
 [![Build Status](https://travis-ci.org/nishiki/manage-password.svg?branch=master)](https://travis-ci.org/nishiki/manage-password)
 [![License](https://img.shields.io/badge/license-GPL--2.0-blue.svg)](https://github.com/nishiki/manage-password/blob/master/LICENSE)
 
diff --git a/VERSION b/VERSION
index ee74734..627a3f4 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.1.0
+4.1.1

From a7a165bca9e401845fbc7d18b6349f2f423fdd88 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 3 May 2017 22:53:56 +0200
Subject: [PATCH 03/58] feat: comment the code with yard

---
 .gitignore        |  2 ++
 lib/mpw/cli.rb    | 71 +++++++++++++++++++++++------------------------
 lib/mpw/config.rb | 21 ++++++--------
 lib/mpw/item.rb   |  8 ++----
 lib/mpw/mpw.rb    | 59 +++++++++++++++++++++------------------
 5 files changed, 81 insertions(+), 80 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7ae6fcf..afd83c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 Gemfile.lock
 *.gem
+.yardoc
+doc
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index d622bff..e3cb85e 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -28,14 +28,13 @@ require 'mpw/mpw'
 
 module MPW
   class Cli
-    # Constructor
-    # @args: config -> the config
+    # @param config [Config]
     def initialize(config)
       @config = config
     end
 
     # Change a parameter int the config after init
-    # @args: options -> param to change
+    # @param options [Hash] param to change
     def set_config(options)
       @config.setup(options)
 
@@ -46,7 +45,7 @@ module MPW
     end
 
     # Change the wallet path
-    # @args: path -> the new path
+    # @param path [String] new path
     def set_wallet_path(path)
       @config.set_wallet_path(path, @wallet)
 
@@ -57,7 +56,7 @@ module MPW
     end
 
     # Create a new config file
-    # @args: options -> set param
+    # @param options [Hash]
     def setup(options)
       options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
@@ -74,7 +73,7 @@ module MPW
     end
 
     # Setup a new GPG key
-    # @args: gpg_key -> the key name
+    # @param gpg_key [String] gpg key name
     def setup_gpg_key(gpg_key)
       return if @config.check_gpg_key?
 
@@ -139,8 +138,8 @@ module MPW
     end
 
     # Format list on a table
-    # @args: title -> the name of table
-    #        list -> array or hash
+    # @param title [String] name of table
+    # @param list  an array or hash
     def table_list(title, list)
       length = { k: 0, v: 0 }
 
@@ -172,7 +171,7 @@ module MPW
     end
 
     # Format items on a table
-    # @args: items -> an aray items
+    # @param items [Array]
     def table_items(items = [])
       group        = '.'
       i            = 1
@@ -252,7 +251,7 @@ module MPW
     end
 
     # Display the query's result
-    # @args: options -> the option to search
+    # @param options [Hash] the options to search
     def list(**options)
       result = @mpw.list(options)
 
@@ -264,8 +263,8 @@ module MPW
     end
 
     # Get an item when multiple choice
-    # @args: items -> array of items
-    # @rtrn: item
+    # @param items [Array] list of items
+    # @return item [Item]
     def get_item(items)
       return items[0] if items.length == 1
 
@@ -276,8 +275,8 @@ module MPW
     end
 
     # Copy in clipboard the login and password
-    # @args: item -> the item
-    #        clipboard -> enable clipboard
+    # @param item [Item]
+    # @param clipboard [Boolean] enable clipboard
     def clipboard(item, clipboard = true)
       # Security: force quit after 90s
       Thread.new do
@@ -350,7 +349,7 @@ module MPW
     end
 
     # Display the wallet
-    # @args: wallet -> the wallet name
+    # @param wallet [String] wallet name
     def get_wallet(wallet = nil)
       @wallet =
         if wallet.to_s.empty?
@@ -375,7 +374,7 @@ module MPW
     end
 
     # Add a new public key
-    # args: key -> the key name or key file to add
+    # @param key [String] key name or key file to add
     def add_key(key)
       @mpw.add_key(key)
       @mpw.write_data
@@ -386,7 +385,7 @@ module MPW
     end
 
     # Add new public key
-    # args: key -> the key name to delete
+    # @param key [String] key name to delete
     def delete_key(key)
       @mpw.delete_key(key)
       @mpw.write_data
@@ -397,10 +396,10 @@ module MPW
     end
 
     # Text editor interface
-    # @args: template -> template name
-    #        item -> the item to edit
-    #        password -> disable field password
-    # @rtrn: a hash with the value for an item
+    # @param template [String] template name
+    # @param item [Item] the item to edit
+    # @param password [Boolean] disable field password
+    # @return [Hash] the values for an item
     def text_editor(template_name, password = false, item = nil, **options)
       editor        = ENV['EDITOR'] || 'nano'
       opts          = {}
@@ -429,9 +428,9 @@ module MPW
     end
 
     # Form to add a new item
-    # @args: password -> generate a random password
-    #        text_editor -> enable text editor mode
-    #        values -> hash with multiples value to set the item
+    # @param password [Boolean] generate a random password
+    # @param text_editor [Boolean] enable text editor mode
+    # @param values [Hash] multiples value to set the item
     def add(password = false, text_editor = false, **values)
       options            = text_editor('add_form', password, nil, values) if text_editor
       item               = Item.new(options)
@@ -448,10 +447,10 @@ module MPW
     end
 
     # Update an item
-    # @args: password -> generate a random password
-    #        text_editor -> enable text editor mode
-    #        options -> the option to search
-    #        values -> hash with multiples value to set the item
+    # @param password [Boolean] generate a random password
+    # @param text_editor [Boolean] enable text editor mode
+    # @param options [Hash] the options to search
+    # @param values [Hash] multiples value to set the item
     def update(password = false, text_editor = false, options = {}, **values)
       items = @mpw.list(options)
 
@@ -476,7 +475,7 @@ module MPW
     end
 
     # Remove an item
-    # @args: options -> the option to search
+    # @param options [Hash] the options to search
     def delete(**options)
       items = @mpw.list(options)
 
@@ -500,8 +499,8 @@ module MPW
     end
 
     # Copy a password, otp, login
-    # @args: clipboard -> enable clipboard
-    #        options -> the option to search
+    # @param clipboard [Boolean] enable clipboard
+    # @param options [Hash] the options to search
     def copy(clipboard = true, **options)
       items = @mpw.list(options)
 
@@ -517,9 +516,9 @@ module MPW
       puts "#{I18n.t('display.error')} #14: #{e}".red
     end
 
-    # Export the items in a CSV file
-    # @args: file -> the destination file
-    #        options -> option to search
+    # Export the items in an yaml file
+    # @param file [String] the path of destination file
+    # @param options [Hash] options to search
     def export(file, options)
       file  = 'export-mpw.yml' if file.to_s.empty?
       items = @mpw.list(options)
@@ -549,8 +548,8 @@ module MPW
       puts "#{I18n.t('display.error')} #17: #{e}".red
     end
 
-    # Import items from a YAML file
-    # @args: file -> the import file
+    # Import items from an yaml file
+    # @param file [String] path of import file
     def import(file)
       raise I18n.t('form.import.file_empty')     if file.to_s.empty?
       raise I18n.t('form.import.file_not_exist') unless File.exist?(file)
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 72b732d..bdcf394 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -35,8 +35,7 @@ module MPW
     attr_accessor :password
     attr_accessor :pinmode
 
-    # Constructor
-    # @args: config_file -> the specify config file
+    # @param config_file [String] path of config file
     def initialize(config_file = nil)
       @config_file = config_file
       @config_dir  =
@@ -52,8 +51,7 @@ module MPW
     end
 
     # Create a new config file
-    # @args: options -> hash with values
-    # @rtrn: true if le config file is create
+    # @param options [Hash] the value to set the config file
     def setup(**options)
       gpg_key        = options[:gpg_key]        || @gpg_key
       lang           = options[:lang]           || @lang
@@ -99,11 +97,10 @@ module MPW
     end
 
     # Setup a new gpg key
-    # @args: password -> the GPG key password
-    #        name -> the name of user
-    #        length -> length of the GPG key
-    #        expire -> the time of expire to GPG key
-    # @rtrn: true if the GPG key is create, else false
+    # @param password [String] gpg key password
+    # @param name [String] the name of user
+    # @param length [Integer] length of the gpg key
+    # @param expire [Integer] time of expire to gpg key
     def setup_gpg_key(password, name, length = 4096, expire = 0)
       raise I18n.t('error.config.genkey_gpg.name') if name.to_s.empty?
       raise I18n.t('error.config.genkey_gpg.password') if password.to_s.empty?
@@ -147,7 +144,7 @@ module MPW
     end
 
     # Check if private key exist
-    # @rtrn: true if the key exist, else false
+    # @return [Boolean] true if the key exist, else false
     def check_gpg_key?
       ctx = GPGME::Ctx.new
       ctx.each_key(@gpg_key, true) do
@@ -158,8 +155,8 @@ module MPW
     end
 
     # Change the path of one wallet
-    # @args: path -> the new directory path
-    #        wallet -> the wallet name
+    # @param path [String]new directory path
+    # @param wallet [String] wallet name
     def set_wallet_path(path, wallet)
       path = @wallet_dir if path == 'default'
 
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index b338998..ba86b96 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -31,10 +31,7 @@ module MPW
     attr_accessor :last_edit
     attr_accessor :created
 
-    # Constructor
-    # Create a new item
-    # @args: options -> a hash of parameter
-    # raise an error if the hash hasn't the key name
+    # @param options [Hash] the option :host is required
     def initialize(**options)
       if !options.key?(:host) || options[:host].to_s.empty?
         raise I18n.t('error.update.host_empty')
@@ -54,7 +51,7 @@ module MPW
     end
 
     # Update the item
-    # @args: options -> a hash of parameter
+    # @param options [Hash]
     def update(**options)
       if options.key?(:host) && options[:host].to_s.empty?
         raise I18n.t('error.update.host_empty')
@@ -95,6 +92,7 @@ module MPW
     private
 
     # Generate an random id
+    # @return [String] random string
     def generate_id
       [*('A'..'Z'), *('a'..'z'), *('0'..'9')].sample(16).join
     end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 71676c7..fbca576 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -25,7 +25,11 @@ require 'mpw/item'
 
 module MPW
   class MPW
-    # Constructor
+    # @param key [String] gpg key name
+    # @param wallet_file [String] path of the wallet file
+    # @param gpg_pass [String] password of the gpg key
+    # @param gpg_exe [String] path of the gpg executable
+    # @param pinmode [Boolean] enable the gpg pinmode
     def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil, pinmode = false)
       @key         = key
       @gpg_pass    = gpg_pass
@@ -98,7 +102,7 @@ module MPW
       raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
     end
 
-    # Encrypt a file
+    # Encrypt all data in tarball
     def write_data
       data     = {}
       tmp_file = "#{@wallet_file}.tmp"
@@ -154,7 +158,7 @@ module MPW
     end
 
     # Get a password
-    # args: id -> the item id
+    # @param id [String] the item id
     def get_password(id)
       password = decrypt(@passwords[id])
 
@@ -165,9 +169,9 @@ module MPW
       end
     end
 
-    # Set a password
-    # args: id -> the item id
-    #       password -> the new password
+    # Set a new password for an item
+    # @param id [String] the item id
+    # @param password [String] the new password
     def set_password(id, password)
       salt     = MPW.password(length: Random.rand(4..9))
       password = "$#{salt}::#{password}"
@@ -176,13 +180,13 @@ module MPW
     end
 
     # Return the list of all gpg keys
-    # rtrn: an array with the gpg keys name
+    # @return [Array] the gpg keys name
     def list_keys
       @keys.keys
     end
 
     # Add a public key
-    # args: key ->  new public key file or name
+    # @param key [String] new public key file or name
     def add_key(key)
       if File.exist?(key)
         data       = File.open(key).read
@@ -200,7 +204,7 @@ module MPW
     end
 
     # Delete a public key
-    # args: key ->  public key to delete
+    # @param key [String] public key to delete
     def delete_key(key)
       @keys.delete(key)
       @passwords.each_key { |id| set_password(id, get_password(id)) }
@@ -208,7 +212,7 @@ module MPW
     end
 
     # Add a new item
-    # @args: item -> Object MPW::Item
+    # @param item [Item]
     def add(item)
       raise I18n.t('error.bad_class') unless item.instance_of?(Item)
       raise I18n.t('error.empty')     if item.empty?
@@ -217,8 +221,8 @@ module MPW
     end
 
     # Search in some csv data
-    # @args: options -> a hash with paramaters
-    # @rtrn: a list with the resultat of the search
+    # @params options [Hash]
+    # @return [Array] a list with the resultat of the search
     def list(**options)
       result = []
 
@@ -240,9 +244,9 @@ module MPW
       result
     end
 
-    # Search in some csv data
-    # @args: id -> the id item
-    # @rtrn: a row with the result of the search
+    # Search an item with an id
+    # @param id [String]the id item
+    # @return [Item] an item or nil
     def search_by_id(id)
       @data.each do |item|
         return item if item.id == id
@@ -251,36 +255,35 @@ module MPW
       nil
     end
 
-    # Set an opt key
-    # args: id -> the item id
-    #       key -> the new key
+    # Set a new opt key
+    # @param id [String] the item id
+    # @param key [String] the new key
     def set_otp_key(id, key)
       @otp_keys[id] = encrypt(key.to_s) unless key.to_s.empty?
     end
 
     # Get an opt key
-    # args: id -> the item id
-    #       key -> the new key
+    # @param id [String] the item id
     def get_otp_key(id)
       @otp_keys.key?(id) ? decrypt(@otp_keys[id]) : nil
     end
 
     # Get an otp code
-    # @args: id -> the item id
-    # @rtrn: an otp code
+    # @param id [String] the item id
+    # @return [String] an otp code
     def get_otp_code(id)
       @otp_keys.key?(id) ? 0 : ROTP::TOTP.new(decrypt(@otp_keys[id])).now
     end
 
     # Get remaining time before expire otp code
-    # @rtrn: return time in seconde
+    # @return [Integer] time in seconde
     def get_otp_remaining_time
       (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
     end
 
     # Generate a random password
-    # @args: options -> :length, :special, :alpha, :numeric
-    # @rtrn: a random string
+    # @param options [Hash] :length, :special, :alpha, :numeric
+    # @return [String] a random string
     def self.password(**options)
       length =
         if !options.include?(:length) || options[:length].to_i <= 0
@@ -310,7 +313,8 @@ module MPW
     private
 
     # Decrypt a gpg file
-    # @args: data -> string to decrypt
+    # @param data [String] data to decrypt
+    # @return [String] data decrypted
     def decrypt(data)
       return nil if data.to_s.empty?
 
@@ -331,7 +335,8 @@ module MPW
     end
 
     # Encrypt a file
-    # args: data -> string to encrypt
+    # @param data [String] data to encrypt
+    # @return [String] data encrypted
     def encrypt(data)
       recipients = []
       crypto     = GPGME::Crypto.new(armor: true, always_trust: true)

From f91531b856e7b1299851331ec204c08c0da92200 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 6 May 2017 09:09:17 +0200
Subject: [PATCH 04/58] feat: replace host by url in table

---
 lib/mpw/cli.rb  | 33 +++++++++++++++++++++------------
 lib/mpw/item.rb | 11 +++++++++++
 2 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index e3cb85e..023b9b2 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -179,16 +179,19 @@ module MPW
       data         = { id:       { length: 3,  color: 'cyan' },
                        host:     { length: 9,  color: 'yellow' },
                        user:     { length: 7,  color: 'green' },
-                       protocol: { length: 9,  color: 'white' },
-                       port:     { length: 5,  color: 'white' },
                        otp:      { length: 4,  color: 'white' },
                        comment:  { length: 14, color: 'magenta' } }
 
       items.each do |item|
         data.each do |k, v|
-          next if k == :id || k == :otp
-
-          v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+          case k
+          when :id, :otp
+            next
+          when :host
+            v[:length] = item.url.length + 3 if item.url.length >= v[:length]
+          else
+            v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+          end
         end
       end
       data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
@@ -231,16 +234,22 @@ module MPW
         data.each do |k, v|
           next if k == :id
 
-          if k == :otp
-            print '| '
+          print '| '
+
+          case k
+          when :otp
             item.otp ? (print ' X  ') : 4.times { print ' ' }
 
-            next
-          end
+          when :host
+            print "#{item.protocol}://" if item.protocol
+            print item.host.send(v[:color])
+            print ":#{item.port}" if item.port
+            (v[:length] - item.url.to_s.length).times { print ' ' }
 
-          print '| '
-          print item.send(k.to_s).to_s.send(v[:color])
-          (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+          else
+            print item.send(k.to_s).to_s.send(v[:color])
+            (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+          end
         end
         print "\n"
 
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index ba86b96..be03dba 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -81,6 +81,17 @@ module MPW
       @last_edit = nil
     end
 
+    # Return data on url format
+    # @return [String] an url
+    def url
+      url = ''
+      url += "#{@protocol}://" if @protocol
+      url += @host
+      url += ":#{@port}" if @port
+
+      url
+    end
+
     def empty?
       @id.to_s.empty?
     end

From 6a3f2ca007823190e755154c5e0df48f6a38cae7 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 7 May 2017 17:11:42 +0200
Subject: [PATCH 05/58] add first tests for cli

---
 .travis.yml      |  7 ++++++-
 test/test_cli.rb | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 test/tests.rb    |  7 -------
 3 files changed, 56 insertions(+), 8 deletions(-)
 create mode 100644 test/test_cli.rb
 delete mode 100644 test/tests.rb

diff --git a/.travis.yml b/.travis.yml
index faa5c04..89d29f0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,4 +13,9 @@ install:
   - gem install mpw-9999.gem
 script:
   - rubocop
-  - ruby ./test/tests.rb 
+  - ruby ./test/init.rb
+  - ruby ./test/test_config.rb
+  - ruby ./test/test_item.rb
+  - ruby ./test/test_mpw.rb
+  - ruby ./test/test_translate.rb
+  - ruby ./test/test_cli.rb
diff --git a/test/test_cli.rb b/test/test_cli.rb
new file mode 100644
index 0000000..0db3ffa
--- /dev/null
+++ b/test/test_cli.rb
@@ -0,0 +1,50 @@
+#!/usr/bin/ruby
+
+require 'fileutils'
+require 'test/unit'
+
+class TestConfig < Test::Unit::TestCase
+  def setup
+    @password = 'password'
+  end
+
+  def test_00_init_config
+    FileUtils.rm_rf("#{Dir.home}/.config/mpw")
+    FileUtils.rm_rf("#{Dir.home}/.gnupg")
+
+    output = `echo "#{@password}\n#{@password}" | mpw config --init test@example.com`
+    assert_match('The config file has been created', output)
+    assert_match('Your GPG key has been created ;-)', output)
+  end
+
+  def test_01_add_item
+    host  = 'example.com'
+
+    output = `echo #{@password} | mpw add --host #{host} -r`
+    assert_match('Item has been added!', output)
+    
+    output = `echo #{@password} | mpw list`
+    assert_match(host, output)
+  end
+
+  def test_02_update_item
+    host_old = 'example.com'
+    host_new = 'example2.com'
+
+    output = `echo #{@password} | mpw update -p #{host_old} --host #{host_new}`
+    assert_match('Item has been updated!', output)
+    
+    output = `echo #{@password} | mpw list`
+    assert_match(host_new, output)
+  end
+
+  def test_03_delete_item
+    host = 'example2.com'
+
+    output = `echo "#{@password}\ny" | mpw delete -p #{host}`
+    assert_match('The item has been removed!', output)
+    
+    output = `echo #{@password} | mpw list`
+    assert_no_match(/#{host}/, output)
+  end
+end
diff --git a/test/tests.rb b/test/tests.rb
deleted file mode 100644
index 7f3fc7d..0000000
--- a/test/tests.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/ruby
-
-require_relative 'init.rb'
-require_relative 'test_config.rb'
-require_relative 'test_item.rb'
-require_relative 'test_mpw.rb'
-require_relative 'test_translate.rb'

From 646f096aac6f6efd9db6b4464b7d6a9b1de908f7 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 7 May 2017 19:29:22 +0200
Subject: [PATCH 06/58] update ruby version for test

---
 .travis.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 89d29f0..f9db456 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,8 @@
 language: ruby
 rvm:
-  - 2.4.0
-  - 2.3.3
-  - 2.2.6
+  - 2.4.1
+  - 2.3.4
+  - 2.2.7
   - 2.1.10
 install:
   - bundle install

From a13fcd147fb53d05bb3df493111e155926dabc85 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 6 May 2017 09:09:17 +0200
Subject: [PATCH 07/58] feat: replace host by url in table

---
 lib/mpw/cli.rb  | 33 +++++++++++++++++++++------------
 lib/mpw/item.rb | 11 +++++++++++
 2 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index e3cb85e..023b9b2 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -179,16 +179,19 @@ module MPW
       data         = { id:       { length: 3,  color: 'cyan' },
                        host:     { length: 9,  color: 'yellow' },
                        user:     { length: 7,  color: 'green' },
-                       protocol: { length: 9,  color: 'white' },
-                       port:     { length: 5,  color: 'white' },
                        otp:      { length: 4,  color: 'white' },
                        comment:  { length: 14, color: 'magenta' } }
 
       items.each do |item|
         data.each do |k, v|
-          next if k == :id || k == :otp
-
-          v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+          case k
+          when :id, :otp
+            next
+          when :host
+            v[:length] = item.url.length + 3 if item.url.length >= v[:length]
+          else
+            v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+          end
         end
       end
       data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
@@ -231,16 +234,22 @@ module MPW
         data.each do |k, v|
           next if k == :id
 
-          if k == :otp
-            print '| '
+          print '| '
+
+          case k
+          when :otp
             item.otp ? (print ' X  ') : 4.times { print ' ' }
 
-            next
-          end
+          when :host
+            print "#{item.protocol}://" if item.protocol
+            print item.host.send(v[:color])
+            print ":#{item.port}" if item.port
+            (v[:length] - item.url.to_s.length).times { print ' ' }
 
-          print '| '
-          print item.send(k.to_s).to_s.send(v[:color])
-          (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+          else
+            print item.send(k.to_s).to_s.send(v[:color])
+            (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+          end
         end
         print "\n"
 
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index ba86b96..be03dba 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -81,6 +81,17 @@ module MPW
       @last_edit = nil
     end
 
+    # Return data on url format
+    # @return [String] an url
+    def url
+      url = ''
+      url += "#{@protocol}://" if @protocol
+      url += @host
+      url += ":#{@port}" if @port
+
+      url
+    end
+
     def empty?
       @id.to_s.empty?
     end

From 09e451bf3be5ac589fdaebf85c4768eec97b13a0 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Tue, 9 May 2017 13:56:52 +0200
Subject: [PATCH 08/58] use ligth_black for port and protocol

---
 lib/mpw/cli.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 023b9b2..4e2f240 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -241,9 +241,9 @@ module MPW
             item.otp ? (print ' X  ') : 4.times { print ' ' }
 
           when :host
-            print "#{item.protocol}://" if item.protocol
+            print "#{item.protocol}://".light_black if item.protocol
             print item.host.send(v[:color])
-            print ":#{item.port}" if item.port
+            print ":#{item.port}".light_black if item.port
             (v[:length] - item.url.to_s.length).times { print ' ' }
 
           else

From 977266def846ec5794494e697d8dc8174c305391 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 10 May 2017 22:38:35 +0200
Subject: [PATCH 09/58] add new test

---
 test/test_cli.rb | 69 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 54 insertions(+), 15 deletions(-)

diff --git a/test/test_cli.rb b/test/test_cli.rb
index 0db3ffa..c3466e8 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -12,39 +12,78 @@ class TestConfig < Test::Unit::TestCase
     FileUtils.rm_rf("#{Dir.home}/.config/mpw")
     FileUtils.rm_rf("#{Dir.home}/.gnupg")
 
-    output = `echo "#{@password}\n#{@password}" | mpw config --init test@example.com`
+    output = %x(echo "#{@password}\n#{@password}" | mpw config --init test@example.com)
     assert_match('The config file has been created', output)
     assert_match('Your GPG key has been created ;-)', output)
   end
 
   def test_01_add_item
-    host  = 'example.com'
+    host    = 'example.com'
+    port    = 1234
+    proto   = 'http'
+    user    = 'root'
+    comment = 'the super website'
+    group   = 'Bank'
 
-    output = `echo #{@password} | mpw add --host #{host} -r`
+    output = %x(
+      echo #{@password} | mpw add \
+      --host #{host} \
+      --port #{port} \
+      --protocol #{proto} \
+      --user #{user} \
+      --comment '#{comment}' \
+      --group #{group} \
+      --random)
+    puts output
     assert_match('Item has been added!', output)
-    
-    output = `echo #{@password} | mpw list`
-    assert_match(host, output)
+
+    output = %x(echo #{@password} | mpw list)
+    puts output
+    assert_match(%r{#{proto}://.+#{host}.+:#{port}}, output)
+    assert_match(user, output)
+    assert_match(comment, output)
+    assert_match(group, output)
   end
 
   def test_02_update_item
-    host_old = 'example.com'
-    host_new = 'example2.com'
+    host_old    = 'example.com'
+    host_new    = 'example2.com'
+    port_new    = 4321
+    proto_new   = 'ssh'
+    user_new    = 'tortue'
+    comment_new = 'my account'
+    group_new   = 'Assurance'
 
-    output = `echo #{@password} | mpw update -p #{host_old} --host #{host_new}`
+    output = %x(
+      echo #{@password} | mpw update \
+      -p #{host_old} \
+      --host #{host_new} \
+      --port #{port_new} \
+      --protocol #{proto_new} \
+      --user #{user_new} \
+      --comment '#{comment_new}' \
+      --new-group #{group_new}
+    )
+    puts output
     assert_match('Item has been updated!', output)
-    
-    output = `echo #{@password} | mpw list`
-    assert_match(host_new, output)
+
+    output = %x(echo #{@password} | mpw list)
+    puts output
+    assert_match(%r{#{proto_new}://.+#{host_new}.+:#{port_new}}, output)
+    assert_match(user_new, output)
+    assert_match(comment_new, output)
+    assert_match(group_new, output)
   end
 
   def test_03_delete_item
     host = 'example2.com'
 
-    output = `echo "#{@password}\ny" | mpw delete -p #{host}`
+    output = %x(echo "#{@password}\ny" | mpw delete -p #{host})
+    puts output
     assert_match('The item has been removed!', output)
-    
-    output = `echo #{@password} | mpw list`
+
+    output = %x(echo #{@password} | mpw list)
+    puts output
     assert_no_match(/#{host}/, output)
   end
 end

From f150feec10240ec8b007283da5bc9ae900789b4e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 11 May 2017 22:24:05 +0200
Subject: [PATCH 10/58] fix syntax for all tests

---
 .rubocop.yml           |  1 -
 test/test_config.rb    | 24 +++++-----
 test/test_item.rb      | 99 ++++++++++++++++++++++--------------------
 test/test_mpw.rb       | 40 ++++++++---------
 test/test_translate.rb |  2 +-
 5 files changed, 85 insertions(+), 81 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index 8685f88..ce1de33 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,7 +3,6 @@ AllCops:
   Exclude:
     - db/**/*
     - config/**/*
-    - test/*
     - Vagrantfile
   TargetRubyVersion: 2.3
 
diff --git a/test/test_config.rb b/test/test_config.rb
index 49af82c..8ad7369 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -20,17 +20,18 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_00_config
-    data = { gpg_key: 'test@example.com',
-             lang: 'en',
-             wallet_dir: '/tmp/test',
-             gpg_exe: '',
-           }
+    data = {
+      gpg_key: 'test@example.com',
+      lang: 'en',
+      wallet_dir: '/tmp/test',
+      gpg_exe: ''
+    }
 
     @config = MPW::Config.new
     @config.setup(data)
     @config.load_config
 
-    data.each do |k,v|
+    data.each do |k, v|
       assert_equal(v, @config.send(k))
     end
 
@@ -39,11 +40,12 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_01_password
-    data = { pwd_alpha: false,
-             pwd_numeric: false,
-             pwd_special: true,
-             pwd_length: 32,
-           }
+    data = {
+      pwd_alpha: false,
+      pwd_numeric: false,
+      pwd_special: true,
+      pwd_length: 32
+    }
 
     @config = MPW::Config.new
     @config.load_config
diff --git a/test/test_item.rb b/test/test_item.rb
index 751e1b0..961834e 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -16,22 +16,22 @@ class TestItem < Test::Unit::TestCase
     I18n.load_path      = Dir['./i18n/cli/*.yml']
     I18n.default_locale = :en
 
-
     puts
   end
 
   def test_00_add_without_name
-    assert_raise(RuntimeError){MPW::Item.new}
+    assert_raise(RuntimeError) { MPW::Item.new }
   end
 
   def test_01_add_new
-    data = { group:    @fixtures['add_new']['group'],
-             host:     @fixtures['add_new']['host'],
-             protocol: @fixtures['add_new']['protocol'],
-             user:     @fixtures['add_new']['user'],
-             port:     @fixtures['add_new']['port'],
-             comment:  @fixtures['add_new']['comment'],
-           }
+    data = {
+      group:    @fixtures['add_new']['group'],
+      host:     @fixtures['add_new']['host'],
+      protocol: @fixtures['add_new']['protocol'],
+      user:     @fixtures['add_new']['user'],
+      port:     @fixtures['add_new']['port'],
+      comment:  @fixtures['add_new']['comment']
+    }
 
     item = MPW::Item.new(data)
 
@@ -47,15 +47,16 @@ class TestItem < Test::Unit::TestCase
   end
 
   def test_02_add_existing
-    data = { id:       @fixtures['add_existing']['id'],
-             group:    @fixtures['add_existing']['group'],
-             host:     @fixtures['add_existing']['host'],
-             protocol: @fixtures['add_existing']['protocol'],
-             user:     @fixtures['add_existing']['user'],
-             port:     @fixtures['add_existing']['port'],
-             comment:  @fixtures['add_existing']['comment'],
-             created:  @fixtures['add_existing']['created'],
-           }
+    data = {
+      id:       @fixtures['add_existing']['id'],
+      group:    @fixtures['add_existing']['group'],
+      host:     @fixtures['add_existing']['host'],
+      protocol: @fixtures['add_existing']['protocol'],
+      user:     @fixtures['add_existing']['user'],
+      port:     @fixtures['add_existing']['port'],
+      comment:  @fixtures['add_existing']['comment'],
+      created:  @fixtures['add_existing']['created']
+    }
 
     item = MPW::Item.new(data)
 
@@ -73,13 +74,14 @@ class TestItem < Test::Unit::TestCase
   end
 
   def test_03_update
-    data = { group:    @fixtures['add_new']['group'],
-             host:     @fixtures['add_new']['host'],
-             protocol: @fixtures['add_new']['protocol'],
-             user:     @fixtures['add_new']['user'],
-             port:     @fixtures['add_new']['port'],
-             comment:  @fixtures['add_new']['comment'],
-           }
+    data = {
+      group:    @fixtures['add_new']['group'],
+      host:     @fixtures['add_new']['host'],
+      protocol: @fixtures['add_new']['protocol'],
+      user:     @fixtures['add_new']['user'],
+      port:     @fixtures['add_new']['port'],
+      comment:  @fixtures['add_new']['comment']
+    }
 
     item = MPW::Item.new(data)
 
@@ -89,13 +91,14 @@ class TestItem < Test::Unit::TestCase
     created   = item.created
     last_edit = item.last_edit
 
-    data = { group:    @fixtures['update']['group'],
-             host:     @fixtures['update']['host'],
-             protocol: @fixtures['update']['protocol'],
-             user:     @fixtures['update']['user'],
-             port:     @fixtures['update']['port'],
-             comment:  @fixtures['update']['comment'],
-           }
+    data = {
+      group:    @fixtures['update']['group'],
+      host:     @fixtures['update']['host'],
+      protocol: @fixtures['update']['protocol'],
+      user:     @fixtures['update']['user'],
+      port:     @fixtures['update']['port'],
+      comment:  @fixtures['update']['comment']
+    }
 
     sleep(1)
     assert(item.update(data))
@@ -114,13 +117,14 @@ class TestItem < Test::Unit::TestCase
   end
 
   def test_05_update_one_element
-    data = { group:    @fixtures['add_new']['group'],
-             host:     @fixtures['add_new']['host'],
-             protocol: @fixtures['add_new']['protocol'],
-             user:     @fixtures['add_new']['user'],
-             port:     @fixtures['add_new']['port'],
-             comment:  @fixtures['add_new']['comment'],
-           }
+    data = {
+      group:    @fixtures['add_new']['group'],
+      host:     @fixtures['add_new']['host'],
+      protocol: @fixtures['add_new']['protocol'],
+      user:     @fixtures['add_new']['user'],
+      port:     @fixtures['add_new']['port'],
+      comment:  @fixtures['add_new']['comment']
+    }
 
     item = MPW::Item.new(data)
 
@@ -130,7 +134,7 @@ class TestItem < Test::Unit::TestCase
     last_edit = item.last_edit
 
     sleep(1)
-    assert(item.update({comment: @fixtures['update']['comment']}))
+    assert(item.update(comment: @fixtures['update']['comment']))
 
     assert_equal(@fixtures['add_new']['group'],     item.group)
     assert_equal(@fixtures['add_new']['host'],      item.host)
@@ -143,13 +147,14 @@ class TestItem < Test::Unit::TestCase
   end
 
   def test_05_delete
-    data = { group:    @fixtures['add_new']['group'],
-             host:     @fixtures['add_new']['host'],
-             protocol: @fixtures['add_new']['protocol'],
-             user:     @fixtures['add_new']['user'],
-             port:     @fixtures['add_new']['port'],
-             comment:  @fixtures['add_new']['comment'],
-           }
+    data = {
+      group:    @fixtures['add_new']['group'],
+      host:     @fixtures['add_new']['host'],
+      protocol: @fixtures['add_new']['protocol'],
+      user:     @fixtures['add_new']['user'],
+      port:     @fixtures['add_new']['port'],
+      comment:  @fixtures['add_new']['comment']
+    }
 
     item = MPW::Item.new(data)
 
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 30c9028..dd005ca 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -33,13 +33,14 @@ class TestMPW < Test::Unit::TestCase
   end
 
   def test_02_add_item
-    data = { group:    @fixtures['add_new']['group'],
-             host:     @fixtures['add_new']['host'],
-             protocol: @fixtures['add_new']['protocol'],
-             user:     @fixtures['add_new']['user'],
-             port:     @fixtures['add_new']['port'],
-             comment:  @fixtures['add_new']['comment'],
-           }
+    data = {
+      group:    @fixtures['add_new']['group'],
+      host:     @fixtures['add_new']['host'],
+      protocol: @fixtures['add_new']['protocol'],
+      user:     @fixtures['add_new']['user'],
+      port:     @fixtures['add_new']['port'],
+      comment:  @fixtures['add_new']['comment']
+    }
 
     item = MPW::Item.new(data)
 
@@ -53,7 +54,7 @@ class TestMPW < Test::Unit::TestCase
     assert_equal(1, @mpw.list.length)
 
     item = @mpw.list[0]
-    @fixtures['add_new'].each do |k,v|
+    @fixtures['add_new'].each do |k, v|
       if k == 'password'
         assert_equal(v, @mpw.get_password(item.id))
       else
@@ -69,7 +70,7 @@ class TestMPW < Test::Unit::TestCase
     assert_equal(1, @mpw.list.length)
 
     item = @mpw.list[0]
-    @fixtures['add_new'].each do |k,v|
+    @fixtures['add_new'].each do |k, v|
       if k == 'password'
         assert_equal(v, @mpw.get_password(item.id))
       else
@@ -80,13 +81,9 @@ class TestMPW < Test::Unit::TestCase
 
   def test_04_delete_item
     @mpw.read_data
-
     assert_equal(1, @mpw.list.length)
 
-    @mpw.list.each do |item|
-      item.delete
-    end
-
+    @mpw.list.each(&:delete)
     assert_equal(0, @mpw.list.length)
 
     @mpw.write_data
@@ -96,13 +93,14 @@ class TestMPW < Test::Unit::TestCase
     @mpw.read_data
 
     @fixtures.each_value do |v|
-      data = { group:    v['group'],
-               host:     v['host'],
-               protocol: v['protocol'],
-               user:     v['user'],
-               port:     v['port'],
-               comment:  v['comment'],
-             }
+      data = {
+        group:    v['group'],
+        host:     v['host'],
+        protocol: v['protocol'],
+        user:     v['user'],
+        port:     v['port'],
+        comment:  v['comment']
+      }
 
       item = MPW::Item.new(data)
 
diff --git a/test/test_translate.rb b/test/test_translate.rb
index 6334a78..d4bb002 100644
--- a/test/test_translate.rb
+++ b/test/test_translate.rb
@@ -11,7 +11,7 @@ class TestTranslate < Test::Unit::TestCase
       lang      = File.basename(yaml, '.yml')
       translate = YAML.load_file(yaml)
 
-      `grep -r -o "I18n.t('.*)" bin/ lib/ | cut -d"'" -f2`.each_line do |line|
+      %x(grep -r -o "I18n.t('.*)" bin/ lib/ | cut -d"'" -f2).each_line do |line|
         begin
           t = translate[lang]
           line.strip.split('.').each do |v|

From 72c213b7b7112727642fced0e04fe6964c8e96a8 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sat, 13 May 2017 01:04:56 +0200
Subject: [PATCH 11/58] test: use dynamic translation

---
 test/test_cli.rb | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/test/test_cli.rb b/test/test_cli.rb
index c3466e8..375de4e 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -1,10 +1,19 @@
 #!/usr/bin/ruby
 
 require 'fileutils'
+require 'i18n'
 require 'test/unit'
 
 class TestConfig < Test::Unit::TestCase
   def setup
+    if defined?(I18n.enforce_available_locales)
+      I18n.enforce_available_locales = true
+    end
+
+    I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+    I18n.load_path = ["#{File.expand_path('../../i18n', __FILE__)}/en.yml"]
+    I18n.locale    = :en
+
     @password = 'password'
   end
 
@@ -13,8 +22,8 @@ class TestConfig < Test::Unit::TestCase
     FileUtils.rm_rf("#{Dir.home}/.gnupg")
 
     output = %x(echo "#{@password}\n#{@password}" | mpw config --init test@example.com)
-    assert_match('The config file has been created', output)
-    assert_match('Your GPG key has been created ;-)', output)
+    assert_match(I18n.t('form.setup_config.valid'), output)
+    assert_match(I18n.t('form.setup_gpg_key.valid'), output)
   end
 
   def test_01_add_item
@@ -35,7 +44,7 @@ class TestConfig < Test::Unit::TestCase
       --group #{group} \
       --random)
     puts output
-    assert_match('Item has been added!', output)
+    assert_match(I18n.t('form.add_item.valid'), output)
 
     output = %x(echo #{@password} | mpw list)
     puts output
@@ -65,7 +74,7 @@ class TestConfig < Test::Unit::TestCase
       --new-group #{group_new}
     )
     puts output
-    assert_match('Item has been updated!', output)
+    assert_match(I18n.t('form.update_item.valid'), output)
 
     output = %x(echo #{@password} | mpw list)
     puts output
@@ -80,7 +89,7 @@ class TestConfig < Test::Unit::TestCase
 
     output = %x(echo "#{@password}\ny" | mpw delete -p #{host})
     puts output
-    assert_match('The item has been removed!', output)
+    assert_match(I18n.t('form.delete_item.valid'), output)
 
     output = %x(echo #{@password} | mpw list)
     puts output

From f41a9e68d32f9ec3651a9690310a3552da679512 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 11 May 2017 22:24:05 +0200
Subject: [PATCH 12/58] fix syntax for all tests

---
 test/test_item.rb | 5 +----
 test/test_mpw.rb  | 4 +---
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/test/test_item.rb b/test/test_item.rb
index 961834e..4b0cf1b 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -6,9 +6,6 @@ require 'yaml'
 
 class TestItem < Test::Unit::TestCase
   def setup
-    @fixture_file = 'test/files/fixtures.yml'
-    @fixtures = YAML.load_file(@fixture_file)
-
     if defined?(I18n.enforce_available_locales)
       I18n.enforce_available_locales = false
     end
@@ -16,7 +13,7 @@ class TestItem < Test::Unit::TestCase
     I18n.load_path      = Dir['./i18n/cli/*.yml']
     I18n.default_locale = :en
 
-    puts
+    @fixtures = YAML.load_file('./test/files/fixtures.yml')
   end
 
   def test_00_add_without_name
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index dd005ca..2877d19 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -8,8 +8,6 @@ require 'csv'
 
 class TestMPW < Test::Unit::TestCase
   def setup
-    fixture_file = './test/files/fixtures.yml'
-
     wallet_file = 'default.gpg'
     key         = 'test@example.com'
     password    = 'password'
@@ -19,7 +17,7 @@ class TestMPW < Test::Unit::TestCase
     end
 
     @mpw      = MPW::MPW.new(key, wallet_file, password)
-    @fixtures = YAML.load_file(fixture_file)
+    @fixtures = YAML.load_file('./test/files/fixtures.yml')
   end
 
   def test_00_decrypt_empty_file

From ca2e0d9e479cd21cc6139428bb7c3076bce02655 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sat, 13 May 2017 12:28:42 +0200
Subject: [PATCH 13/58] test: update fixture

---
 test/files/fixtures.yml |  46 ++++++++---------
 test/test_cli.rb        |  60 ++++++++++------------
 test/test_item.rb       | 108 ++++++++++++++++++++--------------------
 test/test_mpw.rb        |  24 ++++-----
 4 files changed, 114 insertions(+), 124 deletions(-)

diff --git a/test/files/fixtures.yml b/test/files/fixtures.yml
index 5651911..765314e 100644
--- a/test/files/fixtures.yml
+++ b/test/files/fixtures.yml
@@ -1,28 +1,28 @@
-add_new:
-  group: 'test_group'
-  host: 'test_host'
-  protocol: 'test_protocol'
-  user: 'test_user'
-  password: 'test_password'
-  port: '42'
-  comment: 'test_comment'
+add:
+  group: 'Bank'
+  host: 'example.com'
+  protocol: 'https'
+  user: 'admin'
+  password: 'VmfnCN6pPIqgRIbc'
+  port: '8080'
+  comment: 'the website'
 
-add_existing:
+import:
   id: 'TEST-ID-XXXXX'
-  group: 'test_group_existing'
-  host: 'test_host_existing'
-  protocol: 'test_protocol_existing'
-  user: 'test_user_existing'
-  password: 'test_password_existing'
-  port: '44'
-  comment: 'test_comment_existing'
+  group: 'Cloud'
+  host: 'gogole.com'
+  protocol: 'https'
+  user: 'gg-2304'
+  password: 'TITl0kV9CDDa9sVK'
+  port: '8081'
+  comment: 'My little servers'
   created: 1386752948
 
 update:
-  group: 'test_group_update'
-  host: 'test_host_update'
-  protocol: 'test_protocol_update'
-  user: 'test_user_update'
-  password: 'test_password_update'
-  port: '43'
-  comment: 'test_comment_update'
+  group: 'Assurance'
+  host: 'example2.com'
+  protocol: 'ssh'
+  user: 'root'
+  password: 'kbSrbv4WlMaVxaZ7'
+  port: '2222'
+  comment: 'i love ssh'
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 375de4e..d92e175 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -15,6 +15,7 @@ class TestConfig < Test::Unit::TestCase
     I18n.locale    = :en
 
     @password = 'password'
+    @fixtures = YAML.load_file(fixture_file)
   end
 
   def test_00_init_config
@@ -27,65 +28,54 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_01_add_item
-    host    = 'example.com'
-    port    = 1234
-    proto   = 'http'
-    user    = 'root'
-    comment = 'the super website'
-    group   = 'Bank'
+    data = @fixtures['add']
 
     output = %x(
       echo #{@password} | mpw add \
-      --host #{host} \
-      --port #{port} \
-      --protocol #{proto} \
-      --user #{user} \
-      --comment '#{comment}' \
-      --group #{group} \
+      --host #{data['host']} \
+      --port #{data['port']} \
+      --protocol #{data['protocol']} \
+      --user #{data['user']} \
+      --comment '#{data['comment']}' \
+      --group #{data['group']} \
       --random)
     puts output
     assert_match(I18n.t('form.add_item.valid'), output)
 
     output = %x(echo #{@password} | mpw list)
     puts output
-    assert_match(%r{#{proto}://.+#{host}.+:#{port}}, output)
-    assert_match(user, output)
-    assert_match(comment, output)
-    assert_match(group, output)
+    assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
+    assert_match(data['user'], output)
+    assert_match(data['comment'], output)
+    assert_match(data['group'], output)
   end
 
   def test_02_update_item
-    host_old    = 'example.com'
-    host_new    = 'example2.com'
-    port_new    = 4321
-    proto_new   = 'ssh'
-    user_new    = 'tortue'
-    comment_new = 'my account'
-    group_new   = 'Assurance'
+    data = @fixtures['update']
 
     output = %x(
       echo #{@password} | mpw update \
-      -p #{host_old} \
-      --host #{host_new} \
-      --port #{port_new} \
-      --protocol #{proto_new} \
-      --user #{user_new} \
-      --comment '#{comment_new}' \
-      --new-group #{group_new}
+      -p #{@fixtures['add']['host']} \
+      --host #{data['host']} \
+      --port #{data['port']} \
+      --protocol #{data['protocol']} \
+      --user #{data['user']} \
+      --comment '#{data['comment']}' \
+      --new-group #{data['group']}
     )
     puts output
     assert_match(I18n.t('form.update_item.valid'), output)
 
     output = %x(echo #{@password} | mpw list)
     puts output
-    assert_match(%r{#{proto_new}://.+#{host_new}.+:#{port_new}}, output)
-    assert_match(user_new, output)
-    assert_match(comment_new, output)
-    assert_match(group_new, output)
+    assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
+    assert_match(data['user'], output)
+    assert_match(data['comment'], output)
+    assert_match(data['group'], output)
   end
 
   def test_03_delete_item
-    host = 'example2.com'
+    host = @fixtures['update']['host']
 
     output = %x(echo "#{@password}\ny" | mpw delete -p #{host})
     puts output
diff --git a/test/test_item.rb b/test/test_item.rb
index 4b0cf1b..c83cbc3 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -20,14 +20,14 @@ class TestItem < Test::Unit::TestCase
     assert_raise(RuntimeError) { MPW::Item.new }
   end
 
-  def test_01_add_new
+  def test_01_add
     data = {
-      group:    @fixtures['add_new']['group'],
-      host:     @fixtures['add_new']['host'],
-      protocol: @fixtures['add_new']['protocol'],
-      user:     @fixtures['add_new']['user'],
-      port:     @fixtures['add_new']['port'],
-      comment:  @fixtures['add_new']['comment']
+      group:    @fixtures['add']['group'],
+      host:     @fixtures['add']['host'],
+      protocol: @fixtures['add']['protocol'],
+      user:     @fixtures['add']['user'],
+      port:     @fixtures['add']['port'],
+      comment:  @fixtures['add']['comment']
     }
 
     item = MPW::Item.new(data)
@@ -35,24 +35,24 @@ class TestItem < Test::Unit::TestCase
     assert(!item.nil?)
     assert(!item.empty?)
 
-    assert_equal(@fixtures['add_new']['group'],     item.group)
-    assert_equal(@fixtures['add_new']['host'],      item.host)
-    assert_equal(@fixtures['add_new']['protocol'],  item.protocol)
-    assert_equal(@fixtures['add_new']['user'],      item.user)
-    assert_equal(@fixtures['add_new']['port'].to_i, item.port)
-    assert_equal(@fixtures['add_new']['comment'],   item.comment)
+    assert_equal(@fixtures['add']['group'],     item.group)
+    assert_equal(@fixtures['add']['host'],      item.host)
+    assert_equal(@fixtures['add']['protocol'],  item.protocol)
+    assert_equal(@fixtures['add']['user'],      item.user)
+    assert_equal(@fixtures['add']['port'].to_i, item.port)
+    assert_equal(@fixtures['add']['comment'],   item.comment)
   end
 
-  def test_02_add_existing
+  def test_02_import
     data = {
-      id:       @fixtures['add_existing']['id'],
-      group:    @fixtures['add_existing']['group'],
-      host:     @fixtures['add_existing']['host'],
-      protocol: @fixtures['add_existing']['protocol'],
-      user:     @fixtures['add_existing']['user'],
-      port:     @fixtures['add_existing']['port'],
-      comment:  @fixtures['add_existing']['comment'],
-      created:  @fixtures['add_existing']['created']
+      id:       @fixtures['import']['id'],
+      group:    @fixtures['import']['group'],
+      host:     @fixtures['import']['host'],
+      protocol: @fixtures['import']['protocol'],
+      user:     @fixtures['import']['user'],
+      port:     @fixtures['import']['port'],
+      comment:  @fixtures['import']['comment'],
+      created:  @fixtures['import']['created']
     }
 
     item = MPW::Item.new(data)
@@ -60,24 +60,24 @@ class TestItem < Test::Unit::TestCase
     assert(!item.nil?)
     assert(!item.empty?)
 
-    assert_equal(@fixtures['add_existing']['id'],        item.id)
-    assert_equal(@fixtures['add_existing']['group'],     item.group)
-    assert_equal(@fixtures['add_existing']['host'],      item.host)
-    assert_equal(@fixtures['add_existing']['protocol'],  item.protocol)
-    assert_equal(@fixtures['add_existing']['user'],      item.user)
-    assert_equal(@fixtures['add_existing']['port'].to_i, item.port)
-    assert_equal(@fixtures['add_existing']['comment'],   item.comment)
-    assert_equal(@fixtures['add_existing']['created'],   item.created)
+    assert_equal(@fixtures['import']['id'],        item.id)
+    assert_equal(@fixtures['import']['group'],     item.group)
+    assert_equal(@fixtures['import']['host'],      item.host)
+    assert_equal(@fixtures['import']['protocol'],  item.protocol)
+    assert_equal(@fixtures['import']['user'],      item.user)
+    assert_equal(@fixtures['import']['port'].to_i, item.port)
+    assert_equal(@fixtures['import']['comment'],   item.comment)
+    assert_equal(@fixtures['import']['created'],   item.created)
   end
 
   def test_03_update
     data = {
-      group:    @fixtures['add_new']['group'],
-      host:     @fixtures['add_new']['host'],
-      protocol: @fixtures['add_new']['protocol'],
-      user:     @fixtures['add_new']['user'],
-      port:     @fixtures['add_new']['port'],
-      comment:  @fixtures['add_new']['comment']
+      group:    @fixtures['add']['group'],
+      host:     @fixtures['add']['host'],
+      protocol: @fixtures['add']['protocol'],
+      user:     @fixtures['add']['user'],
+      port:     @fixtures['add']['port'],
+      comment:  @fixtures['add']['comment']
     }
 
     item = MPW::Item.new(data)
@@ -115,12 +115,12 @@ class TestItem < Test::Unit::TestCase
 
   def test_05_update_one_element
     data = {
-      group:    @fixtures['add_new']['group'],
-      host:     @fixtures['add_new']['host'],
-      protocol: @fixtures['add_new']['protocol'],
-      user:     @fixtures['add_new']['user'],
-      port:     @fixtures['add_new']['port'],
-      comment:  @fixtures['add_new']['comment']
+      group:    @fixtures['add']['group'],
+      host:     @fixtures['add']['host'],
+      protocol: @fixtures['add']['protocol'],
+      user:     @fixtures['add']['user'],
+      port:     @fixtures['add']['port'],
+      comment:  @fixtures['add']['comment']
     }
 
     item = MPW::Item.new(data)
@@ -133,24 +133,24 @@ class TestItem < Test::Unit::TestCase
     sleep(1)
     assert(item.update(comment: @fixtures['update']['comment']))
 
-    assert_equal(@fixtures['add_new']['group'],     item.group)
-    assert_equal(@fixtures['add_new']['host'],      item.host)
-    assert_equal(@fixtures['add_new']['protocol'],  item.protocol)
-    assert_equal(@fixtures['add_new']['user'],      item.user)
-    assert_equal(@fixtures['add_new']['port'].to_i, item.port)
-    assert_equal(@fixtures['update']['comment'],    item.comment)
+    assert_equal(@fixtures['add']['group'],      item.group)
+    assert_equal(@fixtures['add']['host'],       item.host)
+    assert_equal(@fixtures['add']['protocol'],   item.protocol)
+    assert_equal(@fixtures['add']['user'],       item.user)
+    assert_equal(@fixtures['add']['port'].to_i,  item.port)
+    assert_equal(@fixtures['update']['comment'], item.comment)
 
     assert_not_equal(last_edit, item.last_edit)
   end
 
   def test_05_delete
     data = {
-      group:    @fixtures['add_new']['group'],
-      host:     @fixtures['add_new']['host'],
-      protocol: @fixtures['add_new']['protocol'],
-      user:     @fixtures['add_new']['user'],
-      port:     @fixtures['add_new']['port'],
-      comment:  @fixtures['add_new']['comment']
+      group:    @fixtures['add']['group'],
+      host:     @fixtures['add']['host'],
+      protocol: @fixtures['add']['protocol'],
+      user:     @fixtures['add']['user'],
+      port:     @fixtures['add']['port'],
+      comment:  @fixtures['add']['comment']
     }
 
     item = MPW::Item.new(data)
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 2877d19..a783225 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -32,12 +32,12 @@ class TestMPW < Test::Unit::TestCase
 
   def test_02_add_item
     data = {
-      group:    @fixtures['add_new']['group'],
-      host:     @fixtures['add_new']['host'],
-      protocol: @fixtures['add_new']['protocol'],
-      user:     @fixtures['add_new']['user'],
-      port:     @fixtures['add_new']['port'],
-      comment:  @fixtures['add_new']['comment']
+      group:    @fixtures['add']['group'],
+      host:     @fixtures['add']['host'],
+      protocol: @fixtures['add']['protocol'],
+      user:     @fixtures['add']['user'],
+      port:     @fixtures['add']['port'],
+      comment:  @fixtures['add']['comment']
     }
 
     item = MPW::Item.new(data)
@@ -47,12 +47,12 @@ class TestMPW < Test::Unit::TestCase
 
     @mpw.read_data
     @mpw.add(item)
-    @mpw.set_password(item.id, @fixtures['add_new']['password'])
+    @mpw.set_password(item.id, @fixtures['add']['password'])
 
     assert_equal(1, @mpw.list.length)
 
     item = @mpw.list[0]
-    @fixtures['add_new'].each do |k, v|
+    @fixtures['add'].each do |k, v|
       if k == 'password'
         assert_equal(v, @mpw.get_password(item.id))
       else
@@ -68,7 +68,7 @@ class TestMPW < Test::Unit::TestCase
     assert_equal(1, @mpw.list.length)
 
     item = @mpw.list[0]
-    @fixtures['add_new'].each do |k, v|
+    @fixtures['add'].each do |k, v|
       if k == 'password'
         assert_equal(v, @mpw.get_password(item.id))
       else
@@ -110,9 +110,9 @@ class TestMPW < Test::Unit::TestCase
     end
 
     assert_equal(3, @mpw.list.length)
-    assert_equal(1, @mpw.list(group:    @fixtures['add_new']['group']).length)
-    assert_equal(1, @mpw.list(pattern:  'existing').length)
-    assert_equal(2, @mpw.list(pattern:  'host_[eu]').length)
+    assert_equal(1, @mpw.list(group:   @fixtures['add']['group']).length)
+    assert_equal(1, @mpw.list(pattern: 'gogole').length)
+    assert_equal(2, @mpw.list(pattern: 'example[2\.]').length)
   end
 
   def test_06_add_gpg_key

From 5f7cb6e0cc40b61c1eeaaf392bbf5b54ad733781 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sat, 13 May 2017 19:30:23 +0200
Subject: [PATCH 14/58] add search test in cli

---
 lib/mpw/cli.rb   |  4 ++--
 test/test_cli.rb | 31 ++++++++++++++++++++++++-------
 2 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 4e2f240..cc0ebf0 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -464,7 +464,7 @@ module MPW
       items = @mpw.list(options)
 
       if items.empty?
-        puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+        puts I18n.t('display.nothing')
       else
         table_items(items) if items.length > 1
 
@@ -489,7 +489,7 @@ module MPW
       items = @mpw.list(options)
 
       if items.empty?
-        puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+        puts I18n.t('display.nothing')
       else
         table_items(items)
 
diff --git a/test/test_cli.rb b/test/test_cli.rb
index d92e175..8944ce2 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -15,7 +15,7 @@ class TestConfig < Test::Unit::TestCase
     I18n.locale    = :en
 
     @password = 'password'
-    @fixtures = YAML.load_file(fixture_file)
+    @fixtures = YAML.load_file('./test/files/fixtures.yml')
   end
 
   def test_00_init_config
@@ -50,7 +50,26 @@ class TestConfig < Test::Unit::TestCase
     assert_match(data['group'], output)
   end
 
-  def test_02_update_item
+  def test_02_search
+    data = @fixtures['add']
+
+    output = %x(echo #{@password} | mpw list --group #{data['group']})
+    assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
+
+    output = %x(echo #{@password} | mpw list --pattern #{data['host']})
+    assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
+
+    output = %x(echo #{@password} | mpw list --pattern #{data['comment']})
+    assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
+
+    output = %x(echo #{@password} | mpw list --group R1Pmfbp626TFpjlr)
+    assert_match(I18n.t('display.nothing'), output)
+
+    output = %x(echo #{@password} | mpw list --pattern h1IfnKqamaGM9oEX)
+    assert_match(I18n.t('display.nothing'), output)
+  end
+
+  def test_03_update_item
     data = @fixtures['update']
 
     output = %x(
@@ -74,15 +93,13 @@ class TestConfig < Test::Unit::TestCase
     assert_match(data['group'], output)
   end
 
-  def test_03_delete_item
-    host = @fixtures['update']['host']
-
-    output = %x(echo "#{@password}\ny" | mpw delete -p #{host})
+  def test_04_delete_item
+    output = %x(echo "#{@password}\ny" | mpw delete -p #{@fixtures['update']['host']})
     puts output
     assert_match(I18n.t('form.delete_item.valid'), output)
 
     output = %x(echo #{@password} | mpw list)
     puts output
-    assert_no_match(/#{host}/, output)
+    assert_match(I18n.t('display.nothing'), output)
   end
 end

From 62318fe0778e8c1f17d74ddbcf57e5585330fd1e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 18 May 2017 23:03:30 +0200
Subject: [PATCH 15/58] add test cli for config

---
 bin/mpw-config   |  2 +-
 test/test_cli.rb | 59 ++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index bec687d..0af88bd 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -88,7 +88,7 @@ OptionParser.new do |opts|
     values[:pwd_special] = true
   end
 
-  opts.on('-S', '--disable_special-chars', I18n.t('option.special_chars')) do
+  opts.on('-S', '--disable-special-chars', I18n.t('option.special_chars')) do
     values[:pwd_special] = false
   end
 
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 8944ce2..56326c2 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -16,13 +16,14 @@ class TestConfig < Test::Unit::TestCase
 
     @password = 'password'
     @fixtures = YAML.load_file('./test/files/fixtures.yml')
+    @gpg_key  = 'test@example.com'
   end
 
   def test_00_init_config
     FileUtils.rm_rf("#{Dir.home}/.config/mpw")
     FileUtils.rm_rf("#{Dir.home}/.gnupg")
 
-    output = %x(echo "#{@password}\n#{@password}" | mpw config --init test@example.com)
+    output = %x(echo "#{@password}\n#{@password}" | mpw config --init #{@gpg_key})
     assert_match(I18n.t('form.setup_config.valid'), output)
     assert_match(I18n.t('form.setup_gpg_key.valid'), output)
   end
@@ -38,7 +39,8 @@ class TestConfig < Test::Unit::TestCase
       --user #{data['user']} \
       --comment '#{data['comment']}' \
       --group #{data['group']} \
-      --random)
+      --random
+    )
     puts output
     assert_match(I18n.t('form.add_item.valid'), output)
 
@@ -102,4 +104,57 @@ class TestConfig < Test::Unit::TestCase
     puts output
     assert_match(I18n.t('display.nothing'), output)
   end
+
+  def test_05_setup_config
+    gpg_key    = 'user@example2.com'
+    gpg_exe    = '/usr/bin/gpg2'
+    wallet_dir = '/tmp/mpw'
+    length     = 24
+    wallet     = 'work'
+
+    output = %x(
+      mpw config \
+      --gpg-exe #{gpg_exe} \
+      --enable-pinmode \
+      --disable-alpha \
+      --disable-special-chars \
+      --disable-numeric \
+      --length #{length} \
+      --wallet-dir #{wallet_dir} \
+      --default-wallet #{wallet}
+    )
+    puts output
+    assert_match(I18n.t('form.set_config.valid'), output)
+
+    output = %x(mpw config)
+    puts output
+    assert_match(/gpg_key.+\| #{@gpg_key}/, output)
+    assert_match(/gpg_exe.+\| #{gpg_exe}/, output)
+    assert_match(/pinmode.+\| true/, output)
+    assert_match(/default_wallet.+\| #{wallet}/, output)
+    assert_match(/wallet_dir.+\| #{wallet_dir}/, output)
+    assert_match(/password_length.+\| #{length}/, output)
+    %w[numeric alpha special].each do |k|
+      assert_match(/password_#{k}.+\| false/, output)
+    end
+
+    output = %x(
+      mpw config \
+      --key #{gpg_key} \
+      --alpha \
+      --special-chars \
+      --numeric \
+      --disable-pinmode
+    )
+    puts output
+    assert_match(I18n.t('form.set_config.valid'), output)
+
+    output = %x(mpw config)
+    puts output
+    assert_match(/gpg_key.+\| #{gpg_key}/, output)
+    assert_match(/pinmode.+\| false/, output)
+    %w[numeric alpha special].each do |k|
+      assert_match(/password_#{k}.+\| true/, output)
+    end
+  end
 end

From 8e4fb1c91bf2701d8ded1a9ff9db36d0003d763a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 18 May 2017 23:30:54 +0200
Subject: [PATCH 16/58] fix show config

---
 lib/mpw/cli.rb    |  2 +-
 lib/mpw/config.rb | 14 ++++++++------
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index cc0ebf0..d794434 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -105,7 +105,7 @@ module MPW
         'lang'           => @config.lang,
         'gpg_key'        => @config.gpg_key,
         'default_wallet' => @config.default_wallet,
-        'config_dir'     => @config.config_dir,
+        'wallet_dir'     => @config.wallet_dir,
         'pinmode'        => @config.pinmode,
         'gpg_exe'        => @config.gpg_exe
       }
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index bdcf394..016c690 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -58,11 +58,13 @@ module MPW
       wallet_dir     = options[:wallet_dir]     || @wallet_dir
       default_wallet = options[:default_wallet] || @default_wallet
       gpg_exe        = options[:gpg_exe]        || @gpg_exe
-      pinmode        = options[:pinmode]        || @pinmode
-      password       = { numeric: true,
-                         alpha:   true,
-                         special: false,
-                         length:  16 }
+      pinmode        = options.key?(:pinmode) ? options[:pinmode] : @pinmode
+      password       = {
+        numeric: true,
+        alpha:   true,
+        special: false,
+        length:  16
+      }
 
       %w[numeric special alpha length].each do |k|
         if options.key?("pwd_#{k}".to_sym)
@@ -134,7 +136,7 @@ module MPW
       @default_wallet = config['default_wallet']
       @gpg_exe        = config['gpg_exe']
       @password       = config['password'] || {}
-      @pinmode        = config['pinmode']
+      @pinmode        = config['pinmode'] || false
 
       raise if @gpg_key.empty? || @wallet_dir.empty?
 

From 7c369861418c0b0f5a262476ceef7d515a2562ae Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Fri, 19 May 2017 22:28:15 +0200
Subject: [PATCH 17/58] feat: show wallet list by default

---
 bin/mpw-wallet | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index c23e1e2..caee35d 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -74,12 +74,10 @@ cli    = MPW::Cli.new(config)
 
 cli.load_config
 
-if options.key?(:list)
-  cli.list_wallet
-elsif options.key?(:path)
+if options.key?(:path)
   cli.get_wallet(options[:wallet])
   cli.set_wallet_path(options[:path])
-else
+elsif options.key?(:list_keys) || options.key?(:gpg_key)
   cli.get_wallet(options[:wallet])
   cli.decrypt
 
@@ -88,4 +86,6 @@ else
   elsif options.key?(:gpg_key)
     options[:delete] ? cli.delete_key(options[:gpg_key]) : cli.add_key(options[:gpg_key])
   end
+else
+  cli.list_wallet
 end

From 3b9ff8c15cb2bd0229fa31407da4e6a909b4ebb9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 May 2017 08:06:50 +0200
Subject: [PATCH 18/58] add test cli for wallet options

---
 .travis.yml      |  1 +
 test/init.rb     |  8 ++++++--
 test/test_cli.rb | 50 +++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index f9db456..b640d4d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,4 +18,5 @@ script:
   - ruby ./test/test_item.rb
   - ruby ./test/test_mpw.rb
   - ruby ./test/test_translate.rb
+  - ruby ./test/init.rb
   - ruby ./test/test_cli.rb
diff --git a/test/init.rb b/test/init.rb
index be7a73a..6da9ecb 100644
--- a/test/init.rb
+++ b/test/init.rb
@@ -1,13 +1,17 @@
 #!/usr/bin/ruby
 
+require 'fileutils'
 require 'gpgme'
 
+FileUtils.rm_rf("#{Dir.home}/.config/mpw")
+FileUtils.rm_rf("#{Dir.home}/.gnupg")
+
 param = ''
 param << '<GnupgKeyParms format="internal">' + "\n"
 param << "Key-Type: RSA\n"
-param << "Key-Length: 2048\n"
+param << "Key-Length: 512\n"
 param << "Subkey-Type: ELG-E\n"
-param << "Subkey-Length: 2048\n"
+param << "Subkey-Length: 512\n"
 param << "Name-Real: test\n"
 param << "Name-Comment: test\n"
 param << "Name-Email: test2@example.com\n"
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 56326c2..3e97308 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -1,6 +1,5 @@
 #!/usr/bin/ruby
 
-require 'fileutils'
 require 'i18n'
 require 'test/unit'
 
@@ -20,9 +19,6 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_00_init_config
-    FileUtils.rm_rf("#{Dir.home}/.config/mpw")
-    FileUtils.rm_rf("#{Dir.home}/.gnupg")
-
     output = %x(echo "#{@password}\n#{@password}" | mpw config --init #{@gpg_key})
     assert_match(I18n.t('form.setup_config.valid'), output)
     assert_match(I18n.t('form.setup_gpg_key.valid'), output)
@@ -105,7 +101,51 @@ class TestConfig < Test::Unit::TestCase
     assert_match(I18n.t('display.nothing'), output)
   end
 
-  def test_05_setup_config
+  def test_05_setup_wallet
+    path    = '/tmp/'
+    gpg_key = 'test2@example.com'
+
+    output = %x(echo #{@password} | mpw wallet --add-gpg-key #{gpg_key})
+    puts output
+    assert_match(I18n.t('form.add_key.valid'), output)
+
+    output = %x(echo #{@password} | mpw wallet --list-keys)
+    puts output
+    assert_match("| #{@gpg_key}", output)
+    assert_match("| #{gpg_key}", output)
+
+    output = %x(echo #{@password} | mpw wallet --delete-gpg-key #{gpg_key})
+    puts output
+    assert_match(I18n.t('form.delete_key.valid'), output)
+
+    output = %x(echo #{@password} | mpw wallet --list-keys)
+    puts output
+    assert_match("| #{@gpg_key}", output)
+    assert_no_match(/\| #{gpg_key}/, output)
+
+    output = %x(mpw wallet)
+    puts output
+    assert_match('| default', output)
+
+    output = %x(mpw wallet --path #{path})
+    puts output
+    assert_match(I18n.t('form.set_wallet_path.valid'), output)
+
+    output = %x(mpw config)
+    puts output
+    assert_match(%r{path_wallet_default.+\| #{path}/default.mpw}, output)
+    assert(File.exist?("#{path}/default.mpw"))
+
+    output = %x(mpw wallet --default-path)
+    puts output
+    assert_match(I18n.t('form.set_wallet_path.valid'), output)
+
+    output = %x(mpw config)
+    puts output
+    assert_no_match(/path_wallet_default/, output)
+  end
+
+  def test_06_setup_config
     gpg_key    = 'user@example2.com'
     gpg_exe    = '/usr/bin/gpg2'
     wallet_dir = '/tmp/mpw'

From 01831c1f2b703bc2e99f84e9328f7b52874e52cd Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 May 2017 11:31:37 +0200
Subject: [PATCH 19/58] add test cli for genpwd

---
 test/test_cli.rb | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/test/test_cli.rb b/test/test_cli.rb
index 3e97308..85d5a5a 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -197,4 +197,29 @@ class TestConfig < Test::Unit::TestCase
       assert_match(/password_#{k}.+\| true/, output)
     end
   end
+
+  def test_07_generate_password
+    length = 24
+
+    output = %x(
+      mpw genpwd \
+      --length #{length} \
+      --alpha
+    )
+    assert_match(/[a-zA-Z]{#{length}}/, output)
+
+    output = %x(
+      mpw genpwd \
+      --length #{length} \
+      --numeric
+    )
+    assert_match(/[0-9]{#{length}}/, output)
+
+    output = %x(
+      mpw genpwd \
+      --length #{length} \
+      --special-chars
+    )
+    assert_no_match(/[a-zA-Z0-9]/, output)
+  end
 end

From 811c576aae1866978dfb62b9c04f8d1f53898f0b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 May 2017 12:03:57 +0200
Subject: [PATCH 20/58] fix password generator

---
 lib/mpw/mpw.rb | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index fbca576..9917801 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -301,11 +301,9 @@ module MPW
       chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
 
       result = ''
-      while length > 62
-        result << chars.sample(62).join
-        length -= 62
+      length.times do
+        result << chars.sample
       end
-      result << chars.sample(length).join
 
       result
     end

From efff66b12e8c52bdb374cdac9b39c5c69d888cef Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 May 2017 13:26:12 +0200
Subject: [PATCH 21/58] add test cli for export and import

---
 test/files/fixtures-import.yml | 19 +++++++++++++++++++
 test/test_cli.rb               | 34 +++++++++++++++++++++++++++++++---
 2 files changed, 50 insertions(+), 3 deletions(-)
 create mode 100644 test/files/fixtures-import.yml

diff --git a/test/files/fixtures-import.yml b/test/files/fixtures-import.yml
new file mode 100644
index 0000000..f79ab0a
--- /dev/null
+++ b/test/files/fixtures-import.yml
@@ -0,0 +1,19 @@
+---
+1:
+  host: fric.com
+  user: 230403
+  group: Bank
+  password: 5XdiTQOubRDw9B0aJoMlcEyL
+  protocol: https
+  port:
+  otp_key: 330223432
+  comment: I love my bank
+2:
+  host: assurance.com
+  user: user_2132
+  group: Assurance
+  password: DMyK6B3v4bWO52VzU7aTHIem
+  protocol: https
+  port: 443 
+  otp_key: 
+  comment: 
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 85d5a5a..2dd411f 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -101,7 +101,35 @@ class TestConfig < Test::Unit::TestCase
     assert_match(I18n.t('display.nothing'), output)
   end
 
-  def test_05_setup_wallet
+  def test_05_import_export
+    file_import = './test/files/fixtures-import.yml'
+    file_export = '/tmp/test-mpw.yml'
+
+    output = %x(echo #{@password} | mpw import --file #{file_import})
+    assert_match(I18n.t('form.import.valid', file: file_import), output)
+
+    output = %x(echo #{@password} | mpw export --file #{file_export})
+    assert_match(I18n.t('form.export.valid', file: file_export), output)
+    assert(File.exist?(file_export))
+    assert_equal(YAML.load_file(file_export).length, 2)
+
+    YAML.load_file(file_import).each_value do |import|
+      error = true
+
+      YAML.load_file(file_export).each_value do |export|
+        next if import['host'] != export['host']
+
+        %w[user group password protocol port otp_key comment].each do |key|
+          assert_equal(import[key].to_s, export[key].to_s)
+        end
+
+        error = false
+      end
+      assert(!error)
+    end
+  end
+
+  def test_06_setup_wallet
     path    = '/tmp/'
     gpg_key = 'test2@example.com'
 
@@ -145,7 +173,7 @@ class TestConfig < Test::Unit::TestCase
     assert_no_match(/path_wallet_default/, output)
   end
 
-  def test_06_setup_config
+  def test_07_setup_config
     gpg_key    = 'user@example2.com'
     gpg_exe    = '/usr/bin/gpg2'
     wallet_dir = '/tmp/mpw'
@@ -198,7 +226,7 @@ class TestConfig < Test::Unit::TestCase
     end
   end
 
-  def test_07_generate_password
+  def test_08_generate_password
     length = 24
 
     output = %x(

From 6541931b792431fbe5181e145a76403ffb9ed4a4 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 May 2017 15:30:19 +0200
Subject: [PATCH 22/58] enable text editor if threre isn't nothing value who
 has been set

---
 bin/mpw-update | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/bin/mpw-update b/bin/mpw-update
index 5e2c62d..143e187 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -93,6 +93,8 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config)
 
+options[:text_editor] = true if values.empty?
+
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt

From 90486d1e9338f57801f029622fde5b0d0cd4e96c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 May 2017 15:42:28 +0200
Subject: [PATCH 23/58] update README

---
 README.md | 61 ++++++++++++++++++++++++++++---------------------------
 1 file changed, 31 insertions(+), 30 deletions(-)

diff --git a/README.md b/README.md
index afb4ade..c1abef6 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,9 @@ mpw config --init user@host.com
 
 Add your first item:
 ```
-mpw add
+mpw add --host assurance.com --port 443 --user user_2132 --protocol https --random
+mpw add --host fric.com --user 230403 --otp-code 23434113 --protocol https --comment 'I love my bank' --random
+
 ```
 
 And list your items:
@@ -40,46 +42,45 @@ mpw list
 ```
 or search an item with
 ```
-mpw list --pattern Da
+mpw list --pattern love
 mpw list --group bank
 ```
 
 Output:
 ```
+Assurance
+ ==========================================================================
+  ID | Host                        | User        | OTP | Comment          
+ ==========================================================================
+  1  | https://assurance.com:443   | user_2132   |     |                  
+
 Bank
- ==============================================================================
-  ID | Host          | User      | Protocol | Port | OTP | Comment                
- ==============================================================================
-  1  | bank.com      | 1234456   | https    |      |  X  |                        
-
-Linux
- ==============================================================================
-  ID | Host          | User      | Protocol | Port | OTP | Comment                
- ==============================================================================
-  2  | linuxfr.org   | example   | https    |      |     | Da Linux French Site
-
+ ==========================================================================
+  ID | Host                        | User        | OTP | Comment          
+ ==========================================================================
+  3  | https://fric.com            | 230403      |  X  | I love my bank   
 ```
 
 Copy a password, login or OTP code:
 ```
-mpw copy -p linuxfr
+mpw copy -p assurance.com
 ```
 
 Update an item:
 ```
-mpw update -p linuxfr
+mpw update -p assurance.com
 ```
 
 Delete an item:
 ```
-mpw delete -p linuxfr
+mpw delete -p assurance.com
 ```
 
 ### Manage wallets
 
 List all available wallets:
 ```
-mpw wallet --list
+mpw wallet
 ```
 
 List all GPG keys in wallet:
@@ -116,23 +117,23 @@ Example yaml file for mpw:
 ```
 ---
 1:
-  host: bank.com
-  user: 123456
+  host: fric.com
+  user: 230403
   group: Bank
-  password: secret
+  password: 5XdiTQOubRDw9B0aJoMlcEyL
   protocol: https
-  port: 
-  otp_key: 1afg34
-  comment: 
+  port:
+  otp_key: 330223432
+  comment: I love my bank
 2:
-  host: linuxfr.org
-  user: example
-  group: 
-  password: 'complex %- password'
+  host: assurance.com
+  user: user_2132
+  group: Assurance
+  password: DMyK6B3v4bWO52VzU7aTHIem
   protocol: https
-  port: 
-  otp_key: 
-  comment: Da Linux French Site
+  port: 443
+  otp_key:
+  comment:
 ```
 
 ### Config

From 5f29f7b6e826c104dbd074650cc58d9342d9ee3b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 21 May 2017 17:19:46 +0200
Subject: [PATCH 24/58] add test cli for copy mode

---
 test/test_cli.rb | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/test/test_cli.rb b/test/test_cli.rb
index 2dd411f..8fb91ee 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -129,7 +129,19 @@ class TestConfig < Test::Unit::TestCase
     end
   end
 
-  def test_06_setup_wallet
+  def test_06_copy
+    data = YAML.load_file('./test/files/fixtures-import.yml')[1]
+
+    output = %x(
+      echo "#{@password}\np\nq" | mpw copy \
+      --disable-clipboard \
+      -p #{data['host']}
+    )
+    puts output
+    assert_match(data['password'], output)
+  end
+
+  def test_07_setup_wallet
     path    = '/tmp/'
     gpg_key = 'test2@example.com'
 
@@ -173,7 +185,7 @@ class TestConfig < Test::Unit::TestCase
     assert_no_match(/path_wallet_default/, output)
   end
 
-  def test_07_setup_config
+  def test_08_setup_config
     gpg_key    = 'user@example2.com'
     gpg_exe    = '/usr/bin/gpg2'
     wallet_dir = '/tmp/mpw'
@@ -226,7 +238,7 @@ class TestConfig < Test::Unit::TestCase
     end
   end
 
-  def test_08_generate_password
+  def test_09_generate_password
     length = 24
 
     output = %x(

From ab459f954d0867a69582783acd7f33e338a7b401 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 21 May 2017 18:00:43 +0200
Subject: [PATCH 25/58] feat: add copy url

---
 i18n/en.yml    | 2 ++
 i18n/fr.yml    | 2 ++
 lib/mpw/cli.rb | 9 +++++++++
 3 files changed, 13 insertions(+)

diff --git a/i18n/en.yml b/i18n/en.yml
index 402facd..cd78280 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -111,8 +111,10 @@ en:
       login: "The login has been copied in clipboard."
       password: "The password has been copied in clipboard for 30s!"
       otp: "The OTP code has been copied for %{time}s!"
+      url: "The URL has been copied in clipboard."
       help:
          name: "Help"
+         url: "Press <u> to copy URL"
          login: "Press <l> to copy the login"
          password: "Press <p> to copy the password"
          otp_code: "Press <o> to copy the otp code"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index b1b4ecc..d529ee4 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -111,8 +111,10 @@ fr:
       login: "L'identifiant a été copié dans le presse papier"
       password: "Le mot de passe a été copié dans le presse papier pour 30s!"
       otp: "Le code OTP a été copié dans le presse papier il est valable %{time}s!"
+      url: "L'URL a été copié dans le presse papier"
       help:
          name: "Aide"
+         url: "Pressez <u> pour copier l'URL"
          login: "Pressez <l> pour copier l'identifiant"
          password: "Pressez <p> pour copier le mot de passe"
          otp_code: "Pressez <o> pour copier le code OTP"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index d794434..ccf212c 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -300,6 +300,14 @@ module MPW
         when 'q', 'quit'
           break
 
+        when 'u', 'url'
+          if clipboard
+            Clipboard.copy(item.url)
+            puts I18n.t('form.clipboard.url').green
+          else
+            puts item.url
+          end
+
         when 'l', 'login'
           if clipboard
             Clipboard.copy(item.user)
@@ -332,6 +340,7 @@ module MPW
 
         else
           puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
+          puts I18n.t('form.clipboard.help.url')
           puts I18n.t('form.clipboard.help.login')
           puts I18n.t('form.clipboard.help.password')
           puts I18n.t('form.clipboard.help.otp_code')

From dcb286ec09b56588df5e959459fc3f60f358c678 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 22 May 2017 20:32:06 +0200
Subject: [PATCH 26/58] add gem for development

---
 Gemfile | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Gemfile b/Gemfile
index 71d84d4..1fdac16 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,3 +6,8 @@ gem 'highline',  '~> 1.7', '>= 1.7.8'
 gem 'i18n',      '~> 0.7', '>= 0.7.0'
 gem 'locale',    '~> 2.1', '>= 2.1.2'
 gem 'rotp',      '~> 3.1', '>= 3.1.0'
+
+group :development do
+  gem 'rubocop'
+  gem 'yard'
+end

From 1fcac965e975ba9b11f47cebb3be3d106c165c1d Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 22 May 2017 20:32:27 +0200
Subject: [PATCH 27/58] fix comments

---
 lib/mpw/cli.rb | 4 ++--
 lib/mpw/mpw.rb | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index ccf212c..02ede97 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -273,7 +273,7 @@ module MPW
 
     # Get an item when multiple choice
     # @param items [Array] list of items
-    # @return item [Item]
+    # @return [Item] an item
     def get_item(items)
       return items[0] if items.length == 1
 
@@ -414,7 +414,7 @@ module MPW
     end
 
     # Text editor interface
-    # @param template [String] template name
+    # @param template_name [String] template name
     # @param item [Item] the item to edit
     # @param password [Boolean] disable field password
     # @return [Hash] the values for an item
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 9917801..5905492 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -221,7 +221,7 @@ module MPW
     end
 
     # Search in some csv data
-    # @params options [Hash]
+    # @param options [Hash]
     # @return [Array] a list with the resultat of the search
     def list(**options)
       result = []

From b83b27832e5d44e5c8cb726279b234529dcfa3ed Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 22 May 2017 21:25:56 +0200
Subject: [PATCH 28/58] feat: no ask password if not necessary

---
 lib/mpw/cli.rb | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 02ede97..d92e4e4 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -126,12 +126,20 @@ module MPW
 
     # Request the GPG password and decrypt the file
     def decrypt
-      unless defined?(@mpw)
-        @password = ask(I18n.t('display.gpg_password')) { |q| q.echo = false }
-        @mpw      = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe, @config.pinmode)
-      end
+      if defined?(@mpw)
+        @mpw.read_data
+      else
+        begin
+          @mpw = MPW.new(@config.gpg_key, @wallet_file, nil, @config.gpg_exe, @config.pinmode)
 
-      @mpw.read_data
+          @mpw.read_data
+        rescue
+          @password = ask(I18n.t('display.gpg_password')) { |q| q.echo = false }
+          @mpw      = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe, @config.pinmode)
+
+          @mpw.read_data
+        end
+      end
     rescue => e
       puts "#{I18n.t('display.error')} #11: #{e}".red
       exit 2

From a2c2711ddc1f5a60dc5fd4b62172e6f5b66a6d11 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 23 May 2017 19:33:05 +0200
Subject: [PATCH 29/58] add gem test-unit for development

---
 .travis.yml | 2 --
 Gemfile     | 1 +
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index b640d4d..525a9d0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,6 @@ rvm:
   - 2.1.10
 install:
   - bundle install
-  - gem install 'test-unit'
-  - gem install rubocop
   - echo 9999 > VERSION
   - gem build mpw.gemspec
   - gem install mpw-9999.gem
diff --git a/Gemfile b/Gemfile
index 1fdac16..56b6638 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,5 +9,6 @@ gem 'rotp',      '~> 3.1', '>= 3.1.0'
 
 group :development do
   gem 'rubocop'
+  gem 'test-unit'
   gem 'yard'
 end

From 6ea40e542025d9b186b0edce061da89423627533 Mon Sep 17 00:00:00 2001
From: fbonely <fanny.bonely@arte.tv>
Date: Tue, 23 May 2017 19:49:20 +0200
Subject: [PATCH 30/58] Update translations EN

---
 i18n/en.yml | 96 ++++++++++++++++++++++++++---------------------------
 1 file changed, 48 insertions(+), 48 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index cd78280..b50fd63 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -5,27 +5,27 @@ en:
     config:
       write: "Can't write the config file!"
       load: "Checkconfig failed!"
-      key_bad_format: "The key string isn't in good format!"
-      no_key_public: "You haven't the public key of %{key}!"
+      key_bad_format: "The key string isn't in the right format!"
+      no_key_public: "You haven't entered the public key of %{key}!"
       genkey_gpg:
         exception: "Can't create the GPG key!"
         name: "You must define a name for your GPG key!"
         password: "You must define a password for your GPG key!"
-    empty: "The class is void"
+    empty: "The class is empty"
     export: "Can't export, unable to write in %{file}!"
     export_key: "Can't export the GPG key"
     gpg_file: 
       decrypt: "Can't decrypt file!"
       encrypt: "Can't encrypt the GPG file!"
     mpw_file: 
-      read_data: "Can't to read the MPW file!"
-      write_data: "Can't to write the MPW file!"
+      read_data: "Can't read the MPW file!"
+      write_data: "Can't write the MPW file!"
     import: "Can't import, unable to read %{file}!"
     update:
       host_empty: "You must define a host!"
 
   warning:
-    select: 'Your choice is not a valid element!'
+    select: 'Your choice is not a valid item!'
 
   command:
     add: "Add a new item"
@@ -41,37 +41,37 @@ en:
 
   option:
     add: "Add an item or key"
-    add_gpg_key: "Share the wallet with an other GPG key"
-    alpha: "Use letter to generate a password"
+    add_gpg_key: "Share the wallet with another GPG key"
+    alpha: "Use letter to create a password"
     comment: "Specify a comment"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
-    default_path: "Move the wallet in default directory"
+    default_path: "Move the wallet to the default directory"
     default_wallet: "Specify the default wallet to use"
-    delete_gpg_key: "Delete the wallet's share with an other GPG key"
-    disable_alpha: "Don't use letter to generate a password"
-    disable_numeric: "Don't use number to generate a password"
+    delete_gpg_key: "Delete wallet sharing with an other GPG key"
+    disable_alpha: "Don't use letters to create a password"
+    disable_numeric: "Don't use numbers to generate a password"
     disable_pinmode: "Disable the pinentry mode"
-    disable_special_chars: "Don't use special char to generate a password"
+    disable_special_chars: "Don't use special char to create a password"
     export: "Export a wallet in an yaml file"
-    file_export: "Specify the file where export data"
+    file_export: "Specify the file to export data"
     file_import: "Specify the file to import"
-    force: "No ask to confirm when you delete an item"
-    generate_password: "Generate a random password (default 8 characters)"
+    force: "Do not ask confirmation when deleting an item"
+    generate_password: "Create a random password (default 8 characters)"
     gpg_exe: "Set the gpg binary path to use"
     gpg_key: "Specify a GPG key (ex: user@example.com)"
     group: "Search the items with specified group"
     help: "Show this help message"
     host: "Specify a host or ip"
     init: "Initialize mpw"
-    import: "Import item since a yaml file"
-    key: "Specify the key name"
+    import: "Import item from an yaml file"
+    key: "Define the key name"
     lang: "Set the software language"
     length: "Size of the password"
     list: "List the wallets"
     list_keys: "List the GPG keys in wallet"
-    new_group: "Specify the group for the item"
-    numeric: "Use number to generate a password"
+    new_group: "Define a group for the item"
+    numeric: "Use number to create a password"
     otp_code: "Set an otp key"
     path: "Move the wallet in new specify directory"
     pattern: "Given search pattern"
@@ -81,11 +81,11 @@ en:
     random_password: "Generate a random password"
     setup: "Create a new configuration file" 
     setup_wallet: "Create a new configuration file for a wallet"
-    special_chars: "Use special char to generate a password"
-    show: "Search and show the items"
-    show_all: "List all items"
+    special_chars: "Use special char to create a password"
+    show: "Search and display the items"
+    show_all: "Listing all items"
     text_editor: "Use text editor to edit the item"
-    usage: "Usage"
+    usage: "Use"
     user: "Set an user"
     wallet: "Specify a wallet to use"
     wallet_dir: "Set the wallets folder"
@@ -95,13 +95,13 @@ en:
     add_key:
       valid: "Key has been added!"
     add_item:
-      name: "The item's name (mandatory"
-      group: "The group's name"
-      host: "The hostname or ip"
-      protocol: "The protocol of the connection (ssh, http, ...)"
-      login: "The login of connection"
-      password: "The password"
-      port: "The connection port"
+      name: "Item name (mandatory)"
+      group: "Group name"
+      host: "Hostname or ip"
+      protocol: "Connection protocol (ssh, http, ...)"
+      login: "Connection ID"
+      password: "Password"
+      port: "Connection port"
       comment: "A comment"
       otp_key: "The OTP secret"
       valid: "Item has been added!"
@@ -110,7 +110,7 @@ en:
       clean: "The clipboard has been cleaned."
       login: "The login has been copied in clipboard."
       password: "The password has been copied in clipboard for 30s!"
-      otp: "The OTP code has been copied for %{time}s!"
+      otp: "The OTP code has been copied %{time}s!"
       url: "The URL has been copied in clipboard."
       help:
          name: "Help"
@@ -122,18 +122,18 @@ en:
     delete_key:
       valid: "Key has been deleted!"
     delete_item:
-      ask: "Are you sure you want to remove the item ?"
+      ask: "Are you sure you want to remove this item ?"
       valid: "The item has been removed!"
     import:
       ask: "Are you sure you want to import this file %{file} ?"
       file_empty: "The import file is empty!"
       file_not_exist: "The import file doesn't exist!"
-      valid: "The import is succesfull!"
+      valid: "The import is successful!"
       not_valid: "No data to import!"
     set_config:
       valid: "The config file has been edited!"
     set_wallet_path:
-      valid: "The wallet has well moved!"
+      valid: "The wallet has been moved!"
     setup_config:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...) [default=%{lang}]: "
@@ -143,36 +143,36 @@ en:
       valid: "The config file has been created!"
     setup_gpg_key:
       title: "Setup a GPG key"
-      ask: "Do you want create your GPG key ? (Y/n)"
-      no_create: "You must create manually your GPG key or relaunch the software."
+      ask: "Do you want to create your GPG key ? (Y/n)"
+      no_create: "You must to create manually your GPG key or relaunch the software."
       name: "Your name and lastname: "
       password: "A password for the GPG key: "
       confirm_password: "Confirm your password: "
       error_password: "Your passwords aren't identical!" 
       length: "Size of the GPG key [default=2048]: "
       expire: "Expire time of the GPG key [default=0 (unlimited)]: "
-      wait: "Please waiting during the GPG key generate, this process can take few minutes."
+      wait: "Please wait until GPG key is created, this process can take a few minutes."
       valid: "Your GPG key has been created ;-)"
     update_item:
-      name: "The item's name (mandatory"
-      group: "The group's name"
-      host: "The hostname or ip"
-      protocol: "The protocol of the connection (ssh, http, ...)"
-      login: "The login of connection"
-      password: "The password (leave empty if you don't want change)"
-      port: "The connection port"
+      name: "Item name (mandatory)"
+      group: "Group name"
+      host: "Hostname or ip"
+      protocol: "Connection protocol (ssh, http, ...)"
+      login: "Login id"
+      password: "Password (leave empty if you don't want to update it)"
+      port: "Connection port"
       comment: "A comment"
-      otp_key: "The OTP secret (leave empty if you don't want change"
+      otp_key: "Secret OTP (leave empty if you don't want to update it"
       valid: "Item has been updated!" 
     export:
-      valid: "The export in %{file} is succesfull!"
+      valid: "The export in %{file} is successful!"
 
   display:
     comment: "Comment"
     config: "Configuration"
     error: "ERROR"
     keys: "GPG keys"
-    gpg_password: "GPG passphrase: "
+    gpg_password: "GPG password: "
     group: "Group"
     login: "Login"
     name: "Name"

From 767343e36879dee6f9bed415119be93906b66329 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 6 Jun 2017 22:56:23 +0200
Subject: [PATCH 31/58] update version 4.2.0

---
 CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++-----------
 README.md    |  2 +-
 VERSION      |  2 +-
 3 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1fb8d79..2430516 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,18 @@
 # CHANGELOG
-## v4.1.1
+## v4.2.0 (2017-06-06)
+
+ * feat: improve the interface
+ * feat: add copy url
+ * feat: add unit tests for cli
+ * feat: comment the code with yarn syntax
+ * fix several bugs
+ * fix translations
+
+## v4.1.1 (2017-05-03)
 
  * fix bug in init
 
-## v4.1.0
+## v4.1.0 (2017-04-22)
 
  * feat: add options to update or add an item in command line
  * feat: print config
@@ -12,18 +21,18 @@
  * fix: pinentry mode with gpg >= 2.1
  * remove SSH and FTP synchronization
 
-## v4.0.0
+## v4.0.0 (2017-03-09)
 
  * feature: set default wallet
  * add option for generate a random password when you update an item
  * fix encryption when you share an existing wallet
  * several bugs fix
 
-## v4.0.0-beta1
+## v4.0.0-beta1 (2017-02-16)
 
  * add manage share key with new interface
 
-## v4.0.0-beta
+## v4.0.0-beta (2016-11-11)
 
  * new interface with a table
  * new command line interface
@@ -32,38 +41,50 @@
  * several bugs fix
  * add unit tests
 
-## v3.2.1
+## v3.2.1 (2016-08-06)
 
  * fix bug when add a new item
 
-## v3.2.0
+## v3.2.0 (2016-08-03)
 
  * add support OTP
  * fix bug in synchronize
  * improve interface
 
-## v3.1.0
+## v3.1.0 (2016-07-09)
 
  * add clipboard
  * can change gpg version
  * minor change in interface
  * several bugs fix
 
-## v3.0.0
+## v3.0.0 (2016-07-05)
 
  * new storage format
  * new share system
  * remove MPW server
 
-## v2.0.0
+## v2.0.3 (2015-09-27)
+
+ * add no-sync option
+
+## v2.0.1 (2015-06-23)
+
+ * fix mpw-ssh
+
+## v2.0.0 (2015-06-22)
 
  * change format csv to yaml
  * easy install with gem
  * add sync with ftp and ssh
  * many improvement
 
-## v1.1.0
+## v1.1.0 (2014-01-28)
 
  * Add sync with MPW Server
  * Add MPW Server
  * Fix minors bugs
+
+## v1.0.0 (2014-01-15)
+
+  * first release
diff --git a/README.md b/README.md
index c1abef6..8a39d81 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # MPW: Manage your passwords!
-[![Version](https://img.shields.io/badge/latest_version-4.1.1-green.svg)](https://github.com/nishiki/manage-password/releases)
+[![Version](https://img.shields.io/badge/latest_version-4.2.0-green.svg)](https://github.com/nishiki/manage-password/releases)
 [![Build Status](https://travis-ci.org/nishiki/manage-password.svg?branch=master)](https://travis-ci.org/nishiki/manage-password)
 [![License](https://img.shields.io/badge/license-GPL--2.0-blue.svg)](https://github.com/nishiki/manage-password/blob/master/LICENSE)
 
diff --git a/VERSION b/VERSION
index 627a3f4..6aba2b2 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.1.1
+4.2.0

From 11de55ccb76f6899071130a61ca7ebe0187ad9b9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 6 Jun 2017 23:06:25 +0200
Subject: [PATCH 32/58] update Gemfile to fix rubocop version

---
 Gemfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile b/Gemfile
index 56b6638..de2741f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,7 +8,7 @@ gem 'locale',    '~> 2.1', '>= 2.1.2'
 gem 'rotp',      '~> 3.1', '>= 3.1.0'
 
 group :development do
-  gem 'rubocop'
+  gem 'rubocop', '0.48.1'
   gem 'test-unit'
   gem 'yard'
 end

From d185509113bc11503d7fcdb788f597df7b81efe5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 30 Jul 2017 22:30:15 +0200
Subject: [PATCH 33/58] fix bug in otp generator

---
 CHANGELOG.md   | 4 ++++
 README.md      | 2 +-
 VERSION        | 2 +-
 lib/mpw/mpw.rb | 2 +-
 4 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2430516..9367be4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
 # CHANGELOG
+## v4.2.1 (2017-07-30)
+
+  * fix bug in otp generator
+
 ## v4.2.0 (2017-06-06)
 
  * feat: improve the interface
diff --git a/README.md b/README.md
index 8a39d81..e6a119f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # MPW: Manage your passwords!
-[![Version](https://img.shields.io/badge/latest_version-4.2.0-green.svg)](https://github.com/nishiki/manage-password/releases)
+[![Version](https://img.shields.io/badge/latest_version-4.2.1-green.svg)](https://github.com/nishiki/manage-password/releases)
 [![Build Status](https://travis-ci.org/nishiki/manage-password.svg?branch=master)](https://travis-ci.org/nishiki/manage-password)
 [![License](https://img.shields.io/badge/license-GPL--2.0-blue.svg)](https://github.com/nishiki/manage-password/blob/master/LICENSE)
 
diff --git a/VERSION b/VERSION
index 6aba2b2..fae6e3d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.2.0
+4.2.1
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 5905492..81a0231 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -272,7 +272,7 @@ module MPW
     # @param id [String] the item id
     # @return [String] an otp code
     def get_otp_code(id)
-      @otp_keys.key?(id) ? 0 : ROTP::TOTP.new(decrypt(@otp_keys[id])).now
+      @otp_keys.key?(id) ? ROTP::TOTP.new(decrypt(@otp_keys[id])).now : 0
     end
 
     # Get remaining time before expire otp code

From 948db2e905e8801dfd8b7acf4545317865f3c5d9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 10 Aug 2017 22:45:03 +0200
Subject: [PATCH 34/58] feat: minor improve clipboard interface

---
 lib/mpw/cli.rb | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index d92e4e4..33d4151 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -291,6 +291,17 @@ module MPW
       choice >= 1 && choice <= items.length ? items[choice - 1] : nil
     end
 
+    # Print help message for clipboard mode
+    # @param item [Item]
+    def clipboard_help(item)
+      puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
+      puts I18n.t('form.clipboard.help.url')
+      puts I18n.t('form.clipboard.help.login')
+      puts I18n.t('form.clipboard.help.password')
+      puts I18n.t('form.clipboard.help.otp_code') if item.otp
+      puts I18n.t('form.clipboard.help.quit')
+    end
+
     # Copy in clipboard the login and password
     # @param item [Item]
     # @param clipboard [Boolean] enable clipboard
@@ -339,7 +350,10 @@ module MPW
           end
 
         when 'o', 'otp'
-          if clipboard
+          if !item.otp
+            clipboard_help(item)
+            next
+          elsif clipboard
             Clipboard.copy(@mpw.get_otp_code(item.id))
           else
             puts @mpw.get_otp_code(item.id)
@@ -347,13 +361,7 @@ module MPW
           puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
 
         else
-          puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
-          puts I18n.t('form.clipboard.help.url')
-          puts I18n.t('form.clipboard.help.login')
-          puts I18n.t('form.clipboard.help.password')
-          puts I18n.t('form.clipboard.help.otp_code')
-          puts I18n.t('form.clipboard.help.quit')
-          next
+          clipboard_help(item)
         end
       end
 

From 65db261c00224f8d35f883e703d7a736d79d9f83 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 10 Aug 2017 23:23:06 +0200
Subject: [PATCH 35/58] feat: minor improve get_item interface

---
 i18n/en.yml    | 4 +++-
 i18n/fr.yml    | 4 +++-
 lib/mpw/cli.rb | 6 ++++--
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index b50fd63..962b6ef 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -91,7 +91,9 @@ en:
     wallet_dir: "Set the wallets folder"
 
   form:
-    select: "Select the item: "
+    select:
+      choice: "Select the item: "
+      error: "No item selected"
     add_key:
       valid: "Key has been added!"
     add_item:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index d529ee4..e117c1f 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -91,7 +91,9 @@ fr:
     wallet_dir: "Spécifie le répertoire des portefeuilles"
 
   form:
-    select: "Sélectionner l'élément: "
+    select:
+      choice: "Sélectionner l'élément: "
+      error: "Aucun élément sélectionné"
     add_key:
       valid: "La clé a bien été ajoutée!"
     add_item:
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 33d4151..02d6eb9 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -286,9 +286,11 @@ module MPW
       return items[0] if items.length == 1
 
       items.sort! { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
-      choice = ask(I18n.t('form.select')).to_i
+      choice = ask(I18n.t('form.select.choice')).to_i
 
-      choice >= 1 && choice <= items.length ? items[choice - 1] : nil
+      raise I18n.t('form.select.error') unless choice >= 1 && choice <= items.length
+
+      items[choice - 1]
     end
 
     # Print help message for clipboard mode

From 0195771c76a15b83b407c3ca420cd7b0a02530a9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 10 Aug 2017 23:41:20 +0200
Subject: [PATCH 36/58] feat: minor improvement add_form interface

---
 templates/add_form.erb | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/templates/add_form.erb b/templates/add_form.erb
index d88874e..89b750e 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -1,9 +1,17 @@
 ---
-host: <%= options[:host] %>         # <%= I18n.t('form.add_item.host') %>
-user: <%= options[:user] %>         # <%= I18n.t('form.add_item.login') %>
-group: <%= options[:group] %>       # <%= I18n.t('form.add_item.group') %> 
-protocol: <%= options[:protocol] %> # <%= I18n.t('form.add_item.protocol') %><% unless password %>
-password:                           # <%= I18n.t('form.add_item.password') %><% end %>
-port: <%= options[:port] %>         # <%= I18n.t('form.add_item.port') %>
-comment: <%= options[:comment] %>   # <%= I18n.t('form.add_item.comment') %>
-otp_key: <%= options[:otp] %>       # <%= I18n.t('form.add_item.otp_key') %>
+# <%= I18n.t('form.add_item.host') %>
+host: <%= options[:host] %>
+# <%= I18n.t('form.add_item.login') %>
+user: <%= options[:user] %>
+# <%= I18n.t('form.add_item.group') %>
+group: <%= options[:group] %>
+# <%= I18n.t('form.add_item.protocol') %>
+protocol: <%= options[:protocol] %><% unless password %>
+# <%= I18n.t('form.add_item.password') %>
+password:<% end %>
+# <%= I18n.t('form.add_item.port') %>
+port: <%= options[:port] %>
+# <%= I18n.t('form.add_item.comment') %>
+comment: <%= options[:comment] %>
+# <%= I18n.t('form.add_item.otp_key') %>
+otp_key: <%= options[:otp] %>

From 8aeb7f5224ad165d657b4fe879c180a65021c0b6 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 12 Aug 2017 01:19:52 +0200
Subject: [PATCH 37/58] feat: alway use absolute path for wallet path

---
 lib/mpw/config.rb | 1 +
 test/test_cli.rb  | 7 +++----
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 016c690..95390a4 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -161,6 +161,7 @@ module MPW
     # @param wallet [String] wallet name
     def set_wallet_path(path, wallet)
       path = @wallet_dir if path == 'default'
+      path = File.absolute_path(path)
 
       return if path == @wallet_dir && File.exist?("#{@wallet_dir}/#{wallet}.mpw")
       return if path == @wallet_paths[wallet]
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 8fb91ee..8f5d546 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -142,7 +142,6 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_07_setup_wallet
-    path    = '/tmp/'
     gpg_key = 'test2@example.com'
 
     output = %x(echo #{@password} | mpw wallet --add-gpg-key #{gpg_key})
@@ -167,14 +166,14 @@ class TestConfig < Test::Unit::TestCase
     puts output
     assert_match('| default', output)
 
-    output = %x(mpw wallet --path #{path})
+    output = %x(mpw wallet --path '.')
     puts output
     assert_match(I18n.t('form.set_wallet_path.valid'), output)
 
     output = %x(mpw config)
     puts output
-    assert_match(%r{path_wallet_default.+\| #{path}/default.mpw}, output)
-    assert(File.exist?("#{path}/default.mpw"))
+    assert_match(%r{path_wallet_default.+\| #{Dir.pwd}/default.mpw}, output)
+    assert(File.exist?("#{Dir.pwd}/default.mpw"))
 
     output = %x(mpw wallet --default-path)
     puts output

From b2a38ccd4eeeef088fe0d6bd690b778bd1e2b589 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 15 Aug 2017 21:13:34 +0200
Subject: [PATCH 38/58] update version to 4.2.2

---
 CHANGELOG.md | 4 ++++
 README.md    | 2 +-
 VERSION      | 2 +-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9367be4..199b863 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
 # CHANGELOG
+## v4.2.2 (2017-08-15)
+
+  * minor improvements in the interface
+
 ## v4.2.1 (2017-07-30)
 
   * fix bug in otp generator
diff --git a/README.md b/README.md
index e6a119f..9c2c5d6 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # MPW: Manage your passwords!
-[![Version](https://img.shields.io/badge/latest_version-4.2.1-green.svg)](https://github.com/nishiki/manage-password/releases)
+[![Version](https://img.shields.io/badge/latest_version-4.2.2-green.svg)](https://github.com/nishiki/manage-password/releases)
 [![Build Status](https://travis-ci.org/nishiki/manage-password.svg?branch=master)](https://travis-ci.org/nishiki/manage-password)
 [![License](https://img.shields.io/badge/license-GPL--2.0-blue.svg)](https://github.com/nishiki/manage-password/blob/master/LICENSE)
 
diff --git a/VERSION b/VERSION
index fae6e3d..af8c8ec 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.2.1
+4.2.2

From e2b455798128c0bb58c5ff423491660cbaaf54cc Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 3 Sep 2017 02:37:17 +0200
Subject: [PATCH 39/58] feat: add import gorilla file

---
 bin/mpw-import            | 14 ++++++++--
 i18n/en.yml               |  4 ++-
 i18n/fr.yml               |  4 ++-
 lib/mpw/cli.rb            | 25 +++++++++++------
 lib/mpw/import/gorilla.rb | 57 +++++++++++++++++++++++++++++++++++++++
 lib/mpw/import/mpw.rb     | 29 ++++++++++++++++++++
 lib/mpw/item.rb           | 12 ++++-----
 7 files changed, 126 insertions(+), 19 deletions(-)
 create mode 100644 lib/mpw/import/gorilla.rb
 create mode 100644 lib/mpw/import/mpw.rb

diff --git a/bin/mpw-import b/bin/mpw-import
index 0502b2b..76e163e 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -24,7 +24,13 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
+formats =
+  Dir["#{File.expand_path('../../lib/mpw/import', __FILE__)}/*.rb"]
+    .map { |v| File.basename(v, '.rb') }
+    .join(', ')
+options = {
+  format: 'mpw'
+}
 
 OptionParser.new do |opts|
   opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
@@ -37,6 +43,10 @@ OptionParser.new do |opts|
     options[:file] = file
   end
 
+  opts.on('-F', '--format STRING', I18n.t('option.file_format', formats: formats)) do |format|
+    options[:format] = format
+  end
+
   opts.on('-h', '--help', I18n.t('option.help')) do
     puts opts
     exit 0
@@ -53,4 +63,4 @@ cli    = MPW::Cli.new(config)
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.import(options[:file])
+cli.import(options[:file], options[:format])
diff --git a/i18n/en.yml b/i18n/en.yml
index 962b6ef..2375084 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -22,7 +22,7 @@ en:
       write_data: "Can't write the MPW file!"
     import: "Can't import, unable to read %{file}!"
     update:
-      host_empty: "You must define a host!"
+      host_and_comment_empty: "You must define a host or a comment!"
 
   warning:
     select: 'Your choice is not a valid item!'
@@ -55,6 +55,7 @@ en:
     disable_special_chars: "Don't use special char to create a password"
     export: "Export a wallet in an yaml file"
     file_export: "Specify the file to export data"
+    file_format: "Format of import file (default: mpw; available: %{formats})"
     file_import: "Specify the file to import"
     force: "Do not ask confirmation when deleting an item"
     generate_password: "Create a random password (default 8 characters)"
@@ -130,6 +131,7 @@ en:
       ask: "Are you sure you want to import this file %{file} ?"
       file_empty: "The import file is empty!"
       file_not_exist: "The import file doesn't exist!"
+      format_unknown: "The import format '%{file_format} is unknown!"
       valid: "The import is successful!"
       not_valid: "No data to import!"
     set_config:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index e117c1f..9ab2e58 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -22,7 +22,7 @@ fr:
       write_data: "Impossible d'écrire le fichier MPW!"
     import: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
     update:
-      host_empty: "Vous devez définir un host!"
+      host_and_comment_empty: "Vous devez définir un host ou un commentaire!"
 
   warning:
     select: "Votre choix n'est pas un élément valide!"
@@ -55,6 +55,7 @@ fr:
     disable_special_chars: "Désactive l'utilisation des charactères speciaux dans la génération d'un mot de passe"
     export: "Exporte un portefeuille dans un fichier yaml"
     file_export: "Spécifie le fichier où exporter les données"
+    file_format: "Format du fichier d'import (défault: mpw; disponible: %{formats})"
     file_import: "Spécifie le fichier à importer"
     force: "Ne demande pas de confirmation pour la suppression d'un élément"
     generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
@@ -130,6 +131,7 @@ fr:
       ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
       file_empty: "Le fichier d'import est vide!"
       file_not_exist: "Le fichier d'import n'existe pas"
+      format_unknown: "Le format d'import '%{file_format}' est inconnu!"
       valid: "L'import est un succès!"
       not_valid: "Aucune donnée à importer!"
     set_config:
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 02d6eb9..17701b6 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -586,17 +586,26 @@ module MPW
 
     # Import items from an yaml file
     # @param file [String] path of import file
-    def import(file)
+    # @param format [String] the software import file format
+    def import(file, format = 'mpw')
       raise I18n.t('form.import.file_empty')     if file.to_s.empty?
       raise I18n.t('form.import.file_not_exist') unless File.exist?(file)
 
-      YAML.load_file(file).each_value do |row|
-        item = Item.new(group:    row['group'],
-                        host:     row['host'],
-                        protocol: row['protocol'],
-                        user:     row['user'],
-                        port:     row['port'],
-                        comment:  row['comment'])
+      begin
+        require "mpw/import/#{format}"
+      rescue LoadError
+        raise I18n.t('form.import.format_unknown', file_format: format)
+      end
+
+      Import.send(format, file).each_value do |row|
+        item = Item.new(
+          group:    row['group'],
+          host:     row['host'],
+          protocol: row['protocol'],
+          user:     row['user'],
+          port:     row['port'],
+          comment:  row['comment']
+        )
 
         next if item.empty?
 
diff --git a/lib/mpw/import/gorilla.rb b/lib/mpw/import/gorilla.rb
new file mode 100644
index 0000000..0ab5dc3
--- /dev/null
+++ b/lib/mpw/import/gorilla.rb
@@ -0,0 +1,57 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'csv'
+
+module MPW
+  module Import
+    # Import an export mpw file
+    # @param file [String] the file path to import
+    def self.gorilla(file)
+      data = {}
+
+      CSV.foreach(file, headers: true) do |row|
+        id = row['uuid']
+        comment =
+          if row['title'] && row['notes']
+            "#{row['title']} #{row['notes']}"
+          elsif row['title']
+            row['title']
+          elsif row['notes']
+            row['notes']
+          end
+
+        data[id] = {
+          'group'    => row['group'],
+          'host'     => row['url'],
+          'user'     => row['user'],
+          'password' => row['password'],
+          'comment'  => comment
+        }
+
+        if row['url'] =~ %r{^((?<protocol>[a-z]+)://)?(?<host>[a-zA-Z0-9_.-]+)(:(?<port>[0-9]{1,5}))?$}
+          data[id]['protocol'] = Regexp.last_match(:protocol)
+          data[id]['port']     = Regexp.last_match(:port)
+          data[id]['host']     = Regexp.last_match(:host)
+        end
+      end
+
+      data
+    end
+  end
+end
diff --git a/lib/mpw/import/mpw.rb b/lib/mpw/import/mpw.rb
new file mode 100644
index 0000000..8d6b1c4
--- /dev/null
+++ b/lib/mpw/import/mpw.rb
@@ -0,0 +1,29 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'yaml'
+
+module MPW
+  module Import
+    # Import an export mpw file
+    # @param file [String] the file path to import
+    def self.mpw(file)
+      YAML.load_file(file)
+    end
+  end
+end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index be03dba..4e17912 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -33,9 +33,7 @@ module MPW
 
     # @param options [Hash] the option :host is required
     def initialize(**options)
-      if !options.key?(:host) || options[:host].to_s.empty?
-        raise I18n.t('error.update.host_empty')
-      end
+      @host = ''
 
       if !options.key?(:id) || options[:id].to_s.empty? || !options.key?(:created) || options[:created].to_s.empty?
         @id = generate_id
@@ -53,12 +51,12 @@ module MPW
     # Update the item
     # @param options [Hash]
     def update(**options)
-      if options.key?(:host) && options[:host].to_s.empty?
-        raise I18n.t('error.update.host_empty')
+      unless options[:host] || options[:comment]
+        raise I18n.t('error.update.host_and_comment_empty')
       end
 
       @group     = options[:group]      if options.key?(:group)
-      @host      = options[:host]       if options.key?(:host)
+      @host      = options[:host]       if options.key?(:host) && !options[:host].nil?
       @protocol  = options[:protocol]   if options.key?(:protocol)
       @user      = options[:user]       if options.key?(:user)
       @port      = options[:port].to_i  if options.key?(:port) && !options[:port].to_s.empty?
@@ -86,7 +84,7 @@ module MPW
     def url
       url = ''
       url += "#{@protocol}://" if @protocol
-      url += @host
+      url += @host if @host
       url += ":#{@port}" if @port
 
       url

From 2a647afb10c205a1d62b75b91b6ca9a53133cae5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 3 Sep 2017 10:23:44 +0200
Subject: [PATCH 40/58] fix: force system on travis

---
 .travis.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.travis.yml b/.travis.yml
index 525a9d0..57c6924 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
 language: ruby
+dist: precise
 rvm:
   - 2.4.1
   - 2.3.4

From 42629c61b35da7bf7eb22e78e1f56839bcf35ecf Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 3 Sep 2017 15:10:01 +0200
Subject: [PATCH 41/58] feat: add test for import

---
 .travis.yml                   |  1 +
 test/files/import-gorilla.txt |  4 +++
 test/test_cli.rb              | 12 +++++----
 test/test_import.rb           | 46 +++++++++++++++++++++++++++++++++++
 4 files changed, 58 insertions(+), 5 deletions(-)
 create mode 100644 test/files/import-gorilla.txt
 create mode 100644 test/test_import.rb

diff --git a/.travis.yml b/.travis.yml
index 57c6924..e289bcf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,3 +19,4 @@ script:
   - ruby ./test/test_translate.rb
   - ruby ./test/init.rb
   - ruby ./test/test_cli.rb
+  - ruby ./test/test_import.rb
diff --git a/test/files/import-gorilla.txt b/test/files/import-gorilla.txt
new file mode 100644
index 0000000..a5fc604
--- /dev/null
+++ b/test/files/import-gorilla.txt
@@ -0,0 +1,4 @@
+uuid,group,title,url,user,password,notes
+49627979-e393-48c4-49ca-1cf66603238e,Bank,Fric,http://fric.com,12345,secret,money money
+49627979-e393-48c4-49ca-1cf66603238f,,My little server,server.com,secret2,
+49627979-e393-48c4-49ca-1cf66603238g,Cloud,,ssh://fric.com:4333,username,secret,bastion
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 8f5d546..f4cf356 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -185,15 +185,16 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_08_setup_config
-    gpg_key    = 'user@example2.com'
+    gpg_key    = 'test2@example.com'
     gpg_exe    = '/usr/bin/gpg2'
-    wallet_dir = '/tmp/mpw'
+    wallet_dir = '/tmp'
     length     = 24
     wallet     = 'work'
 
     output = %x(
       mpw config \
       --gpg-exe #{gpg_exe} \
+      --key #{gpg_key} \
       --enable-pinmode \
       --disable-alpha \
       --disable-special-chars \
@@ -207,7 +208,7 @@ class TestConfig < Test::Unit::TestCase
 
     output = %x(mpw config)
     puts output
-    assert_match(/gpg_key.+\| #{@gpg_key}/, output)
+    assert_match(/gpg_key.+\| #{gpg_key}/, output)
     assert_match(/gpg_exe.+\| #{gpg_exe}/, output)
     assert_match(/pinmode.+\| true/, output)
     assert_match(/default_wallet.+\| #{wallet}/, output)
@@ -219,7 +220,8 @@ class TestConfig < Test::Unit::TestCase
 
     output = %x(
       mpw config \
-      --key #{gpg_key} \
+      --gpg-exe '' \
+      --key #{@gpg_key} \
       --alpha \
       --special-chars \
       --numeric \
@@ -230,7 +232,7 @@ class TestConfig < Test::Unit::TestCase
 
     output = %x(mpw config)
     puts output
-    assert_match(/gpg_key.+\| #{gpg_key}/, output)
+    assert_match(/gpg_key.+\| #{@gpg_key}/, output)
     assert_match(/pinmode.+\| false/, output)
     %w[numeric alpha special].each do |k|
       assert_match(/password_#{k}.+\| true/, output)
diff --git a/test/test_import.rb b/test/test_import.rb
new file mode 100644
index 0000000..c27b58f
--- /dev/null
+++ b/test/test_import.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/ruby
+
+require 'i18n'
+require 'test/unit'
+
+class TestImport < Test::Unit::TestCase
+  def setup
+    if defined?(I18n.enforce_available_locales)
+      I18n.enforce_available_locales = true
+    end
+
+    I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+    I18n.load_path = ["#{File.expand_path('../../i18n', __FILE__)}/en.yml"]
+    I18n.locale    = :en
+
+    @password = 'password'
+  end
+
+  def test_00_import
+    Dir['./test/files/import-*.txt'].each do |file|
+      format = File.basename(file, '.txt').partition('-').last
+
+      puts format
+
+      output = %x(
+          mpw import \
+          --file #{file} \
+          --format #{format} \
+          --wallet #{format}
+        )
+      assert_match(I18n.t('form.import.valid'), output)
+
+      output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
+      puts output
+      assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
+
+      output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
+      puts output
+      assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
+
+      output = %x(echo #{@password} | mpw list --wallet #{format})
+      puts output
+      assert_match(/server\.com.*My little server/, output)
+    end
+  end
+end

From cf5b7e014213d592ceb9bf975b137a4c57f03810 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 17 Sep 2017 16:25:05 +0200
Subject: [PATCH 42/58] fix travis tests

---
 .travis.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index e289bcf..7f524d4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,8 @@ rvm:
   - 2.2.7
   - 2.1.10
 install:
+  - sudo cp -a /dev/urandom /dev/random
+  - sudo apt-get purge -y gnupg-agent gnupg2
   - bundle install
   - echo 9999 > VERSION
   - gem build mpw.gemspec

From 8b5c7ae581e391b3c81b079816adb993217d43ec Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 5 Sep 2017 22:16:37 +0200
Subject: [PATCH 43/58] feat: major change the url storage

---
 bin/mpw-add                    | 18 +++-------
 bin/mpw-update                 | 18 +++-------
 i18n/en.yml                    |  4 +--
 i18n/fr.yml                    |  4 +--
 lib/mpw/cli.rb                 | 22 +++++-------
 lib/mpw/import/gorilla.rb      | 12 ++-----
 lib/mpw/item.rb                | 63 ++++++++++++++++------------------
 lib/mpw/mpw.rb                 |  8 ++---
 templates/add_form.erb         | 10 ++----
 templates/update_form.erb      |  8 ++---
 test/files/fixtures-import.yml |  9 ++---
 test/files/fixtures.yml        |  3 ++
 test/test_cli.rb               | 14 ++++----
 test/test_item.rb              | 31 +++++++----------
 test/test_mpw.rb               |  8 ++---
 15 files changed, 87 insertions(+), 145 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index 2c2e996..1d0581a 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -48,22 +48,10 @@ OptionParser.new do |opts|
     exit 0
   end
 
-  opts.on('-H', '--host HOST', I18n.t('option.host')) do |host|
-    values[:host] = host
-  end
-
   opts.on('-o', '--otp-code CODE', I18n.t('option.otp_code')) do |otp|
     values[:otp_key] = otp
   end
 
-  opts.on('-O', '--protocol PROTOCOL', I18n.t('option.protocol')) do |protocol|
-    values[:protocol] = protocol
-  end
-
-  opts.on('-P', '--port NUMBER', I18n.t('option.port')) do |port|
-    values[:port] = port
-  end
-
   opts.on('-r', '--random', I18n.t('option.random_password')) do
     options[:password] = true
   end
@@ -72,7 +60,11 @@ OptionParser.new do |opts|
     options[:text_editor] = true
   end
 
-  opts.on('-u', '--user USER', I18n.t('option.user')) do |user|
+  opts.on('-u', '--url URL', I18n.t('option.url')) do |url|
+    values[:url] = url
+  end
+
+  opts.on('-U', '--user USER', I18n.t('option.user')) do |user|
     values[:user] = user
   end
 
diff --git a/bin/mpw-update b/bin/mpw-update
index 143e187..d15b8e7 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -53,26 +53,14 @@ OptionParser.new do |opts|
     exit 0
   end
 
-  opts.on('-H', '--host HOST', I18n.t('option.host')) do |host|
-    values[:host] = host
-  end
-
   opts.on('-o', '--otp-code CODE', I18n.t('option.otp_code')) do |otp|
     values[:otp_key] = otp
   end
 
-  opts.on('-O', '--protocol PROTOCOL', I18n.t('option.protocol')) do |protocol|
-    values[:protocol] = protocol
-  end
-
   opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
     search[:pattern] = pattern
   end
 
-  opts.on('-P', '--port NUMBER', I18n.t('option.port')) do |port|
-    values[:port] = port
-  end
-
   opts.on('-r', '--random', I18n.t('option.random_password')) do
     options[:password] = true
   end
@@ -81,7 +69,11 @@ OptionParser.new do |opts|
     options[:text_editor] = true
   end
 
-  opts.on('-u', '--user USER', I18n.t('option.user')) do |user|
+  opts.on('-u', '--url URL', I18n.t('option.url')) do |url|
+    values[:url] = url
+  end
+
+  opts.on('-U', '--user USER', I18n.t('option.user')) do |user|
     values[:user] = user
   end
 
diff --git a/i18n/en.yml b/i18n/en.yml
index 2375084..9df7bda 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -63,7 +63,6 @@ en:
     gpg_key: "Specify a GPG key (ex: user@example.com)"
     group: "Search the items with specified group"
     help: "Show this help message"
-    host: "Specify a host or ip"
     init: "Initialize mpw"
     import: "Import item from an yaml file"
     key: "Define the key name"
@@ -77,8 +76,6 @@ en:
     path: "Move the wallet in new specify directory"
     pattern: "Given search pattern"
     pinmode: "Enable pinentry mode (available with gpg >= 2.1)"
-    port: "Set a port of connexion"
-    protocol: "Set a protocol of connexion"
     random_password: "Generate a random password"
     setup: "Create a new configuration file" 
     setup_wallet: "Create a new configuration file for a wallet"
@@ -87,6 +84,7 @@ en:
     show_all: "Listing all items"
     text_editor: "Use text editor to edit the item"
     usage: "Use"
+    url: "Set an url (ex: https://example.com/path)"
     user: "Set an user"
     wallet: "Specify a wallet to use"
     wallet_dir: "Set the wallets folder"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 9ab2e58..04583ad 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -63,7 +63,6 @@ fr:
     gpg_key: "Spécifie une clé GPG (ex: user@example.com)"
     group: "Recherche les éléments appartenant au groupe spécifié"
     help: "Affiche ce message d'aide"
-    host: "Spécifie le nom du serveur ou l'ip"
     import: "Importe des éléments depuis un fichier yaml"
     init: "Initialise mpw"
     key: "Spécifie le nom d'une clé"
@@ -77,8 +76,6 @@ fr:
     path: "Déplace le portefeuille dans un nouveau dossier"
     pattern: "Motif de donnée à chercher"
     pinmode: "Active le mode pinentry (valable avec gpg >= 2.1)"
-    port: "Spécifie un port de connexion"
-    protocol: "Spécifie un protocol de connexion"
     random_password: "Génére un mot de passe aléatoire"
     setup: "Création d'un nouveau fichier de configuration"
     setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
@@ -87,6 +84,7 @@ fr:
     show_all: "Liste tous les éléments"
     text_editor: "Active l'édition avec un éditeur de texte"
     usage: "Utilisation"
+    url: "Spécifie l'url (ex: http://example.com/path)"
     user: "Spécifie un utilisateur"
     wallet: "Spécifie le portefeuille à utiliser"
     wallet_dir: "Spécifie le répertoire des portefeuilles"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 17701b6..d54b314 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -563,16 +563,14 @@ module MPW
       items.each do |item|
         data.merge!(
           item.id => {
-            'host'      => item.host,
-            'user'      => item.user,
-            'group'     => item.group,
-            'password'  => @mpw.get_password(item.id),
-            'protocol'  => item.protocol,
-            'port'      => item.port,
-            'otp_key'   => @mpw.get_otp_key(item.id),
             'comment'   => item.comment,
-            'last_edit' => item.last_edit,
             'created'   => item.created,
+            'group'     => item.group,
+            'last_edit' => item.last_edit,
+            'otp_key'   => @mpw.get_otp_key(item.id),
+            'password'  => @mpw.get_password(item.id),
+            'url'       => item.url,
+            'user'      => item.user
           }
         )
       end
@@ -599,12 +597,10 @@ module MPW
 
       Import.send(format, file).each_value do |row|
         item = Item.new(
+          comment:  row['comment'],
           group:    row['group'],
-          host:     row['host'],
-          protocol: row['protocol'],
-          user:     row['user'],
-          port:     row['port'],
-          comment:  row['comment']
+          url:      row['url'],
+          user:     row['user']
         )
 
         next if item.empty?
diff --git a/lib/mpw/import/gorilla.rb b/lib/mpw/import/gorilla.rb
index 0ab5dc3..fd5d9f1 100644
--- a/lib/mpw/import/gorilla.rb
+++ b/lib/mpw/import/gorilla.rb
@@ -37,18 +37,12 @@ module MPW
           end
 
         data[id] = {
+          'comment'  => comment,
           'group'    => row['group'],
-          'host'     => row['url'],
-          'user'     => row['user'],
           'password' => row['password'],
-          'comment'  => comment
+          'url'      => row['url'],
+          'user'     => row['user']
         }
-
-        if row['url'] =~ %r{^((?<protocol>[a-z]+)://)?(?<host>[a-zA-Z0-9_.-]+)(:(?<port>[0-9]{1,5}))?$}
-          data[id]['protocol'] = Regexp.last_match(:protocol)
-          data[id]['port']     = Regexp.last_match(:port)
-          data[id]['host']     = Regexp.last_match(:host)
-        end
       end
 
       data
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 4e17912..ec8ab0e 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -17,25 +17,27 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 require 'i18n'
+require 'uri'
 
 module MPW
   class Item
-    attr_accessor :id
+    attr_accessor :created
+    attr_accessor :comment
     attr_accessor :group
     attr_accessor :host
-    attr_accessor :protocol
-    attr_accessor :user
-    attr_accessor :port
+    attr_accessor :id
     attr_accessor :otp
-    attr_accessor :comment
+    attr_accessor :port
+    attr_accessor :protocol
     attr_accessor :last_edit
-    attr_accessor :created
+    attr_accessor :url
+    attr_accessor :user
 
     # @param options [Hash] the option :host is required
     def initialize(**options)
       @host = ''
 
-      if !options.key?(:id) || options[:id].to_s.empty? || !options.key?(:created) || options[:created].to_s.empty?
+      if !options[:id] || !options[:created]
         @id = generate_id
         @created = Time.now.to_i
       else
@@ -51,43 +53,38 @@ module MPW
     # Update the item
     # @param options [Hash]
     def update(**options)
-      unless options[:host] || options[:comment]
+      unless options[:url] || options[:comment]
         raise I18n.t('error.update.host_and_comment_empty')
       end
 
-      @group     = options[:group]      if options.key?(:group)
-      @host      = options[:host]       if options.key?(:host) && !options[:host].nil?
-      @protocol  = options[:protocol]   if options.key?(:protocol)
-      @user      = options[:user]       if options.key?(:user)
-      @port      = options[:port].to_i  if options.key?(:port) && !options[:port].to_s.empty?
-      @otp       = options[:otp]        if options.key?(:otp)
-      @comment   = options[:comment]    if options.key?(:comment)
-      @last_edit = Time.now.to_i        unless options.key?(:no_update_last_edit)
+      if options[:url]
+        uri       = URI(options[:url])
+        @host     = uri.host   || options[:url]
+        @port     = uri.port   || nil
+        @protocol = uri.scheme || nil
+        @url      = options[:url]
+      end
+
+      @comment   = options[:comment] if options.key?(:comment)
+      @group     = options[:group]   if options.key?(:group)
+      @last_edit = Time.now.to_i     unless options.key?(:no_update_last_edit)
+      @otp       = options[:otp]     if options.key?(:otp)
+      @user      = options[:user]    if options.key?(:user)
     end
 
     # Delete all data
     def delete
       @id        = nil
-      @group     = nil
-      @host      = nil
-      @protocol  = nil
-      @user      = nil
-      @port      = nil
-      @otp       = nil
       @comment   = nil
       @created   = nil
+      @group     = nil
+      @host      = nil
       @last_edit = nil
-    end
-
-    # Return data on url format
-    # @return [String] an url
-    def url
-      url = ''
-      url += "#{@protocol}://" if @protocol
-      url += @host if @host
-      url += ":#{@port}" if @port
-
-      url
+      @otp       = nil
+      @port      = nil
+      @protocol  = nil
+      @url       = nil
+      @user      = nil
     end
 
     def empty?
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 81a0231..a5fc0ae 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -84,10 +84,8 @@ module MPW
             Item.new(
               id:        d['id'],
               group:     d['group'],
-              host:      d['host'],
-              protocol:  d['protocol'],
               user:      d['user'],
-              port:      d['port'],
+              url:       d['url'],
               otp:       @otp_keys.key?(d['id']),
               comment:   d['comment'],
               last_edit: d['last_edit'],
@@ -114,10 +112,8 @@ module MPW
           item.id => {
             'id'        => item.id,
             'group'     => item.group,
-            'host'      => item.host,
-            'protocol'  => item.protocol,
             'user'      => item.user,
-            'port'      => item.port,
+            'url'       => item.url,
             'comment'   => item.comment,
             'last_edit' => item.last_edit,
             'created'   => item.created,
diff --git a/templates/add_form.erb b/templates/add_form.erb
index 89b750e..e2aea20 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -1,16 +1,12 @@
 ---
-# <%= I18n.t('form.add_item.host') %>
-host: <%= options[:host] %>
+# <%= I18n.t('form.add_item.url') %>
+url: <%= options[:url] %>
 # <%= I18n.t('form.add_item.login') %>
 user: <%= options[:user] %>
 # <%= I18n.t('form.add_item.group') %>
-group: <%= options[:group] %>
-# <%= I18n.t('form.add_item.protocol') %>
-protocol: <%= options[:protocol] %><% unless password %>
+group: <%= options[:group] %><% unless password %>
 # <%= I18n.t('form.add_item.password') %>
 password:<% end %>
-# <%= I18n.t('form.add_item.port') %>
-port: <%= options[:port] %>
 # <%= I18n.t('form.add_item.comment') %>
 comment: <%= options[:comment] %>
 # <%= I18n.t('form.add_item.otp_key') %>
diff --git a/templates/update_form.erb b/templates/update_form.erb
index 5037568..eafeb1b 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -1,16 +1,12 @@
 ---
-# <%= I18n.t('form.update_item.host') %>
-host: <%  if options[:host] %><%= options[:host] %><% else %><%= item.host %><% end %>
+# <%= I18n.t('form.update_item.url') %>
+host: <%  if options[:url] %><%= options[:url] %><% else %><%= item.url %><% end %>
 # <%= I18n.t('form.update_item.login') %>
 user: <%  if options[:user] %><%= options[:user] %><% else %><%= item.user %><% end %><% unless password %>
 # <%= I18n.t('form.update_item.password') %>
 password: <% end %>
 # <%= I18n.t('form.update_item.group') %>
 group: <%  if options[:group] %><%= options[:group] %><% else %><%= item.group %><% end %>
-# <%= I18n.t('form.update_item.protocol') %>
-protocol: <%  if options[:protocol] %><%= options[:protocol] %><% else %><%= item.protocol %><% end %>
-# <%= I18n.t('form.update_item.port') %>
-port: <%  if options[:port] %><%= options[:port] %><% else %><%= item.port %><% end %>
 # <%= I18n.t('form.update_item.otp_key') %>
 otp_key: <%  if options[:otp_key] %><%= options[:otp_key] %><% end %>
 # <%= I18n.t('form.update_item.comment') %>
diff --git a/test/files/fixtures-import.yml b/test/files/fixtures-import.yml
index f79ab0a..8cb1ff4 100644
--- a/test/files/fixtures-import.yml
+++ b/test/files/fixtures-import.yml
@@ -1,19 +1,16 @@
 ---
 1:
-  host: fric.com
+  url: https://fric.com
   user: 230403
   group: Bank
   password: 5XdiTQOubRDw9B0aJoMlcEyL
-  protocol: https
-  port:
   otp_key: 330223432
   comment: I love my bank
 2:
-  host: assurance.com
+  url: https://assurance.com:443
   user: user_2132
+  host: assurance.com
   group: Assurance
   password: DMyK6B3v4bWO52VzU7aTHIem
-  protocol: https
-  port: 443 
   otp_key: 
   comment: 
diff --git a/test/files/fixtures.yml b/test/files/fixtures.yml
index 765314e..d8927cb 100644
--- a/test/files/fixtures.yml
+++ b/test/files/fixtures.yml
@@ -1,4 +1,5 @@
 add:
+  url: 'https://example.com:8080'
   group: 'Bank'
   host: 'example.com'
   protocol: 'https'
@@ -9,6 +10,7 @@ add:
 
 import:
   id: 'TEST-ID-XXXXX'
+  url: 'https://gogole.com:8081/toto'
   group: 'Cloud'
   host: 'gogole.com'
   protocol: 'https'
@@ -19,6 +21,7 @@ import:
   created: 1386752948
 
 update:
+  url: 'ssh://example2.com:2222'
   group: 'Assurance'
   host: 'example2.com'
   protocol: 'ssh'
diff --git a/test/test_cli.rb b/test/test_cli.rb
index f4cf356..07c4273 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -29,9 +29,7 @@ class TestConfig < Test::Unit::TestCase
 
     output = %x(
       echo #{@password} | mpw add \
-      --host #{data['host']} \
-      --port #{data['port']} \
-      --protocol #{data['protocol']} \
+      --url #{data['url']} \
       --user #{data['user']} \
       --comment '#{data['comment']}' \
       --group #{data['group']} \
@@ -73,9 +71,7 @@ class TestConfig < Test::Unit::TestCase
     output = %x(
       echo #{@password} | mpw update \
       -p #{@fixtures['add']['host']} \
-      --host #{data['host']} \
-      --port #{data['port']} \
-      --protocol #{data['protocol']} \
+      --url #{data['url']} \
       --user #{data['user']} \
       --comment '#{data['comment']}' \
       --new-group #{data['group']}
@@ -117,20 +113,22 @@ class TestConfig < Test::Unit::TestCase
       error = true
 
       YAML.load_file(file_export).each_value do |export|
-        next if import['host'] != export['host']
+        next if import['url'] != export['url']
 
         %w[user group password protocol port otp_key comment].each do |key|
           assert_equal(import[key].to_s, export[key].to_s)
         end
 
         error = false
+        break
       end
+
       assert(!error)
     end
   end
 
   def test_06_copy
-    data = YAML.load_file('./test/files/fixtures-import.yml')[1]
+    data = YAML.load_file('./test/files/fixtures-import.yml')[2]
 
     output = %x(
       echo "#{@password}\np\nq" | mpw copy \
diff --git a/test/test_item.rb b/test/test_item.rb
index c83cbc3..80e39ff 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -23,10 +23,8 @@ class TestItem < Test::Unit::TestCase
   def test_01_add
     data = {
       group:    @fixtures['add']['group'],
-      host:     @fixtures['add']['host'],
-      protocol: @fixtures['add']['protocol'],
       user:     @fixtures['add']['user'],
-      port:     @fixtures['add']['port'],
+      url:      @fixtures['add']['url'],
       comment:  @fixtures['add']['comment']
     }
 
@@ -35,6 +33,7 @@ class TestItem < Test::Unit::TestCase
     assert(!item.nil?)
     assert(!item.empty?)
 
+    assert_equal(@fixtures['add']['url'],       item.url)
     assert_equal(@fixtures['add']['group'],     item.group)
     assert_equal(@fixtures['add']['host'],      item.host)
     assert_equal(@fixtures['add']['protocol'],  item.protocol)
@@ -47,10 +46,8 @@ class TestItem < Test::Unit::TestCase
     data = {
       id:       @fixtures['import']['id'],
       group:    @fixtures['import']['group'],
-      host:     @fixtures['import']['host'],
-      protocol: @fixtures['import']['protocol'],
       user:     @fixtures['import']['user'],
-      port:     @fixtures['import']['port'],
+      url:      @fixtures['import']['url'],
       comment:  @fixtures['import']['comment'],
       created:  @fixtures['import']['created']
     }
@@ -61,6 +58,7 @@ class TestItem < Test::Unit::TestCase
     assert(!item.empty?)
 
     assert_equal(@fixtures['import']['id'],        item.id)
+    assert_equal(@fixtures['import']['url'],       item.url)
     assert_equal(@fixtures['import']['group'],     item.group)
     assert_equal(@fixtures['import']['host'],      item.host)
     assert_equal(@fixtures['import']['protocol'],  item.protocol)
@@ -73,10 +71,8 @@ class TestItem < Test::Unit::TestCase
   def test_03_update
     data = {
       group:    @fixtures['add']['group'],
-      host:     @fixtures['add']['host'],
-      protocol: @fixtures['add']['protocol'],
       user:     @fixtures['add']['user'],
-      port:     @fixtures['add']['port'],
+      url:      @fixtures['add']['url'],
       comment:  @fixtures['add']['comment']
     }
 
@@ -90,10 +86,8 @@ class TestItem < Test::Unit::TestCase
 
     data = {
       group:    @fixtures['update']['group'],
-      host:     @fixtures['update']['host'],
-      protocol: @fixtures['update']['protocol'],
       user:     @fixtures['update']['user'],
-      port:     @fixtures['update']['port'],
+      url:      @fixtures['update']['url'],
       comment:  @fixtures['update']['comment']
     }
 
@@ -102,6 +96,7 @@ class TestItem < Test::Unit::TestCase
 
     assert(!item.empty?)
 
+    assert_equal(@fixtures['update']['url'],       item.url)
     assert_equal(@fixtures['update']['group'],     item.group)
     assert_equal(@fixtures['update']['host'],      item.host)
     assert_equal(@fixtures['update']['protocol'],  item.protocol)
@@ -116,10 +111,8 @@ class TestItem < Test::Unit::TestCase
   def test_05_update_one_element
     data = {
       group:    @fixtures['add']['group'],
-      host:     @fixtures['add']['host'],
-      protocol: @fixtures['add']['protocol'],
       user:     @fixtures['add']['user'],
-      port:     @fixtures['add']['port'],
+      url:      @fixtures['add']['url'],
       comment:  @fixtures['add']['comment']
     }
 
@@ -131,8 +124,9 @@ class TestItem < Test::Unit::TestCase
     last_edit = item.last_edit
 
     sleep(1)
-    assert(item.update(comment: @fixtures['update']['comment']))
+    item.update(comment: @fixtures['update']['comment'])
 
+    assert_equal(@fixtures['add']['url'],        item.url)
     assert_equal(@fixtures['add']['group'],      item.group)
     assert_equal(@fixtures['add']['host'],       item.host)
     assert_equal(@fixtures['add']['protocol'],   item.protocol)
@@ -146,10 +140,8 @@ class TestItem < Test::Unit::TestCase
   def test_05_delete
     data = {
       group:    @fixtures['add']['group'],
-      host:     @fixtures['add']['host'],
-      protocol: @fixtures['add']['protocol'],
       user:     @fixtures['add']['user'],
-      port:     @fixtures['add']['port'],
+      url:      @fixtures['add']['url'],
       comment:  @fixtures['add']['comment']
     }
 
@@ -163,6 +155,7 @@ class TestItem < Test::Unit::TestCase
     assert(item.empty?)
 
     assert_equal(nil, item.id)
+    assert_equal(nil, item.url)
     assert_equal(nil, item.group)
     assert_equal(nil, item.host)
     assert_equal(nil, item.protocol)
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index a783225..a1633ef 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -33,10 +33,8 @@ class TestMPW < Test::Unit::TestCase
   def test_02_add_item
     data = {
       group:    @fixtures['add']['group'],
-      host:     @fixtures['add']['host'],
-      protocol: @fixtures['add']['protocol'],
       user:     @fixtures['add']['user'],
-      port:     @fixtures['add']['port'],
+      url:      @fixtures['add']['url'],
       comment:  @fixtures['add']['comment']
     }
 
@@ -93,10 +91,8 @@ class TestMPW < Test::Unit::TestCase
     @fixtures.each_value do |v|
       data = {
         group:    v['group'],
-        host:     v['host'],
-        protocol: v['protocol'],
         user:     v['user'],
-        port:     v['port'],
+        url:      v['url'],
         comment:  v['comment']
       }
 

From dd02776baa297adca47dc344984723ab842ee1dd Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 17 Sep 2017 22:47:59 +0200
Subject: [PATCH 44/58] feat: add import old version of mpw

---
 lib/mpw/import/mpw_old.rb     | 46 +++++++++++++++++++++++++++++++++++
 test/files/import-mpw_old.txt | 35 ++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)
 create mode 100644 lib/mpw/import/mpw_old.rb
 create mode 100644 test/files/import-mpw_old.txt

diff --git a/lib/mpw/import/mpw_old.rb b/lib/mpw/import/mpw_old.rb
new file mode 100644
index 0000000..f6957fe
--- /dev/null
+++ b/lib/mpw/import/mpw_old.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'yaml'
+
+module MPW
+  module Import
+    # Import an export mpw file
+    # @param file [String] the file path to import
+    def self.mpw_old(file)
+      data = {}
+      YAML.load_file(file).each do |id, item|
+        url = ''
+        url += "#{item['protocol']}://" if item['protocol']
+        url += item['host']
+        url += ":#{item['port']}" if item['port']
+
+        data[id] = {
+          'comment'  => item['comment'],
+          'group'    => item['group'],
+          'otp'      => item['otp'],
+          'password' => item['password'],
+          'url'      => url,
+          'user'     => item['user']
+        }
+      end
+
+      data
+    end
+  end
+end
diff --git a/test/files/import-mpw_old.txt b/test/files/import-mpw_old.txt
new file mode 100644
index 0000000..fd162aa
--- /dev/null
+++ b/test/files/import-mpw_old.txt
@@ -0,0 +1,35 @@
+---
+1:
+  host: fric.com
+  user: 12345
+  group: Bank
+  password: secret
+  protocol: http
+  port:
+  otp_key: 
+  comment: Fric money money 
+  last_edit: 1487623641
+  created: 1485729356
+2:
+  host: server.com
+  user: sercret2
+  group:
+  password:
+  protocol:
+  port: 4222
+  otp_key: 
+  comment: My little server
+  last_edit: 1487623641
+  created: 1485729356
+3:
+  host: fric.com
+  user: username
+  group: Cloud
+  password:
+  protocol: ssh
+  port: 4333
+  otp_key: 
+  comment: bastion
+  last_edit: 1487623641
+  created: 1485729356
+

From ce02022ac758b8a4bf20a7a3e28827715df82c5d Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 24 Sep 2017 12:38:14 +0200
Subject: [PATCH 45/58] feat: update rubocop to 0.50.0

---
 .rubocop.yml              | 109 +++++---------------------------------
 Gemfile                   |   2 +-
 bin/mpw-add               |   1 -
 bin/mpw-config            |   1 -
 bin/mpw-copy              |   1 -
 bin/mpw-delete            |   1 -
 bin/mpw-export            |   1 -
 bin/mpw-genpwd            |   1 -
 bin/mpw-import            |   1 -
 bin/mpw-list              |   1 -
 bin/mpw-update            |   1 -
 bin/mpw-wallet            |   1 -
 lib/mpw/cli.rb            |  29 +++++-----
 lib/mpw/config.rb         |  11 ++--
 lib/mpw/import/gorilla.rb |   1 -
 lib/mpw/import/mpw.rb     |   1 -
 lib/mpw/import/mpw_old.rb |   1 -
 lib/mpw/item.rb           |   1 -
 lib/mpw/mpw.rb            |  13 +++--
 mpw.gemspec               |   2 -
 test/init.rb              |   2 -
 test/test_cli.rb          |   2 -
 test/test_config.rb       |   2 -
 test/test_import.rb       |   2 -
 test/test_item.rb         |   2 -
 test/test_mpw.rb          |   2 -
 test/test_translate.rb    |   2 -
 27 files changed, 39 insertions(+), 155 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index ce1de33..56d1540 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -6,116 +6,33 @@ AllCops:
     - Vagrantfile
   TargetRubyVersion: 2.3
 
-Style/AccessorMethodName:
+Naming/AccessorMethodName:
   Enabled: false
-Style/NumericLiteralPrefix:
-  Enabled: false
-Style/TrailingCommaInArguments:
-  Enabled: false
-Style/TrailingCommaInLiteral:
-  Enabled: false
-Style/FrozenStringLiteralComment:
-  Enabled: false
-Metrics/ParameterLists:
-  Max: 5
-  CountKeywordArgs: false
-Style/MutableConstant:
+
+Lint/RescueWithoutErrorClass:
   Enabled: false
+
 Metrics/LineLength:
   Max: 120
-Metrics/AbcSize:
+Metrics/CyclomaticComplexity:
+  Enabled: false
+Metrics/PerceivedComplexity:
   Enabled: false
 Metrics/MethodLength:
   Enabled: false
 Metrics/BlockLength:
   Enabled: false
-Metrics/CyclomaticComplexity:
-  Enabled: false
-Metrics/PerceivedComplexity:
-  Enabled: false
 Metrics/ClassLength:
   Enabled: false
-Style/SpaceInsideHashLiteralBraces:
-  Enabled: false
-Style/AsciiComments:
-  Enabled: true
-Style/Documentation:
-  Enabled: false
-Style/SignalException:
-  Enabled: false
-Style/OptionHash:
-  Enabled: true
-Style/SymbolArray:
-  Enabled: true
-Performance/Casecmp:
-  Enabled: false
-Style/DoubleNegation:
-  Enabled: false
-Style/Alias:
-  EnforcedStyle: prefer_alias_method
-Style/MultilineMethodCallIndentation:
-  EnforcedStyle: indented
-Style/RaiseArgs:
-  EnforcedStyle: exploded
-Style/SpaceInLambdaLiteral:
-  Enabled: false
-Lint/UnneededSplatExpansion:
+Metrics/AbcSize:
   Enabled: false
 
-
-# Generated configuration
-Style/HashSyntax:
-  Enabled: true
-  EnforcedStyle: ruby19
-  UseHashRocketsWithSymbolValues: false
-Style/MethodDefParentheses:
-  Enabled: true
-  EnforcedStyle: require_parentheses
-Style/MultilineAssignmentLayout:
-  Enabled: true
-  EnforcedStyle: new_line
-Style/IndentationConsistency:
-  Enabled: true
-  EnforcedStyle: normal
-Style/AlignParameters:
-  Enabled: true
-  EnforcedStyle: with_fixed_indentation
-Style/BlockDelimiters:
-  Enabled: true
-  EnforcedStyle: line_count_based
-Style/AndOr:
-  Enabled: true
-Style/DotPosition:
-  Enabled: true
-  EnforcedStyle: leading
-Style/EmptyLinesAroundClassBody:
-  Enabled: true
-  EnforcedStyle: no_empty_lines
-Style/EmptyLinesAroundModuleBody:
-  Enabled: true
-  EnforcedStyle: no_empty_lines
-Style/NumericPredicate:
-  Enabled: true
-  EnforcedStyle: comparison
-Style/EvenOdd:
+Style/NumericLiteralPrefix:
+  Enabled: false
+Style/FrozenStringLiteralComment:
   Enabled: false
-Style/CollectionMethods:
-  Enabled: true
-  PreferredMethods:
-    collect: map
-    collect!: map!
-    inject: reduce
-    detect: find
-    find_all: select
-Style/EmptyLinesAroundAccessModifier:
-  Enabled: true
 Style/CommandLiteral:
   Enabled: true
   EnforcedStyle: percent_x
-Style/StringLiterals:
-  Enabled: true
-  EnforcedStyle: single_quotes
-Style/SpaceInsideBlockBraces:
-  EnforcedStyle: space
-Style/VariableNumber:
-  EnforcedStyle: snake_case
+Style/Documentation:
+  Enabled: false
diff --git a/Gemfile b/Gemfile
index de2741f..405c4df 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,7 +8,7 @@ gem 'locale',    '~> 2.1', '>= 2.1.2'
 gem 'rotp',      '~> 3.1', '>= 3.1.0'
 
 group :development do
-  gem 'rubocop', '0.48.1'
+  gem 'rubocop', '0.50.0'
   gem 'test-unit'
   gem 'yard'
 end
diff --git a/bin/mpw-add b/bin/mpw-add
index 1d0581a..cdc23b0 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-config b/bin/mpw-config
index 0af88bd..e36d2f8 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-copy b/bin/mpw-copy
index 95c01d6..01b75aa 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-delete b/bin/mpw-delete
index ea352c4..78dc340 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-export b/bin/mpw-export
index 83d35ae..3ab0945 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd
index 051c5c4..de5628f 100644
--- a/bin/mpw-genpwd
+++ b/bin/mpw-genpwd
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-import b/bin/mpw-import
index 76e163e..8213eb3 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-list b/bin/mpw-list
index 434ea11..71e7b5a 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-update b/bin/mpw-update
index d15b8e7..53ce5f8 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index caee35d..6f84afd 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index d54b314..7309024 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
@@ -39,7 +38,7 @@ module MPW
       @config.setup(options)
 
       puts I18n.t('form.set_config.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #15: #{e}".red
       exit 2
     end
@@ -50,7 +49,7 @@ module MPW
       @config.set_wallet_path(path, @wallet)
 
       puts I18n.t('form.set_wallet_path.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #19: #{e}".red
       exit 2
     end
@@ -67,7 +66,7 @@ module MPW
       load_config
 
       puts I18n.t('form.setup_config.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #8: #{e}".red
       exit 2
     end
@@ -89,7 +88,7 @@ module MPW
       @config.setup_gpg_key(@password, gpg_key)
 
       puts I18n.t('form.setup_gpg_key.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #8: #{e}".red
       exit 2
     end
@@ -119,7 +118,7 @@ module MPW
     # Load config
     def load_config
       @config.load_config
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #10: #{e}".red
       exit 2
     end
@@ -140,7 +139,7 @@ module MPW
           @mpw.read_data
         end
       end
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #11: #{e}".red
       exit 2
     end
@@ -416,7 +415,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.add_key.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #13: #{e}".red
     end
 
@@ -427,7 +426,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.delete_key.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #15: #{e}".red
     end
 
@@ -478,7 +477,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.add_item.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #13: #{e}".red
     end
 
@@ -506,7 +505,7 @@ module MPW
 
         puts I18n.t('form.update_item.valid').to_s.green
       end
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #14: #{e}".red
     end
 
@@ -530,7 +529,7 @@ module MPW
 
         puts I18n.t('form.delete_item.valid').to_s.green
       end
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #16: #{e}".red
     end
 
@@ -548,7 +547,7 @@ module MPW
         item = get_item(items)
         clipboard(item, clipboard)
       end
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #14: #{e}".red
     end
 
@@ -578,7 +577,7 @@ module MPW
       File.open(file, 'w') { |f| f << data.to_yaml }
 
       puts I18n.t('form.export.valid', file: file).to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #17: #{e}".red
     end
 
@@ -613,7 +612,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.import.valid').to_s.green
-    rescue => e
+    rescue e
       puts "#{I18n.t('display.error')} #18: #{e}".red
     end
   end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 95390a4..47440ab 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
@@ -39,9 +38,9 @@ module MPW
     def initialize(config_file = nil)
       @config_file = config_file
       @config_dir  =
-        if /darwin/ =~ RUBY_PLATFORM
+        if RUBY_PLATFORM =~ /darwin/
           "#{Dir.home}/Library/Preferences/mpw"
-        elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+        elsif RUBY_PLATFORM =~ /cygwin|mswin|mingw|bccwin|wince|emx/
           "#{Dir.home}/AppData/Local/mpw"
         else
           "#{Dir.home}/.config/mpw"
@@ -94,7 +93,7 @@ module MPW
       File.open(@config_file, 'w') do |file|
         file << config.to_yaml
       end
-    rescue => e
+    rescue e
       raise "#{I18n.t('error.config.write')}\n#{e}"
     end
 
@@ -122,7 +121,7 @@ module MPW
 
       ctx = GPGME::Ctx.new
       ctx.genkey(param, nil, nil)
-    rescue => e
+    rescue e
       raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
     end
 
@@ -141,7 +140,7 @@ module MPW
       raise if @gpg_key.empty? || @wallet_dir.empty?
 
       I18n.locale = @lang.to_sym
-    rescue => e
+    rescue e
       raise "#{I18n.t('error.config.load')}\n#{e}"
     end
 
diff --git a/lib/mpw/import/gorilla.rb b/lib/mpw/import/gorilla.rb
index fd5d9f1..a5a8fbe 100644
--- a/lib/mpw/import/gorilla.rb
+++ b/lib/mpw/import/gorilla.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/lib/mpw/import/mpw.rb b/lib/mpw/import/mpw.rb
index 8d6b1c4..aca88aa 100644
--- a/lib/mpw/import/mpw.rb
+++ b/lib/mpw/import/mpw.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/lib/mpw/import/mpw_old.rb b/lib/mpw/import/mpw_old.rb
index f6957fe..7f8c33e 100644
--- a/lib/mpw/import/mpw_old.rb
+++ b/lib/mpw/import/mpw_old.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index ec8ab0e..ec4c50e 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index a5fc0ae..257d3bf 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
@@ -89,14 +88,14 @@ module MPW
               otp:       @otp_keys.key?(d['id']),
               comment:   d['comment'],
               last_edit: d['last_edit'],
-              created:   d['created'],
+              created:   d['created']
             )
           )
         end
       end
 
       add_key(@key) unless @keys.key?(@key)
-    rescue => e
+    rescue e
       raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
     end
 
@@ -116,7 +115,7 @@ module MPW
             'url'       => item.url,
             'comment'   => item.comment,
             'last_edit' => item.last_edit,
-            'created'   => item.created,
+            'created'   => item.created
           }
         )
       end
@@ -147,7 +146,7 @@ module MPW
       end
 
       File.rename(tmp_file, @wallet_file)
-    rescue => e
+    rescue e
       File.unlink(tmp_file) if File.exist?(tmp_file)
 
       raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
@@ -324,7 +323,7 @@ module MPW
       crypto
         .decrypt(data, password)
         .read.force_encoding('utf-8')
-    rescue => e
+    rescue e
       raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
     end
 
@@ -342,7 +341,7 @@ module MPW
       end
 
       crypto.encrypt(data, recipients: recipients).read
-    rescue => e
+    rescue e
       raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
     end
   end
diff --git a/mpw.gemspec b/mpw.gemspec
index 4afbe3b..dfb4825 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -1,5 +1,3 @@
-# coding: utf-8
-
 lib = File.expand_path('../lib', __FILE__)
 $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
 
diff --git a/test/init.rb b/test/init.rb
index 6da9ecb..08ebbb8 100644
--- a/test/init.rb
+++ b/test/init.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'fileutils'
 require 'gpgme'
 
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 07c4273..9cfe215 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'i18n'
 require 'test/unit'
 
diff --git a/test/test_config.rb b/test/test_config.rb
index 8ad7369..2b88b54 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'mpw/config'
 require 'test/unit'
 require 'locale'
diff --git a/test/test_import.rb b/test/test_import.rb
index c27b58f..101fd1a 100644
--- a/test/test_import.rb
+++ b/test/test_import.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'i18n'
 require 'test/unit'
 
diff --git a/test/test_item.rb b/test/test_item.rb
index 80e39ff..97503c0 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'mpw/item'
 require 'test/unit'
 require 'yaml'
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index a1633ef..8fc544e 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'mpw/mpw'
 require 'mpw/item'
 require 'test/unit'
diff --git a/test/test_translate.rb b/test/test_translate.rb
index d4bb002..d7cbceb 100644
--- a/test/test_translate.rb
+++ b/test/test_translate.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
 require 'yaml'
 require 'test/unit'
 

From ef0a9081449d0b0fd65859bdca84c53179189485 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 18 Sep 2017 07:58:21 +0200
Subject: [PATCH 46/58] feat: add docker for local tests

---
 .docker-test | 32 ++++++++++++++++++++++++++++++++
 Dockerfile   | 20 ++++++++++++++++++++
 README.md    | 17 +++++++++++++++++
 3 files changed, 69 insertions(+)
 create mode 100644 .docker-test
 create mode 100644 Dockerfile

diff --git a/.docker-test b/.docker-test
new file mode 100644
index 0000000..d36fad8
--- /dev/null
+++ b/.docker-test
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+ruby_version=${1:-2.4.2}
+
+if ! rvm use ruby-${ruby_version} &>/dev/null ; then
+  echo "The ruby version '${ruby_version}' doesn't exist!"
+  echo "Available versions are:"
+  rvm list rubies strings | cut -d '-' -f2
+  exit 2
+fi
+
+echo '# ---------------------------------'
+echo "# Use ruby version: ${ruby_version}"
+echo '# ---------------------------------'
+
+cp -r /mpw ~/mpw
+cd ~/mpw
+gem install bundler --no-ri --no-rdoc
+bundle install
+gem build mpw.gemspec
+gem install mpw-$(cat VERSION).gem
+cp -a /dev/urandom /dev/random
+
+rubocop
+ruby ./test/init.rb
+ruby ./test/test_config.rb
+ruby ./test/test_item.rb
+ruby ./test/test_mpw.rb
+ruby ./test/test_translate.rb
+ruby ./test/init.rb
+ruby ./test/test_cli.rb
+ruby ./test/test_import.rb
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..d8995d0
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+FROM debian:stretch
+MAINTAINER Adrien Waksberg "mpw@yae.im"
+
+RUN apt update
+RUN apt dist-upgrade -y
+
+RUN apt install -y procps gnupg1 curl git
+RUN ln -snvf /usr/bin/gpg1 /usr/bin/gpg
+RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
+RUN curl -sSL https://get.rvm.io | bash -s stable
+RUN echo 'source "/usr/local/rvm/scripts/rvm"' >> /etc/bash.bashrc
+
+run apt install -y patch bzip2 gawk g++ gcc make libc6-dev patch zlib1g-dev libyaml-dev libsqlite3-dev \
+                   sqlite3 autoconf libgmp-dev libgdbm-dev libncurses5-dev automake libtool bison \
+                   pkg-config libffi-dev libgmp-dev libreadline-dev libssl-dev
+
+RUN /bin/bash -l -c "rvm install 2.4.2"
+RUN /bin/bash -l -c "rvm install 2.3.5"
+RUN /bin/bash -l -c "rvm install 2.2.8"
+RUN /bin/bash -l -c "rvm install 2.1.10"
diff --git a/README.md b/README.md
index 9c2c5d6..162be49 100644
--- a/README.md
+++ b/README.md
@@ -161,3 +161,20 @@ Configuration
   password_length  | 16
 
 ```
+
+## Development
+
+Don't run the tests on your local machine, you risk to lost your datas.
+
+### Test on local machine with docker
+
+  * install [docker](https://docs.docker.com/engine/installation/)
+  * build the container
+```
+docker build -t mpw/debian:stretch -f Dockerfile .
+```
+  * run the tests
+
+```
+docker run -v $(pwd):/mpw:ro -it mpw/debian:stretch /bin/bash -l /mpw/.docker-test
+```

From 2abad3695f2883488f6b1f9796542ac262763c4a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 1 Oct 2017 20:01:35 +0200
Subject: [PATCH 47/58] fix no show wallet in other folder

---
 lib/mpw/cli.rb   | 3 ++-
 test/test_cli.rb | 4 ++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 7309024..711e91c 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -373,7 +373,8 @@ module MPW
 
     # List all wallets
     def list_wallet
-      wallets = []
+      wallets = @config.wallet_paths.keys
+
       Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f|
         wallet = File.basename(f, '.mpw')
         wallet += ' *'.green if wallet == @config.default_wallet
diff --git a/test/test_cli.rb b/test/test_cli.rb
index 9cfe215..766aebd 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -171,6 +171,10 @@ class TestConfig < Test::Unit::TestCase
     assert_match(%r{path_wallet_default.+\| #{Dir.pwd}/default.mpw}, output)
     assert(File.exist?("#{Dir.pwd}/default.mpw"))
 
+    output = %x(mpw wallet)
+    puts output
+    assert_match('default', output)
+
     output = %x(mpw wallet --default-path)
     puts output
     assert_match(I18n.t('form.set_wallet_path.valid'), output)

From afd6585acc4fac66133a8264460a4751f1bcdd37 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 1 Oct 2017 20:31:43 +0200
Subject: [PATCH 48/58] fix: remove outputs in tests

---
 test/test_cli.rb | 70 +++++++++++++++++++++---------------------------
 1 file changed, 30 insertions(+), 40 deletions(-)

diff --git a/test/test_cli.rb b/test/test_cli.rb
index 766aebd..8bae4cc 100644
--- a/test/test_cli.rb
+++ b/test/test_cli.rb
@@ -17,7 +17,11 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_00_init_config
-    output = %x(echo "#{@password}\n#{@password}" | mpw config --init #{@gpg_key})
+    output = %x(
+      echo "#{@password}\n#{@password}" | mpw config \
+      --init #{@gpg_key} \
+      2>/dev/null
+    )
     assert_match(I18n.t('form.setup_config.valid'), output)
     assert_match(I18n.t('form.setup_gpg_key.valid'), output)
   end
@@ -31,13 +35,12 @@ class TestConfig < Test::Unit::TestCase
       --user #{data['user']} \
       --comment '#{data['comment']}' \
       --group #{data['group']} \
-      --random
+      --random \
+      2>/dev/null
     )
-    puts output
     assert_match(I18n.t('form.add_item.valid'), output)
 
-    output = %x(echo #{@password} | mpw list)
-    puts output
+    output = %x(echo #{@password} | mpw list 2>/dev/null)
     assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
     assert_match(data['user'], output)
     assert_match(data['comment'], output)
@@ -47,19 +50,19 @@ class TestConfig < Test::Unit::TestCase
   def test_02_search
     data = @fixtures['add']
 
-    output = %x(echo #{@password} | mpw list --group #{data['group']})
+    output = %x(echo #{@password} | mpw list --group #{data['group']} 2>/dev/null)
     assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
 
-    output = %x(echo #{@password} | mpw list --pattern #{data['host']})
+    output = %x(echo #{@password} | mpw list --pattern #{data['host']} 2>/dev/null)
     assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
 
-    output = %x(echo #{@password} | mpw list --pattern #{data['comment']})
+    output = %x(echo #{@password} | mpw list --pattern #{data['comment']} 2>/dev/null)
     assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
 
-    output = %x(echo #{@password} | mpw list --group R1Pmfbp626TFpjlr)
+    output = %x(echo #{@password} | mpw list --group R1Pmfbp626TFpjlr 2>/dev/null)
     assert_match(I18n.t('display.nothing'), output)
 
-    output = %x(echo #{@password} | mpw list --pattern h1IfnKqamaGM9oEX)
+    output = %x(echo #{@password} | mpw list --pattern h1IfnKqamaGM9oEX 2>/dev/null)
     assert_match(I18n.t('display.nothing'), output)
   end
 
@@ -72,13 +75,12 @@ class TestConfig < Test::Unit::TestCase
       --url #{data['url']} \
       --user #{data['user']} \
       --comment '#{data['comment']}' \
-      --new-group #{data['group']}
+      --new-group #{data['group']} \
+      2>/dev/null
     )
-    puts output
     assert_match(I18n.t('form.update_item.valid'), output)
 
-    output = %x(echo #{@password} | mpw list)
-    puts output
+    output = %x(echo #{@password} | mpw list 2>/dev/null)
     assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
     assert_match(data['user'], output)
     assert_match(data['comment'], output)
@@ -86,12 +88,14 @@ class TestConfig < Test::Unit::TestCase
   end
 
   def test_04_delete_item
-    output = %x(echo "#{@password}\ny" | mpw delete -p #{@fixtures['update']['host']})
-    puts output
+    output = %x(
+      echo "#{@password}\ny" | mpw delete \
+      -p #{@fixtures['update']['host']} \
+      2>/dev/null
+    )
     assert_match(I18n.t('form.delete_item.valid'), output)
 
-    output = %x(echo #{@password} | mpw list)
-    puts output
+    output = %x(echo #{@password} | mpw list 2>/dev/null)
     assert_match(I18n.t('display.nothing'), output)
   end
 
@@ -99,10 +103,10 @@ class TestConfig < Test::Unit::TestCase
     file_import = './test/files/fixtures-import.yml'
     file_export = '/tmp/test-mpw.yml'
 
-    output = %x(echo #{@password} | mpw import --file #{file_import})
+    output = %x(echo #{@password} | mpw import --file #{file_import} 2>/dev/null)
     assert_match(I18n.t('form.import.valid', file: file_import), output)
 
-    output = %x(echo #{@password} | mpw export --file #{file_export})
+    output = %x(echo #{@password} | mpw export --file #{file_export} 2>/dev/null)
     assert_match(I18n.t('form.export.valid', file: file_export), output)
     assert(File.exist?(file_export))
     assert_equal(YAML.load_file(file_export).length, 2)
@@ -131,56 +135,46 @@ class TestConfig < Test::Unit::TestCase
     output = %x(
       echo "#{@password}\np\nq" | mpw copy \
       --disable-clipboard \
-      -p #{data['host']}
+      -p #{data['host']} \
+      2>/dev/null
     )
-    puts output
     assert_match(data['password'], output)
   end
 
   def test_07_setup_wallet
     gpg_key = 'test2@example.com'
 
-    output = %x(echo #{@password} | mpw wallet --add-gpg-key #{gpg_key})
-    puts output
+    output = %x(echo #{@password} | mpw wallet --add-gpg-key #{gpg_key} 2>/dev/null)
     assert_match(I18n.t('form.add_key.valid'), output)
 
-    output = %x(echo #{@password} | mpw wallet --list-keys)
-    puts output
+    output = %x(echo #{@password} | mpw wallet --list-keys 2>/dev/null)
     assert_match("| #{@gpg_key}", output)
     assert_match("| #{gpg_key}", output)
 
-    output = %x(echo #{@password} | mpw wallet --delete-gpg-key #{gpg_key})
-    puts output
+    output = %x(echo #{@password} | mpw wallet --delete-gpg-key #{gpg_key} 2>/dev/null)
     assert_match(I18n.t('form.delete_key.valid'), output)
 
-    output = %x(echo #{@password} | mpw wallet --list-keys)
-    puts output
+    output = %x(echo #{@password} | mpw wallet --list-keys 2>/dev/null)
     assert_match("| #{@gpg_key}", output)
     assert_no_match(/\| #{gpg_key}/, output)
 
     output = %x(mpw wallet)
-    puts output
     assert_match('| default', output)
 
     output = %x(mpw wallet --path '.')
-    puts output
     assert_match(I18n.t('form.set_wallet_path.valid'), output)
 
     output = %x(mpw config)
-    puts output
     assert_match(%r{path_wallet_default.+\| #{Dir.pwd}/default.mpw}, output)
     assert(File.exist?("#{Dir.pwd}/default.mpw"))
 
     output = %x(mpw wallet)
-    puts output
     assert_match('default', output)
 
     output = %x(mpw wallet --default-path)
-    puts output
     assert_match(I18n.t('form.set_wallet_path.valid'), output)
 
     output = %x(mpw config)
-    puts output
     assert_no_match(/path_wallet_default/, output)
   end
 
@@ -203,11 +197,9 @@ class TestConfig < Test::Unit::TestCase
       --wallet-dir #{wallet_dir} \
       --default-wallet #{wallet}
     )
-    puts output
     assert_match(I18n.t('form.set_config.valid'), output)
 
     output = %x(mpw config)
-    puts output
     assert_match(/gpg_key.+\| #{gpg_key}/, output)
     assert_match(/gpg_exe.+\| #{gpg_exe}/, output)
     assert_match(/pinmode.+\| true/, output)
@@ -227,11 +219,9 @@ class TestConfig < Test::Unit::TestCase
       --numeric \
       --disable-pinmode
     )
-    puts output
     assert_match(I18n.t('form.set_config.valid'), output)
 
     output = %x(mpw config)
-    puts output
     assert_match(/gpg_key.+\| #{@gpg_key}/, output)
     assert_match(/pinmode.+\| false/, output)
     %w[numeric alpha special].each do |k|

From 348db7cfd0ce14b8374c0573fc581289030cbb01 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 1 Oct 2017 20:32:56 +0200
Subject: [PATCH 49/58] feat: update travis.yml

---
 .travis.yml | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 7f524d4..14d696a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,17 +1,16 @@
 language: ruby
 dist: precise
 rvm:
-  - 2.4.1
-  - 2.3.4
-  - 2.2.7
+  - 2.4.2
+  - 2.3.5
+  - 2.2.8
   - 2.1.10
 install:
   - sudo cp -a /dev/urandom /dev/random
   - sudo apt-get purge -y gnupg-agent gnupg2
   - bundle install
-  - echo 9999 > VERSION
   - gem build mpw.gemspec
-  - gem install mpw-9999.gem
+  - gem install mpw-$(cat VERSION).gem
 script:
   - rubocop
   - ruby ./test/init.rb

From 8fddbd01be1e5a10b24b1ba8ab49536b6f9145de Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 3 Oct 2017 13:07:53 +0200
Subject: [PATCH 50/58] fix: minor change in dockerfile

---
 Dockerfile | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index d8995d0..6654780 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,10 +10,6 @@ RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A170311380
 RUN curl -sSL https://get.rvm.io | bash -s stable
 RUN echo 'source "/usr/local/rvm/scripts/rvm"' >> /etc/bash.bashrc
 
-run apt install -y patch bzip2 gawk g++ gcc make libc6-dev patch zlib1g-dev libyaml-dev libsqlite3-dev \
-                   sqlite3 autoconf libgmp-dev libgdbm-dev libncurses5-dev automake libtool bison \
-                   pkg-config libffi-dev libgmp-dev libreadline-dev libssl-dev
-
 RUN /bin/bash -l -c "rvm install 2.4.2"
 RUN /bin/bash -l -c "rvm install 2.3.5"
 RUN /bin/bash -l -c "rvm install 2.2.8"

From b0905f18f1f4d14d5b9f08f4379809cacc3cf27d Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 3 Oct 2017 13:09:27 +0200
Subject: [PATCH 51/58] fix: remove output for test_import

---
 test/test_import.rb | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/test/test_import.rb b/test/test_import.rb
index 101fd1a..ae53eef 100644
--- a/test/test_import.rb
+++ b/test/test_import.rb
@@ -29,15 +29,12 @@ class TestImport < Test::Unit::TestCase
       assert_match(I18n.t('form.import.valid'), output)
 
       output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
-      puts output
       assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
 
       output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
-      puts output
       assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
 
       output = %x(echo #{@password} | mpw list --wallet #{format})
-      puts output
       assert_match(/server\.com.*My little server/, output)
     end
   end

From 2de66a4eaf9c4b6b7c8c4f7d8766039609f89aba Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 19 Nov 2017 13:49:42 +0100
Subject: [PATCH 52/58] fix syntax for all rescue

---
 lib/mpw/cli.rb    | 28 ++++++++++++++--------------
 lib/mpw/config.rb |  6 +++---
 lib/mpw/mpw.rb    |  8 ++++----
 3 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 711e91c..db703a8 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -38,7 +38,7 @@ module MPW
       @config.setup(options)
 
       puts I18n.t('form.set_config.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #15: #{e}".red
       exit 2
     end
@@ -49,7 +49,7 @@ module MPW
       @config.set_wallet_path(path, @wallet)
 
       puts I18n.t('form.set_wallet_path.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #19: #{e}".red
       exit 2
     end
@@ -66,7 +66,7 @@ module MPW
       load_config
 
       puts I18n.t('form.setup_config.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #8: #{e}".red
       exit 2
     end
@@ -88,7 +88,7 @@ module MPW
       @config.setup_gpg_key(@password, gpg_key)
 
       puts I18n.t('form.setup_gpg_key.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #8: #{e}".red
       exit 2
     end
@@ -118,7 +118,7 @@ module MPW
     # Load config
     def load_config
       @config.load_config
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #10: #{e}".red
       exit 2
     end
@@ -139,7 +139,7 @@ module MPW
           @mpw.read_data
         end
       end
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #11: #{e}".red
       exit 2
     end
@@ -416,7 +416,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.add_key.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #13: #{e}".red
     end
 
@@ -427,7 +427,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.delete_key.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #15: #{e}".red
     end
 
@@ -478,7 +478,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.add_item.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #13: #{e}".red
     end
 
@@ -506,7 +506,7 @@ module MPW
 
         puts I18n.t('form.update_item.valid').to_s.green
       end
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #14: #{e}".red
     end
 
@@ -530,7 +530,7 @@ module MPW
 
         puts I18n.t('form.delete_item.valid').to_s.green
       end
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #16: #{e}".red
     end
 
@@ -548,7 +548,7 @@ module MPW
         item = get_item(items)
         clipboard(item, clipboard)
       end
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #14: #{e}".red
     end
 
@@ -578,7 +578,7 @@ module MPW
       File.open(file, 'w') { |f| f << data.to_yaml }
 
       puts I18n.t('form.export.valid', file: file).to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #17: #{e}".red
     end
 
@@ -613,7 +613,7 @@ module MPW
       @mpw.write_data
 
       puts I18n.t('form.import.valid').to_s.green
-    rescue e
+    rescue => e
       puts "#{I18n.t('display.error')} #18: #{e}".red
     end
   end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 47440ab..a4d2142 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -93,7 +93,7 @@ module MPW
       File.open(@config_file, 'w') do |file|
         file << config.to_yaml
       end
-    rescue e
+    rescue => e
       raise "#{I18n.t('error.config.write')}\n#{e}"
     end
 
@@ -121,7 +121,7 @@ module MPW
 
       ctx = GPGME::Ctx.new
       ctx.genkey(param, nil, nil)
-    rescue e
+    rescue => e
       raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
     end
 
@@ -140,7 +140,7 @@ module MPW
       raise if @gpg_key.empty? || @wallet_dir.empty?
 
       I18n.locale = @lang.to_sym
-    rescue e
+    rescue => e
       raise "#{I18n.t('error.config.load')}\n#{e}"
     end
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 257d3bf..a67f09d 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -95,7 +95,7 @@ module MPW
       end
 
       add_key(@key) unless @keys.key?(@key)
-    rescue e
+    rescue => e
       raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
     end
 
@@ -146,7 +146,7 @@ module MPW
       end
 
       File.rename(tmp_file, @wallet_file)
-    rescue e
+    rescue => e
       File.unlink(tmp_file) if File.exist?(tmp_file)
 
       raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
@@ -323,7 +323,7 @@ module MPW
       crypto
         .decrypt(data, password)
         .read.force_encoding('utf-8')
-    rescue e
+    rescue => e
       raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
     end
 
@@ -341,7 +341,7 @@ module MPW
       end
 
       crypto.encrypt(data, recipients: recipients).read
-    rescue e
+    rescue => e
       raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
     end
   end

From ed09f55ce282bd4b170b8ebc9abf615be633098a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 19 Nov 2017 13:54:17 +0100
Subject: [PATCH 53/58] chore: update README

---
 README.md | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 162be49..4b0c0b0 100644
--- a/README.md
+++ b/README.md
@@ -169,12 +169,8 @@ Don't run the tests on your local machine, you risk to lost your datas.
 ### Test on local machine with docker
 
   * install [docker](https://docs.docker.com/engine/installation/)
-  * build the container
-```
-docker build -t mpw/debian:stretch -f Dockerfile .
-```
   * run the tests
 
 ```
-docker run -v $(pwd):/mpw:ro -it mpw/debian:stretch /bin/bash -l /mpw/.docker-test
+docker run -v $(pwd):/mpw:ro -it nishiki/ruby:stretch /bin/bash -l /mpw/.docker-test
 ```

From 30ca80f6db7b67bfd74c1a8b57e6eebaa859c1b6 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 19 Nov 2017 18:27:48 +0100
Subject: [PATCH 54/58] fix shebang

---
 bin/mpw | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bin/mpw b/bin/mpw
index 811322e..384374a 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #

From 6a2d5da9fbf022ba90fc3ed594eae87616339001 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 19 Nov 2017 20:11:39 +0100
Subject: [PATCH 55/58] feat: update gem dependencies

---
 Gemfile     | 6 +++---
 mpw.gemspec | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Gemfile b/Gemfile
index 405c4df..a76b2c1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,11 +1,11 @@
 source 'https://rubygems.org'
 gem 'clipboard', '~> 1.1', '>= 1.1.1'
 gem 'colorize',  '~> 0.8', '>= 0.8.1'
-gem 'gpgme',     '~> 2.0', '>= 2.0.12'
+gem 'gpgme',     '~> 2.0', '>= 2.0.14'
 gem 'highline',  '~> 1.7', '>= 1.7.8'
-gem 'i18n',      '~> 0.7', '>= 0.7.0'
+gem 'i18n',      '~> 0.9', '>= 0.9.1'
 gem 'locale',    '~> 2.1', '>= 2.1.2'
-gem 'rotp',      '~> 3.1', '>= 3.1.0'
+gem 'rotp',      '~> 3.3', '>= 3.3.0'
 
 group :development do
   gem 'rubocop', '0.50.0'
diff --git a/mpw.gemspec b/mpw.gemspec
index dfb4825..abcb375 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -18,11 +18,11 @@ Gem::Specification.new do |spec|
 
   spec.required_ruby_version = '>= 2.1'
 
-  spec.add_dependency 'i18n',      '~> 0.7', '>= 0.7.0'
-  spec.add_dependency 'gpgme',     '~> 2.0', '>= 2.0.12'
+  spec.add_dependency 'i18n',      '~> 0.9', '>= 0.9.1'
+  spec.add_dependency 'gpgme',     '~> 2.0', '>= 2.0.14'
   spec.add_dependency 'highline',  '~> 1.7', '>= 1.7.8'
   spec.add_dependency 'locale',    '~> 2.1', '>= 2.1.2'
   spec.add_dependency 'colorize',  '~> 0.8', '>= 0.8.1'
   spec.add_dependency 'clipboard', '~> 1.1', '>= 1.1.1'
-  spec.add_dependency 'rotp',      '~> 3.1', '>= 3.1.0'
+  spec.add_dependency 'rotp',      '~> 3.3', '>= 3.3.0'
 end

From a11777cadead7dab24775538be9f171c50d6dbb6 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 19 Nov 2017 21:25:42 +0100
Subject: [PATCH 56/58] feat: add keepass

---
 lib/mpw/import/keepass.rb     | 50 +++++++++++++++++++++++
 test/files/import-keepass.txt |  3 ++
 test/test_import.rb           | 74 ++++++++++++++++++++++++++---------
 3 files changed, 109 insertions(+), 18 deletions(-)
 create mode 100644 lib/mpw/import/keepass.rb
 create mode 100644 test/files/import-keepass.txt

diff --git a/lib/mpw/import/keepass.rb b/lib/mpw/import/keepass.rb
new file mode 100644
index 0000000..b9d7852
--- /dev/null
+++ b/lib/mpw/import/keepass.rb
@@ -0,0 +1,50 @@
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'csv'
+
+module MPW
+  module Import
+    # Import an keepass2 export csv file
+    # @param file [String] the file path to import
+    def self.keepass(file)
+      data = {}
+
+      CSV.foreach(file, headers: true) do |row|
+        id = "#{row['Group']} #{row['Title']}"
+        comment =
+          if row['Title'] && row['Notes']
+            "#{row['Title']} #{row['Notes']}"
+          elsif row['Title']
+            row['Title']
+          elsif row['Notes']
+            row['Notes']
+          end
+
+        data[id] = {
+          'comment'  => comment,
+          'group'    => row['Group'],
+          'password' => row['Password'],
+          'url'      => row['URL'],
+          'user'     => row['Username']
+        }
+      end
+
+      data
+    end
+  end
+end
diff --git a/test/files/import-keepass.txt b/test/files/import-keepass.txt
new file mode 100644
index 0000000..61ce7ad
--- /dev/null
+++ b/test/files/import-keepass.txt
@@ -0,0 +1,3 @@
+"Group","Title","Username","Password","URL","Notes"
+"Racine","Bank","123456","ywcExJW8qmBVTSyi","http://bank.com/login","My little bank"
+"Racine/Cloud","GAFAM","wesh","superpassword","localhost.local",""
diff --git a/test/test_import.rb b/test/test_import.rb
index ae53eef..f1a8727 100644
--- a/test/test_import.rb
+++ b/test/test_import.rb
@@ -14,28 +14,66 @@ class TestImport < Test::Unit::TestCase
     @password = 'password'
   end
 
-  def test_00_import
-    Dir['./test/files/import-*.txt'].each do |file|
-      format = File.basename(file, '.txt').partition('-').last
+  def test_00_import_mpw_old
+    file = './test/files/import-mpw_old.txt'
+    format = 'mpw_old'
 
-      puts format
+    output = %x(
+        mpw import \
+        --file #{file} \
+        --format #{format} \
+        --wallet #{format}
+      )
+    assert_match(I18n.t('form.import.valid'), output)
 
-      output = %x(
-          mpw import \
-          --file #{file} \
-          --format #{format} \
-          --wallet #{format}
-        )
-      assert_match(I18n.t('form.import.valid'), output)
+    output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
+    assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
 
-      output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
-      assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
+    output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
+    assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
 
-      output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
-      assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
+    output = %x(echo #{@password} | mpw list --wallet #{format})
+    assert_match(/server\.com.*My little server/, output)
+  end
 
-      output = %x(echo #{@password} | mpw list --wallet #{format})
-      assert_match(/server\.com.*My little server/, output)
-    end
+  def test_01_import_gorilla
+    file = './test/files/import-gorilla.txt'
+    format = 'gorilla'
+
+    output = %x(
+        mpw import \
+        --file #{file} \
+        --format #{format} \
+        --wallet #{format}
+      )
+    assert_match(I18n.t('form.import.valid'), output)
+
+    output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
+    assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
+
+    output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
+    assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
+
+    output = %x(echo #{@password} | mpw list --wallet #{format})
+    assert_match(/server\.com.*My little server/, output)
+  end
+
+  def test_02_import_keepass
+    file = './test/files/import-keepass.txt'
+    format = 'keepass'
+
+    output = %x(
+        mpw import \
+        --file #{file} \
+        --format #{format} \
+        --wallet #{format}
+      )
+    assert_match(I18n.t('form.import.valid'), output)
+
+    output = %x(echo #{@password} | mpw list --group 'Racine/Cloud' --wallet #{format})
+    assert_match(/localhost\.local.*wesh.*GAFAM/, output)
+
+    output = %x(echo #{@password} | mpw list --wallet #{format})
+    assert_match(%r{http://.*bank\.com.*123456.*Bank My little bank}, output)
   end
 end

From fe7a03ba52e5b7a2ef0f3786cbdc474a53a83c4a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 19 Nov 2017 23:03:16 +0100
Subject: [PATCH 57/58] feat: change license GPL2.0 to Apache2.0

---
 LICENSE                   | 476 ++++++++++++++------------------------
 README.md                 |  23 +-
 bin/mpw                   |  29 +--
 bin/mpw-add               |  29 +--
 bin/mpw-config            |  29 +--
 bin/mpw-copy              |  29 +--
 bin/mpw-delete            |  29 +--
 bin/mpw-export            |  28 +--
 bin/mpw-genpwd            |  28 +--
 bin/mpw-import            |  28 +--
 bin/mpw-list              |  28 +--
 bin/mpw-update            |  28 +--
 bin/mpw-wallet            |  28 +--
 lib/mpw/cli.rb            |  29 +--
 lib/mpw/config.rb         |  29 +--
 lib/mpw/import/gorilla.rb |  29 +--
 lib/mpw/import/keepass.rb |  29 +--
 lib/mpw/import/mpw.rb     |  29 +--
 lib/mpw/import/mpw_old.rb |  29 +--
 lib/mpw/item.rb           |  29 +--
 lib/mpw/mpw.rb            |  29 +--
 21 files changed, 482 insertions(+), 562 deletions(-)

diff --git a/LICENSE b/LICENSE
index d159169..8dada3e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,339 +1,201 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 2, June 1991
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 
-                            Preamble
+   1. Definitions.
 
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
 
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
 
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
 
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
 
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
 
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
 
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
 
-  The precise terms and conditions for copying, distribution and
-modification follow.
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
 
-                    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
 
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
 
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
 
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
 
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
 
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
 
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
 
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
 
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
 
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
 
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
 
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
 
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
 
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
 
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
 
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
+   END OF TERMS AND CONDITIONS
 
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
+   APPENDIX: How to apply the Apache License to your work.
 
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
 
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
+   Copyright {yyyy} {name of copyright owner}
 
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
 
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
+       http://www.apache.org/licenses/LICENSE-2.0
 
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                            NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index 4b0c0b0..be9e7cb 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # MPW: Manage your passwords!
 [![Version](https://img.shields.io/badge/latest_version-4.2.2-green.svg)](https://github.com/nishiki/manage-password/releases)
 [![Build Status](https://travis-ci.org/nishiki/manage-password.svg?branch=master)](https://travis-ci.org/nishiki/manage-password)
-[![License](https://img.shields.io/badge/license-GPL--2.0-blue.svg)](https://github.com/nishiki/manage-password/blob/master/LICENSE)
+[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/nishiki/manage-password/blob/master/LICENSE)
 
 mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg.org/) encrypted files.
 
@@ -33,7 +33,6 @@ Add your first item:
 ```
 mpw add --host assurance.com --port 443 --user user_2132 --protocol https --random
 mpw add --host fric.com --user 230403 --otp-code 23434113 --protocol https --comment 'I love my bank' --random
-
 ```
 
 And list your items:
@@ -174,3 +173,23 @@ Don't run the tests on your local machine, you risk to lost your datas.
 ```
 docker run -v $(pwd):/mpw:ro -it nishiki/ruby:stretch /bin/bash -l /mpw/.docker-test
 ```
+
+## License
+
+```
+* Author:: Adrien Waksberg <mpw@yae.im>
+
+Copyright (c) 2013-2017 Adrien Waksberg
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
diff --git a/bin/mpw b/bin/mpw
index 384374a..11bc726 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -1,20 +1,23 @@
 #!/usr/bin/env ruby
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 $LOAD_PATH << File.expand_path('../../lib', __FILE__)
 
diff --git a/bin/mpw-add b/bin/mpw-add
index cdc23b0..e08caae 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-config b/bin/mpw-config
index e36d2f8..2ac0f77 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-copy b/bin/mpw-copy
index 01b75aa..fc3b6e0 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-delete b/bin/mpw-delete
index 78dc340..48eb792 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-export b/bin/mpw-export
index 3ab0945..92eb7bd 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -1,19 +1,19 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#   http://www.apache.org/licenses/LICENSE-2.0
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd
index de5628f..f6ca795 100644
--- a/bin/mpw-genpwd
+++ b/bin/mpw-genpwd
@@ -1,19 +1,19 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#   http://www.apache.org/licenses/LICENSE-2.0
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/mpw'
diff --git a/bin/mpw-import b/bin/mpw-import
index 8213eb3..d0deae9 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -1,19 +1,19 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#   http://www.apache.org/licenses/LICENSE-2.0
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-list b/bin/mpw-list
index 71e7b5a..fb7899c 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -1,19 +1,19 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#   http://www.apache.org/licenses/LICENSE-2.0
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-update b/bin/mpw-update
index 53ce5f8..26a55c9 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -1,19 +1,19 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#   http://www.apache.org/licenses/LICENSE-2.0
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 6f84afd..6518283 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -1,19 +1,19 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#   http://www.apache.org/licenses/LICENSE-2.0
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'optparse'
 require 'mpw/config'
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index db703a8..e64d27c 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'readline'
 require 'locale'
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index a4d2142..f3974b5 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'gpgme'
 require 'yaml'
diff --git a/lib/mpw/import/gorilla.rb b/lib/mpw/import/gorilla.rb
index a5a8fbe..feffce6 100644
--- a/lib/mpw/import/gorilla.rb
+++ b/lib/mpw/import/gorilla.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'csv'
 
diff --git a/lib/mpw/import/keepass.rb b/lib/mpw/import/keepass.rb
index b9d7852..0b961d4 100644
--- a/lib/mpw/import/keepass.rb
+++ b/lib/mpw/import/keepass.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'csv'
 
diff --git a/lib/mpw/import/mpw.rb b/lib/mpw/import/mpw.rb
index aca88aa..a287357 100644
--- a/lib/mpw/import/mpw.rb
+++ b/lib/mpw/import/mpw.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'yaml'
 
diff --git a/lib/mpw/import/mpw_old.rb b/lib/mpw/import/mpw_old.rb
index 7f8c33e..923bf16 100644
--- a/lib/mpw/import/mpw_old.rb
+++ b/lib/mpw/import/mpw_old.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'yaml'
 
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index ec4c50e..9da60b1 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'i18n'
 require 'uri'
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index a67f09d..191caae 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -1,19 +1,22 @@
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2017  Adrien Waksberg <mpw@yae.im>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# Copyright:: 2013, Adrien Waksberg
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
 #
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
 
 require 'rubygems/package'
 require 'gpgme'

From be526c297f09b68f312a1653898c6e6653cdd08e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 7 Dec 2017 14:15:51 +0100
Subject: [PATCH 58/58] fix: update LICENSE with good copyright

---
 LICENSE | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/LICENSE b/LICENSE
index 8dada3e..0c5ea8e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright {yyyy} {name of copyright owner}
+   Copyright 2017 Adrien Waksberg
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.