From 4aa959d59125684109ffab85761522f97f2e723d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 10 Jan 2014 17:55:05 +0100
Subject: [PATCH 001/531] MPW server, get and update a gpg file

---
 lib/Server.rb | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mpw-server    |  46 +++++++++
 2 files changed, 301 insertions(+)
 create mode 100644 lib/Server.rb
 create mode 100755 mpw-server

diff --git a/lib/Server.rb b/lib/Server.rb
new file mode 100644
index 0000000..77e4415
--- /dev/null
+++ b/lib/Server.rb
@@ -0,0 +1,255 @@
+#!/usr/bin/ruby
+
+require 'socket'
+require 'json'
+require 'highline/import'
+require 'digest'
+require 'base64'
+
+require "#{APP_ROOT}/lib/MPW.rb"
+
+class Server
+	
+	attr_accessor :error_msg
+
+	# Constructor
+	def initialize()
+		YAML::ENGINE.yamler='syck'
+		@error_msg = nil
+	end
+
+	# Start the server
+	def start()
+		server = TCPServer.open(@host, @port)
+		loop do
+			Thread.start(server.accept) do |client|
+				msg = self.getClientMessage(client)
+
+				if !msg
+					next
+				end
+				
+				if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
+					self.closeConnection(client)
+					next
+				end
+
+				case msg['action']
+				when 'get'
+					client.puts self.getFile(msg)
+				when 'update'
+					client.puts self.updateFile(msg)
+				when 'delete'
+					client.puts self.deleteFile(msg)
+				else
+					client.puts 'Unknown command'
+				end
+				
+				self.closeConnection(client)
+			end
+		end
+	end
+
+	# Get a gpg file
+	# @args: msg -> message puts by the client
+	# @rtrn: json message
+	def getFile(msg)
+		gpg_key = msg['gpg_key'].sub('@', '_')
+
+		if msg['suffix'].nil? || msg['suffix'].empty?
+			file_gpg = "#{@data_dir}/#{gpg_key}.yml"
+		else
+			file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
+		end
+
+		if File.exist?(file_gpg)
+			gpg_data  = YAML::load_file(file_gpg)
+			salt      = gpg_data['gpg']['salt']
+			hash      = gpg_data['gpg']['hash']
+			data      = gpg_data['gpg']['data']
+
+			if self.isAuthorized?(msg['password'], salt, hash)
+				send_msg = {:action  => 'get',
+				            :gpg_key => msg['gpg_key'],
+				            :msg     => 'get_done',
+				            :data    => data}
+			else
+				send_msg = {:action  => 'get',
+				            :gpg_key => msg['gpg_key'],
+				            :msg     => 'get_fail',
+				            :error   => 'not_authorized'}
+			end
+		else
+			send_msg = {:action  => 'get',
+			            :gpg_key => msg['gpg_key'],
+			            :msg     => 'get_fail',
+			            :error   => 'file_not_exist'}
+		end
+
+		return send_msg.to_json
+	end
+
+	# Update a file
+	# @args: msg -> message puts by the client
+	# @rtrn: json message
+	def updateFile(msg)
+	begin
+		gpg_key = msg['gpg_key'].sub('@', '_')
+		data    = msg['data']
+
+		if data.nil? || data.empty?
+			send_msg = {:action  => 'update',
+			            :gpg_key => msg['gpg_key'],
+			            :msg     => 'update_fail',
+			            :error   => 'no_data'}
+			
+			return send_msg.to_json
+		end
+
+		if msg['suffix'].nil? || msg['suffix'].empty?
+			file_gpg = "#{@data_dir}/#{gpg_key}.yml"
+		else
+			file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
+		end
+
+		if File.exist?(file_gpg)
+			gpg_data  = YAML::load_file(file_gpg)
+			salt      = gpg_data['gpg']['salt']
+			hash      = gpg_data['gpg']['hash']
+
+		else
+			salt = MPW.generatePassword(4)
+			hash = Digest::SHA256.hexdigest(salt + msg['password'])
+		end
+
+		if self.isAuthorized?(msg['password'], salt, hash)
+			begin
+				config = {'gpg' => {'salt' => salt,
+				                    'hash' => hash,
+				                    'data' => data}}
+
+				File.open(file_gpg, 'w') do |file|
+					file << config.to_yaml
+				end
+
+				send_msg = {:action  => 'update',
+				            :gpg_key => msg['gpg_key'],
+				            :msg    => 'update_done'}
+			rescue Exception => e
+				send_msg = {:action  => 'update',
+				            :gpg_key => msg['gpg_key'],
+				            :msg     => 'update_fail',
+				            :error   => e}
+			end
+		else
+			send_msg = {:action  => 'update',
+			            :gpg_key => msg['gpg_key'],
+			            :msg     => 'update_fail',
+			            :error   => 'not_autorized'}
+		end
+		
+		return send_msg.to_json
+	rescue Exception => e
+		puts e
+		end
+	end
+
+	# Remove a gpg file
+	# @args: msg -> message puts by the client
+	# @rtrn: json message
+	def deleteFile(msg)
+		client.puts 'delete a file'
+	end
+
+	# Check is the hash equal the password with the salt
+	# @args: password -> the user password
+	#        salt -> the salt
+	#        hash -> the hash of the password with the salt
+	# @rtrn: true is is good, else false
+	def isAuthorized?(password, salt, hash)
+		puts hash
+		puts YAML::dump(hash)
+		puts YAML::dump(Digest::SHA256.hexdigest(salt + password))
+		puts Digest::SHA256.hexdigest(salt + password)
+		if hash == Digest::SHA256.hexdigest(salt + password)
+			return true
+		else
+			return false
+		end
+	end
+
+	# Get message to client
+	# @args: client -> client connection
+	# @rtrn: array of the json string, or false if isn't json message
+	def getClientMessage(client)
+		begin
+			msg = client.gets
+			return JSON.parse(msg)
+		rescue
+			client.puts "Communication it's bad"
+			self.closeConnection(client)
+			return false
+		end
+	end
+
+	# Close the client connection
+	# @args: client -> client connection
+	def closeConnection(client)
+			client.puts "Closing the connection. Bye!"
+			client.close
+	end
+
+	# Check the config file
+	# @args: file_config -> the configuration file
+	# @rtrn: true if the config file is correct
+	def checkconfig(file_config)
+		begin
+			config    = YAML::load_file(file_config)
+			@host     = config['config']['host']
+			@port     = config['config']['port']
+			@data_dir = config['config']['data_dir']
+			@timeout  = config['config']['timeout'].to_i
+
+			if @host.empty? || @port.empty? 
+				@error_msg = "Checkconfig failed!"
+				return false
+			end
+
+		rescue Exception => e 
+			@error_msg = "Checkconfig failed!\n#{e}"
+			return false
+		end
+
+		return true
+	end
+
+	# Create a new config file
+	# @args: file_config -> the configuration file
+	# @rtrn: true if le config file is create
+	def setup(file_config)
+
+		puts I18n.t('server.form.setup.title')
+		puts '--------------------'
+		host     = ask(I18n.t('server.form.setup.host')).to_s
+		port     = ask(I18n.t('server.form.setup.port')).to_s
+		data_dir = ask(I18n.t('server.form.setup.data_dir')).to_s
+		timeout  = ask(I18n.t('server.form.setup.timeout')).to_s
+
+		config = {'config' => {'host'     => host,
+		                       'port'     => port,
+		                       'data_dir' => data_dir,
+		                       'timeout'  => timeout}}
+
+		begin
+			File.open(file_config, 'w') do |file|
+				file << config.to_yaml
+			end
+		rescue Exception => e 
+			@error_msg = "Can't write the config file!\n#{e}"
+			return false
+		end
+
+		return true
+	end
+
+end
diff --git a/mpw-server b/mpw-server
new file mode 100755
index 0000000..f764037
--- /dev/null
+++ b/mpw-server
@@ -0,0 +1,46 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+require 'rubygems'
+require 'optparse'
+require 'pathname'
+
+APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
+require "#{APP_ROOT}/lib/Server.rb"
+
+options = {}
+OptionParser.new do |opts|
+	opts.banner = "Usage: mpw-server -c CONFIG [options]"
+
+	opts.on("-c", "--config CONFIG", "Specifie the configuration file") do |config|
+		options[:config] = config
+	end
+
+	opts.on("-t", "--check-config", "Check your configuration") do |b|
+		options[:checkconfig] = b
+	end
+
+	opts.on("-s", "--setup", "Force a server") do |b|
+		options[:setup] = b
+	end
+
+	opts.on("-h", "--help", "Show this message") do |b|
+		puts opts
+		exit 0
+	end
+end.parse!
+
+server = Server.new
+
+if options[:checkconfig]
+	server.checkconfig(options[:config])
+elsif options[:setup]
+	server.setup(options[:config])
+else
+	server.checkconfig(options[:config])
+	server.start()
+end
+
+exit 0

From f684ebe79b215d032471e6fe148fd61802c54b4c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 10 Jan 2014 18:01:56 +0100
Subject: [PATCH 002/531] remove debug exception

---
 lib/Server.rb | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 77e4415..7c83e88 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -93,7 +93,6 @@ class Server
 	# @args: msg -> message puts by the client
 	# @rtrn: json message
 	def updateFile(msg)
-	begin
 		gpg_key = msg['gpg_key'].sub('@', '_')
 		data    = msg['data']
 
@@ -149,9 +148,6 @@ class Server
 		end
 		
 		return send_msg.to_json
-	rescue Exception => e
-		puts e
-		end
 	end
 
 	# Remove a gpg file

From 3662bcb2510e53bba5f279ffd9f698042d368c2d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 10 Jan 2014 18:12:24 +0100
Subject: [PATCH 003/531] add delete a gpg file

---
 lib/Server.rb | 47 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 7c83e88..b90a5f5 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -154,7 +154,48 @@ class Server
 	# @args: msg -> message puts by the client
 	# @rtrn: json message
 	def deleteFile(msg)
-		client.puts 'delete a file'
+		gpg_key = msg['gpg_key'].sub('@', '_')
+
+		if msg['suffix'].nil? || msg['suffix'].empty?
+			file_gpg = "#{@data_dir}/#{gpg_key}.yml"
+		else
+			file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
+		end
+
+		if !File.exist?(file_gpg)
+			send_msg = {:action  => 'delete',
+			            :gpg_key => msg['gpg_key'],
+			            :msg     => 'delete_fail',
+			            :error   => 'file_not_exist'}
+
+			return send_msg.to_json
+		end
+
+		gpg_data  = YAML::load_file(file_gpg)
+		salt      = gpg_data['gpg']['salt']
+		hash      = gpg_data['gpg']['hash']
+
+		if self.isAuthorized?(msg['password'], salt, hash)
+			begin
+				File.unlink(file_gpg)
+
+				send_msg = {:action  => 'delete',
+				            :gpg_key => msg['gpg_key'],
+				            :msg    => 'delete_done'}
+			rescue Exception => e
+				send_msg = {:action  => 'delete',
+				            :gpg_key => msg['gpg_key'],
+				            :msg     => 'delete_fail',
+				            :error   => e}
+			end
+		else
+			send_msg = {:action  => 'delete',
+			            :gpg_key => msg['gpg_key'],
+			            :msg     => 'delete_fail',
+			            :error   => 'not_autorized'}
+		end
+		
+		return send_msg.to_json
 	end
 
 	# Check is the hash equal the password with the salt
@@ -163,10 +204,6 @@ class Server
 	#        hash -> the hash of the password with the salt
 	# @rtrn: true is is good, else false
 	def isAuthorized?(password, salt, hash)
-		puts hash
-		puts YAML::dump(hash)
-		puts YAML::dump(Digest::SHA256.hexdigest(salt + password))
-		puts Digest::SHA256.hexdigest(salt + password)
 		if hash == Digest::SHA256.hexdigest(salt + password)
 			return true
 		else

From 8530f61b76eb0ccec291e8e780c65ccbf4b6967f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 10 Jan 2014 22:36:51 +0100
Subject: [PATCH 004/531] translate in french and in english

---
 i18n/en.yml   | 15 +++++++++++++++
 i18n/fr.yml   | 15 +++++++++++++++
 lib/Server.rb |  3 +--
 mpw-server    | 24 +++++++++++++++++++-----
 4 files changed, 50 insertions(+), 7 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 6eecd3c..27a2d11 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -111,6 +111,21 @@ en:
     display:
       connect: "Connection to:"
       nothing: "Nothing result!"
+  server:
+    option:
+      usage: "Usage"
+      config: "Specifie the configuration file"
+      checkconfig: "Check the configuration"
+      setup: "Setup a new configuration file"
+      help: "Show this message help"
+    form:
+      setup:
+        title: "Serveur configuration" 
+	host: "IP listen: "
+	port: "Port listen: "
+	data_dir: "Data directory: "
+	timeout: "Timeout to second: "
+	not_valid: "ERROR: Impossible to write the configuration file!"
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'
diff --git a/i18n/fr.yml b/i18n/fr.yml
index ba433fb..770dd21 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -111,6 +111,21 @@ fr:
     display:
       connect: "Connexion à:"
       nothing: "Aucun résultat!"
+  server:
+    option:
+      usage: "Utilisation"
+      config: "Spécifie le fichier de configuration"
+      checkconfig: "Vérifie le fichier de configuration"
+      setup: "Permet de générer un nouveau fichier de configuration"
+      help: "Affiche ce message d'aide"
+    form:
+      setup:
+        title: "Configuration du serveur" 
+	host: "IP d'écoute: "
+	port: "Port d'écoute: "
+	data_dir: "Répertoire des données: "
+	timeout: "Timeout en seconde: "
+	not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'
diff --git a/lib/Server.rb b/lib/Server.rb
index b90a5f5..77ee2c2 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -260,7 +260,6 @@ class Server
 	# @args: file_config -> the configuration file
 	# @rtrn: true if le config file is create
 	def setup(file_config)
-
 		puts I18n.t('server.form.setup.title')
 		puts '--------------------'
 		host     = ask(I18n.t('server.form.setup.host')).to_s
@@ -278,7 +277,7 @@ class Server
 				file << config.to_yaml
 			end
 		rescue Exception => e 
-			@error_msg = "Can't write the config file!\n#{e}"
+			puts "#{I18n.t('server.formsetup.not_valid')}\n#{e}"
 			return false
 		end
 
diff --git a/mpw-server b/mpw-server
index f764037..3143b59 100755
--- a/mpw-server
+++ b/mpw-server
@@ -6,32 +6,46 @@
 require 'rubygems'
 require 'optparse'
 require 'pathname'
+require 'locale'
+require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
 require "#{APP_ROOT}/lib/Server.rb"
 
+lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+
+I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+I18n.load_path = Dir["#{APP_ROOT}/i18n/*.yml"]
+I18n.default_locale = :en
+I18n.locale = lang.to_sym
+
 options = {}
 OptionParser.new do |opts|
-	opts.banner = "Usage: mpw-server -c CONFIG [options]"
+	opts.banner = "#{I18n.t('server.option.usage')}: mpw-server -c CONFIG [options]"
 
-	opts.on("-c", "--config CONFIG", "Specifie the configuration file") do |config|
+	opts.on("-c", "--config CONFIG", I18n.t('server.option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on("-t", "--check-config", "Check your configuration") do |b|
+	opts.on("-t", "--checkconfig", I18n.t('server.option.checkconfig')) do |b|
 		options[:checkconfig] = b
 	end
 
-	opts.on("-s", "--setup", "Force a server") do |b|
+	opts.on("-s", "--setup", I18n.t('server.option.setup')) do |b|
 		options[:setup] = b
 	end
 
-	opts.on("-h", "--help", "Show this message") do |b|
+	opts.on("-h", "--help", I18n.t('server.option.help')) do |b|
 		puts opts
 		exit 0
 	end
 end.parse!
 
+if options[:config].nil? || options[:config].empty?
+	puts "#{I18n.t('server.option.usage')}: mpw-server -c CONFIG [options]"
+	exit 2
+end
+
 server = Server.new
 
 if options[:checkconfig]

From c0751b3b25344585b34091d2d884c15ffd4bef85 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 10 Jan 2014 22:49:22 +0100
Subject: [PATCH 005/531] translate checkconfig

---
 i18n/en.yml   |  4 ++++
 i18n/fr.yml   |  4 ++++
 lib/Server.rb | 14 ++++++++++----
 3 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 27a2d11..9a4781a 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -118,6 +118,10 @@ en:
       checkconfig: "Check the configuration"
       setup: "Setup a new configuration file"
       help: "Show this message help"
+    checkconfig:
+      fail: "Checkconfig failed:!"
+      empty: "ERROR: an importe option is missing!"
+      datadir: "ERROR: le data directory doesn't exist!"
     form:
       setup:
         title: "Serveur configuration" 
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 770dd21..74924b7 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -118,6 +118,10 @@ fr:
       checkconfig: "Vérifie le fichier de configuration"
       setup: "Permet de générer un nouveau fichier de configuration"
       help: "Affiche ce message d'aide"
+    checkconfig:
+      fail: "Le fichier de configuration est invalide!"
+      empty: "ERREUR: Une option importante est manquante!"
+      datadir: "ERREUR: Le répertoire des données n'existe pas!"
     form:
       setup:
         title: "Configuration du serveur" 
diff --git a/lib/Server.rb b/lib/Server.rb
index 77ee2c2..699b355 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -15,7 +15,6 @@ class Server
 	# Constructor
 	def initialize()
 		YAML::ENGINE.yamler='syck'
-		@error_msg = nil
 	end
 
 	# Start the server
@@ -243,13 +242,20 @@ class Server
 			@data_dir = config['config']['data_dir']
 			@timeout  = config['config']['timeout'].to_i
 
-			if @host.empty? || @port.empty? 
-				@error_msg = "Checkconfig failed!"
+			if @host.empty? || @port.empty? || @data_dir.empty? 
+				puts I18n.t('server.checkconfig.fail')
+				puts I18n.t('server.checkconfig.empty')
+				return false
+			end
+
+			if !Dir.exist?(@data_dir)
+				puts I18n.t('server.checkconfig.fail')
+				puts I18n.t('server.checkconfig.datadir')
 				return false
 			end
 
 		rescue Exception => e 
-			@error_msg = "Checkconfig failed!\n#{e}"
+			puts "#{I18n.t('server.checkconfig.fail')}\n#{e}"
 			return false
 		end
 

From 55db474039e1c43dbbb87ba8dca6e1ea63915186 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 10 Jan 2014 22:52:55 +0100
Subject: [PATCH 006/531] remove require base64

---
 lib/Server.rb | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 699b355..737d9da 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -4,7 +4,6 @@ require 'socket'
 require 'json'
 require 'highline/import'
 require 'digest'
-require 'base64'
 
 require "#{APP_ROOT}/lib/MPW.rb"
 

From 6b9474e5384920a5e9621455779d822efd53716d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 11 Jan 2014 15:49:22 +0100
Subject: [PATCH 007/531] fix problem in translate file

---
 i18n/en.yml | 10 +++++-----
 i18n/fr.yml | 10 +++++-----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 9a4781a..c2203c4 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -125,11 +125,11 @@ en:
     form:
       setup:
         title: "Serveur configuration" 
-	host: "IP listen: "
-	port: "Port listen: "
-	data_dir: "Data directory: "
-	timeout: "Timeout to second: "
-	not_valid: "ERROR: Impossible to write the configuration file!"
+        host: "IP listen: "
+        port: "Port listen: "
+        data_dir: "Data directory: "
+        timeout: "Timeout to second: "
+        not_valid: "ERROR: Impossible to write the configuration file!"
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 74924b7..a1323be 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -125,11 +125,11 @@ fr:
     form:
       setup:
         title: "Configuration du serveur" 
-	host: "IP d'écoute: "
-	port: "Port d'écoute: "
-	data_dir: "Répertoire des données: "
-	timeout: "Timeout en seconde: "
-	not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
+        host: "IP d'écoute: "
+        port: "Port d'écoute: "
+        data_dir: "Répertoire des données: "
+        timeout: "Timeout en seconde: "
+        not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'

From e0a120d8f12855c01349398e84c2935ab2392435 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 12 Jan 2014 00:37:46 +0100
Subject: [PATCH 008/531] restructiring the code

---
 lib/Cli.rb       |  58 +++++++++++++------------
 lib/MPW.rb       |  87 +++----------------------------------
 lib/MPWConfig.rb | 109 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 146 insertions(+), 108 deletions(-)
 create mode 100644 lib/MPWConfig.rb

diff --git a/lib/Cli.rb b/lib/Cli.rb
index cc67cd1..4947c00 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -10,6 +10,7 @@ require 'readline'
 require 'i18n'
 
 require "#{APP_ROOT}/lib/MPW.rb"
+require "#{APP_ROOT}/lib/MPWConfig.rb"
 
 class Cli
 
@@ -17,14 +18,15 @@ class Cli
 	# @args: lang -> the operating system language
 	#        config_file -> a specify config file
 	def initialize(lang, config_file=nil)
-		@m = MPW.new(config_file)
+		@config = MPWConfig.new(config_file)
 		
-		if not @m.checkconfig()
+		if not @config.checkconfig()
 			self.setup(lang)
 		end
 
+		@mpw = MPW.new(@config.file_gpg, @config.key)
 		if not self.decrypt()
-			puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 			exit 2
 		end
 	end
@@ -44,28 +46,28 @@ class Cli
 		end
 		I18n.locale = language.to_sym
 
-		if @m.setup(key, language, file_gpg, timeout_pwd)
+		if @config.setup(key, language, file_gpg, timeout_pwd)
 			puts I18n.t('cli.form.setup.valid')
 		else
-			puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+			puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
 		end
 
-		if not @m.checkconfig()
-			puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+		if not @config.checkconfig()
+			puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
 		end
 	end
 
 	# Request the GPG password and decrypt the file
 	def decrypt()
 		@passwd = ask(I18n.t('cli.display.gpg_password')) {|q| q.echo = false}
-		return @m.decrypt(@passwd)
+		return @mpw.decrypt(@passwd)
 	end
 
 	# Display the query's result
 	# @args: search -> the string to search
 	#        protocol -> search from a particular protocol
 	def display(search, protocol=nil, group=nil, format=nil)
-		result = @m.search(search, group, protocol)
+		result = @mpw.search(search, group, protocol)
 
 		if not result.empty?
 			result.each do |r|
@@ -125,21 +127,21 @@ class Cli
 		port     = ask(I18n.t('cli.form.add.port')).to_s
 		comment  = ask(I18n.t('cli.form.add.comment')).to_s
 
-		if @m.add(name, group, server, protocol, login, passwd, port, comment)
-			if @m.encrypt()
+		if @mpw.add(name, group, server, protocol, login, passwd, port, comment)
+			if @mpw.encrypt()
 				puts I18n.t('cli.form.add.valid')
 			else
-				puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 			end
 		else
-			puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 		end
 	end
 
 	# Update an item
 	# @args: id -> the item's id
 	def update(id)
-		row = @m.searchById(id)
+		row = @mpw.searchById(id)
 
 		if not row.empty?
 			puts I18n.t('cli.form.update.title')
@@ -153,14 +155,14 @@ class Cli
 			port     = ask(I18n.t('cli.form.update.port'    , :port => row[MPW::PORT])).to_s
 			comment  = ask(I18n.t('cli.form.update.comment' , :comment => row[MPW::COMMENT])).to_s
 				
-			if @m.update(id, name, group, server, protocol, login, passwd, port, comment)
-				if @m.encrypt()
+			if @mpw.update(id, name, group, server, protocol, login, passwd, port, comment)
+				if @mpw.encrypt()
 					puts I18n.t('cli.form.update.valid')
 				else
-					puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+					puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 				end
 			else
-				puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 			end
 		else
 			puts I18n.t('cli.display.nothing')
@@ -172,7 +174,7 @@ class Cli
 	#        force -> no resquest a validation
 	def remove(id, force=false)
 		if not force
-			result = @m.searchById(id)		
+			result = @mpw.searchById(id)
 
 			if result.length > 0
 				self.displayFormat(result)
@@ -187,11 +189,11 @@ class Cli
 		end
 
 		if force
-			if @m.remove(id)
-				if @m.encrypt()
+			if @mpw.remove(id)
+				if @mpw.encrypt()
 					puts I18n.t('cli.form.delete.valid', :id => id)
 				else
-					puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+					puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 				end
 			else
 				puts I18n.t('cli.form.delete.not_valid')
@@ -202,10 +204,10 @@ class Cli
 	# Export the items in a CSV file
 	# @args: file -> the destination file
 	def export(file)
-		if @m.export(file)
+		if @mpw.export(file)
 			puts "The export in #{file} is succesfull!"
 		else
-			puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 		end
 
 	end
@@ -214,7 +216,7 @@ class Cli
 	# @args: file -> the import file
 	#        force -> no resquest a validation
 	def import(file, force=false)
-		result = @m.importPreview(file)
+		result = @mpw.importPreview(file)
 
 		if not force
 			if result.is_a?(Array) && !result.empty?
@@ -232,10 +234,10 @@ class Cli
 		end
 
 		if force
-			if @m.import(file) && @m.encrypt()
+			if @mpw.import(file) && @mpw.encrypt()
 				puts I18n.t('cli.form.import.valid')
 			else
-				puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
+				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 			end
 		end
 	end
@@ -247,7 +249,7 @@ class Cli
 
 		while buf = Readline.readline('<mpw> ', true)
 
-			if @m.timeout_pwd < Time.now.to_i - last_access
+			if @config.timeout_pwd < Time.now.to_i - last_access
 				passwd_confirm = ask(I18n.t('cli.interactive.ask_password')) {|q| q.echo = false}
 
 				if @passwd.eql?(passwd_confirm)
diff --git a/lib/MPW.rb b/lib/MPW.rb
index e38ec0a..937ecdf 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -6,7 +6,6 @@
 require 'rubygems'
 require 'gpgme'
 require 'csv'
-require 'yaml'
 require 'i18n'
 
 class MPW
@@ -22,79 +21,12 @@ class MPW
 	COMMENT  = 8
 
 	attr_accessor :error_msg
-	attr_accessor :timeout_pwd
-
+	
 	# Constructor
-	# @args: file_config -> the specify config file
-	def initialize(file_config=nil)
-		@error_msg  = nil
-		@file_config = "#{Dir.home()}/.mpw.cfg"
-
-		if !file_config.nil? && !file_config.empty?
-			@file_config = file_config
-		end
-	end
-
-	# Create a new config file
-	# @args: key -> the gpg key to encrypt
-	#        lang -> the software language
-	#        file_gpg -> the file who is encrypted
-	#        file_pwd -> the file who stock the password
-	#        timeout_pwd -> time to save the password 
-	# @rtrn: true if le config file is create
-	def setup(key, lang, file_gpg, timeout_pwd)
-
-		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-			@error_msg = I18n.t('error.config.key_bad_format')
-			return false
-		end
-		
-		if file_gpg.empty?
-			file_gpg = "#{Dir.home()}/.mpw.gpg"
-		end
-
-		timeout_pwd.empty? ? (timeout_pwd = 60) : (timeout_pwd = timeout_pwd.to_i)
-
-		config = {'config' => {'key'         => key,
-		                       'lang'        => lang,
-		                       'file_gpg'    => file_gpg,
-		                       'timeout_pwd' => timeout_pwd}}
-
-		begin
-			File.open(@file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-			return false
-		end
-
-		return true
-	end
-
-	# Check the config file
-	# @rtrn: true if the config file is correct
-	def checkconfig()
-		begin
-			config = YAML::load_file(@file_config)
-			@key         = config['config']['key']
-			@lang        = config['config']['lang']
-			@file_gpg    = config['config']['file_gpg']
-			@timeout_pwd = config['config']['timeout_pwd'].to_i
-
-			if @key.empty? || @file_gpg.empty? 
-				@error_msg = I18n.t('error.config.check')
-				return false
-			end
-				
-			I18n.locale = @lang.to_sym
-
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
-			return false
-		end
-
-		return true
+	def initialize(file_gpg, key=nil)
+		@error_msg = nil
+		@file_gpg  = file_gpg
+		@key       = key
 	end
 
 	# Decrypt a gpg file
@@ -117,10 +49,6 @@ class MPW
 
 			return true
 		rescue Exception => e 
-			if !@file_pwd.nil? && File.exist?(@file_pwd)
-				File.delete(@file_pwd)
-			end
-			
 			@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
 			return false
 		end
@@ -327,7 +255,7 @@ class MPW
 		end
 	end
 
-	# Return 
+	# Return a preview import 
 	# @args: file -> path to file import
 	# @rtrn: an array with the items to import, if there is an error return false
 	def importPreview(file)
@@ -372,7 +300,6 @@ class MPW
 		result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
 
 		return result
-		#return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]-%w(0 1 I O l i o)).sample(length).join
 	end
-		
+	
 end
diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
new file mode 100644
index 0000000..25dac98
--- /dev/null
+++ b/lib/MPWConfig.rb
@@ -0,0 +1,109 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+require 'rubygems'
+require 'yaml'
+require 'i18n'
+
+class MPWConfig
+	
+	attr_accessor :error_msg
+
+	attr_accessor :key
+	attr_accessor :lang
+	attr_accessor :file_gpg
+	attr_accessor :timeout_pwd
+	attr_accessor :sync_host
+	attr_accessor :sync_port
+	attr_accessor :sync_pwd
+	attr_accessor :sync_sufix
+
+	# Constructor
+	# @args: file_config -> the specify config file
+	def initialize(file_config=nil)
+		@error_msg  = nil
+		@file_config = "#{Dir.home()}/.mpw.cfg"
+
+		if !file_config.nil? && !file_config.empty?
+			@file_config = file_config
+		end
+	end
+
+	# Create a new config file
+	# @args: key -> the gpg key to encrypt
+	#        lang -> the software language
+	#        file_gpg -> the file who is encrypted
+	#        timeout_pwd -> time to save the password 
+	# @rtrn: true if le config file is create
+	def setup(key, lang, file_gpg, timeout_pwd)
+
+		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+			@error_msg = I18n.t('error.config.key_bad_format')
+			return false
+		end
+		
+		if file_gpg.empty?
+			file_gpg = "#{Dir.home()}/.mpw.gpg"
+		end
+
+		timeout_pwd.empty? ? (timeout_pwd = 60) : (timeout_pwd = timeout_pwd.to_i)
+
+		config = {'config' => {'key'         => key,
+		                       'lang'        => lang,
+		                       'file_gpg'    => file_gpg,
+		                       'timeout_pwd' => timeout_pwd,
+		                       'sync_host'   => host,
+		                       'sync_port'   => port,
+		                       'sync_pwd'    => password,
+		                       'sync_suffix' => suffix}}
+
+		begin
+			File.open(@file_config, 'w') do |file|
+				file << config.to_yaml
+			end
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+			return false
+		end
+
+		return true
+	end
+
+	# Check the config file
+	# @rtrn: true if the config file is correct
+	def checkconfig()
+		begin
+			config = YAML::load_file(@file_config)
+			@key         = config['config']['key']
+			@lang        = config['config']['lang']
+			@file_gpg    = config['config']['file_gpg']
+			@timeout_pwd = config['config']['timeout_pwd'].to_i
+			@sync_host   = config['config']['sync_host']
+			@sync_port   = config['config']['sync_port']
+			@sync_pwd    = config['config']['sync_pwd']
+			@sync_sufix  = config['config']['sync_suffix']
+
+			if @key.empty? || @file_gpg.empty? 
+				@error_msg = I18n.t('error.config.check')
+				return false
+			end
+
+			I18n.locale = @lang.to_sym
+
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
+			return false
+		end
+
+		begin
+			@sync = TCPSocket.new(@sync_host, @sync_port)
+		rescue
+			@sync = false
+		end
+
+		return true
+	end
+	
+end

From 9067a67d506035796d73b6df5079ee8e601048e3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 12 Jan 2014 00:40:58 +0100
Subject: [PATCH 009/531] remove tcp test

---
 lib/MPWConfig.rb | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index 25dac98..a59f3b5 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -97,12 +97,6 @@ class MPWConfig
 			return false
 		end
 
-		begin
-			@sync = TCPSocket.new(@sync_host, @sync_port)
-		rescue
-			@sync = false
-		end
-
 		return true
 	end
 	

From 477178ecdd13f19775552a464fa5458a9ce21254 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 12 Jan 2014 11:07:54 +0100
Subject: [PATCH 010/531] add last_update

---
 lib/Server.rb | 39 ++++++++++++++++++++++-----------------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 737d9da..307d0b6 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -61,26 +61,28 @@ class Server
 		end
 
 		if File.exist?(file_gpg)
-			gpg_data  = YAML::load_file(file_gpg)
-			salt      = gpg_data['gpg']['salt']
-			hash      = gpg_data['gpg']['hash']
-			data      = gpg_data['gpg']['data']
+			gpg_data    = YAML::load_file(file_gpg)
+			salt        = gpg_data['gpg']['salt']
+			hash        = gpg_data['gpg']['hash']
+			data        = gpg_data['gpg']['data']
+			last_update = gpg_data['gpg']['last_update']
 
 			if self.isAuthorized?(msg['password'], salt, hash)
 				send_msg = {:action  => 'get',
 				            :gpg_key => msg['gpg_key'],
-				            :msg     => 'get_done',
+				            :last_update => last_update,
+				            :msg     => 'done',
 				            :data    => data}
 			else
 				send_msg = {:action  => 'get',
 				            :gpg_key => msg['gpg_key'],
-				            :msg     => 'get_fail',
+				            :msg     => 'fail',
 				            :error   => 'not_authorized'}
 			end
 		else
 			send_msg = {:action  => 'get',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'get_fail',
+			            :msg     => 'fail',
 			            :error   => 'file_not_exist'}
 		end
 
@@ -97,7 +99,7 @@ class Server
 		if data.nil? || data.empty?
 			send_msg = {:action  => 'update',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'update_fail',
+			            :msg     => 'fail',
 			            :error   => 'no_data'}
 			
 			return send_msg.to_json
@@ -121,27 +123,30 @@ class Server
 
 		if self.isAuthorized?(msg['password'], salt, hash)
 			begin
-				config = {'gpg' => {'salt' => salt,
-				                    'hash' => hash,
-				                    'data' => data}}
+				last_update = Time.now.to_i
+				config = {'gpg' => {'salt'        => salt,
+				                    'hash'        => hash,
+				                    'last_update' => last_update
+				                    'data'        => data}}
 
 				File.open(file_gpg, 'w') do |file|
 					file << config.to_yaml
 				end
 
-				send_msg = {:action  => 'update',
-				            :gpg_key => msg['gpg_key'],
-				            :msg    => 'update_done'}
+				send_msg = {:action      => 'update',
+				            :gpg_key     => msg['gpg_key'],
+				            :last_update => last_update
+				            :msg         => 'done'}
 			rescue Exception => e
 				send_msg = {:action  => 'update',
 				            :gpg_key => msg['gpg_key'],
-				            :msg     => 'update_fail',
-				            :error   => e}
+				            :msg     => 'fail',
+				            :error   => 'server_error'}
 			end
 		else
 			send_msg = {:action  => 'update',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'update_fail',
+			            :msg     => 'fail',
 			            :error   => 'not_autorized'}
 		end
 		

From b9988102d6dda915ce1e3cb8a4bee4a9dc2569f3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 12 Jan 2014 11:36:56 +0100
Subject: [PATCH 011/531] add last_update in MPWConfig

---
 lib/MPWConfig.rb | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index a59f3b5..a402ee3 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -19,11 +19,12 @@ class MPWConfig
 	attr_accessor :sync_port
 	attr_accessor :sync_pwd
 	attr_accessor :sync_sufix
+	attr_accessor :sync_last_update
 
 	# Constructor
 	# @args: file_config -> the specify config file
 	def initialize(file_config=nil)
-		@error_msg  = nil
+		@error_msg   = nil
 		@file_config = "#{Dir.home()}/.mpw.cfg"
 
 		if !file_config.nil? && !file_config.empty?
@@ -57,7 +58,8 @@ class MPWConfig
 		                       'sync_host'   => host,
 		                       'sync_port'   => port,
 		                       'sync_pwd'    => password,
-		                       'sync_suffix' => suffix}}
+		                       'sync_suffix' => suffix,
+		                       'last_update' => 0 }}
 
 		begin
 			File.open(@file_config, 'w') do |file|
@@ -76,14 +78,15 @@ class MPWConfig
 	def checkconfig()
 		begin
 			config = YAML::load_file(@file_config)
-			@key         = config['config']['key']
-			@lang        = config['config']['lang']
-			@file_gpg    = config['config']['file_gpg']
-			@timeout_pwd = config['config']['timeout_pwd'].to_i
-			@sync_host   = config['config']['sync_host']
-			@sync_port   = config['config']['sync_port']
-			@sync_pwd    = config['config']['sync_pwd']
-			@sync_sufix  = config['config']['sync_suffix']
+			@key               = config['config']['key']
+			@lang              = config['config']['lang']
+			@file_gpg          = config['config']['file_gpg']
+			@timeout_pwd       = config['config']['timeout_pwd'].to_i
+			@sync_host         = config['config']['sync_host']
+			@sync_port         = config['config']['sync_port']
+			@sync_pwd          = config['config']['sync_pwd']
+			@sync_sufix        = config['config']['sync_suffix']
+			@sync_last_update  = config['config']['sync_last_update'].to_i
 
 			if @key.empty? || @file_gpg.empty? 
 				@error_msg = I18n.t('error.config.check')

From d458a09a75c5fd6fc3f7fd0c25a9703e58af4ebb Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 12 Jan 2014 14:09:32 +0100
Subject: [PATCH 012/531] fix bug

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

diff --git a/lib/Server.rb b/lib/Server.rb
index 307d0b6..361835a 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -126,7 +126,7 @@ class Server
 				last_update = Time.now.to_i
 				config = {'gpg' => {'salt'        => salt,
 				                    'hash'        => hash,
-				                    'last_update' => last_update
+				                    'last_update' => last_update,
 				                    'data'        => data}}
 
 				File.open(file_gpg, 'w') do |file|
@@ -135,7 +135,7 @@ class Server
 
 				send_msg = {:action      => 'update',
 				            :gpg_key     => msg['gpg_key'],
-				            :last_update => last_update
+				            :last_update => last_update,
 				            :msg         => 'done'}
 			rescue Exception => e
 				send_msg = {:action  => 'update',

From bc276f23c11bc953f2975e7f2a70659459d7f265 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 13 Jan 2014 20:07:15 +0100
Subject: [PATCH 013/531] add uniq id and date time

---
 lib/MPW.rb | 94 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 49 insertions(+), 45 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 937ecdf..1bd4392 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -19,6 +19,7 @@ class MPW
 	PASSWORD = 6
 	PORT     = 7
 	COMMENT  = 8
+	DATE     = 9
 
 	attr_accessor :error_msg
 	
@@ -40,10 +41,8 @@ class MPW
 				crypto = GPGME::Crypto.new(:armor => true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
 
-				id = 0
 				data_decrypt.lines do |line|
-					@data[id] = line.parse_csv.unshift(id)
-					id += 1;
+					@data.push(line.parse_csv)
 				end
 			end
 
@@ -63,7 +62,7 @@ class MPW
 
 			data_to_encrypt = ''
 			@data.each do |row|
-				data_to_encrypt << row.drop(1).to_csv
+				data_to_encrypt << row.to_csv
 			end
 
 			crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
@@ -107,11 +106,13 @@ class MPW
 	# @args: id -> the id item
 	# @rtrn: a row with the resultat of the search
 	def searchById(id)
-		if not @data[id.to_i].nil?
-			return @data[id.to_i]
-		else
-			return Array.new
+		@data.each do |row|
+			if @data[ID] == id
+				return row
+			end
 		end
+
+		return Array.new()
 	end
 
 	# Add a new item
@@ -136,16 +137,10 @@ class MPW
 			port = nil
 		end
 
-		if not @data.last.nil?
-			id = @data.last
-			id = id[ID].to_i + 1
-		else
-			id = 0
-		end
-
-		row[ID]    = id
+		row[ID]    = MPW.generatePassword(16)
 		row[PORT]  = port
 		row[NAME]  = name.force_encoding('ASCII-8BIT')
+		row[DATE]  = Time.now.to_i
 		group.nil?    || group.empty?    ? (row[GROUP]    = nil) : (row[GROUP]    = group.force_encoding('ASCII-8BIT'))
 		server.nil?   || server.empty?   ? (row[SERVER]   = nil) : (row[SERVER]   = server.force_encoding('ASCII-8BIT'))
 		protocol.nil? || protocol.empty? ? (row[PROTOCOL] = nil) : (row[PROTOCOL] = protocol.force_encoding('ASCII-8BIT'))
@@ -153,7 +148,7 @@ class MPW
 		passwd.nil?   || passwd.empty?   ? (row[PASSWORD] = nil) : (row[PASSWORD] = passwd.force_encoding('ASCII-8BIT'))
 		comment.nil?  || comment.empty?  ? (row[COMMENT]  = nil) : (row[COMMENT]  = comment.force_encoding('ASCII-8BIT'))
 
-		@data[id] = row
+		@data.push(row)
 
 		return true
 	end
@@ -170,45 +165,54 @@ class MPW
 	#        comment -> a comment
 	# @rtrn: true if the item has been updated
 	def update(id, name=nil, group=nil, server=nil, protocol=nil, login=nil, passwd=nil, port=nil, comment=nil)
-		id = id.to_i
+		i = 0
 
-		if not @data[id].nil?
+		@data.each do |row|
+			if not row[ID] == id
 
-			if port.to_i <= 0
-				port = nil
+				if port.to_i <= 0
+					port = nil
+				end
+
+				row_update = Array.new()
+				row[DATE]  = Time.now.to_i
+
+				name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])     : (row_update[NAME]     = name)
+				group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])    : (row_update[GROUP]    = group)
+				server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])   : (row_update[SERVER]   = server)
+				protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL]) : (row_update[PROTOCOL] = protocol)
+				login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])    : (row_update[LOGIN]    = login)
+				passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD]) : (row_update[PASSWORD] = passwd)
+				port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])     : (row_update[PORT]     = port)
+				comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])  : (row_update[COMMENT]  = comment)
+				
+				@data[i] = row_update
+
+				return true
 			end
 
-			row = @data[id]
-			row_update = Array.new()
-
-			name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])     : (row_update[NAME]     = name)
-			group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])    : (row_update[GROUP]    = group)
-			server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])   : (row_update[SERVER]   = server)
-			protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL]) : (row_update[PROTOCOL] = protocol)
-			login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])    : (row_update[LOGIN]    = login)
-			passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD]) : (row_update[PASSWORD] = passwd)
-			port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])     : (row_update[PORT]     = port)
-			comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])  : (row_update[COMMENT]  = comment)
-			
-			@data[id] = row_update
-
-			return true
-		else
-			@error_msg = I18n.t('error.update.id_no_exist', :id => id)
-			return false
+			i += 1
 		end
+
+		@error_msg = I18n.t('error.update.id_no_exist', :id => id)
+		return false
 	end
 	
 	# Remove an item 
 	# @args: id -> the item's identifiant
 	# @rtrn: true if the item has been deleted
 	def remove(id)
-		if not @data.delete_at(id.to_i).nil?
-			return true
-		else
-			@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
-			return false
+		i = 0
+		@data.each do |row|
+			if row[ID] == id
+				@data.delete(i)
+				return true
+			end
+			i += 1
 		end
+
+		@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
+		return false
 	end
 
 	# Export to csv
@@ -218,7 +222,7 @@ class MPW
 		begin
 			File.open(file, 'w+') do |file|
 				@data.each do |row|
-					row.delete_at(ID)
+					row.delete_at(ID).delete_at(DATE)
 					file << row.to_csv
 				end
 			end

From 8b86040af0f16efde46e55bc1d7fe4a980eb2b30 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 14 Jan 2014 23:00:52 +0100
Subject: [PATCH 014/531] first sync work :)

---
 lib/Cli.rb       |  19 +++++++++
 lib/MPW.rb       |  58 +++++++++++++++++++++++---
 lib/MPWConfig.rb |  46 ++++++++++++++++-----
 lib/Server.rb    |  68 ++++++++++++++++--------------
 lib/Sync.rb      | 105 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 248 insertions(+), 48 deletions(-)
 create mode 100644 lib/Sync.rb

diff --git a/lib/Cli.rb b/lib/Cli.rb
index 4947c00..aab3242 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -8,9 +8,11 @@ require 'highline/import'
 require 'pathname'
 require 'readline'
 require 'i18n'
+require 'yaml'
 
 require "#{APP_ROOT}/lib/MPW.rb"
 require "#{APP_ROOT}/lib/MPWConfig.rb"
+require "#{APP_ROOT}/lib/Sync.rb"
 
 class Cli
 
@@ -29,6 +31,23 @@ class Cli
 			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 			exit 2
 		end
+
+		@sync = Sync.new()
+		if @config.sync_host.nil? || @config.sync_port.nil?
+			@sync.disable()
+		elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
+			puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
+		else
+			begin
+				@mpw.sync(@sync.get(@passwd), @config.last_update)
+				@sync.update(File.open(@config.file_gpg).read)
+				@config.setLastUpdate()
+			rescue Exception => e
+				puts "#{I18n.t('cli.sync.error')}:\n#{e}"
+			else
+				@sync.close()
+			end
+		end
 	end
 
 	# Create a new config file
diff --git a/lib/MPW.rb b/lib/MPW.rb
index 1bd4392..1d15076 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -79,7 +79,7 @@ class MPW
 	# @args: search -> the string to search
 	#        protocol -> the connection protocol (ssh, web, other)
 	# @rtrn: a list with the resultat of the search
-	def search(search, group=nil, protocol=nil)
+	def search(search='', group=nil, protocol=nil)
 		result = Array.new()
 
 		if !search.nil?
@@ -107,7 +107,7 @@ class MPW
 	# @rtrn: a row with the resultat of the search
 	def searchById(id)
 		@data.each do |row|
-			if @data[ID] == id
+			if row[ID] == id
 				return row
 			end
 		end
@@ -168,14 +168,15 @@ class MPW
 		i = 0
 
 		@data.each do |row|
-			if not row[ID] == id
+			if row[ID] == id
 
 				if port.to_i <= 0
 					port = nil
 				end
 
-				row_update = Array.new()
-				row[DATE]  = Time.now.to_i
+				row_update        = Array.new()
+				row_update[ID]    = row[ID]
+				row_update[DATE]  = Time.now.to_i
 
 				name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])     : (row_update[NAME]     = name)
 				group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])    : (row_update[GROUP]    = group)
@@ -205,7 +206,7 @@ class MPW
 		i = 0
 		@data.each do |row|
 			if row[ID] == id
-				@data.delete(i)
+				@data.delete_at(i)
 				return true
 			end
 			i += 1
@@ -286,6 +287,51 @@ class MPW
 		end
 	end
 
+	# Sync remote data and local data
+	# @args: data_remote -> array with the data remote
+	#        last_update -> last update
+	# @rtrn: false if data_remote is nil
+	def sync(data_remote, last_update)
+		if !data_remote.instance_of?(Array)
+			return false
+		end
+
+		@data.each do |l|
+			j = 0
+			update = false
+
+			# Update item
+			data_remote.each do |r|
+				if l[ID] == r[ID]
+					if l[DATE].to_i < r[DATE].to_i
+						self.update(l[ID], r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT])
+					end
+					update = true
+					data_remote.delete_at(j)
+					break
+				end
+				j += 1
+			end
+
+			# Delete an old item
+			if !update && l[DATE].to_i < last_update
+				self.remove(l[ID])
+			end
+		end
+
+		# Add item
+		data_remote.each do |r|
+			if r[DATE].to_i > last_update
+				puts 'add'
+				@data.push(r)
+			end
+		end
+
+		self.encrypt()
+
+		return true
+	end
+
 	# Generate a random password
 	# @args: length -> the length password
 	# @rtrn: a random string
diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index a402ee3..790f34e 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -15,11 +15,12 @@ class MPWConfig
 	attr_accessor :lang
 	attr_accessor :file_gpg
 	attr_accessor :timeout_pwd
+	attr_accessor :last_update
 	attr_accessor :sync_host
 	attr_accessor :sync_port
 	attr_accessor :sync_pwd
-	attr_accessor :sync_sufix
-	attr_accessor :sync_last_update
+	attr_accessor :sync_suffix
+	attr_accessor :last_update
 
 	# Constructor
 	# @args: file_config -> the specify config file
@@ -78,15 +79,15 @@ class MPWConfig
 	def checkconfig()
 		begin
 			config = YAML::load_file(@file_config)
-			@key               = config['config']['key']
-			@lang              = config['config']['lang']
-			@file_gpg          = config['config']['file_gpg']
-			@timeout_pwd       = config['config']['timeout_pwd'].to_i
-			@sync_host         = config['config']['sync_host']
-			@sync_port         = config['config']['sync_port']
-			@sync_pwd          = config['config']['sync_pwd']
-			@sync_sufix        = config['config']['sync_suffix']
-			@sync_last_update  = config['config']['sync_last_update'].to_i
+			@key         = config['config']['key']
+			@lang        = config['config']['lang']
+			@file_gpg    = config['config']['file_gpg']
+			@timeout_pwd = config['config']['timeout_pwd'].to_i
+			@sync_host   = config['config']['sync_host']
+			@sync_port   = config['config']['sync_port']
+			@sync_pwd    = config['config']['sync_pwd']
+			@sync_sufix  = config['config']['sync_suffix']
+			@last_update = config['config']['last_update'].to_i
 
 			if @key.empty? || @file_gpg.empty? 
 				@error_msg = I18n.t('error.config.check')
@@ -102,5 +103,28 @@ class MPWConfig
 
 		return true
 	end
+
+	def setLastUpdate()
+		config = {'config' => {'key'         => @key,
+		                       'lang'        => @lang,
+		                       'file_gpg'    => @file_gpg,
+		                       'timeout_pwd' => @timeout_pwd,
+		                       'sync_host'   => @sync_host,
+		                       'sync_port'   => @sync_port,
+		                       'sync_pwd'    => @sync_pwd,
+		                       'sync_suffix' => @sync_uffix,
+		                       'last_update' => Time.now.to_i }}
+
+		begin
+			File.open(@file_config, 'w') do |file|
+				file << config.to_yaml
+			end
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+			return false
+		end
+
+		return true
+	end
 	
 end
diff --git a/lib/Server.rb b/lib/Server.rb
index 361835a..59d377c 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -21,29 +21,33 @@ class Server
 		server = TCPServer.open(@host, @port)
 		loop do
 			Thread.start(server.accept) do |client|
-				msg = self.getClientMessage(client)
+				while true do
+					msg = self.getClientMessage(client)
 
-				if !msg
-					next
-				end
-				
-				if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
-					self.closeConnection(client)
-					next
-				end
+					if !msg
+						next
+					end
+					
+					if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
+						self.closeConnection(client)
+						next
+					end
 
-				case msg['action']
-				when 'get'
-					client.puts self.getFile(msg)
-				when 'update'
-					client.puts self.updateFile(msg)
-				when 'delete'
-					client.puts self.deleteFile(msg)
-				else
-					client.puts 'Unknown command'
+					case msg['action']
+					when 'get'
+						client.puts self.getFile(msg)
+					when 'update'
+						client.puts self.updateFile(msg)
+						puts 'update'
+					when 'delete'
+						client.puts self.deleteFile(msg)
+					when 'close'
+						self.closeConnection(client)
+					else
+						client.puts 'Unknown command'
+						self.closeConnection(client)
+					end
 				end
-				
-				self.closeConnection(client)
 			end
 		end
 	end
@@ -68,11 +72,11 @@ class Server
 			last_update = gpg_data['gpg']['last_update']
 
 			if self.isAuthorized?(msg['password'], salt, hash)
-				send_msg = {:action  => 'get',
-				            :gpg_key => msg['gpg_key'],
+				send_msg = {:action      => 'get',
+				            :gpg_key     => msg['gpg_key'],
 				            :last_update => last_update,
-				            :msg     => 'done',
-				            :data    => data}
+				            :msg         => 'done',
+				            :data        => data}
 			else
 				send_msg = {:action  => 'get',
 				            :gpg_key => msg['gpg_key'],
@@ -80,10 +84,12 @@ class Server
 				            :error   => 'not_authorized'}
 			end
 		else
-			send_msg = {:action  => 'get',
-			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'fail',
-			            :error   => 'file_not_exist'}
+			send_msg = {:action      => 'get',
+			            :gpg_key     => msg['gpg_key'],
+			            :last_update => 0,
+			            :data        => '',
+			            :msg         => 'fail',
+			            :error       => 'file_not_exist'}
 		end
 
 		return send_msg.to_json
@@ -129,7 +135,7 @@ class Server
 				                    'last_update' => last_update,
 				                    'data'        => data}}
 
-				File.open(file_gpg, 'w') do |file|
+				File.open(file_gpg, 'w+') do |file|
 					file << config.to_yaml
 				end
 
@@ -242,11 +248,11 @@ class Server
 		begin
 			config    = YAML::load_file(file_config)
 			@host     = config['config']['host']
-			@port     = config['config']['port']
+			@port     = config['config']['port'].to_i
 			@data_dir = config['config']['data_dir']
 			@timeout  = config['config']['timeout'].to_i
 
-			if @host.empty? || @port.empty? || @data_dir.empty? 
+			if @host.empty? || @port <= 0 || @data_dir.empty? 
 				puts I18n.t('server.checkconfig.fail')
 				puts I18n.t('server.checkconfig.empty')
 				return false
diff --git a/lib/Sync.rb b/lib/Sync.rb
new file mode 100644
index 0000000..99f7d81
--- /dev/null
+++ b/lib/Sync.rb
@@ -0,0 +1,105 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+require 'rubygems'
+require 'i18n'
+require 'socket'
+require 'json'
+
+require "#{APP_ROOT}/lib/MPW.rb"
+
+class Sync
+
+	attr_accessor :error_msg
+
+	def initialize()
+		@error_msg = nil
+	end
+
+	def disable()
+		@sync = false
+	end
+
+	def connect(host, port, gpg_key, password, suffix=nil)
+		@gpg_key     = gpg_key
+		@password    = password
+		@suffix      = suffix
+
+		begin
+			@socket= TCPSocket.new(host, port)
+			@sync = true
+		rescue Exception => e
+			@error_msg = "ERROR: Connection impossible\n#{e}"
+			@sync = false
+		end
+
+		return @sync
+	end
+
+	def get(gpg_password)
+		send_msg = {:action      => 'get',
+		            :gpg_key     => @gpg_key,
+		            :password    => @password,
+		            :suffix      => @suffix}
+		
+		@socket.puts send_msg.to_json
+		msg = JSON.parse(@socket.gets)
+
+		case msg['error']
+		when nil, 'file_not_exist'
+			tmp_file = "/tmp/mpw-#{MPW.generatePassword()}.gpg"
+			File.open(tmp_file, 'w') do |file|
+				file << msg['data']
+			end
+			
+			@mpw = MPW.new(tmp_file)
+			if !@mpw.decrypt(gpg_password)
+				return nil
+			end
+
+			File.unlink(tmp_file)
+			
+			return @mpw.search()
+		when 'not_authorized'
+			@error_msg = 'not authorized'
+		else
+			@error_msg = 'error unknow'
+		end
+
+		return nil
+	end
+
+	def update(data)
+		send_msg = {:action      => 'update',
+		            :gpg_key     => @gpg_key,
+		            :password    => @password,
+		            :suffix      => @suffix,
+		            :data        => data}
+		
+		@socket.puts send_msg.to_json
+		msg = JSON.parse(@socket.gets)
+
+		case msg['error']
+		when nil
+			return true
+		when 'not_authorized'
+			@error_msg = 'not authorized'
+		when 'no_data'
+			@error_msg = 'no data'
+		else
+			@error_msg = 'error unknow'
+		end
+
+		return false
+	end
+
+	def delete()
+	end
+
+	def close()
+		send_msg = {:action => 'close'}
+		@socket.puts send_msg.to_json
+	end
+end

From e8c315403e0c76527ea8c987fd155b50282d7a33 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 14 Jan 2014 23:20:17 +0100
Subject: [PATCH 015/531] remove dev debug

---
 lib/Server.rb | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 59d377c..d3b561b 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -38,7 +38,6 @@ class Server
 						client.puts self.getFile(msg)
 					when 'update'
 						client.puts self.updateFile(msg)
-						puts 'update'
 					when 'delete'
 						client.puts self.deleteFile(msg)
 					when 'close'

From 213743bad22abc8cdc8752a91f8a756f199e0d15 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 20:01:23 +0100
Subject: [PATCH 016/531] fusion MPW:add and MPW:update

---
 lib/Cli.rb |  22 +++++-----
 lib/MPW.rb | 115 ++++++++++++++++++++---------------------------------
 2 files changed, 54 insertions(+), 83 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index aab3242..ed28ca2 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -23,11 +23,11 @@ class Cli
 		@config = MPWConfig.new(config_file)
 		
 		if not @config.checkconfig()
-			self.setup(lang)
+			setup(lang)
 		end
 
 		@mpw = MPW.new(@config.file_gpg, @config.key)
-		if not self.decrypt()
+		if not decrypt()
 			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
 			exit 2
 		end
@@ -91,9 +91,9 @@ class Cli
 		if not result.empty?
 			result.each do |r|
 				if format.nil? || !format
-					self.displayFormat(r)
+					displayFormat(r)
 				else
-					self.displayFormatAlt(r)
+					displayFormatAlt(r)
 				end
 			end
 		else
@@ -146,7 +146,7 @@ class Cli
 		port     = ask(I18n.t('cli.form.add.port')).to_s
 		comment  = ask(I18n.t('cli.form.add.comment')).to_s
 
-		if @mpw.add(name, group, server, protocol, login, passwd, port, comment)
+		if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
 			if @mpw.encrypt()
 				puts I18n.t('cli.form.add.valid')
 			else
@@ -174,7 +174,7 @@ class Cli
 			port     = ask(I18n.t('cli.form.update.port'    , :port => row[MPW::PORT])).to_s
 			comment  = ask(I18n.t('cli.form.update.comment' , :comment => row[MPW::COMMENT])).to_s
 				
-			if @mpw.update(id, name, group, server, protocol, login, passwd, port, comment)
+			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt()
 					puts I18n.t('cli.form.update.valid')
 				else
@@ -196,7 +196,7 @@ class Cli
 			result = @mpw.searchById(id)
 
 			if result.length > 0
-				self.displayFormat(result)
+				displayFormat(result)
 
 				confirm = ask("#{I18n.t('cli.form.delete.ask', :id => id)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
@@ -240,7 +240,7 @@ class Cli
 		if not force
 			if result.is_a?(Array) && !result.empty?
 				result.each do |r|
-					self.displayFormat(r)
+					displayFormat(r)
 				end
 
 				confirm = ask("#{I18n.t('cli.form.import.ask', :file => file)} (y/N) ").to_s
@@ -286,17 +286,17 @@ class Cli
 			case command[0]
 			when 'display', 'show', 'd', 's'
 				if !command[1].nil? && !command[1].empty?
-					self.display(command[1], group, command[2])
+					display(command[1], group, command[2])
 				end
 			when 'add', 'a'
 				add()
 			when 'update', 'u'
 				if !command[1].nil? && !command[1].empty?
-					self.update(command[1])
+					update(command[1])
 				end
 			when 'remove', 'delete', 'r', 'd'
 				if !command[1].nil? && !command[1].empty?
-					self.remove(command[1])
+					remove(command[1])
 				end
 			when 'group', 'g'
 				if !command[1].nil? && !command[1].empty?
diff --git a/lib/MPW.rb b/lib/MPW.rb
index 1d15076..6886b85 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -115,44 +115,6 @@ class MPW
 		return Array.new()
 	end
 
-	# Add a new item
-	# @args: name -> the item name
-	#        group -> the item group
-	#        server -> the ip or server
-	#        protocol -> the protocol
-	#        login -> the login
-	#        passwd -> the password
-	#        port -> the port
-	#        comment -> a comment
-	# @rtrn: true if it works
-	def add(name, group=nil, server=nil, protocol=nil, login=nil, passwd=nil, port=nil, comment=nil)
-		row = Array.new()
-		
-		if name.nil? || name.empty?
-			@error_msg = I18n.t('error.add.name_empty')
-			return false
-		end
-		
-		if port.to_i <= 0
-			port = nil
-		end
-
-		row[ID]    = MPW.generatePassword(16)
-		row[PORT]  = port
-		row[NAME]  = name.force_encoding('ASCII-8BIT')
-		row[DATE]  = Time.now.to_i
-		group.nil?    || group.empty?    ? (row[GROUP]    = nil) : (row[GROUP]    = group.force_encoding('ASCII-8BIT'))
-		server.nil?   || server.empty?   ? (row[SERVER]   = nil) : (row[SERVER]   = server.force_encoding('ASCII-8BIT'))
-		protocol.nil? || protocol.empty? ? (row[PROTOCOL] = nil) : (row[PROTOCOL] = protocol.force_encoding('ASCII-8BIT'))
-		login.nil?    || login.empty?    ? (row[LOGIN]    = nil) : (row[LOGIN]    = login.force_encoding('ASCII-8BIT'))
-		passwd.nil?   || passwd.empty?   ? (row[PASSWORD] = nil) : (row[PASSWORD] = passwd.force_encoding('ASCII-8BIT'))
-		comment.nil?  || comment.empty?  ? (row[COMMENT]  = nil) : (row[COMMENT]  = comment.force_encoding('ASCII-8BIT'))
-
-		@data.push(row)
-
-		return true
-	end
-	
 	# Update an item
 	# @args: id -> the item's identifiant
 	#        name -> the item name
@@ -164,39 +126,49 @@ class MPW
 	#        port -> the port
 	#        comment -> a comment
 	# @rtrn: true if the item has been updated
-	def update(id, name=nil, group=nil, server=nil, protocol=nil, login=nil, passwd=nil, port=nil, comment=nil)
-		i = 0
+	def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
+		row    = Array.new()
+		update = false
 
-		@data.each do |row|
-			if row[ID] == id
-
-				if port.to_i <= 0
-					port = nil
-				end
-
-				row_update        = Array.new()
-				row_update[ID]    = row[ID]
-				row_update[DATE]  = Time.now.to_i
-
-				name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])     : (row_update[NAME]     = name)
-				group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])    : (row_update[GROUP]    = group)
-				server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])   : (row_update[SERVER]   = server)
-				protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL]) : (row_update[PROTOCOL] = protocol)
-				login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])    : (row_update[LOGIN]    = login)
-				passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD]) : (row_update[PASSWORD] = passwd)
-				port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])     : (row_update[PORT]     = port)
-				comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])  : (row_update[COMMENT]  = comment)
-				
-				@data[i] = row_update
-
-				return true
+		i  = 0
+		@data.each do |r|
+			if r[ID] == id
+				row    = r
+				update = true
+				break
 			end
-
 			i += 1
 		end
 
-		@error_msg = I18n.t('error.update.id_no_exist', :id => id)
-		return false
+		if port.to_i <= 0
+			port = nil
+		end
+
+		row_update       = Array.new()
+		row_update[DATE] = Time.now.to_i
+
+		id.nil?	      || id.empty?       ? (row_update[ID]       = MPW.generatePassword(16)) : (row_update[ID]       = row[ID])
+		name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])                : (row_update[NAME]     = name)
+		group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])               : (row_update[GROUP]    = group)
+		server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])              : (row_update[SERVER]   = server)
+		protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL])            : (row_update[PROTOCOL] = protocol)
+		login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])               : (row_update[LOGIN]    = login)
+		passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD])            : (row_update[PASSWORD] = passwd)
+		port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])                : (row_update[PORT]     = port)
+		comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])             : (row_update[COMMENT]  = comment)
+		
+		if row_update[NAME].nil? || row_update[NAME].empty?
+			@error_msg = I18n.t('error.update.name_empty')
+			return false
+		end
+
+		if update
+			@data[i] = row_update
+		else
+			@data.push(row_update)
+		end
+
+		return true
 	end
 	
 	# Remove an item 
@@ -247,7 +219,7 @@ class MPW
 					return false
 				else
 					row = line.parse_csv.unshift(0)
-					if not add(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
+					if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
 						return false
 					end
 				end
@@ -304,7 +276,7 @@ class MPW
 			data_remote.each do |r|
 				if l[ID] == r[ID]
 					if l[DATE].to_i < r[DATE].to_i
-						self.update(l[ID], r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT])
+						update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
 					end
 					update = true
 					data_remote.delete_at(j)
@@ -315,19 +287,18 @@ class MPW
 
 			# Delete an old item
 			if !update && l[DATE].to_i < last_update
-				self.remove(l[ID])
+				remove(l[ID])
 			end
 		end
 
 		# Add item
 		data_remote.each do |r|
 			if r[DATE].to_i > last_update
-				puts 'add'
-				@data.push(r)
+				update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], r[ID])
 			end
 		end
 
-		self.encrypt()
+		encrypt()
 
 		return true
 	end

From 90086f3262d4b371ca43499f17ca09b457c97645 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 20:16:14 +0100
Subject: [PATCH 017/531] disable sync if connect fail

---
 lib/Sync.rb | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 99f7d81..126b02d 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -39,6 +39,10 @@ class Sync
 	end
 
 	def get(gpg_password)
+		if !@sync
+			return nil
+		end
+
 		send_msg = {:action      => 'get',
 		            :gpg_key     => @gpg_key,
 		            :password    => @password,
@@ -72,6 +76,10 @@ class Sync
 	end
 
 	def update(data)
+		if !@sync
+			return true
+		end
+
 		send_msg = {:action      => 'update',
 		            :gpg_key     => @gpg_key,
 		            :password    => @password,
@@ -99,6 +107,10 @@ class Sync
 	end
 
 	def close()
+		if !@sync
+			return
+		end
+
 		send_msg = {:action => 'close'}
 		@socket.puts send_msg.to_json
 	end

From 5d19550371975a2dce452b05c8b1f6ebeb446716 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 20:17:47 +0100
Subject: [PATCH 018/531] add version file

---
 VERSION | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 VERSION

diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..3eebe6a
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+1.1.0 - dev

From 049ad927f219e05925116424bb866d82a3c40735 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 20:21:45 +0100
Subject: [PATCH 019/531] update translation for MPW.rb

---
 i18n/en.yml | 4 +---
 i18n/fr.yml | 4 +---
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index c2203c4..a3141bf 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -1,8 +1,6 @@
 ---
 en:
   error:
-    add:
-      name_empty: "You must define a name!"
     config:
       write: "Can't write the config file!"
       check: "Checkconfig failed!"
@@ -18,7 +16,7 @@ en:
       bad_format: "Can't import, the file is badly formated!"
       read: "Can't import, unable to read %{file}!"
     update:
-      id_no_exist: "Can't update the item, the item %{id} doesn't exist!"
+      name_empty: "You must define a name!"
   cli:
     option:
       usage: "Usage"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index a1323be..5ccef5f 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -1,8 +1,6 @@
 ---
 fr:
   error:
-    add:
-      name_empty: "Vous devez définir un nom!"
     config:
       write: "Impossible d'écrire le fichier de configuration!"
       check: "Le fichier de configuration est invalide!"
@@ -18,7 +16,7 @@ fr:
       bad_format: "Impossible d'importer le fichier car son format est incorrect!"
       read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
     update:
-      id_no_exist: "Impossible de mettre à jour l'élément %{id}, car il n'existe pas!"
+      name_empty: "Vous devez définir un nom!"
   cli:
     option:
       usage: "Utilisation"

From a01a28112a5280e92c4c6e995016467dfe9ce068 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 20:52:59 +0100
Subject: [PATCH 020/531] fix bug suffix

---
 lib/MPWConfig.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index 790f34e..21c1437 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -112,7 +112,7 @@ class MPWConfig
 		                       'sync_host'   => @sync_host,
 		                       'sync_port'   => @sync_port,
 		                       'sync_pwd'    => @sync_pwd,
-		                       'sync_suffix' => @sync_uffix,
+		                       'sync_suffix' => @sync_suffix,
 		                       'last_update' => Time.now.to_i }}
 
 		begin

From d5c2af0867539f1d48699d533317d981a1c8651b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 20:55:17 +0100
Subject: [PATCH 021/531] fix bug suffix

---
 lib/MPWConfig.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index 21c1437..d68d0c7 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -86,7 +86,7 @@ class MPWConfig
 			@sync_host   = config['config']['sync_host']
 			@sync_port   = config['config']['sync_port']
 			@sync_pwd    = config['config']['sync_pwd']
-			@sync_sufix  = config['config']['sync_suffix']
+			@sync_suffix  = config['config']['sync_suffix']
 			@last_update = config['config']['last_update'].to_i
 
 			if @key.empty? || @file_gpg.empty? 

From 0598ec134f5965f2f912d69619b25bfb3b86f5ea Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 22:49:01 +0100
Subject: [PATCH 022/531] clean code

---
 lib/Server.rb | 29 +++++++++++------------------
 1 file changed, 11 insertions(+), 18 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index d3b561b..5b76de7 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -22,29 +22,29 @@ class Server
 		loop do
 			Thread.start(server.accept) do |client|
 				while true do
-					msg = self.getClientMessage(client)
+					msg = getClientMessage(client)
 
 					if !msg
 						next
 					end
 					
 					if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
-						self.closeConnection(client)
+						closeConnection(client)
 						next
 					end
 
 					case msg['action']
 					when 'get'
-						client.puts self.getFile(msg)
+						client.puts getFile(msg)
 					when 'update'
-						client.puts self.updateFile(msg)
+						client.puts updateFile(msg)
 					when 'delete'
-						client.puts self.deleteFile(msg)
+						client.puts deleteFile(msg)
 					when 'close'
-						self.closeConnection(client)
+						closeConnection(client)
 					else
 						client.puts 'Unknown command'
-						self.closeConnection(client)
+						closeConnection(client)
 					end
 				end
 			end
@@ -68,12 +68,10 @@ class Server
 			salt        = gpg_data['gpg']['salt']
 			hash        = gpg_data['gpg']['hash']
 			data        = gpg_data['gpg']['data']
-			last_update = gpg_data['gpg']['last_update']
 
-			if self.isAuthorized?(msg['password'], salt, hash)
+			if isAuthorized?(msg['password'], salt, hash)
 				send_msg = {:action      => 'get',
 				            :gpg_key     => msg['gpg_key'],
-				            :last_update => last_update,
 				            :msg         => 'done',
 				            :data        => data}
 			else
@@ -85,7 +83,6 @@ class Server
 		else
 			send_msg = {:action      => 'get',
 			            :gpg_key     => msg['gpg_key'],
-			            :last_update => 0,
 			            :data        => '',
 			            :msg         => 'fail',
 			            :error       => 'file_not_exist'}
@@ -126,12 +123,10 @@ class Server
 			hash = Digest::SHA256.hexdigest(salt + msg['password'])
 		end
 
-		if self.isAuthorized?(msg['password'], salt, hash)
+		if isAuthorized?(msg['password'], salt, hash)
 			begin
-				last_update = Time.now.to_i
 				config = {'gpg' => {'salt'        => salt,
 				                    'hash'        => hash,
-				                    'last_update' => last_update,
 				                    'data'        => data}}
 
 				File.open(file_gpg, 'w+') do |file|
@@ -140,7 +135,6 @@ class Server
 
 				send_msg = {:action      => 'update',
 				            :gpg_key     => msg['gpg_key'],
-				            :last_update => last_update,
 				            :msg         => 'done'}
 			rescue Exception => e
 				send_msg = {:action  => 'update',
@@ -183,7 +177,7 @@ class Server
 		salt      = gpg_data['gpg']['salt']
 		hash      = gpg_data['gpg']['hash']
 
-		if self.isAuthorized?(msg['password'], salt, hash)
+		if isAuthorized?(msg['password'], salt, hash)
 			begin
 				File.unlink(file_gpg)
 
@@ -227,8 +221,7 @@ class Server
 			msg = client.gets
 			return JSON.parse(msg)
 		rescue
-			client.puts "Communication it's bad"
-			self.closeConnection(client)
+			closeConnection(client)
 			return false
 		end
 	end

From 1a9fb0795053c7a03979e1991f70ecb5353b244c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 23:09:57 +0100
Subject: [PATCH 023/531] translate Sync.rb

---
 i18n/en.yml |  4 ++++
 i18n/fr.yml |  4 ++++
 lib/Sync.rb | 35 ++++++++++++++++++++++++++---------
 3 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index a3141bf..76bce2a 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -17,6 +17,10 @@ en:
       read: "Can't import, unable to read %{file}!"
     update:
       name_empty: "You must define a name!"
+    sync: 
+      no_data: "Nothing data!"
+      not_authorized: "You haven't the access to remote file!"
+      unknown: "An unknown error is occured!" 
   cli:
     option:
       usage: "Usage"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 5ccef5f..aa847ed 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -17,6 +17,10 @@ fr:
       read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
     update:
       name_empty: "Vous devez définir un nom!"
+    sync: 
+      no_data: "Aucune data!"
+      not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
+      unknown: "Une erreur inconnue est survenue!" 
   cli:
     option:
       usage: "Utilisation"
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 126b02d..72743a3 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -14,30 +14,42 @@ class Sync
 
 	attr_accessor :error_msg
 
+	# Constructor
 	def initialize()
 		@error_msg = nil
 	end
 
+	# Disable the sync
 	def disable()
 		@sync = false
 	end
 
+	# Connect to server
+	# @args: host -> the server host
+	#        port -> ther connection port
+	#        gpg_key -> the gpg key
+	#        password -> the remote password
+	#        suffix -> the suffix file
+	# @rtrn: false if the connection fail
 	def connect(host, port, gpg_key, password, suffix=nil)
-		@gpg_key     = gpg_key
-		@password    = password
-		@suffix      = suffix
+		@gpg_key  = gpg_key
+		@password = password
+		@suffix   = suffix
 
 		begin
 			@socket= TCPSocket.new(host, port)
 			@sync = true
 		rescue Exception => e
-			@error_msg = "ERROR: Connection impossible\n#{e}"
+			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
 			@sync = false
 		end
 
 		return @sync
 	end
 
+	# Get data on server
+	# @args: gpg_password -> the gpg password
+	# @rtrn: nil if nothing data or error
 	def get(gpg_password)
 		if !@sync
 			return nil
@@ -51,6 +63,7 @@ class Sync
 		@socket.puts send_msg.to_json
 		msg = JSON.parse(@socket.gets)
 
+		puts msg
 		case msg['error']
 		when nil, 'file_not_exist'
 			tmp_file = "/tmp/mpw-#{MPW.generatePassword()}.gpg"
@@ -67,14 +80,17 @@ class Sync
 			
 			return @mpw.search()
 		when 'not_authorized'
-			@error_msg = 'not authorized'
+			@error_msg = "#{I18n.t('error.sync.not_authorized')}\n#{e}"
 		else
-			@error_msg = 'error unknow'
+			@error_msg = "#{I18n.t('error.sync.unknown')}\n#{e}"
 		end
 
 		return nil
 	end
 
+	# Update the remote data
+	# @args: data -> the data to send on server
+	# @rtrn: false if there is a problem
 	def update(data)
 		if !@sync
 			return true
@@ -93,11 +109,11 @@ class Sync
 		when nil
 			return true
 		when 'not_authorized'
-			@error_msg = 'not authorized'
+			@error_msg = "#{I18n.t('error.sync.not_authorized')}\n#{e}"
 		when 'no_data'
-			@error_msg = 'no data'
+			@error_msg = "#{I18n.t('error.sync.no_data')}\n#{e}"
 		else
-			@error_msg = 'error unknow'
+			@error_msg = "#{I18n.t('error.sync.unknown')}\n#{e}"
 		end
 
 		return false
@@ -106,6 +122,7 @@ class Sync
 	def delete()
 	end
 
+	# Close the connection
 	def close()
 		if !@sync
 			return

From a77266daf70d76bdcdd0be0b86ec571311b68f82 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 23:11:39 +0100
Subject: [PATCH 024/531] restruc the code

---
 lib/Cli.rb | 35 +++++++++++++++++++----------------
 mpw        | 23 ++++++++++++++++++-----
 2 files changed, 37 insertions(+), 21 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index ed28ca2..8e2976a 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -19,12 +19,8 @@ class Cli
 	# Constructor
 	# @args: lang -> the operating system language
 	#        config_file -> a specify config file
-	def initialize(lang, config_file=nil)
-		@config = MPWConfig.new(config_file)
-		
-		if not @config.checkconfig()
-			setup(lang)
-		end
+	def initialize(lang, config)
+		@config = config
 
 		@mpw = MPW.new(@config.file_gpg, @config.key)
 		if not decrypt()
@@ -37,16 +33,23 @@ class Cli
 			@sync.disable()
 		elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
 			puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
-		else
-			begin
-				@mpw.sync(@sync.get(@passwd), @config.last_update)
-				@sync.update(File.open(@config.file_gpg).read)
-				@config.setLastUpdate()
-			rescue Exception => e
-				puts "#{I18n.t('cli.sync.error')}:\n#{e}"
-			else
-				@sync.close()
-			end
+		end
+	end
+
+	# Destructor
+	def finalize()
+		@sync.close()
+	end
+
+	# Sync the data with the server
+	def sync()
+		begin
+			@mpw.sync(@sync.get(@passwd), @config.last_update)
+			@sync.update(File.open(@config.file_gpg).read)
+			@config.setLastUpdate()
+			puts @sync.error_msg
+		rescue Exception => e
+			puts "#{I18n.t('cli.sync.error')}:\n#{e}"
 		end
 	end
 
diff --git a/mpw b/mpw
index a313ed7..80f3344 100755
--- a/mpw
+++ b/mpw
@@ -93,13 +93,17 @@ OptionParser.new do |opts|
 	end
 end.parse!
 
+config      = MPWConfig.new(options[:config])
+check_error = config.checkconfig()
 
-cli = Cli.new(lang, options[:config])
+cli = Cli.new(lang, config)
+cli.sync()
+	
+# Setup a new config 
+if !check_error || !options[:setup].nil?
+	cli.setup(lang)
 
 # Display the item's informations
-if not options[:setup].nil?
-	cli.setup()
-
 elsif not options[:display].nil?
 	cli.display(options[:display], options[:group], options[:type], options[:format])
 
@@ -125,7 +129,16 @@ elsif not options[:import].nil?
 
 # Interactive mode
 else
-	cli.interactive
+	begin
+		cli.interactive()
+	rescue SystemExit, Interrupt
+		cli.sync()
+		cli = nil
+		return 1
+	end
 end
 
+cli.sync()
+cli = nil
+
 exit 0

From ff15c3d6e79c809f129a58098318ec949c583566 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 23:13:59 +0100
Subject: [PATCH 025/531] add translate for Sync.rb

---
 i18n/en.yml | 1 +
 i18n/fr.yml | 1 +
 2 files changed, 2 insertions(+)

diff --git a/i18n/en.yml b/i18n/en.yml
index 76bce2a..933f3cd 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -18,6 +18,7 @@ en:
     update:
       name_empty: "You must define a name!"
     sync: 
+      connection: "Connection fail!"
       no_data: "Nothing data!"
       not_authorized: "You haven't the access to remote file!"
       unknown: "An unknown error is occured!" 
diff --git a/i18n/fr.yml b/i18n/fr.yml
index aa847ed..3423af1 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -18,6 +18,7 @@ fr:
     update:
       name_empty: "Vous devez définir un nom!"
     sync: 
+      connection: "La connexion n'a pu être établie"
       no_data: "Aucune data!"
       not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
       unknown: "Une erreur inconnue est survenue!" 

From e0c1f1bfc10f624ec98f4f51de5cab97060efad1 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 23:16:49 +0100
Subject: [PATCH 026/531] add translate for Cli.rb

---
 i18n/en.yml | 2 ++
 i18n/fr.yml | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/i18n/en.yml b/i18n/en.yml
index 933f3cd..7805f04 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -104,6 +104,8 @@ en:
       port: "Port"
       protocol: "Protocol"
       server: "Server"
+    sync:
+      not_connect: "The server connection fail!"	
   ssh:
     option:
       usage: "Usage"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 3423af1..9005238 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -104,6 +104,8 @@ fr:
       port: "Port"
       protocol: "Protocol"
       server: "Serveur"
+    sync:
+      not_connect: "La connexion au serveur n'a pu être établie!"	
   ssh:
     option:
       usage: "Utilisation"

From 1298caed2e43e6d970b190bd359ad565c32cc72f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 15 Jan 2014 23:20:22 +0100
Subject: [PATCH 027/531] remove debug puts

---
 lib/Cli.rb  | 1 -
 lib/Sync.rb | 1 -
 2 files changed, 2 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index 8e2976a..31c4a57 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -47,7 +47,6 @@ class Cli
 			@mpw.sync(@sync.get(@passwd), @config.last_update)
 			@sync.update(File.open(@config.file_gpg).read)
 			@config.setLastUpdate()
-			puts @sync.error_msg
 		rescue Exception => e
 			puts "#{I18n.t('cli.sync.error')}:\n#{e}"
 		end
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 72743a3..0c3ed31 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -63,7 +63,6 @@ class Sync
 		@socket.puts send_msg.to_json
 		msg = JSON.parse(@socket.gets)
 
-		puts msg
 		case msg['error']
 		when nil, 'file_not_exist'
 			tmp_file = "/tmp/mpw-#{MPW.generatePassword()}.gpg"

From e2839d136c8941a43b8ab83e4b097633bb5ab1c2 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 16 Jan 2014 21:24:08 +0100
Subject: [PATCH 028/531] clean error message with the server

---
 i18n/en.yml   |  6 ++++++
 i18n/fr.yml   | 10 ++++++++--
 lib/Server.rb | 49 ++++++++++++++++++++++---------------------------
 lib/Sync.rb   | 47 ++++++++++++++++++++++-------------------------
 4 files changed, 58 insertions(+), 54 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 7805f04..22f2f7b 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -19,6 +19,7 @@ en:
       name_empty: "You must define a name!"
     sync: 
       connection: "Connection fail!"
+      communication: "A communication problem with the server is appeared!"
       no_data: "Nothing data!"
       not_authorized: "You haven't the access to remote file!"
       unknown: "An unknown error is occured!" 
@@ -117,6 +118,11 @@ en:
       connect: "Connection to:"
       nothing: "Nothing result!"
   server:
+    error:
+      client:
+        unknown: "An unknown error is appeared!"
+        no_authorized: "You haven't access to remote file!"
+        no_data: "Nothing data has been sent!"
     option:
       usage: "Usage"
       config: "Specifie the configuration file"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 9005238..b14aa99 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -18,8 +18,9 @@ fr:
     update:
       name_empty: "Vous devez définir un nom!"
     sync: 
-      connection: "La connexion n'a pu être établie"
-      no_data: "Aucune data!"
+      connection: "La connexion n'a pu être établie!"
+      communication: "Un problème de communication avec le serveur est apparu!"
+      no_data: "Aucune donnée!"
       not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
       unknown: "Une erreur inconnue est survenue!" 
   cli:
@@ -117,6 +118,11 @@ fr:
       connect: "Connexion à:"
       nothing: "Aucun résultat!"
   server:
+    error:
+      client:
+        unknown: "Une erreur inconnue est apparue!"
+        no_authorized: "Vous n'avez pas les accès au fichier distant!"
+        no_data: "Aucune donnée n'a été transmise!"
     option:
       usage: "Utilisation"
       config: "Spécifie le fichier de configuration"
diff --git a/lib/Server.rb b/lib/Server.rb
index 5b76de7..267105f 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -43,7 +43,10 @@ class Server
 					when 'close'
 						closeConnection(client)
 					else
-						client.puts 'Unknown command'
+						send_msg = {:action      => 'unknown',
+						            :gpg_key     => msg['gpg_key'],
+						            :error       => 'server.error.client.unknown'}
+						client.puts send_msg 
 						closeConnection(client)
 					end
 				end
@@ -72,20 +75,18 @@ class Server
 			if isAuthorized?(msg['password'], salt, hash)
 				send_msg = {:action      => 'get',
 				            :gpg_key     => msg['gpg_key'],
-				            :msg         => 'done',
-				            :data        => data}
+				            :data        => data,
+				            :error       => nil}
 			else
 				send_msg = {:action  => 'get',
 				            :gpg_key => msg['gpg_key'],
-				            :msg     => 'fail',
-				            :error   => 'not_authorized'}
+				            :error   => 'server.error.client.no_authorized'}
 			end
 		else
-			send_msg = {:action      => 'get',
-			            :gpg_key     => msg['gpg_key'],
-			            :data        => '',
-			            :msg         => 'fail',
-			            :error       => 'file_not_exist'}
+			send_msg = {:action  => 'get',
+			            :gpg_key => msg['gpg_key'],
+			            :data    => '',
+			            :error   => nil}
 		end
 
 		return send_msg.to_json
@@ -101,8 +102,7 @@ class Server
 		if data.nil? || data.empty?
 			send_msg = {:action  => 'update',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'fail',
-			            :error   => 'no_data'}
+			            :error   => 'server.error.client.no_data'}
 			
 			return send_msg.to_json
 		end
@@ -133,20 +133,18 @@ class Server
 					file << config.to_yaml
 				end
 
-				send_msg = {:action      => 'update',
-				            :gpg_key     => msg['gpg_key'],
-				            :msg         => 'done'}
+				send_msg = {:action  => 'update',
+				            :gpg_key => msg['gpg_key'],
+				            :error   => nil}
 			rescue Exception => e
 				send_msg = {:action  => 'update',
 				            :gpg_key => msg['gpg_key'],
-				            :msg     => 'fail',
-				            :error   => 'server_error'}
+				            :error   => 'server.error.client.unknown'}
 			end
 		else
 			send_msg = {:action  => 'update',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'fail',
-			            :error   => 'not_autorized'}
+			            :error   => 'server.error.client.no_authorized'}
 		end
 		
 		return send_msg.to_json
@@ -167,8 +165,7 @@ class Server
 		if !File.exist?(file_gpg)
 			send_msg = {:action  => 'delete',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'delete_fail',
-			            :error   => 'file_not_exist'}
+			            :error   => nil}
 
 			return send_msg.to_json
 		end
@@ -183,18 +180,16 @@ class Server
 
 				send_msg = {:action  => 'delete',
 				            :gpg_key => msg['gpg_key'],
-				            :msg    => 'delete_done'}
+				            :error   => nil}
 			rescue Exception => e
 				send_msg = {:action  => 'delete',
 				            :gpg_key => msg['gpg_key'],
-				            :msg     => 'delete_fail',
-				            :error   => e}
+				            :error   => 'server.error.client.unknown'}
 			end
 		else
 			send_msg = {:action  => 'delete',
 			            :gpg_key => msg['gpg_key'],
-			            :msg     => 'delete_fail',
-			            :error   => 'not_autorized'}
+			            :error   => 'server.error.client.no_authorized'}
 		end
 		
 		return send_msg.to_json
@@ -285,7 +280,7 @@ class Server
 				file << config.to_yaml
 			end
 		rescue Exception => e 
-			puts "#{I18n.t('server.formsetup.not_valid')}\n#{e}"
+			puts "#{I18n.t('server.form.setup.not_valid')}\n#{e}"
 			return false
 		end
 
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 0c3ed31..a02a08d 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -55,16 +55,18 @@ class Sync
 			return nil
 		end
 
-		send_msg = {:action      => 'get',
-		            :gpg_key     => @gpg_key,
-		            :password    => @password,
-		            :suffix      => @suffix}
+		send_msg = {:action   => 'get',
+		            :gpg_key  => @gpg_key,
+		            :password => @password,
+		            :suffix   => @suffix}
 		
 		@socket.puts send_msg.to_json
 		msg = JSON.parse(@socket.gets)
 
-		case msg['error']
-		when nil, 'file_not_exist'
+		if !msg['error']
+			@error_msg = I18n.t('error.sync.communication')
+			return nil
+		elsif msg['error'].nil?
 			tmp_file = "/tmp/mpw-#{MPW.generatePassword()}.gpg"
 			File.open(tmp_file, 'w') do |file|
 				file << msg['data']
@@ -76,15 +78,12 @@ class Sync
 			end
 
 			File.unlink(tmp_file)
-			
 			return @mpw.search()
-		when 'not_authorized'
-			@error_msg = "#{I18n.t('error.sync.not_authorized')}\n#{e}"
 		else
-			@error_msg = "#{I18n.t('error.sync.unknown')}\n#{e}"
+			@error_msg = I18n.t(msg['error'])
+			return nil
 		end
 
-		return nil
 	end
 
 	# Update the remote data
@@ -95,29 +94,27 @@ class Sync
 			return true
 		end
 
-		send_msg = {:action      => 'update',
-		            :gpg_key     => @gpg_key,
-		            :password    => @password,
-		            :suffix      => @suffix,
-		            :data        => data}
+		send_msg = {:action   => 'update',
+		            :gpg_key  => @gpg_key,
+		            :password => @password,
+		            :suffix   => @suffix,
+		            :data     => data}
 		
 		@socket.puts send_msg.to_json
 		msg = JSON.parse(@socket.gets)
 
-		case msg['error']
-		when nil
+		if !msg['error']
+			@error_msg = I18n.t('error.sync.communication')
+			return false
+		elsif msg['error'].nil?
 			return true
-		when 'not_authorized'
-			@error_msg = "#{I18n.t('error.sync.not_authorized')}\n#{e}"
-		when 'no_data'
-			@error_msg = "#{I18n.t('error.sync.no_data')}\n#{e}"
 		else
-			@error_msg = "#{I18n.t('error.sync.unknown')}\n#{e}"
+			@error_msg = I18n.t(msg['error'])
+			return false
 		end
-
-		return false
 	end
 
+	# TODO
 	def delete()
 	end
 

From 8c1e2f5e872dab10d16166b1f714ed554a609c99 Mon Sep 17 00:00:00 2001
From: adrien <adrien.waksberg@believedigital.com>
Date: Thu, 16 Jan 2014 22:50:54 +0100
Subject: [PATCH 029/531] fix bug with update

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

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 6886b85..630df2f 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -147,7 +147,7 @@ class MPW
 		row_update       = Array.new()
 		row_update[DATE] = Time.now.to_i
 
-		id.nil?	      || id.empty?       ? (row_update[ID]       = MPW.generatePassword(16)) : (row_update[ID]       = row[ID])
+		id.nil?	      || id.empty?       ? (row_update[ID]       = MPW.generatePassword(16)) : (row_update[ID]       = id)
 		name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])                : (row_update[NAME]     = name)
 		group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])               : (row_update[GROUP]    = group)
 		server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])              : (row_update[SERVER]   = server)
@@ -298,9 +298,7 @@ class MPW
 			end
 		end
 
-		encrypt()
-
-		return true
+		return encrypt()
 	end
 
 	# Generate a random password

From 77412b4794fe0d6b779693c5e14fef300bfbd8c9 Mon Sep 17 00:00:00 2001
From: adrien <adrien.waksberg@believedigital.com>
Date: Thu, 16 Jan 2014 23:29:39 +0100
Subject: [PATCH 030/531] fix bug encoding

---
 lib/MPW.rb | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 630df2f..0f2daf6 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -157,6 +157,14 @@ class MPW
 		port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])                : (row_update[PORT]     = port)
 		comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])             : (row_update[COMMENT]  = comment)
 		
+		row_update[NAME].nil?     ? (row_update[NAME]     = row_update[NAME])     : (row_update[NAME]     = row_update[NAME].force_encoding('ASCII-8BIT'))
+		row_update[GROUP].nil?    ? (row_update[GROUP]    = row_update[GROUP])    : (row_update[GROUP]    = row_update[GROUP].force_encoding('ASCII-8BIT'))
+		row_update[SERVER].nil?   ? (row_update[SERVER]   = row_update[SERVER])   : (row_update[SERVER]   = row_update[SERVER].force_encoding('ASCII-8BIT'))
+		row_update[PROTOCOL].nil? ? (row_update[PROTOCOL] = row_update[PROTOCOL]) : (row_update[PROTOCOL] = row_update[PROTOCOL].force_encoding('ASCII-8BIT'))
+		row_update[LOGIN].nil?    ? (row_update[LOGIN]    = row_update[LOGIN])    : (row_update[LOGIN]    = row_update[LOGIN].force_encoding('ASCII-8BIT'))
+		row_update[PASSWORD].nil? ? (row_update[PASSWORD] = row_update[PASSWORD]) : (row_update[PASSWORD] = row_update[PASSWORD].force_encoding('ASCII-8BIT'))
+		row_update[COMMENT].nil?  ? (row_update[COMMENT]  = row_update[COMMENT])  : (row_update[COMMENT]  = row_update[COMMENT].force_encoding('ASCII-8BIT'))
+
 		if row_update[NAME].nil? || row_update[NAME].empty?
 			@error_msg = I18n.t('error.update.name_empty')
 			return false

From 2199e8cf8a4bba39fe624a4a03f60a8d4def8d2b Mon Sep 17 00:00:00 2001
From: adrien <adrien.waksberg@believedigital.com>
Date: Thu, 16 Jan 2014 23:33:40 +0100
Subject: [PATCH 031/531] fix check error message

---
 lib/Sync.rb | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index a02a08d..2fa8ec7 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -63,7 +63,7 @@ class Sync
 		@socket.puts send_msg.to_json
 		msg = JSON.parse(@socket.gets)
 
-		if !msg['error']
+		if !defined?(msg['error'])
 			@error_msg = I18n.t('error.sync.communication')
 			return nil
 		elsif msg['error'].nil?
@@ -74,6 +74,7 @@ class Sync
 			
 			@mpw = MPW.new(tmp_file)
 			if !@mpw.decrypt(gpg_password)
+				puts @mpw.error_msg
 				return nil
 			end
 
@@ -103,7 +104,7 @@ class Sync
 		@socket.puts send_msg.to_json
 		msg = JSON.parse(@socket.gets)
 
-		if !msg['error']
+		if !defined?(msg['error'])
 			@error_msg = I18n.t('error.sync.communication')
 			return false
 		elsif msg['error'].nil?

From 352bc24f1a37158af9e742b1ea580104664c623d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 23 Jan 2014 22:32:48 +0100
Subject: [PATCH 032/531] fix bug in mpw-ssh since add sync

---
 lib/CliSSH.rb |  2 +-
 mpw-ssh       | 22 ++++++++++++++++------
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/lib/CliSSH.rb b/lib/CliSSH.rb
index 7e7d00b..7fba1ad 100644
--- a/lib/CliSSH.rb
+++ b/lib/CliSSH.rb
@@ -12,7 +12,7 @@ class CliSSH < Cli
 	# Connect to SSH
 	# args: search -> string to search
 	def ssh(search)
-		result = @m.search(search, nil, 'ssh')
+		result = @mpw.search(search, nil, 'ssh')
 
 		if result.length > 0
 			result.each do |r|
diff --git a/mpw-ssh b/mpw-ssh
index a991cc1..168803f 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -39,20 +39,30 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
-	opts.on("-h", "--help", I18n.t('ssh.option.help')) do |b|
+	opts.on("-h", "--help", I18n.t('ssh.option.help')) do
 		puts opts
 		exit 0
 	end
 end.parse!
 
-cli        = CliSSH.new(lang, options[:config])
-cli.login  = options[:login]
-cli.server = options[:server]
-cli.port   = options[:port]
+config      = MPWConfig.new(options[:config])
+check_error = config.checkconfig()
+
+cli         = CliSSH.new(lang, config)
+cli.login   = options[:login]
+cli.server  = options[:server]
+cli.port    = options[:port]
 
 search     = ARGV[0]
 
-if ARGV.length < 1
+cli.sync()
+
+# Setup a new config 
+if !check_error 
+	cli.setup(lang)
+
+# Connect ssh
+elsif ARGV.length < 1
 	puts "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
 	exit 2
 end

From c654dac5326203f9aa8a0fa1995c6a02fdbfcfb4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 23 Jan 2014 22:40:16 +0100
Subject: [PATCH 033/531] close connection

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

diff --git a/mpw-ssh b/mpw-ssh
index 168803f..a517e9e 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -61,12 +61,12 @@ cli.sync()
 if !check_error 
 	cli.setup(lang)
 
-# Connect ssh
 elsif ARGV.length < 1
 	puts "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
 	exit 2
 end
 
 cli.ssh(search)
+cli = nil
 
 exit 0

From 66f07e907cda5140abd4bf318c52aa2e80e3b2f4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 23 Jan 2014 23:04:13 +0100
Subject: [PATCH 034/531] fix when sync with the server

---
 lib/Cli.rb | 20 +++++++++++++++++---
 mpw        |  3 ---
 mpw-ssh    |  5 ++---
 3 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index 31c4a57..d68ac04 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -34,6 +34,8 @@ class Cli
 		elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
 			puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
 		end
+
+		sync()
 	end
 
 	# Destructor
@@ -44,9 +46,17 @@ class Cli
 	# Sync the data with the server
 	def sync()
 		begin
-			@mpw.sync(@sync.get(@passwd), @config.last_update)
-			@sync.update(File.open(@config.file_gpg).read)
-			@config.setLastUpdate()
+			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
+				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+			end
+
+			if !@sync.update(File.open(@config.file_gpg).read)
+				puts "#{I18n.t('cli.display.error')}: #{@sync.error_msg}"
+			end
+
+			if !@config.setLastUpdate()
+				puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
+			end
 		rescue Exception => e
 			puts "#{I18n.t('cli.sync.error')}:\n#{e}"
 		end
@@ -150,6 +160,7 @@ class Cli
 
 		if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
 			if @mpw.encrypt()
+				sync()
 				puts I18n.t('cli.form.add.valid')
 			else
 				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
@@ -178,6 +189,7 @@ class Cli
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt()
+					sync()
 					puts I18n.t('cli.form.update.valid')
 				else
 					puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
@@ -212,6 +224,7 @@ class Cli
 		if force
 			if @mpw.remove(id)
 				if @mpw.encrypt()
+					sync()
 					puts I18n.t('cli.form.delete.valid', :id => id)
 				else
 					puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
@@ -256,6 +269,7 @@ class Cli
 
 		if force
 			if @mpw.import(file) && @mpw.encrypt()
+				sync()
 				puts I18n.t('cli.form.import.valid')
 			else
 				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
diff --git a/mpw b/mpw
index 80f3344..f6660f6 100755
--- a/mpw
+++ b/mpw
@@ -97,7 +97,6 @@ config      = MPWConfig.new(options[:config])
 check_error = config.checkconfig()
 
 cli = Cli.new(lang, config)
-cli.sync()
 	
 # Setup a new config 
 if !check_error || !options[:setup].nil?
@@ -132,13 +131,11 @@ else
 	begin
 		cli.interactive()
 	rescue SystemExit, Interrupt
-		cli.sync()
 		cli = nil
 		return 1
 	end
 end
 
-cli.sync()
 cli = nil
 
 exit 0
diff --git a/mpw-ssh b/mpw-ssh
index a517e9e..aa0e80a 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -55,8 +55,6 @@ cli.port    = options[:port]
 
 search     = ARGV[0]
 
-cli.sync()
-
 # Setup a new config 
 if !check_error 
 	cli.setup(lang)
@@ -64,9 +62,10 @@ if !check_error
 elsif ARGV.length < 1
 	puts "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
 	exit 2
+else
+	cli.ssh(search)
 end
 
-cli.ssh(search)
 cli = nil
 
 exit 0

From 961117338cca5217965abfb879f6d5991f43c5f5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 25 Jan 2014 11:35:12 +0100
Subject: [PATCH 035/531] add log on server

---
 lib/Server.rb | 44 ++++++++++++++++++++++++++++++++++++++++++--
 mpw-server    |  5 +++--
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 267105f..83d6b19 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -4,11 +4,17 @@ require 'socket'
 require 'json'
 require 'highline/import'
 require 'digest'
+require 'logger'
 
 require "#{APP_ROOT}/lib/MPW.rb"
 
 class Server
 	
+	INFO    = 0
+	WARNING = 1
+	ERROR   = 2
+	DEBUG   = 3
+
 	attr_accessor :error_msg
 
 	# Constructor
@@ -18,9 +24,18 @@ class Server
 
 	# Start the server
 	def start()
-		server = TCPServer.open(@host, @port)
+		begin
+			server = TCPServer.open(@host, @port)
+			@log.info("The server is started on #{@host}:#{@port}")
+		rescue Exception => e
+			@log.error("Impossible to start the server: #{e}")
+			exit 2
+		end
+
 		loop do
 			Thread.start(server.accept) do |client|
+				@log.info("#{client.peeraddr[3]} is connected")
+
 				while true do
 					msg = getClientMessage(client)
 
@@ -35,14 +50,19 @@ class Server
 
 					case msg['action']
 					when 'get'
+						@log.debug("#{client.peeraddr[3]} GET gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
 						client.puts getFile(msg)
 					when 'update'
+						@log.debug("#{client.peeraddr[3]} UPDATE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
 						client.puts updateFile(msg)
 					when 'delete'
+						@log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
 						client.puts deleteFile(msg)
 					when 'close'
+						@log.info("#{client.peeraddr[3]} is disconnected")
 						closeConnection(client)
 					else
+						@log.warning("#{client.peeraddr[3]} is disconnected for unkwnow command")
 						send_msg = {:action      => 'unknown',
 						            :gpg_key     => msg['gpg_key'],
 						            :error       => 'server.error.client.unknown'}
@@ -237,6 +257,7 @@ class Server
 			@host     = config['config']['host']
 			@port     = config['config']['port'].to_i
 			@data_dir = config['config']['data_dir']
+			@log_file = config['config']['log_file']
 			@timeout  = config['config']['timeout'].to_i
 
 			if @host.empty? || @port <= 0 || @data_dir.empty? 
@@ -251,6 +272,24 @@ class Server
 				return false
 			end
 
+			if @log_file.nil? || @log_file.empty?
+				puts I18n.t('server.checkconfig.fail')
+				puts I18n.t('server.checkconfig.log_file_empty')
+				return false
+			#elsif !File.writable?(@log_file)
+			#	puts I18n.t('server.checkconfig.fail')
+			#	puts I18n.t('server.checkconfig.log_file_ro')
+			#	return false
+			else
+				begin
+					@log = Logger.new(@log_file)
+				rescue
+					puts I18n.t('server.checkconfig.fail')
+					puts I18n.t('server.checkconfig.log_file_create')
+					return false
+				end
+			end
+
 		rescue Exception => e 
 			puts "#{I18n.t('server.checkconfig.fail')}\n#{e}"
 			return false
@@ -268,11 +307,13 @@ class Server
 		host     = ask(I18n.t('server.form.setup.host')).to_s
 		port     = ask(I18n.t('server.form.setup.port')).to_s
 		data_dir = ask(I18n.t('server.form.setup.data_dir')).to_s
+		log_file = ask(I18n.t('server.form.setup.log_file')).to_s
 		timeout  = ask(I18n.t('server.form.setup.timeout')).to_s
 
 		config = {'config' => {'host'     => host,
 		                       'port'     => port,
 		                       'data_dir' => data_dir,
+		                       'log_file' => log_file,
 		                       'timeout'  => timeout}}
 
 		begin
@@ -286,5 +327,4 @@ class Server
 
 		return true
 	end
-
 end
diff --git a/mpw-server b/mpw-server
index 3143b59..016cebf 100755
--- a/mpw-server
+++ b/mpw-server
@@ -53,8 +53,9 @@ if options[:checkconfig]
 elsif options[:setup]
 	server.setup(options[:config])
 else
-	server.checkconfig(options[:config])
-	server.start()
+	if server.checkconfig(options[:config])
+		server.start()
+	end
 end
 
 exit 0

From d65316896e38e68e577de3836ad2ab0d9d281434 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 25 Jan 2014 16:53:48 +0100
Subject: [PATCH 036/531] add sync information in cli setup

---
 lib/Cli.rb       | 46 +++++++++++++++++++++++++++++-----------------
 lib/MPWConfig.rb | 20 +++++++++++++-------
 mpw              |  6 +++++-
 3 files changed, 47 insertions(+), 25 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index d68ac04..03c7595 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -21,21 +21,6 @@ class Cli
 	#        config_file -> a specify config file
 	def initialize(lang, config)
 		@config = config
-
-		@mpw = MPW.new(@config.file_gpg, @config.key)
-		if not decrypt()
-			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
-			exit 2
-		end
-
-		@sync = Sync.new()
-		if @config.sync_host.nil? || @config.sync_port.nil?
-			@sync.disable()
-		elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
-			puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
-		end
-
-		sync()
 	end
 
 	# Destructor
@@ -45,6 +30,16 @@ class Cli
 
 	# Sync the data with the server
 	def sync()
+		if !defined?(@sync)
+			@sync = Sync.new()
+
+			if @config.sync_host.nil? || @config.sync_port.nil?
+				@sync.disable()
+			elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
+				puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
+			end
+		end
+		
 		begin
 			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
 				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
@@ -71,13 +66,22 @@ class Cli
 		key         = ask(I18n.t('cli.form.setup.gpg_key')).to_s
 		file_gpg    = ask(I18n.t('cli.form.setup.gpg_file', :home => Dir.home())).to_s
 		timeout_pwd = ask(I18n.t('cli.form.setup.timeout')).to_s
+		sync_host   = ask(I18n.t('cli.form.setup.sync_host')).to_s
+		sync_port   = ask(I18n.t('cli.form.setup.sync_port')).to_s
+		sync_pwd    = ask(I18n.t('cli.form.setup.sync_pwd')).to_s
+		sync_suffix = ask(I18n.t('cli.form.setup.sync_suffix')).to_s
 		
 		if !File.exist?("#{APP_ROOT}/i18n/#{language}.yml")
 			language= 'en'
 		end
 		I18n.locale = language.to_sym
 
-		if @config.setup(key, language, file_gpg, timeout_pwd)
+		sync_host.empty?   ? (sync_host = nil)   : (sync_host   = sync_host)
+		sync_port.empty?   ? (sync_port = nil)   : (sync_port   = sync_port.to_i)
+		sync_pwd.empty?    ? (sync_pwd = nil)    : (sync_pwd    = sync_pwd)
+		sync_suffix.empty? ? (sync_suffix = nil) : (sync_suffix = sync_suffix)
+
+		if @config.setup(key, language, file_gpg, timeout_pwd, sync_host, sync_port, sync_pwd, sync_suffix)
 			puts I18n.t('cli.form.setup.valid')
 		else
 			puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
@@ -85,13 +89,21 @@ class Cli
 
 		if not @config.checkconfig()
 			puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
+			exit 2
 		end
 	end
 
 	# Request the GPG password and decrypt the file
 	def decrypt()
+		if !defined?(@mpw)
+			@mpw = MPW.new(@config.file_gpg, @config.key)
+		end
+
 		@passwd = ask(I18n.t('cli.display.gpg_password')) {|q| q.echo = false}
-		return @mpw.decrypt(@passwd)
+		if !@mpw.decrypt(@passwd)
+			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+			exit 2
+		end
 	end
 
 	# Display the query's result
diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index d68d0c7..6362b4c 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -37,9 +37,13 @@ class MPWConfig
 	# @args: key -> the gpg key to encrypt
 	#        lang -> the software language
 	#        file_gpg -> the file who is encrypted
-	#        timeout_pwd -> time to save the password 
+	#        timeout_pwd -> time to save the password
+	#        sync_host -> the server host for synchronization
+	#        sync_port -> the server port for synchronization
+	#        sync_pwd -> the password for synchronization
+	#        sync_suffix -> the suffix file (optionnal) 
 	# @rtrn: true if le config file is create
-	def setup(key, lang, file_gpg, timeout_pwd)
+	def setup(key, lang, file_gpg, timeout_pwd, sync_host=nil, sync_port=nil, sync_pwd=nil, sync_suffix=nil)
 
 		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 			@error_msg = I18n.t('error.config.key_bad_format')
@@ -56,10 +60,10 @@ class MPWConfig
 		                       'lang'        => lang,
 		                       'file_gpg'    => file_gpg,
 		                       'timeout_pwd' => timeout_pwd,
-		                       'sync_host'   => host,
-		                       'sync_port'   => port,
-		                       'sync_pwd'    => password,
-		                       'sync_suffix' => suffix,
+		                       'sync_host'   => sync_host,
+		                       'sync_port'   => sync_port,
+		                       'sync_pwd'    => sync_pwd,
+		                       'sync_suffix' => sync_suffix,
 		                       'last_update' => 0 }}
 
 		begin
@@ -86,7 +90,7 @@ class MPWConfig
 			@sync_host   = config['config']['sync_host']
 			@sync_port   = config['config']['sync_port']
 			@sync_pwd    = config['config']['sync_pwd']
-			@sync_suffix  = config['config']['sync_suffix']
+			@sync_suffix = config['config']['sync_suffix']
 			@last_update = config['config']['last_update'].to_i
 
 			if @key.empty? || @file_gpg.empty? 
@@ -104,6 +108,8 @@ class MPWConfig
 		return true
 	end
 
+	# Set the last update when there is a sync
+	# @rtrn: true is the file has been updated
 	def setLastUpdate()
 		config = {'config' => {'key'         => @key,
 		                       'lang'        => @lang,
diff --git a/mpw b/mpw
index f6660f6..e1f0569 100755
--- a/mpw
+++ b/mpw
@@ -101,9 +101,13 @@ cli = Cli.new(lang, config)
 # Setup a new config 
 if !check_error || !options[:setup].nil?
 	cli.setup(lang)
+end
+
+cli.decrypt()
+cli.sync()
 
 # Display the item's informations
-elsif not options[:display].nil?
+if not options[:display].nil?
 	cli.display(options[:display], options[:group], options[:type], options[:format])
 
 # Remove an item

From 445626c7a2abc49652df3652bd6b63e7a4484c0b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 25 Jan 2014 18:40:47 +0100
Subject: [PATCH 037/531] separate server translation

---
 i18n/server/en.yml | 25 +++++++++++++++++++++++++
 i18n/server/fr.yml | 25 +++++++++++++++++++++++++
 lib/Server.rb      | 36 ++++++++++++++++--------------------
 mpw-server         | 14 +++++++-------
 4 files changed, 73 insertions(+), 27 deletions(-)
 create mode 100644 i18n/server/en.yml
 create mode 100644 i18n/server/fr.yml

diff --git a/i18n/server/en.yml b/i18n/server/en.yml
new file mode 100644
index 0000000..b3fc13c
--- /dev/null
+++ b/i18n/server/en.yml
@@ -0,0 +1,25 @@
+---
+en:
+  option:
+    usage: "Usage"
+    config: "Specifie the configuration file"
+    checkconfig: "Check the configuration"
+    setup: "Setup a new configuration file"
+    help: "Show this message help"
+  checkconfig:
+    fail: "Checkconfig failed:!"
+    empty: "ERROR: an importe option is missing!"
+    datadir: "ERROR: le data directory doesn't exist!"
+  form:
+    setup:
+      title: "Serveur configuration" 
+      host: "IP listen: "
+      port: "Port listen: "
+      data_dir: "Data directory: "
+      timeout: "Timeout to second: "
+      not_valid: "ERROR: Impossible to write the configuration file!"
+  formats:
+    default: ! '%Y-%m-%d'
+    long: ! '%B %d, %Y'
+    short: ! '%b %d'
+    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/i18n/server/fr.yml b/i18n/server/fr.yml
new file mode 100644
index 0000000..962ac44
--- /dev/null
+++ b/i18n/server/fr.yml
@@ -0,0 +1,25 @@
+---
+fr:
+  option:
+    usage: "Utilisation"
+    config: "Spécifie le fichier de configuration"
+    checkconfig: "Vérifie le fichier de configuration"
+    setup: "Permet de générer un nouveau fichier de configuration"
+    help: "Affiche ce message d'aide"
+  checkconfig:
+    fail: "Le fichier de configuration est invalide!"
+    empty: "ERREUR: Une option importante est manquante!"
+    datadir: "ERREUR: Le répertoire des données n'existe pas!"
+  form:
+    setup:
+      title: "Configuration du serveur" 
+      host: "IP d'écoute: "
+      port: "Port d'écoute: "
+      data_dir: "Répertoire des données: "
+      timeout: "Timeout en seconde: "
+      not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
+  formats:
+    default: ! '%Y-%m-%d'
+    long: ! '%B %d, %Y'
+    short: ! '%b %d'
+    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/lib/Server.rb b/lib/Server.rb
index 83d6b19..7c617b8 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -261,37 +261,33 @@ class Server
 			@timeout  = config['config']['timeout'].to_i
 
 			if @host.empty? || @port <= 0 || @data_dir.empty? 
-				puts I18n.t('server.checkconfig.fail')
-				puts I18n.t('server.checkconfig.empty')
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.empty')
 				return false
 			end
 
 			if !Dir.exist?(@data_dir)
-				puts I18n.t('server.checkconfig.fail')
-				puts I18n.t('server.checkconfig.datadir')
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.datadir')
 				return false
 			end
 
 			if @log_file.nil? || @log_file.empty?
-				puts I18n.t('server.checkconfig.fail')
-				puts I18n.t('server.checkconfig.log_file_empty')
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.log_file_empty')
 				return false
-			#elsif !File.writable?(@log_file)
-			#	puts I18n.t('server.checkconfig.fail')
-			#	puts I18n.t('server.checkconfig.log_file_ro')
-			#	return false
 			else
 				begin
 					@log = Logger.new(@log_file)
 				rescue
-					puts I18n.t('server.checkconfig.fail')
-					puts I18n.t('server.checkconfig.log_file_create')
+					puts I18n.t('checkconfig.fail')
+					puts I18n.t('checkconfig.log_file_create')
 					return false
 				end
 			end
 
 		rescue Exception => e 
-			puts "#{I18n.t('server.checkconfig.fail')}\n#{e}"
+			puts "#{I18n.t('checkconfig.fail')}\n#{e}"
 			return false
 		end
 
@@ -302,13 +298,13 @@ class Server
 	# @args: file_config -> the configuration file
 	# @rtrn: true if le config file is create
 	def setup(file_config)
-		puts I18n.t('server.form.setup.title')
+		puts I18n.t('form.setup.title')
 		puts '--------------------'
-		host     = ask(I18n.t('server.form.setup.host')).to_s
-		port     = ask(I18n.t('server.form.setup.port')).to_s
-		data_dir = ask(I18n.t('server.form.setup.data_dir')).to_s
-		log_file = ask(I18n.t('server.form.setup.log_file')).to_s
-		timeout  = ask(I18n.t('server.form.setup.timeout')).to_s
+		host     = ask(I18n.t('form.setup.host')).to_s
+		port     = ask(I18n.t('form.setup.port')).to_s
+		data_dir = ask(I18n.t('form.setup.data_dir')).to_s
+		log_file = ask(I18n.t('form.setup.log_file')).to_s
+		timeout  = ask(I18n.t('form.setup.timeout')).to_s
 
 		config = {'config' => {'host'     => host,
 		                       'port'     => port,
@@ -321,7 +317,7 @@ class Server
 				file << config.to_yaml
 			end
 		rescue Exception => e 
-			puts "#{I18n.t('server.form.setup.not_valid')}\n#{e}"
+			puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
 			return false
 		end
 
diff --git a/mpw-server b/mpw-server
index 016cebf..07b42f5 100755
--- a/mpw-server
+++ b/mpw-server
@@ -15,34 +15,34 @@ require "#{APP_ROOT}/lib/Server.rb"
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/i18n/*.yml"]
+I18n.load_path = Dir["#{APP_ROOT}/i18n/server/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
 options = {}
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('server.option.usage')}: mpw-server -c CONFIG [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
 
-	opts.on("-c", "--config CONFIG", I18n.t('server.option.config')) do |config|
+	opts.on("-c", "--config CONFIG", I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on("-t", "--checkconfig", I18n.t('server.option.checkconfig')) do |b|
+	opts.on("-t", "--checkconfig", I18n.t('option.checkconfig')) do |b|
 		options[:checkconfig] = b
 	end
 
-	opts.on("-s", "--setup", I18n.t('server.option.setup')) do |b|
+	opts.on("-s", "--setup", I18n.t('option.setup')) do |b|
 		options[:setup] = b
 	end
 
-	opts.on("-h", "--help", I18n.t('server.option.help')) do |b|
+	opts.on("-h", "--help", I18n.t('option.help')) do |b|
 		puts opts
 		exit 0
 	end
 end.parse!
 
 if options[:config].nil? || options[:config].empty?
-	puts "#{I18n.t('server.option.usage')}: mpw-server -c CONFIG [options]"
+	puts "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
 	exit 2
 end
 

From 1400cf78346d92fc8381374723555f14a7763488 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 26 Jan 2014 11:39:53 +0100
Subject: [PATCH 038/531] separate cli translate

---
 i18n/cli/en.yml | 121 ++++++++++++++++++++++++++++++++++++++
 i18n/cli/fr.yml | 121 ++++++++++++++++++++++++++++++++++++++
 i18n/en.yml     | 148 -----------------------------------------------
 i18n/fr.yml     | 148 -----------------------------------------------
 lib/Cli.rb      | 150 ++++++++++++++++++++++++------------------------
 mpw             |  34 +++++------
 6 files changed, 334 insertions(+), 388 deletions(-)
 create mode 100644 i18n/cli/en.yml
 create mode 100644 i18n/cli/fr.yml
 delete mode 100644 i18n/en.yml
 delete mode 100644 i18n/fr.yml

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
new file mode 100644
index 0000000..dc76087
--- /dev/null
+++ b/i18n/cli/en.yml
@@ -0,0 +1,121 @@
+---
+en:
+  error:
+    config:
+      write: "Can't write the config file!"
+      check: "Checkconfig failed!"
+      key_bad_format: "The key string isn't in good format!"
+    delete:
+      id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
+    export:
+      write: "Can't export, unable to write in %{file}!"
+    gpg_file: 
+      decrypt: "Can't decrypt file!"
+      encrypt: "Can't encrypt the GPG file!"
+    import:
+      bad_format: "Can't import, the file is badly formated!"
+      read: "Can't import, unable to read %{file}!"
+    update:
+      name_empty: "You must define a name!"
+    sync: 
+      connection: "Connection fail!"
+      communication: "A communication problem with the server is appeared!"
+      no_data: "Nothing data!"
+      not_authorized: "You haven't the access to remote file!"
+      unknown: "An unknown error is occured!" 
+  option:
+    usage: "Usage"
+    show: "Search and show the items"
+    show_all: "Show all items"
+    update: "Update an item"
+    remove: "Delete an item"
+    group: "Search the items with specified group"
+    add: "Add an item"
+    config: "Specify the configuration file to use"
+    setup: "Create a new configuration file" 
+    protocol: "Select the items with the specified protocol"
+    export: "Export all items in a CSV file"
+    import: "Import item since a CSV file"
+    force: "Force an action"
+    format: "Change the display items format by an alternative format"
+    generate_password: "Generate a random password (default 8 characters)"
+    help: "Show this help message"
+  form:
+    add:
+      title: "Add a new item"
+      name: "Enter the name: "
+      group: "Enter the group (optional): "
+      server: "Enter the hostname or ip: "
+      protocol: "Enter the protocol of the connection (ssh, http, other): "
+      login: "Enter the login connection: "
+      password: "Enter the the password: "
+      port: "Enter the connection port (optional): "
+      comment: "Enter a comment (optional): "
+      valid: "Item has been added!"
+    delete:
+      ask: "Are you sure you want to remove the item %{id} ?"
+      valid: "The item %{id} has been removed!"
+      not_valid: "The item %{id} hasn't been removed, because it doesn't exist!"
+    import:
+      ask: "Are you sure you want to import this file %{file} ?"
+      valid: "The import is succesfull!"
+      not_valid: "No data to import!"
+    setup:
+      title: "Setup a new config file"
+      lang: "Choose your language (en, fr, ...): "
+      gpg_key: "Enter the GPG key: " 
+      gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
+      timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
+      valid: "The config file has been created!"
+    update:
+      title: "Update an item"
+      name: "Enter the name [%{name}]: "
+      group: "Enter the group [%{group}]: "
+      server: "Enter the hostname or ip [%{server}]: "
+      protocol: "Enter the protocol of the connection [%{protocol}]: "
+      login: "Enter the login connection [%{login}]: "
+      password: "Enter the the password: "
+      port: "Enter the connection port [%{port}]: "
+      comment: "Enter a comment [%{comment}]: "
+      valid: "Item has been updated!" 
+  interactive:
+    ask_password: "Password GPG: "
+    bad_password: "Bad password!"
+    goodbye: "Goodbye!"
+    unknown_command: "Unknown command!"
+    option:
+      title: "Help"
+      show: "Search and show the results"
+      group: "Change the group for the search"
+      add: "Add an item"
+      update: "Update an item"
+      remove: "Remove an item"
+      help: "Show this message"
+      quit: "Quit the software"
+  display:
+    comment: "Comment"
+    error: "ERROR"
+    gpg_password: "Password GPG: "
+    group: "Group"
+    login: "Login"
+    name: "Name"
+    nothing: "Nothing result!"
+    password: "Password"
+    port: "Port"
+    protocol: "Protocol"
+    server: "Server"
+  ssh:
+    option:
+      usage: "Usage"
+      login: "Change the login"
+      server: "Change the host or the ip"
+      port: "Change the port"
+      help: "Show this help message"
+    display:
+      connect: "Connection to:"
+      nothing: "Nothing result!"
+  formats:
+    default: ! '%Y-%m-%d'
+    long: ! '%B %d, %Y'
+    short: ! '%b %d'
+    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
new file mode 100644
index 0000000..e80cec7
--- /dev/null
+++ b/i18n/cli/fr.yml
@@ -0,0 +1,121 @@
+---
+fr:
+  error:
+    config:
+      write: "Impossible d'écrire le fichier de configuration!"
+      check: "Le fichier de configuration est invalide!"
+      key_bad_format: "La clé GPG est invalide!"
+    delete:
+      id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
+    export:
+      write: "Impossible d'exporter les données dans le fichier %{file}!"
+    gpg_file: 
+      decrypt: "Impossible de déchiffrer le fichier GPG!"
+      encrypt: "Impossible de chiffrer le fichier GPG!"
+    import:
+      bad_format: "Impossible d'importer le fichier car son format est incorrect!"
+      read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
+    update:
+      name_empty: "Vous devez définir un nom!"
+    sync: 
+      connection: "La connexion n'a pu être établie!"
+      communication: "Un problème de communication avec le serveur est apparu!"
+      no_data: "Aucune donnée!"
+      not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
+      unknown: "Une erreur inconnue est survenue!" 
+  option:
+    usage: "Utilisation"
+    show: "Recherche et affiche les éléments"
+    show_all: "Affiche tous les éléments"
+    update: "Met à jour un élément"
+    remove: "Supprime un élément"
+    group: "Recherche les éléments appartenant au groupe spécifié"
+    add: "Ajoute un élément"
+    config: "Spécifie le fichier de configuration à utiliser"
+    setup: "Création d'un nouveau fichier de configuration" 
+    protocol: "Sélectionne les éléments ayant le protocole spécifié"
+    export: "Exporte tous les éléments dans un fichier au format CSV"
+    import: "Importe des éléments depuis un fichier au format CSV"
+    force: "Force une action, l'action ne demandera pas de confirmation"
+    format: "Change le format d'affichage des éléments par un alternatif"
+    generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
+    help: "Affiche ce message d'aide"
+  form:
+    add:
+      title: "Ajout d'un nouvel élément"
+      name: "Entrez le nom: "
+      group: "Entrez le groupe (optionnel): "
+      server: "Entrez the hostname or ip: "
+      protocol: "Entrez le protocole de connexion (ssh, http, other): "
+      login: "Entrez l'identifiant de connexion: "
+      password: "Entrez le mot de passe: "
+      port: "Entrez le port de connexion (optionnel): "
+      comment: "Entrez un commentaire (optionnel): "
+      valid: "L'élément a bien été ajouté!"
+    delete:
+      ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?"
+      valid: "L'élément %{id} a bien été supprimé!"
+      not_valid: "L'élément %{id} n'a pu être supprimé, car il n'existe pas!"
+    import:
+      ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
+      valid: "L'import est un succès!"
+      not_valid: "Aucune donnée à importer!"
+    setup:
+      title: "Création d'un nouveau fichier de configuration"
+      lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
+      gpg_key: "Entrez la clé GPG: " 
+      gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
+      timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
+      valid: "Le fichier de configuration a bien été créé!"
+    update:
+      title: "Mis à jour d'un élément"
+      name: "Entrez le nom [%{name}]: "
+      group: "Entrez le groupe [%{group}]: "
+      server: "Entrez le nom de domaine ou l'ip du serveur [%{server}]: "
+      protocol: "Entrez le protocole de connexion [%{protocol}]: "
+      login: "Entrez votre identifiant de connexion [%{login}]: "
+      password: "Entrez le mot de passe: "
+      port: "Entrez un port de connexion [%{port}]: "
+      comment: "Entrez un commentaire [%{comment}]: "
+      valid: "L'élément a bien été mis à jour!" 
+  interactive:
+    ask_password: "Mot de passe GPG: "
+    bad_password: "Mauvais mot de passe!"
+    goodbye: "Au revoir!"
+    unknown_command: "Commande inconnue!"
+    option:
+      title: "Aide"
+      show: "Cherche et affiche les résulats"
+      group: "Change de groupe pour la recherche"
+      add: "Ajout un élément"
+      update: "Met à jour un élément"
+      remove: "Supprime un élément"
+      help: "Affiche ce message d'aide"
+      quit: "Quitte le programme"
+  display:
+    comment: "Commentaire"
+    error: "ERREUR"
+    gpg_password: "Mot de passe GPG: "
+    group: "Groupe"
+    login: "Identifiant"
+    name: "Nom"
+    nothing: "Aucun résultat!"
+    password: "Mot de passe"
+    port: "Port"
+    protocol: "Protocol"
+    server: "Serveur"
+  ssh:
+    option:
+      usage: "Utilisation"
+      login: "Change l'identifiant de connexion"
+      server: "Change le nom de domaine ou l'ip du serveur"
+      port: "Change le port de connexion"
+      help: "Affiche ce message d'aide"
+    display:
+      connect: "Connexion à:"
+      nothing: "Aucun résultat!"
+  formats:
+    default: ! '%Y-%m-%d'
+    long: ! '%B %d, %Y'
+    short: ! '%b %d'
+    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/i18n/en.yml b/i18n/en.yml
deleted file mode 100644
index 22f2f7b..0000000
--- a/i18n/en.yml
+++ /dev/null
@@ -1,148 +0,0 @@
----
-en:
-  error:
-    config:
-      write: "Can't write the config file!"
-      check: "Checkconfig failed!"
-      key_bad_format: "The key string isn't in good format!"
-    delete:
-      id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
-    export:
-      write: "Can't export, unable to write in %{file}!"
-    gpg_file: 
-      decrypt: "Can't decrypt file!"
-      encrypt: "Can't encrypt the GPG file!"
-    import:
-      bad_format: "Can't import, the file is badly formated!"
-      read: "Can't import, unable to read %{file}!"
-    update:
-      name_empty: "You must define a name!"
-    sync: 
-      connection: "Connection fail!"
-      communication: "A communication problem with the server is appeared!"
-      no_data: "Nothing data!"
-      not_authorized: "You haven't the access to remote file!"
-      unknown: "An unknown error is occured!" 
-  cli:
-    option:
-      usage: "Usage"
-      show: "Search and show the items"
-      show_all: "Show all items"
-      update: "Update an item"
-      remove: "Delete an item"
-      group: "Search the items with specified group"
-      add: "Add an item"
-      config: "Specify the configuration file to use"
-      setup: "Create a new configuration file" 
-      protocol: "Select the items with the specified protocol"
-      export: "Export all items in a CSV file"
-      import: "Import item since a CSV file"
-      force: "Force an action"
-      format: "Change the display items format by an alternative format"
-      generate_password: "Generate a random password (default 8 characters)"
-      help: "Show this help message"
-    form:
-      add:
-        title: "Add a new item"
-        name: "Enter the name: "
-        group: "Enter the group (optional): "
-        server: "Enter the hostname or ip: "
-        protocol: "Enter the protocol of the connection (ssh, http, other): "
-        login: "Enter the login connection: "
-        password: "Enter the the password: "
-        port: "Enter the connection port (optional): "
-        comment: "Enter a comment (optional): "
-        valid: "Item has been added!"
-      delete:
-        ask: "Are you sure you want to remove the item %{id} ?"
-        valid: "The item %{id} has been removed!"
-        not_valid: "The item %{id} hasn't been removed, because it doesn't exist!"
-      import:
-        ask: "Are you sure you want to import this file %{file} ?"
-        valid: "The import is succesfull!"
-        not_valid: "No data to import!"
-      setup:
-        title: "Setup a new config file"
-        lang: "Choose your language (en, fr, ...): "
-        gpg_key: "Enter the GPG key: " 
-        gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
-        timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
-        valid: "The config file has been created!"
-      update:
-        title: "Update an item"
-        name: "Enter the name [%{name}]: "
-        group: "Enter the group [%{group}]: "
-        server: "Enter the hostname or ip [%{server}]: "
-        protocol: "Enter the protocol of the connection [%{protocol}]: "
-        login: "Enter the login connection [%{login}]: "
-        password: "Enter the the password: "
-        port: "Enter the connection port [%{port}]: "
-        comment: "Enter a comment [%{comment}]: "
-        valid: "Item has been updated!" 
-    interactive:
-      ask_password: "Password GPG: "
-      bad_password: "Bad password!"
-      goodbye: "Goodbye!"
-      unknown_command: "Unknown command!"
-      option:
-        title: "Help"
-        show: "Search and show the results"
-        group: "Change the group for the search"
-        add: "Add an item"
-        update: "Update an item"
-        remove: "Remove an item"
-        help: "Show this message"
-        quit: "Quit the software"
-    display:
-      comment: "Comment"
-      error: "ERROR"
-      gpg_password: "Password GPG: "
-      group: "Group"
-      login: "Login"
-      name: "Name"
-      nothing: "Nothing result!"
-      password: "Password"
-      port: "Port"
-      protocol: "Protocol"
-      server: "Server"
-    sync:
-      not_connect: "The server connection fail!"	
-  ssh:
-    option:
-      usage: "Usage"
-      login: "Change the login"
-      server: "Change the host or the ip"
-      port: "Change the port"
-      help: "Show this help message"
-    display:
-      connect: "Connection to:"
-      nothing: "Nothing result!"
-  server:
-    error:
-      client:
-        unknown: "An unknown error is appeared!"
-        no_authorized: "You haven't access to remote file!"
-        no_data: "Nothing data has been sent!"
-    option:
-      usage: "Usage"
-      config: "Specifie the configuration file"
-      checkconfig: "Check the configuration"
-      setup: "Setup a new configuration file"
-      help: "Show this message help"
-    checkconfig:
-      fail: "Checkconfig failed:!"
-      empty: "ERROR: an importe option is missing!"
-      datadir: "ERROR: le data directory doesn't exist!"
-    form:
-      setup:
-        title: "Serveur configuration" 
-        host: "IP listen: "
-        port: "Port listen: "
-        data_dir: "Data directory: "
-        timeout: "Timeout to second: "
-        not_valid: "ERROR: Impossible to write the configuration file!"
-  formats:
-    default: ! '%Y-%m-%d'
-    long: ! '%B %d, %Y'
-    short: ! '%b %d'
-    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/i18n/fr.yml b/i18n/fr.yml
deleted file mode 100644
index b14aa99..0000000
--- a/i18n/fr.yml
+++ /dev/null
@@ -1,148 +0,0 @@
----
-fr:
-  error:
-    config:
-      write: "Impossible d'écrire le fichier de configuration!"
-      check: "Le fichier de configuration est invalide!"
-      key_bad_format: "La clé GPG est invalide!"
-    delete:
-      id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
-    export:
-      write: "Impossible d'exporter les données dans le fichier %{file}!"
-    gpg_file: 
-      decrypt: "Impossible de déchiffrer le fichier GPG!"
-      encrypt: "Impossible de chiffrer le fichier GPG!"
-    import:
-      bad_format: "Impossible d'importer le fichier car son format est incorrect!"
-      read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
-    update:
-      name_empty: "Vous devez définir un nom!"
-    sync: 
-      connection: "La connexion n'a pu être établie!"
-      communication: "Un problème de communication avec le serveur est apparu!"
-      no_data: "Aucune donnée!"
-      not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
-      unknown: "Une erreur inconnue est survenue!" 
-  cli:
-    option:
-      usage: "Utilisation"
-      show: "Recherche et affiche les éléments"
-      show_all: "Affiche tous les éléments"
-      update: "Met à jour un élément"
-      remove: "Supprime un élément"
-      group: "Recherche les éléments appartenant au groupe spécifié"
-      add: "Ajoute un élément"
-      config: "Spécifie le fichier de configuration à utiliser"
-      setup: "Création d'un nouveau fichier de configuration" 
-      protocol: "Sélectionne les éléments ayant le protocole spécifié"
-      export: "Exporte tous les éléments dans un fichier au format CSV"
-      import: "Importe des éléments depuis un fichier au format CSV"
-      force: "Force une action, l'action ne demandera pas de confirmation"
-      format: "Change le format d'affichage des éléments par un alternatif"
-      generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
-      help: "Affiche ce message d'aide"
-    form:
-      add:
-        title: "Ajout d'un nouvel élément"
-        name: "Entrez le nom: "
-        group: "Entrez le groupe (optionnel): "
-        server: "Entrez the hostname or ip: "
-        protocol: "Entrez le protocole de connexion (ssh, http, other): "
-        login: "Entrez l'identifiant de connexion: "
-        password: "Entrez le mot de passe: "
-        port: "Entrez le port de connexion (optionnel): "
-        comment: "Entrez un commentaire (optionnel): "
-        valid: "L'élément a bien été ajouté!"
-      delete:
-        ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?"
-        valid: "L'élément %{id} a bien été supprimé!"
-        not_valid: "L'élément %{id} n'a pu être supprimé, car il n'existe pas!"
-      import:
-        ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
-        valid: "L'import est un succès!"
-        not_valid: "Aucune donnée à importer!"
-      setup:
-        title: "Création d'un nouveau fichier de configuration"
-        lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
-        gpg_key: "Entrez la clé GPG: " 
-        gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
-        timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
-        valid: "Le fichier de configuration a bien été créé!"
-      update:
-        title: "Mis à jour d'un élément"
-        name: "Entrez le nom [%{name}]: "
-        group: "Entrez le groupe [%{group}]: "
-        server: "Entrez le nom de domaine ou l'ip du serveur [%{server}]: "
-        protocol: "Entrez le protocole de connexion [%{protocol}]: "
-        login: "Entrez votre identifiant de connexion [%{login}]: "
-        password: "Entrez le mot de passe: "
-        port: "Entrez un port de connexion [%{port}]: "
-        comment: "Entrez un commentaire [%{comment}]: "
-        valid: "L'élément a bien été mis à jour!" 
-    interactive:
-      ask_password: "Mot de passe GPG: "
-      bad_password: "Mauvais mot de passe!"
-      goodbye: "Au revoir!"
-      unknown_command: "Commande inconnue!"
-      option:
-        title: "Aide"
-        show: "Cherche et affiche les résulats"
-        group: "Change de groupe pour la recherche"
-        add: "Ajout un élément"
-        update: "Met à jour un élément"
-        remove: "Supprime un élément"
-        help: "Affiche ce message d'aide"
-        quit: "Quitte le programme"
-    display:
-      comment: "Commentaire"
-      error: "ERREUR"
-      gpg_password: "Mot de passe GPG: "
-      group: "Groupe"
-      login: "Identifiant"
-      name: "Nom"
-      nothing: "Aucun résultat!"
-      password: "Mot de passe"
-      port: "Port"
-      protocol: "Protocol"
-      server: "Serveur"
-    sync:
-      not_connect: "La connexion au serveur n'a pu être établie!"	
-  ssh:
-    option:
-      usage: "Utilisation"
-      login: "Change l'identifiant de connexion"
-      server: "Change le nom de domaine ou l'ip du serveur"
-      port: "Change le port de connexion"
-      help: "Affiche ce message d'aide"
-    display:
-      connect: "Connexion à:"
-      nothing: "Aucun résultat!"
-  server:
-    error:
-      client:
-        unknown: "Une erreur inconnue est apparue!"
-        no_authorized: "Vous n'avez pas les accès au fichier distant!"
-        no_data: "Aucune donnée n'a été transmise!"
-    option:
-      usage: "Utilisation"
-      config: "Spécifie le fichier de configuration"
-      checkconfig: "Vérifie le fichier de configuration"
-      setup: "Permet de générer un nouveau fichier de configuration"
-      help: "Affiche ce message d'aide"
-    checkconfig:
-      fail: "Le fichier de configuration est invalide!"
-      empty: "ERREUR: Une option importante est manquante!"
-      datadir: "ERREUR: Le répertoire des données n'existe pas!"
-    form:
-      setup:
-        title: "Configuration du serveur" 
-        host: "IP d'écoute: "
-        port: "Port d'écoute: "
-        data_dir: "Répertoire des données: "
-        timeout: "Timeout en seconde: "
-        not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
-  formats:
-    default: ! '%Y-%m-%d'
-    long: ! '%B %d, %Y'
-    short: ! '%b %d'
-    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/lib/Cli.rb b/lib/Cli.rb
index 03c7595..f848457 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -36,40 +36,40 @@ class Cli
 			if @config.sync_host.nil? || @config.sync_port.nil?
 				@sync.disable()
 			elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
-				puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
+				puts "#{I18n.t('sync.not_connect')}:\n#{@sync.error_msg}"
 			end
 		end
 		
 		begin
 			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
-				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 			end
 
 			if !@sync.update(File.open(@config.file_gpg).read)
-				puts "#{I18n.t('cli.display.error')}: #{@sync.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
 			end
 
 			if !@config.setLastUpdate()
-				puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@config.error_msg}"
 			end
 		rescue Exception => e
-			puts "#{I18n.t('cli.sync.error')}:\n#{e}"
+			puts "#{I18n.t('sync.error')}:\n#{e}"
 		end
 	end
 
 	# Create a new config file
 	# @args: lang -> the software language
 	def setup(lang)
-		puts I18n.t('cli.form.setup.title')
+		puts I18n.t('form.setup.title')
 		puts '--------------------'
-		language    = ask(I18n.t('cli.form.setup.lang', :lang => lang)).to_s
-		key         = ask(I18n.t('cli.form.setup.gpg_key')).to_s
-		file_gpg    = ask(I18n.t('cli.form.setup.gpg_file', :home => Dir.home())).to_s
-		timeout_pwd = ask(I18n.t('cli.form.setup.timeout')).to_s
-		sync_host   = ask(I18n.t('cli.form.setup.sync_host')).to_s
-		sync_port   = ask(I18n.t('cli.form.setup.sync_port')).to_s
-		sync_pwd    = ask(I18n.t('cli.form.setup.sync_pwd')).to_s
-		sync_suffix = ask(I18n.t('cli.form.setup.sync_suffix')).to_s
+		language    = ask(I18n.t('form.setup.lang', :lang => lang)).to_s
+		key         = ask(I18n.t('form.setup.gpg_key')).to_s
+		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => Dir.home())).to_s
+		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
+		sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
+		sync_port   = ask(I18n.t('form.setup.sync_port')).to_s
+		sync_pwd    = ask(I18n.t('form.setup.sync_pwd')).to_s
+		sync_suffix = ask(I18n.t('form.setup.sync_suffix')).to_s
 		
 		if !File.exist?("#{APP_ROOT}/i18n/#{language}.yml")
 			language= 'en'
@@ -82,13 +82,13 @@ class Cli
 		sync_suffix.empty? ? (sync_suffix = nil) : (sync_suffix = sync_suffix)
 
 		if @config.setup(key, language, file_gpg, timeout_pwd, sync_host, sync_port, sync_pwd, sync_suffix)
-			puts I18n.t('cli.form.setup.valid')
+			puts I18n.t('form.setup.valid')
 		else
-			puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
 		end
 
 		if not @config.checkconfig()
-			puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
 			exit 2
 		end
 	end
@@ -99,9 +99,9 @@ class Cli
 			@mpw = MPW.new(@config.file_gpg, @config.key)
 		end
 
-		@passwd = ask(I18n.t('cli.display.gpg_password')) {|q| q.echo = false}
+		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
 		if !@mpw.decrypt(@passwd)
-			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 			exit 2
 		end
 	end
@@ -121,7 +121,7 @@ class Cli
 				end
 			end
 		else
-			puts I18n.t('cli.display.nothing')
+			puts I18n.t('display.nothing')
 		end
 	end
 
@@ -130,14 +130,14 @@ class Cli
 	def displayFormat(item)
 		puts '--------------------'
 		puts "Id: #{item[MPW::ID]}"
-		puts "#{I18n.t('cli.display.name')}: #{item[MPW::NAME]}"
-		puts "#{I18n.t('cli.display.group')}: #{item[MPW::GROUP]}"
-		puts "#{I18n.t('cli.display.server')}: #{item[MPW::SERVER]}"
-		puts "#{I18n.t('cli.display.protocol')}: #{item[MPW::PROTOCOL]}"
-		puts "#{I18n.t('cli.display.login')}: #{item[MPW::LOGIN]}"
-		puts "#{I18n.t('cli.display.password')}: #{item[MPW::PASSWORD]}"
-		puts "#{I18n.t('cli.display.port')}: #{item[MPW::PORT]}"
-		puts "#{I18n.t('cli.display.comment')}: #{item[MPW::COMMENT]}"
+		puts "#{I18n.t('display.name')}: #{item[MPW::NAME]}"
+		puts "#{I18n.t('display.group')}: #{item[MPW::GROUP]}"
+		puts "#{I18n.t('display.server')}: #{item[MPW::SERVER]}"
+		puts "#{I18n.t('display.protocol')}: #{item[MPW::PROTOCOL]}"
+		puts "#{I18n.t('display.login')}: #{item[MPW::LOGIN]}"
+		puts "#{I18n.t('display.password')}: #{item[MPW::PASSWORD]}"
+		puts "#{I18n.t('display.port')}: #{item[MPW::PORT]}"
+		puts "#{I18n.t('display.comment')}: #{item[MPW::COMMENT]}"
 	end
 
 	# Display an item in the alternative format
@@ -159,26 +159,26 @@ class Cli
 	# Form to add a new item
 	def add()
 		row = Array.new()
-		puts I18n.t('cli.form.add.title')
+		puts I18n.t('form.add.title')
 		puts '--------------------'
-		name     = ask(I18n.t('cli.form.add.name')).to_s
-		group    = ask(I18n.t('cli.form.add.group')).to_s
-		server   = ask(I18n.t('cli.form.add.server')).to_s
-		protocol = ask(I18n.t('cli.form.add.protocol')).to_s
-		login    = ask(I18n.t('cli.form.add.login')).to_s
-		passwd   = ask(I18n.t('cli.form.add.password')).to_s
-		port     = ask(I18n.t('cli.form.add.port')).to_s
-		comment  = ask(I18n.t('cli.form.add.comment')).to_s
+		name     = ask(I18n.t('form.add.name')).to_s
+		group    = ask(I18n.t('form.add.group')).to_s
+		server   = ask(I18n.t('form.add.server')).to_s
+		protocol = ask(I18n.t('form.add.protocol')).to_s
+		login    = ask(I18n.t('form.add.login')).to_s
+		passwd   = ask(I18n.t('form.add.password')).to_s
+		port     = ask(I18n.t('form.add.port')).to_s
+		comment  = ask(I18n.t('form.add.comment')).to_s
 
 		if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
 			if @mpw.encrypt()
 				sync()
-				puts I18n.t('cli.form.add.valid')
+				puts I18n.t('form.add.valid')
 			else
-				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 			end
 		else
-			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 		end
 	end
 
@@ -188,29 +188,29 @@ class Cli
 		row = @mpw.searchById(id)
 
 		if not row.empty?
-			puts I18n.t('cli.form.update.title')
+			puts I18n.t('form.update.title')
 			puts '--------------------'
-			name     = ask(I18n.t('cli.form.update.name'    , :name => row[MPW::NAME])).to_s
-			group    = ask(I18n.t('cli.form.update.group'   , :group => row[MPW::GROUP])).to_s
-			server   = ask(I18n.t('cli.form.update.server'  , :server => row[MPW::SERVER])).to_s
-			protocol = ask(I18n.t('cli.form.update.protocol', :protocol => row[MPW::PROTOCOL])).to_s
-			login    = ask(I18n.t('cli.form.update.login'   , :login => row[MPW::LOGIN])).to_s
-			passwd   = ask(I18n.t('cli.form.update.password')).to_s
-			port     = ask(I18n.t('cli.form.update.port'    , :port => row[MPW::PORT])).to_s
-			comment  = ask(I18n.t('cli.form.update.comment' , :comment => row[MPW::COMMENT])).to_s
+			name     = ask(I18n.t('form.update.name'    , :name => row[MPW::NAME])).to_s
+			group    = ask(I18n.t('form.update.group'   , :group => row[MPW::GROUP])).to_s
+			server   = ask(I18n.t('form.update.server'  , :server => row[MPW::SERVER])).to_s
+			protocol = ask(I18n.t('form.update.protocol', :protocol => row[MPW::PROTOCOL])).to_s
+			login    = ask(I18n.t('form.update.login'   , :login => row[MPW::LOGIN])).to_s
+			passwd   = ask(I18n.t('form.update.password')).to_s
+			port     = ask(I18n.t('form.update.port'    , :port => row[MPW::PORT])).to_s
+			comment  = ask(I18n.t('form.update.comment' , :comment => row[MPW::COMMENT])).to_s
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt()
 					sync()
-					puts I18n.t('cli.form.update.valid')
+					puts I18n.t('form.update.valid')
 				else
-					puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 				end
 			else
-				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 			end
 		else
-			puts I18n.t('cli.display.nothing')
+			puts I18n.t('display.nothing')
 		end
 	end
 
@@ -224,12 +224,12 @@ class Cli
 			if result.length > 0
 				displayFormat(result)
 
-				confirm = ask("#{I18n.t('cli.form.delete.ask', :id => id)} (y/N) ").to_s
+				confirm = ask("#{I18n.t('form.delete.ask', :id => id)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
 					force = true
 				end
 			else
-				puts I18n.t('cli.display.nothing')
+				puts I18n.t('display.nothing')
 			end
 		end
 
@@ -237,12 +237,12 @@ class Cli
 			if @mpw.remove(id)
 				if @mpw.encrypt()
 					sync()
-					puts I18n.t('cli.form.delete.valid', :id => id)
+					puts I18n.t('form.delete.valid', :id => id)
 				else
-					puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 				end
 			else
-				puts I18n.t('cli.form.delete.not_valid')
+				puts I18n.t('form.delete.not_valid')
 			end
 		end
 	end
@@ -253,7 +253,7 @@ class Cli
 		if @mpw.export(file)
 			puts "The export in #{file} is succesfull!"
 		else
-			puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 		end
 
 	end
@@ -270,21 +270,21 @@ class Cli
 					displayFormat(r)
 				end
 
-				confirm = ask("#{I18n.t('cli.form.import.ask', :file => file)} (y/N) ").to_s
+				confirm = ask("#{I18n.t('form.import.ask', :file => file)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
 					force = true
 				end
 			else
-				puts I18n.t('cli.form.import.not_valid')
+				puts I18n.t('form.import.not_valid')
 			end
 		end
 
 		if force
 			if @mpw.import(file) && @mpw.encrypt()
 				sync()
-				puts I18n.t('cli.form.import.valid')
+				puts I18n.t('form.import.valid')
 			else
-				puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 			end
 		end
 	end
@@ -297,12 +297,12 @@ class Cli
 		while buf = Readline.readline('<mpw> ', true)
 
 			if @config.timeout_pwd < Time.now.to_i - last_access
-				passwd_confirm = ask(I18n.t('cli.interactive.ask_password')) {|q| q.echo = false}
+				passwd_confirm = ask(I18n.t('interactive.ask_password')) {|q| q.echo = false}
 
 				if @passwd.eql?(passwd_confirm)
 					last_access = Time.now.to_i
 				else
-					puts I18n.t('cli.interactive.bad_password')
+					puts I18n.t('interactive.bad_password')
 					next
 				end
 			else
@@ -333,21 +333,21 @@ class Cli
 					group = nil
 				end
 			when 'help', 'h', '?'
-				puts I18n.t('cli.interactive.option.title')
+				puts I18n.t('interactive.option.title')
 				puts '--------------------'
-				puts "display, show, d, s SEARCH    #{I18n.t('cli.interactive.option.show')}"
-				puts "group, g                      #{I18n.t('cli.interactive.option.group')}"
-				puts "add, a                        #{I18n.t('cli.interactive.option.add')}"
-				puts "update, u ID                  #{I18n.t('cli.interactive.option.update')}"
-				puts "remove, delete, r, d ID       #{I18n.t('cli.interactive.option.remove')}"
-				puts "help, h, ?                    #{I18n.t('cli.interactive.option.help')}"
-				puts "quit, exit, q                 #{I18n.t('cli.interactive.option.quit')}"
+				puts "display, show, d, s SEARCH    #{I18n.t('interactive.option.show')}"
+				puts "group, g                      #{I18n.t('interactive.option.group')}"
+				puts "add, a                        #{I18n.t('interactive.option.add')}"
+				puts "update, u ID                  #{I18n.t('interactive.option.update')}"
+				puts "remove, delete, r, d ID       #{I18n.t('interactive.option.remove')}"
+				puts "help, h, ?                    #{I18n.t('interactive.option.help')}"
+				puts "quit, exit, q                 #{I18n.t('interactive.option.quit')}"
 			when 'quit', 'exit', 'q'
-				puts I18n.t('cli.interactive.goodbye')
+				puts I18n.t('interactive.goodbye')
 				break
 			else
 				if !command[0].nil? && !command[0].empty?
-					puts I18n.t('cli.interactive.unknown_command')
+					puts I18n.t('interactive.unknown_command')
 				end
 			end
 
diff --git a/mpw b/mpw
index e1f0569..ef119a1 100755
--- a/mpw
+++ b/mpw
@@ -16,7 +16,7 @@ require "#{APP_ROOT}/lib/MPW.rb"
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/i18n/*.yml"]
+I18n.load_path = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
@@ -27,67 +27,67 @@ options[:group]  = nil
 options[:config] = nil
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('cli.option.usage')}: mpw [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
 
-	opts.on('-d', '--display [SEARCH]', I18n.t('cli.option.show')) do |search|
+	opts.on('-d', '--display [SEARCH]', I18n.t('option.show')) do |search|
 		search.nil? ? (options[:display]  = '')  : (options[:display] = search)
 	end
 
-	opts.on('-A', '--show-all', I18n.t('cli.option.show_all')) do
+	opts.on('-A', '--show-all', I18n.t('option.show_all')) do
 		options[:type]    = nil
 		options[:display] = ''
 	end
 
-	opts.on('-u', '--update ID', I18n.t('cli.option.update')) do |id|
+	opts.on('-u', '--update ID', I18n.t('option.update')) do |id|
 		options[:update] = id
 	end
 
-	opts.on('-r', '--remove ID', I18n.t('cli.option.remove')) do |id|
+	opts.on('-r', '--remove ID', I18n.t('option.remove')) do |id|
 		options[:remove] = id
 	end
 
-	opts.on('-g', '--group GROUP', I18n.t('cli.option.group')) do |group|
+	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
 		options[:group] = group
 	end
 
-	opts.on('-a', '--add', I18n.t('cli.option.add')) do
+	opts.on('-a', '--add', I18n.t('option.add')) do
 		options[:add] = true
 	end
 
-	opts.on('-c', '--config CONFIG', I18n.t('cli.option.config')) do |config|
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on('-S', '--setup', I18n.t('cli.option.setup')) do
+	opts.on('-S', '--setup', I18n.t('option.setup')) do
 		options[:setup] = true
 	end
 
-	opts.on('-p', '--protocol PROTOCOL', I18n.t('cli.option.protocol')) do |type|
+	opts.on('-p', '--protocol PROTOCOL', I18n.t('option.protocol')) do |type|
 		options[:type] = type
 	end
 
-	opts.on('-e', '--export FILE', I18n.t('cli.option.export')) do |file|
+	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
 		options[:export] = file
 	end
 
-	opts.on('-i', '--import FILE', I18n.t('cli.option.import')) do |file|
+	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
 		options[:import] = file
 	end
 
-	opts.on('-f', '--force', I18n.t('cli.option.force')) do
+	opts.on('-f', '--force', I18n.t('option.force')) do
 		options[:force] = true
 	end
 
-	opts.on('-F', '--format', I18n.t('cli.option.format')) do
+	opts.on('-F', '--format', I18n.t('option.format')) do
 		options[:format] = true
 	end
 
-	opts.on('-G', '--generate-password [LENGTH]', I18n.t('cli.option.generate_password')) do |length|
+	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
 		puts MPW::generatePassword(length)
 		exit 0
 	end
 
-	opts.on('-h', '--help', I18n.t('cli.option.help')) do
+	opts.on('-h', '--help', I18n.t('option.help')) do
 		puts opts
 		exit 0
 	end

From 174a0282382a104e7a612ad876c7d5ea13097e8f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 26 Jan 2014 12:13:32 +0100
Subject: [PATCH 039/531] fix bug last_update when sync no work

---
 lib/Cli.rb  | 33 ++++++++++++++++++---------------
 lib/Sync.rb | 21 +++++++++------------
 2 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index f848457..0d426a9 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -29,32 +29,35 @@ class Cli
 	end
 
 	# Sync the data with the server
+	# @rtnr: true if the synchro is finish
 	def sync()
 		if !defined?(@sync)
 			@sync = Sync.new()
 
-			if @config.sync_host.nil? || @config.sync_port.nil?
-				@sync.disable()
-			elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
-				puts "#{I18n.t('sync.not_connect')}:\n#{@sync.error_msg}"
+			if !@config.sync_host.nil? && !@config.sync_port.nil?
+				if !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
+					puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+				end
 			end
 		end
 		
 		begin
-			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
-				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
-			end
-
-			if !@sync.update(File.open(@config.file_gpg).read)
-				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
-			end
-
-			if !@config.setLastUpdate()
-				puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			if @sync.enable
+				if !@mpw.sync(@sync.get(@passwd), @config.last_update)
+					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+				elsif !@sync.update(File.open(@config.file_gpg).read)
+					puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+				elsif !@config.setLastUpdate()
+					puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+				else
+					return true
+				end
 			end
 		rescue Exception => e
-			puts "#{I18n.t('sync.error')}:\n#{e}"
+			puts "#{I18n.t('display.error')}: #{I18n.t('sync.error')}\n#{e}"
 		end
+
+		return false
 	end
 
 	# Create a new config file
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 2fa8ec7..9969ea5 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -13,15 +13,12 @@ require "#{APP_ROOT}/lib/MPW.rb"
 class Sync
 
 	attr_accessor :error_msg
+	attr_accessor :enable
 
 	# Constructor
 	def initialize()
 		@error_msg = nil
-	end
-
-	# Disable the sync
-	def disable()
-		@sync = false
+		@enable    = false
 	end
 
 	# Connect to server
@@ -37,21 +34,21 @@ class Sync
 		@suffix   = suffix
 
 		begin
-			@socket= TCPSocket.new(host, port)
-			@sync = true
+			@socket = TCPSocket.new(host, port)
+			@enable = true
 		rescue Exception => e
 			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-			@sync = false
+			@enable    = false
 		end
 
-		return @sync
+		return @enable
 	end
 
 	# Get data on server
 	# @args: gpg_password -> the gpg password
 	# @rtrn: nil if nothing data or error
 	def get(gpg_password)
-		if !@sync
+		if !@enable
 			return nil
 		end
 
@@ -91,7 +88,7 @@ class Sync
 	# @args: data -> the data to send on server
 	# @rtrn: false if there is a problem
 	def update(data)
-		if !@sync
+		if !@enable
 			return true
 		end
 
@@ -121,7 +118,7 @@ class Sync
 
 	# Close the connection
 	def close()
-		if !@sync
+		if !@enable
 			return
 		end
 

From 1cabe202e739e1ac2759f046125a9d2eb8b71448 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 26 Jan 2014 15:09:48 +0100
Subject: [PATCH 040/531] rename function

---
 lib/Cli.rb       | 12 ++++++------
 lib/MPW.rb       |  4 ++--
 lib/MPWConfig.rb |  2 +-
 lib/Server.rb    | 37 ++++++++++++++++---------------------
 mpw              |  2 +-
 mpw-ssh          |  2 +-
 6 files changed, 27 insertions(+), 32 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index 0d426a9..b6ed04d 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -23,8 +23,8 @@ class Cli
 		@config = config
 	end
 
-	# Destructor
-	def finalize()
+	# Close sync
+	def sync_close()
 		@sync.close()
 	end
 
@@ -47,7 +47,7 @@ class Cli
 					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 				elsif !@sync.update(File.open(@config.file_gpg).read)
 					puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
-				elsif !@config.setLastUpdate()
+				elsif !@config.set_last_update()
 					puts "#{I18n.t('display.error')}: #{@config.error_msg}"
 				else
 					return true
@@ -188,7 +188,7 @@ class Cli
 	# Update an item
 	# @args: id -> the item's id
 	def update(id)
-		row = @mpw.searchById(id)
+		row = @mpw.search_by_id(id)
 
 		if not row.empty?
 			puts I18n.t('form.update.title')
@@ -222,7 +222,7 @@ class Cli
 	#        force -> no resquest a validation
 	def remove(id, force=false)
 		if not force
-			result = @mpw.searchById(id)
+			result = @mpw.search_by_id(id)
 
 			if result.length > 0
 				displayFormat(result)
@@ -265,7 +265,7 @@ class Cli
 	# @args: file -> the import file
 	#        force -> no resquest a validation
 	def import(file, force=false)
-		result = @mpw.importPreview(file)
+		result = @mpw.import_preview(file)
 
 		if not force
 			if result.is_a?(Array) && !result.empty?
diff --git a/lib/MPW.rb b/lib/MPW.rb
index 0f2daf6..d69f334 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -105,7 +105,7 @@ class MPW
 	# Search in some csv data
 	# @args: id -> the id item
 	# @rtrn: a row with the resultat of the search
-	def searchById(id)
+	def search_by_id(id)
 		@data.each do |row|
 			if row[ID] == id
 				return row
@@ -243,7 +243,7 @@ class MPW
 	# Return a preview import 
 	# @args: file -> path to file import
 	# @rtrn: an array with the items to import, if there is an error return false
-	def importPreview(file)
+	def import_preview(file)
 		begin
 			result = Array.new()
 			id = 0
diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index 6362b4c..43fdc66 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -110,7 +110,7 @@ class MPWConfig
 
 	# Set the last update when there is a sync
 	# @rtrn: true is the file has been updated
-	def setLastUpdate()
+	def set_last_update()
 		config = {'config' => {'key'         => @key,
 		                       'lang'        => @lang,
 		                       'file_gpg'    => @file_gpg,
diff --git a/lib/Server.rb b/lib/Server.rb
index 7c617b8..489c8d5 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -9,11 +9,6 @@ require 'logger'
 require "#{APP_ROOT}/lib/MPW.rb"
 
 class Server
-	
-	INFO    = 0
-	WARNING = 1
-	ERROR   = 2
-	DEBUG   = 3
 
 	attr_accessor :error_msg
 
@@ -37,37 +32,37 @@ class Server
 				@log.info("#{client.peeraddr[3]} is connected")
 
 				while true do
-					msg = getClientMessage(client)
+					msg = get_client_msg(client)
 
 					if !msg
 						next
 					end
 					
 					if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
-						closeConnection(client)
+						close_connection(client)
 						next
 					end
 
 					case msg['action']
 					when 'get'
 						@log.debug("#{client.peeraddr[3]} GET gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-						client.puts getFile(msg)
+						client.puts get_file(msg)
 					when 'update'
 						@log.debug("#{client.peeraddr[3]} UPDATE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-						client.puts updateFile(msg)
+						client.puts update_file(msg)
 					when 'delete'
 						@log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-						client.puts deleteFile(msg)
+						client.puts delete_file(msg)
 					when 'close'
 						@log.info("#{client.peeraddr[3]} is disconnected")
-						closeConnection(client)
+						close_connection(client)
 					else
 						@log.warning("#{client.peeraddr[3]} is disconnected for unkwnow command")
 						send_msg = {:action      => 'unknown',
 						            :gpg_key     => msg['gpg_key'],
 						            :error       => 'server.error.client.unknown'}
 						client.puts send_msg 
-						closeConnection(client)
+						close_connection(client)
 					end
 				end
 			end
@@ -77,7 +72,7 @@ class Server
 	# Get a gpg file
 	# @args: msg -> message puts by the client
 	# @rtrn: json message
-	def getFile(msg)
+	def get_file(msg)
 		gpg_key = msg['gpg_key'].sub('@', '_')
 
 		if msg['suffix'].nil? || msg['suffix'].empty?
@@ -92,7 +87,7 @@ class Server
 			hash        = gpg_data['gpg']['hash']
 			data        = gpg_data['gpg']['data']
 
-			if isAuthorized?(msg['password'], salt, hash)
+			if is_authorized?(msg['password'], salt, hash)
 				send_msg = {:action      => 'get',
 				            :gpg_key     => msg['gpg_key'],
 				            :data        => data,
@@ -115,7 +110,7 @@ class Server
 	# Update a file
 	# @args: msg -> message puts by the client
 	# @rtrn: json message
-	def updateFile(msg)
+	def update_file(msg)
 		gpg_key = msg['gpg_key'].sub('@', '_')
 		data    = msg['data']
 
@@ -143,7 +138,7 @@ class Server
 			hash = Digest::SHA256.hexdigest(salt + msg['password'])
 		end
 
-		if isAuthorized?(msg['password'], salt, hash)
+		if is_authorized?(msg['password'], salt, hash)
 			begin
 				config = {'gpg' => {'salt'        => salt,
 				                    'hash'        => hash,
@@ -173,7 +168,7 @@ class Server
 	# Remove a gpg file
 	# @args: msg -> message puts by the client
 	# @rtrn: json message
-	def deleteFile(msg)
+	def delete_file(msg)
 		gpg_key = msg['gpg_key'].sub('@', '_')
 
 		if msg['suffix'].nil? || msg['suffix'].empty?
@@ -194,7 +189,7 @@ class Server
 		salt      = gpg_data['gpg']['salt']
 		hash      = gpg_data['gpg']['hash']
 
-		if isAuthorized?(msg['password'], salt, hash)
+		if is_authorized?(msg['password'], salt, hash)
 			begin
 				File.unlink(file_gpg)
 
@@ -220,7 +215,7 @@ class Server
 	#        salt -> the salt
 	#        hash -> the hash of the password with the salt
 	# @rtrn: true is is good, else false
-	def isAuthorized?(password, salt, hash)
+	def is_authorized?(password, salt, hash)
 		if hash == Digest::SHA256.hexdigest(salt + password)
 			return true
 		else
@@ -231,7 +226,7 @@ class Server
 	# Get message to client
 	# @args: client -> client connection
 	# @rtrn: array of the json string, or false if isn't json message
-	def getClientMessage(client)
+	def get_client_msg(client)
 		begin
 			msg = client.gets
 			return JSON.parse(msg)
@@ -243,7 +238,7 @@ class Server
 
 	# Close the client connection
 	# @args: client -> client connection
-	def closeConnection(client)
+	def close_connection(client)
 			client.puts "Closing the connection. Bye!"
 			client.close
 	end
diff --git a/mpw b/mpw
index ef119a1..b7ebf60 100755
--- a/mpw
+++ b/mpw
@@ -140,6 +140,6 @@ else
 	end
 end
 
-cli = nil
+cli.sync_close()
 
 exit 0
diff --git a/mpw-ssh b/mpw-ssh
index aa0e80a..83f835e 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -66,6 +66,6 @@ else
 	cli.ssh(search)
 end
 
-cli = nil
+cli.sync_close()
 
 exit 0

From c76c2db2be80ccbc98a0bcd75f111a4f5c117880 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 26 Jan 2014 17:26:02 +0100
Subject: [PATCH 041/531] fix log disconnected

---
 lib/Server.rb | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 489c8d5..1f995b6 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -38,7 +38,13 @@ class Server
 						next
 					end
 					
+					if !msg['action'].nil? && msg['action'] == 'close'
+						@log.info("#{client.peeraddr[3]} is disconnected")
+						close_connection(client)
+					end
+
 					if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
+						@log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
 						close_connection(client)
 						next
 					end
@@ -53,11 +59,8 @@ class Server
 					when 'delete'
 						@log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
 						client.puts delete_file(msg)
-					when 'close'
-						@log.info("#{client.peeraddr[3]} is disconnected")
-						close_connection(client)
 					else
-						@log.warning("#{client.peeraddr[3]} is disconnected for unkwnow command")
+						@log.warning("#{client.peeraddr[3]} is disconnected because unkwnow command")
 						send_msg = {:action      => 'unknown',
 						            :gpg_key     => msg['gpg_key'],
 						            :error       => 'server.error.client.unknown'}

From 52c1554e9056ec927a9baf499bc6dda51b43fadf Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 26 Jan 2014 17:48:24 +0100
Subject: [PATCH 042/531] add translate

---
 i18n/cli/en.yml    | 4 ++++
 i18n/cli/fr.yml    | 4 ++++
 i18n/server/en.yml | 1 +
 i18n/server/fr.yml | 1 +
 4 files changed, 10 insertions(+)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index dc76087..bf8b9fc 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -66,6 +66,10 @@ en:
       gpg_key: "Enter the GPG key: " 
       gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
       timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
+      sync_host: "Synchronization server: "
+      sync_port: "Port of the synchronization server: "
+      sync_pwd: "Password for the synchronization: "
+      sync_suffix: "Suffix for the synchronization (optionnal): "
       valid: "The config file has been created!"
     update:
       title: "Update an item"
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index e80cec7..0936cdb 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -66,6 +66,10 @@ fr:
       gpg_key: "Entrez la clé GPG: " 
       gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
       timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
+      sync_host: "Serveur de synchronisation: "
+      sync_port: "Port du serveur de synchronisation: "
+      sync_pwd: "Mot de passe pour la synchronisation: "
+      sync_suffix: "Suffix pour la synchronisation (optionnel): "
       valid: "Le fichier de configuration a bien été créé!"
     update:
       title: "Mis à jour d'un élément"
diff --git a/i18n/server/en.yml b/i18n/server/en.yml
index b3fc13c..df88dfe 100644
--- a/i18n/server/en.yml
+++ b/i18n/server/en.yml
@@ -17,6 +17,7 @@ en:
       port: "Port listen: "
       data_dir: "Data directory: "
       timeout: "Timeout to second: "
+      log_file: "Log file path: "
       not_valid: "ERROR: Impossible to write the configuration file!"
   formats:
     default: ! '%Y-%m-%d'
diff --git a/i18n/server/fr.yml b/i18n/server/fr.yml
index 962ac44..667757c 100644
--- a/i18n/server/fr.yml
+++ b/i18n/server/fr.yml
@@ -16,6 +16,7 @@ fr:
       host: "IP d'écoute: "
       port: "Port d'écoute: "
       data_dir: "Répertoire des données: "
+      log_file: "Chemin du ficier de log: "
       timeout: "Timeout en seconde: "
       not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
   formats:

From 0c89715e3a1cd3a0ff3cfb3851db04f61800e2b8 Mon Sep 17 00:00:00 2001
From: adrien <adrien.waksberg@believedigital.com>
Date: Mon, 27 Jan 2014 08:38:34 +0100
Subject: [PATCH 043/531] fix bug

---
 mpw-ssh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/mpw-ssh b/mpw-ssh
index 83f835e..39fe1b3 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -15,7 +15,7 @@ require "#{APP_ROOT}/lib/CliSSH.rb"
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/i18n/*.yml"]
+I18n.load_path = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
@@ -63,6 +63,8 @@ elsif ARGV.length < 1
 	puts "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
 	exit 2
 else
+	cli.decrypt()
+	cli.sync()
 	cli.ssh(search)
 end
 

From 074643a1677e58a8b8aa55b50960f779a48728d5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 27 Jan 2014 18:27:37 +0100
Subject: [PATCH 044/531] add method salt

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

diff --git a/lib/Server.rb b/lib/Server.rb
index 1f995b6..3baa22f 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -6,8 +6,6 @@ require 'highline/import'
 require 'digest'
 require 'logger'
 
-require "#{APP_ROOT}/lib/MPW.rb"
-
 class Server
 
 	attr_accessor :error_msg
@@ -137,7 +135,7 @@ class Server
 			hash      = gpg_data['gpg']['hash']
 
 		else
-			salt = MPW.generatePassword(4)
+			salt = salt()
 			hash = Digest::SHA256.hexdigest(salt + msg['password'])
 		end
 
@@ -321,4 +319,18 @@ class Server
 
 		return true
 	end
+
+	# Generate a random salt
+	# @args: length -> the length salt
+	# @rtrn: a random string
+	def salt(length=4)
+		if length.to_i <= 0 || length.to_i > 16
+			length = 4
+		else
+			length = length.to_i
+		end
+
+		return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
+	end
+	
 end

From 76c5ab07c7d543189b0a18273bded20a35954a89 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 27 Jan 2014 20:19:38 +0100
Subject: [PATCH 045/531] rename function

---
 lib/Cli.rb  |  2 +-
 lib/MPW.rb  | 21 ++++++++++-----------
 lib/Sync.rb |  2 +-
 mpw         |  2 +-
 4 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index b6ed04d..74f966b 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -54,7 +54,7 @@ class Cli
 				end
 			end
 		rescue Exception => e
-			puts "#{I18n.t('display.error')}: #{I18n.t('sync.error')}\n#{e}"
+			puts "#{I18n.t('display.error')}: #{e}"
 		end
 
 		return false
diff --git a/lib/MPW.rb b/lib/MPW.rb
index d69f334..366f657 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -147,15 +147,15 @@ class MPW
 		row_update       = Array.new()
 		row_update[DATE] = Time.now.to_i
 
-		id.nil?	      || id.empty?       ? (row_update[ID]       = MPW.generatePassword(16)) : (row_update[ID]       = id)
-		name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])                : (row_update[NAME]     = name)
-		group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])               : (row_update[GROUP]    = group)
-		server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])              : (row_update[SERVER]   = server)
-		protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL])            : (row_update[PROTOCOL] = protocol)
-		login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])               : (row_update[LOGIN]    = login)
-		passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD])            : (row_update[PASSWORD] = passwd)
-		port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])                : (row_update[PORT]     = port)
-		comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])             : (row_update[COMMENT]  = comment)
+		id.nil?	      || id.empty?       ? (row_update[ID]       = MPW.password(16)) : (row_update[ID]       = id)
+		name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])        : (row_update[NAME]     = name)
+		group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])       : (row_update[GROUP]    = group)
+		server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])      : (row_update[SERVER]   = server)
+		protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL])    : (row_update[PROTOCOL] = protocol)
+		login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])       : (row_update[LOGIN]    = login)
+		passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD])    : (row_update[PASSWORD] = passwd)
+		port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])        : (row_update[PORT]     = port)
+		comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])     : (row_update[COMMENT]  = comment)
 		
 		row_update[NAME].nil?     ? (row_update[NAME]     = row_update[NAME])     : (row_update[NAME]     = row_update[NAME].force_encoding('ASCII-8BIT'))
 		row_update[GROUP].nil?    ? (row_update[GROUP]    = row_update[GROUP])    : (row_update[GROUP]    = row_update[GROUP].force_encoding('ASCII-8BIT'))
@@ -312,7 +312,7 @@ class MPW
 	# Generate a random password
 	# @args: length -> the length password
 	# @rtrn: a random string
-	def self.generatePassword(length=8)
+	def self.password(length=8)
 		if length.to_i <= 0
 			length = 8
 		else
@@ -328,5 +328,4 @@ class MPW
 
 		return result
 	end
-	
 end
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 9969ea5..e85b0ce 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -64,7 +64,7 @@ class Sync
 			@error_msg = I18n.t('error.sync.communication')
 			return nil
 		elsif msg['error'].nil?
-			tmp_file = "/tmp/mpw-#{MPW.generatePassword()}.gpg"
+			tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
 			File.open(tmp_file, 'w') do |file|
 				file << msg['data']
 			end
diff --git a/mpw b/mpw
index b7ebf60..ff27575 100755
--- a/mpw
+++ b/mpw
@@ -83,7 +83,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
-		puts MPW::generatePassword(length)
+		puts MPW::password(length)
 		exit 0
 	end
 

From f4e492079578084911f8f9511fb0bd87cb7d1b3d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 28 Jan 2014 20:33:00 +0100
Subject: [PATCH 046/531] version 1.1.0

---
 CHANGELOG | 7 +++++++
 README.md | 1 +
 VERSION   | 2 +-
 3 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 CHANGELOG

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..21cf7ae
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,7 @@
+= CHANGELOG =
+
+== v1.1.0 ==
+
+* Add sync with MPW Server
+* Add MPW Server
+* Fix minors bugs
diff --git a/README.md b/README.md
index 95dba72..277d937 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
 # Manage your passwords!
 
 MPW is a little software which stores your passwords in an GPG encrypted file.
+MPW can sync your password with a MPW Server.
 
 # Installation
 
diff --git a/VERSION b/VERSION
index 3eebe6a..e532f18 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.0 - dev
+1.1.0 - stable

From 00aea4f4069db9e4afab8c76822932286796d467 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 18:40:37 +0100
Subject: [PATCH 047/531] version dev

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

diff --git a/VERSION b/VERSION
index e532f18..4482064 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.0 - stable
+1.2.0 - dev

From 47ee15adb92c05a0f7386a656360de29d1a2a9e4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 19:12:24 +0100
Subject: [PATCH 048/531] factorize the code

---
 lib/MPW.rb | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 366f657..f82fcc2 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -88,9 +88,9 @@ class MPW
 		search = search.force_encoding('ASCII-8BIT')
 
 		@data.each do |row|
-			row[NAME].nil?    ? (name    = nil) : (name    = row[NAME].downcase)
-			row[SERVER].nil?  ? (server  = nil) : (server  = row[SERVER].downcase)
-			row[COMMENT].nil? ? (comment = nil) : (comment = row[COMMENT].downcase)
+			name    = row[NAME].nil?    ? nil : row[NAME].downcase
+			server  = row[SERVER].nil?  ? nil : row[SERVER].downcase
+			comment = row[COMMENT].nil? ? nil : row[COMMENT].downcase
 
 			if name =~ /^.*#{search}.*$/  || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/ 
 				if (protocol.nil? || protocol.eql?(row[PROTOCOL])) && (group.nil? || group.eql?(row[GROUP]))
@@ -147,23 +147,23 @@ class MPW
 		row_update       = Array.new()
 		row_update[DATE] = Time.now.to_i
 
-		id.nil?	      || id.empty?       ? (row_update[ID]       = MPW.password(16)) : (row_update[ID]       = id)
-		name.nil?     || name.empty?     ? (row_update[NAME]     = row[NAME])        : (row_update[NAME]     = name)
-		group.nil?    || group.empty?    ? (row_update[GROUP]    = row[GROUP])       : (row_update[GROUP]    = group)
-		server.nil?   || server.empty?   ? (row_update[SERVER]   = row[SERVER])      : (row_update[SERVER]   = server)
-		protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL])    : (row_update[PROTOCOL] = protocol)
-		login.nil?    || login.empty?    ? (row_update[LOGIN]    = row[LOGIN])       : (row_update[LOGIN]    = login)
-		passwd.nil?   || passwd.empty?   ? (row_update[PASSWORD] = row[PASSWORD])    : (row_update[PASSWORD] = passwd)
-		port.nil?     || port.empty?     ? (row_update[PORT]     = row[PORT])        : (row_update[PORT]     = port)
-		comment.nil?  || comment.empty?  ? (row_update[COMMENT]  = row[COMMENT])     : (row_update[COMMENT]  = comment)
+		row_update[ID]       = id.nil?       || id.empty?       ? MPW.password(16) : id
+		row_update[NAME]     = name.nil?     || name.empty?     ? row[NAME]        : name
+		row_update[GROUP]    = group.nil?    || group.empty?    ? row[GROUP]       : group
+		row_update[SERVER]   = server.nil?   || server.empty?   ? row[SERVER]      : server
+		row_update[PROTOCOL] = protocol.nil? || protocol.empty? ? row[PROTOCOL]    : protocol
+		row_update[LOGIN]    = login.nil?    || login.empty?    ? row[LOGIN]       : login
+		row_update[PASSWORD] = passwd.nil?   || passwd.empty?   ? row[PASSWORD]    : passwd
+		row_update[PORT]     = port.nil?     || port.empty?     ? row[PORT]        : port
+		row_update[COMMENT]  = comment.nil?  || comment.empty?  ? row[COMMENT]     : comment
 		
-		row_update[NAME].nil?     ? (row_update[NAME]     = row_update[NAME])     : (row_update[NAME]     = row_update[NAME].force_encoding('ASCII-8BIT'))
-		row_update[GROUP].nil?    ? (row_update[GROUP]    = row_update[GROUP])    : (row_update[GROUP]    = row_update[GROUP].force_encoding('ASCII-8BIT'))
-		row_update[SERVER].nil?   ? (row_update[SERVER]   = row_update[SERVER])   : (row_update[SERVER]   = row_update[SERVER].force_encoding('ASCII-8BIT'))
-		row_update[PROTOCOL].nil? ? (row_update[PROTOCOL] = row_update[PROTOCOL]) : (row_update[PROTOCOL] = row_update[PROTOCOL].force_encoding('ASCII-8BIT'))
-		row_update[LOGIN].nil?    ? (row_update[LOGIN]    = row_update[LOGIN])    : (row_update[LOGIN]    = row_update[LOGIN].force_encoding('ASCII-8BIT'))
-		row_update[PASSWORD].nil? ? (row_update[PASSWORD] = row_update[PASSWORD]) : (row_update[PASSWORD] = row_update[PASSWORD].force_encoding('ASCII-8BIT'))
-		row_update[COMMENT].nil?  ? (row_update[COMMENT]  = row_update[COMMENT])  : (row_update[COMMENT]  = row_update[COMMENT].force_encoding('ASCII-8BIT'))
+		row_update[] = row_update[NAME].nil?     ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
+		row_update[] = row_update[GROUP].nil?    ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
+		row_update[] = row_update[SERVER].nil?   ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
+		row_update[] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
+		row_update[] = row_update[LOGIN].nil?    ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
+		row_update[] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
+		row_update[] = row_update[COMMENT].nil?  ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
 
 		if row_update[NAME].nil? || row_update[NAME].empty?
 			@error_msg = I18n.t('error.update.name_empty')

From 7a50c4cf1c98abbc19847d9bc8d6dd13f62e67bb Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 19:18:36 +0100
Subject: [PATCH 049/531] factorize the code

---
 lib/Cli.rb       | 10 +++++-----
 lib/CliSSH.rb    |  6 +++---
 lib/MPWConfig.rb |  2 +-
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/lib/Cli.rb b/lib/Cli.rb
index 74f966b..152f3bf 100644
--- a/lib/Cli.rb
+++ b/lib/Cli.rb
@@ -79,10 +79,10 @@ class Cli
 		end
 		I18n.locale = language.to_sym
 
-		sync_host.empty?   ? (sync_host = nil)   : (sync_host   = sync_host)
-		sync_port.empty?   ? (sync_port = nil)   : (sync_port   = sync_port.to_i)
-		sync_pwd.empty?    ? (sync_pwd = nil)    : (sync_pwd    = sync_pwd)
-		sync_suffix.empty? ? (sync_suffix = nil) : (sync_suffix = sync_suffix)
+		sync_host   = sync_host.empty?   ? nil : sync_host
+		sync_port   = sync_port.empty?   ? nil : sync_port.to_i
+		sync_pwd    = sync_pwd.empty?    ? nil : sync_pwd
+		sync_suffix = sync_suffix.empty? ? nil : sync_suffix
 
 		if @config.setup(key, language, file_gpg, timeout_pwd, sync_host, sync_port, sync_pwd, sync_suffix)
 			puts I18n.t('form.setup.valid')
@@ -146,7 +146,7 @@ class Cli
 	# Display an item in the alternative format
 	# @args: item -> an array with the item information
 	def displayFormatAlt(item)
-		item[MPW::PORT].nil? ? (port = '') : (port = ":#{item[MPW::PORT]}")
+		port = item[MPW::PORT].nil? ? '' : ":#{item[MPW::PORT]}"
 
 		if item[MPW::PASSWORD].nil? || item[MPW::PASSWORD].empty?
 			if item[MPW::LOGIN].include('@')
diff --git a/lib/CliSSH.rb b/lib/CliSSH.rb
index 7fba1ad..ebcea04 100644
--- a/lib/CliSSH.rb
+++ b/lib/CliSSH.rb
@@ -16,9 +16,9 @@ class CliSSH < Cli
 
 		if result.length > 0
 			result.each do |r|
-				@server.nil? ? (server = r[MPW::SERVER]) : (server = @server)
-				@port.nil?   ? (port   = r[MPW::PORT])   : (port   = @port)
-				@login.nil?  ? (login  = r[MPW::LOGIN])  : (login  = @login)
+				server = @server.nil? ? r[MPW::SERVER] : @server
+				port   = @port.nil?   ? r[MPW::PORT]   : @port
+				login  = @login.nil?  ? r[MPW::LOGIN]  : @login
 
 				passwd = r[MPW::PASSWORD]
 
diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
index 43fdc66..bce49e6 100644
--- a/lib/MPWConfig.rb
+++ b/lib/MPWConfig.rb
@@ -54,7 +54,7 @@ class MPWConfig
 			file_gpg = "#{Dir.home()}/.mpw.gpg"
 		end
 
-		timeout_pwd.empty? ? (timeout_pwd = 60) : (timeout_pwd = timeout_pwd.to_i)
+		timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
 
 		config = {'config' => {'key'         => key,
 		                       'lang'        => lang,

From 96fe71e07d6c67f72492cb8a5ef036cc23da3b29 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 20:49:39 +0100
Subject: [PATCH 050/531] add module MPW

---
 MPW/Config.rb             | 140 ++++++++++++++++
 MPW/MPW.rb                | 335 ++++++++++++++++++++++++++++++++++++++
 MPW/Sync/MPW.rb           | 130 +++++++++++++++
 {lib => MPW/UI}/Cli.rb    |  53 +++---
 {lib => MPW/UI}/CliSSH.rb |  10 +-
 lib/MPW.rb                | 331 -------------------------------------
 lib/MPWConfig.rb          | 136 ----------------
 lib/Sync.rb               | 128 ---------------
 mpw                       |   6 +-
 mpw-ssh                   |   5 +-
 10 files changed, 642 insertions(+), 632 deletions(-)
 create mode 100644 MPW/Config.rb
 create mode 100644 MPW/MPW.rb
 create mode 100644 MPW/Sync/MPW.rb
 rename {lib => MPW/UI}/Cli.rb (86%)
 rename {lib => MPW/UI}/CliSSH.rb (74%)
 delete mode 100644 lib/MPW.rb
 delete mode 100644 lib/MPWConfig.rb
 delete mode 100644 lib/Sync.rb

diff --git a/MPW/Config.rb b/MPW/Config.rb
new file mode 100644
index 0000000..74d309c
--- /dev/null
+++ b/MPW/Config.rb
@@ -0,0 +1,140 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+module MPW
+
+	require 'rubygems'
+	require 'yaml'
+	require 'i18n'
+	
+	class Config
+		
+		attr_accessor :error_msg
+	
+		attr_accessor :key
+		attr_accessor :lang
+		attr_accessor :file_gpg
+		attr_accessor :timeout_pwd
+		attr_accessor :last_update
+		attr_accessor :sync_host
+		attr_accessor :sync_port
+		attr_accessor :sync_pwd
+		attr_accessor :sync_suffix
+		attr_accessor :last_update
+	
+		# Constructor
+		# @args: file_config -> the specify config file
+		def initialize(file_config=nil)
+			@error_msg   = nil
+			@file_config = "#{Dir.home()}/.mpw.cfg"
+	
+			if !file_config.nil? && !file_config.empty?
+				@file_config = file_config
+			end
+		end
+	
+		# Create a new config file
+		# @args: key -> the gpg key to encrypt
+		#        lang -> the software language
+		#        file_gpg -> the file who is encrypted
+		#        timeout_pwd -> time to save the password
+		#        sync_host -> the server host for synchronization
+		#        sync_port -> the server port for synchronization
+		#        sync_pwd -> the password for synchronization
+		#        sync_suffix -> the suffix file (optionnal) 
+		# @rtrn: true if le config file is create
+		def setup(key, lang, file_gpg, timeout_pwd, sync_host=nil, sync_port=nil, sync_pwd=nil, sync_suffix=nil)
+	
+			if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+				@error_msg = I18n.t('error.config.key_bad_format')
+				return false
+			end
+			
+			if file_gpg.empty?
+				file_gpg = "#{Dir.home()}/.mpw.gpg"
+			end
+	
+			timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
+	
+			config = {'config' => {'key'         => key,
+			                       'lang'        => lang,
+			                       'file_gpg'    => file_gpg,
+			                       'timeout_pwd' => timeout_pwd,
+			                       'sync_host'   => sync_host,
+			                       'sync_port'   => sync_port,
+			                       'sync_pwd'    => sync_pwd,
+			                       'sync_suffix' => sync_suffix,
+			                       'last_update' => 0 }}
+	
+			begin
+				File.open(@file_config, 'w') do |file|
+					file << config.to_yaml
+				end
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+				return false
+			end
+	
+			return true
+		end
+	
+		# Check the config file
+		# @rtrn: true if the config file is correct
+		def checkconfig()
+			begin
+				config = YAML::load_file(@file_config)
+				@key         = config['config']['key']
+				@lang        = config['config']['lang']
+				@file_gpg    = config['config']['file_gpg']
+				@timeout_pwd = config['config']['timeout_pwd'].to_i
+				@sync_host   = config['config']['sync_host']
+				@sync_port   = config['config']['sync_port']
+				@sync_pwd    = config['config']['sync_pwd']
+				@sync_suffix = config['config']['sync_suffix']
+				@last_update = config['config']['last_update'].to_i
+	
+				if @key.empty? || @file_gpg.empty? 
+					@error_msg = I18n.t('error.config.check')
+					return false
+				end
+	
+				I18n.locale = @lang.to_sym
+	
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
+				return false
+			end
+	
+			return true
+		end
+	
+		# Set the last update when there is a sync
+		# @rtrn: true is the file has been updated
+		def set_last_update()
+			config = {'config' => {'key'         => @key,
+			                       'lang'        => @lang,
+			                       'file_gpg'    => @file_gpg,
+			                       'timeout_pwd' => @timeout_pwd,
+			                       'sync_host'   => @sync_host,
+			                       'sync_port'   => @sync_port,
+			                       'sync_pwd'    => @sync_pwd,
+			                       'sync_suffix' => @sync_suffix,
+			                       'last_update' => Time.now.to_i }}
+	
+			begin
+				File.open(@file_config, 'w') do |file|
+					file << config.to_yaml
+				end
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+				return false
+			end
+	
+			return true
+		end
+		
+	end
+
+end
diff --git a/MPW/MPW.rb b/MPW/MPW.rb
new file mode 100644
index 0000000..1f84400
--- /dev/null
+++ b/MPW/MPW.rb
@@ -0,0 +1,335 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+module MPW
+
+	require 'rubygems'
+	require 'gpgme'
+	require 'csv'
+	require 'i18n'
+	
+	class MPW
+		
+		ID       = 0
+		NAME     = 1
+		GROUP    = 2
+		PROTOCOL = 3
+		SERVER   = 4
+		LOGIN    = 5
+		PASSWORD = 6
+		PORT     = 7
+		COMMENT  = 8
+		DATE     = 9
+	
+		attr_accessor :error_msg
+		
+		# Constructor
+		def initialize(file_gpg, key=nil)
+			@error_msg = nil
+			@file_gpg  = file_gpg
+			@key       = key
+		end
+	
+		# Decrypt a gpg file
+		# @args: password -> the GPG key password
+		# @rtrn: true if data has been decrypted
+		def decrypt(passwd=nil)
+			@data = Array.new
+	
+			begin
+				if File.exist?(@file_gpg)
+					crypto = GPGME::Crypto.new(:armor => true)
+					data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
+	
+					data_decrypt.lines do |line|
+						@data.push(line.parse_csv)
+					end
+				end
+	
+				return true
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
+				return false
+			end
+		end
+	
+		# Encrypt a file
+		# @rtrn: true if the file has been encrypted
+		def encrypt()
+			begin
+				crypto = GPGME::Crypto.new(:armor => true)
+				file_gpg = File.open(@file_gpg, 'w+')
+	
+				data_to_encrypt = ''
+				@data.each do |row|
+					data_to_encrypt << row.to_csv
+				end
+	
+				crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
+				file_gpg.close
+	
+				return true
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+				return false
+			end
+		end
+		
+		# Search in some csv data
+		# @args: search -> the string to search
+		#        protocol -> the connection protocol (ssh, web, other)
+		# @rtrn: a list with the resultat of the search
+		def search(search='', group=nil, protocol=nil)
+			result = Array.new()
+	
+			if !search.nil?
+				search = search.downcase
+			end
+			search = search.force_encoding('ASCII-8BIT')
+	
+			@data.each do |row|
+				name    = row[NAME].nil?    ? nil : row[NAME].downcase
+				server  = row[SERVER].nil?  ? nil : row[SERVER].downcase
+				comment = row[COMMENT].nil? ? nil : row[COMMENT].downcase
+	
+				if name =~ /^.*#{search}.*$/  || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/ 
+					if (protocol.nil? || protocol.eql?(row[PROTOCOL])) && (group.nil? || group.eql?(row[GROUP]))
+						result.push(row)
+					end
+				end
+			end
+	
+			return result
+		end
+	
+		# Search in some csv data
+		# @args: id -> the id item
+		# @rtrn: a row with the resultat of the search
+		def search_by_id(id)
+			@data.each do |row|
+				if row[ID] == id
+					return row
+				end
+			end
+	
+			return Array.new()
+		end
+	
+		# Update an item
+		# @args: id -> the item's identifiant
+		#        name -> the item name
+		#        group ->  the item group
+		#        server -> the ip or hostname
+		#        protocol -> the protocol
+		#        login -> the login
+		#        passwd -> the password
+		#        port -> the port
+		#        comment -> a comment
+		# @rtrn: true if the item has been updated
+		def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
+			row    = Array.new()
+			update = false
+	
+			i  = 0
+			@data.each do |r|
+				if r[ID] == id
+					row    = r
+					update = true
+					break
+				end
+				i += 1
+			end
+	
+			if port.to_i <= 0
+				port = nil
+			end
+	
+			row_update       = Array.new()
+			row_update[DATE] = Time.now.to_i
+	
+			row_update[ID]       = id.nil?       || id.empty?       ? MPW.password(16) : id
+			row_update[NAME]     = name.nil?     || name.empty?     ? row[NAME]        : name
+			row_update[GROUP]    = group.nil?    || group.empty?    ? row[GROUP]       : group
+			row_update[SERVER]   = server.nil?   || server.empty?   ? row[SERVER]      : server
+			row_update[PROTOCOL] = protocol.nil? || protocol.empty? ? row[PROTOCOL]    : protocol
+			row_update[LOGIN]    = login.nil?    || login.empty?    ? row[LOGIN]       : login
+			row_update[PASSWORD] = passwd.nil?   || passwd.empty?   ? row[PASSWORD]    : passwd
+			row_update[PORT]     = port.nil?     || port.empty?     ? row[PORT]        : port
+			row_update[COMMENT]  = comment.nil?  || comment.empty?  ? row[COMMENT]     : comment
+			
+			row_update[] = row_update[NAME].nil?     ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
+			row_update[] = row_update[GROUP].nil?    ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
+			row_update[] = row_update[SERVER].nil?   ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
+			row_update[] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
+			row_update[] = row_update[LOGIN].nil?    ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
+			row_update[] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
+			row_update[] = row_update[COMMENT].nil?  ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
+	
+			if row_update[NAME].nil? || row_update[NAME].empty?
+				@error_msg = I18n.t('error.update.name_empty')
+				return false
+			end
+	
+			if update
+				@data[i] = row_update
+			else
+				@data.push(row_update)
+			end
+	
+			return true
+		end
+		
+		# Remove an item 
+		# @args: id -> the item's identifiant
+		# @rtrn: true if the item has been deleted
+		def remove(id)
+			i = 0
+			@data.each do |row|
+				if row[ID] == id
+					@data.delete_at(i)
+					return true
+				end
+				i += 1
+			end
+	
+			@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
+			return false
+		end
+	
+		# Export to csv
+		# @args: file -> a string to match
+		# @rtrn: true if export work
+		def export(file)
+			begin
+				File.open(file, 'w+') do |file|
+					@data.each do |row|
+						row.delete_at(ID).delete_at(DATE)
+						file << row.to_csv
+					end
+				end
+	
+				return true
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
+				return false
+			end
+		end
+	
+		# Import to csv
+		# @args: file -> path to file import
+		# @rtrn: true if the import work
+		def import(file)
+			begin
+				data_new = IO.read(file)
+				data_new.lines do |line|
+					if not line =~ /(.*,){6}/
+						@error_msg = I18n.t('error.import.bad_format')
+						return false
+					else
+						row = line.parse_csv.unshift(0)
+						if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
+							return false
+						end
+					end
+				end
+	
+				return true
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
+				return false
+			end
+		end
+	
+		# Return a preview import 
+		# @args: file -> path to file import
+		# @rtrn: an array with the items to import, if there is an error return false
+		def import_preview(file)
+			begin
+				result = Array.new()
+				id = 0
+	
+				data = IO.read(file)
+				data.lines do |line|
+					if not line =~ /(.*,){6}/
+						@error_msg = I18n.t('error.import.bad_format')
+						return false
+					else
+						result.push(line.parse_csv.unshift(id))
+					end
+	
+					id += 1
+				end
+	
+				return result
+			rescue Exception => e 
+				@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
+				return false
+			end
+		end
+	
+		# Sync remote data and local data
+		# @args: data_remote -> array with the data remote
+		#        last_update -> last update
+		# @rtrn: false if data_remote is nil
+		def sync(data_remote, last_update)
+			if !data_remote.instance_of?(Array)
+				return false
+			end
+	
+			@data.each do |l|
+				j = 0
+				update = false
+	
+				# Update item
+				data_remote.each do |r|
+					if l[ID] == r[ID]
+						if l[DATE].to_i < r[DATE].to_i
+							update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
+						end
+						update = true
+						data_remote.delete_at(j)
+						break
+					end
+					j += 1
+				end
+	
+				# Delete an old item
+				if !update && l[DATE].to_i < last_update
+					remove(l[ID])
+				end
+			end
+	
+			# Add item
+			data_remote.each do |r|
+				if r[DATE].to_i > last_update
+					update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], r[ID])
+				end
+			end
+	
+			return encrypt()
+		end
+	
+		# Generate a random password
+		# @args: length -> the length password
+		# @rtrn: a random string
+		def self.password(length=8)
+			if length.to_i <= 0
+				length = 8
+			else
+				length = length.to_i
+			end
+	
+			result = ''
+			while length > 62 do
+				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
+				length -= 62
+			end
+			result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
+	
+			return result
+		end
+	end
+
+end
diff --git a/MPW/Sync/MPW.rb b/MPW/Sync/MPW.rb
new file mode 100644
index 0000000..da72e43
--- /dev/null
+++ b/MPW/Sync/MPW.rb
@@ -0,0 +1,130 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+module MPW
+
+	module Sync
+
+		require 'rubygems'
+		require 'i18n'
+		require 'socket'
+		require 'json'
+		
+		class MPW
+		
+			attr_accessor :error_msg
+			attr_accessor :enable
+		
+			# Constructor
+			def initialize()
+				@error_msg = nil
+				@enable    = false
+			end
+		
+			# Connect to server
+			# @args: host -> the server host
+			#        port -> ther connection port
+			#        gpg_key -> the gpg key
+			#        password -> the remote password
+			#        suffix -> the suffix file
+			# @rtrn: false if the connection fail
+			def connect(host, port, gpg_key, password, suffix=nil)
+				@gpg_key  = gpg_key
+				@password = password
+				@suffix   = suffix
+		
+				begin
+					@socket = TCPSocket.new(host, port)
+					@enable = true
+				rescue Exception => e
+					@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+					@enable    = false
+				end
+		
+				return @enable
+			end
+		
+			# Get data on server
+			# @args: gpg_password -> the gpg password
+			# @rtrn: nil if nothing data or error
+			def get(gpg_password)
+				if !@enable
+					return nil
+				end
+		
+				send_msg = {:action   => 'get',
+				            :gpg_key  => @gpg_key,
+				            :password => @password,
+				            :suffix   => @suffix}
+				
+				@socket.puts send_msg.to_json
+				msg = JSON.parse(@socket.gets)
+		
+				if !defined?(msg['error'])
+					@error_msg = I18n.t('error.sync.communication')
+					return nil
+				elsif msg['error'].nil?
+					tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
+					File.open(tmp_file, 'w') do |file|
+						file << msg['data']
+					end
+					
+					@mpw = MPW.new(tmp_file)
+					if !@mpw.decrypt(gpg_password)
+						puts @mpw.error_msg
+						return nil
+					end
+		
+					File.unlink(tmp_file)
+					return @mpw.search()
+				else
+					@error_msg = I18n.t(msg['error'])
+					return nil
+				end
+		
+			end
+		
+			# Update the remote data
+			# @args: data -> the data to send on server
+			# @rtrn: false if there is a problem
+			def update(data)
+				if !@enable
+					return true
+				end
+		
+				send_msg = {:action   => 'update',
+				            :gpg_key  => @gpg_key,
+				            :password => @password,
+				            :suffix   => @suffix,
+				            :data     => data}
+				
+				@socket.puts send_msg.to_json
+				msg = JSON.parse(@socket.gets)
+		
+				if !defined?(msg['error'])
+					@error_msg = I18n.t('error.sync.communication')
+					return false
+				elsif msg['error'].nil?
+					return true
+				else
+					@error_msg = I18n.t(msg['error'])
+					return false
+				end
+			end
+		
+			# Close the connection
+			def close()
+				if !@enable
+					return
+				end
+		
+				send_msg = {:action => 'close'}
+				@socket.puts send_msg.to_json
+			end
+		end
+
+	end
+
+end
diff --git a/lib/Cli.rb b/MPW/UI/Cli.rb
similarity index 86%
rename from lib/Cli.rb
rename to MPW/UI/Cli.rb
index 152f3bf..870c9a2 100644
--- a/lib/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -10,9 +10,8 @@ require 'readline'
 require 'i18n'
 require 'yaml'
 
-require "#{APP_ROOT}/lib/MPW.rb"
-require "#{APP_ROOT}/lib/MPWConfig.rb"
-require "#{APP_ROOT}/lib/Sync.rb"
+require "#{APP_ROOT}/MPW/MPW"
+require "#{APP_ROOT}/MPW/Sync/MPW"
 
 class Cli
 
@@ -32,7 +31,7 @@ class Cli
 	# @rtnr: true if the synchro is finish
 	def sync()
 		if !defined?(@sync)
-			@sync = Sync.new()
+			@sync = MPW::Sync::MPW.new
 
 			if !@config.sync_host.nil? && !@config.sync_port.nil?
 				if !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
@@ -99,7 +98,7 @@ class Cli
 	# Request the GPG password and decrypt the file
 	def decrypt()
 		if !defined?(@mpw)
-			@mpw = MPW.new(@config.file_gpg, @config.key)
+			@mpw = MPW::MPW.new(@config.file_gpg, @config.key)
 		end
 
 		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
@@ -132,30 +131,30 @@ class Cli
 	# @args: item -> an array with the item information
 	def displayFormat(item)
 		puts '--------------------'
-		puts "Id: #{item[MPW::ID]}"
-		puts "#{I18n.t('display.name')}: #{item[MPW::NAME]}"
-		puts "#{I18n.t('display.group')}: #{item[MPW::GROUP]}"
-		puts "#{I18n.t('display.server')}: #{item[MPW::SERVER]}"
-		puts "#{I18n.t('display.protocol')}: #{item[MPW::PROTOCOL]}"
-		puts "#{I18n.t('display.login')}: #{item[MPW::LOGIN]}"
-		puts "#{I18n.t('display.password')}: #{item[MPW::PASSWORD]}"
-		puts "#{I18n.t('display.port')}: #{item[MPW::PORT]}"
-		puts "#{I18n.t('display.comment')}: #{item[MPW::COMMENT]}"
+		puts "Id: #{item[MPW::MPW::ID]}"
+		puts "#{I18n.t('display.name')}: #{item[MPW::MPW::NAME]}"
+		puts "#{I18n.t('display.group')}: #{item[MPW::MPW::GROUP]}"
+		puts "#{I18n.t('display.server')}: #{item[MPW::MPW::SERVER]}"
+		puts "#{I18n.t('display.protocol')}: #{item[MPW::MPW::PROTOCOL]}"
+		puts "#{I18n.t('display.login')}: #{item[MPW::MPW::LOGIN]}"
+		puts "#{I18n.t('display.password')}: #{item[MPW::MPW::PASSWORD]}"
+		puts "#{I18n.t('display.port')}: #{item[MPW::MPW::PORT]}"
+		puts "#{I18n.t('display.comment')}: #{item[MPW::MPW::COMMENT]}"
 	end
 
 	# Display an item in the alternative format
 	# @args: item -> an array with the item information
 	def displayFormatAlt(item)
-		port = item[MPW::PORT].nil? ? '' : ":#{item[MPW::PORT]}"
+		port = item[MPW::MPW::PORT].nil? ? '' : ":#{item[MPW::MPW::PORT]}"
 
-		if item[MPW::PASSWORD].nil? || item[MPW::PASSWORD].empty?
-			if item[MPW::LOGIN].include('@')
-				puts "# #{item[MPW::ID]} #{item[MPW::PROTOCOL]}://#{item[MPW::LOGIN]}@#{item[MPW::SERVER]}#{port}"
+		if item[MPW::MPW::PASSWORD].nil? || item[MPW::MPW::PASSWORD].empty?
+			if item[MPW::MPW::LOGIN].include('@')
+				puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://#{item[MPW::MPW::LOGIN]}@#{item[MPW::MPW::SERVER]}#{port}"
 			else
-				puts "# #{item[MPW::ID]} #{item[MPW::PROTOCOL]}://{#{item[MPW::LOGIN]}}@#{item[MPW::SERVER]}#{port}"
+				puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://{#{item[MPW::MPW::LOGIN]}}@#{item[MPW::MPW::SERVER]}#{port}"
 			end
 		else
-			puts "# #{item[MPW::ID]} #{item[MPW::PROTOCOL]}://{#{item[MPW::LOGIN]}:#{item[MPW::PASSWORD]}}@#{item[MPW::SERVER]}#{port}"
+			puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://{#{item[MPW::MPW::LOGIN]}:#{item[MPW::MPW::PASSWORD]}}@#{item[MPW::MPW::SERVER]}#{port}"
 		end
 	end
 
@@ -193,14 +192,14 @@ class Cli
 		if not row.empty?
 			puts I18n.t('form.update.title')
 			puts '--------------------'
-			name     = ask(I18n.t('form.update.name'    , :name => row[MPW::NAME])).to_s
-			group    = ask(I18n.t('form.update.group'   , :group => row[MPW::GROUP])).to_s
-			server   = ask(I18n.t('form.update.server'  , :server => row[MPW::SERVER])).to_s
-			protocol = ask(I18n.t('form.update.protocol', :protocol => row[MPW::PROTOCOL])).to_s
-			login    = ask(I18n.t('form.update.login'   , :login => row[MPW::LOGIN])).to_s
+			name     = ask(I18n.t('form.update.name'    , :name => row[MPW::MPW::NAME])).to_s
+			group    = ask(I18n.t('form.update.group'   , :group => row[MPW::MPW::GROUP])).to_s
+			server   = ask(I18n.t('form.update.server'  , :server => row[MPW::MPW::SERVER])).to_s
+			protocol = ask(I18n.t('form.update.protocol', :protocol => row[MPW::MPW::PROTOCOL])).to_s
+			login    = ask(I18n.t('form.update.login'   , :login => row[MPW::MPW::LOGIN])).to_s
 			passwd   = ask(I18n.t('form.update.password')).to_s
-			port     = ask(I18n.t('form.update.port'    , :port => row[MPW::PORT])).to_s
-			comment  = ask(I18n.t('form.update.comment' , :comment => row[MPW::COMMENT])).to_s
+			port     = ask(I18n.t('form.update.port'    , :port => row[MPW::MPW::PORT])).to_s
+			comment  = ask(I18n.t('form.update.comment' , :comment => row[MPW::MPW::COMMENT])).to_s
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt()
diff --git a/lib/CliSSH.rb b/MPW/UI/CliSSH.rb
similarity index 74%
rename from lib/CliSSH.rb
rename to MPW/UI/CliSSH.rb
index ebcea04..9292ca8 100644
--- a/lib/CliSSH.rb
+++ b/MPW/UI/CliSSH.rb
@@ -3,7 +3,7 @@
 # mail: nishiki@yaegashi.fr
 # info: a simple script who manage your passwords
 
-require "#{APP_ROOT}/lib/Cli.rb"
+require "#{APP_ROOT}/MPW/UI/Cli"
 
 class CliSSH < Cli
 
@@ -16,11 +16,11 @@ class CliSSH < Cli
 
 		if result.length > 0
 			result.each do |r|
-				server = @server.nil? ? r[MPW::SERVER] : @server
-				port   = @port.nil?   ? r[MPW::PORT]   : @port
-				login  = @login.nil?  ? r[MPW::LOGIN]  : @login
+				server = @server.nil? ? r[MPW::MPW::SERVER] : @server
+				port   = @port.nil?   ? r[MPW::MPW::PORT]   : @port
+				login  = @login.nil?  ? r[MPW::MPW::LOGIN]  : @login
 
-				passwd = r[MPW::PASSWORD]
+				passwd = r[MPW::MPW::PASSWORD]
 
 				if port.nil? || port.empty?
 					port = 22
diff --git a/lib/MPW.rb b/lib/MPW.rb
deleted file mode 100644
index f82fcc2..0000000
--- a/lib/MPW.rb
+++ /dev/null
@@ -1,331 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'rubygems'
-require 'gpgme'
-require 'csv'
-require 'i18n'
-
-class MPW
-	
-	ID       = 0
-	NAME     = 1
-	GROUP    = 2
-	PROTOCOL = 3
-	SERVER   = 4
-	LOGIN    = 5
-	PASSWORD = 6
-	PORT     = 7
-	COMMENT  = 8
-	DATE     = 9
-
-	attr_accessor :error_msg
-	
-	# Constructor
-	def initialize(file_gpg, key=nil)
-		@error_msg = nil
-		@file_gpg  = file_gpg
-		@key       = key
-	end
-
-	# Decrypt a gpg file
-	# @args: password -> the GPG key password
-	# @rtrn: true if data has been decrypted
-	def decrypt(passwd=nil)
-		@data = Array.new
-
-		begin
-			if File.exist?(@file_gpg)
-				crypto = GPGME::Crypto.new(:armor => true)
-				data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
-
-				data_decrypt.lines do |line|
-					@data.push(line.parse_csv)
-				end
-			end
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
-			return false
-		end
-	end
-
-	# Encrypt a file
-	# @rtrn: true if the file has been encrypted
-	def encrypt()
-		begin
-			crypto = GPGME::Crypto.new(:armor => true)
-			file_gpg = File.open(@file_gpg, 'w+')
-
-			data_to_encrypt = ''
-			@data.each do |row|
-				data_to_encrypt << row.to_csv
-			end
-
-			crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
-			file_gpg.close
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
-			return false
-		end
-	end
-	
-	# Search in some csv data
-	# @args: search -> the string to search
-	#        protocol -> the connection protocol (ssh, web, other)
-	# @rtrn: a list with the resultat of the search
-	def search(search='', group=nil, protocol=nil)
-		result = Array.new()
-
-		if !search.nil?
-			search = search.downcase
-		end
-		search = search.force_encoding('ASCII-8BIT')
-
-		@data.each do |row|
-			name    = row[NAME].nil?    ? nil : row[NAME].downcase
-			server  = row[SERVER].nil?  ? nil : row[SERVER].downcase
-			comment = row[COMMENT].nil? ? nil : row[COMMENT].downcase
-
-			if name =~ /^.*#{search}.*$/  || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/ 
-				if (protocol.nil? || protocol.eql?(row[PROTOCOL])) && (group.nil? || group.eql?(row[GROUP]))
-					result.push(row)
-				end
-			end
-		end
-
-		return result
-	end
-
-	# Search in some csv data
-	# @args: id -> the id item
-	# @rtrn: a row with the resultat of the search
-	def search_by_id(id)
-		@data.each do |row|
-			if row[ID] == id
-				return row
-			end
-		end
-
-		return Array.new()
-	end
-
-	# Update an item
-	# @args: id -> the item's identifiant
-	#        name -> the item name
-	#        group ->  the item group
-	#        server -> the ip or hostname
-	#        protocol -> the protocol
-	#        login -> the login
-	#        passwd -> the password
-	#        port -> the port
-	#        comment -> a comment
-	# @rtrn: true if the item has been updated
-	def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
-		row    = Array.new()
-		update = false
-
-		i  = 0
-		@data.each do |r|
-			if r[ID] == id
-				row    = r
-				update = true
-				break
-			end
-			i += 1
-		end
-
-		if port.to_i <= 0
-			port = nil
-		end
-
-		row_update       = Array.new()
-		row_update[DATE] = Time.now.to_i
-
-		row_update[ID]       = id.nil?       || id.empty?       ? MPW.password(16) : id
-		row_update[NAME]     = name.nil?     || name.empty?     ? row[NAME]        : name
-		row_update[GROUP]    = group.nil?    || group.empty?    ? row[GROUP]       : group
-		row_update[SERVER]   = server.nil?   || server.empty?   ? row[SERVER]      : server
-		row_update[PROTOCOL] = protocol.nil? || protocol.empty? ? row[PROTOCOL]    : protocol
-		row_update[LOGIN]    = login.nil?    || login.empty?    ? row[LOGIN]       : login
-		row_update[PASSWORD] = passwd.nil?   || passwd.empty?   ? row[PASSWORD]    : passwd
-		row_update[PORT]     = port.nil?     || port.empty?     ? row[PORT]        : port
-		row_update[COMMENT]  = comment.nil?  || comment.empty?  ? row[COMMENT]     : comment
-		
-		row_update[] = row_update[NAME].nil?     ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
-		row_update[] = row_update[GROUP].nil?    ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
-		row_update[] = row_update[SERVER].nil?   ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
-		row_update[] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
-		row_update[] = row_update[LOGIN].nil?    ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
-		row_update[] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
-		row_update[] = row_update[COMMENT].nil?  ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
-
-		if row_update[NAME].nil? || row_update[NAME].empty?
-			@error_msg = I18n.t('error.update.name_empty')
-			return false
-		end
-
-		if update
-			@data[i] = row_update
-		else
-			@data.push(row_update)
-		end
-
-		return true
-	end
-	
-	# Remove an item 
-	# @args: id -> the item's identifiant
-	# @rtrn: true if the item has been deleted
-	def remove(id)
-		i = 0
-		@data.each do |row|
-			if row[ID] == id
-				@data.delete_at(i)
-				return true
-			end
-			i += 1
-		end
-
-		@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
-		return false
-	end
-
-	# Export to csv
-	# @args: file -> a string to match
-	# @rtrn: true if export work
-	def export(file)
-		begin
-			File.open(file, 'w+') do |file|
-				@data.each do |row|
-					row.delete_at(ID).delete_at(DATE)
-					file << row.to_csv
-				end
-			end
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
-			return false
-		end
-	end
-
-	# Import to csv
-	# @args: file -> path to file import
-	# @rtrn: true if the import work
-	def import(file)
-		begin
-			data_new = IO.read(file)
-			data_new.lines do |line|
-				if not line =~ /(.*,){6}/
-					@error_msg = I18n.t('error.import.bad_format')
-					return false
-				else
-					row = line.parse_csv.unshift(0)
-					if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
-						return false
-					end
-				end
-			end
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
-			return false
-		end
-	end
-
-	# Return a preview import 
-	# @args: file -> path to file import
-	# @rtrn: an array with the items to import, if there is an error return false
-	def import_preview(file)
-		begin
-			result = Array.new()
-			id = 0
-
-			data = IO.read(file)
-			data.lines do |line|
-				if not line =~ /(.*,){6}/
-					@error_msg = I18n.t('error.import.bad_format')
-					return false
-				else
-					result.push(line.parse_csv.unshift(id))
-				end
-
-				id += 1
-			end
-
-			return result
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
-			return false
-		end
-	end
-
-	# Sync remote data and local data
-	# @args: data_remote -> array with the data remote
-	#        last_update -> last update
-	# @rtrn: false if data_remote is nil
-	def sync(data_remote, last_update)
-		if !data_remote.instance_of?(Array)
-			return false
-		end
-
-		@data.each do |l|
-			j = 0
-			update = false
-
-			# Update item
-			data_remote.each do |r|
-				if l[ID] == r[ID]
-					if l[DATE].to_i < r[DATE].to_i
-						update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
-					end
-					update = true
-					data_remote.delete_at(j)
-					break
-				end
-				j += 1
-			end
-
-			# Delete an old item
-			if !update && l[DATE].to_i < last_update
-				remove(l[ID])
-			end
-		end
-
-		# Add item
-		data_remote.each do |r|
-			if r[DATE].to_i > last_update
-				update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], r[ID])
-			end
-		end
-
-		return encrypt()
-	end
-
-	# Generate a random password
-	# @args: length -> the length password
-	# @rtrn: a random string
-	def self.password(length=8)
-		if length.to_i <= 0
-			length = 8
-		else
-			length = length.to_i
-		end
-
-		result = ''
-		while length > 62 do
-			result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
-			length -= 62
-		end
-		result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
-
-		return result
-	end
-end
diff --git a/lib/MPWConfig.rb b/lib/MPWConfig.rb
deleted file mode 100644
index bce49e6..0000000
--- a/lib/MPWConfig.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'rubygems'
-require 'yaml'
-require 'i18n'
-
-class MPWConfig
-	
-	attr_accessor :error_msg
-
-	attr_accessor :key
-	attr_accessor :lang
-	attr_accessor :file_gpg
-	attr_accessor :timeout_pwd
-	attr_accessor :last_update
-	attr_accessor :sync_host
-	attr_accessor :sync_port
-	attr_accessor :sync_pwd
-	attr_accessor :sync_suffix
-	attr_accessor :last_update
-
-	# Constructor
-	# @args: file_config -> the specify config file
-	def initialize(file_config=nil)
-		@error_msg   = nil
-		@file_config = "#{Dir.home()}/.mpw.cfg"
-
-		if !file_config.nil? && !file_config.empty?
-			@file_config = file_config
-		end
-	end
-
-	# Create a new config file
-	# @args: key -> the gpg key to encrypt
-	#        lang -> the software language
-	#        file_gpg -> the file who is encrypted
-	#        timeout_pwd -> time to save the password
-	#        sync_host -> the server host for synchronization
-	#        sync_port -> the server port for synchronization
-	#        sync_pwd -> the password for synchronization
-	#        sync_suffix -> the suffix file (optionnal) 
-	# @rtrn: true if le config file is create
-	def setup(key, lang, file_gpg, timeout_pwd, sync_host=nil, sync_port=nil, sync_pwd=nil, sync_suffix=nil)
-
-		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-			@error_msg = I18n.t('error.config.key_bad_format')
-			return false
-		end
-		
-		if file_gpg.empty?
-			file_gpg = "#{Dir.home()}/.mpw.gpg"
-		end
-
-		timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
-
-		config = {'config' => {'key'         => key,
-		                       'lang'        => lang,
-		                       'file_gpg'    => file_gpg,
-		                       'timeout_pwd' => timeout_pwd,
-		                       'sync_host'   => sync_host,
-		                       'sync_port'   => sync_port,
-		                       'sync_pwd'    => sync_pwd,
-		                       'sync_suffix' => sync_suffix,
-		                       'last_update' => 0 }}
-
-		begin
-			File.open(@file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-			return false
-		end
-
-		return true
-	end
-
-	# Check the config file
-	# @rtrn: true if the config file is correct
-	def checkconfig()
-		begin
-			config = YAML::load_file(@file_config)
-			@key         = config['config']['key']
-			@lang        = config['config']['lang']
-			@file_gpg    = config['config']['file_gpg']
-			@timeout_pwd = config['config']['timeout_pwd'].to_i
-			@sync_host   = config['config']['sync_host']
-			@sync_port   = config['config']['sync_port']
-			@sync_pwd    = config['config']['sync_pwd']
-			@sync_suffix = config['config']['sync_suffix']
-			@last_update = config['config']['last_update'].to_i
-
-			if @key.empty? || @file_gpg.empty? 
-				@error_msg = I18n.t('error.config.check')
-				return false
-			end
-
-			I18n.locale = @lang.to_sym
-
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
-			return false
-		end
-
-		return true
-	end
-
-	# Set the last update when there is a sync
-	# @rtrn: true is the file has been updated
-	def set_last_update()
-		config = {'config' => {'key'         => @key,
-		                       'lang'        => @lang,
-		                       'file_gpg'    => @file_gpg,
-		                       'timeout_pwd' => @timeout_pwd,
-		                       'sync_host'   => @sync_host,
-		                       'sync_port'   => @sync_port,
-		                       'sync_pwd'    => @sync_pwd,
-		                       'sync_suffix' => @sync_suffix,
-		                       'last_update' => Time.now.to_i }}
-
-		begin
-			File.open(@file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-			return false
-		end
-
-		return true
-	end
-	
-end
diff --git a/lib/Sync.rb b/lib/Sync.rb
deleted file mode 100644
index e85b0ce..0000000
--- a/lib/Sync.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'rubygems'
-require 'i18n'
-require 'socket'
-require 'json'
-
-require "#{APP_ROOT}/lib/MPW.rb"
-
-class Sync
-
-	attr_accessor :error_msg
-	attr_accessor :enable
-
-	# Constructor
-	def initialize()
-		@error_msg = nil
-		@enable    = false
-	end
-
-	# Connect to server
-	# @args: host -> the server host
-	#        port -> ther connection port
-	#        gpg_key -> the gpg key
-	#        password -> the remote password
-	#        suffix -> the suffix file
-	# @rtrn: false if the connection fail
-	def connect(host, port, gpg_key, password, suffix=nil)
-		@gpg_key  = gpg_key
-		@password = password
-		@suffix   = suffix
-
-		begin
-			@socket = TCPSocket.new(host, port)
-			@enable = true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-			@enable    = false
-		end
-
-		return @enable
-	end
-
-	# Get data on server
-	# @args: gpg_password -> the gpg password
-	# @rtrn: nil if nothing data or error
-	def get(gpg_password)
-		if !@enable
-			return nil
-		end
-
-		send_msg = {:action   => 'get',
-		            :gpg_key  => @gpg_key,
-		            :password => @password,
-		            :suffix   => @suffix}
-		
-		@socket.puts send_msg.to_json
-		msg = JSON.parse(@socket.gets)
-
-		if !defined?(msg['error'])
-			@error_msg = I18n.t('error.sync.communication')
-			return nil
-		elsif msg['error'].nil?
-			tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
-			File.open(tmp_file, 'w') do |file|
-				file << msg['data']
-			end
-			
-			@mpw = MPW.new(tmp_file)
-			if !@mpw.decrypt(gpg_password)
-				puts @mpw.error_msg
-				return nil
-			end
-
-			File.unlink(tmp_file)
-			return @mpw.search()
-		else
-			@error_msg = I18n.t(msg['error'])
-			return nil
-		end
-
-	end
-
-	# Update the remote data
-	# @args: data -> the data to send on server
-	# @rtrn: false if there is a problem
-	def update(data)
-		if !@enable
-			return true
-		end
-
-		send_msg = {:action   => 'update',
-		            :gpg_key  => @gpg_key,
-		            :password => @password,
-		            :suffix   => @suffix,
-		            :data     => data}
-		
-		@socket.puts send_msg.to_json
-		msg = JSON.parse(@socket.gets)
-
-		if !defined?(msg['error'])
-			@error_msg = I18n.t('error.sync.communication')
-			return false
-		elsif msg['error'].nil?
-			return true
-		else
-			@error_msg = I18n.t(msg['error'])
-			return false
-		end
-	end
-
-	# TODO
-	def delete()
-	end
-
-	# Close the connection
-	def close()
-		if !@enable
-			return
-		end
-
-		send_msg = {:action => 'close'}
-		@socket.puts send_msg.to_json
-	end
-end
diff --git a/mpw b/mpw
index ff27575..ae59416 100755
--- a/mpw
+++ b/mpw
@@ -10,8 +10,8 @@ require 'locale'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/lib/Cli.rb"
-require "#{APP_ROOT}/lib/MPW.rb"
+require "#{APP_ROOT}/MPW/UI/Cli"
+require "#{APP_ROOT}/MPW/Config"
 
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
@@ -93,7 +93,7 @@ OptionParser.new do |opts|
 	end
 end.parse!
 
-config      = MPWConfig.new(options[:config])
+config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig()
 
 cli = Cli.new(lang, config)
diff --git a/mpw-ssh b/mpw-ssh
index 39fe1b3..9ba3791 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -10,7 +10,8 @@ require 'locale'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/lib/CliSSH.rb"
+require "#{APP_ROOT}/MPW/UI/CliSSH"
+require "#{APP_ROOT}/MPW/Config"
 
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
@@ -45,7 +46,7 @@ OptionParser.new do |opts|
 	end
 end.parse!
 
-config      = MPWConfig.new(options[:config])
+config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig()
 
 cli         = CliSSH.new(lang, config)

From 1ab3c7e0b0ac69cff28b5f56a209426c9c1791a1 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 22:49:13 +0100
Subject: [PATCH 051/531] change rescue

---
 MPW/Sync/MPW.rb | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/MPW/Sync/MPW.rb b/MPW/Sync/MPW.rb
index da72e43..054fa28 100644
--- a/MPW/Sync/MPW.rb
+++ b/MPW/Sync/MPW.rb
@@ -35,14 +35,12 @@ module MPW
 				@password = password
 				@suffix   = suffix
 		
-				begin
-					@socket = TCPSocket.new(host, port)
-					@enable = true
-				rescue Exception => e
-					@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-					@enable    = false
-				end
-		
+				@socket = TCPSocket.new(host, port)
+				@enable = true
+			rescue Exception => e
+				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+				@enable    = false
+			else
 				return @enable
 			end
 		

From 83f935dc21affca1ca636e7ab3a3c12d0d520166 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 23:10:35 +0100
Subject: [PATCH 052/531] beautiful code

---
 MPW/Sync/MPW.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/MPW/Sync/MPW.rb b/MPW/Sync/MPW.rb
index 054fa28..14a43c5 100644
--- a/MPW/Sync/MPW.rb
+++ b/MPW/Sync/MPW.rb
@@ -18,7 +18,7 @@ module MPW
 			attr_accessor :enable
 		
 			# Constructor
-			def initialize()
+			def initialize
 				@error_msg = nil
 				@enable    = false
 			end
@@ -113,7 +113,7 @@ module MPW
 			end
 		
 			# Close the connection
-			def close()
+			def close
 				if !@enable
 					return
 				end

From 8ac531ffedf37e3bf1fdcdc614f0f7ccdb8f08c9 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 29 Jan 2014 23:11:01 +0100
Subject: [PATCH 053/531] add Sync/SSH

---
 MPW/Sync/SSH.rb | 107 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 MPW/Sync/SSH.rb

diff --git a/MPW/Sync/SSH.rb b/MPW/Sync/SSH.rb
new file mode 100644
index 0000000..b63d912
--- /dev/null
+++ b/MPW/Sync/SSH.rb
@@ -0,0 +1,107 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+module MPW
+
+	module Sync
+
+		require 'rubygems'
+		require 'i18n'
+		
+		class MPW
+		
+			attr_accessor :error_msg
+			attr_accessor :enable
+		
+			# Constructor
+			def initialize
+				@error_msg = nil
+				@enable    = false
+			end
+		
+			# Connect to server
+			# @args: host -> the server host
+			#        port -> ther connection port
+			#        gpg_key -> the gpg key
+			#        password -> the remote password
+			#        suffix -> the suffix file
+			# @rtrn: false if the connection fail
+			def connect(host, user, password, path, port=nil)
+				@host     = host
+				@user     = user
+				@password = password
+				@path     = path
+				@port     = port.nil? || port.empty? ? 22 : port.to_i
+					
+				Net::SSH.start(@host, @user, :password => @password, :port => @port) do
+				end
+			rescue
+				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+				@enable    = false
+			else
+		
+				return @enable
+			end
+		
+			# Get data on server
+			# @args: gpg_password -> the gpg password
+			# @rtrn: nil if nothing data or error
+			def get(gpg_password)
+				if !@enable
+					return nil
+				end
+				
+				tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
+				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |ssh|
+					ssh.scp.download(@path, tmp_file)
+				end
+			
+				File.open(tmp_file, 'w') do |file|
+					file << msg['data']
+				end
+					
+				@mpw = MPW.new(tmp_file)
+				if !@mpw.decrypt(gpg_password)
+					puts @mpw.error_msg
+					return nil
+				end
+		
+				File.unlink(tmp_file)
+
+				return @mpw.search()
+			rescue Exception => e
+				@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
+				return nil
+			end
+		
+			# Update the remote data
+			# @args: data -> the data to send on server
+			# @rtrn: false if there is a problem
+			def update(data)
+				if !@enable
+					return true
+				end
+		
+				tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
+				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |ssh|
+					ssh.scp.upload(tmp_file, @path)
+				end
+
+				File.unlink(tmp_file)
+
+				return true
+			rescue Exception => e
+				@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
+				return false
+			end
+		
+			# Close the connection
+			def close
+			end
+		end
+
+	end
+
+end

From 958792801ecb21b27209f98fd49dec6db7762e86 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 30 Jan 2014 23:08:38 +0100
Subject: [PATCH 054/531] recode for multiple protocol to sync

---
 MPW/Config.rb   |  89 ++++++++++++++--------------
 MPW/MPW.rb      | 154 ++++++++++++++++++++++--------------------------
 MPW/Sync/MPW.rb |  33 +++++++----
 MPW/Sync/SSH.rb |  24 +++++---
 MPW/UI/Cli.rb   |  51 ++++++++--------
 5 files changed, 183 insertions(+), 168 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 74d309c..92a58fd 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -18,10 +18,12 @@ module MPW
 		attr_accessor :file_gpg
 		attr_accessor :timeout_pwd
 		attr_accessor :last_update
+		attr_accessor :sync_type
 		attr_accessor :sync_host
 		attr_accessor :sync_port
+		attr_accessor :sync_user
 		attr_accessor :sync_pwd
-		attr_accessor :sync_suffix
+		attr_accessor :sync_path
 		attr_accessor :last_update
 	
 		# Constructor
@@ -40,12 +42,14 @@ module MPW
 		#        lang -> the software language
 		#        file_gpg -> the file who is encrypted
 		#        timeout_pwd -> time to save the password
+		#        sync_type -> the type to synchronization
 		#        sync_host -> the server host for synchronization
 		#        sync_port -> the server port for synchronization
+		#        sync_user -> the user for synchronization
 		#        sync_pwd -> the password for synchronization
 		#        sync_suffix -> the suffix file (optionnal) 
 		# @rtrn: true if le config file is create
-		def setup(key, lang, file_gpg, timeout_pwd, sync_host=nil, sync_port=nil, sync_pwd=nil, sync_suffix=nil)
+		def setup(key, lang, file_gpg, timeout_pwd, sync_type=nil, sync_host=nil, sync_port=nil, sync_user=nil, sync_pwd=nil, sync_path=nil)
 	
 			if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 				@error_msg = I18n.t('error.config.key_bad_format')
@@ -62,77 +66,76 @@ module MPW
 			                       'lang'        => lang,
 			                       'file_gpg'    => file_gpg,
 			                       'timeout_pwd' => timeout_pwd,
+			                       'sync_type'   => sync_type,
 			                       'sync_host'   => sync_host,
 			                       'sync_port'   => sync_port,
+			                       'sync_user'   => sync_user,
 			                       'sync_pwd'    => sync_pwd,
-			                       'sync_suffix' => sync_suffix,
+			                       'sync_path'   => sync_path,
 			                       'last_update' => 0 }}
 	
-			begin
-				File.open(@file_config, 'w') do |file|
-					file << config.to_yaml
-				end
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-				return false
+			File.open(@file_config, 'w') do |file|
+				file << config.to_yaml
 			end
-	
+			
 			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+			return false
 		end
 	
 		# Check the config file
 		# @rtrn: true if the config file is correct
 		def checkconfig()
-			begin
-				config = YAML::load_file(@file_config)
-				@key         = config['config']['key']
-				@lang        = config['config']['lang']
-				@file_gpg    = config['config']['file_gpg']
-				@timeout_pwd = config['config']['timeout_pwd'].to_i
-				@sync_host   = config['config']['sync_host']
-				@sync_port   = config['config']['sync_port']
-				@sync_pwd    = config['config']['sync_pwd']
-				@sync_suffix = config['config']['sync_suffix']
-				@last_update = config['config']['last_update'].to_i
-	
-				if @key.empty? || @file_gpg.empty? 
-					@error_msg = I18n.t('error.config.check')
-					return false
-				end
-	
-				I18n.locale = @lang.to_sym
-	
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
+			config = YAML::load_file(@file_config)
+			@key         = config['config']['key']
+			@lang        = config['config']['lang']
+			@file_gpg    = config['config']['file_gpg']
+			@timeout_pwd = config['config']['timeout_pwd'].to_i
+			@sync_type   = config['config']['sync_type']
+			@sync_host   = config['config']['sync_host']
+			@sync_port   = config['config']['sync_port']
+			@sync_user   = config['config']['sync_user']
+			@sync_pwd    = config['config']['sync_pwd']
+			@sync_path   = config['config']['sync_path']
+			@last_update = config['config']['last_update'].to_i
+
+			if @key.empty? || @file_gpg.empty? 
+				@error_msg = I18n.t('error.config.check')
 				return false
 			end
-	
+
+			I18n.locale = @lang.to_sym
+
 			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
+			return false
 		end
 	
 		# Set the last update when there is a sync
 		# @rtrn: true is the file has been updated
-		def set_last_update()
+		def set_last_update
 			config = {'config' => {'key'         => @key,
 			                       'lang'        => @lang,
 			                       'file_gpg'    => @file_gpg,
 			                       'timeout_pwd' => @timeout_pwd,
+			                       'sync_type'   => @sync_type,
 			                       'sync_host'   => @sync_host,
 			                       'sync_port'   => @sync_port,
+			                       'sync_user'   => @sync_user,
 			                       'sync_pwd'    => @sync_pwd,
-			                       'sync_suffix' => @sync_suffix,
+			                       'sync_path'   => @sync_path,
 			                       'last_update' => Time.now.to_i }}
 	
-			begin
-				File.open(@file_config, 'w') do |file|
-					file << config.to_yaml
-				end
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-				return false
+			File.open(@file_config, 'w') do |file|
+				file << config.to_yaml
 			end
-	
+
 			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+			return false
 		end
 		
 	end
diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 1f84400..c57b696 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -36,45 +36,41 @@ module MPW
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(passwd=nil)
-			@data = Array.new
+			@data = []
 	
-			begin
-				if File.exist?(@file_gpg)
-					crypto = GPGME::Crypto.new(:armor => true)
-					data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
+			if File.exist?(@file_gpg)
+				crypto = GPGME::Crypto.new(:armor => true)
+				data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
 	
-					data_decrypt.lines do |line|
-						@data.push(line.parse_csv)
-					end
+				data_decrypt.lines do |line|
+					@data.push(line.parse_csv)
 				end
-	
-				return true
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
-				return false
 			end
+	
+			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
+			return false
 		end
 	
 		# Encrypt a file
 		# @rtrn: true if the file has been encrypted
-		def encrypt()
-			begin
-				crypto = GPGME::Crypto.new(:armor => true)
-				file_gpg = File.open(@file_gpg, 'w+')
+		def encrypt
+			crypto = GPGME::Crypto.new(:armor => true)
+			file_gpg = File.open(@file_gpg, 'w+')
 	
-				data_to_encrypt = ''
-				@data.each do |row|
-					data_to_encrypt << row.to_csv
-				end
-	
-				crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
-				file_gpg.close
-	
-				return true
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
-				return false
+			data_to_encrypt = ''
+			@data.each do |row|
+				data_to_encrypt << row.to_csv
 			end
+	
+			crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
+			file_gpg.close
+	
+			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+			return false
 		end
 		
 		# Search in some csv data
@@ -159,13 +155,13 @@ module MPW
 			row_update[PORT]     = port.nil?     || port.empty?     ? row[PORT]        : port
 			row_update[COMMENT]  = comment.nil?  || comment.empty?  ? row[COMMENT]     : comment
 			
-			row_update[] = row_update[NAME].nil?     ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
-			row_update[] = row_update[GROUP].nil?    ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
-			row_update[] = row_update[SERVER].nil?   ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
-			row_update[] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
-			row_update[] = row_update[LOGIN].nil?    ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
-			row_update[] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
-			row_update[] = row_update[COMMENT].nil?  ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
+			row_update[NAME]     = row_update[NAME].nil?     ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
+			row_update[GROUP]    = row_update[GROUP].nil?    ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
+			row_update[SERVER]   = row_update[SERVER].nil?   ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
+			row_update[PROTOCOL] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
+			row_update[LOGIN]    = row_update[LOGIN].nil?    ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
+			row_update[PASSWORD] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
+			row_update[COMMENT]  = row_update[COMMENT].nil?  ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
 	
 			if row_update[NAME].nil? || row_update[NAME].empty?
 				@error_msg = I18n.t('error.update.name_empty')
@@ -202,71 +198,65 @@ module MPW
 		# @args: file -> a string to match
 		# @rtrn: true if export work
 		def export(file)
-			begin
-				File.open(file, 'w+') do |file|
-					@data.each do |row|
-						row.delete_at(ID).delete_at(DATE)
-						file << row.to_csv
-					end
+			File.open(file, 'w+') do |file|
+				@data.each do |row|
+					row.delete_at(ID).delete_at(DATE)
+					file << row.to_csv
 				end
-	
-				return true
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
-				return false
 			end
+	
+			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
+			return false
 		end
 	
 		# Import to csv
 		# @args: file -> path to file import
 		# @rtrn: true if the import work
 		def import(file)
-			begin
-				data_new = IO.read(file)
-				data_new.lines do |line|
-					if not line =~ /(.*,){6}/
-						@error_msg = I18n.t('error.import.bad_format')
+			data_new = IO.read(file)
+			data_new.lines do |line|
+				if not line =~ /(.*,){6}/
+					@error_msg = I18n.t('error.import.bad_format')
+					return false
+				else
+					row = line.parse_csv.unshift(0)
+					if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
 						return false
-					else
-						row = line.parse_csv.unshift(0)
-						if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
-							return false
-						end
 					end
 				end
-	
-				return true
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
-				return false
 			end
+	
+			return true
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
+			return false
 		end
 	
 		# Return a preview import 
 		# @args: file -> path to file import
 		# @rtrn: an array with the items to import, if there is an error return false
 		def import_preview(file)
-			begin
-				result = Array.new()
-				id = 0
-	
-				data = IO.read(file)
-				data.lines do |line|
-					if not line =~ /(.*,){6}/
-						@error_msg = I18n.t('error.import.bad_format')
-						return false
-					else
-						result.push(line.parse_csv.unshift(id))
-					end
-	
-					id += 1
+			result = Array.new()
+			id = 0
+
+			data = IO.read(file)
+			data.lines do |line|
+				if not line =~ /(.*,){6}/
+					@error_msg = I18n.t('error.import.bad_format')
+					return false
+				else
+					result.push(line.parse_csv.unshift(id))
 				end
-	
-				return result
-			rescue Exception => e 
-				@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
-				return false
+
+				id += 1
 			end
+
+			return result
+		rescue Exception => e 
+			@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
+			return false
 		end
 	
 		# Sync remote data and local data
@@ -308,7 +298,7 @@ module MPW
 				end
 			end
 	
-			return encrypt()
+			return encrypt
 		end
 	
 		# Generate a random password
diff --git a/MPW/Sync/MPW.rb b/MPW/Sync/MPW.rb
index 14a43c5..d0462a8 100644
--- a/MPW/Sync/MPW.rb
+++ b/MPW/Sync/MPW.rb
@@ -12,7 +12,7 @@ module MPW
 		require 'socket'
 		require 'json'
 		
-		class MPW
+		class MPWSync
 		
 			attr_accessor :error_msg
 			attr_accessor :enable
@@ -30,12 +30,12 @@ module MPW
 			#        password -> the remote password
 			#        suffix -> the suffix file
 			# @rtrn: false if the connection fail
-			def connect(host, port, gpg_key, password, suffix=nil)
-				@gpg_key  = gpg_key
+			def connect(host, user, password, path, port=nil)
+				@gpg_key  = user
 				@password = password
-				@suffix   = suffix
+				@suffix   = path
 		
-				@socket = TCPSocket.new(host, port)
+				@socket = TCPSocket.new(host, port.to_i)
 				@enable = true
 			rescue Exception => e
 				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
@@ -64,24 +64,23 @@ module MPW
 					@error_msg = I18n.t('error.sync.communication')
 					return nil
 				elsif msg['error'].nil?
-					tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
+					tmp_file = tmpfile
 					File.open(tmp_file, 'w') do |file|
 						file << msg['data']
 					end
 					
-					@mpw = MPW.new(tmp_file)
-					if !@mpw.decrypt(gpg_password)
-						puts @mpw.error_msg
+					mpw = MPW.new(tmp_file)
+					if !mpw.decrypt(gpg_password)
+						@error_msg = mpw.error_msg
 						return nil
 					end
-		
+					
 					File.unlink(tmp_file)
-					return @mpw.search()
+					return mpw.search
 				else
 					@error_msg = I18n.t(msg['error'])
 					return nil
 				end
-		
 			end
 		
 			# Update the remote data
@@ -121,6 +120,16 @@ module MPW
 				send_msg = {:action => 'close'}
 				@socket.puts send_msg.to_json
 			end
+
+			# Generate a random string
+			# @rtrn: a random string
+			def tmpfile
+				result = ''
+				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(6).join
+		
+				return "/tmp/mpw-#{result}"
+			end
+	
 		end
 
 	end
diff --git a/MPW/Sync/SSH.rb b/MPW/Sync/SSH.rb
index b63d912..bd92fc6 100644
--- a/MPW/Sync/SSH.rb
+++ b/MPW/Sync/SSH.rb
@@ -41,7 +41,6 @@ module MPW
 				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
 				@enable    = false
 			else
-		
 				return @enable
 			end
 		
@@ -53,7 +52,7 @@ module MPW
 					return nil
 				end
 				
-				tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
+				tmp_file = tmpfile
 				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |ssh|
 					ssh.scp.download(@path, tmp_file)
 				end
@@ -62,15 +61,14 @@ module MPW
 					file << msg['data']
 				end
 					
-				@mpw = MPW.new(tmp_file)
-				if !@mpw.decrypt(gpg_password)
-					puts @mpw.error_msg
+				mpw = MPW.new(tmp_file)
+				if !mpw.decrypt(gpg_password)
+					@error_msg = mpw.error_msg
 					return nil
 				end
 		
 				File.unlink(tmp_file)
-
-				return @mpw.search()
+				return mpw.search
 			rescue Exception => e
 				@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
 				return nil
@@ -84,7 +82,7 @@ module MPW
 					return true
 				end
 		
-				tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
+				tmp_file = tmpfile
 				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |ssh|
 					ssh.scp.upload(tmp_file, @path)
 				end
@@ -100,6 +98,16 @@ module MPW
 			# Close the connection
 			def close
 			end
+
+			# Generate a random string
+			# @rtrn: a random string
+			def tmpfile
+				result = ''
+				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(6).join
+		
+				return "/tmp/mpw-#{result}"
+			end
+	
 		end
 
 	end
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 870c9a2..1b0ce0b 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -23,39 +23,44 @@ class Cli
 	end
 
 	# Close sync
-	def sync_close()
-		@sync.close()
+	def sync_close
+		@sync.close
 	end
 
 	# Sync the data with the server
 	# @rtnr: true if the synchro is finish
-	def sync()
+	def sync
 		if !defined?(@sync)
-			@sync = MPW::Sync::MPW.new
-
-			if !@config.sync_host.nil? && !@config.sync_port.nil?
-				if !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
-					puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
-				end
+			case @config.sync_type
+			when 'mpw'
+				@sync = MPW::Sync::MPWSync.new
+			when 'sftp', 'scp', 'ssh'
+				@sync = MPW::Sync::SSH.new
+			else
+				return false
 			end
 		end
 		
-		begin
-			if @sync.enable
-				if !@mpw.sync(@sync.get(@passwd), @config.last_update)
-					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
-				elsif !@sync.update(File.open(@config.file_gpg).read)
-					puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
-				elsif !@config.set_last_update()
-					puts "#{I18n.t('display.error')}: #{@config.error_msg}"
-				else
-					return true
-				end
+		if !@config.sync_host.nil? && !@config.sync_port.nil?
+			if !@sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
+				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
 			end
-		rescue Exception => e
-			puts "#{I18n.t('display.error')}: #{e}"
 		end
 
+		if @sync.enable
+			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
+				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+			elsif !@sync.update(File.open(@config.file_gpg).read)
+				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+			elsif !@config.set_last_update
+				puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			else
+				return true
+			end
+		end
+	rescue Exception => e
+		puts "#{I18n.t('display.error')}: #{e}"
+	else
 		return false
 	end
 
@@ -96,7 +101,7 @@ class Cli
 	end
 
 	# Request the GPG password and decrypt the file
-	def decrypt()
+	def decrypt
 		if !defined?(@mpw)
 			@mpw = MPW::MPW.new(@config.file_gpg, @config.key)
 		end

From 1f5b37ed3c0a217aede1223f7b9a4f1929c9dedf Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 1 Feb 2014 00:09:22 +0100
Subject: [PATCH 055/531] change array.new

---
 MPW/MPW.rb    | 10 +++++-----
 MPW/UI/Cli.rb |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index c57b696..6d851d6 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -78,7 +78,7 @@ module MPW
 		#        protocol -> the connection protocol (ssh, web, other)
 		# @rtrn: a list with the resultat of the search
 		def search(search='', group=nil, protocol=nil)
-			result = Array.new()
+			result = []
 	
 			if !search.nil?
 				search = search.downcase
@@ -110,7 +110,7 @@ module MPW
 				end
 			end
 	
-			return Array.new()
+			return []
 		end
 	
 		# Update an item
@@ -125,7 +125,7 @@ module MPW
 		#        comment -> a comment
 		# @rtrn: true if the item has been updated
 		def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
-			row    = Array.new()
+			row    = []
 			update = false
 	
 			i  = 0
@@ -142,7 +142,7 @@ module MPW
 				port = nil
 			end
 	
-			row_update       = Array.new()
+			row_update       = []
 			row_update[DATE] = Time.now.to_i
 	
 			row_update[ID]       = id.nil?       || id.empty?       ? MPW.password(16) : id
@@ -238,7 +238,7 @@ module MPW
 		# @args: file -> path to file import
 		# @rtrn: an array with the items to import, if there is an error return false
 		def import_preview(file)
-			result = Array.new()
+			result = []
 			id = 0
 
 			data = IO.read(file)
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 1b0ce0b..8501f22 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -165,7 +165,7 @@ class Cli
 
 	# Form to add a new item
 	def add()
-		row = Array.new()
+		row = []
 		puts I18n.t('form.add.title')
 		puts '--------------------'
 		name     = ask(I18n.t('form.add.name')).to_s

From dbfe1184900047f7ed76b903c6288c95e018cf46 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 1 Feb 2014 00:30:52 +0100
Subject: [PATCH 056/531] beautiful code

---
 lib/Server.rb | 106 +++++++++++++++++++++++---------------------------
 mpw           |   2 +
 2 files changed, 51 insertions(+), 57 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 3baa22f..1821f5e 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -17,13 +17,8 @@ class Server
 
 	# Start the server
 	def start()
-		begin
-			server = TCPServer.open(@host, @port)
-			@log.info("The server is started on #{@host}:#{@port}")
-		rescue Exception => e
-			@log.error("Impossible to start the server: #{e}")
-			exit 2
-		end
+		server = TCPServer.open(@host, @port)
+		@log.info("The server is started on #{@host}:#{@port}")
 
 		loop do
 			Thread.start(server.accept) do |client|
@@ -68,6 +63,10 @@ class Server
 				end
 			end
 		end
+
+	rescue Exception => e
+		@log.error("Impossible to start the server: #{e}")
+		exit 2
 	end
 
 	# Get a gpg file
@@ -228,13 +227,11 @@ class Server
 	# @args: client -> client connection
 	# @rtrn: array of the json string, or false if isn't json message
 	def get_client_msg(client)
-		begin
-			msg = client.gets
-			return JSON.parse(msg)
-		rescue
-			closeConnection(client)
-			return false
-		end
+		msg = client.gets
+		return JSON.parse(msg)
+	rescue
+		closeConnection(client)
+		return false
 	end
 
 	# Close the client connection
@@ -248,46 +245,43 @@ class Server
 	# @args: file_config -> the configuration file
 	# @rtrn: true if the config file is correct
 	def checkconfig(file_config)
-		begin
-			config    = YAML::load_file(file_config)
-			@host     = config['config']['host']
-			@port     = config['config']['port'].to_i
-			@data_dir = config['config']['data_dir']
-			@log_file = config['config']['log_file']
-			@timeout  = config['config']['timeout'].to_i
+		config    = YAML::load_file(file_config)
+		@host     = config['config']['host']
+		@port     = config['config']['port'].to_i
+		@data_dir = config['config']['data_dir']
+		@log_file = config['config']['log_file']
+		@timeout  = config['config']['timeout'].to_i
 
-			if @host.empty? || @port <= 0 || @data_dir.empty? 
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.empty')
-				return false
-			end
-
-			if !Dir.exist?(@data_dir)
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.datadir')
-				return false
-			end
-
-			if @log_file.nil? || @log_file.empty?
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.log_file_empty')
-				return false
-			else
-				begin
-					@log = Logger.new(@log_file)
-				rescue
-					puts I18n.t('checkconfig.fail')
-					puts I18n.t('checkconfig.log_file_create')
-					return false
-				end
-			end
-
-		rescue Exception => e 
-			puts "#{I18n.t('checkconfig.fail')}\n#{e}"
+		if @host.empty? || @port <= 0 || @data_dir.empty? 
+			puts I18n.t('checkconfig.fail')
+			puts I18n.t('checkconfig.empty')
 			return false
 		end
 
+		if !Dir.exist?(@data_dir)
+			puts I18n.t('checkconfig.fail')
+			puts I18n.t('checkconfig.datadir')
+			return false
+		end
+
+		if @log_file.nil? || @log_file.empty?
+			puts I18n.t('checkconfig.fail')
+			puts I18n.t('checkconfig.log_file_empty')
+			return false
+		else
+			begin
+				@log = Logger.new(@log_file)
+			rescue
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.log_file_create')
+				return false
+			end
+		end
+
 		return true
+	rescue Exception => e 
+		puts "#{I18n.t('checkconfig.fail')}\n#{e}"
+		return false
 	end
 
 	# Create a new config file
@@ -308,16 +302,14 @@ class Server
 		                       'log_file' => log_file,
 		                       'timeout'  => timeout}}
 
-		begin
-			File.open(file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-		rescue Exception => e 
-			puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
-			return false
+		File.open(file_config, 'w') do |file|
+			file << config.to_yaml
 		end
-
+			
 		return true
+	rescue Exception => e 
+		puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
+		return false
 	end
 
 	# Generate a random salt
diff --git a/mpw b/mpw
index ae59416..e5cf0a3 100755
--- a/mpw
+++ b/mpw
@@ -135,11 +135,13 @@ else
 	begin
 		cli.interactive()
 	rescue SystemExit, Interrupt
+		cli.sync_close()
 		cli = nil
 		return 1
 	end
 end
 
 cli.sync_close()
+cli = nil
 
 exit 0

From b71f98d2384fe1b51afa2e87ab0e75c223c322ba Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 1 Feb 2014 13:52:50 +0100
Subject: [PATCH 057/531] clean code

---
 MPW/UI/Cli.rb | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 8501f22..20ba037 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -76,19 +76,16 @@ class Cli
 		sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
 		sync_port   = ask(I18n.t('form.setup.sync_port')).to_s
 		sync_pwd    = ask(I18n.t('form.setup.sync_pwd')).to_s
-		sync_suffix = ask(I18n.t('form.setup.sync_suffix')).to_s
+		sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
 		
-		if !File.exist?("#{APP_ROOT}/i18n/#{language}.yml")
-			language= 'en'
-		end
 		I18n.locale = language.to_sym
 
-		sync_host   = sync_host.empty?   ? nil : sync_host
-		sync_port   = sync_port.empty?   ? nil : sync_port.to_i
-		sync_pwd    = sync_pwd.empty?    ? nil : sync_pwd
-		sync_suffix = sync_suffix.empty? ? nil : sync_suffix
+		sync_host = sync_host.empty? ? nil : sync_host
+		sync_port = sync_port.empty? ? nil : sync_port.to_i
+		sync_pwd  = sync_pwd.empty?  ? nil : sync_pwd
+		sync_path = sync_path.empty? ? nil : sync_path
 
-		if @config.setup(key, language, file_gpg, timeout_pwd, sync_host, sync_port, sync_pwd, sync_suffix)
+		if @config.setup(key, language, file_gpg, timeout_pwd, sync_host, sync_port, sync_pwd, sync_path)
 			puts I18n.t('form.setup.valid')
 		else
 			puts "#{I18n.t('display.error')}: #{@config.error_msg}"

From 1043b5c21c09af96801d7ca35f2895d60c250506 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 1 Feb 2014 19:11:19 +0100
Subject: [PATCH 058/531] fix sync via ssh

---
 MPW/Sync/SSH.rb | 25 ++++++++++++++-----------
 MPW/UI/Cli.rb   | 14 ++++++++++++--
 2 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/MPW/Sync/SSH.rb b/MPW/Sync/SSH.rb
index bd92fc6..e69a6e2 100644
--- a/MPW/Sync/SSH.rb
+++ b/MPW/Sync/SSH.rb
@@ -9,8 +9,10 @@ module MPW
 
 		require 'rubygems'
 		require 'i18n'
+		require 'net/ssh'
+		require 'net/scp'
 		
-		class MPW
+		class SSH
 		
 			attr_accessor :error_msg
 			attr_accessor :enable
@@ -33,11 +35,12 @@ module MPW
 				@user     = user
 				@password = password
 				@path     = path
-				@port     = port.nil? || port.empty? ? 22 : port.to_i
+				@port     = port.instance_of?(Integer) ? 22 : port
 					
 				Net::SSH.start(@host, @user, :password => @password, :port => @port) do
+					@enable = true
 				end
-			rescue
+			rescue Exception => e
 				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
 				@enable    = false
 			else
@@ -53,14 +56,10 @@ module MPW
 				end
 				
 				tmp_file = tmpfile
-				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |ssh|
-					ssh.scp.download(@path, tmp_file)
+				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |scp|
+					scp.download!(@path, tmp_file)
 				end
 			
-				File.open(tmp_file, 'w') do |file|
-					file << msg['data']
-				end
-					
 				mpw = MPW.new(tmp_file)
 				if !mpw.decrypt(gpg_password)
 					@error_msg = mpw.error_msg
@@ -83,8 +82,12 @@ module MPW
 				end
 		
 				tmp_file = tmpfile
-				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |ssh|
-					ssh.scp.upload(tmp_file, @path)
+				File.open(tmp_file, "w") do |file|
+					file << data
+				end
+
+				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |scp|
+					scp.upload!(tmp_file, @path)
 				end
 
 				File.unlink(tmp_file)
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 20ba037..1b5c017 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -12,6 +12,7 @@ require 'yaml'
 
 require "#{APP_ROOT}/MPW/MPW"
 require "#{APP_ROOT}/MPW/Sync/MPW"
+require "#{APP_ROOT}/MPW/Sync/SSH"
 
 class Cli
 
@@ -50,7 +51,9 @@ class Cli
 		if @sync.enable
 			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
 				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
-			elsif !@sync.update(File.open(@config.file_gpg).read)
+			end
+
+			if !@sync.update(File.open(@config.file_gpg).read)
 				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
 			elsif !@config.set_last_update
 				puts "#{I18n.t('display.error')}: #{@config.error_msg}"
@@ -60,6 +63,9 @@ class Cli
 		end
 	rescue Exception => e
 		puts "#{I18n.t('display.error')}: #{e}"
+		puts @sync.error_msg
+		puts @config.error_msg
+		puts @mpw.error_msg
 	else
 		return false
 	end
@@ -73,19 +79,23 @@ class Cli
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
 		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => Dir.home())).to_s
 		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
+		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
 		sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
 		sync_port   = ask(I18n.t('form.setup.sync_port')).to_s
+		sync_user   = ask(I18n.t('form.setup.sync_user')).to_s
 		sync_pwd    = ask(I18n.t('form.setup.sync_pwd')).to_s
 		sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
 		
 		I18n.locale = language.to_sym
 
+		sync_type = sync_type.empty? ? nil : sync_type
 		sync_host = sync_host.empty? ? nil : sync_host
 		sync_port = sync_port.empty? ? nil : sync_port.to_i
+		sync_user = sync_user.empty? ? nil : sync_user
 		sync_pwd  = sync_pwd.empty?  ? nil : sync_pwd
 		sync_path = sync_path.empty? ? nil : sync_path
 
-		if @config.setup(key, language, file_gpg, timeout_pwd, sync_host, sync_port, sync_pwd, sync_path)
+		if @config.setup(key, language, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 			puts I18n.t('form.setup.valid')
 		else
 			puts "#{I18n.t('display.error')}: #{@config.error_msg}"

From 63af86e7d43009aa54d612d686025eb0984f9ba6 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 1 Feb 2014 19:29:56 +0100
Subject: [PATCH 059/531] add package for sync via ssh

---
 README.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 277d937..b2eda64 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # Manage your passwords!
 
 MPW is a little software which stores your passwords in an GPG encrypted file.
-MPW can sync your password with a MPW Server.
+MPW can synchronize your password with a MPW Server or via SSH.
 
 # Installation
 
@@ -12,5 +12,9 @@ This program work with ruby >= 1.9
 
 * apt-get install ruby ruby-gpgme ruby-highline ruby-i18n ruby-locale
 
+If you want to synchronize your password via SSH/SCP:
+* apt-get install ruby-ssh ruby-scp
+
 For mpw-ssh:
 * apt-get install sshpass
+

From b1c281f3186d32bd94fc21b0415c6366addcfd84 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 1 Feb 2014 20:17:55 +0100
Subject: [PATCH 060/531] fix I18n.enforce_available_locales

---
 mpw        | 16 ++++++++++++++++
 mpw-server | 19 ++++++++++++++++++-
 mpw-ssh    | 16 ++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/mpw b/mpw
index e5cf0a3..ebe9f2a 100755
--- a/mpw
+++ b/mpw
@@ -13,13 +13,25 @@ APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
 require "#{APP_ROOT}/MPW/UI/Cli"
 require "#{APP_ROOT}/MPW/Config"
 
+# --------------------------------------------------------- #
+# Set local
+# --------------------------------------------------------- #
+
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
+if defined? I18n.enforce_available_locales
+	I18n.enforce_available_locales = true
+end
+
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
 I18n.load_path = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
 options = {}
 options[:force]  = false
 options[:format] = false
@@ -93,6 +105,10 @@ OptionParser.new do |opts|
 	end
 end.parse!
 
+# --------------------------------------------------------- #
+# Main
+# --------------------------------------------------------- #
+
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig()
 
diff --git a/mpw-server b/mpw-server
index 07b42f5..d1f5192 100755
--- a/mpw-server
+++ b/mpw-server
@@ -12,13 +12,25 @@ require 'i18n'
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
 require "#{APP_ROOT}/lib/Server.rb"
 
+# --------------------------------------------------------- #
+# Set local
+# --------------------------------------------------------- #
+
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
+if defined? I18n.enforce_available_locales
+	I18n.enforce_available_locales = true
+end
+
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
 I18n.load_path = Dir["#{APP_ROOT}/i18n/server/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
 options = {}
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
@@ -41,6 +53,10 @@ OptionParser.new do |opts|
 	end
 end.parse!
 
+# --------------------------------------------------------- #
+# Main
+# --------------------------------------------------------- #
+
 if options[:config].nil? || options[:config].empty?
 	puts "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
 	exit 2
@@ -54,8 +70,9 @@ elsif options[:setup]
 	server.setup(options[:config])
 else
 	if server.checkconfig(options[:config])
-		server.start()
+		server.start
 	end
 end
 
+server = nil
 exit 0
diff --git a/mpw-ssh b/mpw-ssh
index 9ba3791..f90cc81 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -15,11 +15,23 @@ require "#{APP_ROOT}/MPW/Config"
 
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
+# --------------------------------------------------------- #
+# Set local
+# --------------------------------------------------------- #
+
+if defined? I18n.enforce_available_locales
+	I18n.enforce_available_locales = true
+end
+
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
 I18n.load_path = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
 options = {}
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
@@ -46,6 +58,10 @@ OptionParser.new do |opts|
 	end
 end.parse!
 
+# --------------------------------------------------------- #
+# Main
+# --------------------------------------------------------- #
+
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig()
 

From 71fb489958e5002ec1fd0990b517c9342c990b94 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 2 Feb 2014 10:16:10 +0100
Subject: [PATCH 061/531] delete sync close function

---
 MPW/Config.rb   |  6 +++---
 MPW/Sync/MPW.rb | 54 +++++++++++++++++++++++--------------------------
 MPW/Sync/SSH.rb |  4 ----
 MPW/UI/Cli.rb   | 31 ++++++++++++----------------
 lib/Server.rb   |  6 +++---
 mpw             | 12 +++++------
 mpw-ssh         |  8 ++++----
 7 files changed, 53 insertions(+), 68 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 92a58fd..2919fcf 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -30,7 +30,7 @@ module MPW
 		# @args: file_config -> the specify config file
 		def initialize(file_config=nil)
 			@error_msg   = nil
-			@file_config = "#{Dir.home()}/.mpw.cfg"
+			@file_config = "#{Dir.home}/.mpw.cfg"
 	
 			if !file_config.nil? && !file_config.empty?
 				@file_config = file_config
@@ -57,7 +57,7 @@ module MPW
 			end
 			
 			if file_gpg.empty?
-				file_gpg = "#{Dir.home()}/.mpw.gpg"
+				file_gpg = "#{Dir.home}/.mpw.gpg"
 			end
 	
 			timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
@@ -86,7 +86,7 @@ module MPW
 	
 		# Check the config file
 		# @rtrn: true if the config file is correct
-		def checkconfig()
+		def checkconfig
 			config = YAML::load_file(@file_config)
 			@key         = config['config']['key']
 			@lang        = config['config']['lang']
diff --git a/MPW/Sync/MPW.rb b/MPW/Sync/MPW.rb
index d0462a8..69fdd83 100644
--- a/MPW/Sync/MPW.rb
+++ b/MPW/Sync/MPW.rb
@@ -35,8 +35,9 @@ module MPW
 				@password = password
 				@suffix   = path
 		
-				@socket = TCPSocket.new(host, port.to_i)
-				@enable = true
+				TCPSocket.new(host, port.to_i) do 
+					@enable = true
+				end
 			rescue Exception => e
 				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
 				@enable    = false
@@ -51,17 +52,19 @@ module MPW
 				if !@enable
 					return nil
 				end
-		
-				send_msg = {:action   => 'get',
-				            :gpg_key  => @gpg_key,
-				            :password => @password,
-				            :suffix   => @suffix}
 				
-				@socket.puts send_msg.to_json
-				msg = JSON.parse(@socket.gets)
-		
+				TCPSocket.new(host, port.to_i) do |socket|
+					send_msg = {:action   => 'get',
+						    :gpg_key  => @gpg_key,
+						    :password => @password,
+						    :suffix   => @suffix}
+					
+					socket.puts send_msg.to_json
+					msg = JSON.parse(socket.gets)
+				end
+
 				if !defined?(msg['error'])
-					@error_msg = I18n.t('error.sync.communication')
+					error_msg = I18n.t('error.sync.communication')
 					return nil
 				elsif msg['error'].nil?
 					tmp_file = tmpfile
@@ -81,6 +84,7 @@ module MPW
 					@error_msg = I18n.t(msg['error'])
 					return nil
 				end
+				
 			end
 		
 			# Update the remote data
@@ -91,14 +95,16 @@ module MPW
 					return true
 				end
 		
-				send_msg = {:action   => 'update',
-				            :gpg_key  => @gpg_key,
-				            :password => @password,
-				            :suffix   => @suffix,
-				            :data     => data}
-				
-				@socket.puts send_msg.to_json
-				msg = JSON.parse(@socket.gets)
+				TCPSocket.new(host, port.to_i) do |socket|
+					send_msg = {:action   => 'update',
+						    :gpg_key  => @gpg_key,
+						    :password => @password,
+						    :suffix   => @suffix,
+						    :data     => data}
+					
+					socket.puts send_msg.to_json
+					msg = JSON.parse(socket.gets)
+				end
 		
 				if !defined?(msg['error'])
 					@error_msg = I18n.t('error.sync.communication')
@@ -111,16 +117,6 @@ module MPW
 				end
 			end
 		
-			# Close the connection
-			def close
-				if !@enable
-					return
-				end
-		
-				send_msg = {:action => 'close'}
-				@socket.puts send_msg.to_json
-			end
-
 			# Generate a random string
 			# @rtrn: a random string
 			def tmpfile
diff --git a/MPW/Sync/SSH.rb b/MPW/Sync/SSH.rb
index e69a6e2..21c9c03 100644
--- a/MPW/Sync/SSH.rb
+++ b/MPW/Sync/SSH.rb
@@ -97,10 +97,6 @@ module MPW
 				@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
 				return false
 			end
-		
-			# Close the connection
-			def close
-			end
 
 			# Generate a random string
 			# @rtrn: a random string
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 1b5c017..ead9701 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -23,11 +23,6 @@ class Cli
 		@config = config
 	end
 
-	# Close sync
-	def sync_close
-		@sync.close
-	end
-
 	# Sync the data with the server
 	# @rtnr: true if the synchro is finish
 	def sync
@@ -77,7 +72,7 @@ class Cli
 		puts '--------------------'
 		language    = ask(I18n.t('form.setup.lang', :lang => lang)).to_s
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
-		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => Dir.home())).to_s
+		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => Dir.home)).to_s
 		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
 		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
 		sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
@@ -101,7 +96,7 @@ class Cli
 			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
 		end
 
-		if not @config.checkconfig()
+		if not @config.checkconfig
 			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
 			exit 2
 		end
@@ -171,7 +166,7 @@ class Cli
 	end
 
 	# Form to add a new item
-	def add()
+	def add
 		row = []
 		puts I18n.t('form.add.title')
 		puts '--------------------'
@@ -185,8 +180,8 @@ class Cli
 		comment  = ask(I18n.t('form.add.comment')).to_s
 
 		if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
-			if @mpw.encrypt()
-				sync()
+			if @mpw.encrypt
+				sync
 				puts I18n.t('form.add.valid')
 			else
 				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
@@ -214,8 +209,8 @@ class Cli
 			comment  = ask(I18n.t('form.update.comment' , :comment => row[MPW::MPW::COMMENT])).to_s
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
-				if @mpw.encrypt()
-					sync()
+				if @mpw.encrypt
+					sync
 					puts I18n.t('form.update.valid')
 				else
 					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
@@ -249,8 +244,8 @@ class Cli
 
 		if force
 			if @mpw.remove(id)
-				if @mpw.encrypt()
-					sync()
+				if @mpw.encrypt
+					sync
 					puts I18n.t('form.delete.valid', :id => id)
 				else
 					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
@@ -294,8 +289,8 @@ class Cli
 		end
 
 		if force
-			if @mpw.import(file) && @mpw.encrypt()
-				sync()
+			if @mpw.import(file) && @mpw.encrypt
+				sync
 				puts I18n.t('form.import.valid')
 			else
 				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
@@ -304,7 +299,7 @@ class Cli
 	end
 
 	# Interactive mode
-	def interactive()
+	def interactive
 		group       = nil
 		last_access = Time.now.to_i
 
@@ -331,7 +326,7 @@ class Cli
 					display(command[1], group, command[2])
 				end
 			when 'add', 'a'
-				add()
+				add
 			when 'update', 'u'
 				if !command[1].nil? && !command[1].empty?
 					update(command[1])
diff --git a/lib/Server.rb b/lib/Server.rb
index 1821f5e..85d8f98 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -11,12 +11,12 @@ class Server
 	attr_accessor :error_msg
 
 	# Constructor
-	def initialize()
+	def initialize
 		YAML::ENGINE.yamler='syck'
 	end
 
 	# Start the server
-	def start()
+	def start
 		server = TCPServer.open(@host, @port)
 		@log.info("The server is started on #{@host}:#{@port}")
 
@@ -134,7 +134,7 @@ class Server
 			hash      = gpg_data['gpg']['hash']
 
 		else
-			salt = salt()
+			salt = salt
 			hash = Digest::SHA256.hexdigest(salt + msg['password'])
 		end
 
diff --git a/mpw b/mpw
index ebe9f2a..c13baeb 100755
--- a/mpw
+++ b/mpw
@@ -110,7 +110,7 @@ end.parse!
 # --------------------------------------------------------- #
 
 config      = MPW::Config.new(options[:config])
-check_error = config.checkconfig()
+check_error = config.checkconfig
 
 cli = Cli.new(lang, config)
 	
@@ -119,8 +119,8 @@ if !check_error || !options[:setup].nil?
 	cli.setup(lang)
 end
 
-cli.decrypt()
-cli.sync()
+cli.decrypt
+cli.sync
 
 # Display the item's informations
 if not options[:display].nil?
@@ -136,7 +136,7 @@ elsif not options[:update].nil?
 
 # Add a new item
 elsif not options[:add].nil?
-	cli.add()
+	cli.add
 
 # Export
 elsif not options[:export].nil?
@@ -149,15 +149,13 @@ elsif not options[:import].nil?
 # Interactive mode
 else
 	begin
-		cli.interactive()
+		cli.interactive
 	rescue SystemExit, Interrupt
-		cli.sync_close()
 		cli = nil
 		return 1
 	end
 end
 
-cli.sync_close()
 cli = nil
 
 exit 0
diff --git a/mpw-ssh b/mpw-ssh
index f90cc81..079771e 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -63,7 +63,7 @@ end.parse!
 # --------------------------------------------------------- #
 
 config      = MPW::Config.new(options[:config])
-check_error = config.checkconfig()
+check_error = config.checkconfig
 
 cli         = CliSSH.new(lang, config)
 cli.login   = options[:login]
@@ -80,11 +80,11 @@ elsif ARGV.length < 1
 	puts "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
 	exit 2
 else
-	cli.decrypt()
-	cli.sync()
+	cli.decrypt
+	cli.sync
 	cli.ssh(search)
 end
 
-cli.sync_close()
+cli = nil
 
 exit 0

From ae98d34135054bfa9fecc69b4cb31cf8acaca628 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 2 Feb 2014 10:45:18 +0100
Subject: [PATCH 062/531] rename MPWSync

---
 MPW/Sync/{MPW.rb => MPWSync.rb} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename MPW/Sync/{MPW.rb => MPWSync.rb} (100%)

diff --git a/MPW/Sync/MPW.rb b/MPW/Sync/MPWSync.rb
similarity index 100%
rename from MPW/Sync/MPW.rb
rename to MPW/Sync/MPWSync.rb

From f59a5584cf7fec87720c985828ca2ea7a31defa8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 2 Feb 2014 11:24:28 +0100
Subject: [PATCH 063/531] add sync via FTP

---
 MPW/Sync/FTP.rb | 115 ++++++++++++++++++++++++++++++++++++++++++++++++
 MPW/UI/Cli.rb   |   5 ++-
 2 files changed, 119 insertions(+), 1 deletion(-)
 create mode 100644 MPW/Sync/FTP.rb

diff --git a/MPW/Sync/FTP.rb b/MPW/Sync/FTP.rb
new file mode 100644
index 0000000..6a6b236
--- /dev/null
+++ b/MPW/Sync/FTP.rb
@@ -0,0 +1,115 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+module MPW
+
+	module Sync
+
+		require 'rubygems'
+		require 'i18n'
+		require 'net/ftp'
+		
+		class FTP
+		
+			attr_accessor :error_msg
+			attr_accessor :enable
+		
+			# Constructor
+			def initialize
+				@error_msg = nil
+				@enable    = false
+			end
+		
+			# Connect to server
+			# @args: host -> the server host
+			#        port -> ther connection port
+			#        gpg_key -> the gpg key
+			#        password -> the remote password
+			#        suffix -> the suffix file
+			# @rtrn: false if the connection fail
+			def connect(host, user, password, path, port=nil)
+				@host     = host
+				@user     = user
+				@password = password
+				@path     = path
+				@port     = port.instance_of?(Integer) ? 21 : port
+					
+				Net::FTP.open(@host) do |ftp|
+					ftp.login(@user, @password)
+					@enable = true
+				end
+			rescue Exception => e
+				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+				@enable    = false
+			else
+				return @enable
+			end
+		
+			# Get data on server
+			# @args: gpg_password -> the gpg password
+			# @rtrn: nil if nothing data or error
+			def get(gpg_password)
+				if !@enable
+					return nil
+				end
+				
+				tmp_file = tmpfile
+				Net::FTP.open(@host) do |ftp|
+					ftp.login(@user, @password)
+					ftp.gettextfile(@path, tmp_file)
+				end
+			
+				mpw = MPW.new(tmp_file)
+				if !mpw.decrypt(gpg_password)
+					@error_msg = mpw.error_msg
+					return nil
+				end
+		
+				File.unlink(tmp_file)
+				return mpw.search
+			rescue Exception => e
+				@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
+				return nil
+			end
+		
+			# Update the remote data
+			# @args: data -> the data to send on server
+			# @rtrn: false if there is a problem
+			def update(data)
+				if !@enable
+					return true
+				end
+		
+				tmp_file = tmpfile
+				File.open(tmp_file, "w") do |file|
+					file << data
+				end
+
+				Net::FTP.open(@host) do |ftp|
+					ftp.login(@user, @password)
+					ftp.puttextfile(tmp_file, @path)
+				end
+
+				File.unlink(tmp_file)
+				return true
+			rescue Exception => e
+				@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
+				return false
+			end
+
+			# Generate a random string
+			# @rtrn: a random string
+			def tmpfile
+				result = ''
+				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(6).join
+		
+				return "/tmp/mpw-#{result}"
+			end
+	
+		end
+
+	end
+
+end
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index ead9701..75adfa0 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -11,8 +11,9 @@ require 'i18n'
 require 'yaml'
 
 require "#{APP_ROOT}/MPW/MPW"
-require "#{APP_ROOT}/MPW/Sync/MPW"
+require "#{APP_ROOT}/MPW/Sync/MPWSync"
 require "#{APP_ROOT}/MPW/Sync/SSH"
+require "#{APP_ROOT}/MPW/Sync/FTP"
 
 class Cli
 
@@ -32,6 +33,8 @@ class Cli
 				@sync = MPW::Sync::MPWSync.new
 			when 'sftp', 'scp', 'ssh'
 				@sync = MPW::Sync::SSH.new
+			when 'ftp'
+				@sync = MPW::Sync::FTP.new
 			else
 				return false
 			end

From 851ca63f90a31cf2d7cd348c047418ea5c094164 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 2 Feb 2014 11:48:46 +0100
Subject: [PATCH 064/531] add translate

---
 i18n/cli/en.yml | 13 +++++++++++--
 i18n/cli/fr.yml | 13 +++++++++++--
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index bf8b9fc..88fce39 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -20,9 +20,11 @@ en:
     sync: 
       connection: "Connection fail!"
       communication: "A communication problem with the server is appeared!"
-      no_data: "Nothing data!"
+      download: "Can't download the file!"
       not_authorized: "You haven't the access to remote file!"
+      upload: "Can't upload the file on the server!"
       unknown: "An unknown error is occured!" 
+
   option:
     usage: "Usage"
     show: "Search and show the items"
@@ -40,6 +42,7 @@ en:
     format: "Change the display items format by an alternative format"
     generate_password: "Generate a random password (default 8 characters)"
     help: "Show this help message"
+
   form:
     add:
       title: "Add a new item"
@@ -66,10 +69,12 @@ en:
       gpg_key: "Enter the GPG key: " 
       gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
       timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
+      sync_type: "Synchronization type (mpw, ssh, ftp, or nil): "
       sync_host: "Synchronization server: "
       sync_port: "Port of the synchronization server: "
+      sync_user: "Username for the synchronization: "
       sync_pwd: "Password for the synchronization: "
-      sync_suffix: "Suffix for the synchronization (optionnal): "
+      sync_path: "File path for the synchronization : "
       valid: "The config file has been created!"
     update:
       title: "Update an item"
@@ -82,6 +87,7 @@ en:
       port: "Enter the connection port [%{port}]: "
       comment: "Enter a comment [%{comment}]: "
       valid: "Item has been updated!" 
+
   interactive:
     ask_password: "Password GPG: "
     bad_password: "Bad password!"
@@ -96,6 +102,7 @@ en:
       remove: "Remove an item"
       help: "Show this message"
       quit: "Quit the software"
+
   display:
     comment: "Comment"
     error: "ERROR"
@@ -108,6 +115,7 @@ en:
     port: "Port"
     protocol: "Protocol"
     server: "Server"
+
   ssh:
     option:
       usage: "Usage"
@@ -118,6 +126,7 @@ en:
     display:
       connect: "Connection to:"
       nothing: "Nothing result!"
+
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 0936cdb..17d01b8 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -20,9 +20,11 @@ fr:
     sync: 
       connection: "La connexion n'a pu être établie!"
       communication: "Un problème de communication avec le serveur est apparu!"
-      no_data: "Aucune donnée!"
+      download: "Impossible de télécharger le fichier!"
       not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
+      upload: "Impossible d'envoyer le fichier sur le serveur!"
       unknown: "Une erreur inconnue est survenue!" 
+
   option:
     usage: "Utilisation"
     show: "Recherche et affiche les éléments"
@@ -40,6 +42,7 @@ fr:
     format: "Change le format d'affichage des éléments par un alternatif"
     generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
     help: "Affiche ce message d'aide"
+
   form:
     add:
       title: "Ajout d'un nouvel élément"
@@ -66,10 +69,12 @@ fr:
       gpg_key: "Entrez la clé GPG: " 
       gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
       timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
+      sync_type: "Type de synchronisation (mpw, ssh, ftp, or nil): "
       sync_host: "Serveur de synchronisation: "
       sync_port: "Port du serveur de synchronisation: "
+      sync_user: "Utilisateur pour la synchronisation: "
       sync_pwd: "Mot de passe pour la synchronisation: "
-      sync_suffix: "Suffix pour la synchronisation (optionnel): "
+      sync_path: "Chemin du fichier pour la synchronisation: "
       valid: "Le fichier de configuration a bien été créé!"
     update:
       title: "Mis à jour d'un élément"
@@ -82,6 +87,7 @@ fr:
       port: "Entrez un port de connexion [%{port}]: "
       comment: "Entrez un commentaire [%{comment}]: "
       valid: "L'élément a bien été mis à jour!" 
+
   interactive:
     ask_password: "Mot de passe GPG: "
     bad_password: "Mauvais mot de passe!"
@@ -96,6 +102,7 @@ fr:
       remove: "Supprime un élément"
       help: "Affiche ce message d'aide"
       quit: "Quitte le programme"
+
   display:
     comment: "Commentaire"
     error: "ERREUR"
@@ -108,6 +115,7 @@ fr:
     port: "Port"
     protocol: "Protocol"
     server: "Serveur"
+
   ssh:
     option:
       usage: "Utilisation"
@@ -118,6 +126,7 @@ fr:
     display:
       connect: "Connexion à:"
       nothing: "Aucun résultat!"
+
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'

From 076aedeb3ca868e2fba739ef3562f3f1f4eab675 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 2 Feb 2014 14:20:24 +0100
Subject: [PATCH 065/531] add share_keys in config

---
 MPW/Config.rb   | 16 +++++++++++++++-
 MPW/UI/Cli.rb   | 35 ++++++++++++++++++++++-------------
 i18n/cli/en.yml |  1 +
 i18n/cli/fr.yml |  1 +
 4 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 2919fcf..bd993bb 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -14,6 +14,7 @@ module MPW
 		attr_accessor :error_msg
 	
 		attr_accessor :key
+		attr_accessor :share_keys
 		attr_accessor :lang
 		attr_accessor :file_gpg
 		attr_accessor :timeout_pwd
@@ -39,6 +40,7 @@ module MPW
 	
 		# Create a new config file
 		# @args: key -> the gpg key to encrypt
+		#        share_keys -> multiple keys to share the password with other people
 		#        lang -> the software language
 		#        file_gpg -> the file who is encrypted
 		#        timeout_pwd -> time to save the password
@@ -49,12 +51,21 @@ module MPW
 		#        sync_pwd -> the password for synchronization
 		#        sync_suffix -> the suffix file (optionnal) 
 		# @rtrn: true if le config file is create
-		def setup(key, lang, file_gpg, timeout_pwd, sync_type=nil, sync_host=nil, sync_port=nil, sync_user=nil, sync_pwd=nil, sync_path=nil)
+		def setup(key, share_keys, lang, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 	
 			if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 				@error_msg = I18n.t('error.config.key_bad_format')
 				return false
 			end
+
+			if !share_keys.nil? && !share_keys.empty?
+				share_keys.split.each do |k|
+					if not k =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+						@error_msg = I18n.t('error.config.key_bad_format')
+						return false
+					end
+				end
+			end
 			
 			if file_gpg.empty?
 				file_gpg = "#{Dir.home}/.mpw.gpg"
@@ -63,6 +74,7 @@ module MPW
 			timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
 	
 			config = {'config' => {'key'         => key,
+			                       'share_keys'  => share_keys,
 			                       'lang'        => lang,
 			                       'file_gpg'    => file_gpg,
 			                       'timeout_pwd' => timeout_pwd,
@@ -89,6 +101,7 @@ module MPW
 		def checkconfig
 			config = YAML::load_file(@file_config)
 			@key         = config['config']['key']
+			@share_keys  = config['config']['share_keys']
 			@lang        = config['config']['lang']
 			@file_gpg    = config['config']['file_gpg']
 			@timeout_pwd = config['config']['timeout_pwd'].to_i
@@ -117,6 +130,7 @@ module MPW
 		# @rtrn: true is the file has been updated
 		def set_last_update
 			config = {'config' => {'key'         => @key,
+			                       'share_keys'  => @share_keys,
 			                       'lang'        => @lang,
 			                       'file_gpg'    => @file_gpg,
 			                       'timeout_pwd' => @timeout_pwd,
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 75adfa0..46d5a81 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -75,28 +75,37 @@ class Cli
 		puts '--------------------'
 		language    = ask(I18n.t('form.setup.lang', :lang => lang)).to_s
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
+		share_keys  = ask(I18n.t('form.setup.share_gpg_keys')).to_s
 		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => Dir.home)).to_s
 		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
 		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
-		sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
-		sync_port   = ask(I18n.t('form.setup.sync_port')).to_s
-		sync_user   = ask(I18n.t('form.setup.sync_user')).to_s
-		sync_pwd    = ask(I18n.t('form.setup.sync_pwd')).to_s
-		sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
+
+		if ['ssh', 'ftp', 'mpw'].include?(sync_type)
+			sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
+			sync_port   = ask(I18n.t('form.setup.sync_port')).to_s
+			sync_user   = ask(I18n.t('form.setup.sync_user')).to_s
+			sync_pwd    = ask(I18n.t('form.setup.sync_pwd')).to_s
+			sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
+		end
 		
-		I18n.locale = language.to_sym
+		if !language.nil? && !language.empty?
+			I18n.locale = language.to_sym
+		else
+			language = nil
+		end
 
-		sync_type = sync_type.empty? ? nil : sync_type
-		sync_host = sync_host.empty? ? nil : sync_host
-		sync_port = sync_port.empty? ? nil : sync_port.to_i
-		sync_user = sync_user.empty? ? nil : sync_user
-		sync_pwd  = sync_pwd.empty?  ? nil : sync_pwd
-		sync_path = sync_path.empty? ? nil : sync_path
+		sync_type = sync_type.nil? || sync_type.empty? ? nil : sync_type
+		sync_host = sync_host.nil? || sync_host.empty? ? nil : sync_host
+		sync_port = sync_port.nil? || sync_port.empty? ? nil : sync_port.to_i
+		sync_user = sync_user.nil? || sync_user.empty? ? nil : sync_user
+		sync_pwd  = sync_pwd.nil?  || sync_pwd.empty?  ? nil : sync_pwd
+		sync_path = sync_path.nil? || sync_path.empty? ? nil : sync_path
 
-		if @config.setup(key, language, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
+		if @config.setup(key, share_keys, language, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 			puts I18n.t('form.setup.valid')
 		else
 			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			exit 2
 		end
 
 		if not @config.checkconfig
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 88fce39..622dfe8 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -67,6 +67,7 @@ en:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...): "
       gpg_key: "Enter the GPG key: " 
+      share_gpg_key: "Enter the GPG keys with who you want to share the passwords: " 
       gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
       timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
       sync_type: "Synchronization type (mpw, ssh, ftp, or nil): "
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 17d01b8..1f2f7b2 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -67,6 +67,7 @@ fr:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
       gpg_key: "Entrez la clé GPG: " 
+      share_gpg_key: "Entrez les clés GPG avec qui vous voulez partager les mots de passe: " 
       gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
       timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
       sync_type: "Type de synchronisation (mpw, ssh, ftp, or nil): "

From bed8cbb8b65ff8be7ddcd07edf1595f76ad7d529 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 2 Feb 2014 17:34:05 +0100
Subject: [PATCH 066/531] add share_keys

---
 MPW/Config.rb       |  3 ++-
 MPW/MPW.rb          | 17 ++++++++++++-----
 MPW/Sync/MPWSync.rb | 10 +++++++---
 MPW/UI/Cli.rb       |  2 +-
 4 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index bd993bb..4ace575 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -58,7 +58,8 @@ module MPW
 				return false
 			end
 
-			if !share_keys.nil? && !share_keys.empty?
+			share_keys = share_keys.nil? ? '' : share_keys
+			if !share_keys.empty?
 				share_keys.split.each do |k|
 					if not k =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 						@error_msg = I18n.t('error.config.key_bad_format')
diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 6d851d6..05da9cf 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -26,10 +26,11 @@ module MPW
 		attr_accessor :error_msg
 		
 		# Constructor
-		def initialize(file_gpg, key=nil)
-			@error_msg = nil
-			@file_gpg  = file_gpg
-			@key       = key
+		def initialize(file_gpg, key=nil, share_keys='')
+			@error_msg  = nil
+			@file_gpg   = file_gpg
+			@key        = key
+			@share_keys = share_keys
 		end
 	
 		# Decrypt a gpg file
@@ -64,7 +65,13 @@ module MPW
 				data_to_encrypt << row.to_csv
 			end
 	
-			crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
+			recipients = []
+			recipients.push(@key)
+			if !@share_keys.nil?
+				@share_keys.split.each { |k| recipients.push(k) }
+			end
+
+			crypto.encrypt(data_to_encrypt, :recipients => recipients, :output => file_gpg)
 			file_gpg.close
 	
 			return true
diff --git a/MPW/Sync/MPWSync.rb b/MPW/Sync/MPWSync.rb
index 69fdd83..da62975 100644
--- a/MPW/Sync/MPWSync.rb
+++ b/MPW/Sync/MPWSync.rb
@@ -31,11 +31,13 @@ module MPW
 			#        suffix -> the suffix file
 			# @rtrn: false if the connection fail
 			def connect(host, user, password, path, port=nil)
+				@host     = host
+				@port     = !port.instance_of?(Integer) ? 2201 : port
 				@gpg_key  = user
 				@password = password
 				@suffix   = path
 		
-				TCPSocket.new(host, port.to_i) do 
+				TCPSocket.open(@host, @port) do 
 					@enable = true
 				end
 			rescue Exception => e
@@ -53,7 +55,8 @@ module MPW
 					return nil
 				end
 				
-				TCPSocket.new(host, port.to_i) do |socket|
+				msg = nil
+				TCPSocket.open(@host, @port) do |socket|
 					send_msg = {:action   => 'get',
 						    :gpg_key  => @gpg_key,
 						    :password => @password,
@@ -95,7 +98,8 @@ module MPW
 					return true
 				end
 		
-				TCPSocket.new(host, port.to_i) do |socket|
+				msg = nil
+				TCPSocket.open(@host, @port) do |socket|
 					send_msg = {:action   => 'update',
 						    :gpg_key  => @gpg_key,
 						    :password => @password,
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 46d5a81..708f3ec 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -117,7 +117,7 @@ class Cli
 	# Request the GPG password and decrypt the file
 	def decrypt
 		if !defined?(@mpw)
-			@mpw = MPW::MPW.new(@config.file_gpg, @config.key)
+			@mpw = MPW::MPW.new(@config.file_gpg, @config.key, @config.share_keys)
 		end
 
 		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}

From 684972e44086f9a8c1690fb1472d6d5dadd475fa Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 5 Feb 2014 22:47:20 +0100
Subject: [PATCH 067/531] clean old code

---
 lib/Server.rb | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index 85d8f98..2d55de0 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -31,11 +31,6 @@ class Server
 						next
 					end
 					
-					if !msg['action'].nil? && msg['action'] == 'close'
-						@log.info("#{client.peeraddr[3]} is disconnected")
-						close_connection(client)
-					end
-
 					if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
 						@log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
 						close_connection(client)
@@ -65,6 +60,7 @@ class Server
 		end
 
 	rescue Exception => e
+		puts "Impossible to start the server: #{e}"
 		@log.error("Impossible to start the server: #{e}")
 		exit 2
 	end

From ea9b2fb411c52a56e42b9c455ca7ea661c4c044d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 16 Mar 2014 14:44:26 +0100
Subject: [PATCH 068/531] fix error message when sync

---
 MPW/UI/Cli.rb | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 708f3ec..9753d09 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -48,22 +48,26 @@ class Cli
 
 		if @sync.enable
 			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
-				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"  if !@mpw.error_msg.nil?
+				puts "#{I18n.t('display.error')}: #{@sync.error_msg}" if !@sync.error_msg.nil?
+				return false
 			end
 
 			if !@sync.update(File.open(@config.file_gpg).read)
 				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
 			elsif !@config.set_last_update
 				puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			elsif !@mpw.encrypt
+				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
 			else
 				return true
 			end
 		end
 	rescue Exception => e
 		puts "#{I18n.t('display.error')}: #{e}"
-		puts @sync.error_msg
-		puts @config.error_msg
-		puts @mpw.error_msg
+		puts @sync.error_msg   if @sync.error_msg.nil?
+		puts @config.error_msg if @config.error_msg.nil?
+		puts @mpw.error_msg    if @mpw.error_msg.nil?
 	else
 		return false
 	end

From a0720672bd91ae7d4df8ea638bf37650b0250956 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 16 Mar 2014 15:29:21 +0100
Subject: [PATCH 069/531] minor change

---
 MPW/UI/Cli.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 9753d09..c3ea99f 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -50,10 +50,7 @@ class Cli
 			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
 				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"  if !@mpw.error_msg.nil?
 				puts "#{I18n.t('display.error')}: #{@sync.error_msg}" if !@sync.error_msg.nil?
-				return false
-			end
-
-			if !@sync.update(File.open(@config.file_gpg).read)
+			elsif !@sync.update(File.open(@config.file_gpg).read)
 				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
 			elsif !@config.set_last_update
 				puts "#{I18n.t('display.error')}: #{@config.error_msg}"

From f9f06f245caa4d8ebd12653ac460ef27d68eb529 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 16 Mar 2014 21:28:23 +0100
Subject: [PATCH 070/531] add generate gpg key

---
 MPW/Config.rb   | 46 +++++++++++++++++++++++++++++++++++++++++++++-
 MPW/UI/Cli.rb   | 29 +++++++++++++++++++++++++----
 i18n/cli/en.yml | 11 +++++++++++
 i18n/cli/fr.yml | 11 +++++++++++
 mpw             |  2 ++
 5 files changed, 94 insertions(+), 5 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 4ace575..42a840d 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -6,6 +6,7 @@
 module MPW
 
 	require 'rubygems'
+	require 'gpgme'
 	require 'yaml'
 	require 'i18n'
 	
@@ -96,6 +97,38 @@ module MPW
 			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
 			return false
 		end
+
+		# Setup a new gpg key
+		def setup_gpg_key(password, name, length = 2048, expire = 0)
+			if name.nil? || name.empty?
+				@error_msg = "#{I18n.t('error.config.genkey_gpg.name')}"
+				return false
+			elsif password.nil? || password.empty?
+				@error_msg = "#{I18n.t('error.config.genkey_gpg.password')}"
+				return false
+			end
+
+			param = ''
+			param << '<GnupgKeyParms format="internal">' + "\n"
+			param << "Key-Type: DSA\n"  
+			param << "Key-Length: #{length}\n"
+			param << "Subkey-Type: ELG-E\n"
+			param << "Subkey-Length: #{length}\n"
+			param << "Name-Real: #{name}\n"
+			param << "Name-Comment: #{name}\n"
+			param << "Name-Email: #{@key}\n"
+			param << "Expire-Date: #{expire}\n"
+			param << "Passphrase: apc\n"
+			param << "</GnupgKeyParms>\n"
+
+			ctx = GPGME::Ctx.new
+			ctx.genkey(param, nil, nil)
+
+			return true
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
+			return false
+		end
 	
 		# Check the config file
 		# @rtrn: true if the config file is correct
@@ -118,14 +151,25 @@ module MPW
 				@error_msg = I18n.t('error.config.check')
 				return false
 			end
-
 			I18n.locale = @lang.to_sym
 
 			return true
 		rescue Exception => e 
+			puts e
 			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
 			return false
 		end
+
+		# Check if private key exist
+		# @rtrn: true if the key exist, else false
+		def check_gpg_key?
+			ctx = GPGME::Ctx.new
+			ctx.each_key(@key, true) do |key|
+				return true
+			end
+
+			return false
+		end
 	
 		# Set the last update when there is a sync
 		# @rtrn: true is the file has been updated
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index c3ea99f..050e0d4 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -89,11 +89,10 @@ class Cli
 			sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
 		end
 		
-		if !language.nil? && !language.empty?
-			I18n.locale = language.to_sym
-		else
-			language = nil
+		if language.nil? || language.empty?
+			language = lang
 		end
+		I18n.locale = language.to_sym
 
 		sync_type = sync_type.nil? || sync_type.empty? ? nil : sync_type
 		sync_host = sync_host.nil? || sync_host.empty? ? nil : sync_host
@@ -115,6 +114,28 @@ class Cli
 		end
 	end
 
+	# Setup a new GPG key
+	def setup_gpg_key
+		puts I18n.t('form.setup_gpg_key.title')
+		puts '--------------------'
+		name     = ask(I18n.t('form.setup_gpg_key.name')).to_s
+		password = ask(I18n.t('form.setup_gpg_key.password')).to_s
+		length   = ask(I18n.t('form.setup_gpg_key.length')).to_s
+		expire   = ask(I18n.t('form.setup_gpg_key.expire')).to_s
+
+		length = length.nil? || length.empty? ? 2048 : length.to_i
+		expire = expire.nil? || expire.empty? ? 0    : expire.to_i
+
+		puts I18n.t('form.setup_gpg_key.wait')
+		
+		if @config.setup_gpg_key(password, name, length, expire)
+			puts I18n.t('form.setup_gpg_key.valid')
+		else
+			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			exit 2
+		end
+	end
+
 	# Request the GPG password and decrypt the file
 	def decrypt
 		if !defined?(@mpw)
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 622dfe8..dd58ab0 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -5,6 +5,10 @@ en:
       write: "Can't write the config file!"
       check: "Checkconfig failed!"
       key_bad_format: "The key string isn't in good format!"
+      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!"
     delete:
       id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
     export:
@@ -77,6 +81,13 @@ en:
       sync_pwd: "Password for the synchronization: "
       sync_path: "File path for the synchronization : "
       valid: "The config file has been created!"
+    setup_gpg_key:
+      title: "Setup a GPG key"
+      name: "Your name and lastname: "
+      password: "A password for the GPG key: "
+      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."
     update:
       title: "Update an item"
       name: "Enter the name [%{name}]: "
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 1f2f7b2..e38e00d 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -5,6 +5,10 @@ fr:
       write: "Impossible d'écrire le fichier de configuration!"
       check: "Le fichier de configuration est invalide!"
       key_bad_format: "La clé GPG est invalide!"
+      genkey_gpg:
+        exception: "La création de la clé GPG n'a pas pu aboutir!"
+        name: "Vous devez définir un nom pour votre clé GPG!"
+        password: "Vous devez définir un mot de passe pour votre clé GPG!"
     delete:
       id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
     export:
@@ -77,6 +81,13 @@ fr:
       sync_pwd: "Mot de passe pour la synchronisation: "
       sync_path: "Chemin du fichier pour la synchronisation: "
       valid: "Le fichier de configuration a bien été créé!"
+    setup_gpg_key:
+      title: "Configuration d'une clé GPG"
+      name: "Votre nom et prénom: "
+      password: "Mot de passe de la clé GPG: "
+      length: "Taille de la clé GPG [défaut=2048]: "
+      expire: "Expiration de la clé GPG [défaut=0 (illimité)]: "
+      wait: "Veuillez patienter durant la génération de votre clé GPG, ce processus peut prendre quelques minutes."
     update:
       title: "Mis à jour d'un élément"
       name: "Entrez le nom [%{name}]: "
diff --git a/mpw b/mpw
index c13baeb..6db35f0 100755
--- a/mpw
+++ b/mpw
@@ -117,6 +117,8 @@ cli = Cli.new(lang, config)
 # Setup a new config 
 if !check_error || !options[:setup].nil?
 	cli.setup(lang)
+elsif !config.check_gpg_key?
+	cli.setup_gpg_key
 end
 
 cli.decrypt

From ba297c3d7db6046b7796397d45502b1bf2e4326d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 18 Mar 2014 21:08:09 +0100
Subject: [PATCH 071/531] ask if you want create gpg key

---
 MPW/Config.rb   | 5 +++++
 MPW/UI/Cli.rb   | 7 +++++++
 i18n/cli/en.yml | 2 ++
 i18n/cli/fr.yml | 2 ++
 4 files changed, 16 insertions(+)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 42a840d..fadfbff 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -99,6 +99,11 @@ 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
 		def setup_gpg_key(password, name, length = 2048, expire = 0)
 			if name.nil? || name.empty?
 				@error_msg = "#{I18n.t('error.config.genkey_gpg.name')}"
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 050e0d4..baf522e 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -118,6 +118,13 @@ class Cli
 	def setup_gpg_key
 		puts I18n.t('form.setup_gpg_key.title')
 		puts '--------------------'
+		ask      = ask(I18n.t('form.setup_gpg_key.ask')).to_s
+		
+		if !['Y', 'y', 'O', 'o'].include?(ask)
+			puts I18n.t('form.setup_gpg_key.no_create')
+			exit 2
+		end
+
 		name     = ask(I18n.t('form.setup_gpg_key.name')).to_s
 		password = ask(I18n.t('form.setup_gpg_key.password')).to_s
 		length   = ask(I18n.t('form.setup_gpg_key.length')).to_s
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index dd58ab0..61df851 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -83,6 +83,8 @@ 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."
       name: "Your name and lastname: "
       password: "A password for the GPG key: "
       length: "Size of the GPG key [default=2048]: "
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index e38e00d..cc2a982 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -83,6 +83,8 @@ fr:
       valid: "Le fichier de configuration a bien été créé!"
     setup_gpg_key:
       title: "Configuration d'une clé GPG"
+      ask: "Voulez vous créer votre clé GPG ? (O/n)"
+      no_create: "Veuillez créer manuellement votre clé GPG ou relancer le logiciel."
       name: "Votre nom et prénom: "
       password: "Mot de passe de la clé GPG: "
       length: "Taille de la clé GPG [défaut=2048]: "

From d477a8a939f6fd783d7dcb2399b38c7372e8fbc7 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 19 Mar 2014 20:50:12 +0100
Subject: [PATCH 072/531] check if we have the public share key

---
 MPW/Config.rb   | 10 ++++++++--
 i18n/cli/en.yml |  2 ++
 i18n/cli/fr.yml |  2 ++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index fadfbff..c2c7d8d 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -66,6 +66,11 @@ module MPW
 						@error_msg = I18n.t('error.config.key_bad_format')
 						return false
 					end
+					
+					if !check_gpg_key?(k, false)
+						@error_msg = I18n.t('error.config.no_key_public', :key => k)
+						return false
+					end
 				end
 			end
 			
@@ -166,10 +171,11 @@ module MPW
 		end
 
 		# Check if private key exist
+		# @args: only_private -> search only private key
 		# @rtrn: true if the key exist, else false
-		def check_gpg_key?
+		def check_gpg_key?(key = @key, only_private = true)
 			ctx = GPGME::Ctx.new
-			ctx.each_key(@key, true) do |key|
+			ctx.each_key(key, only_private) do
 				return true
 			end
 
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 61df851..1d29686 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -5,6 +5,7 @@ en:
       write: "Can't write the config file!"
       check: "Checkconfig failed!"
       key_bad_format: "The key string isn't in good format!"
+      no_key_public: "You haven't the public key of %{key}!"
       genkey_gpg:
         exception: "Can't create the GPG key!"
         name: "You must define a name for your GPG key!"
@@ -90,6 +91,7 @@ en:
       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."
+      valid: "Your GPG key has been created ;-)"
     update:
       title: "Update an item"
       name: "Enter the name [%{name}]: "
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index cc2a982..1ea2e57 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -5,6 +5,7 @@ fr:
       write: "Impossible d'écrire le fichier de configuration!"
       check: "Le fichier de configuration est invalide!"
       key_bad_format: "La clé GPG est invalide!"
+      no_key_public: "Vous ne possédez pas la clé publique de %{key}!"
       genkey_gpg:
         exception: "La création de la clé GPG n'a pas pu aboutir!"
         name: "Vous devez définir un nom pour votre clé GPG!"
@@ -90,6 +91,7 @@ fr:
       length: "Taille de la clé GPG [défaut=2048]: "
       expire: "Expiration de la clé GPG [défaut=0 (illimité)]: "
       wait: "Veuillez patienter durant la génération de votre clé GPG, ce processus peut prendre quelques minutes."
+      valid: "Votre clé GPG a bien été créée ;-)"
     update:
       title: "Mis à jour d'un élément"
       name: "Entrez le nom [%{name}]: "

From f15b4e47bf7144f9fe32876df1555c9b75ff9ef6 Mon Sep 17 00:00:00 2001
From: adrien <adrien.waksberg@believedigital.com>
Date: Fri, 4 Apr 2014 17:03:46 +0200
Subject: [PATCH 073/531] fix show GPG password in setup and add confirm
 password

---
 MPW/UI/Cli.rb   | 10 +++++++++-
 i18n/cli/en.yml |  2 ++
 i18n/cli/fr.yml |  2 ++
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index baf522e..d59519a 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -126,9 +126,17 @@ class Cli
 		end
 
 		name     = ask(I18n.t('form.setup_gpg_key.name')).to_s
-		password = ask(I18n.t('form.setup_gpg_key.password')).to_s
+		password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
+		confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
+
+		if password != confirm 
+			puts I18n.t('form.setup_gpg_key.error_password')
+			exit 2
+		end
+
 		length   = ask(I18n.t('form.setup_gpg_key.length')).to_s
 		expire   = ask(I18n.t('form.setup_gpg_key.expire')).to_s
+		password = password.to_s
 
 		length = length.nil? || length.empty? ? 2048 : length.to_i
 		expire = expire.nil? || expire.empty? ? 0    : expire.to_i
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 1d29686..082c2f1 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -88,6 +88,8 @@ en:
       no_create: "You must 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."
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 1ea2e57..146ca02 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -88,6 +88,8 @@ fr:
       no_create: "Veuillez créer manuellement votre clé GPG ou relancer le logiciel."
       name: "Votre nom et prénom: "
       password: "Mot de passe de la clé GPG: "
+      confirm_password: "Retapez votre mot de passe: "
+      error_password: "Vos deux mots de passes ne sont pas identiques!"
       length: "Taille de la clé GPG [défaut=2048]: "
       expire: "Expiration de la clé GPG [défaut=0 (illimité)]: "
       wait: "Veuillez patienter durant la génération de votre clé GPG, ce processus peut prendre quelques minutes."

From 57d1a700c65d5bd45dac3cb4e0d19b3be6a09d7c Mon Sep 17 00:00:00 2001
From: adrien <adrien.waksberg@believedigital.com>
Date: Fri, 4 Apr 2014 17:04:51 +0200
Subject: [PATCH 074/531] fix missing and error translation

---
 i18n/cli/en.yml | 2 +-
 i18n/cli/fr.yml | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 082c2f1..e8f7d16 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -72,7 +72,7 @@ en:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...): "
       gpg_key: "Enter the GPG key: " 
-      share_gpg_key: "Enter the GPG keys with who you want to share the passwords: " 
+      share_gpg_keys: "Enter the GPG keys with who you want to share the passwords: " 
       gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
       timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
       sync_type: "Synchronization type (mpw, ssh, ftp, or nil): "
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 146ca02..bb5491d 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -53,7 +53,7 @@ fr:
       title: "Ajout d'un nouvel élément"
       name: "Entrez le nom: "
       group: "Entrez le groupe (optionnel): "
-      server: "Entrez the hostname or ip: "
+      server: "Entrez le nom de domaine ou l'ip: "
       protocol: "Entrez le protocole de connexion (ssh, http, other): "
       login: "Entrez l'identifiant de connexion: "
       password: "Entrez le mot de passe: "
@@ -72,7 +72,7 @@ fr:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
       gpg_key: "Entrez la clé GPG: " 
-      share_gpg_key: "Entrez les clés GPG avec qui vous voulez partager les mots de passe: " 
+      share_gpg_keys: "Entrez les clés GPG avec qui vous voulez partager les mots de passe: " 
       gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
       timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
       sync_type: "Type de synchronisation (mpw, ssh, ftp, or nil): "

From 08fbd258462d600dccf963d033e7cfe124869cce Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 21 Apr 2014 22:30:41 +0200
Subject: [PATCH 075/531] fix the issue 3, bad password

---
 MPW/Config.rb | 48 +++++++++++++++++++++++++++++++-----------------
 1 file changed, 31 insertions(+), 17 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index c2c7d8d..4a3b6b1 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -59,19 +59,8 @@ module MPW
 				return false
 			end
 
-			share_keys = share_keys.nil? ? '' : share_keys
-			if !share_keys.empty?
-				share_keys.split.each do |k|
-					if not k =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-						@error_msg = I18n.t('error.config.key_bad_format')
-						return false
-					end
-					
-					if !check_gpg_key?(k, false)
-						@error_msg = I18n.t('error.config.no_key_public', :key => k)
-						return false
-					end
-				end
+			if !check_public_gpg_key(share_keys)
+				return false
 			end
 			
 			if file_gpg.empty?
@@ -128,7 +117,7 @@ module MPW
 			param << "Name-Comment: #{name}\n"
 			param << "Name-Email: #{@key}\n"
 			param << "Expire-Date: #{expire}\n"
-			param << "Passphrase: apc\n"
+			param << "Passphrase: #{password}\n"
 			param << "</GnupgKeyParms>\n"
 
 			ctx = GPGME::Ctx.new
@@ -171,16 +160,41 @@ module MPW
 		end
 
 		# Check if private key exist
-		# @args: only_private -> search only private key
 		# @rtrn: true if the key exist, else false
-		def check_gpg_key?(key = @key, only_private = true)
+		def check_gpg_key?
 			ctx = GPGME::Ctx.new
-			ctx.each_key(key, only_private) do
+			ctx.each_key(key, true) do
 				return true
 			end
 
 			return false
 		end
+
+		# Check if private key exist
+		# @args: share_keys -> string with all public keys
+		# @rtrn: true if the key exist, else false
+		def check_public_gpg_key(share_keys = @share_keys)
+			ctx = GPGME::Ctx.new
+
+			share_keys = share_keys.nil? ? '' : share_keys
+			if !share_keys.empty?
+				share_keys.split.each do |k|
+					if not k =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+						@error_msg = I18n.t('error.config.key_bad_format')
+						return false
+					end
+					
+					ctx.each_key(key, false) do
+						next
+					end
+
+					@error_msg = I18n.t('error.config.no_key_public', :key => k)
+					return false
+				end
+			end
+
+			return true
+		end
 	
 		# Set the last update when there is a sync
 		# @rtrn: true is the file has been updated

From 7305ecef0f3a3cf30f885950dca353ecc01ba2df Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 15:13:43 +0200
Subject: [PATCH 076/531] fix bug salt in mpw server

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

diff --git a/lib/Server.rb b/lib/Server.rb
index 2d55de0..94ca075 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -130,7 +130,7 @@ class Server
 			hash      = gpg_data['gpg']['hash']
 
 		else
-			salt = salt
+			salt = generate_salt
 			hash = Digest::SHA256.hexdigest(salt + msg['password'])
 		end
 
@@ -311,7 +311,7 @@ class Server
 	# Generate a random salt
 	# @args: length -> the length salt
 	# @rtrn: a random string
-	def salt(length=4)
+	def generate_salt(length=4)
 		if length.to_i <= 0 || length.to_i > 16
 			length = 4
 		else

From 0739be1763426315ba47f9f0cd6589e40094a271 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 15:17:37 +0200
Subject: [PATCH 077/531] fix bug when the MPW server haven't data

---
 MPW/MPW.rb          |  2 ++
 MPW/Sync/MPWSync.rb | 13 +++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 05da9cf..71e6f3d 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -273,6 +273,8 @@ module MPW
 		def sync(data_remote, last_update)
 			if !data_remote.instance_of?(Array)
 				return false
+			else data_remote.nil? || data_remote.empty?
+				return true
 			end
 	
 			@data.each do |l|
diff --git a/MPW/Sync/MPWSync.rb b/MPW/Sync/MPWSync.rb
index da62975..8e6c700 100644
--- a/MPW/Sync/MPWSync.rb
+++ b/MPW/Sync/MPWSync.rb
@@ -67,9 +67,14 @@ module MPW
 				end
 
 				if !defined?(msg['error'])
-					error_msg = I18n.t('error.sync.communication')
+					@error_msg = I18n.t('error.sync.communication')
 					return nil
-				elsif msg['error'].nil?
+				elsif !msg['error'].nil?
+					@error_msg = I18n.t(msg['error'])
+					return nil
+				elsif msg['data'].nil? || msg['data'].empty?
+					return []
+				else
 					tmp_file = tmpfile
 					File.open(tmp_file, 'w') do |file|
 						file << msg['data']
@@ -83,11 +88,7 @@ module MPW
 					
 					File.unlink(tmp_file)
 					return mpw.search
-				else
-					@error_msg = I18n.t(msg['error'])
-					return nil
 				end
-				
 			end
 		
 			# Update the remote data

From fe973c2fac6937e59f5333e6c1aa7a8393207bea Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 18:10:21 +0200
Subject: [PATCH 078/531] fix bug sync when the gpg file doesn't exist

---
 MPW/MPW.rb | 42 ++++++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 71e6f3d..f48bad4 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -273,30 +273,28 @@ module MPW
 		def sync(data_remote, last_update)
 			if !data_remote.instance_of?(Array)
 				return false
-			else data_remote.nil? || data_remote.empty?
-				return true
-			end
-	
-			@data.each do |l|
-				j = 0
-				update = false
-	
-				# Update item
-				data_remote.each do |r|
-					if l[ID] == r[ID]
-						if l[DATE].to_i < r[DATE].to_i
-							update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
+			else !data_remote.nil? && !data_remote.empty?
+				@data.each do |l|
+					j = 0
+					update = false
+		
+					# Update item
+					data_remote.each do |r|
+						if l[ID] == r[ID]
+							if l[DATE].to_i < r[DATE].to_i
+								update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
+							end
+							update = true
+							data_remote.delete_at(j)
+							break
 						end
-						update = true
-						data_remote.delete_at(j)
-						break
+						j += 1
+					end
+		
+					# Delete an old item
+					if !update && l[DATE].to_i < last_update
+						remove(l[ID])
 					end
-					j += 1
-				end
-	
-				# Delete an old item
-				if !update && l[DATE].to_i < last_update
-					remove(l[ID])
 				end
 			end
 	

From 64c0e7766f12e874e8f30e0b17c5006c672c4e0c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 18:16:42 +0200
Subject: [PATCH 079/531] fix bug, you can't install the ruby-net-scp

---
 MPW/UI/Cli.rb | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index d59519a..2ea57f1 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -11,9 +11,6 @@ require 'i18n'
 require 'yaml'
 
 require "#{APP_ROOT}/MPW/MPW"
-require "#{APP_ROOT}/MPW/Sync/MPWSync"
-require "#{APP_ROOT}/MPW/Sync/SSH"
-require "#{APP_ROOT}/MPW/Sync/FTP"
 
 class Cli
 
@@ -30,10 +27,13 @@ class Cli
 		if !defined?(@sync)
 			case @config.sync_type
 			when 'mpw'
+				require "#{APP_ROOT}/MPW/Sync/MPWSync"
 				@sync = MPW::Sync::MPWSync.new
 			when 'sftp', 'scp', 'ssh'
+				require "#{APP_ROOT}/MPW/Sync/SSH"
 				@sync = MPW::Sync::SSH.new
 			when 'ftp'
+				require "#{APP_ROOT}/MPW/Sync/FTP"
 				@sync = MPW::Sync::FTP.new
 			else
 				return false

From e290352dceccb88854638c9c51bddc892830abf5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 18:17:13 +0200
Subject: [PATCH 080/531] update readme

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index b2eda64..7816d0d 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ This program work with ruby >= 1.9
 * apt-get install ruby ruby-gpgme ruby-highline ruby-i18n ruby-locale
 
 If you want to synchronize your password via SSH/SCP:
-* apt-get install ruby-ssh ruby-scp
+* apt-get install ruby-net-ssh ruby-net-scp
 
 For mpw-ssh:
 * apt-get install sshpass

From 3c272b3127c3a39d4654adf33967d68d11972a41 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 18:23:28 +0200
Subject: [PATCH 081/531] mv lib/Server.rb in MPW

---
 MPW/Server.rb | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/Server.rb | 324 -------------------------------------------------
 mpw-server    |   4 +-
 3 files changed, 331 insertions(+), 326 deletions(-)
 create mode 100644 MPW/Server.rb
 delete mode 100644 lib/Server.rb

diff --git a/MPW/Server.rb b/MPW/Server.rb
new file mode 100644
index 0000000..2a52994
--- /dev/null
+++ b/MPW/Server.rb
@@ -0,0 +1,329 @@
+#!/usr/bin/ruby
+
+module MPW
+
+	require 'socket'
+	require 'json'
+	require 'highline/import'
+	require 'digest'
+	require 'logger'
+
+
+	class Server
+
+		attr_accessor :error_msg
+
+		# Constructor
+		def initialize
+			YAML::ENGINE.yamler='syck'
+		end
+
+		# Start the server
+		def start
+			server = TCPServer.open(@host, @port)
+			@log.info("The server is started on #{@host}:#{@port}")
+
+			loop do
+				Thread.start(server.accept) do |client|
+					@log.info("#{client.peeraddr[3]} is connected")
+
+					while true do
+						msg = get_client_msg(client)
+
+						if !msg
+							next
+						end
+						
+						if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
+							@log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
+							close_connection(client)
+							next
+						end
+
+						case msg['action']
+						when 'get'
+							@log.debug("#{client.peeraddr[3]} GET gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
+							client.puts get_file(msg)
+						when 'update'
+							@log.debug("#{client.peeraddr[3]} UPDATE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
+							client.puts update_file(msg)
+						when 'delete'
+							@log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
+							client.puts delete_file(msg)
+						else
+							@log.warning("#{client.peeraddr[3]} is disconnected because unkwnow command")
+							send_msg = {:action      => 'unknown',
+								    :gpg_key     => msg['gpg_key'],
+								    :error       => 'server.error.client.unknown'}
+							client.puts send_msg 
+							close_connection(client)
+						end
+					end
+				end
+			end
+
+		rescue Exception => e
+			puts "Impossible to start the server: #{e}"
+			@log.error("Impossible to start the server: #{e}")
+			exit 2
+		end
+
+		# Get a gpg file
+		# @args: msg -> message puts by the client
+		# @rtrn: json message
+		def get_file(msg)
+			gpg_key = msg['gpg_key'].sub('@', '_')
+
+			if msg['suffix'].nil? || msg['suffix'].empty?
+				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
+			else
+				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
+			end
+
+			if File.exist?(file_gpg)
+				gpg_data    = YAML::load_file(file_gpg)
+				salt        = gpg_data['gpg']['salt']
+				hash        = gpg_data['gpg']['hash']
+				data        = gpg_data['gpg']['data']
+
+				if is_authorized?(msg['password'], salt, hash)
+					send_msg = {:action      => 'get',
+						    :gpg_key     => msg['gpg_key'],
+						    :data        => data,
+						    :error       => nil}
+				else
+					send_msg = {:action  => 'get',
+						    :gpg_key => msg['gpg_key'],
+						    :error   => 'server.error.client.no_authorized'}
+				end
+			else
+				send_msg = {:action  => 'get',
+					    :gpg_key => msg['gpg_key'],
+					    :data    => '',
+					    :error   => nil}
+			end
+
+			return send_msg.to_json
+		end
+
+		# Update a file
+		# @args: msg -> message puts by the client
+		# @rtrn: json message
+		def update_file(msg)
+			gpg_key = msg['gpg_key'].sub('@', '_')
+			data    = msg['data']
+
+			if data.nil? || data.empty?
+				send_msg = {:action  => 'update',
+					    :gpg_key => msg['gpg_key'],
+					    :error   => 'server.error.client.no_data'}
+				
+				return send_msg.to_json
+			end
+
+			if msg['suffix'].nil? || msg['suffix'].empty?
+				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
+			else
+				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
+			end
+
+			if File.exist?(file_gpg)
+				gpg_data  = YAML::load_file(file_gpg)
+				salt      = gpg_data['gpg']['salt']
+				hash      = gpg_data['gpg']['hash']
+
+			else
+				salt = generate_salt
+				hash = Digest::SHA256.hexdigest(salt + msg['password'])
+			end
+
+			if is_authorized?(msg['password'], salt, hash)
+				begin
+					config = {'gpg' => {'salt'        => salt,
+							    'hash'        => hash,
+							    'data'        => data}}
+
+					File.open(file_gpg, 'w+') do |file|
+						file << config.to_yaml
+					end
+
+					send_msg = {:action  => 'update',
+						    :gpg_key => msg['gpg_key'],
+						    :error   => nil}
+				rescue Exception => e
+					send_msg = {:action  => 'update',
+						    :gpg_key => msg['gpg_key'],
+						    :error   => 'server.error.client.unknown'}
+				end
+			else
+				send_msg = {:action  => 'update',
+					    :gpg_key => msg['gpg_key'],
+					    :error   => 'server.error.client.no_authorized'}
+			end
+			
+			return send_msg.to_json
+		end
+
+		# Remove a gpg file
+		# @args: msg -> message puts by the client
+		# @rtrn: json message
+		def delete_file(msg)
+			gpg_key = msg['gpg_key'].sub('@', '_')
+
+			if msg['suffix'].nil? || msg['suffix'].empty?
+				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
+			else
+				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
+			end
+
+			if !File.exist?(file_gpg)
+				send_msg = {:action  => 'delete',
+					    :gpg_key => msg['gpg_key'],
+					    :error   => nil}
+
+				return send_msg.to_json
+			end
+
+			gpg_data  = YAML::load_file(file_gpg)
+			salt      = gpg_data['gpg']['salt']
+			hash      = gpg_data['gpg']['hash']
+
+			if is_authorized?(msg['password'], salt, hash)
+				begin
+					File.unlink(file_gpg)
+
+					send_msg = {:action  => 'delete',
+						    :gpg_key => msg['gpg_key'],
+						    :error   => nil}
+				rescue Exception => e
+					send_msg = {:action  => 'delete',
+						    :gpg_key => msg['gpg_key'],
+						    :error   => 'server.error.client.unknown'}
+				end
+			else
+				send_msg = {:action  => 'delete',
+					    :gpg_key => msg['gpg_key'],
+					    :error   => 'server.error.client.no_authorized'}
+			end
+			
+			return send_msg.to_json
+		end
+
+		# Check is the hash equal the password with the salt
+		# @args: password -> the user password
+		#        salt -> the salt
+		#        hash -> the hash of the password with the salt
+		# @rtrn: true is is good, else false
+		def is_authorized?(password, salt, hash)
+			if hash == Digest::SHA256.hexdigest(salt + password)
+				return true
+			else
+				return false
+			end
+		end
+
+		# Get message to client
+		# @args: client -> client connection
+		# @rtrn: array of the json string, or false if isn't json message
+		def get_client_msg(client)
+			msg = client.gets
+			return JSON.parse(msg)
+		rescue
+			closeConnection(client)
+			return false
+		end
+
+		# Close the client connection
+		# @args: client -> client connection
+		def close_connection(client)
+				client.puts "Closing the connection. Bye!"
+				client.close
+		end
+
+		# Check the config file
+		# @args: file_config -> the configuration file
+		# @rtrn: true if the config file is correct
+		def checkconfig(file_config)
+			config    = YAML::load_file(file_config)
+			@host     = config['config']['host']
+			@port     = config['config']['port'].to_i
+			@data_dir = config['config']['data_dir']
+			@log_file = config['config']['log_file']
+			@timeout  = config['config']['timeout'].to_i
+
+			if @host.empty? || @port <= 0 || @data_dir.empty? 
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.empty')
+				return false
+			end
+
+			if !Dir.exist?(@data_dir)
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.datadir')
+				return false
+			end
+
+			if @log_file.nil? || @log_file.empty?
+				puts I18n.t('checkconfig.fail')
+				puts I18n.t('checkconfig.log_file_empty')
+				return false
+			else
+				begin
+					@log = Logger.new(@log_file)
+				rescue
+					puts I18n.t('checkconfig.fail')
+					puts I18n.t('checkconfig.log_file_create')
+					return false
+				end
+			end
+
+			return true
+		rescue Exception => e 
+			puts "#{I18n.t('checkconfig.fail')}\n#{e}"
+			return false
+		end
+
+		# Create a new config file
+		# @args: file_config -> the configuration file
+		# @rtrn: true if le config file is create
+		def setup(file_config)
+			puts I18n.t('form.setup.title')
+			puts '--------------------'
+			host     = ask(I18n.t('form.setup.host')).to_s
+			port     = ask(I18n.t('form.setup.port')).to_s
+			data_dir = ask(I18n.t('form.setup.data_dir')).to_s
+			log_file = ask(I18n.t('form.setup.log_file')).to_s
+			timeout  = ask(I18n.t('form.setup.timeout')).to_s
+
+			config = {'config' => {'host'     => host,
+					       'port'     => port,
+					       'data_dir' => data_dir,
+					       'log_file' => log_file,
+					       'timeout'  => timeout}}
+
+			File.open(file_config, 'w') do |file|
+				file << config.to_yaml
+			end
+				
+			return true
+		rescue Exception => e 
+			puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
+			return false
+		end
+
+		# Generate a random salt
+		# @args: length -> the length salt
+		# @rtrn: a random string
+		def generate_salt(length=4)
+			if length.to_i <= 0 || length.to_i > 16
+				length = 4
+			else
+				length = length.to_i
+			end
+
+			return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
+		end
+		
+	end
+
+end
diff --git a/lib/Server.rb b/lib/Server.rb
deleted file mode 100644
index 94ca075..0000000
--- a/lib/Server.rb
+++ /dev/null
@@ -1,324 +0,0 @@
-#!/usr/bin/ruby
-
-require 'socket'
-require 'json'
-require 'highline/import'
-require 'digest'
-require 'logger'
-
-class Server
-
-	attr_accessor :error_msg
-
-	# Constructor
-	def initialize
-		YAML::ENGINE.yamler='syck'
-	end
-
-	# Start the server
-	def start
-		server = TCPServer.open(@host, @port)
-		@log.info("The server is started on #{@host}:#{@port}")
-
-		loop do
-			Thread.start(server.accept) do |client|
-				@log.info("#{client.peeraddr[3]} is connected")
-
-				while true do
-					msg = get_client_msg(client)
-
-					if !msg
-						next
-					end
-					
-					if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
-						@log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
-						close_connection(client)
-						next
-					end
-
-					case msg['action']
-					when 'get'
-						@log.debug("#{client.peeraddr[3]} GET gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-						client.puts get_file(msg)
-					when 'update'
-						@log.debug("#{client.peeraddr[3]} UPDATE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-						client.puts update_file(msg)
-					when 'delete'
-						@log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-						client.puts delete_file(msg)
-					else
-						@log.warning("#{client.peeraddr[3]} is disconnected because unkwnow command")
-						send_msg = {:action      => 'unknown',
-						            :gpg_key     => msg['gpg_key'],
-						            :error       => 'server.error.client.unknown'}
-						client.puts send_msg 
-						close_connection(client)
-					end
-				end
-			end
-		end
-
-	rescue Exception => e
-		puts "Impossible to start the server: #{e}"
-		@log.error("Impossible to start the server: #{e}")
-		exit 2
-	end
-
-	# Get a gpg file
-	# @args: msg -> message puts by the client
-	# @rtrn: json message
-	def get_file(msg)
-		gpg_key = msg['gpg_key'].sub('@', '_')
-
-		if msg['suffix'].nil? || msg['suffix'].empty?
-			file_gpg = "#{@data_dir}/#{gpg_key}.yml"
-		else
-			file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
-		end
-
-		if File.exist?(file_gpg)
-			gpg_data    = YAML::load_file(file_gpg)
-			salt        = gpg_data['gpg']['salt']
-			hash        = gpg_data['gpg']['hash']
-			data        = gpg_data['gpg']['data']
-
-			if is_authorized?(msg['password'], salt, hash)
-				send_msg = {:action      => 'get',
-				            :gpg_key     => msg['gpg_key'],
-				            :data        => data,
-				            :error       => nil}
-			else
-				send_msg = {:action  => 'get',
-				            :gpg_key => msg['gpg_key'],
-				            :error   => 'server.error.client.no_authorized'}
-			end
-		else
-			send_msg = {:action  => 'get',
-			            :gpg_key => msg['gpg_key'],
-			            :data    => '',
-			            :error   => nil}
-		end
-
-		return send_msg.to_json
-	end
-
-	# Update a file
-	# @args: msg -> message puts by the client
-	# @rtrn: json message
-	def update_file(msg)
-		gpg_key = msg['gpg_key'].sub('@', '_')
-		data    = msg['data']
-
-		if data.nil? || data.empty?
-			send_msg = {:action  => 'update',
-			            :gpg_key => msg['gpg_key'],
-			            :error   => 'server.error.client.no_data'}
-			
-			return send_msg.to_json
-		end
-
-		if msg['suffix'].nil? || msg['suffix'].empty?
-			file_gpg = "#{@data_dir}/#{gpg_key}.yml"
-		else
-			file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
-		end
-
-		if File.exist?(file_gpg)
-			gpg_data  = YAML::load_file(file_gpg)
-			salt      = gpg_data['gpg']['salt']
-			hash      = gpg_data['gpg']['hash']
-
-		else
-			salt = generate_salt
-			hash = Digest::SHA256.hexdigest(salt + msg['password'])
-		end
-
-		if is_authorized?(msg['password'], salt, hash)
-			begin
-				config = {'gpg' => {'salt'        => salt,
-				                    'hash'        => hash,
-				                    'data'        => data}}
-
-				File.open(file_gpg, 'w+') do |file|
-					file << config.to_yaml
-				end
-
-				send_msg = {:action  => 'update',
-				            :gpg_key => msg['gpg_key'],
-				            :error   => nil}
-			rescue Exception => e
-				send_msg = {:action  => 'update',
-				            :gpg_key => msg['gpg_key'],
-				            :error   => 'server.error.client.unknown'}
-			end
-		else
-			send_msg = {:action  => 'update',
-			            :gpg_key => msg['gpg_key'],
-			            :error   => 'server.error.client.no_authorized'}
-		end
-		
-		return send_msg.to_json
-	end
-
-	# Remove a gpg file
-	# @args: msg -> message puts by the client
-	# @rtrn: json message
-	def delete_file(msg)
-		gpg_key = msg['gpg_key'].sub('@', '_')
-
-		if msg['suffix'].nil? || msg['suffix'].empty?
-			file_gpg = "#{@data_dir}/#{gpg_key}.yml"
-		else
-			file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
-		end
-
-		if !File.exist?(file_gpg)
-			send_msg = {:action  => 'delete',
-			            :gpg_key => msg['gpg_key'],
-			            :error   => nil}
-
-			return send_msg.to_json
-		end
-
-		gpg_data  = YAML::load_file(file_gpg)
-		salt      = gpg_data['gpg']['salt']
-		hash      = gpg_data['gpg']['hash']
-
-		if is_authorized?(msg['password'], salt, hash)
-			begin
-				File.unlink(file_gpg)
-
-				send_msg = {:action  => 'delete',
-				            :gpg_key => msg['gpg_key'],
-				            :error   => nil}
-			rescue Exception => e
-				send_msg = {:action  => 'delete',
-				            :gpg_key => msg['gpg_key'],
-				            :error   => 'server.error.client.unknown'}
-			end
-		else
-			send_msg = {:action  => 'delete',
-			            :gpg_key => msg['gpg_key'],
-			            :error   => 'server.error.client.no_authorized'}
-		end
-		
-		return send_msg.to_json
-	end
-
-	# Check is the hash equal the password with the salt
-	# @args: password -> the user password
-	#        salt -> the salt
-	#        hash -> the hash of the password with the salt
-	# @rtrn: true is is good, else false
-	def is_authorized?(password, salt, hash)
-		if hash == Digest::SHA256.hexdigest(salt + password)
-			return true
-		else
-			return false
-		end
-	end
-
-	# Get message to client
-	# @args: client -> client connection
-	# @rtrn: array of the json string, or false if isn't json message
-	def get_client_msg(client)
-		msg = client.gets
-		return JSON.parse(msg)
-	rescue
-		closeConnection(client)
-		return false
-	end
-
-	# Close the client connection
-	# @args: client -> client connection
-	def close_connection(client)
-			client.puts "Closing the connection. Bye!"
-			client.close
-	end
-
-	# Check the config file
-	# @args: file_config -> the configuration file
-	# @rtrn: true if the config file is correct
-	def checkconfig(file_config)
-		config    = YAML::load_file(file_config)
-		@host     = config['config']['host']
-		@port     = config['config']['port'].to_i
-		@data_dir = config['config']['data_dir']
-		@log_file = config['config']['log_file']
-		@timeout  = config['config']['timeout'].to_i
-
-		if @host.empty? || @port <= 0 || @data_dir.empty? 
-			puts I18n.t('checkconfig.fail')
-			puts I18n.t('checkconfig.empty')
-			return false
-		end
-
-		if !Dir.exist?(@data_dir)
-			puts I18n.t('checkconfig.fail')
-			puts I18n.t('checkconfig.datadir')
-			return false
-		end
-
-		if @log_file.nil? || @log_file.empty?
-			puts I18n.t('checkconfig.fail')
-			puts I18n.t('checkconfig.log_file_empty')
-			return false
-		else
-			begin
-				@log = Logger.new(@log_file)
-			rescue
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.log_file_create')
-				return false
-			end
-		end
-
-		return true
-	rescue Exception => e 
-		puts "#{I18n.t('checkconfig.fail')}\n#{e}"
-		return false
-	end
-
-	# Create a new config file
-	# @args: file_config -> the configuration file
-	# @rtrn: true if le config file is create
-	def setup(file_config)
-		puts I18n.t('form.setup.title')
-		puts '--------------------'
-		host     = ask(I18n.t('form.setup.host')).to_s
-		port     = ask(I18n.t('form.setup.port')).to_s
-		data_dir = ask(I18n.t('form.setup.data_dir')).to_s
-		log_file = ask(I18n.t('form.setup.log_file')).to_s
-		timeout  = ask(I18n.t('form.setup.timeout')).to_s
-
-		config = {'config' => {'host'     => host,
-		                       'port'     => port,
-		                       'data_dir' => data_dir,
-		                       'log_file' => log_file,
-		                       'timeout'  => timeout}}
-
-		File.open(file_config, 'w') do |file|
-			file << config.to_yaml
-		end
-			
-		return true
-	rescue Exception => e 
-		puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
-		return false
-	end
-
-	# Generate a random salt
-	# @args: length -> the length salt
-	# @rtrn: a random string
-	def generate_salt(length=4)
-		if length.to_i <= 0 || length.to_i > 16
-			length = 4
-		else
-			length = length.to_i
-		end
-
-		return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
-	end
-	
-end
diff --git a/mpw-server b/mpw-server
index d1f5192..0c94289 100755
--- a/mpw-server
+++ b/mpw-server
@@ -10,7 +10,7 @@ require 'locale'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/lib/Server.rb"
+require "#{APP_ROOT}/MPW/Server.rb"
 
 # --------------------------------------------------------- #
 # Set local
@@ -62,7 +62,7 @@ if options[:config].nil? || options[:config].empty?
 	exit 2
 end
 
-server = Server.new
+server = MPW::Server.new
 
 if options[:checkconfig]
 	server.checkconfig(options[:config])

From b52443f9f127888ff569b43c6e0664c1a8e9d521 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 18:31:50 +0200
Subject: [PATCH 082/531] add the numbering of errors

---
 MPW/UI/Cli.rb | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 2ea57f1..cb49464 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -42,26 +42,26 @@ class Cli
 		
 		if !@config.sync_host.nil? && !@config.sync_port.nil?
 			if !@sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
-				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+				puts "#{I18n.t('display.error')} #1: #{@sync.error_msg}"
 			end
 		end
 
 		if @sync.enable
 			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
-				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"  if !@mpw.error_msg.nil?
-				puts "#{I18n.t('display.error')}: #{@sync.error_msg}" if !@sync.error_msg.nil?
+				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}"  if !@mpw.error_msg.nil?
+				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}" if !@sync.error_msg.nil?
 			elsif !@sync.update(File.open(@config.file_gpg).read)
-				puts "#{I18n.t('display.error')}: #{@sync.error_msg}"
+				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}"
 			elsif !@config.set_last_update
-				puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+				puts "#{I18n.t('display.error')} #5: #{@config.error_msg}"
 			elsif !@mpw.encrypt
-				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #6: #{@mpw.error_msg}"
 			else
 				return true
 			end
 		end
 	rescue Exception => e
-		puts "#{I18n.t('display.error')}: #{e}"
+		puts "#{I18n.t('display.error')} #7: #{e}"
 		puts @sync.error_msg   if @sync.error_msg.nil?
 		puts @config.error_msg if @config.error_msg.nil?
 		puts @mpw.error_msg    if @mpw.error_msg.nil?
@@ -104,12 +104,12 @@ class Cli
 		if @config.setup(key, share_keys, language, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 			puts I18n.t('form.setup.valid')
 		else
-			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}"
 			exit 2
 		end
 
 		if not @config.checkconfig
-			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')} #9: #{@config.error_msg}"
 			exit 2
 		end
 	end
@@ -146,7 +146,7 @@ class Cli
 		if @config.setup_gpg_key(password, name, length, expire)
 			puts I18n.t('form.setup_gpg_key.valid')
 		else
-			puts "#{I18n.t('display.error')}: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')} #10: #{@config.error_msg}"
 			exit 2
 		end
 	end
@@ -159,7 +159,7 @@ class Cli
 
 		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
 		if !@mpw.decrypt(@passwd)
-			puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}"
 			exit 2
 		end
 	end
@@ -233,10 +233,10 @@ class Cli
 				sync
 				puts I18n.t('form.add.valid')
 			else
-				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #12: #{@mpw.error_msg}"
 			end
 		else
-			puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')} #13: #{@mpw.error_msg}"
 		end
 	end
 
@@ -262,10 +262,10 @@ class Cli
 					sync
 					puts I18n.t('form.update.valid')
 				else
-					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+					puts "#{I18n.t('display.error')} #14: #{@mpw.error_msg}"
 				end
 			else
-				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #15: #{@mpw.error_msg}"
 			end
 		else
 			puts I18n.t('display.nothing')
@@ -297,7 +297,7 @@ class Cli
 					sync
 					puts I18n.t('form.delete.valid', :id => id)
 				else
-					puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+					puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}"
 				end
 			else
 				puts I18n.t('form.delete.not_valid')
@@ -311,7 +311,7 @@ class Cli
 		if @mpw.export(file)
 			puts "The export in #{file} is succesfull!"
 		else
-			puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}"
 		end
 
 	end
@@ -342,7 +342,7 @@ class Cli
 				sync
 				puts I18n.t('form.import.valid')
 			else
-				puts "#{I18n.t('display.error')}: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #18: #{@mpw.error_msg}"
 			end
 		end
 	end

From d17a8b557eecfcecbbccd3132f06db122ca0061a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 27 Apr 2014 18:53:20 +0200
Subject: [PATCH 083/531] add a directory conf

---
 MPW/Config.rb   | 19 +++++++++++++++----
 MPW/UI/Cli.rb   |  2 +-
 i18n/cli/en.yml |  2 +-
 i18n/cli/fr.yml |  2 +-
 4 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 4a3b6b1..9f6d4a1 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -27,13 +27,22 @@ module MPW
 		attr_accessor :sync_pwd
 		attr_accessor :sync_path
 		attr_accessor :last_update
+		attr_accessor :dir_config
 	
 		# Constructor
 		# @args: file_config -> the specify config file
 		def initialize(file_config=nil)
-			@error_msg   = nil
-			@file_config = "#{Dir.home}/.mpw.cfg"
-	
+			@error_msg  = nil
+
+			if /darwin/ =~ RUBY_PLATFORM
+				@dir_config = "#{Dir.home}/Library/Preferences/mpw"
+			elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+				@dir_config = "#{Dir.home}/AppData/Local/mpw"
+			else 
+				@dir_config = "#{Dir.home}/.config/mpw"
+			end
+			
+			@file_config = "#{@dir_config}/conf/default.cfg"
 			if !file_config.nil? && !file_config.empty?
 				@file_config = file_config
 			end
@@ -64,7 +73,7 @@ module MPW
 			end
 			
 			if file_gpg.empty?
-				file_gpg = "#{Dir.home}/.mpw.gpg"
+				file_gpg = "#{@dir_config}/db/default.gpg"
 			end
 	
 			timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
@@ -82,6 +91,8 @@ module MPW
 			                       'sync_path'   => sync_path,
 			                       'last_update' => 0 }}
 	
+			Dir.mkdir("#{@config_dir}/conf", 700)
+			Dir.mkdir("#{@config_dir}/db", 700)
 			File.open(@file_config, 'w') do |file|
 				file << config.to_yaml
 			end
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index cb49464..58fa371 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -77,7 +77,7 @@ class Cli
 		language    = ask(I18n.t('form.setup.lang', :lang => lang)).to_s
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
 		share_keys  = ask(I18n.t('form.setup.share_gpg_keys')).to_s
-		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => Dir.home)).to_s
+		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => @conf.dir_home)).to_s
 		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
 		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
 
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index e8f7d16..0a4c03a 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -73,7 +73,7 @@ en:
       lang: "Choose your language (en, fr, ...): "
       gpg_key: "Enter the GPG key: " 
       share_gpg_keys: "Enter the GPG keys with who you want to share the passwords: " 
-      gpg_file: "Enter the path to encrypt file [default=%{home}/.mpw.gpg]: "
+      gpg_file: "Enter the path to encrypt file [default=%{home}/db/default.gpg]: "
       timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
       sync_type: "Synchronization type (mpw, ssh, ftp, or nil): "
       sync_host: "Synchronization server: "
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index bb5491d..1640cb7 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -73,7 +73,7 @@ fr:
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
       gpg_key: "Entrez la clé GPG: " 
       share_gpg_keys: "Entrez les clés GPG avec qui vous voulez partager les mots de passe: " 
-      gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/.mpw.gpg]: "
+      gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/db/default.gpg]: "
       timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
       sync_type: "Type de synchronisation (mpw, ssh, ftp, or nil): "
       sync_host: "Serveur de synchronisation: "

From 3e9e5e18d5dd0bb7b1e26522a9ac5b16230c9e29 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 29 Apr 2014 23:24:05 +0200
Subject: [PATCH 084/531] fix bug: generate password

---
 mpw | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mpw b/mpw
index 6db35f0..a9a784a 100755
--- a/mpw
+++ b/mpw
@@ -12,6 +12,7 @@ require 'i18n'
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
 require "#{APP_ROOT}/MPW/UI/Cli"
 require "#{APP_ROOT}/MPW/Config"
+require "#{APP_ROOT}/MPW/MPW"
 
 # --------------------------------------------------------- #
 # Set local
@@ -95,7 +96,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
-		puts MPW::password(length)
+		puts MPW::MPW::password(length)
 		exit 0
 	end
 

From fe51dafd1719c5f50230bc129ebdb0d39ec16f3b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 29 Apr 2014 23:42:06 +0200
Subject: [PATCH 085/531] Add Makefile for install on system (/usr/local/mpw)

---
 Makefile | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 Makefile

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..336204a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+all:
+	$(info 'Nothing todo!')
+	$(info 'Use make install or make uninstall')
+
+
+install:
+	mkdir -p /usr/local/mpw
+	cp -rv ./MPW /usr/local/mpw/
+	cp -rv ./i18n /usr/local/mpw/
+	cp -v ./mpw /usr/local/mpw/
+	ln -snvf /usr/local/mpw/mpw /usr/local/bin/
+	cp -v ./mpw-server /usr/local/mpw/
+	ln -snvf /usr/local/mpw/mpw-server /usr/local/bin/mpw-server
+	cp -v ./mpw-ssh /usr/local/mpw/
+	ln -snvf /usr/local/mpw/mpw-ssh /usr/local/bin/mpw-ssh
+
+uninstall:
+	rm -v /usr/local/bin/mpw-server
+	rm -v /usr/local/bin/mpw
+	rm -v /usr/local/bin/mpw-ssh
+	rm -rf /usr/local/mpw

From 07f7aa5b679add9507ab9406d7299b396a74d8bc Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 11 May 2014 16:42:37 +0200
Subject: [PATCH 086/531] Install ubuntu package in makefile

---
 Makefile | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile
index 336204a..1e06dd9 100644
--- a/Makefile
+++ b/Makefile
@@ -2,20 +2,22 @@ all:
 	$(info 'Nothing todo!')
 	$(info 'Use make install or make uninstall')
 
+dep-ubuntu:
+	apt-get install ruby ruby-gpgme ruby-highline ruby-i18n ruby-locale
 
 install:
 	mkdir -p /usr/local/mpw
-	cp -rv ./MPW /usr/local/mpw/
-	cp -rv ./i18n /usr/local/mpw/
-	cp -v ./mpw /usr/local/mpw/
-	ln -snvf /usr/local/mpw/mpw /usr/local/bin/
-	cp -v ./mpw-server /usr/local/mpw/
-	ln -snvf /usr/local/mpw/mpw-server /usr/local/bin/mpw-server
-	cp -v ./mpw-ssh /usr/local/mpw/
-	ln -snvf /usr/local/mpw/mpw-ssh /usr/local/bin/mpw-ssh
+	cp -r ./MPW /usr/local/mpw/
+	cp -r ./i18n /usr/local/mpw/
+	cp ./mpw /usr/local/mpw/
+	ln -snf /usr/local/mpw/mpw /usr/local/bin/
+	cp ./mpw-server /usr/local/mpw/
+	ln -snf /usr/local/mpw/mpw-server /usr/local/bin/mpw-server
+	cp ./mpw-ssh /usr/local/mpw/
+	ln -snf /usr/local/mpw/mpw-ssh /usr/local/bin/mpw-ssh
 
 uninstall:
-	rm -v /usr/local/bin/mpw-server
-	rm -v /usr/local/bin/mpw
-	rm -v /usr/local/bin/mpw-ssh
+	rm /usr/local/bin/mpw-server
+	rm /usr/local/bin/mpw
+	rm /usr/local/bin/mpw-ssh
 	rm -rf /usr/local/mpw

From 4a564dd07e46788218d4a0e51dd42db58f4055a4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 10:58:34 +0200
Subject: [PATCH 087/531] new syntax

---
 MPW/Server.rb | 105 ++++++++++++++++++++++++++++----------------------
 1 file changed, 60 insertions(+), 45 deletions(-)

diff --git a/MPW/Server.rb b/MPW/Server.rb
index 2a52994..fe70cca 100644
--- a/MPW/Server.rb
+++ b/MPW/Server.rb
@@ -52,9 +52,10 @@ module MPW
 							client.puts delete_file(msg)
 						else
 							@log.warning("#{client.peeraddr[3]} is disconnected because unkwnow command")
-							send_msg = {:action      => 'unknown',
-								    :gpg_key     => msg['gpg_key'],
-								    :error       => 'server.error.client.unknown'}
+							send_msg = {action: 'unknown',
+							            gpg_key: msg['gpg_key'],
+							            error:  'server.error.client.unknown'
+							           }
 							client.puts send_msg 
 							close_connection(client)
 						end
@@ -87,20 +88,23 @@ module MPW
 				data        = gpg_data['gpg']['data']
 
 				if is_authorized?(msg['password'], salt, hash)
-					send_msg = {:action      => 'get',
-						    :gpg_key     => msg['gpg_key'],
-						    :data        => data,
-						    :error       => nil}
+					send_msg = {action:  'get',
+					            gpg_key: msg['gpg_key'],
+					            data:    data,
+					            error:   nil
+					           }
 				else
-					send_msg = {:action  => 'get',
-						    :gpg_key => msg['gpg_key'],
-						    :error   => 'server.error.client.no_authorized'}
+					send_msg = {action:  'get',
+					            gpg_key: msg['gpg_key'],
+					            error:   'server.error.client.no_authorized'
+					           }
 				end
 			else
-				send_msg = {:action  => 'get',
-					    :gpg_key => msg['gpg_key'],
-					    :data    => '',
-					    :error   => nil}
+				send_msg = {action:  'get',
+				            gpg_key: msg['gpg_key'],
+				            data:    '',
+				            error:   nil
+				           }
 			end
 
 			return send_msg.to_json
@@ -114,9 +118,10 @@ module MPW
 			data    = msg['data']
 
 			if data.nil? || data.empty?
-				send_msg = {:action  => 'update',
-					    :gpg_key => msg['gpg_key'],
-					    :error   => 'server.error.client.no_data'}
+				send_msg = {action:  'update',
+				            gpg_key: msg['gpg_key'],
+				            error:   'server.error.client.no_data'
+				           }
 				
 				return send_msg.to_json
 			end
@@ -139,26 +144,31 @@ module MPW
 
 			if is_authorized?(msg['password'], salt, hash)
 				begin
-					config = {'gpg' => {'salt'        => salt,
-							    'hash'        => hash,
-							    'data'        => data}}
+					config = {'gpg' => {'salt' => salt,
+					                    'hash' => hash,
+					                    'data' => data
+					                   }
+					         }
 
 					File.open(file_gpg, 'w+') do |file|
 						file << config.to_yaml
 					end
 
-					send_msg = {:action  => 'update',
-						    :gpg_key => msg['gpg_key'],
-						    :error   => nil}
+					send_msg = {action:  'update',
+					            gpg_key: msg['gpg_key'],
+					            error:   nil
+					           }
 				rescue Exception => e
-					send_msg = {:action  => 'update',
-						    :gpg_key => msg['gpg_key'],
-						    :error   => 'server.error.client.unknown'}
+					send_msg = {action:  'update',
+					            gpg_key: msg['gpg_key'],
+					            error:   'server.error.client.unknown'
+					           }
 				end
 			else
-				send_msg = {:action  => 'update',
-					    :gpg_key => msg['gpg_key'],
-					    :error   => 'server.error.client.no_authorized'}
+				send_msg = {action:  'update',
+				            gpg_key: msg['gpg_key'],
+				            error:   'server.error.client.no_authorized'
+				           }
 			end
 			
 			return send_msg.to_json
@@ -184,26 +194,29 @@ module MPW
 				return send_msg.to_json
 			end
 
-			gpg_data  = YAML::load_file(file_gpg)
-			salt      = gpg_data['gpg']['salt']
-			hash      = gpg_data['gpg']['hash']
+			gpg_data = YAML::load_file(file_gpg)
+			salt     = gpg_data['gpg']['salt']
+			hash     = gpg_data['gpg']['hash']
 
 			if is_authorized?(msg['password'], salt, hash)
 				begin
 					File.unlink(file_gpg)
 
-					send_msg = {:action  => 'delete',
-						    :gpg_key => msg['gpg_key'],
-						    :error   => nil}
+					send_msg = {action:  'delete',
+					            gpg_key: msg['gpg_key'],
+					            error:   nil
+					           }
 				rescue Exception => e
-					send_msg = {:action  => 'delete',
-						    :gpg_key => msg['gpg_key'],
-						    :error   => 'server.error.client.unknown'}
+					send_msg = {action:  'delete',
+					            gpg_key: msg['gpg_key'],
+					            error:   'server.error.client.unknown'
+					           }
 				end
 			else
-				send_msg = {:action  => 'delete',
-					    :gpg_key => msg['gpg_key'],
-					    :error   => 'server.error.client.no_authorized'}
+				send_msg = {action:  'delete',
+				            gpg_key: msg['gpg_key'],
+				            error:   'server.error.client.no_authorized'
+				           }
 			end
 			
 			return send_msg.to_json
@@ -296,10 +309,12 @@ module MPW
 			timeout  = ask(I18n.t('form.setup.timeout')).to_s
 
 			config = {'config' => {'host'     => host,
-					       'port'     => port,
-					       'data_dir' => data_dir,
-					       'log_file' => log_file,
-					       'timeout'  => timeout}}
+			                       'port'     => port,
+			                       'data_dir' => data_dir,
+			                       'log_file' => log_file,
+			                       'timeout'  => timeout
+			                      }
+			         }
 
 			File.open(file_config, 'w') do |file|
 				file << config.to_yaml

From 1390850e1808b27890f19d7454b666a58b1282c6 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 10:59:33 +0200
Subject: [PATCH 088/531] add timeout

---
 MPW/Sync/MPWSync.rb | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/MPW/Sync/MPWSync.rb b/MPW/Sync/MPWSync.rb
index 8e6c700..cdb05bf 100644
--- a/MPW/Sync/MPWSync.rb
+++ b/MPW/Sync/MPWSync.rb
@@ -11,6 +11,7 @@ module MPW
 		require 'i18n'
 		require 'socket'
 		require 'json'
+		require 'timeout'
 		
 		class MPWSync
 		
@@ -37,9 +38,20 @@ module MPW
 				@password = password
 				@suffix   = path
 		
-				TCPSocket.open(@host, @port) do 
-					@enable = true
+				Timeout.timeout(10) do
+					begin
+						TCPSocket.open(@host, @port) do 
+							puts 'testi2'
+							@enable = true
+						end
+                    rescue Errno::ENETUNREACH
+							retry
+					end
 				end
+			rescue Timeout::Error
+				puts 'timeout'
+				@error_msg = "#{I18n.t('error.timeout')}\n#{e}"
+				@enable    = false
 			rescue Exception => e
 				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
 				@enable    = false
@@ -58,9 +70,9 @@ module MPW
 				msg = nil
 				TCPSocket.open(@host, @port) do |socket|
 					send_msg = {:action   => 'get',
-						    :gpg_key  => @gpg_key,
-						    :password => @password,
-						    :suffix   => @suffix}
+					            :gpg_key  => @gpg_key,
+					            :password => @password,
+					            :suffix   => @suffix}
 					
 					socket.puts send_msg.to_json
 					msg = JSON.parse(socket.gets)

From be2fb0fd3f7466f00583feefcafd65e34316a10b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 11:09:39 +0200
Subject: [PATCH 089/531] remove test

---
 MPW/Sync/MPWSync.rb | 1 -
 1 file changed, 1 deletion(-)

diff --git a/MPW/Sync/MPWSync.rb b/MPW/Sync/MPWSync.rb
index cdb05bf..16d314b 100644
--- a/MPW/Sync/MPWSync.rb
+++ b/MPW/Sync/MPWSync.rb
@@ -41,7 +41,6 @@ module MPW
 				Timeout.timeout(10) do
 					begin
 						TCPSocket.open(@host, @port) do 
-							puts 'testi2'
 							@enable = true
 						end
                     rescue Errno::ENETUNREACH

From f06059518c166deb518ea1db149154701aa7ed47 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 12:28:20 +0200
Subject: [PATCH 090/531] improve csv with symbole

---
 MPW/MPW.rb    | 115 +++++++++++++++++++++++---------------------------
 MPW/UI/Cli.rb |  44 +++++++++----------
 2 files changed, 75 insertions(+), 84 deletions(-)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index f48bad4..9bdceb8 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -11,17 +11,6 @@ module MPW
 	require 'i18n'
 	
 	class MPW
-		
-		ID       = 0
-		NAME     = 1
-		GROUP    = 2
-		PROTOCOL = 3
-		SERVER   = 4
-		LOGIN    = 5
-		PASSWORD = 6
-		PORT     = 7
-		COMMENT  = 8
-		DATE     = 9
 	
 		attr_accessor :error_msg
 		
@@ -37,15 +26,13 @@ module MPW
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(passwd=nil)
-			@data = []
+			@data = {}
 	
 			if File.exist?(@file_gpg)
-				crypto = GPGME::Crypto.new(:armor => true)
-				data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
+				crypto = GPGME::Crypto.new(armor: true)
+				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read
 	
-				data_decrypt.lines do |line|
-					@data.push(line.parse_csv)
-				end
+				@data = CSV.parse(data_decrypt, {headers: true, header_converters: :symbol})
 			end
 	
 			return true
@@ -57,21 +44,25 @@ module MPW
 		# Encrypt a file
 		# @rtrn: true if the file has been encrypted
 		def encrypt
-			crypto = GPGME::Crypto.new(:armor => true)
+			crypto = GPGME::Crypto.new(armor: true)
 			file_gpg = File.open(@file_gpg, 'w+')
 	
-			data_to_encrypt = ''
-			@data.each do |row|
-				data_to_encrypt << row.to_csv
+			data_to_encrypt = CSV.generate(write_headers: true,
+			                               headers: ['id', 'name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment', 'date']) do |csv|
+				@data.each do |r|
+					csv << [r[:id], r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment], r[:date]]
+				end
 			end
 	
+			puts 'test'
+			puts data_to_encrypt
 			recipients = []
 			recipients.push(@key)
 			if !@share_keys.nil?
 				@share_keys.split.each { |k| recipients.push(k) }
 			end
 
-			crypto.encrypt(data_to_encrypt, :recipients => recipients, :output => file_gpg)
+			crypto.encrypt(data_to_encrypt, recipients: recipients, output: file_gpg)
 			file_gpg.close
 	
 			return true
@@ -93,12 +84,12 @@ module MPW
 			search = search.force_encoding('ASCII-8BIT')
 	
 			@data.each do |row|
-				name    = row[NAME].nil?    ? nil : row[NAME].downcase
-				server  = row[SERVER].nil?  ? nil : row[SERVER].downcase
-				comment = row[COMMENT].nil? ? nil : row[COMMENT].downcase
+				name    = row[:name].nil?    ? nil : row[:name].downcase
+				server  = row[:host].nil?  ? nil : row[:host].downcase
+				comment = row[:comment].nil? ? nil : row[:comment].downcase
 	
 				if name =~ /^.*#{search}.*$/  || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/ 
-					if (protocol.nil? || protocol.eql?(row[PROTOCOL])) && (group.nil? || group.eql?(row[GROUP]))
+					if (protocol.nil? || protocol.eql?(row[:protocol])) && (group.nil? || group.eql?(row[:group]))
 						result.push(row)
 					end
 				end
@@ -112,7 +103,7 @@ module MPW
 		# @rtrn: a row with the resultat of the search
 		def search_by_id(id)
 			@data.each do |row|
-				if row[ID] == id
+				if row[:id] == id
 					return row
 				end
 			end
@@ -132,12 +123,12 @@ module MPW
 		#        comment -> a comment
 		# @rtrn: true if the item has been updated
 		def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
-			row    = []
+			row    = {}
 			update = false
 	
 			i  = 0
 			@data.each do |r|
-				if r[ID] == id
+				if r[:id] == id
 					row    = r
 					update = true
 					break
@@ -149,28 +140,28 @@ module MPW
 				port = nil
 			end
 	
-			row_update       = []
-			row_update[DATE] = Time.now.to_i
+			row_update        = {}
+			row_update[:date] = Time.now.to_i
 	
-			row_update[ID]       = id.nil?       || id.empty?       ? MPW.password(16) : id
-			row_update[NAME]     = name.nil?     || name.empty?     ? row[NAME]        : name
-			row_update[GROUP]    = group.nil?    || group.empty?    ? row[GROUP]       : group
-			row_update[SERVER]   = server.nil?   || server.empty?   ? row[SERVER]      : server
-			row_update[PROTOCOL] = protocol.nil? || protocol.empty? ? row[PROTOCOL]    : protocol
-			row_update[LOGIN]    = login.nil?    || login.empty?    ? row[LOGIN]       : login
-			row_update[PASSWORD] = passwd.nil?   || passwd.empty?   ? row[PASSWORD]    : passwd
-			row_update[PORT]     = port.nil?     || port.empty?     ? row[PORT]        : port
-			row_update[COMMENT]  = comment.nil?  || comment.empty?  ? row[COMMENT]     : comment
+			row_update[:id]       = id.nil?       || id.empty?       ? MPW.password(16)  : id
+			row_update[:name]     = name.nil?     || name.empty?     ? row[:name]        : name
+			row_update[:group]    = group.nil?    || group.empty?    ? row[:group]       : group
+			row_update[:host]     = server.nil?   || server.empty?   ? row[:host]        : server
+			row_update[:protocol] = protocol.nil? || protocol.empty? ? row[:protocol]    : protocol
+			row_update[:login]    = login.nil?    || login.empty?    ? row[:login]       : login
+			row_update[:password] = passwd.nil?   || passwd.empty?   ? row[:password]    : passwd
+			row_update[:port]     = port.nil?     || port.empty?     ? row[:port]        : port
+			row_update[:comment]  = comment.nil?  || comment.empty?  ? row[:comment]     : comment
 			
-			row_update[NAME]     = row_update[NAME].nil?     ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
-			row_update[GROUP]    = row_update[GROUP].nil?    ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
-			row_update[SERVER]   = row_update[SERVER].nil?   ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
-			row_update[PROTOCOL] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
-			row_update[LOGIN]    = row_update[LOGIN].nil?    ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
-			row_update[PASSWORD] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
-			row_update[COMMENT]  = row_update[COMMENT].nil?  ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
+			row_update[:name]     = row_update[:name].nil?     ? nil : row_update[:name].force_encoding('ASCII-8BIT')
+			row_update[:group]    = row_update[:group].nil?    ? nil : row_update[:group].force_encoding('ASCII-8BIT')
+			row_update[:host]     = row_update[:host].nil?     ? nil : row_update[:host].force_encoding('ASCII-8BIT')
+			row_update[:protocol] = row_update[:protocol].nil? ? nil : row_update[:protocol].force_encoding('ASCII-8BIT')
+			row_update[:login]    = row_update[:login].nil?    ? nil : row_update[:login].force_encoding('ASCII-8BIT')
+			row_update[:password] = row_update[:password].nil? ? nil : row_update[:password].force_encoding('ASCII-8BIT')
+			row_update[:comment]  = row_update[:comment].nil?  ? nil : row_update[:comment].force_encoding('ASCII-8BIT')
 	
-			if row_update[NAME].nil? || row_update[NAME].empty?
+			if row_update[:name].nil? || row_update[:name].empty?
 				@error_msg = I18n.t('error.update.name_empty')
 				return false
 			end
@@ -190,14 +181,14 @@ module MPW
 		def remove(id)
 			i = 0
 			@data.each do |row|
-				if row[ID] == id
+				if row[:id] == id
 					@data.delete_at(i)
 					return true
 				end
 				i += 1
 			end
 	
-			@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
+			@error_msg = I18n.t('error.delete.id_no_exist', id: id)
 			return false
 		end
 	
@@ -207,14 +198,14 @@ module MPW
 		def export(file)
 			File.open(file, 'w+') do |file|
 				@data.each do |row|
-					row.delete_at(ID).delete_at(DATE)
+					row.delete_at(:id).delete_at(:date)
 					file << row.to_csv
 				end
 			end
 	
 			return true
 		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
+			@error_msg = "#{I18n.t('error.export.write', file: file)}\n#{e}"
 			return false
 		end
 	
@@ -229,7 +220,7 @@ module MPW
 					return false
 				else
 					row = line.parse_csv.unshift(0)
-					if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
+					if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
 						return false
 					end
 				end
@@ -237,7 +228,7 @@ module MPW
 	
 			return true
 		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
+			@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
 			return false
 		end
 	
@@ -262,7 +253,7 @@ module MPW
 
 			return result
 		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
+			@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
 			return false
 		end
 	
@@ -280,9 +271,9 @@ module MPW
 		
 					# Update item
 					data_remote.each do |r|
-						if l[ID] == r[ID]
-							if l[DATE].to_i < r[DATE].to_i
-								update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
+						if l[:id] == r[:id]
+							if l[:date].to_i < r[:date].to_i
+								update(r[:name], r[:group], r[:host], r[:protocol], r[:login], r[:password], r[:port], r[:comment], l[:id])
 							end
 							update = true
 							data_remote.delete_at(j)
@@ -292,16 +283,16 @@ module MPW
 					end
 		
 					# Delete an old item
-					if !update && l[DATE].to_i < last_update
-						remove(l[ID])
+					if !update && l[:date].to_i < last_update
+						remove(l[:id])
 					end
 				end
 			end
 	
 			# Add item
 			data_remote.each do |r|
-				if r[DATE].to_i > last_update
-					update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], r[ID])
+				if r[:date].to_i > last_update
+					update(r[:name], r[:group], r[:host], r[:protocol], r[:login], r[:password], r[:port], r[:comment], r[:id])
 				end
 			end
 	
diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 58fa371..410b11b 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -187,30 +187,30 @@ class Cli
 	# @args: item -> an array with the item information
 	def displayFormat(item)
 		puts '--------------------'
-		puts "Id: #{item[MPW::MPW::ID]}"
-		puts "#{I18n.t('display.name')}: #{item[MPW::MPW::NAME]}"
-		puts "#{I18n.t('display.group')}: #{item[MPW::MPW::GROUP]}"
-		puts "#{I18n.t('display.server')}: #{item[MPW::MPW::SERVER]}"
-		puts "#{I18n.t('display.protocol')}: #{item[MPW::MPW::PROTOCOL]}"
-		puts "#{I18n.t('display.login')}: #{item[MPW::MPW::LOGIN]}"
-		puts "#{I18n.t('display.password')}: #{item[MPW::MPW::PASSWORD]}"
-		puts "#{I18n.t('display.port')}: #{item[MPW::MPW::PORT]}"
-		puts "#{I18n.t('display.comment')}: #{item[MPW::MPW::COMMENT]}"
+		puts "Id: #{item[:id]}"
+		puts "#{I18n.t('display.name')}: #{item[:name]}"
+		puts "#{I18n.t('display.group')}: #{item[:group]}"
+		puts "#{I18n.t('display.server')}: #{item[:host]}"
+		puts "#{I18n.t('display.protocol')}: #{item[:protocol]}"
+		puts "#{I18n.t('display.login')}: #{item[:login]}"
+		puts "#{I18n.t('display.password')}: #{item[:password]}"
+		puts "#{I18n.t('display.port')}: #{item[:port]}"
+		puts "#{I18n.t('display.comment')}: #{item[:comment]}"
 	end
 
 	# Display an item in the alternative format
 	# @args: item -> an array with the item information
 	def displayFormatAlt(item)
-		port = item[MPW::MPW::PORT].nil? ? '' : ":#{item[MPW::MPW::PORT]}"
+		port = item[:port].nil? ? '' : ":#{item[:port]}"
 
-		if item[MPW::MPW::PASSWORD].nil? || item[MPW::MPW::PASSWORD].empty?
-			if item[MPW::MPW::LOGIN].include('@')
-				puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://#{item[MPW::MPW::LOGIN]}@#{item[MPW::MPW::SERVER]}#{port}"
+		if item[:password].nil? || item[:password].empty?
+			if item[:login].include('@')
+				puts "# #{item[:id]} #{item[:protocol]}://#{item[:login]}@#{item[:host]}#{port}"
 			else
-				puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://{#{item[MPW::MPW::LOGIN]}}@#{item[MPW::MPW::SERVER]}#{port}"
+				puts "# #{item[:id]} #{item[:protocol]}://{#{item[:login]}}@#{item[:host]}#{port}"
 			end
 		else
-			puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://{#{item[MPW::MPW::LOGIN]}:#{item[MPW::MPW::PASSWORD]}}@#{item[MPW::MPW::SERVER]}#{port}"
+			puts "# #{item[:id]} #{item[:protocol]}://{#{item[:login]}:#{item[:password]}}@#{item[:host]}#{port}"
 		end
 	end
 
@@ -248,14 +248,14 @@ class Cli
 		if not row.empty?
 			puts I18n.t('form.update.title')
 			puts '--------------------'
-			name     = ask(I18n.t('form.update.name'    , :name => row[MPW::MPW::NAME])).to_s
-			group    = ask(I18n.t('form.update.group'   , :group => row[MPW::MPW::GROUP])).to_s
-			server   = ask(I18n.t('form.update.server'  , :server => row[MPW::MPW::SERVER])).to_s
-			protocol = ask(I18n.t('form.update.protocol', :protocol => row[MPW::MPW::PROTOCOL])).to_s
-			login    = ask(I18n.t('form.update.login'   , :login => row[MPW::MPW::LOGIN])).to_s
+			name     = ask(I18n.t('form.update.name'    , name:     row[:name])).to_s
+			group    = ask(I18n.t('form.update.group'   , group:    row[:group])).to_s
+			server   = ask(I18n.t('form.update.server'  , server:   row[:host])).to_s
+			protocol = ask(I18n.t('form.update.protocol', protocol: row[:protocol])).to_s
+			login    = ask(I18n.t('form.update.login'   , login:    row[:login])).to_s
 			passwd   = ask(I18n.t('form.update.password')).to_s
-			port     = ask(I18n.t('form.update.port'    , :port => row[MPW::MPW::PORT])).to_s
-			comment  = ask(I18n.t('form.update.comment' , :comment => row[MPW::MPW::COMMENT])).to_s
+			port     = ask(I18n.t('form.update.port'    , port:     row[:port])).to_s
+			comment  = ask(I18n.t('form.update.comment' , comment:  row[:comment])).to_s
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt

From 33f92d9aae88f2130d02f195d7bdd2bdd47db744 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 12:29:57 +0200
Subject: [PATCH 091/531] new syntax for symbol

---
 MPW/UI/Cli.rb | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index 410b11b..f364b9a 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -74,10 +74,10 @@ class Cli
 	def setup(lang)
 		puts I18n.t('form.setup.title')
 		puts '--------------------'
-		language    = ask(I18n.t('form.setup.lang', :lang => lang)).to_s
+		language    = ask(I18n.t('form.setup.lang', lang: lang)).to_s
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
 		share_keys  = ask(I18n.t('form.setup.share_gpg_keys')).to_s
-		file_gpg    = ask(I18n.t('form.setup.gpg_file', :home => @conf.dir_home)).to_s
+		file_gpg    = ask(I18n.t('form.setup.gpg_file', home: @conf.dir_home)).to_s
 		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
 		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
 
@@ -282,7 +282,7 @@ class Cli
 			if result.length > 0
 				displayFormat(result)
 
-				confirm = ask("#{I18n.t('form.delete.ask', :id => id)} (y/N) ").to_s
+				confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
 					force = true
 				end
@@ -295,7 +295,7 @@ class Cli
 			if @mpw.remove(id)
 				if @mpw.encrypt
 					sync
-					puts I18n.t('form.delete.valid', :id => id)
+					puts I18n.t('form.delete.valid', id: id)
 				else
 					puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}"
 				end
@@ -328,7 +328,7 @@ class Cli
 					displayFormat(r)
 				end
 
-				confirm = ask("#{I18n.t('form.import.ask', :file => file)} (y/N) ").to_s
+				confirm = ask("#{I18n.t('form.import.ask', file: file)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
 					force = true
 				end

From ce05c53c20b146d8de29d5a125de19da388b90b3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 12:39:40 +0200
Subject: [PATCH 092/531] fix new syntax for csv

---
 MPW/UI/CliSSH.rb | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/MPW/UI/CliSSH.rb b/MPW/UI/CliSSH.rb
index 9292ca8..a5c0eb1 100644
--- a/MPW/UI/CliSSH.rb
+++ b/MPW/UI/CliSSH.rb
@@ -16,11 +16,11 @@ class CliSSH < Cli
 
 		if result.length > 0
 			result.each do |r|
-				server = @server.nil? ? r[MPW::MPW::SERVER] : @server
-				port   = @port.nil?   ? r[MPW::MPW::PORT]   : @port
-				login  = @login.nil?  ? r[MPW::MPW::LOGIN]  : @login
+				server = @server.nil? ? r[:host]  : @server
+				port   = @port.nil?   ? r[:port]  : @port
+				login  = @login.nil?  ? r[:login] : @login
 
-				passwd = r[MPW::MPW::PASSWORD]
+				passwd = r[:password]
 
 				if port.nil? || port.empty?
 					port = 22

From 6c09c7f1b3ac234c45a770e17a4458babe55f97c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 12:42:26 +0200
Subject: [PATCH 093/531] fix for new syntax

---
 MPW/Config.rb | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/MPW/Config.rb b/MPW/Config.rb
index 9f6d4a1..8dbbc8b 100644
--- a/MPW/Config.rb
+++ b/MPW/Config.rb
@@ -89,7 +89,9 @@ module MPW
 			                       'sync_user'   => sync_user,
 			                       'sync_pwd'    => sync_pwd,
 			                       'sync_path'   => sync_path,
-			                       'last_update' => 0 }}
+			                       'last_update' => 0 
+			                      }
+			         }
 	
 			Dir.mkdir("#{@config_dir}/conf", 700)
 			Dir.mkdir("#{@config_dir}/db", 700)
@@ -199,7 +201,7 @@ module MPW
 						next
 					end
 
-					@error_msg = I18n.t('error.config.no_key_public', :key => k)
+					@error_msg = I18n.t('error.config.no_key_public', key: k)
 					return false
 				end
 			end
@@ -221,7 +223,9 @@ module MPW
 			                       'sync_user'   => @sync_user,
 			                       'sync_pwd'    => @sync_pwd,
 			                       'sync_path'   => @sync_path,
-			                       'last_update' => Time.now.to_i }}
+			                       'last_update' => Time.now.to_i
+			                      }
+		           }
 	
 			File.open(@file_config, 'w') do |file|
 				file << config.to_yaml

From 1d3b127df03929d3acb45a426075b45860f034cf Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 13:07:06 +0200
Subject: [PATCH 094/531] fix new syntax

---
 MPW/Sync/MPWSync.rb | 20 +++++++++++---------
 MPW/Sync/SSH.rb     |  6 +++---
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/MPW/Sync/MPWSync.rb b/MPW/Sync/MPWSync.rb
index 16d314b..730d9fb 100644
--- a/MPW/Sync/MPWSync.rb
+++ b/MPW/Sync/MPWSync.rb
@@ -68,10 +68,11 @@ module MPW
 				
 				msg = nil
 				TCPSocket.open(@host, @port) do |socket|
-					send_msg = {:action   => 'get',
-					            :gpg_key  => @gpg_key,
-					            :password => @password,
-					            :suffix   => @suffix}
+					send_msg = {action:  'get',
+					            gpg_key:  @gpg_key,
+					            password: @password,
+					            suffix:   @suffix
+					           }
 					
 					socket.puts send_msg.to_json
 					msg = JSON.parse(socket.gets)
@@ -112,11 +113,12 @@ module MPW
 		
 				msg = nil
 				TCPSocket.open(@host, @port) do |socket|
-					send_msg = {:action   => 'update',
-						    :gpg_key  => @gpg_key,
-						    :password => @password,
-						    :suffix   => @suffix,
-						    :data     => data}
+					send_msg = {action:   'update',
+					            gpg_key:  @gpg_key,
+					            password: @password,
+					            suffix:   @suffix,
+					            data:     data
+					           }
 					
 					socket.puts send_msg.to_json
 					msg = JSON.parse(socket.gets)
diff --git a/MPW/Sync/SSH.rb b/MPW/Sync/SSH.rb
index 21c9c03..4868a85 100644
--- a/MPW/Sync/SSH.rb
+++ b/MPW/Sync/SSH.rb
@@ -37,7 +37,7 @@ module MPW
 				@path     = path
 				@port     = port.instance_of?(Integer) ? 22 : port
 					
-				Net::SSH.start(@host, @user, :password => @password, :port => @port) do
+				Net::SSH.start(@host, @user, password: @password, port: @port) do
 					@enable = true
 				end
 			rescue Exception => e
@@ -56,7 +56,7 @@ module MPW
 				end
 				
 				tmp_file = tmpfile
-				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |scp|
+				Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
 					scp.download!(@path, tmp_file)
 				end
 			
@@ -86,7 +86,7 @@ module MPW
 					file << data
 				end
 
-				Net::SCP.start(@host, @user, :password => @password, :port => @port) do |scp|
+				Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
 					scp.upload!(tmp_file, @path)
 				end
 

From be00f04353dc549b4eeafa30d5ea1c8d21e814e3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 17:40:37 +0200
Subject: [PATCH 095/531] fix import and export

---
 MPW/MPW.rb | 40 ++++++++++------------------------------
 1 file changed, 10 insertions(+), 30 deletions(-)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 9bdceb8..2f5f3e6 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -26,7 +26,7 @@ module MPW
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(passwd=nil)
-			@data = {}
+			@data = []
 	
 			if File.exist?(@file_gpg)
 				crypto = GPGME::Crypto.new(armor: true)
@@ -54,9 +54,6 @@ module MPW
 				end
 			end
 	
-			puts 'test'
-			puts data_to_encrypt
-			recipients = []
 			recipients.push(@key)
 			if !@share_keys.nil?
 				@share_keys.split.each { |k| recipients.push(k) }
@@ -196,13 +193,13 @@ module MPW
 		# @args: file -> a string to match
 		# @rtrn: true if export work
 		def export(file)
-			File.open(file, 'w+') do |file|
-				@data.each do |row|
-					row.delete_at(:id).delete_at(:date)
-					file << row.to_csv
+			CSV.open(file, 'w', write_headers: true,
+			                    headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
+				@data.each do |r|
+					csv << [r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment]]
 				end
 			end
-	
+
 			return true
 		rescue Exception => e 
 			@error_msg = "#{I18n.t('error.export.write', file: file)}\n#{e}"
@@ -213,16 +210,9 @@ module MPW
 		# @args: file -> path to file import
 		# @rtrn: true if the import work
 		def import(file)
-			data_new = IO.read(file)
-			data_new.lines do |line|
-				if not line =~ /(.*,){6}/
-					@error_msg = I18n.t('error.import.bad_format')
+			CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
+				if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
 					return false
-				else
-					row = line.parse_csv.unshift(0)
-					if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
-						return false
-					end
 				end
 			end
 	
@@ -237,18 +227,8 @@ module MPW
 		# @rtrn: an array with the items to import, if there is an error return false
 		def import_preview(file)
 			result = []
-			id = 0
-
-			data = IO.read(file)
-			data.lines do |line|
-				if not line =~ /(.*,){6}/
-					@error_msg = I18n.t('error.import.bad_format')
-					return false
-				else
-					result.push(line.parse_csv.unshift(id))
-				end
-
-				id += 1
+			CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
+				result << row
 			end
 
 			return result

From bd7a3e5ecf0901a08b46a30ed627d45578e9fd8b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 31 Aug 2014 18:31:14 +0200
Subject: [PATCH 096/531] fix regression

---
 MPW/MPW.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 2f5f3e6..d8908c8 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -54,6 +54,7 @@ module MPW
 				end
 			end
 	
+			recipients = []
 			recipients.push(@key)
 			if !@share_keys.nil?
 				@share_keys.split.each { |k| recipients.push(k) }

From a055c7779e70897689f159d5ba5edc03d3e993a4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 1 Sep 2014 19:24:24 +0200
Subject: [PATCH 097/531] improve backup when encrypt new file

---
 MPW/MPW.rb | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index d8908c8..8e88e03 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -44,6 +44,8 @@ module MPW
 		# Encrypt a file
 		# @rtrn: true if the file has been encrypted
 		def encrypt
+			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk")
+
 			crypto = GPGME::Crypto.new(armor: true)
 			file_gpg = File.open(@file_gpg, 'w+')
 	
@@ -63,9 +65,11 @@ module MPW
 			crypto.encrypt(data_to_encrypt, recipients: recipients, output: file_gpg)
 			file_gpg.close
 	
+			FileUtils.rm("#{@file_gpg}.bk")
 			return true
 		rescue Exception => e 
 			@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+			FileUtils.mv("#{@file_gpg}.bk", @file_gpg)
 			return false
 		end
 		

From 3807c2866c8269bb8454e203186a8264f73f7329 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 1 Sep 2014 20:34:25 +0200
Subject: [PATCH 098/531] add require fileutils

---
 MPW/MPW.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MPW/MPW.rb b/MPW/MPW.rb
index 8e88e03..d38ffc8 100644
--- a/MPW/MPW.rb
+++ b/MPW/MPW.rb
@@ -9,6 +9,7 @@ module MPW
 	require 'gpgme'
 	require 'csv'
 	require 'i18n'
+	require 'fileutils'
 	
 	class MPW
 	

From e8590912a2491afcf3e1e6f76fd4a2c5ca244801 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 7 Sep 2014 10:00:46 +0200
Subject: [PATCH 099/531] clean construtor Cli

---
 MPW/UI/Cli.rb | 2 +-
 mpw           | 2 +-
 mpw-ssh       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/MPW/UI/Cli.rb b/MPW/UI/Cli.rb
index f364b9a..3a3502c 100644
--- a/MPW/UI/Cli.rb
+++ b/MPW/UI/Cli.rb
@@ -17,7 +17,7 @@ class Cli
 	# Constructor
 	# @args: lang -> the operating system language
 	#        config_file -> a specify config file
-	def initialize(lang, config)
+	def initialize(config)
 		@config = config
 	end
 
diff --git a/mpw b/mpw
index a9a784a..3c206de 100755
--- a/mpw
+++ b/mpw
@@ -113,7 +113,7 @@ end.parse!
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig
 
-cli = Cli.new(lang, config)
+cli = Cli.new(config)
 	
 # Setup a new config 
 if !check_error || !options[:setup].nil?
diff --git a/mpw-ssh b/mpw-ssh
index 079771e..1af952d 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -65,7 +65,7 @@ end.parse!
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig
 
-cli         = CliSSH.new(lang, config)
+cli         = CliSSH.new(config)
 cli.login   = options[:login]
 cli.server  = options[:server]
 cli.port    = options[:port]

From 5964044f7858dfa67764c6aaa7b42d689c3051f4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 12 Sep 2014 08:41:27 +0200
Subject: [PATCH 100/531] rename folder MPW to lib

---
 {MPW => lib}/Config.rb       | 0
 {MPW => lib}/MPW.rb          | 0
 {MPW => lib}/Server.rb       | 0
 {MPW => lib}/Sync/FTP.rb     | 0
 {MPW => lib}/Sync/MPWSync.rb | 0
 {MPW => lib}/Sync/SSH.rb     | 0
 {MPW => lib}/UI/Cli.rb       | 8 ++++----
 {MPW => lib}/UI/CliSSH.rb    | 0
 mpw                          | 6 +++---
 mpw-server                   | 2 +-
 mpw-ssh                      | 4 ++--
 11 files changed, 10 insertions(+), 10 deletions(-)
 rename {MPW => lib}/Config.rb (100%)
 rename {MPW => lib}/MPW.rb (100%)
 rename {MPW => lib}/Server.rb (100%)
 rename {MPW => lib}/Sync/FTP.rb (100%)
 rename {MPW => lib}/Sync/MPWSync.rb (100%)
 rename {MPW => lib}/Sync/SSH.rb (100%)
 rename {MPW => lib}/UI/Cli.rb (98%)
 rename {MPW => lib}/UI/CliSSH.rb (100%)

diff --git a/MPW/Config.rb b/lib/Config.rb
similarity index 100%
rename from MPW/Config.rb
rename to lib/Config.rb
diff --git a/MPW/MPW.rb b/lib/MPW.rb
similarity index 100%
rename from MPW/MPW.rb
rename to lib/MPW.rb
diff --git a/MPW/Server.rb b/lib/Server.rb
similarity index 100%
rename from MPW/Server.rb
rename to lib/Server.rb
diff --git a/MPW/Sync/FTP.rb b/lib/Sync/FTP.rb
similarity index 100%
rename from MPW/Sync/FTP.rb
rename to lib/Sync/FTP.rb
diff --git a/MPW/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
similarity index 100%
rename from MPW/Sync/MPWSync.rb
rename to lib/Sync/MPWSync.rb
diff --git a/MPW/Sync/SSH.rb b/lib/Sync/SSH.rb
similarity index 100%
rename from MPW/Sync/SSH.rb
rename to lib/Sync/SSH.rb
diff --git a/MPW/UI/Cli.rb b/lib/UI/Cli.rb
similarity index 98%
rename from MPW/UI/Cli.rb
rename to lib/UI/Cli.rb
index 3a3502c..c045885 100644
--- a/MPW/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -10,7 +10,7 @@ require 'readline'
 require 'i18n'
 require 'yaml'
 
-require "#{APP_ROOT}/MPW/MPW"
+require "#{APP_ROOT}/lib/MPW"
 
 class Cli
 
@@ -27,13 +27,13 @@ class Cli
 		if !defined?(@sync)
 			case @config.sync_type
 			when 'mpw'
-				require "#{APP_ROOT}/MPW/Sync/MPWSync"
+				require "#{APP_ROOT}/lib/Sync/MPWSync"
 				@sync = MPW::Sync::MPWSync.new
 			when 'sftp', 'scp', 'ssh'
-				require "#{APP_ROOT}/MPW/Sync/SSH"
+				require "#{APP_ROOT}/lib/Sync/SSH"
 				@sync = MPW::Sync::SSH.new
 			when 'ftp'
-				require "#{APP_ROOT}/MPW/Sync/FTP"
+				require "#{APP_ROOT}/lib/Sync/FTP"
 				@sync = MPW::Sync::FTP.new
 			else
 				return false
diff --git a/MPW/UI/CliSSH.rb b/lib/UI/CliSSH.rb
similarity index 100%
rename from MPW/UI/CliSSH.rb
rename to lib/UI/CliSSH.rb
diff --git a/mpw b/mpw
index 3c206de..1250bba 100755
--- a/mpw
+++ b/mpw
@@ -10,9 +10,9 @@ require 'locale'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/MPW/UI/Cli"
-require "#{APP_ROOT}/MPW/Config"
-require "#{APP_ROOT}/MPW/MPW"
+require "#{APP_ROOT}/lib/UI/Cli"
+require "#{APP_ROOT}/lib/Config"
+require "#{APP_ROOT}/lib/MPW"
 
 # --------------------------------------------------------- #
 # Set local
diff --git a/mpw-server b/mpw-server
index 0c94289..688973f 100755
--- a/mpw-server
+++ b/mpw-server
@@ -10,7 +10,7 @@ require 'locale'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/MPW/Server.rb"
+require "#{APP_ROOT}/lib/Server.rb"
 
 # --------------------------------------------------------- #
 # Set local
diff --git a/mpw-ssh b/mpw-ssh
index 1af952d..5674b1e 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -10,8 +10,8 @@ require 'locale'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/MPW/UI/CliSSH"
-require "#{APP_ROOT}/MPW/Config"
+require "#{APP_ROOT}/lib/UI/CliSSH"
+require "#{APP_ROOT}/lib/Config"
 
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 

From 0e9f235be8bdc8d7b587d8446d227015f53be73e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 12 Sep 2014 08:42:29 +0200
Subject: [PATCH 101/531] update the makefile

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

diff --git a/Makefile b/Makefile
index 1e06dd9..cb1fa7a 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ dep-ubuntu:
 
 install:
 	mkdir -p /usr/local/mpw
-	cp -r ./MPW /usr/local/mpw/
+	cp -r ./lib /usr/local/mpw/
 	cp -r ./i18n /usr/local/mpw/
 	cp ./mpw /usr/local/mpw/
 	ln -snf /usr/local/mpw/mpw /usr/local/bin/

From 60fa43aef88f68bfab01e9b0351f8d96d5f79e68 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 12 Sep 2014 08:55:08 +0200
Subject: [PATCH 102/531] add

---
 Gemfile | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 Gemfile

diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..600f694
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,10 @@
+source 'https://rubygems.org'
+gem 'highline'
+gem 'i18n'
+gem 'locale'
+gem 'gpgme'
+
+group :ssh do
+	gem 'net-ssh'
+	gem 'net-scp'
+end

From 901317ca6ad24d9efd22837f96b9c6dcd24b5cae Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 12 Sep 2014 09:01:01 +0200
Subject: [PATCH 103/531] update readme

---
 README.md | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 7816d0d..1fc4d7e 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,27 @@
 # Manage your passwords!
 
 MPW is a little software which stores your passwords in an GPG encrypted file.
-MPW can synchronize your password with a MPW Server or via SSH.
+MPW can synchronize your password with a MPW Server or via SSH or via FTP.
 
 # Installation
 
 You must generate a GPG Key with GPG or with Seahorse (GUI on linux).
 This program work with ruby >= 1.9
 
+* install ruby on your computer
+* gem install bundler
+* bundle install 
+or
+* bundle install --without ssh # if you don't want synchronize your password via SSH
+
 ## On Debian/Ubuntu:
 
 * apt-get install ruby ruby-gpgme ruby-highline ruby-i18n ruby-locale
 
-If you want to synchronize your password via SSH/SCP:
+If you want to synchronize your passwords via SSH/SCP:
 * apt-get install ruby-net-ssh ruby-net-scp
 
 For mpw-ssh:
 * apt-get install sshpass
 
+

From 7fcb0fbde6994fa9819dcf60ca96b9a4fc5a9b8c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 12 Sep 2014 09:28:21 +0200
Subject: [PATCH 104/531] fix regression with new version of i18n

---
 mpw        | 1 +
 mpw-server | 1 +
 mpw-ssh    | 1 +
 3 files changed, 3 insertions(+)

diff --git a/mpw b/mpw
index 1250bba..6353633 100755
--- a/mpw
+++ b/mpw
@@ -7,6 +7,7 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
+require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
diff --git a/mpw-server b/mpw-server
index 688973f..d5b8fa2 100755
--- a/mpw-server
+++ b/mpw-server
@@ -7,6 +7,7 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
+require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
diff --git a/mpw-ssh b/mpw-ssh
index 5674b1e..b3c5f70 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -7,6 +7,7 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
+require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)

From 5c20c7c361833a0812994cb01867d5e13232ac0c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 12 Sep 2014 09:28:42 +0200
Subject: [PATCH 105/531] fix regression change folder MPW to lib

---
 lib/UI/CliSSH.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/UI/CliSSH.rb b/lib/UI/CliSSH.rb
index a5c0eb1..c982a28 100644
--- a/lib/UI/CliSSH.rb
+++ b/lib/UI/CliSSH.rb
@@ -3,7 +3,7 @@
 # mail: nishiki@yaegashi.fr
 # info: a simple script who manage your passwords
 
-require "#{APP_ROOT}/MPW/UI/Cli"
+require "#{APP_ROOT}/lib/UI/Cli"
 
 class CliSSH < Cli
 

From 76ee0fe2b4aa9d9d3a1a65dd7d682a2a2fe544bf Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 21 Sep 2014 18:44:44 +0200
Subject: [PATCH 106/531] fix version of gem i18n

---
 Gemfile    | 2 +-
 mpw        | 1 -
 mpw-server | 1 -
 mpw-ssh    | 1 -
 4 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/Gemfile b/Gemfile
index 600f694..d005d66 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
 source 'https://rubygems.org'
 gem 'highline'
-gem 'i18n'
+gem 'i18n', '0.6.9'
 gem 'locale'
 gem 'gpgme'
 
diff --git a/mpw b/mpw
index 6353633..1250bba 100755
--- a/mpw
+++ b/mpw
@@ -7,7 +7,6 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
-require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
diff --git a/mpw-server b/mpw-server
index d5b8fa2..688973f 100755
--- a/mpw-server
+++ b/mpw-server
@@ -7,7 +7,6 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
-require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
diff --git a/mpw-ssh b/mpw-ssh
index b3c5f70..5674b1e 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -7,7 +7,6 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
-require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)

From af04b8761df5e075d22318c7bb9ffa7a3cd8ef1f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 15 Nov 2014 19:14:42 +0100
Subject: [PATCH 107/531] add export to yaml

---
 i18n/cli/en.yml |  2 ++
 i18n/cli/fr.yml |  2 ++
 lib/MPW.rb      | 39 ++++++++++++++++++++++++++++++++++-----
 lib/UI/Cli.rb   |  4 ++--
 mpw             |  8 +++++++-
 5 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 0a4c03a..dde1d7d 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -13,6 +13,7 @@ en:
     delete:
       id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
     export:
+      unknown_type: "The data type %{type} is unknown!"
       write: "Can't export, unable to write in %{file}!"
     gpg_file: 
       decrypt: "Can't decrypt file!"
@@ -42,6 +43,7 @@ en:
     setup: "Create a new configuration file" 
     protocol: "Select the items with the specified protocol"
     export: "Export all items in a CSV file"
+    type: "Data's type export file [csv|yaml]"
     import: "Import item since a CSV file"
     force: "Force an action"
     format: "Change the display items format by an alternative format"
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 1640cb7..64e11ee 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -13,6 +13,7 @@ fr:
     delete:
       id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
     export:
+      unknown_type: "Le type de donnée %{type} est inconnu!"
       write: "Impossible d'exporter les données dans le fichier %{file}!"
     gpg_file: 
       decrypt: "Impossible de déchiffrer le fichier GPG!"
@@ -42,6 +43,7 @@ fr:
     setup: "Création d'un nouveau fichier de configuration" 
     protocol: "Sélectionne les éléments ayant le protocole spécifié"
     export: "Exporte tous les éléments dans un fichier au format CSV"
+    type: "Format des données du fichier d'export [csv|yaml]"
     import: "Importe des éléments depuis un fichier au format CSV"
     force: "Force une action, l'action ne demandera pas de confirmation"
     format: "Change le format d'affichage des éléments par un alternatif"
diff --git a/lib/MPW.rb b/lib/MPW.rb
index d38ffc8..f262a9d 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -196,14 +196,43 @@ module MPW
 		end
 	
 		# Export to csv
-		# @args: file -> a string to match
+		# @args: file -> file where you export the data
+		#        type -> data type
 		# @rtrn: true if export work
-		def export(file)
-			CSV.open(file, 'w', write_headers: true,
-			                    headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
+		def export(file, type=:csv)
+			case type
+			when :csv
+					CSV.open(file, 'w', write_headers: true,
+										headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
+						@data.each do |r|
+							csv << [r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment]]
+						end
+					end
+			when :yaml
+				data = {}
+
+				i = 0
 				@data.each do |r|
-					csv << [r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment]]
+					data.merge!({i => {'id'       => r[:id],
+					                   'name'     => r[:name],
+					                   'group'    => r[:group],
+					                   'protocol' => r[:protocol],
+					                   'host'     => r[:host],
+					                   'login'    => r[:login],
+					                   'password' => r[:password],
+					                   'port'     => r[:port],
+					                   'comment'  => r[:comment]
+					                  }
+					            }
+					          )
+
+					i += 1
 				end
+
+				File.open(file, 'w') {|f| f << data.to_yaml}
+			else
+				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
+				return false
 			end
 
 			return true
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index c045885..c84df63 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -307,8 +307,8 @@ class Cli
 
 	# Export the items in a CSV file
 	# @args: file -> the destination file
-	def export(file)
-		if @mpw.export(file)
+	def export(file, type)
+		if @mpw.export(file, type)
 			puts "The export in #{file} is succesfull!"
 		else
 			puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}"
diff --git a/mpw b/mpw
index 1250bba..9056610 100755
--- a/mpw
+++ b/mpw
@@ -7,6 +7,7 @@ require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
+require 'set'
 require 'i18n'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
@@ -81,6 +82,11 @@ OptionParser.new do |opts|
 
 	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
 		options[:export] = file
+		options[:type]   = :csv
+	end
+
+	opts.on('-t', '--type TYPE', I18n.t('option.type')) do |type|
+		options[:type] = type.to_sym
 	end
 
 	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
@@ -143,7 +149,7 @@ elsif not options[:add].nil?
 
 # Export
 elsif not options[:export].nil?
-	cli.export(options[:export])
+	cli.export(options[:export], options[:type])
 
 # Add a new item
 elsif not options[:import].nil?

From 4aa4a750cdade27c3fade04b8f1e6fca7cc04717 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 16 Nov 2014 19:39:38 +0100
Subject: [PATCH 108/531] update syntax

---
 lib/Config.rb    | 12 +++++------
 lib/MPW.rb       | 34 +++++++++++++++----------------
 lib/UI/Cli.rb    | 52 ++++++++++++++++++++++++------------------------
 lib/UI/CliSSH.rb |  2 +-
 mpw              | 10 +++++-----
 mpw-ssh          |  8 ++++----
 6 files changed, 59 insertions(+), 59 deletions(-)

diff --git a/lib/Config.rb b/lib/Config.rb
index 8dbbc8b..aaab3ac 100644
--- a/lib/Config.rb
+++ b/lib/Config.rb
@@ -43,7 +43,7 @@ module MPW
 			end
 			
 			@file_config = "#{@dir_config}/conf/default.cfg"
-			if !file_config.nil? && !file_config.empty?
+			if not file_config.nil? and not file_config.empty?
 				@file_config = file_config
 			end
 		end
@@ -68,7 +68,7 @@ module MPW
 				return false
 			end
 
-			if !check_public_gpg_key(share_keys)
+			if not check_public_gpg_key(share_keys)
 				return false
 			end
 			
@@ -112,10 +112,10 @@ module MPW
 		#        expire -> the time of expire to GPG key
 		# @rtrn: true if the GPG key is create, else false
 		def setup_gpg_key(password, name, length = 2048, expire = 0)
-			if name.nil? || name.empty?
+			if name.nil? or name.empty?
 				@error_msg = "#{I18n.t('error.config.genkey_gpg.name')}"
 				return false
-			elsif password.nil? || password.empty?
+			elsif password.nil? or password.empty?
 				@error_msg = "#{I18n.t('error.config.genkey_gpg.password')}"
 				return false
 			end
@@ -159,7 +159,7 @@ module MPW
 			@sync_path   = config['config']['sync_path']
 			@last_update = config['config']['last_update'].to_i
 
-			if @key.empty? || @file_gpg.empty? 
+			if @key.empty? or @file_gpg.empty? 
 				@error_msg = I18n.t('error.config.check')
 				return false
 			end
@@ -190,7 +190,7 @@ module MPW
 			ctx = GPGME::Ctx.new
 
 			share_keys = share_keys.nil? ? '' : share_keys
-			if !share_keys.empty?
+			if not share_keys.empty?
 				share_keys.split.each do |k|
 					if not k =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 						@error_msg = I18n.t('error.config.key_bad_format')
diff --git a/lib/MPW.rb b/lib/MPW.rb
index f262a9d..0b0fafb 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -59,7 +59,7 @@ module MPW
 	
 			recipients = []
 			recipients.push(@key)
-			if !@share_keys.nil?
+			if not @share_keys.nil?
 				@share_keys.split.each { |k| recipients.push(k) }
 			end
 
@@ -81,7 +81,7 @@ module MPW
 		def search(search='', group=nil, protocol=nil)
 			result = []
 	
-			if !search.nil?
+			if not search.nil?
 				search = search.downcase
 			end
 			search = search.force_encoding('ASCII-8BIT')
@@ -91,8 +91,8 @@ module MPW
 				server  = row[:host].nil?  ? nil : row[:host].downcase
 				comment = row[:comment].nil? ? nil : row[:comment].downcase
 	
-				if name =~ /^.*#{search}.*$/  || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/ 
-					if (protocol.nil? || protocol.eql?(row[:protocol])) && (group.nil? || group.eql?(row[:group]))
+				if name =~ /^.*#{search}.*$/ or server =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/ 
+					if (protocol.nil? or protocol.eql?(row[:protocol])) and (group.nil? or group.eql?(row[:group]))
 						result.push(row)
 					end
 				end
@@ -146,15 +146,15 @@ module MPW
 			row_update        = {}
 			row_update[:date] = Time.now.to_i
 	
-			row_update[:id]       = id.nil?       || id.empty?       ? MPW.password(16)  : id
-			row_update[:name]     = name.nil?     || name.empty?     ? row[:name]        : name
-			row_update[:group]    = group.nil?    || group.empty?    ? row[:group]       : group
-			row_update[:host]     = server.nil?   || server.empty?   ? row[:host]        : server
-			row_update[:protocol] = protocol.nil? || protocol.empty? ? row[:protocol]    : protocol
-			row_update[:login]    = login.nil?    || login.empty?    ? row[:login]       : login
-			row_update[:password] = passwd.nil?   || passwd.empty?   ? row[:password]    : passwd
-			row_update[:port]     = port.nil?     || port.empty?     ? row[:port]        : port
-			row_update[:comment]  = comment.nil?  || comment.empty?  ? row[:comment]     : comment
+			row_update[:id]       = id.nil?       or id.empty?       ? MPW.password(16)  : id
+			row_update[:name]     = name.nil?     or name.empty?     ? row[:name]        : name
+			row_update[:group]    = group.nil?    or group.empty?    ? row[:group]       : group
+			row_update[:host]     = server.nil?   or server.empty?   ? row[:host]        : server
+			row_update[:protocol] = protocol.nil? or protocol.empty? ? row[:protocol]    : protocol
+			row_update[:login]    = login.nil?    or login.empty?    ? row[:login]       : login
+			row_update[:password] = passwd.nil?   or passwd.empty?   ? row[:password]    : passwd
+			row_update[:port]     = port.nil?     or port.empty?     ? row[:port]        : port
+			row_update[:comment]  = comment.nil?  or comment.empty?  ? row[:comment]     : comment
 			
 			row_update[:name]     = row_update[:name].nil?     ? nil : row_update[:name].force_encoding('ASCII-8BIT')
 			row_update[:group]    = row_update[:group].nil?    ? nil : row_update[:group].force_encoding('ASCII-8BIT')
@@ -164,7 +164,7 @@ module MPW
 			row_update[:password] = row_update[:password].nil? ? nil : row_update[:password].force_encoding('ASCII-8BIT')
 			row_update[:comment]  = row_update[:comment].nil?  ? nil : row_update[:comment].force_encoding('ASCII-8BIT')
 	
-			if row_update[:name].nil? || row_update[:name].empty?
+			if row_update[:name].nil? or row_update[:name].empty?
 				@error_msg = I18n.t('error.update.name_empty')
 				return false
 			end
@@ -277,9 +277,9 @@ module MPW
 		#        last_update -> last update
 		# @rtrn: false if data_remote is nil
 		def sync(data_remote, last_update)
-			if !data_remote.instance_of?(Array)
+			if not data_remote.instance_of?(Array)
 				return false
-			else !data_remote.nil? && !data_remote.empty?
+			else not data_remote.nil? and not data_remote.empty?
 				@data.each do |l|
 					j = 0
 					update = false
@@ -298,7 +298,7 @@ module MPW
 					end
 		
 					# Delete an old item
-					if !update && l[:date].to_i < last_update
+					if not update and l[:date].to_i < last_update
 						remove(l[:id])
 					end
 				end
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index c84df63..3b74105 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -24,7 +24,7 @@ class Cli
 	# Sync the data with the server
 	# @rtnr: true if the synchro is finish
 	def sync
-		if !defined?(@sync)
+		if not defined?(@sync)
 			case @config.sync_type
 			when 'mpw'
 				require "#{APP_ROOT}/lib/Sync/MPWSync"
@@ -40,21 +40,21 @@ class Cli
 			end
 		end
 		
-		if !@config.sync_host.nil? && !@config.sync_port.nil?
-			if !@sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
+		if  not @config.sync_host.nil? and not @config.sync_port.nil?
+			if not @sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 				puts "#{I18n.t('display.error')} #1: #{@sync.error_msg}"
 			end
 		end
 
 		if @sync.enable
-			if !@mpw.sync(@sync.get(@passwd), @config.last_update)
+			if not @mpw.sync(@sync.get(@passwd), @config.last_update)
 				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}"  if !@mpw.error_msg.nil?
 				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}" if !@sync.error_msg.nil?
-			elsif !@sync.update(File.open(@config.file_gpg).read)
+			elsif not @sync.update(File.open(@config.file_gpg).read)
 				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}"
-			elsif !@config.set_last_update
+			elsif not @config.set_last_update
 				puts "#{I18n.t('display.error')} #5: #{@config.error_msg}"
-			elsif !@mpw.encrypt
+			elsif not @mpw.encrypt
 				puts "#{I18n.t('display.error')} #6: #{@mpw.error_msg}"
 			else
 				return true
@@ -89,17 +89,17 @@ class Cli
 			sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
 		end
 		
-		if language.nil? || language.empty?
+		if language.nil? or language.empty?
 			language = lang
 		end
 		I18n.locale = language.to_sym
 
-		sync_type = sync_type.nil? || sync_type.empty? ? nil : sync_type
-		sync_host = sync_host.nil? || sync_host.empty? ? nil : sync_host
-		sync_port = sync_port.nil? || sync_port.empty? ? nil : sync_port.to_i
-		sync_user = sync_user.nil? || sync_user.empty? ? nil : sync_user
-		sync_pwd  = sync_pwd.nil?  || sync_pwd.empty?  ? nil : sync_pwd
-		sync_path = sync_path.nil? || sync_path.empty? ? nil : sync_path
+		sync_type = sync_type.nil? or sync_type.empty? ? nil : sync_type
+		sync_host = sync_host.nil? or sync_host.empty? ? nil : sync_host
+		sync_port = sync_port.nil? or sync_port.empty? ? nil : sync_port.to_i
+		sync_user = sync_user.nil? or sync_user.empty? ? nil : sync_user
+		sync_pwd  = sync_pwd.nil?  or sync_pwd.empty?  ? nil : sync_pwd
+		sync_path = sync_path.nil? or sync_path.empty? ? nil : sync_path
 
 		if @config.setup(key, share_keys, language, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 			puts I18n.t('form.setup.valid')
@@ -120,7 +120,7 @@ class Cli
 		puts '--------------------'
 		ask      = ask(I18n.t('form.setup_gpg_key.ask')).to_s
 		
-		if !['Y', 'y', 'O', 'o'].include?(ask)
+		if not ['Y', 'y', 'O', 'o'].include?(ask)
 			puts I18n.t('form.setup_gpg_key.no_create')
 			exit 2
 		end
@@ -138,8 +138,8 @@ class Cli
 		expire   = ask(I18n.t('form.setup_gpg_key.expire')).to_s
 		password = password.to_s
 
-		length = length.nil? || length.empty? ? 2048 : length.to_i
-		expire = expire.nil? || expire.empty? ? 0    : expire.to_i
+		length = length.nil? or length.empty? ? 2048 : length.to_i
+		expire = expire.nil? or expire.empty? ? 0    : expire.to_i
 
 		puts I18n.t('form.setup_gpg_key.wait')
 		
@@ -153,12 +153,12 @@ class Cli
 
 	# Request the GPG password and decrypt the file
 	def decrypt
-		if !defined?(@mpw)
+		if not defined?(@mpw)
 			@mpw = MPW::MPW.new(@config.file_gpg, @config.key, @config.share_keys)
 		end
 
 		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
-		if !@mpw.decrypt(@passwd)
+		if not @mpw.decrypt(@passwd)
 			puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}"
 			exit 2
 		end
@@ -203,7 +203,7 @@ class Cli
 	def displayFormatAlt(item)
 		port = item[:port].nil? ? '' : ":#{item[:port]}"
 
-		if item[:password].nil? || item[:password].empty?
+		if item[:password].nil? or item[:password].empty?
 			if item[:login].include('@')
 				puts "# #{item[:id]} #{item[:protocol]}://#{item[:login]}@#{item[:host]}#{port}"
 			else
@@ -338,7 +338,7 @@ class Cli
 		end
 
 		if force
-			if @mpw.import(file) && @mpw.encrypt
+			if @mpw.import(file) and @mpw.encrypt
 				sync
 				puts I18n.t('form.import.valid')
 			else
@@ -371,21 +371,21 @@ class Cli
 
 			case command[0]
 			when 'display', 'show', 'd', 's'
-				if !command[1].nil? && !command[1].empty?
+				if not command[1].nil? and not command[1].empty?
 					display(command[1], group, command[2])
 				end
 			when 'add', 'a'
 				add
 			when 'update', 'u'
-				if !command[1].nil? && !command[1].empty?
+				if not command[1].nil? and not command[1].empty?
 					update(command[1])
 				end
 			when 'remove', 'delete', 'r', 'd'
-				if !command[1].nil? && !command[1].empty?
+				if not command[1].nil? and not command[1].empty?
 					remove(command[1])
 				end
 			when 'group', 'g'
-				if !command[1].nil? && !command[1].empty?
+				if not command[1].nil? and not command[1].empty?
 					group = command[1]
 				else
 					group = nil
@@ -404,7 +404,7 @@ class Cli
 				puts I18n.t('interactive.goodbye')
 				break
 			else
-				if !command[0].nil? && !command[0].empty?
+				if not command[0].nil? and not command[0].empty?
 					puts I18n.t('interactive.unknown_command')
 				end
 			end
diff --git a/lib/UI/CliSSH.rb b/lib/UI/CliSSH.rb
index c982a28..b32097e 100644
--- a/lib/UI/CliSSH.rb
+++ b/lib/UI/CliSSH.rb
@@ -22,7 +22,7 @@ class CliSSH < Cli
 
 				passwd = r[:password]
 
-				if port.nil? || port.empty?
+				if port.nil? and port.empty?
 					port = 22
 				end
 
diff --git a/mpw b/mpw
index 9056610..309f910 100755
--- a/mpw
+++ b/mpw
@@ -21,14 +21,14 @@ require "#{APP_ROOT}/lib/MPW"
 
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
-if defined? I18n.enforce_available_locales
+if defined?(I18n.enforce_available_locales)
 	I18n.enforce_available_locales = true
 end
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
+I18n.load_path      = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en
-I18n.locale = lang.to_sym
+I18n.locale         = lang.to_sym
 
 # --------------------------------------------------------- #
 # Options
@@ -122,9 +122,9 @@ check_error = config.checkconfig
 cli = Cli.new(config)
 	
 # Setup a new config 
-if !check_error || !options[:setup].nil?
+if not check_error or not options[:setup].nil?
 	cli.setup(lang)
-elsif !config.check_gpg_key?
+elsif not config.check_gpg_key?
 	cli.setup_gpg_key
 end
 
diff --git a/mpw-ssh b/mpw-ssh
index 5674b1e..89a8c99 100755
--- a/mpw-ssh
+++ b/mpw-ssh
@@ -19,14 +19,14 @@ lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 # Set local
 # --------------------------------------------------------- #
 
-if defined? I18n.enforce_available_locales
+if defined?(I18n.enforce_available_locales)
 	I18n.enforce_available_locales = true
 end
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
+I18n.load_path      = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en
-I18n.locale = lang.to_sym
+I18n.locale         = lang.to_sym
 
 # --------------------------------------------------------- #
 # Options
@@ -73,7 +73,7 @@ cli.port    = options[:port]
 search     = ARGV[0]
 
 # Setup a new config 
-if !check_error 
+if not check_error 
 	cli.setup(lang)
 
 elsif ARGV.length < 1

From 3e2c8eef0ce858de8acba61854e56d133d674b9f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 16 Nov 2014 19:46:21 +0100
Subject: [PATCH 109/531] update syntax

---
 lib/Server.rb | 29 ++++++++++++++---------------
 mpw-server    |  4 ++--
 2 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/lib/Server.rb b/lib/Server.rb
index fe70cca..415bb26 100644
--- a/lib/Server.rb
+++ b/lib/Server.rb
@@ -30,11 +30,9 @@ module MPW
 					while true do
 						msg = get_client_msg(client)
 
-						if !msg
-							next
-						end
+						next if not msg
 						
-						if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
+						if msg['gpg_key'].nil? or msg['gpg_key'].empty? or msg['password'].nil? or msg['password'].empty?
 							@log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
 							close_connection(client)
 							next
@@ -75,7 +73,7 @@ module MPW
 		def get_file(msg)
 			gpg_key = msg['gpg_key'].sub('@', '_')
 
-			if msg['suffix'].nil? || msg['suffix'].empty?
+			if msg['suffix'].nil? or msg['suffix'].empty?
 				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
 			else
 				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
@@ -117,7 +115,7 @@ module MPW
 			gpg_key = msg['gpg_key'].sub('@', '_')
 			data    = msg['data']
 
-			if data.nil? || data.empty?
+			if data.nil? or data.empty?
 				send_msg = {action:  'update',
 				            gpg_key: msg['gpg_key'],
 				            error:   'server.error.client.no_data'
@@ -126,7 +124,7 @@ module MPW
 				return send_msg.to_json
 			end
 
-			if msg['suffix'].nil? || msg['suffix'].empty?
+			if msg['suffix'].nil? or msg['suffix'].empty?
 				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
 			else
 				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
@@ -180,16 +178,17 @@ module MPW
 		def delete_file(msg)
 			gpg_key = msg['gpg_key'].sub('@', '_')
 
-			if msg['suffix'].nil? || msg['suffix'].empty?
+			if msg['suffix'].nil? or msg['suffix'].empty?
 				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
 			else
 				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
 			end
 
-			if !File.exist?(file_gpg)
+			if not File.exist?(file_gpg)
 				send_msg = {:action  => 'delete',
-					    :gpg_key => msg['gpg_key'],
-					    :error   => nil}
+				            :gpg_key => msg['gpg_key'],
+				            :error   => nil
+				           }
 
 				return send_msg.to_json
 			end
@@ -264,19 +263,19 @@ module MPW
 			@log_file = config['config']['log_file']
 			@timeout  = config['config']['timeout'].to_i
 
-			if @host.empty? || @port <= 0 || @data_dir.empty? 
+			if @host.empty? or @port <= 0 or @data_dir.empty? 
 				puts I18n.t('checkconfig.fail')
 				puts I18n.t('checkconfig.empty')
 				return false
 			end
 
-			if !Dir.exist?(@data_dir)
+			if not Dir.exist?(@data_dir)
 				puts I18n.t('checkconfig.fail')
 				puts I18n.t('checkconfig.datadir')
 				return false
 			end
 
-			if @log_file.nil? || @log_file.empty?
+			if @log_file.nil? or @log_file.empty?
 				puts I18n.t('checkconfig.fail')
 				puts I18n.t('checkconfig.log_file_empty')
 				return false
@@ -330,7 +329,7 @@ module MPW
 		# @args: length -> the length salt
 		# @rtrn: a random string
 		def generate_salt(length=4)
-			if length.to_i <= 0 || length.to_i > 16
+			if length.to_i <= 0 or length.to_i > 16
 				length = 4
 			else
 				length = length.to_i
diff --git a/mpw-server b/mpw-server
index 688973f..30f6105 100755
--- a/mpw-server
+++ b/mpw-server
@@ -18,7 +18,7 @@ require "#{APP_ROOT}/lib/Server.rb"
 
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
-if defined? I18n.enforce_available_locales
+if defined?(I18n.enforce_available_locales)
 	I18n.enforce_available_locales = true
 end
 
@@ -57,7 +57,7 @@ end.parse!
 # Main
 # --------------------------------------------------------- #
 
-if options[:config].nil? || options[:config].empty?
+if options[:config].nil? or options[:config].empty?
 	puts "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
 	exit 2
 end

From d2eb57a5c42ccc27b459d2234e2d80777c864738 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 16 Nov 2014 19:50:09 +0100
Subject: [PATCH 110/531] update syntax

---
 lib/Sync/FTP.rb     | 10 +++-------
 lib/Sync/MPWSync.rb | 20 ++++++++------------
 lib/Sync/SSH.rb     | 10 +++-------
 3 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/lib/Sync/FTP.rb b/lib/Sync/FTP.rb
index 6a6b236..4b6ba33 100644
--- a/lib/Sync/FTP.rb
+++ b/lib/Sync/FTP.rb
@@ -51,9 +51,7 @@ module MPW
 			# @args: gpg_password -> the gpg password
 			# @rtrn: nil if nothing data or error
 			def get(gpg_password)
-				if !@enable
-					return nil
-				end
+				return nil if not @enable
 				
 				tmp_file = tmpfile
 				Net::FTP.open(@host) do |ftp|
@@ -62,7 +60,7 @@ module MPW
 				end
 			
 				mpw = MPW.new(tmp_file)
-				if !mpw.decrypt(gpg_password)
+				if not mpw.decrypt(gpg_password)
 					@error_msg = mpw.error_msg
 					return nil
 				end
@@ -78,9 +76,7 @@ module MPW
 			# @args: data -> the data to send on server
 			# @rtrn: false if there is a problem
 			def update(data)
-				if !@enable
-					return true
-				end
+				return true if not @enable
 		
 				tmp_file = tmpfile
 				File.open(tmp_file, "w") do |file|
diff --git a/lib/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
index 730d9fb..1a9f7b6 100644
--- a/lib/Sync/MPWSync.rb
+++ b/lib/Sync/MPWSync.rb
@@ -33,7 +33,7 @@ module MPW
 			# @rtrn: false if the connection fail
 			def connect(host, user, password, path, port=nil)
 				@host     = host
-				@port     = !port.instance_of?(Integer) ? 2201 : port
+				@port     = not port.instance_of?(Integer) ? 2201 : port
 				@gpg_key  = user
 				@password = password
 				@suffix   = path
@@ -62,9 +62,7 @@ module MPW
 			# @args: gpg_password -> the gpg password
 			# @rtrn: nil if nothing data or error
 			def get(gpg_password)
-				if !@enable
-					return nil
-				end
+				return nil if not @enable
 				
 				msg = nil
 				TCPSocket.open(@host, @port) do |socket|
@@ -78,13 +76,13 @@ module MPW
 					msg = JSON.parse(socket.gets)
 				end
 
-				if !defined?(msg['error'])
+				if not defined?(msg['error'])
 					@error_msg = I18n.t('error.sync.communication')
 					return nil
-				elsif !msg['error'].nil?
+				elsif not msg['error'].nil?
 					@error_msg = I18n.t(msg['error'])
 					return nil
-				elsif msg['data'].nil? || msg['data'].empty?
+				elsif msg['data'].nil? or msg['data'].empty?
 					return []
 				else
 					tmp_file = tmpfile
@@ -93,7 +91,7 @@ module MPW
 					end
 					
 					mpw = MPW.new(tmp_file)
-					if !mpw.decrypt(gpg_password)
+					if not mpw.decrypt(gpg_password)
 						@error_msg = mpw.error_msg
 						return nil
 					end
@@ -107,9 +105,7 @@ module MPW
 			# @args: data -> the data to send on server
 			# @rtrn: false if there is a problem
 			def update(data)
-				if !@enable
-					return true
-				end
+				return true if not @enable
 		
 				msg = nil
 				TCPSocket.open(@host, @port) do |socket|
@@ -124,7 +120,7 @@ module MPW
 					msg = JSON.parse(socket.gets)
 				end
 		
-				if !defined?(msg['error'])
+				if not defined?(msg['error'])
 					@error_msg = I18n.t('error.sync.communication')
 					return false
 				elsif msg['error'].nil?
diff --git a/lib/Sync/SSH.rb b/lib/Sync/SSH.rb
index 4868a85..d1f3801 100644
--- a/lib/Sync/SSH.rb
+++ b/lib/Sync/SSH.rb
@@ -51,9 +51,7 @@ module MPW
 			# @args: gpg_password -> the gpg password
 			# @rtrn: nil if nothing data or error
 			def get(gpg_password)
-				if !@enable
-					return nil
-				end
+				return nil if not @enable
 				
 				tmp_file = tmpfile
 				Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
@@ -61,7 +59,7 @@ module MPW
 				end
 			
 				mpw = MPW.new(tmp_file)
-				if !mpw.decrypt(gpg_password)
+				if not mpw.decrypt(gpg_password)
 					@error_msg = mpw.error_msg
 					return nil
 				end
@@ -77,9 +75,7 @@ module MPW
 			# @args: data -> the data to send on server
 			# @rtrn: false if there is a problem
 			def update(data)
-				if !@enable
-					return true
-				end
+				return true if not @enable
 		
 				tmp_file = tmpfile
 				File.open(tmp_file, "w") do |file|

From 7ed067b1b75a1142e7e7b547dd11a9c3cd4b7db6 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 12:03:57 +0100
Subject: [PATCH 111/531] update VERSION file

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

diff --git a/VERSION b/VERSION
index 4482064..5d32997 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.0 - dev
+2.0.0 - dev

From a0559258662f8f5e890d6e54b62c9dab099b0d54 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 16:24:40 +0100
Subject: [PATCH 112/531] change the data format csv to yaml

---
 lib/MPW.rb    | 164 ++++++++++++++++++++++++--------------------------
 lib/UI/Cli.rb |  22 +++----
 2 files changed, 90 insertions(+), 96 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 0b0fafb..44b74ee 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -31,9 +31,8 @@ module MPW
 	
 			if File.exist?(@file_gpg)
 				crypto = GPGME::Crypto.new(armor: true)
-				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read
-	
-				@data = CSV.parse(data_decrypt, {headers: true, header_converters: :symbol})
+				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read.force_encoding('utf-8')
+				@data = YAML.load(data_decrypt)
 			end
 	
 			return true
@@ -45,17 +44,9 @@ module MPW
 		# Encrypt a file
 		# @rtrn: true if the file has been encrypted
 		def encrypt
-			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk")
-
-			crypto = GPGME::Crypto.new(armor: true)
-			file_gpg = File.open(@file_gpg, 'w+')
+			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk") if File.exist?(@file_gpg)
 	
-			data_to_encrypt = CSV.generate(write_headers: true,
-			                               headers: ['id', 'name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment', 'date']) do |csv|
-				@data.each do |r|
-					csv << [r[:id], r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment], r[:date]]
-				end
-			end
+			data_to_encrypt = @data.to_yaml
 	
 			recipients = []
 			recipients.push(@key)
@@ -63,14 +54,16 @@ module MPW
 				@share_keys.split.each { |k| recipients.push(k) }
 			end
 
+			crypto = GPGME::Crypto.new(armor: true)
+			file_gpg = File.open(@file_gpg, 'w+')
 			crypto.encrypt(data_to_encrypt, recipients: recipients, output: file_gpg)
 			file_gpg.close
 	
-			FileUtils.rm("#{@file_gpg}.bk")
+			FileUtils.rm("#{@file_gpg}.bk") if File.exist?("#{@file_gpg}.bk")
 			return true
 		rescue Exception => e 
 			@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
-			FileUtils.mv("#{@file_gpg}.bk", @file_gpg)
+			FileUtils.mv("#{@file_gpg}.bk", @file_gpg) if File.exist?("#{@file_gpg}.bk")
 			return false
 		end
 		
@@ -84,12 +77,11 @@ module MPW
 			if not search.nil?
 				search = search.downcase
 			end
-			search = search.force_encoding('ASCII-8BIT')
 	
-			@data.each do |row|
-				name    = row[:name].nil?    ? nil : row[:name].downcase
-				server  = row[:host].nil?  ? nil : row[:host].downcase
-				comment = row[:comment].nil? ? nil : row[:comment].downcase
+			@data.each do |id, row|
+				name    = row['name'].nil?    ? nil : row['name'].downcase
+				server  = row['host'].nil?    ? nil : row['host'].downcase
+				comment = row['comment'].nil? ? nil : row['comment'].downcase
 	
 				if name =~ /^.*#{search}.*$/ or server =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/ 
 					if (protocol.nil? or protocol.eql?(row[:protocol])) and (group.nil? or group.eql?(row[:group]))
@@ -102,13 +94,11 @@ module MPW
 		end
 	
 		# Search in some csv data
-		# @args: id -> the id item
+		# @args: id_search -> the id item
 		# @rtrn: a row with the resultat of the search
-		def search_by_id(id)
-			@data.each do |row|
-				if row[:id] == id
-					return row
-				end
+		def search_by_id(id_search)
+			@data.each do |id, row|
+				return row if id == id_search
 			end
 	
 			return []
@@ -130,49 +120,35 @@ module MPW
 			update = false
 	
 			i  = 0
-			@data.each do |r|
-				if r[:id] == id
-					row    = r
-					update = true
-					break
-				end
-				i += 1
+			if @data.has_key?(id)
+				row = @data[id]
 			end
 	
 			if port.to_i <= 0
 				port = nil
 			end
 	
-			row_update        = {}
-			row_update[:date] = Time.now.to_i
+			row_update             = {}
+			row_update['id']       = id.to_s.empty?       ? MPW.password(16) : id
+			row_update['name']     = name.to_s.empty?     ? row['name']      : name
+			row_update['group']    = group.to_s.empty?    ? row['group']     : group
+			row_update['host']     = server.to_s.empty?   ? row['host']      : server
+			row_update['protocol'] = protocol.to_s.empty? ? row['protocol']  : protocol
+			row_update['login']    = login.to_s.empty?    ? row['login']     : login
+			row_update['password'] = passwd.to_s.empty?   ? row['password']  : passwd
+			row_update['port']     = port.to_s.empty?     ? row['port']      : port
+			row_update['comment']  = comment.to_s.empty?  ? row['comment']   : comment
+			row_update['date']     = Time.now.to_i
 	
-			row_update[:id]       = id.nil?       or id.empty?       ? MPW.password(16)  : id
-			row_update[:name]     = name.nil?     or name.empty?     ? row[:name]        : name
-			row_update[:group]    = group.nil?    or group.empty?    ? row[:group]       : group
-			row_update[:host]     = server.nil?   or server.empty?   ? row[:host]        : server
-			row_update[:protocol] = protocol.nil? or protocol.empty? ? row[:protocol]    : protocol
-			row_update[:login]    = login.nil?    or login.empty?    ? row[:login]       : login
-			row_update[:password] = passwd.nil?   or passwd.empty?   ? row[:password]    : passwd
-			row_update[:port]     = port.nil?     or port.empty?     ? row[:port]        : port
-			row_update[:comment]  = comment.nil?  or comment.empty?  ? row[:comment]     : comment
-			
-			row_update[:name]     = row_update[:name].nil?     ? nil : row_update[:name].force_encoding('ASCII-8BIT')
-			row_update[:group]    = row_update[:group].nil?    ? nil : row_update[:group].force_encoding('ASCII-8BIT')
-			row_update[:host]     = row_update[:host].nil?     ? nil : row_update[:host].force_encoding('ASCII-8BIT')
-			row_update[:protocol] = row_update[:protocol].nil? ? nil : row_update[:protocol].force_encoding('ASCII-8BIT')
-			row_update[:login]    = row_update[:login].nil?    ? nil : row_update[:login].force_encoding('ASCII-8BIT')
-			row_update[:password] = row_update[:password].nil? ? nil : row_update[:password].force_encoding('ASCII-8BIT')
-			row_update[:comment]  = row_update[:comment].nil?  ? nil : row_update[:comment].force_encoding('ASCII-8BIT')
-	
-			if row_update[:name].nil? or row_update[:name].empty?
+			if row_update['name'].to_s.empty?
 				@error_msg = I18n.t('error.update.name_empty')
 				return false
 			end
 	
 			if update
-				@data[i] = row_update
+				@data[id] = row_update
 			else
-				@data.push(row_update)
+				@data[row_update['id']] = row_update
 			end
 	
 			return true
@@ -182,13 +158,11 @@ module MPW
 		# @args: id -> the item's identifiant
 		# @rtrn: true if the item has been deleted
 		def remove(id)
-			i = 0
-			@data.each do |row|
-				if row[:id] == id
-					@data.delete_at(i)
+			@data.each do |k, row|
+				if k == id
+					@data.delete(id)
 					return true
 				end
-				i += 1
 			end
 	
 			@error_msg = I18n.t('error.delete.id_no_exist', id: id)
@@ -197,36 +171,33 @@ module MPW
 	
 		# Export to csv
 		# @args: file -> file where you export the data
-		#        type -> data type
+		#        type -> udata type
 		# @rtrn: true if export work
 		def export(file, type=:csv)
 			case type
 			when :csv
 					CSV.open(file, 'w', write_headers: true,
 										headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
-						@data.each do |r|
-							csv << [r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment]]
+						@data.each do |id, r|
+							csv << [r['name'], r['group'], r['protocol'], r['host'], r['login'], r['password'], r['port'], r['comment']]
 						end
 					end
 			when :yaml
 				data = {}
 
-				i = 0
-				@data.each do |r|
-					data.merge!({i => {'id'       => r[:id],
-					                   'name'     => r[:name],
-					                   'group'    => r[:group],
-					                   'protocol' => r[:protocol],
-					                   'host'     => r[:host],
-					                   'login'    => r[:login],
-					                   'password' => r[:password],
-					                   'port'     => r[:port],
-					                   'comment'  => r[:comment]
-					                  }
+				@data.each do |id, r|
+					data.merge!({id => {'id'       => r['id'],
+					                    'name'     => r['name'],
+					                    'group'    => r['group'],
+					                    'protocol' => r['protocol'],
+					                    'host'     => r['host'],
+					                    'login'    => r['login'],
+					                    'password' => r['password'],
+					                    'port'     => r['port'],
+					                    'comment'  => r['comment'],
+					                   }
 					            }
 					          )
-
-					i += 1
 				end
 
 				File.open(file, 'w') {|f| f << data.to_yaml}
@@ -243,12 +214,25 @@ module MPW
 	
 		# Import to csv
 		# @args: file -> path to file import
+		#        type -> udata type
 		# @rtrn: true if the import work
-		def import(file)
-			CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
-				if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
-					return false
+		def import(file, type=:csv)
+			case type
+			when :csv
+				CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
+					if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
+						return false
+					end
 				end
+			when :yaml
+				YAML::load_file(file).each do |k, row| 
+					if not update(row['name'], row['group'], row['host'], row['protocol'], row['login'], row['password'], row['port'], row['comment'])
+						return false
+					end
+				end
+			else
+				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
+				return false
 			end
 	
 			return true
@@ -260,10 +244,20 @@ module MPW
 		# Return a preview import 
 		# @args: file -> path to file import
 		# @rtrn: an array with the items to import, if there is an error return false
-		def import_preview(file)
+		def import_preview(file, type=:csv)
 			result = []
-			CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
-				result << row
+			case type
+			when :csv
+				CSV.foreach(file, {headers: true}) do |row|
+					result << row
+				end
+			when :yaml
+				YAML::load_file(file).each do |k, row| 
+					result << row
+				end
+			else
+				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
+				return false
 			end
 
 			return result
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 3b74105..a8d165e 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -77,7 +77,7 @@ class Cli
 		language    = ask(I18n.t('form.setup.lang', lang: lang)).to_s
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
 		share_keys  = ask(I18n.t('form.setup.share_gpg_keys')).to_s
-		file_gpg    = ask(I18n.t('form.setup.gpg_file', home: @conf.dir_home)).to_s
+		file_gpg    = ask(I18n.t('form.setup.gpg_file', home: @conf.dir_config)).to_s
 		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
 		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
 
@@ -187,15 +187,15 @@ class Cli
 	# @args: item -> an array with the item information
 	def displayFormat(item)
 		puts '--------------------'
-		puts "Id: #{item[:id]}"
-		puts "#{I18n.t('display.name')}: #{item[:name]}"
-		puts "#{I18n.t('display.group')}: #{item[:group]}"
-		puts "#{I18n.t('display.server')}: #{item[:host]}"
-		puts "#{I18n.t('display.protocol')}: #{item[:protocol]}"
-		puts "#{I18n.t('display.login')}: #{item[:login]}"
-		puts "#{I18n.t('display.password')}: #{item[:password]}"
-		puts "#{I18n.t('display.port')}: #{item[:port]}"
-		puts "#{I18n.t('display.comment')}: #{item[:comment]}"
+		puts "Id: #{item['id']}"
+		puts "#{I18n.t('display.name')}: #{item['name']}"
+		puts "#{I18n.t('display.group')}: #{item['group']}"
+		puts "#{I18n.t('display.server')}: #{item['host']}"
+		puts "#{I18n.t('display.protocol')}: #{item['protocol']}"
+		puts "#{I18n.t('display.login')}: #{item['login']}"
+		puts "#{I18n.t('display.password')}: #{item['password']}"
+		puts "#{I18n.t('display.port')}: #{item['port']}"
+		puts "#{I18n.t('display.comment')}: #{item['comment']}"
 	end
 
 	# Display an item in the alternative format
@@ -320,9 +320,9 @@ class Cli
 	# @args: file -> the import file
 	#        force -> no resquest a validation
 	def import(file, force=false)
-		result = @mpw.import_preview(file)
 
 		if not force
+			result = @mpw.import_preview(file)
 			if result.is_a?(Array) && !result.empty?
 				result.each do |r|
 					displayFormat(r)

From c3d484a85efd1f76bc11cc07e1acd23584a1efd0 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 16:25:03 +0100
Subject: [PATCH 113/531] fix a bug

---
 lib/Sync/MPWSync.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
index 1a9f7b6..ee0d6cb 100644
--- a/lib/Sync/MPWSync.rb
+++ b/lib/Sync/MPWSync.rb
@@ -33,7 +33,7 @@ module MPW
 			# @rtrn: false if the connection fail
 			def connect(host, user, password, path, port=nil)
 				@host     = host
-				@port     = not port.instance_of?(Integer) ? 2201 : port
+				@port     = !port.instance_of?(Integer) ? 2201 : port
 				@gpg_key  = user
 				@password = password
 				@suffix   = path

From 291d68a1f753e6dc595ef668c21772821004ae98 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 18:20:43 +0100
Subject: [PATCH 114/531] code factoring

---
 lib/MPW.rb | 68 ++++++++++++++++++++++--------------------------------
 1 file changed, 28 insertions(+), 40 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 44b74ee..938b616 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -3,14 +3,13 @@
 # mail: nishiki@yaegashi.fr
 # info: a simple script who manage your passwords
 
-module MPW
-
-	require 'rubygems'
-	require 'gpgme'
-	require 'csv'
-	require 'i18n'
-	require 'fileutils'
+require 'rubygems'
+require 'gpgme'
+require 'csv'
+require 'i18n'
+require 'fileutils'
 	
+module MPW
 	class MPW
 	
 		attr_accessor :error_msg
@@ -182,25 +181,10 @@ module MPW
 							csv << [r['name'], r['group'], r['protocol'], r['host'], r['login'], r['password'], r['port'], r['comment']]
 						end
 					end
+
 			when :yaml
-				data = {}
+				File.open(file, 'w') {|f| f << @data.to_yaml}
 
-				@data.each do |id, r|
-					data.merge!({id => {'id'       => r['id'],
-					                    'name'     => r['name'],
-					                    'group'    => r['group'],
-					                    'protocol' => r['protocol'],
-					                    'host'     => r['host'],
-					                    'login'    => r['login'],
-					                    'password' => r['password'],
-					                    'port'     => r['port'],
-					                    'comment'  => r['comment'],
-					                   }
-					            }
-					          )
-				end
-
-				File.open(file, 'w') {|f| f << data.to_yaml}
 			else
 				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
 				return false
@@ -219,17 +203,19 @@ module MPW
 		def import(file, type=:csv)
 			case type
 			when :csv
-				CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
-					if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
+				CSV.foreach(file, {headers: true}) do |row|
+					if not update(row['name'], row['group'], row['host'], row['protocol'], row['login'], row['password'], row['port'], row['comment'])
 						return false
 					end
 				end
+
 			when :yaml
 				YAML::load_file(file).each do |k, row| 
 					if not update(row['name'], row['group'], row['host'], row['protocol'], row['login'], row['password'], row['port'], row['comment'])
 						return false
 					end
 				end
+
 			else
 				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
 				return false
@@ -271,37 +257,39 @@ module MPW
 		#        last_update -> last update
 		# @rtrn: false if data_remote is nil
 		def sync(data_remote, last_update)
-			if not data_remote.instance_of?(Array)
+			if not data_remote.instance_of?(Hash)
+				@error_msg = I18n.t('error.sync.hash')
 				return false
-			else not data_remote.nil? and not data_remote.empty?
-				@data.each do |l|
+
+			else not data_remote.to_s.empty?
+				@data.each do |lk, l|
 					j = 0
 					update = false
 		
 					# Update item
-					data_remote.each do |r|
-						if l[:id] == r[:id]
-							if l[:date].to_i < r[:date].to_i
-								update(r[:name], r[:group], r[:host], r[:protocol], r[:login], r[:password], r[:port], r[:comment], l[:id])
+					data_remote.each do |rk, r|
+						if l['id'] == r['id']
+							if l['date'].to_i < r['date'].to_i
+								update(r['name'], r['group'], r['host'], r['protocol'], r['login'], r['password'], r['port'], r['comment'], l['id'])
 							end
 							update = true
-							data_remote.delete_at(j)
+							data_remote.delete(rk)
 							break
 						end
 						j += 1
 					end
 		
 					# Delete an old item
-					if not update and l[:date].to_i < last_update
-						remove(l[:id])
+					if not update and l['date'].to_i < last_update
+						remove(l['id'])
 					end
 				end
 			end
 	
 			# Add item
-			data_remote.each do |r|
-				if r[:date].to_i > last_update
-					update(r[:name], r[:group], r[:host], r[:protocol], r[:login], r[:password], r[:port], r[:comment], r[:id])
+			data_remote.each do |rk, r|
+				if r['date'].to_i > last_update
+					update(r['name'], r['group'], r['host'], r['protocol'], r['login'], r['password'], r['port'], r['comment'], r['id'])
 				end
 			end
 	
@@ -327,6 +315,6 @@ module MPW
 	
 			return result
 		end
-	end
 
+	end
 end

From 568a8514cfe8c2a2a08232608bf3b6c6b491731f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 21:37:18 +0100
Subject: [PATCH 115/531] fix sync with new data format

---
 lib/MPW.rb          | 19 +++++++++----------
 lib/Sync/MPWSync.rb |  6 +++---
 lib/UI/Cli.rb       | 12 ++++++------
 3 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 938b616..791dd3a 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -26,12 +26,12 @@ module MPW
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(passwd=nil)
-			@data = []
+			@data = {}
 	
 			if File.exist?(@file_gpg)
 				crypto = GPGME::Crypto.new(armor: true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read.force_encoding('utf-8')
-				@data = YAML.load(data_decrypt)
+				@data = YAML.load(data_decrypt) if not data_decrypt.to_s.empty?
 			end
 	
 			return true
@@ -119,7 +119,7 @@ module MPW
 			update = false
 	
 			i  = 0
-			if @data.has_key?(id)
+			if @data.instance_of?(Hash) and @data.has_key?(id)
 				row = @data[id]
 			end
 	
@@ -229,7 +229,7 @@ module MPW
 	
 		# Return a preview import 
 		# @args: file -> path to file import
-		# @rtrn: an array with the items to import, if there is an error return false
+		# @rtrn: a hash with the items to import, if there is an error return false
 		def import_preview(file, type=:csv)
 			result = []
 			case type
@@ -257,23 +257,22 @@ module MPW
 		#        last_update -> last update
 		# @rtrn: false if data_remote is nil
 		def sync(data_remote, last_update)
-			if not data_remote.instance_of?(Hash)
-				@error_msg = I18n.t('error.sync.hash')
+			if not data_remote.instance_of?(Array)
+				@error_msg = I18n.t('error.sync.array')
 				return false
-
 			else not data_remote.to_s.empty?
 				@data.each do |lk, l|
 					j = 0
 					update = false
 		
 					# Update item
-					data_remote.each do |rk, r|
+					data_remote.each do |r|
 						if l['id'] == r['id']
 							if l['date'].to_i < r['date'].to_i
 								update(r['name'], r['group'], r['host'], r['protocol'], r['login'], r['password'], r['port'], r['comment'], l['id'])
 							end
 							update = true
-							data_remote.delete(rk)
+							data_remote.delete(r['id'])
 							break
 						end
 						j += 1
@@ -287,7 +286,7 @@ module MPW
 			end
 	
 			# Add item
-			data_remote.each do |rk, r|
+			data_remote.each do |r|
 				if r['date'].to_i > last_update
 					update(r['name'], r['group'], r['host'], r['protocol'], r['login'], r['password'], r['port'], r['comment'], r['id'])
 				end
diff --git a/lib/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
index ee0d6cb..ae0c466 100644
--- a/lib/Sync/MPWSync.rb
+++ b/lib/Sync/MPWSync.rb
@@ -63,7 +63,7 @@ module MPW
 			# @rtrn: nil if nothing data or error
 			def get(gpg_password)
 				return nil if not @enable
-				
+			
 				msg = nil
 				TCPSocket.open(@host, @port) do |socket|
 					send_msg = {action:  'get',
@@ -75,7 +75,7 @@ module MPW
 					socket.puts send_msg.to_json
 					msg = JSON.parse(socket.gets)
 				end
-
+				
 				if not defined?(msg['error'])
 					@error_msg = I18n.t('error.sync.communication')
 					return nil
@@ -83,7 +83,7 @@ module MPW
 					@error_msg = I18n.t(msg['error'])
 					return nil
 				elsif msg['data'].nil? or msg['data'].empty?
-					return []
+					return {}
 				else
 					tmp_file = tmpfile
 					File.open(tmp_file, 'w') do |file|
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index a8d165e..59edc54 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -48,8 +48,8 @@ class Cli
 
 		if @sync.enable
 			if not @mpw.sync(@sync.get(@passwd), @config.last_update)
-				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}"  if !@mpw.error_msg.nil?
-				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}" if !@sync.error_msg.nil?
+				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}"  if not @mpw.error_msg.nil?
+				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}" if not @sync.error_msg.nil?
 			elsif not @sync.update(File.open(@config.file_gpg).read)
 				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}"
 			elsif not @config.set_last_update
@@ -62,9 +62,9 @@ class Cli
 		end
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #7: #{e}"
-		puts @sync.error_msg   if @sync.error_msg.nil?
-		puts @config.error_msg if @config.error_msg.nil?
-		puts @mpw.error_msg    if @mpw.error_msg.nil?
+		puts @sync.error_msg   if not @sync.error_msg.nil?
+		puts @config.error_msg if not @config.error_msg.nil?
+		puts @mpw.error_msg    if not @mpw.error_msg.nil?
 	else
 		return false
 	end
@@ -323,7 +323,7 @@ class Cli
 
 		if not force
 			result = @mpw.import_preview(file)
-			if result.is_a?(Array) && !result.empty?
+			if result.is_a?(Array) and not result.empty?
 				result.each do |r|
 					displayFormat(r)
 				end

From 30f52dfe92f7b05c15d6aac1a7dd66ce87af5d78 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 23:32:09 +0100
Subject: [PATCH 116/531] fix import and export to yaml

---
 lib/MPW.rb    | 4 ++--
 lib/UI/Cli.rb | 8 ++++----
 mpw           | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 791dd3a..f0971cb 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -172,7 +172,7 @@ module MPW
 		# @args: file -> file where you export the data
 		#        type -> udata type
 		# @rtrn: true if export work
-		def export(file, type=:csv)
+		def export(file, type=:yaml)
 			case type
 			when :csv
 					CSV.open(file, 'w', write_headers: true,
@@ -200,7 +200,7 @@ module MPW
 		# @args: file -> path to file import
 		#        type -> udata type
 		# @rtrn: true if the import work
-		def import(file, type=:csv)
+		def import(file, type=:yaml)
 			case type
 			when :csv
 				CSV.foreach(file, {headers: true}) do |row|
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 59edc54..14b2e97 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -307,7 +307,7 @@ class Cli
 
 	# Export the items in a CSV file
 	# @args: file -> the destination file
-	def export(file, type)
+	def export(file, type=:yaml)
 		if @mpw.export(file, type)
 			puts "The export in #{file} is succesfull!"
 		else
@@ -319,10 +319,10 @@ class Cli
 	# Import items from a CSV file
 	# @args: file -> the import file
 	#        force -> no resquest a validation
-	def import(file, force=false)
+	def import(file, type=:yaml, force=false)
 
 		if not force
-			result = @mpw.import_preview(file)
+			result = @mpw.import_preview(file, type)
 			if result.is_a?(Array) and not result.empty?
 				result.each do |r|
 					displayFormat(r)
@@ -338,7 +338,7 @@ class Cli
 		end
 
 		if force
-			if @mpw.import(file) and @mpw.encrypt
+			if @mpw.import(file, type) and @mpw.encrypt
 				sync
 				puts I18n.t('form.import.valid')
 			else
diff --git a/mpw b/mpw
index 309f910..f4092d3 100755
--- a/mpw
+++ b/mpw
@@ -153,7 +153,7 @@ elsif not options[:export].nil?
 
 # Add a new item
 elsif not options[:import].nil?
-	cli.import(options[:import], options[:force])
+	cli.import(options[:import], options[:type], options[:force])
 
 # Interactive mode
 else

From ac07b629929bcea420f39478395a197ee0209275 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 23:33:48 +0100
Subject: [PATCH 117/531] add .gitignore

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b844b14
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+Gemfile.lock

From 6290bfb3bbfd602bb85acbb6583e709b4ba6894a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 23:40:09 +0100
Subject: [PATCH 118/531] remove interactive mode

---
 lib/Config.rb |  9 +------
 lib/UI/Cli.rb | 68 +--------------------------------------------------
 mpw           |  7 ------
 3 files changed, 2 insertions(+), 82 deletions(-)

diff --git a/lib/Config.rb b/lib/Config.rb
index aaab3ac..c1cc17e 100644
--- a/lib/Config.rb
+++ b/lib/Config.rb
@@ -18,7 +18,6 @@ module MPW
 		attr_accessor :share_keys
 		attr_accessor :lang
 		attr_accessor :file_gpg
-		attr_accessor :timeout_pwd
 		attr_accessor :last_update
 		attr_accessor :sync_type
 		attr_accessor :sync_host
@@ -53,7 +52,6 @@ module MPW
 		#        share_keys -> multiple keys to share the password with other people
 		#        lang -> the software language
 		#        file_gpg -> the file who is encrypted
-		#        timeout_pwd -> time to save the password
 		#        sync_type -> the type to synchronization
 		#        sync_host -> the server host for synchronization
 		#        sync_port -> the server port for synchronization
@@ -61,7 +59,7 @@ module MPW
 		#        sync_pwd -> the password for synchronization
 		#        sync_suffix -> the suffix file (optionnal) 
 		# @rtrn: true if le config file is create
-		def setup(key, share_keys, lang, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
+		def setup(key, share_keys, lang, file_gpg, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 	
 			if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 				@error_msg = I18n.t('error.config.key_bad_format')
@@ -76,13 +74,10 @@ module MPW
 				file_gpg = "#{@dir_config}/db/default.gpg"
 			end
 	
-			timeout_pwd = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
-	
 			config = {'config' => {'key'         => key,
 			                       'share_keys'  => share_keys,
 			                       'lang'        => lang,
 			                       'file_gpg'    => file_gpg,
-			                       'timeout_pwd' => timeout_pwd,
 			                       'sync_type'   => sync_type,
 			                       'sync_host'   => sync_host,
 			                       'sync_port'   => sync_port,
@@ -150,7 +145,6 @@ module MPW
 			@share_keys  = config['config']['share_keys']
 			@lang        = config['config']['lang']
 			@file_gpg    = config['config']['file_gpg']
-			@timeout_pwd = config['config']['timeout_pwd'].to_i
 			@sync_type   = config['config']['sync_type']
 			@sync_host   = config['config']['sync_host']
 			@sync_port   = config['config']['sync_port']
@@ -216,7 +210,6 @@ module MPW
 			                       'share_keys'  => @share_keys,
 			                       'lang'        => @lang,
 			                       'file_gpg'    => @file_gpg,
-			                       'timeout_pwd' => @timeout_pwd,
 			                       'sync_type'   => @sync_type,
 			                       'sync_host'   => @sync_host,
 			                       'sync_port'   => @sync_port,
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 14b2e97..765039d 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -78,7 +78,6 @@ class Cli
 		key         = ask(I18n.t('form.setup.gpg_key')).to_s
 		share_keys  = ask(I18n.t('form.setup.share_gpg_keys')).to_s
 		file_gpg    = ask(I18n.t('form.setup.gpg_file', home: @conf.dir_config)).to_s
-		timeout_pwd = ask(I18n.t('form.setup.timeout')).to_s
 		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
 
 		if ['ssh', 'ftp', 'mpw'].include?(sync_type)
@@ -101,7 +100,7 @@ class Cli
 		sync_pwd  = sync_pwd.nil?  or sync_pwd.empty?  ? nil : sync_pwd
 		sync_path = sync_path.nil? or sync_path.empty? ? nil : sync_path
 
-		if @config.setup(key, share_keys, language, file_gpg, timeout_pwd, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
+		if @config.setup(key, share_keys, language, file_gpg, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
 			puts I18n.t('form.setup.valid')
 		else
 			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}"
@@ -347,69 +346,4 @@ class Cli
 		end
 	end
 
-	# Interactive mode
-	def interactive
-		group       = nil
-		last_access = Time.now.to_i
-
-		while buf = Readline.readline('<mpw> ', true)
-
-			if @config.timeout_pwd < Time.now.to_i - last_access
-				passwd_confirm = ask(I18n.t('interactive.ask_password')) {|q| q.echo = false}
-
-				if @passwd.eql?(passwd_confirm)
-					last_access = Time.now.to_i
-				else
-					puts I18n.t('interactive.bad_password')
-					next
-				end
-			else
-				last_access = Time.now.to_i
-			end
-
-			command = buf.split(' ')
-
-			case command[0]
-			when 'display', 'show', 'd', 's'
-				if not command[1].nil? and not command[1].empty?
-					display(command[1], group, command[2])
-				end
-			when 'add', 'a'
-				add
-			when 'update', 'u'
-				if not command[1].nil? and not command[1].empty?
-					update(command[1])
-				end
-			when 'remove', 'delete', 'r', 'd'
-				if not command[1].nil? and not command[1].empty?
-					remove(command[1])
-				end
-			when 'group', 'g'
-				if not command[1].nil? and not command[1].empty?
-					group = command[1]
-				else
-					group = nil
-				end
-			when 'help', 'h', '?'
-				puts I18n.t('interactive.option.title')
-				puts '--------------------'
-				puts "display, show, d, s SEARCH    #{I18n.t('interactive.option.show')}"
-				puts "group, g                      #{I18n.t('interactive.option.group')}"
-				puts "add, a                        #{I18n.t('interactive.option.add')}"
-				puts "update, u ID                  #{I18n.t('interactive.option.update')}"
-				puts "remove, delete, r, d ID       #{I18n.t('interactive.option.remove')}"
-				puts "help, h, ?                    #{I18n.t('interactive.option.help')}"
-				puts "quit, exit, q                 #{I18n.t('interactive.option.quit')}"
-			when 'quit', 'exit', 'q'
-				puts I18n.t('interactive.goodbye')
-				break
-			else
-				if not command[0].nil? and not command[0].empty?
-					puts I18n.t('interactive.unknown_command')
-				end
-			end
-
-		end
-
-	end
 end
diff --git a/mpw b/mpw
index f4092d3..0e2a171 100755
--- a/mpw
+++ b/mpw
@@ -156,13 +156,6 @@ elsif not options[:import].nil?
 	cli.import(options[:import], options[:type], options[:force])
 
 # Interactive mode
-else
-	begin
-		cli.interactive
-	rescue SystemExit, Interrupt
-		cli = nil
-		return 1
-	end
 end
 
 cli = nil

From ccb0e71edc1648e60be307d3f3b9816444cce0ff Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Dec 2014 23:53:03 +0100
Subject: [PATCH 119/531] add text color

---
 Gemfile         |  1 +
 i18n/cli/en.yml | 21 ++++----------------
 i18n/cli/fr.yml | 21 ++++----------------
 lib/UI/Cli.rb   | 52 ++++++++++++++++++++++++-------------------------
 4 files changed, 35 insertions(+), 60 deletions(-)

diff --git a/Gemfile b/Gemfile
index d005d66..7046265 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,6 +3,7 @@ gem 'highline'
 gem 'i18n', '0.6.9'
 gem 'locale'
 gem 'gpgme'
+gem 'colorize'
 
 group :ssh do
 	gem 'net-ssh'
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index dde1d7d..646671c 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -42,9 +42,9 @@ en:
     config: "Specify the configuration file to use"
     setup: "Create a new configuration file" 
     protocol: "Select the items with the specified protocol"
-    export: "Export all items in a CSV file"
+    export: "Export all items in a file"
     type: "Data's type export file [csv|yaml]"
-    import: "Import item since a CSV file"
+    import: "Import item since a yaml or csv file"
     force: "Force an action"
     format: "Change the display items format by an alternative format"
     generate_password: "Generate a random password (default 8 characters)"
@@ -107,21 +107,8 @@ en:
       port: "Enter the connection port [%{port}]: "
       comment: "Enter a comment [%{comment}]: "
       valid: "Item has been updated!" 
-
-  interactive:
-    ask_password: "Password GPG: "
-    bad_password: "Bad password!"
-    goodbye: "Goodbye!"
-    unknown_command: "Unknown command!"
-    option:
-      title: "Help"
-      show: "Search and show the results"
-      group: "Change the group for the search"
-      add: "Add an item"
-      update: "Update an item"
-      remove: "Remove an item"
-      help: "Show this message"
-      quit: "Quit the software"
+    export:
+      valid: "The export in %{file} is succesfull!"
 
   display:
     comment: "Comment"
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 64e11ee..18f78af 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -42,9 +42,9 @@ fr:
     config: "Spécifie le fichier de configuration à utiliser"
     setup: "Création d'un nouveau fichier de configuration" 
     protocol: "Sélectionne les éléments ayant le protocole spécifié"
-    export: "Exporte tous les éléments dans un fichier au format CSV"
+    export: "Exporte tous les éléments dans un fichier"
     type: "Format des données du fichier d'export [csv|yaml]"
-    import: "Importe des éléments depuis un fichier au format CSV"
+    import: "Importe des éléments depuis un fichier yaml ou csv"
     force: "Force une action, l'action ne demandera pas de confirmation"
     format: "Change le format d'affichage des éléments par un alternatif"
     generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
@@ -107,21 +107,8 @@ fr:
       port: "Entrez un port de connexion [%{port}]: "
       comment: "Entrez un commentaire [%{comment}]: "
       valid: "L'élément a bien été mis à jour!" 
-
-  interactive:
-    ask_password: "Mot de passe GPG: "
-    bad_password: "Mauvais mot de passe!"
-    goodbye: "Au revoir!"
-    unknown_command: "Commande inconnue!"
-    option:
-      title: "Aide"
-      show: "Cherche et affiche les résulats"
-      group: "Change de groupe pour la recherche"
-      add: "Ajout un élément"
-      update: "Met à jour un élément"
-      remove: "Supprime un élément"
-      help: "Affiche ce message d'aide"
-      quit: "Quitte le programme"
+    export:
+      valid: "L'export dans %{file} est un succès!"
 
   display:
     comment: "Commentaire"
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 765039d..af8719f 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -8,7 +8,7 @@ require 'highline/import'
 require 'pathname'
 require 'readline'
 require 'i18n'
-require 'yaml'
+require 'colorize'
 
 require "#{APP_ROOT}/lib/MPW"
 
@@ -42,26 +42,26 @@ class Cli
 		
 		if  not @config.sync_host.nil? and not @config.sync_port.nil?
 			if not @sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
-				puts "#{I18n.t('display.error')} #1: #{@sync.error_msg}"
+				puts "#{I18n.t('display.error')} #1: #{@sync.error_msg}".red
 			end
 		end
 
 		if @sync.enable
 			if not @mpw.sync(@sync.get(@passwd), @config.last_update)
-				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}"  if not @mpw.error_msg.nil?
-				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}" if not @sync.error_msg.nil?
+				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}".red  if not @mpw.error_msg.nil?
+				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}".red if not @sync.error_msg.nil?
 			elsif not @sync.update(File.open(@config.file_gpg).read)
-				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}"
+				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}".red
 			elsif not @config.set_last_update
-				puts "#{I18n.t('display.error')} #5: #{@config.error_msg}"
+				puts "#{I18n.t('display.error')} #5: #{@config.error_msg}".red
 			elsif not @mpw.encrypt
-				puts "#{I18n.t('display.error')} #6: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #6: #{@mpw.error_msg}".red
 			else
 				return true
 			end
 		end
 	rescue Exception => e
-		puts "#{I18n.t('display.error')} #7: #{e}"
+		puts "#{I18n.t('display.error')} #7: #{e}".red
 		puts @sync.error_msg   if not @sync.error_msg.nil?
 		puts @config.error_msg if not @config.error_msg.nil?
 		puts @mpw.error_msg    if not @mpw.error_msg.nil?
@@ -101,14 +101,14 @@ class Cli
 		sync_path = sync_path.nil? or sync_path.empty? ? nil : sync_path
 
 		if @config.setup(key, share_keys, language, file_gpg, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
-			puts I18n.t('form.setup.valid')
+			puts "#{I18n.t('form.setup.valid')}".green
 		else
-			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}".red
 			exit 2
 		end
 
 		if not @config.checkconfig
-			puts "#{I18n.t('display.error')} #9: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')} #9: #{@config.error_msg}".red
 			exit 2
 		end
 	end
@@ -143,9 +143,9 @@ class Cli
 		puts I18n.t('form.setup_gpg_key.wait')
 		
 		if @config.setup_gpg_key(password, name, length, expire)
-			puts I18n.t('form.setup_gpg_key.valid')
+			puts "#{I18n.t('form.setup_gpg_key.valid')}".green
 		else
-			puts "#{I18n.t('display.error')} #10: #{@config.error_msg}"
+			puts "#{I18n.t('display.error')} #10: #{@config.error_msg}".red
 			exit 2
 		end
 	end
@@ -158,7 +158,7 @@ class Cli
 
 		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
 		if not @mpw.decrypt(@passwd)
-			puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}".red
 			exit 2
 		end
 	end
@@ -230,12 +230,12 @@ class Cli
 		if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
 			if @mpw.encrypt
 				sync
-				puts I18n.t('form.add.valid')
+				puts "#{I18n.t('form.add.valid')}".green
 			else
-				puts "#{I18n.t('display.error')} #12: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #12: #{@mpw.error_msg}".red
 			end
 		else
-			puts "#{I18n.t('display.error')} #13: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')} #13: #{@mpw.error_msg}".red
 		end
 	end
 
@@ -259,12 +259,12 @@ class Cli
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt
 					sync
-					puts I18n.t('form.update.valid')
+					puts "#{I18n.t('form.update.valid')}".green
 				else
-					puts "#{I18n.t('display.error')} #14: #{@mpw.error_msg}"
+					puts "#{I18n.t('display.error')} #14: #{@mpw.error_msg}".red
 				end
 			else
-				puts "#{I18n.t('display.error')} #15: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #15: #{@mpw.error_msg}".red
 			end
 		else
 			puts I18n.t('display.nothing')
@@ -294,9 +294,9 @@ class Cli
 			if @mpw.remove(id)
 				if @mpw.encrypt
 					sync
-					puts I18n.t('form.delete.valid', id: id)
+					puts "#{I18n.t('form.delete.valid', id: id)}".green
 				else
-					puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}"
+					puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
 				end
 			else
 				puts I18n.t('form.delete.not_valid')
@@ -308,9 +308,9 @@ class Cli
 	# @args: file -> the destination file
 	def export(file, type=:yaml)
 		if @mpw.export(file, type)
-			puts "The export in #{file} is succesfull!"
+			puts "#{I18n.t('export.valid', file)}".green
 		else
-			puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}"
+			puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}".red
 		end
 
 	end
@@ -339,9 +339,9 @@ class Cli
 		if force
 			if @mpw.import(file, type) and @mpw.encrypt
 				sync
-				puts I18n.t('form.import.valid')
+				puts "#{I18n.t('form.import.valid')}".green
 			else
-				puts "#{I18n.t('display.error')} #18: #{@mpw.error_msg}"
+				puts "#{I18n.t('display.error')} #18: #{@mpw.error_msg}".red
 			end
 		end
 	end

From d7a7dfbe10645855781da2fbb0905f2320bf0ffd Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 7 Dec 2014 10:32:33 +0100
Subject: [PATCH 120/531] fix minor bugs

---
 lib/MPW.rb | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index f0971cb..812a24a 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -20,20 +20,20 @@ module MPW
 			@file_gpg   = file_gpg
 			@key        = key
 			@share_keys = share_keys
+			@data       = {}
 		end
 	
 		# Decrypt a gpg file
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(passwd=nil)
-			@data = {}
-	
 			if File.exist?(@file_gpg)
 				crypto = GPGME::Crypto.new(armor: true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read.force_encoding('utf-8')
 				@data = YAML.load(data_decrypt) if not data_decrypt.to_s.empty?
 			end
 	
+			puts @data.class
 			return true
 		rescue Exception => e 
 			@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
@@ -135,7 +135,7 @@ module MPW
 			row_update['protocol'] = protocol.to_s.empty? ? row['protocol']  : protocol
 			row_update['login']    = login.to_s.empty?    ? row['login']     : login
 			row_update['password'] = passwd.to_s.empty?   ? row['password']  : passwd
-			row_update['port']     = port.to_s.empty?     ? row['port']      : port
+			row_update['port']     = port.to_s.empty?     ? row['port']      : port.to_i
 			row_update['comment']  = comment.to_s.empty?  ? row['comment']   : comment
 			row_update['date']     = Time.now.to_i
 	
@@ -147,7 +147,8 @@ module MPW
 			if update
 				@data[id] = row_update
 			else
-				@data[row_update['id']] = row_update
+				id = row_update['id']
+				@data[id] = row_update
 			end
 	
 			return true

From e5529e6a7b8c00410721abc4df9978b71404c5be Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 7 Dec 2014 10:35:05 +0100
Subject: [PATCH 121/531] add unit tests

---
 test/test.cfg | 13 +++++++++++++
 test/test.rb  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)
 create mode 100644 test/test.cfg
 create mode 100644 test/test.rb

diff --git a/test/test.cfg b/test/test.cfg
new file mode 100644
index 0000000..c0ace9e
--- /dev/null
+++ b/test/test.cfg
@@ -0,0 +1,13 @@
+---
+config:
+  key: test@test-mpw.org
+  share_keys: 
+  lang: en
+  file_gpg: "test.gpg"
+  sync_type: mpw
+  sync_host: 
+  sync_port: 
+  sync_user: 
+  sync_pwd: 
+  sync_path: 
+  last_update: 
diff --git a/test/test.rb b/test/test.rb
new file mode 100644
index 0000000..0f03d1b
--- /dev/null
+++ b/test/test.rb
@@ -0,0 +1,45 @@
+# File:  tc_simple_number.rb
+ 
+require_relative "../lib/MPW"
+require "test/unit"
+ 
+class TestMPW < Test::Unit::TestCase
+
+
+	def test_initialize
+		File.delete('test.gpg') if File.exist?('test.gpg')
+	end
+	
+	def test_load_empty_file
+		mpw = MPW::MPW.new('test.cfg')
+		mpw.decrypt
+
+		assert_equal(0, mpw.search.length)
+	end
+
+	def test_add
+		mpw = MPW::MPW.new('test.cfg')
+
+		name     = 'test_name'
+		group    = 'test_group'
+		host     = 'test_host'
+		protocol = 'test_protocol'
+		login    = 'test_login'
+		password = 'test_password'
+		port     = '42'
+		comment  = 'test_comment'
+
+		mpw.update(name, group, host, protocol, login, password, port, comment)
+
+		assert_equal(1, mpw.search.length)
+		assert_equal('test_name', mpw.search[0]['name'])
+		assert_equal('test_group', mpw.search[0]['group'])
+		assert_equal('test_host', mpw.search[0]['host'])
+		assert_equal('test_protocol', mpw.search[0]['protocol'])
+		assert_equal('test_login', mpw.search[0]['login'])
+		assert_equal('test_password', mpw.search[0]['password'])
+		assert_equal(42, mpw.search[0]['port'])
+		assert_equal('test_comment', mpw.search[0]['comment'])
+	end
+ 
+end

From 140819dbb56d4965ef06f00b0229733b56765a39 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 7 Dec 2014 16:24:31 +0100
Subject: [PATCH 122/531] add new unit test

---
 lib/MPW.rb       |   4 +-
 test/test.rb     |  45 --------------------
 test/test_mpw.rb | 105 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 47 deletions(-)
 delete mode 100644 test/test.rb
 create mode 100644 test/test_mpw.rb

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 812a24a..a4c5a1d 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -8,6 +8,7 @@ require 'gpgme'
 require 'csv'
 require 'i18n'
 require 'fileutils'
+require 'yaml'
 	
 module MPW
 	class MPW
@@ -15,7 +16,7 @@ module MPW
 		attr_accessor :error_msg
 		
 		# Constructor
-		def initialize(file_gpg, key=nil, share_keys='')
+		def initialize(file_gpg, key, share_keys='')
 			@error_msg  = nil
 			@file_gpg   = file_gpg
 			@key        = key
@@ -33,7 +34,6 @@ module MPW
 				@data = YAML.load(data_decrypt) if not data_decrypt.to_s.empty?
 			end
 	
-			puts @data.class
 			return true
 		rescue Exception => e 
 			@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
diff --git a/test/test.rb b/test/test.rb
deleted file mode 100644
index 0f03d1b..0000000
--- a/test/test.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# File:  tc_simple_number.rb
- 
-require_relative "../lib/MPW"
-require "test/unit"
- 
-class TestMPW < Test::Unit::TestCase
-
-
-	def test_initialize
-		File.delete('test.gpg') if File.exist?('test.gpg')
-	end
-	
-	def test_load_empty_file
-		mpw = MPW::MPW.new('test.cfg')
-		mpw.decrypt
-
-		assert_equal(0, mpw.search.length)
-	end
-
-	def test_add
-		mpw = MPW::MPW.new('test.cfg')
-
-		name     = 'test_name'
-		group    = 'test_group'
-		host     = 'test_host'
-		protocol = 'test_protocol'
-		login    = 'test_login'
-		password = 'test_password'
-		port     = '42'
-		comment  = 'test_comment'
-
-		mpw.update(name, group, host, protocol, login, password, port, comment)
-
-		assert_equal(1, mpw.search.length)
-		assert_equal('test_name', mpw.search[0]['name'])
-		assert_equal('test_group', mpw.search[0]['group'])
-		assert_equal('test_host', mpw.search[0]['host'])
-		assert_equal('test_protocol', mpw.search[0]['protocol'])
-		assert_equal('test_login', mpw.search[0]['login'])
-		assert_equal('test_password', mpw.search[0]['password'])
-		assert_equal(42, mpw.search[0]['port'])
-		assert_equal('test_comment', mpw.search[0]['comment'])
-	end
- 
-end
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
new file mode 100644
index 0000000..bfed5d0
--- /dev/null
+++ b/test/test_mpw.rb
@@ -0,0 +1,105 @@
+# File:  tc_simple_number.rb
+ 
+require_relative "../lib/MPW"
+require "test/unit"
+ 
+class TestMPW < Test::Unit::TestCase
+
+	def setup
+		@file_gpg = 'test.gpg'
+		@key      = 'test-mpw@test-mpw.local'
+
+		if defined?(I18n.enforce_available_locales)
+			I18n.enforce_available_locales = false
+		end
+
+		File.delete(@file_gpg) if File.exist?(@file_gpg)
+		@mpw = MPW::MPW.new(@file_gpg, @key)
+	end
+	
+	def test_load_empty_file
+		assert(@mpw.decrypt)
+		assert_equal(0, @mpw.search.length)
+	end
+
+	def test_add
+		name     = 'test_name'
+		group    = 'test_group'
+		host     = 'test_host'
+		protocol = 'test_protocol'
+		login    = 'test_login'
+		password = 'test_password'
+		port     = '42'
+		comment  = 'test_comment'
+
+		assert(@mpw.update(name, group, host, protocol, login, password, port, comment))
+
+		assert_equal(1, @mpw.search.length)
+
+		result = @mpw.search[0]
+		assert_equal('test_name',     result['name'])
+		assert_equal('test_group',    result['group'])
+		assert_equal('test_host',     result['host'])
+		assert_equal('test_protocol', result['protocol'])
+		assert_equal('test_login',    result['login'])
+		assert_equal('test_password', result['password'])
+		assert_equal(42,              result['port'])
+		assert_equal('test_comment',  result['comment'])
+
+		assert(@mpw.update(name, group, host, protocol, login, password, port, comment))
+
+		assert_equal(2, @mpw.search.length)
+	end
+
+	def test_add_empty_name
+		name     = ''
+		group    = 'test_group'
+		host     = 'test_host'
+		protocol = 'test_protocol'
+		login    = 'test_login'
+		password = 'test_password'
+		port     = '42'
+		comment  = 'test_comment'
+
+		assert(!@mpw.update(name, group, host, protocol, login, password, port, comment))
+
+		assert_equal(0, @mpw.search.length)
+	end
+
+	def test_update
+		name     = 'test_name'
+		group    = 'test_group'
+		host     = 'test_host'
+		protocol = 'test_protocol'
+		login    = 'test_login'
+		password = 'test_password'
+		port     = '42'
+		comment  = 'test_comment'
+
+		assert(@mpw.update(name, group, host, protocol, login, password, port, comment))
+
+		id       = @mpw.search[0]['id']
+		name     = 'test_name_update'
+		group    = 'test_group_update'
+		host     = 'test_host_update'
+		protocol = 'test_protocol_update'
+		login    = 'test_login_update'
+		password = 'test_password_update'
+		port     = '43'
+		comment  = 'test_comment_update'
+
+		assert(@mpw.update(name, group, host, protocol, login, password, port, comment, id))
+		assert_equal(1, @mpw.search.length)
+
+		result = @mpw.search_by_id(id)
+		assert_equal('test_name_update',     result['name'])
+		assert_equal('test_group_update',    result['group'])
+		assert_equal('test_host_update',     result['host'])
+		assert_equal('test_protocol_update', result['protocol'])
+		assert_equal('test_login_update',    result['login'])
+		assert_equal('test_password_update', result['password'])
+		assert_equal(43,                     result['port'])
+		assert_equal('test_comment_update',  result['comment'])
+	end
+ 
+end

From f443485ee0da995b6bf410901754e72192ec005f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 9 Dec 2014 22:53:49 +0100
Subject: [PATCH 123/531] add unit test import + fixture

---
 test/fixtures.yml |  19 +++++++++
 test/test_mpw.rb  | 100 +++++++++++++++++++++++++---------------------
 2 files changed, 73 insertions(+), 46 deletions(-)
 create mode 100644 test/fixtures.yml

diff --git a/test/fixtures.yml b/test/fixtures.yml
new file mode 100644
index 0000000..7cd5520
--- /dev/null
+++ b/test/fixtures.yml
@@ -0,0 +1,19 @@
+add:
+  name:	'test_name'
+  group: 'test_group'
+  host: 'test_host'
+  protocol: 'test_protocol'
+  login: 'test_login'
+  password: 'test_password'
+  port: '42'
+  comment: 'test_comment'
+update:
+  name:	'test_name_update'
+  group: 'test_group_update'
+  host: 'test_host_update'
+  protocol: 'test_protocol_update'
+  login: 'test_login_update'
+  password: 'test_password_update'
+  port: '43'
+  comment: 'test_comment_update'
+
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index bfed5d0..431f05c 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -1,20 +1,23 @@
 # File:  tc_simple_number.rb
  
-require_relative "../lib/MPW"
-require "test/unit"
+require_relative '../lib/MPW'
+require 'test/unit'
+require 'yaml'
  
 class TestMPW < Test::Unit::TestCase
 
+	#@@mpw = MPW::MPW.new('test.gpg', 'a.waksberg@yaegashi.fr')
 	def setup
-		@file_gpg = 'test.gpg'
-		@key      = 'test-mpw@test-mpw.local'
+		file_gpg = 'test.gpg'
+		key      = 'test-mpw@test-mpw.local'
 
 		if defined?(I18n.enforce_available_locales)
 			I18n.enforce_available_locales = false
 		end
 
-		File.delete(@file_gpg) if File.exist?(@file_gpg)
-		@mpw = MPW::MPW.new(@file_gpg, @key)
+		File.delete(file_gpg) if File.exist?(file_gpg)
+		@mpw = MPW::MPW.new(file_gpg, key)
+		@fixtures = YAML.load_file('fixtures.yml')
 	end
 	
 	def test_load_empty_file
@@ -23,16 +26,14 @@ class TestMPW < Test::Unit::TestCase
 	end
 
 	def test_add
-		name     = 'test_name'
-		group    = 'test_group'
-		host     = 'test_host'
-		protocol = 'test_protocol'
-		login    = 'test_login'
-		password = 'test_password'
-		port     = '42'
-		comment  = 'test_comment'
-
-		assert(@mpw.update(name, group, host, protocol, login, password, port, comment))
+		assert(@mpw.update(@fixtures['add']['name'], 
+		                   @fixtures['add']['group'], 
+		                   @fixtures['add']['host'],
+		                   @fixtures['add']['protocol'],
+		                   @fixtures['add']['login'],
+		                   @fixtures['add']['password'],
+		                   @fixtures['add']['port'],
+		                   @fixtures['add']['comment']))
 
 		assert_equal(1, @mpw.search.length)
 
@@ -46,49 +47,54 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(42,              result['port'])
 		assert_equal('test_comment',  result['comment'])
 
-		assert(@mpw.update(name, group, host, protocol, login, password, port, comment))
+		assert(@mpw.update(@fixtures['add']['name'], 
+		                   @fixtures['add']['group'], 
+		                   @fixtures['add']['host'],
+		                   @fixtures['add']['protocol'],
+		                   @fixtures['add']['login'],
+		                   @fixtures['add']['password'],
+		                   @fixtures['add']['port'],
+		                   @fixtures['add']['comment']))
+
 
 		assert_equal(2, @mpw.search.length)
 	end
 
 	def test_add_empty_name
-		name     = ''
-		group    = 'test_group'
-		host     = 'test_host'
-		protocol = 'test_protocol'
-		login    = 'test_login'
-		password = 'test_password'
-		port     = '42'
-		comment  = 'test_comment'
-
-		assert(!@mpw.update(name, group, host, protocol, login, password, port, comment))
+		assert(!@mpw.update('', 
+		                   @fixtures['add']['group'], 
+		                   @fixtures['add']['host'],
+		                   @fixtures['add']['protocol'],
+		                   @fixtures['add']['login'],
+		                   @fixtures['add']['password'],
+		                   @fixtures['add']['port'],
+		                   @fixtures['add']['comment']))
 
 		assert_equal(0, @mpw.search.length)
 	end
 
 	def test_update
-		name     = 'test_name'
-		group    = 'test_group'
-		host     = 'test_host'
-		protocol = 'test_protocol'
-		login    = 'test_login'
-		password = 'test_password'
-		port     = '42'
-		comment  = 'test_comment'
+		assert(@mpw.update(@fixtures['add']['name'], 
+		                   @fixtures['add']['group'], 
+		                   @fixtures['add']['host'],
+		                   @fixtures['add']['protocol'],
+		                   @fixtures['add']['login'],
+		                   @fixtures['add']['password'],
+		                   @fixtures['add']['port'],
+		                   @fixtures['add']['comment']))
 
-		assert(@mpw.update(name, group, host, protocol, login, password, port, comment))
+		id = @mpw.search[0]['id']
 
-		id       = @mpw.search[0]['id']
-		name     = 'test_name_update'
-		group    = 'test_group_update'
-		host     = 'test_host_update'
-		protocol = 'test_protocol_update'
-		login    = 'test_login_update'
-		password = 'test_password_update'
-		port     = '43'
-		comment  = 'test_comment_update'
+		assert(@mpw.update(@fixtures['update']['name'], 
+		                   @fixtures['update']['group'], 
+		                   @fixtures['update']['host'],
+		                   @fixtures['update']['protocol'],
+		                   @fixtures['update']['login'],
+		                   @fixtures['update']['password'],
+		                   @fixtures['update']['port'],
+		                   @fixtures['update']['comment'],
+		                   id))
 
-		assert(@mpw.update(name, group, host, protocol, login, password, port, comment, id))
 		assert_equal(1, @mpw.search.length)
 
 		result = @mpw.search_by_id(id)
@@ -102,4 +108,6 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal('test_comment_update',  result['comment'])
 	end
  
+ 	def test_import
+	end
 end

From 8bbddd1656cd3d65c2a5c5d619a5479c63aab626 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 10 Dec 2014 21:44:20 +0100
Subject: [PATCH 124/531] add test import

---
 test/test_mpw.rb | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 431f05c..5697a7b 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -109,5 +109,7 @@ class TestMPW < Test::Unit::TestCase
 	end
  
  	def test_import
+		assert(@mpw.import('fixtures.yml'))
+		assert_equal(2, @mpw.search.length)
 	end
 end

From 2cc4ed75bd4f7306fb6dfd17d037affe884f7c16 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 28 Dec 2014 18:18:45 +0100
Subject: [PATCH 125/531] add export test

---
 test/test_mpw.rb | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 5697a7b..870a66d 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -3,6 +3,7 @@
 require_relative '../lib/MPW'
 require 'test/unit'
 require 'yaml'
+require 'csv'
  
 class TestMPW < Test::Unit::TestCase
 
@@ -112,4 +113,21 @@ class TestMPW < Test::Unit::TestCase
 		assert(@mpw.import('fixtures.yml'))
 		assert_equal(2, @mpw.search.length)
 	end
+
+	def test_export_yaml
+		assert(@mpw.import('fixtures.yml'))
+		assert_equal(2, @mpw.search.length)
+		assert(@mpw.export('export.yml', :yaml))
+		export = YAML::load_file('export.yml')
+		assert_equal(2, export.length)
+	end
+
+	def test_export_csv
+		assert(@mpw.import('fixtures.yml'))
+		assert_equal(2, @mpw.search.length)
+		assert(@mpw.export('export.yml', :csv))
+		export = CSV.parse(File.read('export.yml'), headers: true)
+		assert_equal(2, export.length)
+
+	end
 end

From 4d1da879885d318aff7068f00574f2013bb40aa5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 28 Dec 2014 18:26:30 +0100
Subject: [PATCH 126/531] add import csv

---
 test/test_mpw.rb | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 870a66d..dd348d7 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -109,8 +109,8 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal('test_comment_update',  result['comment'])
 	end
  
- 	def test_import
-		assert(@mpw.import('fixtures.yml'))
+ 	def test_import_yaml
+		assert(@mpw.import('fixtures.yml', :yaml))
 		assert_equal(2, @mpw.search.length)
 	end
 
@@ -125,9 +125,16 @@ class TestMPW < Test::Unit::TestCase
 	def test_export_csv
 		assert(@mpw.import('fixtures.yml'))
 		assert_equal(2, @mpw.search.length)
-		assert(@mpw.export('export.yml', :csv))
-		export = CSV.parse(File.read('export.yml'), headers: true)
+		assert(@mpw.export('export.csv', :csv))
+		export = CSV.parse(File.read('export.csv'), headers: true)
 		assert_equal(2, export.length)
+	end
 
+	def test_import_csv
+		assert(@mpw.import('fixtures.yml'))
+		assert_equal(2, @mpw.search.length)
+		assert(@mpw.export('export.csv', :csv))
+		assert(@mpw.import('export.csv', :csv))
+		assert_equal(4, @mpw.search.length)
 	end
 end

From 178010ab913876106cb72d994344f4f92d83948f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 28 Dec 2014 18:48:37 +0100
Subject: [PATCH 127/531] add empty update test

---
 test/test_mpw.rb | 74 +++++++++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 29 deletions(-)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index dd348d7..cade965 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -7,8 +7,9 @@ require 'csv'
  
 class TestMPW < Test::Unit::TestCase
 
-	#@@mpw = MPW::MPW.new('test.gpg', 'a.waksberg@yaegashi.fr')
 	def setup
+		@fixture_file = 'fixtures.yml'
+
 		file_gpg = 'test.gpg'
 		key      = 'test-mpw@test-mpw.local'
 
@@ -18,7 +19,7 @@ class TestMPW < Test::Unit::TestCase
 
 		File.delete(file_gpg) if File.exist?(file_gpg)
 		@mpw = MPW::MPW.new(file_gpg, key)
-		@fixtures = YAML.load_file('fixtures.yml')
+		@fixtures = YAML.load_file(@fixture_file)
 	end
 	
 	def test_load_empty_file
@@ -39,14 +40,14 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(1, @mpw.search.length)
 
 		result = @mpw.search[0]
-		assert_equal('test_name',     result['name'])
-		assert_equal('test_group',    result['group'])
-		assert_equal('test_host',     result['host'])
-		assert_equal('test_protocol', result['protocol'])
-		assert_equal('test_login',    result['login'])
-		assert_equal('test_password', result['password'])
-		assert_equal(42,              result['port'])
-		assert_equal('test_comment',  result['comment'])
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
 
 		assert(@mpw.update(@fixtures['add']['name'], 
 		                   @fixtures['add']['group'], 
@@ -63,13 +64,13 @@ class TestMPW < Test::Unit::TestCase
 
 	def test_add_empty_name
 		assert(!@mpw.update('', 
-		                   @fixtures['add']['group'], 
-		                   @fixtures['add']['host'],
-		                   @fixtures['add']['protocol'],
-		                   @fixtures['add']['login'],
-		                   @fixtures['add']['password'],
-		                   @fixtures['add']['port'],
-		                   @fixtures['add']['comment']))
+		                    @fixtures['add']['group'], 
+		                    @fixtures['add']['host'],
+		                    @fixtures['add']['protocol'],
+		                    @fixtures['add']['login'],
+		                    @fixtures['add']['password'],
+		                    @fixtures['add']['port'],
+		                    @fixtures['add']['comment']))
 
 		assert_equal(0, @mpw.search.length)
 	end
@@ -86,6 +87,21 @@ class TestMPW < Test::Unit::TestCase
 
 		id = @mpw.search[0]['id']
 
+		# Test empty update
+		assert(@mpw.update('','', '','','','','', '', id))
+
+		result = @mpw.search_by_id(id)
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
+
+
+		# Test real update
 		assert(@mpw.update(@fixtures['update']['name'], 
 		                   @fixtures['update']['group'], 
 		                   @fixtures['update']['host'],
@@ -99,23 +115,23 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(1, @mpw.search.length)
 
 		result = @mpw.search_by_id(id)
-		assert_equal('test_name_update',     result['name'])
-		assert_equal('test_group_update',    result['group'])
-		assert_equal('test_host_update',     result['host'])
-		assert_equal('test_protocol_update', result['protocol'])
-		assert_equal('test_login_update',    result['login'])
-		assert_equal('test_password_update', result['password'])
-		assert_equal(43,                     result['port'])
-		assert_equal('test_comment_update',  result['comment'])
+		assert_equal(@fixtures['update']['name'],      result['name'])
+		assert_equal(@fixtures['update']['group'],     result['group'])
+		assert_equal(@fixtures['update']['host'],      result['host'])
+		assert_equal(@fixtures['update']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['update']['login'],     result['login'])
+		assert_equal(@fixtures['update']['password'],  result['password'])
+		assert_equal(@fixtures['update']['port'].to_i, result['port'])
+		assert_equal(@fixtures['update']['comment'],   result['comment'])
 	end
  
  	def test_import_yaml
-		assert(@mpw.import('fixtures.yml', :yaml))
+		assert(@mpw.import(@fixture_file, :yaml))
 		assert_equal(2, @mpw.search.length)
 	end
 
 	def test_export_yaml
-		assert(@mpw.import('fixtures.yml'))
+		assert(@mpw.import(@fixture_file))
 		assert_equal(2, @mpw.search.length)
 		assert(@mpw.export('export.yml', :yaml))
 		export = YAML::load_file('export.yml')
@@ -123,7 +139,7 @@ class TestMPW < Test::Unit::TestCase
 	end
 
 	def test_export_csv
-		assert(@mpw.import('fixtures.yml'))
+		assert(@mpw.import(@fixture_file))
 		assert_equal(2, @mpw.search.length)
 		assert(@mpw.export('export.csv', :csv))
 		export = CSV.parse(File.read('export.csv'), headers: true)
@@ -131,7 +147,7 @@ class TestMPW < Test::Unit::TestCase
 	end
 
 	def test_import_csv
-		assert(@mpw.import('fixtures.yml'))
+		assert(@mpw.import(@fixture_file))
 		assert_equal(2, @mpw.search.length)
 		assert(@mpw.export('export.csv', :csv))
 		assert(@mpw.import('export.csv', :csv))

From 9aca9da49b603891328905b042d556dec6aed505 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 28 Dec 2014 19:35:51 +0100
Subject: [PATCH 128/531] unittest: fix export and import test

---
 test/test_mpw.rb | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index cade965..2208c6c 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -128,6 +128,16 @@ class TestMPW < Test::Unit::TestCase
  	def test_import_yaml
 		assert(@mpw.import(@fixture_file, :yaml))
 		assert_equal(2, @mpw.search.length)
+
+		result = @mpw.search[0]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
 	end
 
 	def test_export_yaml
@@ -136,6 +146,16 @@ class TestMPW < Test::Unit::TestCase
 		assert(@mpw.export('export.yml', :yaml))
 		export = YAML::load_file('export.yml')
 		assert_equal(2, export.length)
+
+		result = export.values[0]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
 	end
 
 	def test_export_csv
@@ -144,6 +164,17 @@ class TestMPW < Test::Unit::TestCase
 		assert(@mpw.export('export.csv', :csv))
 		export = CSV.parse(File.read('export.csv'), headers: true)
 		assert_equal(2, export.length)
+
+		result = export[0]
+		assert_equal(@fixtures['add']['name'],     result['name'])
+		assert_equal(@fixtures['add']['group'],    result['group'])
+		assert_equal(@fixtures['add']['host'],     result['host'])
+		assert_equal(@fixtures['add']['protocol'], result['protocol'])
+		assert_equal(@fixtures['add']['login'],    result['login'])
+		assert_equal(@fixtures['add']['password'], result['password'])
+		assert_equal(@fixtures['add']['port'],     result['port'])
+		assert_equal(@fixtures['add']['comment'],  result['comment'])
+
 	end
 
 	def test_import_csv
@@ -152,5 +183,16 @@ class TestMPW < Test::Unit::TestCase
 		assert(@mpw.export('export.csv', :csv))
 		assert(@mpw.import('export.csv', :csv))
 		assert_equal(4, @mpw.search.length)
+
+		result = @mpw.search[2]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
+	
 	end
 end

From 1c4e54582fcdc09f453203b98ed23ad1c43fe9c8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 28 Dec 2014 19:43:13 +0100
Subject: [PATCH 129/531] fix import preview default yaml

---
 lib/MPW.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index a4c5a1d..61d9661 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -231,7 +231,7 @@ module MPW
 		# Return a preview import 
 		# @args: file -> path to file import
 		# @rtrn: a hash with the items to import, if there is an error return false
-		def import_preview(file, type=:csv)
+		def import_preview(file, type=:yaml)
 			result = []
 			case type
 			when :csv

From 3d9367189053bd9e9dc19f5390b5bdc70ab53009 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 29 Dec 2014 13:59:17 +0100
Subject: [PATCH 130/531] fix minor bug

---
 lib/Config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Config.rb b/lib/Config.rb
index c1cc17e..92abad8 100644
--- a/lib/Config.rb
+++ b/lib/Config.rb
@@ -170,7 +170,7 @@ module MPW
 		# @rtrn: true if the key exist, else false
 		def check_gpg_key?
 			ctx = GPGME::Ctx.new
-			ctx.each_key(key, true) do
+			ctx.each_key(@key, true) do
 				return true
 			end
 

From 60d7e9ef8e36b1067f35671deb878f96c27ded1e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 29 Dec 2014 15:36:06 +0100
Subject: [PATCH 131/531] improve unittest

---
 test/test_import.csv |   3 +
 test/test_import.yml |  23 ++++++
 test/test_mpw.rb     | 172 ++++++++++++++++++++++---------------------
 3 files changed, 115 insertions(+), 83 deletions(-)
 create mode 100644 test/test_import.csv
 create mode 100644 test/test_import.yml

diff --git a/test/test_import.csv b/test/test_import.csv
new file mode 100644
index 0000000..1452c07
--- /dev/null
+++ b/test/test_import.csv
@@ -0,0 +1,3 @@
+name,group,protocol,host,login,password,port,comment
+test_name_csv,test_group_csv,test_protocol_csv,test_host_csv,test_login_csv,test_password_csv,44,test_comment_csv
+test_name_update_csv,test_group_update_csv,test_protocol_update_csv,test_host_update_csv,test_login_update_csv,test_password_update_csv,45,test_comment_update_csv
diff --git a/test/test_import.yml b/test/test_import.yml
new file mode 100644
index 0000000..e7fef00
--- /dev/null
+++ b/test/test_import.yml
@@ -0,0 +1,23 @@
+---
+XWas7vpy0HerhOYd:
+  id: XWas7vpy0HerhOYd
+  name: test_name
+  group: test_group
+  host: test_host
+  protocol: test_protocol
+  login: test_login
+  password: test_password
+  port: 42
+  comment: test_comment
+  date: 1419858983
+D7URyJENLa91jt0b:
+  id: D7URyJENLa91jt0b
+  name: test_name_update
+  group: test_group_update
+  host: test_host_update
+  protocol: test_protocol_update
+  login: test_login_update
+  password: test_password_update
+  port: 43
+  comment: test_comment_update
+  date: 1419858983
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 2208c6c..9c5b87f 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -26,6 +26,85 @@ class TestMPW < Test::Unit::TestCase
 		assert(@mpw.decrypt)
 		assert_equal(0, @mpw.search.length)
 	end
+ 
+ 	def test_import_yaml
+		import_file = 'test_import.yml'
+
+		assert(@mpw.import(import_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+
+		result = @mpw.search[0]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
+	end
+
+	def test_export_yaml
+		export_file = 'test_export.yml'
+
+		assert(@mpw.import(@fixture_file))
+		assert_equal(2, @mpw.search.length)
+		assert(@mpw.export(export_file, :yaml))
+		export = YAML::load_file(export_file)
+		assert_equal(2, export.length)
+
+		result = export.values[0]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
+
+		File.unlink(export_file)
+	end
+
+	def test_import_csv
+		import_file = 'test_import.csv'
+
+		assert(@mpw.import(import_file, :csv))
+		assert_equal(2, @mpw.search.length)
+
+		import = CSV.parse(File.read(import_file), headers: true)
+
+		result = @mpw.search[0]
+		assert_equal(import[0]['name'],      result['name'])
+		assert_equal(import[0]['group'],     result['group'])
+		assert_equal(import[0]['host'],      result['host'])
+		assert_equal(import[0]['protocol'],  result['protocol'])
+		assert_equal(import[0]['login'],     result['login'])
+		assert_equal(import[0]['password'],  result['password'])
+		assert_equal(import[0]['port'].to_i, result['port'])
+		assert_equal(import[0]['comment'],   result['comment'])
+	end
+
+	def test_export_csv
+		export_file = 'test_export.csv'
+		assert(@mpw.import(@fixture_file))
+		assert_equal(2, @mpw.search.length)
+		assert(@mpw.export(export_file, :csv))
+		export = CSV.parse(File.read(export_file), headers: true)
+		assert_equal(2, export.length)
+
+		result = export[0]
+		assert_equal(@fixtures['add']['name'],     result['name'])
+		assert_equal(@fixtures['add']['group'],    result['group'])
+		assert_equal(@fixtures['add']['host'],     result['host'])
+		assert_equal(@fixtures['add']['protocol'], result['protocol'])
+		assert_equal(@fixtures['add']['login'],    result['login'])
+		assert_equal(@fixtures['add']['password'], result['password'])
+		assert_equal(@fixtures['add']['port'],     result['port'])
+		assert_equal(@fixtures['add']['comment'],  result['comment'])
+
+		File.unlink(export_file)
+	end
 
 	def test_add
 		assert(@mpw.update(@fixtures['add']['name'], 
@@ -75,19 +154,12 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(0, @mpw.search.length)
 	end
 
-	def test_update
-		assert(@mpw.update(@fixtures['add']['name'], 
-		                   @fixtures['add']['group'], 
-		                   @fixtures['add']['host'],
-		                   @fixtures['add']['protocol'],
-		                   @fixtures['add']['login'],
-		                   @fixtures['add']['password'],
-		                   @fixtures['add']['port'],
-		                   @fixtures['add']['comment']))
+	def test_update_empty
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
 
 		id = @mpw.search[0]['id']
 
-		# Test empty update
 		assert(@mpw.update('','', '','','','','', '', id))
 
 		result = @mpw.search_by_id(id)
@@ -99,9 +171,14 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['add']['password'],  result['password'])
 		assert_equal(@fixtures['add']['port'].to_i, result['port'])
 		assert_equal(@fixtures['add']['comment'],   result['comment'])
+	end
 
+	def test_update
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+
+		id = @mpw.search[0]['id']
 
-		# Test real update
 		assert(@mpw.update(@fixtures['update']['name'], 
 		                   @fixtures['update']['group'], 
 		                   @fixtures['update']['host'],
@@ -112,7 +189,7 @@ class TestMPW < Test::Unit::TestCase
 		                   @fixtures['update']['comment'],
 		                   id))
 
-		assert_equal(1, @mpw.search.length)
+		assert_equal(2, @mpw.search.length)
 
 		result = @mpw.search_by_id(id)
 		assert_equal(@fixtures['update']['name'],      result['name'])
@@ -124,75 +201,4 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['update']['port'].to_i, result['port'])
 		assert_equal(@fixtures['update']['comment'],   result['comment'])
 	end
- 
- 	def test_import_yaml
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
-
-		result = @mpw.search[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
-	end
-
-	def test_export_yaml
-		assert(@mpw.import(@fixture_file))
-		assert_equal(2, @mpw.search.length)
-		assert(@mpw.export('export.yml', :yaml))
-		export = YAML::load_file('export.yml')
-		assert_equal(2, export.length)
-
-		result = export.values[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
-	end
-
-	def test_export_csv
-		assert(@mpw.import(@fixture_file))
-		assert_equal(2, @mpw.search.length)
-		assert(@mpw.export('export.csv', :csv))
-		export = CSV.parse(File.read('export.csv'), headers: true)
-		assert_equal(2, export.length)
-
-		result = export[0]
-		assert_equal(@fixtures['add']['name'],     result['name'])
-		assert_equal(@fixtures['add']['group'],    result['group'])
-		assert_equal(@fixtures['add']['host'],     result['host'])
-		assert_equal(@fixtures['add']['protocol'], result['protocol'])
-		assert_equal(@fixtures['add']['login'],    result['login'])
-		assert_equal(@fixtures['add']['password'], result['password'])
-		assert_equal(@fixtures['add']['port'],     result['port'])
-		assert_equal(@fixtures['add']['comment'],  result['comment'])
-
-	end
-
-	def test_import_csv
-		assert(@mpw.import(@fixture_file))
-		assert_equal(2, @mpw.search.length)
-		assert(@mpw.export('export.csv', :csv))
-		assert(@mpw.import('export.csv', :csv))
-		assert_equal(4, @mpw.search.length)
-
-		result = @mpw.search[2]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
-	
-	end
 end

From e7c03269a606cbca2c66b93bafc21e62da9ae49c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 29 Dec 2014 15:45:25 +0100
Subject: [PATCH 132/531] unittest: add remove tests

---
 test/test_mpw.rb | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 9c5b87f..46760db 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -1,4 +1,4 @@
-# File:  tc_simple_number.rb
+#!/usr/bin/ruby
  
 require_relative '../lib/MPW'
 require 'test/unit'
@@ -201,4 +201,23 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['update']['port'].to_i, result['port'])
 		assert_equal(@fixtures['update']['comment'],   result['comment'])
 	end
+
+	def test_remove
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+
+		id = @mpw.search[0]['id']
+		assert(@mpw.remove(id)) 
+
+		assert_equal(1, @mpw.search.length)
+	end
+
+	def test_remove_noexistent
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+
+		assert(!@mpw.remove('TEST_NOEXISTENT_ID')) 
+
+		assert_equal(2, @mpw.search.length)
+	end
 end

From c04fa3af94eefadf95d0938ecfefcd9f41788a85 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 29 Dec 2014 22:32:36 +0100
Subject: [PATCH 133/531] fix indent

---
 lib/MPW.rb | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 61d9661..ea9ac2f 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -176,12 +176,12 @@ module MPW
 		def export(file, type=:yaml)
 			case type
 			when :csv
-					CSV.open(file, 'w', write_headers: true,
-										headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
-						@data.each do |id, r|
-							csv << [r['name'], r['group'], r['protocol'], r['host'], r['login'], r['password'], r['port'], r['comment']]
-						end
+				CSV.open(file, 'w', write_headers: true,
+									headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
+					@data.each do |id, r|
+						csv << [r['name'], r['group'], r['protocol'], r['host'], r['login'], r['password'], r['port'], r['comment']]
 					end
+				end
 
 			when :yaml
 				File.open(file, 'w') {|f| f << @data.to_yaml}

From cad292b3f95d1415fb999989aa1bdd27ced55245 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 30 Dec 2014 08:57:45 +0100
Subject: [PATCH 134/531] unittest: move files

---
 test/{ => files}/fixtures.yml    | 0
 test/{ => files}/test_import.csv | 0
 test/{ => files}/test_import.yml | 0
 test/test_mpw.rb                 | 6 +++---
 4 files changed, 3 insertions(+), 3 deletions(-)
 rename test/{ => files}/fixtures.yml (100%)
 rename test/{ => files}/test_import.csv (100%)
 rename test/{ => files}/test_import.yml (100%)

diff --git a/test/fixtures.yml b/test/files/fixtures.yml
similarity index 100%
rename from test/fixtures.yml
rename to test/files/fixtures.yml
diff --git a/test/test_import.csv b/test/files/test_import.csv
similarity index 100%
rename from test/test_import.csv
rename to test/files/test_import.csv
diff --git a/test/test_import.yml b/test/files/test_import.yml
similarity index 100%
rename from test/test_import.yml
rename to test/files/test_import.yml
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 46760db..1874cb6 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -8,7 +8,7 @@ require 'csv'
 class TestMPW < Test::Unit::TestCase
 
 	def setup
-		@fixture_file = 'fixtures.yml'
+		@fixture_file = 'files/fixtures.yml'
 
 		file_gpg = 'test.gpg'
 		key      = 'test-mpw@test-mpw.local'
@@ -28,7 +28,7 @@ class TestMPW < Test::Unit::TestCase
 	end
  
  	def test_import_yaml
-		import_file = 'test_import.yml'
+		import_file = 'files/test_import.yml'
 
 		assert(@mpw.import(import_file, :yaml))
 		assert_equal(2, @mpw.search.length)
@@ -67,7 +67,7 @@ class TestMPW < Test::Unit::TestCase
 	end
 
 	def test_import_csv
-		import_file = 'test_import.csv'
+		import_file = 'files/test_import.csv'
 
 		assert(@mpw.import(import_file, :csv))
 		assert_equal(2, @mpw.search.length)

From 087320e52b9b81b264f0998909307c7a6b7f3aac Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 30 Dec 2014 09:22:41 +0100
Subject: [PATCH 135/531] unittest: add encrypt tests

---
 test/test.cfg    | 13 ----------
 test/test_mpw.rb | 66 +++++++++++++++++++++++++++++++++++-------------
 2 files changed, 49 insertions(+), 30 deletions(-)
 delete mode 100644 test/test.cfg

diff --git a/test/test.cfg b/test/test.cfg
deleted file mode 100644
index c0ace9e..0000000
--- a/test/test.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
----
-config:
-  key: test@test-mpw.org
-  share_keys: 
-  lang: en
-  file_gpg: "test.gpg"
-  sync_type: mpw
-  sync_host: 
-  sync_port: 
-  sync_user: 
-  sync_pwd: 
-  sync_path: 
-  last_update: 
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 1874cb6..9a9bbda 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -6,12 +6,13 @@ require 'yaml'
 require 'csv'
  
 class TestMPW < Test::Unit::TestCase
-
 	def setup
 		@fixture_file = 'files/fixtures.yml'
 
 		file_gpg = 'test.gpg'
-		key      = 'test-mpw@test-mpw.local'
+		key      = 'a.waksberg@yaegashi.fr'
+
+		puts
 
 		if defined?(I18n.enforce_available_locales)
 			I18n.enforce_available_locales = false
@@ -21,13 +22,8 @@ class TestMPW < Test::Unit::TestCase
 		@mpw = MPW::MPW.new(file_gpg, key)
 		@fixtures = YAML.load_file(@fixture_file)
 	end
-	
-	def test_load_empty_file
-		assert(@mpw.decrypt)
-		assert_equal(0, @mpw.search.length)
-	end
  
- 	def test_import_yaml
+ 	def test_01_import_yaml
 		import_file = 'files/test_import.yml'
 
 		assert(@mpw.import(import_file, :yaml))
@@ -44,7 +40,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['add']['comment'],   result['comment'])
 	end
 
-	def test_export_yaml
+	def test_02_export_yaml
 		export_file = 'test_export.yml'
 
 		assert(@mpw.import(@fixture_file))
@@ -66,7 +62,7 @@ class TestMPW < Test::Unit::TestCase
 		File.unlink(export_file)
 	end
 
-	def test_import_csv
+	def test_03_import_csv
 		import_file = 'files/test_import.csv'
 
 		assert(@mpw.import(import_file, :csv))
@@ -85,7 +81,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(import[0]['comment'],   result['comment'])
 	end
 
-	def test_export_csv
+	def test_04_export_csv
 		export_file = 'test_export.csv'
 		assert(@mpw.import(@fixture_file))
 		assert_equal(2, @mpw.search.length)
@@ -106,7 +102,7 @@ class TestMPW < Test::Unit::TestCase
 		File.unlink(export_file)
 	end
 
-	def test_add
+	def test_05_add
 		assert(@mpw.update(@fixtures['add']['name'], 
 		                   @fixtures['add']['group'], 
 		                   @fixtures['add']['host'],
@@ -141,7 +137,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(2, @mpw.search.length)
 	end
 
-	def test_add_empty_name
+	def test_06_add_empty_name
 		assert(!@mpw.update('', 
 		                    @fixtures['add']['group'], 
 		                    @fixtures['add']['host'],
@@ -154,7 +150,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(0, @mpw.search.length)
 	end
 
-	def test_update_empty
+	def test_07_update_empty
 		assert(@mpw.import(@fixture_file, :yaml))
 		assert_equal(2, @mpw.search.length)
 
@@ -173,7 +169,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['add']['comment'],   result['comment'])
 	end
 
-	def test_update
+	def test_08_update
 		assert(@mpw.import(@fixture_file, :yaml))
 		assert_equal(2, @mpw.search.length)
 
@@ -202,7 +198,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['update']['comment'],   result['comment'])
 	end
 
-	def test_remove
+	def test_09_remove
 		assert(@mpw.import(@fixture_file, :yaml))
 		assert_equal(2, @mpw.search.length)
 
@@ -212,7 +208,7 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(1, @mpw.search.length)
 	end
 
-	def test_remove_noexistent
+	def test_10_remove_noexistent
 		assert(@mpw.import(@fixture_file, :yaml))
 		assert_equal(2, @mpw.search.length)
 
@@ -220,4 +216,40 @@ class TestMPW < Test::Unit::TestCase
 
 		assert_equal(2, @mpw.search.length)
 	end
+
+	def test_11_encrypt_empty_file
+		assert(@mpw.encrypt)	
+	end
+
+	def test_12_encrypt
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+
+		assert(@mpw.encrypt)	
+	end
+
+	def test_13_decrypt_empty_file
+		assert(@mpw.decrypt)
+		assert_equal(0, @mpw.search.length)
+	end
+
+	def test_14_decrypt
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+
+		assert(@mpw.encrypt)	
+
+		assert(@mpw.decrypt)
+		assert_equal(2, @mpw.search.length)
+
+		result = @mpw.search[0]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
+	end
 end

From 04a4173f1609ba0a529ec5250fafccccbf7dabe2 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 30 Dec 2014 09:32:14 +0100
Subject: [PATCH 136/531] unittest: add env for key white a real key

---
 test/test_mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 9a9bbda..a670e10 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -10,7 +10,7 @@ class TestMPW < Test::Unit::TestCase
 		@fixture_file = 'files/fixtures.yml'
 
 		file_gpg = 'test.gpg'
-		key      = 'a.waksberg@yaegashi.fr'
+		key      = ENV['MPW_TEST_KEY']
 
 		puts
 

From 3ac63c0c8a4b764e04a10a59674d5b365bc4d661 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 30 Dec 2014 09:55:31 +0100
Subject: [PATCH 137/531] unittest: add firsts sync tests

---
 test/test_mpw.rb | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index a670e10..b3ca25d 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -252,4 +252,37 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['add']['port'].to_i, result['port'])
 		assert_equal(@fixtures['add']['comment'],   result['comment'])
 	end
+
+	def test_15_sync_local_empty
+		import_file = 'files/test_import.yml'
+
+		data = []
+		YAML.load_file(import_file).each_value { |v| data.push(v) }
+
+		@mpw.sync(data, 0)
+
+		result = @mpw.search[0]
+		assert_equal(@fixtures['add']['name'],      result['name'])
+		assert_equal(@fixtures['add']['group'],     result['group'])
+		assert_equal(@fixtures['add']['host'],      result['host'])
+		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add']['login'],     result['login'])
+		assert_equal(@fixtures['add']['password'],  result['password'])
+		assert_equal(@fixtures['add']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add']['comment'],   result['comment'])
+
+		assert_equal(2, @mpw.search.length)
+	end
+
+	def test_16_sync_remote_outdated_and_local_empty
+		import_file = 'files/test_import.yml'
+
+		data = []
+		YAML.load_file(import_file).each_value { |v| data.push(v) }
+
+		@mpw.sync(data, Time.now.to_i)
+
+		assert_equal(0, @mpw.search.length)
+	end
+
 end

From 4e4efb8979bdb6a8367eacbcaa3733aa0df4b4c7 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 31 Dec 2014 11:30:30 +0100
Subject: [PATCH 138/531] unittest: add sync empty data test

---
 test/test_mpw.rb | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index b3ca25d..94bbfa3 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -280,9 +280,17 @@ class TestMPW < Test::Unit::TestCase
 		data = []
 		YAML.load_file(import_file).each_value { |v| data.push(v) }
 
-		@mpw.sync(data, Time.now.to_i)
+		assert(@mpw.sync(data, Time.now.to_i))
 
 		assert_equal(0, @mpw.search.length)
 	end
 
+	def test_17_sync_empty_data
+		assert(@mpw.import(@fixture_file, :yaml))
+		assert_equal(2, @mpw.search.length)
+	
+		assert(@mpw.sync([], 0))
+
+		assert_equal(2, @mpw.search.length)
+	end
 end

From 1eac4001d92836cb8df33cdb9505c00067edf269 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 31 Dec 2014 11:31:28 +0100
Subject: [PATCH 139/531] minor fix for the syntax

---
 lib/MPW.rb | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index ea9ac2f..f6d58e9 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -77,7 +77,7 @@ module MPW
 				search = search.downcase
 			end
 	
-			@data.each do |id, row|
+			@data.each_value do |row|
 				name    = row['name'].nil?    ? nil : row['name'].downcase
 				server  = row['host'].nil?    ? nil : row['host'].downcase
 				comment = row['comment'].nil? ? nil : row['comment'].downcase
@@ -93,11 +93,11 @@ module MPW
 		end
 	
 		# Search in some csv data
-		# @args: id_search -> the id item
+		# @args: id -> the id item
 		# @rtrn: a row with the resultat of the search
-		def search_by_id(id_search)
-			@data.each do |id, row|
-				return row if id == id_search
+		def search_by_id(id)
+			@data.each_value do |row|
+				return row if row['id'] == id
 			end
 	
 			return []
@@ -158,8 +158,8 @@ module MPW
 		# @args: id -> the item's identifiant
 		# @rtrn: true if the item has been deleted
 		def remove(id)
-			@data.each do |k, row|
-				if k == id
+			@data.each_value do |row|
+				if row['id'] == id
 					@data.delete(id)
 					return true
 				end
@@ -211,7 +211,7 @@ module MPW
 				end
 
 			when :yaml
-				YAML::load_file(file).each do |k, row| 
+				YAML::load_file(file).each_value do |row| 
 					if not update(row['name'], row['group'], row['host'], row['protocol'], row['login'], row['password'], row['port'], row['comment'])
 						return false
 					end
@@ -262,7 +262,7 @@ module MPW
 				@error_msg = I18n.t('error.sync.array')
 				return false
 			else not data_remote.to_s.empty?
-				@data.each do |lk, l|
+				@data.each_value do |l|
 					j = 0
 					update = false
 		

From 40291632eb4812cc663cf78a5b157444de63c02b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 16 Jan 2015 23:11:10 +0100
Subject: [PATCH 140/531] remove alternative format

---
 lib/UI/Cli.rb | 28 ++++------------------------
 1 file changed, 4 insertions(+), 24 deletions(-)

diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index af8719f..40938b6 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -171,11 +171,7 @@ class Cli
 
 		if not result.empty?
 			result.each do |r|
-				if format.nil? || !format
-					displayFormat(r)
-				else
-					displayFormatAlt(r)
-				end
+				display_item(r)
 			end
 		else
 			puts I18n.t('display.nothing')
@@ -184,7 +180,7 @@ class Cli
 
 	# Display an item in the default format
 	# @args: item -> an array with the item information
-	def displayFormat(item)
+	def diplay_item(item)
 		puts '--------------------'
 		puts "Id: #{item['id']}"
 		puts "#{I18n.t('display.name')}: #{item['name']}"
@@ -197,22 +193,6 @@ class Cli
 		puts "#{I18n.t('display.comment')}: #{item['comment']}"
 	end
 
-	# Display an item in the alternative format
-	# @args: item -> an array with the item information
-	def displayFormatAlt(item)
-		port = item[:port].nil? ? '' : ":#{item[:port]}"
-
-		if item[:password].nil? or item[:password].empty?
-			if item[:login].include('@')
-				puts "# #{item[:id]} #{item[:protocol]}://#{item[:login]}@#{item[:host]}#{port}"
-			else
-				puts "# #{item[:id]} #{item[:protocol]}://{#{item[:login]}}@#{item[:host]}#{port}"
-			end
-		else
-			puts "# #{item[:id]} #{item[:protocol]}://{#{item[:login]}:#{item[:password]}}@#{item[:host]}#{port}"
-		end
-	end
-
 	# Form to add a new item
 	def add
 		row = []
@@ -279,7 +259,7 @@ class Cli
 			result = @mpw.search_by_id(id)
 
 			if result.length > 0
-				displayFormat(result)
+				display_item(result)
 
 				confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
@@ -324,7 +304,7 @@ class Cli
 			result = @mpw.import_preview(file, type)
 			if result.is_a?(Array) and not result.empty?
 				result.each do |r|
-					displayFormat(r)
+					display_item(r)
 				end
 
 				confirm = ask("#{I18n.t('form.import.ask', file: file)} (y/N) ").to_s

From f5b98de4c1d678224f1e2e1f42ce3879e9a072c6 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 17 Jan 2015 00:05:40 +0100
Subject: [PATCH 141/531] add select form when multiple result

---
 i18n/cli/en.yml     |  5 +++++
 i18n/cli/fr.yml     |  5 +++++
 lib/Sync/MPWSync.rb |  2 +-
 lib/UI/Cli.rb       | 33 +++++++++++++++++++++++++--------
 mpw                 |  2 +-
 5 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 646671c..b8c1bd7 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -31,6 +31,9 @@ en:
       upload: "Can't upload the file on the server!"
       unknown: "An unknown error is occured!" 
 
+  warning:
+    select: 'Your choice is not a valid element!'
+
   option:
     usage: "Usage"
     show: "Search and show the items"
@@ -51,6 +54,7 @@ en:
     help: "Show this help message"
 
   form:
+    select: "Select the item: "
     add:
       title: "Add a new item"
       name: "Enter the name: "
@@ -122,6 +126,7 @@ en:
     port: "Port"
     protocol: "Protocol"
     server: "Server"
+    warning: "Warning"
 
   ssh:
     option:
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 18f78af..ea9b9f0 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -31,6 +31,9 @@ fr:
       upload: "Impossible d'envoyer le fichier sur le serveur!"
       unknown: "Une erreur inconnue est survenue!" 
 
+  warning:
+    select: "Votre choix n'est pas un élément valide!"
+
   option:
     usage: "Utilisation"
     show: "Recherche et affiche les éléments"
@@ -51,6 +54,7 @@ fr:
     help: "Affiche ce message d'aide"
 
   form:
+    select: "Sélectionner l'élément: "
     add:
       title: "Ajout d'un nouvel élément"
       name: "Entrez le nom: "
@@ -122,6 +126,7 @@ fr:
     port: "Port"
     protocol: "Protocol"
     server: "Serveur"
+    warning: "Warning"
 
   ssh:
     option:
diff --git a/lib/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
index ae0c466..df8066f 100644
--- a/lib/Sync/MPWSync.rb
+++ b/lib/Sync/MPWSync.rb
@@ -90,7 +90,7 @@ module MPW
 						file << msg['data']
 					end
 					
-					mpw = MPW.new(tmp_file)
+					mpw = MPW.new(tmp_file, @gpg_key)
 					if not mpw.decrypt(gpg_password)
 						@error_msg = mpw.error_msg
 						return nil
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 40938b6..b1b4cda 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -39,7 +39,7 @@ class Cli
 				return false
 			end
 		end
-		
+
 		if  not @config.sync_host.nil? and not @config.sync_port.nil?
 			if not @sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 				puts "#{I18n.t('display.error')} #1: #{@sync.error_msg}".red
@@ -166,21 +166,37 @@ class Cli
 	# Display the query's result
 	# @args: search -> the string to search
 	#        protocol -> search from a particular protocol
-	def display(search, protocol=nil, group=nil, format=nil)
+	def display(search, protocol=nil, group=nil)
 		result = @mpw.search(search, group, protocol)
 
-		if not result.empty?
-			result.each do |r|
-				display_item(r)
-			end
-		else
+		case result.length
+		when 0
 			puts I18n.t('display.nothing')
+		when 1
+			display_item(result.first)
+		else
+			i = 1
+			result.each do |r|
+				print "#{i}: ".cyan
+				print r['name']
+				print " -> #{r['comment']}".magenta if not r['comment'].to_s.empty?
+				print "\n"
+
+				i += 1
+			end
+			choice = ask(I18n.t('form.select')).to_i
+
+			if choice >= 1 and choice < i 
+				display_item(result[choice-1])
+			else
+				puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+			end
 		end
 	end
 
 	# Display an item in the default format
 	# @args: item -> an array with the item information
-	def diplay_item(item)
+	def display_item(item)
 		puts '--------------------'
 		puts "Id: #{item['id']}"
 		puts "#{I18n.t('display.name')}: #{item['name']}"
@@ -193,6 +209,7 @@ class Cli
 		puts "#{I18n.t('display.comment')}: #{item['comment']}"
 	end
 
+
 	# Form to add a new item
 	def add
 		row = []
diff --git a/mpw b/mpw
index 0e2a171..d18eaf5 100755
--- a/mpw
+++ b/mpw
@@ -133,7 +133,7 @@ cli.sync
 
 # Display the item's informations
 if not options[:display].nil?
-	cli.display(options[:display], options[:group], options[:type], options[:format])
+	cli.display(options[:display], options[:group], options[:type])
 
 # Remove an item
 elsif not options[:remove].nil?

From 298a020e351e952ef94eb4be71ec57b60c5a5c39 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 17 Jan 2015 00:12:01 +0100
Subject: [PATCH 142/531] add color when display an item

---
 lib/UI/Cli.rb | 30 +++++++++++++++++++-----------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index b1b4cda..95f6f5f 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -197,19 +197,27 @@ class Cli
 	# Display an item in the default format
 	# @args: item -> an array with the item information
 	def display_item(item)
-		puts '--------------------'
-		puts "Id: #{item['id']}"
-		puts "#{I18n.t('display.name')}: #{item['name']}"
-		puts "#{I18n.t('display.group')}: #{item['group']}"
-		puts "#{I18n.t('display.server')}: #{item['host']}"
-		puts "#{I18n.t('display.protocol')}: #{item['protocol']}"
-		puts "#{I18n.t('display.login')}: #{item['login']}"
-		puts "#{I18n.t('display.password')}: #{item['password']}"
-		puts "#{I18n.t('display.port')}: #{item['port']}"
-		puts "#{I18n.t('display.comment')}: #{item['comment']}"
+		puts '--------------------'.cyan
+		print 'Id: '.cyan
+		puts  item['id']
+		print "#{I18n.t('display.name')}: ".cyan
+		puts  item['name']
+		print "#{I18n.t('display.group')}: ".cyan
+		puts  item['group']
+		print "#{I18n.t('display.server')}: ".cyan
+		puts  item['host']
+		print "#{I18n.t('display.protocol')}: ".cyan
+		puts  item['protocol']
+		print "#{I18n.t('display.login')}: ".cyan
+		puts  item['login']
+		print "#{I18n.t('display.password')}: ".cyan
+		puts  item['password']
+		print "#{I18n.t('display.port')}: ".cyan
+		puts  item['port']
+		print "#{I18n.t('display.comment')}: ".cyan
+		puts  item['comment']
 	end
 
-
 	# Form to add a new item
 	def add
 		row = []

From a49c975f19331f7462fa3c4199440f2f30ca9783 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 17 Jan 2015 12:01:37 +0100
Subject: [PATCH 143/531] fix sync ftp and ssh

---
 lib/Sync/FTP.rb     | 4 ++--
 lib/Sync/MPWSync.rb | 4 ++--
 lib/Sync/SSH.rb     | 4 ++--
 lib/UI/Cli.rb       | 6 +++++-
 4 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/lib/Sync/FTP.rb b/lib/Sync/FTP.rb
index 4b6ba33..1bb2a55 100644
--- a/lib/Sync/FTP.rb
+++ b/lib/Sync/FTP.rb
@@ -50,7 +50,7 @@ module MPW
 			# Get data on server
 			# @args: gpg_password -> the gpg password
 			# @rtrn: nil if nothing data or error
-			def get(gpg_password)
+			def get(gpg_key, gpg_password)
 				return nil if not @enable
 				
 				tmp_file = tmpfile
@@ -59,7 +59,7 @@ module MPW
 					ftp.gettextfile(@path, tmp_file)
 				end
 			
-				mpw = MPW.new(tmp_file)
+				mpw = MPW.new(tmp_file, gpg_key)
 				if not mpw.decrypt(gpg_password)
 					@error_msg = mpw.error_msg
 					return nil
diff --git a/lib/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
index df8066f..9bb6572 100644
--- a/lib/Sync/MPWSync.rb
+++ b/lib/Sync/MPWSync.rb
@@ -61,7 +61,7 @@ module MPW
 			# Get data on server
 			# @args: gpg_password -> the gpg password
 			# @rtrn: nil if nothing data or error
-			def get(gpg_password)
+			def get(gpg_key, gpg_password)
 				return nil if not @enable
 			
 				msg = nil
@@ -90,7 +90,7 @@ module MPW
 						file << msg['data']
 					end
 					
-					mpw = MPW.new(tmp_file, @gpg_key)
+					mpw = MPW.new(tmp_file, gpg_key)
 					if not mpw.decrypt(gpg_password)
 						@error_msg = mpw.error_msg
 						return nil
diff --git a/lib/Sync/SSH.rb b/lib/Sync/SSH.rb
index d1f3801..9ebca2d 100644
--- a/lib/Sync/SSH.rb
+++ b/lib/Sync/SSH.rb
@@ -50,7 +50,7 @@ module MPW
 			# Get data on server
 			# @args: gpg_password -> the gpg password
 			# @rtrn: nil if nothing data or error
-			def get(gpg_password)
+			def get(gpg_key, gpg_password)
 				return nil if not @enable
 				
 				tmp_file = tmpfile
@@ -58,7 +58,7 @@ module MPW
 					scp.download!(@path, tmp_file)
 				end
 			
-				mpw = MPW.new(tmp_file)
+				mpw = MPW.new(tmp_file, gpg_key)
 				if not mpw.decrypt(gpg_password)
 					@error_msg = mpw.error_msg
 					return nil
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 95f6f5f..2dc3bd7 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -47,15 +47,19 @@ class Cli
 		end
 
 		if @sync.enable
-			if not @mpw.sync(@sync.get(@passwd), @config.last_update)
+			if not @mpw.sync(@sync.get(@config.key, @passwd), @config.last_update)
 				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}".red  if not @mpw.error_msg.nil?
 				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}".red if not @sync.error_msg.nil?
+
 			elsif not @sync.update(File.open(@config.file_gpg).read)
 				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}".red
+
 			elsif not @config.set_last_update
 				puts "#{I18n.t('display.error')} #5: #{@config.error_msg}".red
+
 			elsif not @mpw.encrypt
 				puts "#{I18n.t('display.error')} #6: #{@mpw.error_msg}".red
+
 			else
 				return true
 			end

From 271138ae809214f227c44b6ea22dcc927d42df4c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@believedigital.com>
Date: Tue, 27 Jan 2015 09:37:53 +0100
Subject: [PATCH 144/531] fix bug'

---
 lib/UI/Cli.rb | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 2dc3bd7..2142b65 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -256,14 +256,14 @@ class Cli
 		if not row.empty?
 			puts I18n.t('form.update.title')
 			puts '--------------------'
-			name     = ask(I18n.t('form.update.name'    , name:     row[:name])).to_s
-			group    = ask(I18n.t('form.update.group'   , group:    row[:group])).to_s
-			server   = ask(I18n.t('form.update.server'  , server:   row[:host])).to_s
-			protocol = ask(I18n.t('form.update.protocol', protocol: row[:protocol])).to_s
-			login    = ask(I18n.t('form.update.login'   , login:    row[:login])).to_s
+			name     = ask(I18n.t('form.update.name'    , name:     row['name'])).to_s
+			group    = ask(I18n.t('form.update.group'   , group:    row['group'])).to_s
+			server   = ask(I18n.t('form.update.server'  , server:   row['host'])).to_s
+			protocol = ask(I18n.t('form.update.protocol', protocol: row['protocol'])).to_s
+			login    = ask(I18n.t('form.update.login'   , login:    row['login'])).to_s
 			passwd   = ask(I18n.t('form.update.password')).to_s
-			port     = ask(I18n.t('form.update.port'    , port:     row[:port])).to_s
-			comment  = ask(I18n.t('form.update.comment' , comment:  row[:comment])).to_s
+			port     = ask(I18n.t('form.update.port'    , port:     row['port'])).to_s
+			comment  = ask(I18n.t('form.update.comment' , comment:  row['comment'])).to_s
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt

From 7e546b65d27c30eca4efde416cae2ee18850d089 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 27 Jan 2015 23:17:16 +0100
Subject: [PATCH 145/531] unstable: new class item

---
 lib/Item.rb |  80 ++++++++++++++++++++++++++++++++++++++++
 lib/MPW.rb  | 103 +++++++++++++++++-----------------------------------
 2 files changed, 113 insertions(+), 70 deletions(-)
 create mode 100644 lib/Item.rb

diff --git a/lib/Item.rb b/lib/Item.rb
new file mode 100644
index 0000000..df36916
--- /dev/null
+++ b/lib/Item.rb
@@ -0,0 +1,80 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+require 'rubygems'
+require 'i18n'
+	
+module MPW
+	class Item
+
+		attr_accessor :error_msg
+
+		attr_accessor :id
+		attr_accessor :name
+		attr_accessor :group
+		attr_accessor :host
+		attr_accessor :protocol
+		attr_accessor :user
+		attr_accessor :password
+		attr_accessor :port
+		attr_accessor :comment
+		attr_accessor :last_edit
+		attr_accessor :created
+
+		def initialize(options={})
+			if not defined?(options[:id]) or not options[:id].to_s.empty? or 
+			   not defined?(options[:created]) or not options[:created].to_s.empty?  
+				@id      = generate_id
+				@created = Time.now.to_i
+			else
+				@id      = options[:id]
+				@created = options[:created]
+			end
+
+			update(options)
+		end
+
+		def update(options={})
+			if defined?(options[:name]) and options[:name].to_s.empty?
+				@error_msg = I18n.t('error.update.name_empty')
+				return false
+			end
+
+			@name      = options[:name]       if defined?(options[:name])
+			@group     = options[:group]      if defined?(options[:group])
+			@host      = options[:host]       if defined?(options[:host])
+			@protocol  = options[:protocol]   if defined?(options[:protocol])
+			@user      = options[:user]       if defined?(options[:user])
+			@password  = options[:password]   if defined?(options[:password])
+			@port      = options[:port].to_i  if defined?(options[:port])
+			@comment   = options[:comment]    if defined?(options[:comment])
+			@last_edit = Time.now.to_i
+
+			return true
+		end
+
+		def empty?
+			return @name.to_s.empty?
+		end
+
+		def nil?
+			return false
+		end
+
+		private
+		def set_name(name)
+			if name.to_s.empty?
+				return false
+
+			@name = name
+			return true
+		end
+
+		private
+		def generate_id
+			return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
+		end
+	end
+end	
diff --git a/lib/MPW.rb b/lib/MPW.rb
index f6d58e9..11dc5fe 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -9,6 +9,7 @@ require 'csv'
 require 'i18n'
 require 'fileutils'
 require 'yaml'
+require "#{APP_ROOT}/lib/Item"
 	
 module MPW
 	class MPW
@@ -21,17 +22,31 @@ module MPW
 			@file_gpg   = file_gpg
 			@key        = key
 			@share_keys = share_keys
-			@data       = {}
+			@data       = []
 		end
 	
 		# Decrypt a gpg file
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
-		def decrypt(passwd=nil)
+		def decrypt(password=nil)
 			if File.exist?(@file_gpg)
-				crypto = GPGME::Crypto.new(armor: true)
-				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read.force_encoding('utf-8')
-				@data = YAML.load(data_decrypt) if not data_decrypt.to_s.empty?
+				crypto       = GPGME::Crypto.new(armor: true)
+				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
+				if not data_decrypt.to_s.empty?
+					YAML.load(data_decrypt).each do |d|
+						@data.push(MPW::Item.new(id:        d['id'],
+						                         name:      d['name'],
+						                         group:     d['group'],
+						                         host:      d['host'],
+						                         protocol:  d['protocol'],
+						                         user:      d['login'],
+						                         password:  d['password'],
+						                         port:      d['port'],
+						                         comment:   d['comment'],
+						                         last_edit: d['last_edit'],
+						                         created:   d['created'],
+						                        )
+					end
 			end
 	
 			return true
@@ -70,21 +85,20 @@ module MPW
 		# @args: search -> the string to search
 		#        protocol -> the connection protocol (ssh, web, other)
 		# @rtrn: a list with the resultat of the search
-		def search(search='', group=nil, protocol=nil)
+		def list(options={})
 			result = []
 	
-			if not search.nil?
-				search = search.downcase
-			end
+			search = defined?(options[:search]) ? options[:search].downcase : ''
 	
-			@data.each_value do |row|
-				name    = row['name'].nil?    ? nil : row['name'].downcase
-				server  = row['host'].nil?    ? nil : row['host'].downcase
-				comment = row['comment'].nil? ? nil : row['comment'].downcase
+			@data.each do |item|
+				name    = item.name.nil?    ? nil : item.name.downcase
+				host    = item.host.nil?    ? nil : item.host.downcase
+				comment = item.comment.nil? ? nil : item.comment.downcase
 	
-				if name =~ /^.*#{search}.*$/ or server =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/ 
-					if (protocol.nil? or protocol.eql?(row[:protocol])) and (group.nil? or group.eql?(row[:group]))
-						result.push(row)
+				if name =~ /^.*#{search}.*$/ or host =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/ 
+					if (not defined?(options[:protocol] or options[:protocol].eql?(item.protocol)) and 
+					   (group.nil? or options[:group].eql?(item.group))
+						result.push(item)
 					end
 				end
 			end
@@ -96,62 +110,11 @@ module MPW
 		# @args: id -> the id item
 		# @rtrn: a row with the resultat of the search
 		def search_by_id(id)
-			@data.each_value do |row|
-				return row if row['id'] == id
+			@data.each do |item|
+				return item if item.id == id
 			end
 	
-			return []
-		end
-	
-		# Update an item
-		# @args: id -> the item's identifiant
-		#        name -> the item name
-		#        group ->  the item group
-		#        server -> the ip or hostname
-		#        protocol -> the protocol
-		#        login -> the login
-		#        passwd -> the password
-		#        port -> the port
-		#        comment -> a comment
-		# @rtrn: true if the item has been updated
-		def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
-			row    = {}
-			update = false
-	
-			i  = 0
-			if @data.instance_of?(Hash) and @data.has_key?(id)
-				row = @data[id]
-			end
-	
-			if port.to_i <= 0
-				port = nil
-			end
-	
-			row_update             = {}
-			row_update['id']       = id.to_s.empty?       ? MPW.password(16) : id
-			row_update['name']     = name.to_s.empty?     ? row['name']      : name
-			row_update['group']    = group.to_s.empty?    ? row['group']     : group
-			row_update['host']     = server.to_s.empty?   ? row['host']      : server
-			row_update['protocol'] = protocol.to_s.empty? ? row['protocol']  : protocol
-			row_update['login']    = login.to_s.empty?    ? row['login']     : login
-			row_update['password'] = passwd.to_s.empty?   ? row['password']  : passwd
-			row_update['port']     = port.to_s.empty?     ? row['port']      : port.to_i
-			row_update['comment']  = comment.to_s.empty?  ? row['comment']   : comment
-			row_update['date']     = Time.now.to_i
-	
-			if row_update['name'].to_s.empty?
-				@error_msg = I18n.t('error.update.name_empty')
-				return false
-			end
-	
-			if update
-				@data[id] = row_update
-			else
-				id = row_update['id']
-				@data[id] = row_update
-			end
-	
-			return true
+			return nil
 		end
 		
 		# Remove an item 

From 16fd5d6c24a68ee87b6c5c12df9fff1f75c019d5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 28 Jan 2015 23:08:25 +0100
Subject: [PATCH 146/531] unstable: work in progress to Item class

---
 lib/Item.rb   |  1 +
 lib/MPW.rb    | 96 ++++++++++++++++++++++++++++++---------------------
 lib/UI/Cli.rb | 42 +++++++++++-----------
 3 files changed, 79 insertions(+), 60 deletions(-)

diff --git a/lib/Item.rb b/lib/Item.rb
index df36916..b6964be 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -67,6 +67,7 @@ module MPW
 		def set_name(name)
 			if name.to_s.empty?
 				return false
+			end
 
 			@name = name
 			return true
diff --git a/lib/MPW.rb b/lib/MPW.rb
index 11dc5fe..875a8e4 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -33,20 +33,22 @@ module MPW
 				crypto       = GPGME::Crypto.new(armor: true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
 				if not data_decrypt.to_s.empty?
-					YAML.load(data_decrypt).each do |d|
-						@data.push(MPW::Item.new(id:        d['id'],
-						                         name:      d['name'],
-						                         group:     d['group'],
-						                         host:      d['host'],
-						                         protocol:  d['protocol'],
-						                         user:      d['login'],
-						                         password:  d['password'],
-						                         port:      d['port'],
-						                         comment:   d['comment'],
-						                         last_edit: d['last_edit'],
-						                         created:   d['created'],
-						                        )
+					YAML.load(data_decrypt).each_value do |d|
+						@data.push(Item.new(id:        d['id'],
+						                    name:      d['name'],
+						                    group:     d['group'],
+						                    host:      d['host'],
+						                    protocol:  d['protocol'],
+						                    user:      d['login'],
+						                    password:  d['password'],
+						                    port:      d['port'],
+						                    comment:   d['comment'],
+						                    last_edit: d['last_edit'],
+						                    created:   d['created'],
+						                   )
+						          )
 					end
+				end
 			end
 	
 			return true
@@ -60,7 +62,21 @@ module MPW
 		def encrypt
 			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk") if File.exist?(@file_gpg)
 	
-			data_to_encrypt = @data.to_yaml
+			data_to_encrypt = {}
+			@data.each do |item|
+				data_to_encrypt.merge!({'id'        => item.id,
+				                        'name'      => item.name,
+				                        'group'     => item.group,
+				                        'host'      => item.host,
+				                        'protocol'  => item.protocol,
+				                        'user'      => item.user,
+				                        'password'  => item.password,
+				                        'port'      => item.port,
+				                        'comment'   => item.comment,
+				                        'last_edit' => item.last_edit,
+				                        'created'   => item.created,
+				                      })
+			end
 	
 			recipients = []
 			recipients.push(@key)
@@ -70,7 +86,7 @@ module MPW
 
 			crypto = GPGME::Crypto.new(armor: true)
 			file_gpg = File.open(@file_gpg, 'w+')
-			crypto.encrypt(data_to_encrypt, recipients: recipients, output: file_gpg)
+			crypto.encrypt(data_to_encrypt.to_yaml, recipients: recipients, output: file_gpg)
 			file_gpg.close
 	
 			FileUtils.rm("#{@file_gpg}.bk") if File.exist?("#{@file_gpg}.bk")
@@ -88,7 +104,11 @@ module MPW
 		def list(options={})
 			result = []
 	
-			search = defined?(options[:search]) ? options[:search].downcase : ''
+			if not defined?(options[:search]) or options[:search].nil?
+				search = ''
+			else
+				search = options[:search].downcase
+			end
 	
 			@data.each do |item|
 				name    = item.name.nil?    ? nil : item.name.downcase
@@ -96,10 +116,7 @@ module MPW
 				comment = item.comment.nil? ? nil : item.comment.downcase
 	
 				if name =~ /^.*#{search}.*$/ or host =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/ 
-					if (not defined?(options[:protocol] or options[:protocol].eql?(item.protocol)) and 
-					   (group.nil? or options[:group].eql?(item.group))
-						result.push(item)
-					end
+					result.push(item)
 				end
 			end
 	
@@ -117,21 +134,6 @@ module MPW
 			return nil
 		end
 		
-		# Remove an item 
-		# @args: id -> the item's identifiant
-		# @rtrn: true if the item has been deleted
-		def remove(id)
-			@data.each_value do |row|
-				if row['id'] == id
-					@data.delete(id)
-					return true
-				end
-			end
-	
-			@error_msg = I18n.t('error.delete.id_no_exist', id: id)
-			return false
-		end
-	
 		# Export to csv
 		# @args: file -> file where you export the data
 		#        type -> udata type
@@ -140,14 +142,30 @@ module MPW
 			case type
 			when :csv
 				CSV.open(file, 'w', write_headers: true,
-									headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
-					@data.each do |id, r|
-						csv << [r['name'], r['group'], r['protocol'], r['host'], r['login'], r['password'], r['port'], r['comment']]
+									headers: ['name', 'group', 'protocol', 'host', 'user', 'password', 'port', 'comment']) do |csv|
+					@data.each do |item|
+						csv << [item.id, item.group, item.protocol, item.host, item.user, item.password, item.port, item.comment]
 					end
 				end
 
 			when :yaml
-				File.open(file, 'w') {|f| f << @data.to_yaml}
+				data = {}
+				@data.each do |item|
+						data.merge!({'id'        => item.id,
+						             'name'      => item.name,
+						             'group'     => item.group,
+						             'host'      => item.host,
+						             'protocol'  => item.protocol,
+						             'user'      => item.user,
+						             'password'  => item.password,
+						             'port'      => item.port,
+						             'comment'   => item.comment,
+						             'last_edit' => item.last_edit,
+						             'created'   => item.created,
+						           })
+				end
+
+				File.open(file, 'w') {|f| f << data.to_yaml}
 
 			else
 				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 2142b65..6a3e8ff 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -171,7 +171,7 @@ class Cli
 	# @args: search -> the string to search
 	#        protocol -> search from a particular protocol
 	def display(search, protocol=nil, group=nil)
-		result = @mpw.search(search, group, protocol)
+		result = @mpw.list(search: search)
 
 		case result.length
 		when 0
@@ -180,10 +180,10 @@ class Cli
 			display_item(result.first)
 		else
 			i = 1
-			result.each do |r|
+			result.each do |item|
 				print "#{i}: ".cyan
-				print r['name']
-				print " -> #{r['comment']}".magenta if not r['comment'].to_s.empty?
+				print item.name
+				print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
 				print "\n"
 
 				i += 1
@@ -203,23 +203,23 @@ class Cli
 	def display_item(item)
 		puts '--------------------'.cyan
 		print 'Id: '.cyan
-		puts  item['id']
+		puts  item.id
 		print "#{I18n.t('display.name')}: ".cyan
-		puts  item['name']
+		puts  item.name
 		print "#{I18n.t('display.group')}: ".cyan
-		puts  item['group']
+		puts  item.group
 		print "#{I18n.t('display.server')}: ".cyan
-		puts  item['host']
+		puts  item.host
 		print "#{I18n.t('display.protocol')}: ".cyan
-		puts  item['protocol']
+		puts  item.protocol
 		print "#{I18n.t('display.login')}: ".cyan
-		puts  item['login']
+		puts  item.user
 		print "#{I18n.t('display.password')}: ".cyan
-		puts  item['password']
+		puts  item.password
 		print "#{I18n.t('display.port')}: ".cyan
-		puts  item['port']
+		puts  item.port
 		print "#{I18n.t('display.comment')}: ".cyan
-		puts  item['comment']
+		puts  item.comment
 	end
 
 	# Form to add a new item
@@ -251,19 +251,19 @@ class Cli
 	# Update an item
 	# @args: id -> the item's id
 	def update(id)
-		row = @mpw.search_by_id(id)
+		item = @mpw.search_by_id(id)
 
 		if not row.empty?
 			puts I18n.t('form.update.title')
 			puts '--------------------'
-			name     = ask(I18n.t('form.update.name'    , name:     row['name'])).to_s
-			group    = ask(I18n.t('form.update.group'   , group:    row['group'])).to_s
-			server   = ask(I18n.t('form.update.server'  , server:   row['host'])).to_s
-			protocol = ask(I18n.t('form.update.protocol', protocol: row['protocol'])).to_s
-			login    = ask(I18n.t('form.update.login'   , login:    row['login'])).to_s
+			name     = ask(I18n.t('form.update.name'    , name:     item.name)).to_s
+			group    = ask(I18n.t('form.update.group'   , group:    item.group)).to_s
+			server   = ask(I18n.t('form.update.server'  , server:   item.host)).to_s
+			protocol = ask(I18n.t('form.update.protocol', protocol: item.protocol)).to_s
+			login    = ask(I18n.t('form.update.login'   , login:    item.user)).to_s
 			passwd   = ask(I18n.t('form.update.password')).to_s
-			port     = ask(I18n.t('form.update.port'    , port:     row['port'])).to_s
-			comment  = ask(I18n.t('form.update.comment' , comment:  row['comment'])).to_s
+			port     = ask(I18n.t('form.update.port'    , port:     item.port)).to_s
+			comment  = ask(I18n.t('form.update.comment' , comment:  item.comment)).to_s
 				
 			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
 				if @mpw.encrypt

From 7f7d8c862d0c39acbe4978469a3e7732679a55b1 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 29 Jan 2015 21:48:56 +0100
Subject: [PATCH 147/531] fix bug encrypt + export yaml

---
 lib/MPW.rb | 53 +++++++++++++++++++++++++++++------------------------
 mpw        |  2 +-
 2 files changed, 30 insertions(+), 25 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 875a8e4..e8fc983 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -32,6 +32,7 @@ module MPW
 			if File.exist?(@file_gpg)
 				crypto       = GPGME::Crypto.new(armor: true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
+
 				if not data_decrypt.to_s.empty?
 					YAML.load(data_decrypt).each_value do |d|
 						@data.push(Item.new(id:        d['id'],
@@ -64,18 +65,21 @@ module MPW
 	
 			data_to_encrypt = {}
 			@data.each do |item|
-				data_to_encrypt.merge!({'id'        => item.id,
-				                        'name'      => item.name,
-				                        'group'     => item.group,
-				                        'host'      => item.host,
-				                        'protocol'  => item.protocol,
-				                        'user'      => item.user,
-				                        'password'  => item.password,
-				                        'port'      => item.port,
-				                        'comment'   => item.comment,
-				                        'last_edit' => item.last_edit,
-				                        'created'   => item.created,
-				                      })
+				next if item.empty?
+
+				data_to_encrypt.merge!(item.id => {'id'        => item.id,
+				                                   'name'      => item.name,
+				                                   'group'     => item.group,
+				                                   'host'      => item.host,
+				                                   'protocol'  => item.protocol,
+				                                   'user'      => item.user,
+				                                   'password'  => item.password,
+				                                   'port'      => item.port,
+				                                   'comment'   => item.comment,
+				                                   'last_edit' => item.last_edit,
+				                                   'created'   => item.created,
+				                                  }
+				                      )
 			end
 	
 			recipients = []
@@ -151,18 +155,19 @@ module MPW
 			when :yaml
 				data = {}
 				@data.each do |item|
-						data.merge!({'id'        => item.id,
-						             'name'      => item.name,
-						             'group'     => item.group,
-						             'host'      => item.host,
-						             'protocol'  => item.protocol,
-						             'user'      => item.user,
-						             'password'  => item.password,
-						             'port'      => item.port,
-						             'comment'   => item.comment,
-						             'last_edit' => item.last_edit,
-						             'created'   => item.created,
-						           })
+					data.merge!(item.id => {'id'        => item.id,
+					                        'name'      => item.name,
+					                        'group'     => item.group,
+					                        'host'      => item.host,
+					                        'protocol'  => item.protocol,
+					                        'user'      => item.user,
+					                        'password'  => item.password,
+					                        'port'      => item.port,
+					                        'comment'   => item.comment,
+					                        'last_edit' => item.last_edit,
+					                        'created'   => item.created,
+					                       }
+					            )
 				end
 
 				File.open(file, 'w') {|f| f << data.to_yaml}
diff --git a/mpw b/mpw
index d18eaf5..c1791e2 100755
--- a/mpw
+++ b/mpw
@@ -82,7 +82,7 @@ OptionParser.new do |opts|
 
 	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
 		options[:export] = file
-		options[:type]   = :csv
+		options[:type]   = :yaml
 	end
 
 	opts.on('-t', '--type TYPE', I18n.t('option.type')) do |type|

From 25a84556582da7d5b914e1da1f5cd05dcf3f7d18 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 29 Jan 2015 22:09:42 +0100
Subject: [PATCH 148/531] fix import

---
 lib/MPW.rb | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index e8fc983..c90bcd3 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -191,16 +191,36 @@ module MPW
 			case type
 			when :csv
 				CSV.foreach(file, {headers: true}) do |row|
-					if not update(row['name'], row['group'], row['host'], row['protocol'], row['login'], row['password'], row['port'], row['comment'])
-						return false
-					end
+					item = Item.new(name:     row['name'], 
+					                group:    row['group'],
+					                host:     row['host'],
+					                protocol: row['protocol'],
+					                login:    row['login'],
+					                password: row['password'],
+					                port:     row['port'],
+					                comment:  row['comment'],
+					               )
+
+					return false if item.empty?
+
+					@data.push(item)
 				end
 
 			when :yaml
 				YAML::load_file(file).each_value do |row| 
-					if not update(row['name'], row['group'], row['host'], row['protocol'], row['login'], row['password'], row['port'], row['comment'])
-						return false
-					end
+					item = Item.new(name:     row['name'], 
+					                group:    row['group'],
+					                host:     row['host'],
+					                protocol: row['protocol'],
+					                login:    row['login'],
+					                password: row['password'],
+					                port:     row['port'],
+					                comment:  row['comment'],
+					               )
+
+					return false if item.empty?
+
+					@data.push(item)
 				end
 
 			else

From 57b2ca5fca858afc82ce7d4ac535460cf64f0568 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 1 Feb 2015 11:25:52 +0100
Subject: [PATCH 149/531] remove old option format

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

diff --git a/mpw b/mpw
index c1791e2..4ae1280 100755
--- a/mpw
+++ b/mpw
@@ -97,10 +97,6 @@ OptionParser.new do |opts|
 		options[:force] = true
 	end
 
-	opts.on('-F', '--format', I18n.t('option.format')) do
-		options[:format] = true
-	end
-
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
 		puts MPW::MPW::password(length)
 		exit 0

From 9921d3057ff851f2097548905abcf0796300c66e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 1 Feb 2015 11:48:02 +0100
Subject: [PATCH 150/531] fix add item

---
 lib/MPW.rb    | 18 +++++++++++++++++-
 lib/UI/Cli.rb | 22 ++++++++++++----------
 2 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index c90bcd3..9401382 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -63,8 +63,8 @@ module MPW
 		def encrypt
 			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk") if File.exist?(@file_gpg)
 	
-			data_to_encrypt = {}
 			@data.each do |item|
+				puts item.class
 				next if item.empty?
 
 				data_to_encrypt.merge!(item.id => {'id'        => item.id,
@@ -101,6 +101,22 @@ module MPW
 			return false
 		end
 		
+		# Add a new item
+		# @args: item -> Object MPW::Item
+		# @rtrn: true if add item
+		def add(item)
+			if not item.instance_of?(Item)
+				@error_msg = I18n.t('error.bad_class')
+				return false
+			elsif item.empty?
+				@error_msg = I18n.t('error.add.empty')
+				return false
+			else
+				@data.push(item)
+				return true
+			end
+		end
+
 		# Search in some csv data
 		# @args: search -> the string to search
 		#        protocol -> the connection protocol (ssh, web, other)
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 6a3e8ff..4b468b0 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -224,19 +224,21 @@ class Cli
 
 	# Form to add a new item
 	def add
-		row = []
+		options = {}
+
 		puts I18n.t('form.add.title')
 		puts '--------------------'
-		name     = ask(I18n.t('form.add.name')).to_s
-		group    = ask(I18n.t('form.add.group')).to_s
-		server   = ask(I18n.t('form.add.server')).to_s
-		protocol = ask(I18n.t('form.add.protocol')).to_s
-		login    = ask(I18n.t('form.add.login')).to_s
-		passwd   = ask(I18n.t('form.add.password')).to_s
-		port     = ask(I18n.t('form.add.port')).to_s
-		comment  = ask(I18n.t('form.add.comment')).to_s
+		options[:name]     = ask(I18n.t('form.add.name')).to_s
+		options[:group]    = ask(I18n.t('form.add.group')).to_s
+		options[:host]     = ask(I18n.t('form.add.server')).to_s
+		options[:protocol] = ask(I18n.t('form.add.protocol')).to_s
+		options[:user]     = ask(I18n.t('form.add.login')).to_s
+		options[:passwd]   = ask(I18n.t('form.add.password')).to_s
+		options[:port]     = ask(I18n.t('form.add.port')).to_s
+		options[:comment]  = ask(I18n.t('form.add.comment')).to_s
 
-		if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
+		item = MPW::Item.new(options)
+		if @mpw.add(item)
 			if @mpw.encrypt
 				sync
 				puts "#{I18n.t('form.add.valid')}".green

From 446faf704ed3d1ffe12296dbb6996d28fc9f385d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 1 Feb 2015 12:23:50 +0100
Subject: [PATCH 151/531] fix update

---
 lib/Item.rb   | 31 ++++++++++---------------------
 lib/MPW.rb    |  5 +++--
 lib/UI/Cli.rb | 30 +++++++++++++++++-------------
 3 files changed, 30 insertions(+), 36 deletions(-)

diff --git a/lib/Item.rb b/lib/Item.rb
index b6964be..ffd5f93 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -24,8 +24,7 @@ module MPW
 		attr_accessor :created
 
 		def initialize(options={})
-			if not defined?(options[:id]) or not options[:id].to_s.empty? or 
-			   not defined?(options[:created]) or not options[:created].to_s.empty?  
+			if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
 				@id      = generate_id
 				@created = Time.now.to_i
 			else
@@ -37,19 +36,19 @@ module MPW
 		end
 
 		def update(options={})
-			if defined?(options[:name]) and options[:name].to_s.empty?
+			if options.has_key?(:name) and options[:name].to_s.empty?
 				@error_msg = I18n.t('error.update.name_empty')
 				return false
 			end
 
-			@name      = options[:name]       if defined?(options[:name])
-			@group     = options[:group]      if defined?(options[:group])
-			@host      = options[:host]       if defined?(options[:host])
-			@protocol  = options[:protocol]   if defined?(options[:protocol])
-			@user      = options[:user]       if defined?(options[:user])
-			@password  = options[:password]   if defined?(options[:password])
-			@port      = options[:port].to_i  if defined?(options[:port])
-			@comment   = options[:comment]    if defined?(options[:comment])
+			@name      = options[:name]       if options.has_key?(:name)
+			@group     = options[:group]      if options.has_key?(:group)
+			@host      = options[:host]       if options.has_key?(:host)
+			@protocol  = options[:protocol]   if options.has_key?(:protocol)
+			@user      = options[:user]       if options.has_key?(:user)
+			@password  = options[:password]   if options.has_key?(:password)
+			@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
+			@comment   = options[:comment]    if options.has_key?(:comment)
 			@last_edit = Time.now.to_i
 
 			return true
@@ -63,16 +62,6 @@ module MPW
 			return false
 		end
 
-		private
-		def set_name(name)
-			if name.to_s.empty?
-				return false
-			end
-
-			@name = name
-			return true
-		end
-
 		private
 		def generate_id
 			return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
diff --git a/lib/MPW.rb b/lib/MPW.rb
index 9401382..9bb0e1a 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -40,7 +40,7 @@ module MPW
 						                    group:     d['group'],
 						                    host:      d['host'],
 						                    protocol:  d['protocol'],
-						                    user:      d['login'],
+						                    user:      d['user'],
 						                    password:  d['password'],
 						                    port:      d['port'],
 						                    comment:   d['comment'],
@@ -62,9 +62,10 @@ module MPW
 		# @rtrn: true if the file has been encrypted
 		def encrypt
 			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk") if File.exist?(@file_gpg)
+
+			data_to_encrypt = {}
 	
 			@data.each do |item|
-				puts item.class
 				next if item.empty?
 
 				data_to_encrypt.merge!(item.id => {'id'        => item.id,
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 4b468b0..8636c69 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -233,7 +233,7 @@ class Cli
 		options[:host]     = ask(I18n.t('form.add.server')).to_s
 		options[:protocol] = ask(I18n.t('form.add.protocol')).to_s
 		options[:user]     = ask(I18n.t('form.add.login')).to_s
-		options[:passwd]   = ask(I18n.t('form.add.password')).to_s
+		options[:password] = ask(I18n.t('form.add.password')).to_s
 		options[:port]     = ask(I18n.t('form.add.port')).to_s
 		options[:comment]  = ask(I18n.t('form.add.comment')).to_s
 
@@ -246,7 +246,7 @@ class Cli
 				puts "#{I18n.t('display.error')} #12: #{@mpw.error_msg}".red
 			end
 		else
-			puts "#{I18n.t('display.error')} #13: #{@mpw.error_msg}".red
+			puts "#{I18n.t('display.error')} #13: #{item.error_msg}".red
 		end
 	end
 
@@ -255,19 +255,23 @@ class Cli
 	def update(id)
 		item = @mpw.search_by_id(id)
 
-		if not row.empty?
+		if not item.nil?
+			options = {}
+
 			puts I18n.t('form.update.title')
 			puts '--------------------'
-			name     = ask(I18n.t('form.update.name'    , name:     item.name)).to_s
-			group    = ask(I18n.t('form.update.group'   , group:    item.group)).to_s
-			server   = ask(I18n.t('form.update.server'  , server:   item.host)).to_s
-			protocol = ask(I18n.t('form.update.protocol', protocol: item.protocol)).to_s
-			login    = ask(I18n.t('form.update.login'   , login:    item.user)).to_s
-			passwd   = ask(I18n.t('form.update.password')).to_s
-			port     = ask(I18n.t('form.update.port'    , port:     item.port)).to_s
-			comment  = ask(I18n.t('form.update.comment' , comment:  item.comment)).to_s
+			options[:name]     = ask(I18n.t('form.update.name'    , name:     item.name)).to_s
+			options[:group]    = ask(I18n.t('form.update.group'   , group:    item.group)).to_s
+			options[:host]     = ask(I18n.t('form.update.server'  , server:   item.host)).to_s
+			options[:protocol] = ask(I18n.t('form.update.protocol', protocol: item.protocol)).to_s
+			options[:user]     = ask(I18n.t('form.update.login'   , login:    item.user)).to_s
+			options[:password] = ask(I18n.t('form.update.password')).to_s
+			options[:port]     = ask(I18n.t('form.update.port'    , port:     item.port)).to_s
+			options[:comment]  = ask(I18n.t('form.update.comment' , comment:  item.comment)).to_s
+
+			options.delete_if { |k,v| v.empty? }
 				
-			if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
+			if item.update(options)
 				if @mpw.encrypt
 					sync
 					puts "#{I18n.t('form.update.valid')}".green
@@ -275,7 +279,7 @@ class Cli
 					puts "#{I18n.t('display.error')} #14: #{@mpw.error_msg}".red
 				end
 			else
-				puts "#{I18n.t('display.error')} #15: #{@mpw.error_msg}".red
+				puts "#{I18n.t('display.error')} #15: #{item.error_msg}".red
 			end
 		else
 			puts I18n.t('display.nothing')

From 5ceb3cfbdb2feadb7d0b067c1f924bd2689eec88 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 1 Feb 2015 12:35:42 +0100
Subject: [PATCH 152/531] fix delete item

---
 lib/Item.rb   | 13 +++++++++++++
 lib/UI/Cli.rb | 23 ++++++++++-------------
 mpw           | 20 ++++++++++----------
 3 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/lib/Item.rb b/lib/Item.rb
index ffd5f93..21312fe 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -54,6 +54,19 @@ module MPW
 			return true
 		end
 
+		def delete
+			@id        = nil
+			@name      = nil
+			@group     = nil
+			@host      = nil
+			@protocol  = nil
+			@user      = nil
+			@password  = nil
+			@port      = nil
+			@created   = nil
+			@last_edit = nil
+		end
+
 		def empty?
 			return @name.to_s.empty?
 		end
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 8636c69..c0d4f44 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -289,12 +289,12 @@ class Cli
 	# Remove an item
 	# @args: id -> the item's id
 	#        force -> no resquest a validation
-	def remove(id, force=false)
+	def delete(id, force=false)
 		if not force
-			result = @mpw.search_by_id(id)
+			item = @mpw.search_by_id(id)
 
-			if result.length > 0
-				display_item(result)
+			if not item.nil?
+				display_item(item)
 
 				confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
 				if confirm =~ /^(y|yes|YES|Yes|Y)$/
@@ -306,15 +306,13 @@ class Cli
 		end
 
 		if force
-			if @mpw.remove(id)
-				if @mpw.encrypt
-					sync
-					puts "#{I18n.t('form.delete.valid', id: id)}".green
-				else
-					puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
-				end
+			item.delete
+
+			if @mpw.encrypt
+				sync
+				puts "#{I18n.t('form.delete.valid', id: id)}".green
 			else
-				puts I18n.t('form.delete.not_valid')
+				puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
 			end
 		end
 	end
@@ -327,7 +325,6 @@ class Cli
 		else
 			puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}".red
 		end
-
 	end
 
 	# Import items from a CSV file
diff --git a/mpw b/mpw
index 4ae1280..6689ade 100755
--- a/mpw
+++ b/mpw
@@ -43,21 +43,21 @@ options[:config] = nil
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
 
-	opts.on('-d', '--display [SEARCH]', I18n.t('option.show')) do |search|
-		search.nil? ? (options[:display]  = '')  : (options[:display] = search)
+	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
+		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
 	end
 
 	opts.on('-A', '--show-all', I18n.t('option.show_all')) do
-		options[:type]    = nil
-		options[:display] = ''
+		options[:type] = nil
+		options[:show] = ''
 	end
 
 	opts.on('-u', '--update ID', I18n.t('option.update')) do |id|
 		options[:update] = id
 	end
 
-	opts.on('-r', '--remove ID', I18n.t('option.remove')) do |id|
-		options[:remove] = id
+	opts.on('-d', '--delete ID', I18n.t('option.remove')) do |id|
+		options[:delete] = id
 	end
 
 	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
@@ -128,12 +128,12 @@ cli.decrypt
 cli.sync
 
 # Display the item's informations
-if not options[:display].nil?
-	cli.display(options[:display], options[:group], options[:type])
+if not options[:show].nil?
+	cli.display(options[:show], options[:group], options[:type])
 
 # Remove an item
-elsif not options[:remove].nil?
-	cli.remove(options[:remove], options[:force])
+elsif not options[:delete].nil?
+	cli.delete(options[:delete], options[:force])
 
 # Update an item
 elsif not options[:update].nil?

From 40240934c555cbfefbe7b010be33366301207e44 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 Feb 2015 00:10:39 +0100
Subject: [PATCH 153/531] unstable: new sync class

---
 lib/MPW.rb      |  46 +-----------
 lib/Sync/SSH.rb | 191 +++++++++++++++++++++++-------------------------
 lib/UI/Cli.rb   |  51 ++-----------
 3 files changed, 98 insertions(+), 190 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 9bb0e1a..fae4e95 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -1,7 +1,6 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
 
 require 'rubygems'
 require 'gpgme'
@@ -276,50 +275,7 @@ module MPW
 			return false
 		end
 	
-		# Sync remote data and local data
-		# @args: data_remote -> array with the data remote
-		#        last_update -> last update
-		# @rtrn: false if data_remote is nil
-		def sync(data_remote, last_update)
-			if not data_remote.instance_of?(Array)
-				@error_msg = I18n.t('error.sync.array')
-				return false
-			else not data_remote.to_s.empty?
-				@data.each_value do |l|
-					j = 0
-					update = false
-		
-					# Update item
-					data_remote.each do |r|
-						if l['id'] == r['id']
-							if l['date'].to_i < r['date'].to_i
-								update(r['name'], r['group'], r['host'], r['protocol'], r['login'], r['password'], r['port'], r['comment'], l['id'])
-							end
-							update = true
-							data_remote.delete(r['id'])
-							break
-						end
-						j += 1
-					end
-		
-					# Delete an old item
-					if not update and l['date'].to_i < last_update
-						remove(l['id'])
-					end
-				end
-			end
-	
-			# Add item
-			data_remote.each do |r|
-				if r['date'].to_i > last_update
-					update(r['name'], r['group'], r['host'], r['protocol'], r['login'], r['password'], r['port'], r['comment'], r['id'])
-				end
-			end
-	
-			return encrypt
-		end
-	
-		# Generate a random password
+	# Generate a random password
 		# @args: length -> the length password
 		# @rtrn: a random string
 		def self.password(length=8)
diff --git a/lib/Sync/SSH.rb b/lib/Sync/SSH.rb
index 9ebca2d..d61531b 100644
--- a/lib/Sync/SSH.rb
+++ b/lib/Sync/SSH.rb
@@ -1,110 +1,99 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
 
-module MPW
-
-	module Sync
-
-		require 'rubygems'
-		require 'i18n'
-		require 'net/ssh'
-		require 'net/scp'
-		
-		class SSH
-		
-			attr_accessor :error_msg
-			attr_accessor :enable
-		
-			# Constructor
-			def initialize
-				@error_msg = nil
-				@enable    = false
-			end
-		
-			# Connect to server
-			# @args: host -> the server host
-			#        port -> ther connection port
-			#        gpg_key -> the gpg key
-			#        password -> the remote password
-			#        suffix -> the suffix file
-			# @rtrn: false if the connection fail
-			def connect(host, user, password, path, port=nil)
-				@host     = host
-				@user     = user
-				@password = password
-				@path     = path
-				@port     = port.instance_of?(Integer) ? 22 : port
-					
-				Net::SSH.start(@host, @user, password: @password, port: @port) do
-					@enable = true
-				end
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-				@enable    = false
-			else
-				return @enable
-			end
-		
-			# Get data on server
-			# @args: gpg_password -> the gpg password
-			# @rtrn: nil if nothing data or error
-			def get(gpg_key, gpg_password)
-				return nil if not @enable
-				
-				tmp_file = tmpfile
-				Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
-					scp.download!(@path, tmp_file)
-				end
-			
-				mpw = MPW.new(tmp_file, gpg_key)
-				if not mpw.decrypt(gpg_password)
-					@error_msg = mpw.error_msg
-					return nil
-				end
-		
-				File.unlink(tmp_file)
-				return mpw.search
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-				return nil
-			end
-		
-			# Update the remote data
-			# @args: data -> the data to send on server
-			# @rtrn: false if there is a problem
-			def update(data)
-				return true if not @enable
-		
-				tmp_file = tmpfile
-				File.open(tmp_file, "w") do |file|
-					file << data
-				end
-
-				Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
-					scp.upload!(tmp_file, @path)
-				end
-
-				File.unlink(tmp_file)
-
-				return true
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
-				return false
-			end
-
-			# Generate a random string
-			# @rtrn: a random string
-			def tmpfile
-				result = ''
-				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(6).join
-		
-				return "/tmp/mpw-#{result}"
-			end
+require 'rubygems'
+require 'i18n'
+require 'yaml'
+require 'tempfile'
+require 'net/ssh'
+require 'net/scp'
 	
+module MPW
+	class SyncSSH
+	
+		attr_accessor :error_msg
+		attr_accessor :enable
+	
+		# Constructor
+		# @args: host -> the server host
+		#        port -> ther connection port
+		#        gpg_key -> the gpg key
+		#        password -> the remote password
+		def initialize(host, user, password, path, port=nil)
+			@error_msg = nil
+			@enable    = false
+
+			@host      = host
+			@user      = user
+			@password  = password
+			@path      = path
+			@port      = port.instance_of?(Integer) ? 22 : port
+		end
+	
+		# Connect to server
+		# @rtrn: false if the connection fail
+		def connect
+			Net::SSH.start(@host, @user, password: @password, port: @port) do
+				@enable = true
+			end
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+			@enable    = false
+		else
+			return @enable
+		end
+	
+		# Get data on server
+		# @args: gpg_password -> the gpg password
+		# @rtrn: nil if nothing data or error
+		def get(gpg_key, gpg_password)
+			return nil if not @enable
+			
+			file_tmp = Tempfile.new('mpw')
+			Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
+				scp.download!(@path, file_tmp.path)
+			end
+		
+			mpw = MPW.new(file_tmp, gpg_key)
+			raise mpw.error_msg if not mpw.decrypt(gpg_password)
+	
+			file_tmp.close(true)
+
+			return mpw.list
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
+			file_tmp.close(true)
+			return nil
+		end
+	
+		# Update the remote data
+		# @args: items -> the data to send on server
+		# @rtrn: false if there is a problem
+		def update(items)
+			return true if not @enable
+	
+			file_tmp = Tempfile.new('mpw')
+
+			mpw = MPW.new(file_tmp, gpg_key)
+			items.each do |item|
+				mpw.add(item)
+			end
+
+			raise(mpw.error_msg) if not mpw.encrypt
+
+			Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
+				scp.upload!(file_tmp, @path)
+			end
+
+			file_tmp.close(true)
+
+			return true
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
+			file_tmp.close(true)
+			return false
 		end
 
 	end
-
 end
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index c0d4f44..b31f426 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -11,6 +11,7 @@ require 'i18n'
 require 'colorize'
 
 require "#{APP_ROOT}/lib/MPW"
+require "#{APP_ROOT}/lib/Sync"
 
 class Cli
 
@@ -24,52 +25,14 @@ class Cli
 	# Sync the data with the server
 	# @rtnr: true if the synchro is finish
 	def sync
-		if not defined?(@sync)
-			case @config.sync_type
-			when 'mpw'
-				require "#{APP_ROOT}/lib/Sync/MPWSync"
-				@sync = MPW::Sync::MPWSync.new
-			when 'sftp', 'scp', 'ssh'
-				require "#{APP_ROOT}/lib/Sync/SSH"
-				@sync = MPW::Sync::SSH.new
-			when 'ftp'
-				require "#{APP_ROOT}/lib/Sync/FTP"
-				@sync = MPW::Sync::FTP.new
-			else
-				return false
-			end
-		end
+		@sync = MPW::Sync.new(@config, @password, @mpw.list) 
 
-		if  not @config.sync_host.nil? and not @config.sync_port.nil?
-			if not @sync.connect(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
-				puts "#{I18n.t('display.error')} #1: #{@sync.error_msg}".red
-			end
-		end
+		raise(@sync.error_msg) if not @sync.get_remote
+		raise(@sync.error_msg) if not @sync.sync
 
-		if @sync.enable
-			if not @mpw.sync(@sync.get(@config.key, @passwd), @config.last_update)
-				puts "#{I18n.t('display.error')} #2: #{@mpw.error_msg}".red  if not @mpw.error_msg.nil?
-				puts "#{I18n.t('display.error')} #3: #{@sync.error_msg}".red if not @sync.error_msg.nil?
-
-			elsif not @sync.update(File.open(@config.file_gpg).read)
-				puts "#{I18n.t('display.error')} #4: #{@sync.error_msg}".red
-
-			elsif not @config.set_last_update
-				puts "#{I18n.t('display.error')} #5: #{@config.error_msg}".red
-
-			elsif not @mpw.encrypt
-				puts "#{I18n.t('display.error')} #6: #{@mpw.error_msg}".red
-
-			else
-				return true
-			end
-		end
+		return true
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #7: #{e}".red
-		puts @sync.error_msg   if not @sync.error_msg.nil?
-		puts @config.error_msg if not @config.error_msg.nil?
-		puts @mpw.error_msg    if not @mpw.error_msg.nil?
-	else
 		return false
 	end
 
@@ -160,8 +123,8 @@ class Cli
 			@mpw = MPW::MPW.new(@config.file_gpg, @config.key, @config.share_keys)
 		end
 
-		@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
-		if not @mpw.decrypt(@passwd)
+		@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
+		if not @mpw.decrypt(@password)
 			puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}".red
 			exit 2
 		end

From 75a8348086a8af5f006724e257d1b7b82cfcc174 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 20:56:39 +0100
Subject: [PATCH 154/531] unstable: fix sync via ssh

---
 lib/MPW.rb          |   2 +-
 lib/Sync/MPWSync.rb | 147 --------------------------------------------
 lib/Sync/SSH.rb     |  48 +++++----------
 lib/Sync/SyncMPW.rb | 132 +++++++++++++++++++++++++++++++++++++++
 lib/UI/Cli.rb       |   4 +-
 5 files changed, 151 insertions(+), 182 deletions(-)
 delete mode 100644 lib/Sync/MPWSync.rb
 create mode 100644 lib/Sync/SyncMPW.rb

diff --git a/lib/MPW.rb b/lib/MPW.rb
index fae4e95..6817755 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -28,7 +28,7 @@ module MPW
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(password=nil)
-			if File.exist?(@file_gpg)
+			if File.exist?(@file_gpg) and not File.zero?(@file_gpg)
 				crypto       = GPGME::Crypto.new(armor: true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
 
diff --git a/lib/Sync/MPWSync.rb b/lib/Sync/MPWSync.rb
deleted file mode 100644
index 9bb6572..0000000
--- a/lib/Sync/MPWSync.rb
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-module MPW
-
-	module Sync
-
-		require 'rubygems'
-		require 'i18n'
-		require 'socket'
-		require 'json'
-		require 'timeout'
-		
-		class MPWSync
-		
-			attr_accessor :error_msg
-			attr_accessor :enable
-		
-			# Constructor
-			def initialize
-				@error_msg = nil
-				@enable    = false
-			end
-		
-			# Connect to server
-			# @args: host -> the server host
-			#        port -> ther connection port
-			#        gpg_key -> the gpg key
-			#        password -> the remote password
-			#        suffix -> the suffix file
-			# @rtrn: false if the connection fail
-			def connect(host, user, password, path, port=nil)
-				@host     = host
-				@port     = !port.instance_of?(Integer) ? 2201 : port
-				@gpg_key  = user
-				@password = password
-				@suffix   = path
-		
-				Timeout.timeout(10) do
-					begin
-						TCPSocket.open(@host, @port) do 
-							@enable = true
-						end
-                    rescue Errno::ENETUNREACH
-							retry
-					end
-				end
-			rescue Timeout::Error
-				puts 'timeout'
-				@error_msg = "#{I18n.t('error.timeout')}\n#{e}"
-				@enable    = false
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-				@enable    = false
-			else
-				return @enable
-			end
-		
-			# Get data on server
-			# @args: gpg_password -> the gpg password
-			# @rtrn: nil if nothing data or error
-			def get(gpg_key, gpg_password)
-				return nil if not @enable
-			
-				msg = nil
-				TCPSocket.open(@host, @port) do |socket|
-					send_msg = {action:  'get',
-					            gpg_key:  @gpg_key,
-					            password: @password,
-					            suffix:   @suffix
-					           }
-					
-					socket.puts send_msg.to_json
-					msg = JSON.parse(socket.gets)
-				end
-				
-				if not defined?(msg['error'])
-					@error_msg = I18n.t('error.sync.communication')
-					return nil
-				elsif not msg['error'].nil?
-					@error_msg = I18n.t(msg['error'])
-					return nil
-				elsif msg['data'].nil? or msg['data'].empty?
-					return {}
-				else
-					tmp_file = tmpfile
-					File.open(tmp_file, 'w') do |file|
-						file << msg['data']
-					end
-					
-					mpw = MPW.new(tmp_file, gpg_key)
-					if not mpw.decrypt(gpg_password)
-						@error_msg = mpw.error_msg
-						return nil
-					end
-					
-					File.unlink(tmp_file)
-					return mpw.search
-				end
-			end
-		
-			# Update the remote data
-			# @args: data -> the data to send on server
-			# @rtrn: false if there is a problem
-			def update(data)
-				return true if not @enable
-		
-				msg = nil
-				TCPSocket.open(@host, @port) do |socket|
-					send_msg = {action:   'update',
-					            gpg_key:  @gpg_key,
-					            password: @password,
-					            suffix:   @suffix,
-					            data:     data
-					           }
-					
-					socket.puts send_msg.to_json
-					msg = JSON.parse(socket.gets)
-				end
-		
-				if not defined?(msg['error'])
-					@error_msg = I18n.t('error.sync.communication')
-					return false
-				elsif msg['error'].nil?
-					return true
-				else
-					@error_msg = I18n.t(msg['error'])
-					return false
-				end
-			end
-		
-			# Generate a random string
-			# @rtrn: a random string
-			def tmpfile
-				result = ''
-				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(6).join
-		
-				return "/tmp/mpw-#{result}"
-			end
-	
-		end
-
-	end
-
-end
diff --git a/lib/Sync/SSH.rb b/lib/Sync/SSH.rb
index d61531b..db721ca 100644
--- a/lib/Sync/SSH.rb
+++ b/lib/Sync/SSH.rb
@@ -4,10 +4,9 @@
 
 require 'rubygems'
 require 'i18n'
-require 'yaml'
 require 'tempfile'
 require 'net/ssh'
-require 'net/scp'
+require 'net/sftp'
 	
 module MPW
 	class SyncSSH
@@ -34,7 +33,7 @@ module MPW
 		# Connect to server
 		# @rtrn: false if the connection fail
 		def connect
-			Net::SSH.start(@host, @user, password: @password, port: @port) do
+			Net::SSH.start(@host, @user, password: @password, port: @port) do |ssh|
 				@enable = true
 			end
 		rescue Exception => e
@@ -47,51 +46,36 @@ module MPW
 		# Get data on server
 		# @args: gpg_password -> the gpg password
 		# @rtrn: nil if nothing data or error
-		def get(gpg_key, gpg_password)
-			return nil if not @enable
+		def get(file_tmp)
+			return false if not @enable
 			
-			file_tmp = Tempfile.new('mpw')
-			Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
-				scp.download!(@path, file_tmp.path)
+			puts @path
+			puts file_tmp
+			Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
+				sftp.lstat(@path) do |response|
+					sftp.download!(@path, file_tmp) if response.ok?
+				end
 			end
-		
-			mpw = MPW.new(file_tmp, gpg_key)
-			raise mpw.error_msg if not mpw.decrypt(gpg_password)
-	
-			file_tmp.close(true)
 
-			return mpw.list
+			return true
 		rescue Exception => e
 			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-			file_tmp.close(true)
-			return nil
+			return false
 		end
 	
 		# Update the remote data
-		# @args: items -> the data to send on server
+		# @args: file_gpg -> the data to send on server
 		# @rtrn: false if there is a problem
-		def update(items)
+		def update(file_gpg)
 			return true if not @enable
 	
-			file_tmp = Tempfile.new('mpw')
-
-			mpw = MPW.new(file_tmp, gpg_key)
-			items.each do |item|
-				mpw.add(item)
+			Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
+				sftp.upload!(file_gpg, @path)
 			end
 
-			raise(mpw.error_msg) if not mpw.encrypt
-
-			Net::SCP.start(@host, @user, password: @password, port: @port) do |scp|
-				scp.upload!(file_tmp, @path)
-			end
-
-			file_tmp.close(true)
-
 			return true
 		rescue Exception => e
 			@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
-			file_tmp.close(true)
 			return false
 		end
 
diff --git a/lib/Sync/SyncMPW.rb b/lib/Sync/SyncMPW.rb
new file mode 100644
index 0000000..0fc8068
--- /dev/null
+++ b/lib/Sync/SyncMPW.rb
@@ -0,0 +1,132 @@
+#!/usr/bin/ruby
+# author: nishiki
+
+require 'rubygems'
+require 'i18n'
+require 'socket'
+require 'json'
+require 'timeout'
+	
+module MPW
+	class SyncMPW
+	
+		attr_accessor :error_msg
+		attr_accessor :enable
+	
+		# Constructor
+		# @args: host -> the server host
+		#        port -> ther connection port
+		#        gpg_key -> the gpg key
+		#        password -> the remote password
+		#        suffix -> the suffix file
+		def initialize(host, user, password, suffix, port=nil)
+			@error_msg = nil
+			@enable    = false
+
+			@host     = host
+			@port     = !port.instance_of?(Integer) ? 2201 : port
+			@gpg_key  = user
+			@password = password
+			@suffix   = suffix
+		end
+	
+		# Connect to server
+		# @rtrn: false if the connection fail
+		def connect
+			Timeout.timeout(10) do
+				begin
+					TCPSocket.open(@host, @port) do 
+						@enable = true
+					end
+                rescue Errno::ENETUNREACH
+						retry
+				end
+			end
+		rescue Timeout::Error
+			@error_msg = "#{I18n.t('error.timeout')}\n#{e}"
+			@enable    = false
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+			@enable    = false
+		else
+			return @enable
+		end
+	
+		# Get data on server
+		# @args: gpg_password -> the gpg password
+		# @rtrn: nil if nothing data or error
+		def get(gpg_key, gpg_password)
+			return nil if not @enable
+		
+			msg = nil
+			TCPSocket.open(@host, @port) do |socket|
+				send_msg = {action:  'get',
+				            gpg_key:  @gpg_key,
+				            password: @password,
+				            suffix:   @suffix
+				           }
+				
+				socket.puts send_msg.to_json
+				msg = JSON.parse(socket.gets)
+			end
+			
+			if not defined?(msg['error'])
+				@error_msg = I18n.t('error.sync.communication')
+				return nil
+			elsif not msg['error'].nil?
+				@error_msg = I18n.t(msg['error'])
+				return nil
+			elsif msg['data'].nil? or msg['data'].empty?
+				return {}
+			else
+				file_tmp = Tempfile.new('mpw-')
+				File.open(file_tmp, 'w') do |file|
+					file << msg['data']
+				end
+				
+				mpw = MPW.new(file_tmp, gpg_key)
+				raise mpw.error_msg if not mpw.decrypt(gpg_password)
+				
+				file_tmp.close(true)
+
+				puts 'test'
+				return mpw.list
+			end
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
+			file_tmp.close(true)
+			return nil
+		end
+	
+		# Update the remote data
+		# @args: data -> the data to send on server
+		# @rtrn: false if there is a problem
+		def update(data)
+			return true if not @enable
+	
+			msg = nil
+			TCPSocket.open(@host, @port) do |socket|
+				send_msg = {action:   'update',
+				            gpg_key:  @gpg_key,
+				            password: @password,
+				            suffix:   @suffix,
+				            data:     data
+				           }
+				
+				socket.puts send_msg.to_json
+				msg = JSON.parse(socket.gets)
+			end
+	
+			if not defined?(msg['error'])
+				@error_msg = I18n.t('error.sync.communication')
+				return false
+			elsif msg['error'].nil?
+				return true
+			else
+				@error_msg = I18n.t(msg['error'])
+				return false
+			end
+		end
+	
+	end
+end
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index b31f426..b29ff91 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -25,13 +25,13 @@ class Cli
 	# Sync the data with the server
 	# @rtnr: true if the synchro is finish
 	def sync
-		@sync = MPW::Sync.new(@config, @password, @mpw.list) 
+		@sync = MPW::Sync.new(@config, @mpw, @password) 
 
 		raise(@sync.error_msg) if not @sync.get_remote
 		raise(@sync.error_msg) if not @sync.sync
 
 		return true
-	rescue Exception => e
+#	rescue Exception => e
 		puts "#{I18n.t('display.error')} #7: #{e}".red
 		return false
 	end

From 5da53efc0834b82b8019305776451ca14d14b423 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 21:15:06 +0100
Subject: [PATCH 155/531] fix missing file

---
 lib/Sync.rb | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 lib/Sync.rb

diff --git a/lib/Sync.rb b/lib/Sync.rb
new file mode 100644
index 0000000..5426fab
--- /dev/null
+++ b/lib/Sync.rb
@@ -0,0 +1,112 @@
+#!/usr/bin/ruby
+# author: nishiki
+# mail: nishiki@yaegashi.fr
+# info: a simple script who manage your passwords
+
+require 'rubygems'
+require 'i18n'
+require 'yaml'
+require "#{APP_ROOT}/lib/Item"
+require "#{APP_ROOT}/lib/MPW"
+	
+module MPW
+	class Sync
+
+		attr_accessor :error_msg
+
+		def initialize(config, local, password=nil)
+			@error_msg = nil
+			@config    = config
+			@local     = local
+			@password  = password
+
+			raise I18n.t('error.class') if not @local.instance_of?(MPW)
+		end
+
+		def get_remote
+			case @config.sync_type
+			when 'mpw'
+				require "#{APP_ROOT}/lib/Sync/SyncMPW"
+				@sync = SyncMPW.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
+			when 'sftp', 'scp', 'ssh'
+				require "#{APP_ROOT}/lib/Sync/SSH"
+				@sync = SyncSSH.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
+			when 'ftp'
+				require "#{APP_ROOT}/lib/Sync/FTP"
+				@sync = SyncFTP.new
+			else
+				@error_msg =  I18n.t('error.unknown_type')
+				return false
+			end
+
+			if not @sync.connect
+				@error_msg = @sync.error_msg
+				return false
+			end
+
+			
+			file_tmp = Tempfile.new('mpw-')
+			raise @sync.error_msg if not @sync.get(file_tmp.path)	
+
+			@remote = MPW.new(file_tmp.path, @config.key)
+			raise @remote.error_msg if not @remote.decrypt(@password)
+
+			file_tmp.close(true)
+			return true
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync')} #{e}"
+			file_tmp.close(true)
+			return false
+		end
+
+		# Sync remote data and local data
+		# @args: data_remote -> array with the data remote
+		#        last_update -> last update
+		# @rtrn: false if data_remote is nil
+		def sync
+			if not @remote.to_s.empty?
+				@local.list.each do |item|
+					j      = 0
+					update = false
+		
+					# Update item
+					@remote.list.each do |r|
+						if item.id == r.id
+							if item.last_edit < r.last_edit
+								raise item.error_msg if not item.update(r.name, r.group, r.host, r.protocol, r.user, r.password, r.port, r.comment)
+							end
+
+							update = true
+							data_remote.delete(j)
+
+							break
+						end
+
+						j += 1
+					end
+		
+					# Delete an old item
+					if not update and item.last_edit < @config.last_update
+						item.delete
+					end
+				end
+			end
+	
+			# Add item
+			@remote.list.each do |r|
+				if r.last_edit > @config.last_update
+					item = Item.new(r.name, r.group, r.host, r.protocol, r.user, r.password, r.port, r.comment)
+					raise @local.error_msg if not @local.add(item)
+				end
+			end
+	
+			raise @sync.error_msg if not @sync.update(@config.file_gpg)
+			raise @mpw.error_msg  if not @local.encrypt 
+
+			return true
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync')} #{e}"
+			return false
+		end
+	end
+end

From b7b56c306dac61e559f1419920234027a33be237 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 21:40:01 +0100
Subject: [PATCH 156/531] fix rescue

---
 lib/UI/Cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index b29ff91..98bcf70 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -31,7 +31,7 @@ class Cli
 		raise(@sync.error_msg) if not @sync.sync
 
 		return true
-#	rescue Exception => e
+	rescue Exception => e
 		puts "#{I18n.t('display.error')} #7: #{e}".red
 		return false
 	end

From cdf2de0fb43dc45cbee2c782fd82f53a555bc937 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 21:57:15 +0100
Subject: [PATCH 157/531] fix sync MPW

---
 lib/Sync.rb         |  1 +
 lib/Sync/SyncMPW.rb | 36 ++++++++++++++----------------------
 2 files changed, 15 insertions(+), 22 deletions(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 5426fab..b71b136 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -6,6 +6,7 @@
 require 'rubygems'
 require 'i18n'
 require 'yaml'
+require 'tempfile'
 require "#{APP_ROOT}/lib/Item"
 require "#{APP_ROOT}/lib/MPW"
 	
diff --git a/lib/Sync/SyncMPW.rb b/lib/Sync/SyncMPW.rb
index 0fc8068..596414d 100644
--- a/lib/Sync/SyncMPW.rb
+++ b/lib/Sync/SyncMPW.rb
@@ -55,8 +55,8 @@ module MPW
 		# Get data on server
 		# @args: gpg_password -> the gpg password
 		# @rtrn: nil if nothing data or error
-		def get(gpg_key, gpg_password)
-			return nil if not @enable
+		def get(file_tmp)
+			return false if not @enable
 		
 			msg = nil
 			TCPSocket.open(@host, @port) do |socket|
@@ -72,38 +72,30 @@ module MPW
 			
 			if not defined?(msg['error'])
 				@error_msg = I18n.t('error.sync.communication')
-				return nil
+				return false
 			elsif not msg['error'].nil?
 				@error_msg = I18n.t(msg['error'])
-				return nil
-			elsif msg['data'].nil? or msg['data'].empty?
-				return {}
-			else
-				file_tmp = Tempfile.new('mpw-')
-				File.open(file_tmp, 'w') do |file|
-					file << msg['data']
-				end
-				
-				mpw = MPW.new(file_tmp, gpg_key)
-				raise mpw.error_msg if not mpw.decrypt(gpg_password)
-				
-				file_tmp.close(true)
-
-				puts 'test'
-				return mpw.list
+				return false
 			end
+
+			File.open(file_tmp, 'w') do |file|
+				file << msg['data']
+			end
+
+			return true
 		rescue Exception => e
 			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-			file_tmp.close(true)
-			return nil
+			return false
 		end
 	
 		# Update the remote data
 		# @args: data -> the data to send on server
 		# @rtrn: false if there is a problem
-		def update(data)
+		def update(file_gpg)
 			return true if not @enable
 	
+			data = File.open(file_gpg, 'r').read
+
 			msg = nil
 			TCPSocket.open(@host, @port) do |socket|
 				send_msg = {action:   'update',

From 68062fe7287c154bb9d3d360718eb0ae6c3c604f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 22:06:37 +0100
Subject: [PATCH 158/531] fix FTP sync

---
 lib/Sync/FTP.rb | 172 ++++++++++++++++++++----------------------------
 lib/Sync/SSH.rb |   1 -
 2 files changed, 71 insertions(+), 102 deletions(-)

diff --git a/lib/Sync/FTP.rb b/lib/Sync/FTP.rb
index 1bb2a55..0d324e7 100644
--- a/lib/Sync/FTP.rb
+++ b/lib/Sync/FTP.rb
@@ -1,111 +1,81 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
 
+require 'rubygems'
+require 'i18n'
+require 'net/ftp'
+		
 module MPW
-
-	module Sync
-
-		require 'rubygems'
-		require 'i18n'
-		require 'net/ftp'
-		
-		class FTP
-		
-			attr_accessor :error_msg
-			attr_accessor :enable
-		
-			# Constructor
-			def initialize
-				@error_msg = nil
-				@enable    = false
-			end
-		
-			# Connect to server
-			# @args: host -> the server host
-			#        port -> ther connection port
-			#        gpg_key -> the gpg key
-			#        password -> the remote password
-			#        suffix -> the suffix file
-			# @rtrn: false if the connection fail
-			def connect(host, user, password, path, port=nil)
-				@host     = host
-				@user     = user
-				@password = password
-				@path     = path
-				@port     = port.instance_of?(Integer) ? 21 : port
-					
-				Net::FTP.open(@host) do |ftp|
-					ftp.login(@user, @password)
-					@enable = true
-				end
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-				@enable    = false
-			else
-				return @enable
-			end
-		
-			# Get data on server
-			# @args: gpg_password -> the gpg password
-			# @rtrn: nil if nothing data or error
-			def get(gpg_key, gpg_password)
-				return nil if not @enable
-				
-				tmp_file = tmpfile
-				Net::FTP.open(@host) do |ftp|
-					ftp.login(@user, @password)
-					ftp.gettextfile(@path, tmp_file)
-				end
-			
-				mpw = MPW.new(tmp_file, gpg_key)
-				if not mpw.decrypt(gpg_password)
-					@error_msg = mpw.error_msg
-					return nil
-				end
-		
-				File.unlink(tmp_file)
-				return mpw.search
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-				return nil
-			end
-		
-			# Update the remote data
-			# @args: data -> the data to send on server
-			# @rtrn: false if there is a problem
-			def update(data)
-				return true if not @enable
-		
-				tmp_file = tmpfile
-				File.open(tmp_file, "w") do |file|
-					file << data
-				end
-
-				Net::FTP.open(@host) do |ftp|
-					ftp.login(@user, @password)
-					ftp.puttextfile(tmp_file, @path)
-				end
-
-				File.unlink(tmp_file)
-				return true
-			rescue Exception => e
-				@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
-				return false
-			end
-
-			# Generate a random string
-			# @rtrn: a random string
-			def tmpfile
-				result = ''
-				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(6).join
-		
-				return "/tmp/mpw-#{result}"
-			end
+	class FTP
 	
+		attr_accessor :error_msg
+		attr_accessor :enable
+	
+		# Constructor
+		# @args: host -> the server host
+		#        port -> ther connection port
+		#        gpg_key -> the gpg key
+		#        password -> the remote password
+		#        suffix -> the suffix file
+		def initialize(host, user, password, path, port=nil)
+			@error_msg = nil
+			@enable    = false
+
+			@host     = host
+			@user     = user
+			@password = password
+			@path     = path
+			@port     = port.instance_of?(Integer) ? 21 : port
+		end
+	
+		# Connect to server
+		# @rtrn: false if the connection fail
+		def connect
+			Net::FTP.open(@host) do |ftp|
+				ftp.login(@user, @password)
+				@enable = true
+			end
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
+			@enable    = false
+		else
+			return @enable
+		end
+	
+		# Get data on server
+		# @args: gpg_password -> the gpg password
+		# @rtrn: nil if nothing data or error
+		def get(file_tmp)
+			return false if not @enable
+			
+			Net::FTP.open(@host) do |ftp|
+				ftp.login(@user, @password)
+				ftp.gettextfile(@path, file_tmp)
+			end
+		
+			return true
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
+			return false
+		end
+	
+		# Update the remote data
+		# @args: data -> the data to send on server
+		# @rtrn: false if there is a problem
+		def update(file_gpg)
+			return true if not @enable
+	
+			Net::FTP.open(@host) do |ftp|
+				ftp.login(@user, @password)
+				ftp.puttextfile(file_gpg, @path)
+			end
+
+			return true
+		rescue Exception => e
+			@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
+			return false
 		end
 
 	end
-
 end
diff --git a/lib/Sync/SSH.rb b/lib/Sync/SSH.rb
index db721ca..ddb00b5 100644
--- a/lib/Sync/SSH.rb
+++ b/lib/Sync/SSH.rb
@@ -4,7 +4,6 @@
 
 require 'rubygems'
 require 'i18n'
-require 'tempfile'
 require 'net/ssh'
 require 'net/sftp'
 	

From 59f9a5d40fdd8f322781d98ee39174ed3f1074da Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 22:43:35 +0100
Subject: [PATCH 159/531] fix sync

---
 lib/Sync.rb | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index b71b136..afad14e 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -34,7 +34,7 @@ module MPW
 				@sync = SyncSSH.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 			when 'ftp'
 				require "#{APP_ROOT}/lib/Sync/FTP"
-				@sync = SyncFTP.new
+				@sync = SyncFTP.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 			else
 				@error_msg =  I18n.t('error.unknown_type')
 				return false
@@ -74,11 +74,19 @@ module MPW
 					@remote.list.each do |r|
 						if item.id == r.id
 							if item.last_edit < r.last_edit
-								raise item.error_msg if not item.update(r.name, r.group, r.host, r.protocol, r.user, r.password, r.port, r.comment)
+								raise item.error_msg if not item.update(name:     r.name,
+								                                        group:    r.group,
+								                                        host:     r.host,
+								                                        protocol: r.protocol,
+								                                        user:     r.user,
+								                                        password: r.password,
+								                                        port:     r.port,
+								                                        comment:  r.comment
+								                                       )
 							end
 
 							update = true
-							data_remote.delete(j)
+							item.delete
 
 							break
 						end
@@ -96,7 +104,15 @@ module MPW
 			# Add item
 			@remote.list.each do |r|
 				if r.last_edit > @config.last_update
-					item = Item.new(r.name, r.group, r.host, r.protocol, r.user, r.password, r.port, r.comment)
+					item = Item.new(name:     r.name,
+					                group:    r.group,
+					                host:     r.host,
+					                protocol: r.protocol,
+					                user:     r.user,
+					                password: r.password,
+					                port:     r.port,
+					                comment:  r.comment
+					               )
 					raise @local.error_msg if not @local.add(item)
 				end
 			end

From 24949b2bde63c483672d81c7e4f56cf2ed7e1329 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 22:47:52 +0100
Subject: [PATCH 160/531] fix sync when add new item

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

diff --git a/lib/Sync.rb b/lib/Sync.rb
index afad14e..3f18f26 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -104,14 +104,16 @@ module MPW
 			# Add item
 			@remote.list.each do |r|
 				if r.last_edit > @config.last_update
-					item = Item.new(name:     r.name,
+					item = Item.new(id:       r.id,
+					                name:     r.name,
 					                group:    r.group,
 					                host:     r.host,
 					                protocol: r.protocol,
 					                user:     r.user,
 					                password: r.password,
 					                port:     r.port,
-					                comment:  r.comment
+					                comment:  r.comment,
+					                created:  r.created
 					               )
 					raise @local.error_msg if not @local.add(item)
 				end

From 4569ad4d1c3451da23f8d19e1e155f2406e5977b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 22:54:04 +0100
Subject: [PATCH 161/531] add require_relative

---
 lib/Config.rb | 13 +++++--------
 lib/MPW.rb    |  3 ++-
 lib/Sync.rb   |  5 +++--
 lib/UI/Cli.rb |  5 +++--
 mpw           |  9 +++++----
 5 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/lib/Config.rb b/lib/Config.rb
index 92abad8..7663809 100644
--- a/lib/Config.rb
+++ b/lib/Config.rb
@@ -1,15 +1,13 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
 
-module MPW
-
-	require 'rubygems'
-	require 'gpgme'
-	require 'yaml'
-	require 'i18n'
+require 'rubygems'
+require 'gpgme'
+require 'yaml'
+require 'i18n'
 	
+module MPW
 	class Config
 		
 		attr_accessor :error_msg
@@ -231,5 +229,4 @@ module MPW
 		end
 		
 	end
-
 end
diff --git a/lib/MPW.rb b/lib/MPW.rb
index 6817755..a29cff0 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -8,7 +8,8 @@ require 'csv'
 require 'i18n'
 require 'fileutils'
 require 'yaml'
-require "#{APP_ROOT}/lib/Item"
+
+require_relative './Item'
 	
 module MPW
 	class MPW
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 3f18f26..18dbad9 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -7,8 +7,9 @@ require 'rubygems'
 require 'i18n'
 require 'yaml'
 require 'tempfile'
-require "#{APP_ROOT}/lib/Item"
-require "#{APP_ROOT}/lib/MPW"
+
+require_relative './MPW'
+require_relative './Item'
 	
 module MPW
 	class Sync
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 98bcf70..eba7273 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -10,8 +10,9 @@ require 'readline'
 require 'i18n'
 require 'colorize'
 
-require "#{APP_ROOT}/lib/MPW"
-require "#{APP_ROOT}/lib/Sync"
+require_relative '../Sync'
+require_relative '../MPW'
+require_relative '../Item'
 
 class Cli
 
diff --git a/mpw b/mpw
index 6689ade..bc63313 100755
--- a/mpw
+++ b/mpw
@@ -10,10 +10,9 @@ require 'locale'
 require 'set'
 require 'i18n'
 
-APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/lib/UI/Cli"
-require "#{APP_ROOT}/lib/Config"
-require "#{APP_ROOT}/lib/MPW"
+require_relative 'lib/UI/Cli'
+require_relative 'lib/Config'
+require_relative 'lib/MPW'
 
 # --------------------------------------------------------- #
 # Set local
@@ -25,6 +24,8 @@ if defined?(I18n.enforce_available_locales)
 	I18n.enforce_available_locales = true
 end
 
+APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
+
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
 I18n.load_path      = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
 I18n.default_locale = :en

From 43abe955790521abd29a8609249e8a3eb0877f36 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 Feb 2015 22:58:18 +0100
Subject: [PATCH 162/531] update Gemfile

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

diff --git a/Gemfile b/Gemfile
index 7046265..1ccb9c5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,6 +6,5 @@ gem 'gpgme'
 gem 'colorize'
 
 group :ssh do
-	gem 'net-ssh'
-	gem 'net-scp'
+	gem 'net-sftp'
 end

From a119a954d39e59ff7a0e11baac34db245c38fc8b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 9 Feb 2015 21:39:13 +0100
Subject: [PATCH 163/531] fix bug in error message

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

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 18dbad9..984181d 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -56,7 +56,7 @@ module MPW
 			file_tmp.close(true)
 			return true
 		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync')} #{e}"
+			@error_msg = "#{I18n.t('error.sync.download')} #{e}"
 			file_tmp.close(true)
 			return false
 		end
@@ -125,7 +125,7 @@ module MPW
 
 			return true
 		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync')} #{e}"
+			@error_msg = "#{I18n.t('error.sync.unknown')} #{e}"
 			return false
 		end
 	end

From 886595f31af8b12acffeedb82a408f66c4c8bed1 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 Feb 2015 18:01:58 +0100
Subject: [PATCH 164/531] add unittest for items

---
 lib/Item.rb             |   4 +-
 test/files/fixtures.yml |  19 +++-
 test/test_item.rb       | 210 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 229 insertions(+), 4 deletions(-)
 create mode 100644 test/test_item.rb

diff --git a/lib/Item.rb b/lib/Item.rb
index 21312fe..7841d8b 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -1,7 +1,6 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
 
 require 'rubygems'
 require 'i18n'
@@ -63,8 +62,11 @@ module MPW
 			@user      = nil
 			@password  = nil
 			@port      = nil
+			@comment   = nil
 			@created   = nil
 			@last_edit = nil
+
+			return true
 		end
 
 		def empty?
diff --git a/test/files/fixtures.yml b/test/files/fixtures.yml
index 7cd5520..197e085 100644
--- a/test/files/fixtures.yml
+++ b/test/files/fixtures.yml
@@ -1,18 +1,31 @@
-add:
+add_new:
   name:	'test_name'
   group: 'test_group'
   host: 'test_host'
   protocol: 'test_protocol'
-  login: 'test_login'
+  user: 'test_login'
   password: 'test_password'
   port: '42'
   comment: 'test_comment'
+
+add_existing:
+  id: 'TEST-ID-XXXXX'
+  name:	'test_name_existing'
+  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'
+  created: 1386752948
+
 update:
   name:	'test_name_update'
   group: 'test_group_update'
   host: 'test_host_update'
   protocol: 'test_protocol_update'
-  login: 'test_login_update'
+  user: 'test_login_update'
   password: 'test_password_update'
   port: '43'
   comment: 'test_comment_update'
diff --git a/test/test_item.rb b/test/test_item.rb
new file mode 100644
index 0000000..277265d
--- /dev/null
+++ b/test/test_item.rb
@@ -0,0 +1,210 @@
+#!/usr/bin/ruby
+ 
+require_relative '../lib/Item'
+require 'test/unit'
+require 'yaml'
+ 
+class TestItem < Test::Unit::TestCase
+	def setup
+		@fixture_file = 'files/fixtures.yml'
+		@fixtures = YAML.load_file(@fixture_file)
+		
+		if defined?(I18n.enforce_available_locales)
+			I18n.enforce_available_locales = false
+		end
+
+		puts
+	end
+
+	def test_01_add_new
+		data = {name:     @fixtures['add_new']['name'],
+		        group:    @fixtures['add_new']['group'],
+		        host:     @fixtures['add_new']['host'],
+		        protocol: @fixtures['add_new']['protocol'],
+		        user:     @fixtures['add_new']['user'],
+		        password: @fixtures['add_new']['password'],
+		        port:     @fixtures['add_new']['port'],
+		        comment:  @fixtures['add_new']['comment'],
+		       }
+		
+		item = MPW::Item.new(data)
+
+		assert(!item.nil?)
+		assert(!item.empty?)
+
+		assert_equal(@fixtures['add_new']['name'],      item.name)
+		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']['password'],  item.password)
+		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
+		assert_equal(@fixtures['add_new']['comment'],   item.comment)
+	end
+
+	def test_02_add_existing
+		data = {id:       @fixtures['add_existing']['id'],
+		        name:     @fixtures['add_existing']['name'],
+		        group:    @fixtures['add_existing']['group'],
+		        host:     @fixtures['add_existing']['host'],
+		        protocol: @fixtures['add_existing']['protocol'],
+		        user:     @fixtures['add_existing']['user'],
+		        password: @fixtures['add_existing']['password'],
+		        port:     @fixtures['add_existing']['port'],
+		        comment:  @fixtures['add_existing']['comment'],
+		        created:  @fixtures['add_existing']['created'],
+		       }
+
+		item = MPW::Item.new(data)
+
+		assert(!item.nil?)
+		assert(!item.empty?)
+
+		assert_equal(@fixtures['add_existing']['id'],        item.id)
+		assert_equal(@fixtures['add_existing']['name'],      item.name)
+		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']['password'],  item.password)
+		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)
+	end
+
+	def test_03_update
+		data = {name:     @fixtures['add_new']['name'],
+		        group:    @fixtures['add_new']['group'],
+		        host:     @fixtures['add_new']['host'],
+		        protocol: @fixtures['add_new']['protocol'],
+		        user:     @fixtures['add_new']['user'],
+		        password: @fixtures['add_new']['password'],
+		        port:     @fixtures['add_new']['port'],
+		        comment:  @fixtures['add_new']['comment'],
+		       }
+		
+		item = MPW::Item.new(data)
+
+		assert(!item.nil?)
+		assert(!item.empty?)
+
+		created   = item.created
+		last_edit = item.last_edit
+
+		data = {name:     @fixtures['update']['name'],
+		        group:    @fixtures['update']['group'],
+		        host:     @fixtures['update']['host'],
+		        protocol: @fixtures['update']['protocol'],
+		        user:     @fixtures['update']['user'],
+		        password: @fixtures['update']['password'],
+		        port:     @fixtures['update']['port'],
+		        comment:  @fixtures['update']['comment'],
+		       }
+		
+		sleep(1)
+		assert(item.update(data))
+
+		assert(!item.empty?)
+
+		assert_equal(@fixtures['update']['name'],      item.name)
+		assert_equal(@fixtures['update']['group'],     item.group)
+		assert_equal(@fixtures['update']['host'],      item.host)
+		assert_equal(@fixtures['update']['protocol'],  item.protocol)
+		assert_equal(@fixtures['update']['user'],      item.user)
+		assert_equal(@fixtures['update']['password'],  item.password)
+		assert_equal(@fixtures['update']['port'].to_i, item.port)
+		assert_equal(@fixtures['update']['comment'],   item.comment)
+
+		assert_equal(created, item.created)
+		assert_not_equal(last_edit, item.last_edit)
+	end
+
+	def test_04_update_with_empty_name
+		data = {name:     @fixtures['add_new']['name'],
+		        group:    @fixtures['add_new']['group'],
+		        host:     @fixtures['add_new']['host'],
+		        protocol: @fixtures['add_new']['protocol'],
+		        user:     @fixtures['add_new']['user'],
+		        password: @fixtures['add_new']['password'],
+		        port:     @fixtures['add_new']['port'],
+		        comment:  @fixtures['add_new']['comment'],
+		       }
+		
+		item = MPW::Item.new(data)
+
+		assert(!item.nil?)
+		assert(!item.empty?)
+
+		last_edit = item.last_edit
+
+		sleep(1)
+		assert(!item.update({name: ''}))
+
+		assert_equal(last_edit, item.last_edit)
+	end
+
+	def test_05_update_one_element
+		data = {name:     @fixtures['add_new']['name'],
+		        group:    @fixtures['add_new']['group'],
+		        host:     @fixtures['add_new']['host'],
+		        protocol: @fixtures['add_new']['protocol'],
+		        user:     @fixtures['add_new']['user'],
+		        password: @fixtures['add_new']['password'],
+		        port:     @fixtures['add_new']['port'],
+		        comment:  @fixtures['add_new']['comment'],
+		       }
+		
+		item = MPW::Item.new(data)
+
+		assert(!item.nil?)
+		assert(!item.empty?)
+
+		last_edit = item.last_edit
+
+		sleep(1)
+		assert(item.update({comment: @fixtures['update']['comment']}))
+
+		assert_equal(@fixtures['add_new']['name'],      item.name)
+		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']['password'],  item.password)
+		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
+		assert_equal(@fixtures['update']['comment'],    item.comment)
+	
+		assert_not_equal(last_edit, item.last_edit)
+	end
+
+	def test_04_delete
+		data = {name:     @fixtures['add_new']['name'],
+		        group:    @fixtures['add_new']['group'],
+		        host:     @fixtures['add_new']['host'],
+		        protocol: @fixtures['add_new']['protocol'],
+		        user:     @fixtures['add_new']['user'],
+		        password: @fixtures['add_new']['password'],
+		        port:     @fixtures['add_new']['port'],
+		        comment:  @fixtures['add_new']['comment'],
+		       }
+		
+		item = MPW::Item.new(data)
+
+		assert(!item.nil?)
+		assert(!item.empty?)
+
+		assert(item.delete)
+		assert(!item.nil?)
+		assert(item.empty?)
+
+		assert_equal(nil, item.id)
+		assert_equal(nil, item.name)
+		assert_equal(nil, item.group)
+		assert_equal(nil, item.host)
+		assert_equal(nil, item.protocol)
+		assert_equal(nil, item.user)
+		assert_equal(nil, item.password)
+		assert_equal(nil, item.port)
+		assert_equal(nil, item.comment)
+		assert_equal(nil, item.created)
+	end
+end 

From 21ef7659fcaf5eebd14f55a3a107bd1eb3d0dbc5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 Feb 2015 23:02:09 +0100
Subject: [PATCH 165/531] unittest: update for item

---
 lib/Item.rb             |  5 +++++
 test/files/fixtures.yml |  4 ++--
 test/test_item.rb       | 10 +++++++++-
 3 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/lib/Item.rb b/lib/Item.rb
index 7841d8b..33ab1bc 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -23,6 +23,11 @@ module MPW
 		attr_accessor :created
 
 		def initialize(options={})
+			if not options.has_key?(:name) or options[:name].to_s.empty?
+				@error_msg = I18n.t('error.update.name_empty')
+				raise ExceptionMPW @error_msg
+			end
+
 			if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
 				@id      = generate_id
 				@created = Time.now.to_i
diff --git a/test/files/fixtures.yml b/test/files/fixtures.yml
index 197e085..73eacf7 100644
--- a/test/files/fixtures.yml
+++ b/test/files/fixtures.yml
@@ -3,7 +3,7 @@ add_new:
   group: 'test_group'
   host: 'test_host'
   protocol: 'test_protocol'
-  user: 'test_login'
+  user: 'test_user'
   password: 'test_password'
   port: '42'
   comment: 'test_comment'
@@ -25,7 +25,7 @@ update:
   group: 'test_group_update'
   host: 'test_host_update'
   protocol: 'test_protocol_update'
-  user: 'test_login_update'
+  user: 'test_user_update'
   password: 'test_password_update'
   port: '43'
   comment: 'test_comment_update'
diff --git a/test/test_item.rb b/test/test_item.rb
index 277265d..32d84f3 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -13,9 +13,17 @@ class TestItem < Test::Unit::TestCase
 			I18n.enforce_available_locales = false
 		end
 
+		I18n.load_path      = Dir['../i18n/cli/*.yml']
+		I18n.default_locale = :en
+
+
 		puts
 	end
 
+	def test_00_add_without_name
+		assert_raise {MPW::Item.new}
+	end
+
 	def test_01_add_new
 		data = {name:     @fixtures['add_new']['name'],
 		        group:    @fixtures['add_new']['group'],
@@ -176,7 +184,7 @@ class TestItem < Test::Unit::TestCase
 		assert_not_equal(last_edit, item.last_edit)
 	end
 
-	def test_04_delete
+	def test_05_delete
 		data = {name:     @fixtures['add_new']['name'],
 		        group:    @fixtures['add_new']['group'],
 		        host:     @fixtures['add_new']['host'],

From 0708c3d4c05fdf5a403d194c317c12bd557d2672 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 Feb 2015 23:03:01 +0100
Subject: [PATCH 166/531] unittest: fix for mpw

---
 lib/MPW.rb                 |   8 +-
 test/files/test_import.csv |   6 +-
 test/files/test_import.yml |   4 +-
 test/test_mpw.rb           | 300 ++++++++++++-------------------------
 4 files changed, 102 insertions(+), 216 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index a29cff0..1b1b81a 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -29,6 +29,8 @@ module MPW
 		# @args: password -> the GPG key password
 		# @rtrn: true if data has been decrypted
 		def decrypt(password=nil)
+			@data = []
+
 			if File.exist?(@file_gpg) and not File.zero?(@file_gpg)
 				crypto       = GPGME::Crypto.new(armor: true)
 				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
@@ -165,7 +167,7 @@ module MPW
 				CSV.open(file, 'w', write_headers: true,
 									headers: ['name', 'group', 'protocol', 'host', 'user', 'password', 'port', 'comment']) do |csv|
 					@data.each do |item|
-						csv << [item.id, item.group, item.protocol, item.host, item.user, item.password, item.port, item.comment]
+						csv << [item.name, item.group, item.protocol, item.host, item.user, item.password, item.port, item.comment]
 					end
 				end
 
@@ -212,7 +214,7 @@ module MPW
 					                group:    row['group'],
 					                host:     row['host'],
 					                protocol: row['protocol'],
-					                login:    row['login'],
+					                user:     row['user'],
 					                password: row['password'],
 					                port:     row['port'],
 					                comment:  row['comment'],
@@ -229,7 +231,7 @@ module MPW
 					                group:    row['group'],
 					                host:     row['host'],
 					                protocol: row['protocol'],
-					                login:    row['login'],
+					                user:     row['user'],
 					                password: row['password'],
 					                port:     row['port'],
 					                comment:  row['comment'],
diff --git a/test/files/test_import.csv b/test/files/test_import.csv
index 1452c07..74c1844 100644
--- a/test/files/test_import.csv
+++ b/test/files/test_import.csv
@@ -1,3 +1,3 @@
-name,group,protocol,host,login,password,port,comment
-test_name_csv,test_group_csv,test_protocol_csv,test_host_csv,test_login_csv,test_password_csv,44,test_comment_csv
-test_name_update_csv,test_group_update_csv,test_protocol_update_csv,test_host_update_csv,test_login_update_csv,test_password_update_csv,45,test_comment_update_csv
+name,group,protocol,host,user,password,port,comment
+test_name,test_group,test_protocol,test_host,test_user,test_password,42,test_comment
+test_name_update,test_group_update,test_protocol_update,test_host_update,test_user_update,test_password_update,43,test_comment_update
diff --git a/test/files/test_import.yml b/test/files/test_import.yml
index e7fef00..889a264 100644
--- a/test/files/test_import.yml
+++ b/test/files/test_import.yml
@@ -5,7 +5,7 @@ XWas7vpy0HerhOYd:
   group: test_group
   host: test_host
   protocol: test_protocol
-  login: test_login
+  user: test_user
   password: test_password
   port: 42
   comment: test_comment
@@ -16,7 +16,7 @@ D7URyJENLa91jt0b:
   group: test_group_update
   host: test_host_update
   protocol: test_protocol_update
-  login: test_login_update
+  user: test_user_update
   password: test_password_update
   port: 43
   comment: test_comment_update
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 94bbfa3..e8b51bf 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -1,13 +1,14 @@
 #!/usr/bin/ruby
  
 require_relative '../lib/MPW'
+require_relative '../lib/Item'
 require 'test/unit'
 require 'yaml'
 require 'csv'
  
 class TestMPW < Test::Unit::TestCase
 	def setup
-		@fixture_file = 'files/fixtures.yml'
+		fixture_file = 'files/fixtures.yml'
 
 		file_gpg = 'test.gpg'
 		key      = ENV['MPW_TEST_KEY']
@@ -19,45 +20,47 @@ class TestMPW < Test::Unit::TestCase
 		end
 
 		File.delete(file_gpg) if File.exist?(file_gpg)
-		@mpw = MPW::MPW.new(file_gpg, key)
-		@fixtures = YAML.load_file(@fixture_file)
+
+		@mpw      = MPW::MPW.new(file_gpg, key)
+		@fixtures = YAML.load_file(fixture_file)
 	end
  
  	def test_01_import_yaml
 		import_file = 'files/test_import.yml'
 
 		assert(@mpw.import(import_file, :yaml))
-		assert_equal(2, @mpw.search.length)
+		assert_equal(2, @mpw.list.length)
 
-		result = @mpw.search[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
+		item = @mpw.list[0]
+		assert_equal(@fixtures['add_new']['name'],      item.name)
+		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']['password'],  item.password)
+		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
+		assert_equal(@fixtures['add_new']['comment'],   item.comment)
 	end
 
 	def test_02_export_yaml
+		import_file = 'files/test_import.yml'
 		export_file = 'test_export.yml'
 
-		assert(@mpw.import(@fixture_file))
-		assert_equal(2, @mpw.search.length)
+		assert(@mpw.import(import_file))
+		assert_equal(2, @mpw.list.length)
 		assert(@mpw.export(export_file, :yaml))
 		export = YAML::load_file(export_file)
 		assert_equal(2, export.length)
 
 		result = export.values[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
+		assert_equal(@fixtures['add_new']['name'],      result['name'])
+		assert_equal(@fixtures['add_new']['group'],     result['group'])
+		assert_equal(@fixtures['add_new']['host'],      result['host'])
+		assert_equal(@fixtures['add_new']['protocol'],  result['protocol'])
+		assert_equal(@fixtures['add_new']['user'],      result['user'])
+		assert_equal(@fixtures['add_new']['password'],  result['password'])
+		assert_equal(@fixtures['add_new']['port'].to_i, result['port'])
+		assert_equal(@fixtures['add_new']['comment'],   result['comment'])
 
 		File.unlink(export_file)
 	end
@@ -66,155 +69,73 @@ class TestMPW < Test::Unit::TestCase
 		import_file = 'files/test_import.csv'
 
 		assert(@mpw.import(import_file, :csv))
-		assert_equal(2, @mpw.search.length)
+		assert_equal(2, @mpw.list.length)
 
 		import = CSV.parse(File.read(import_file), headers: true)
 
-		result = @mpw.search[0]
-		assert_equal(import[0]['name'],      result['name'])
-		assert_equal(import[0]['group'],     result['group'])
-		assert_equal(import[0]['host'],      result['host'])
-		assert_equal(import[0]['protocol'],  result['protocol'])
-		assert_equal(import[0]['login'],     result['login'])
-		assert_equal(import[0]['password'],  result['password'])
-		assert_equal(import[0]['port'].to_i, result['port'])
-		assert_equal(import[0]['comment'],   result['comment'])
+		item = @mpw.list[0]
+		assert_equal(import[0]['name'],      item.name)
+		assert_equal(import[0]['group'],     item.group)
+		assert_equal(import[0]['host'],      item.host)
+		assert_equal(import[0]['protocol'],  item.protocol)
+		assert_equal(import[0]['user'],      item.user)
+		assert_equal(import[0]['password'],  item.password)
+		assert_equal(import[0]['port'].to_i, item.port)
+		assert_equal(import[0]['comment'],   item.comment)
 	end
 
 	def test_04_export_csv
+		import_file = 'files/test_import.csv'
 		export_file = 'test_export.csv'
-		assert(@mpw.import(@fixture_file))
-		assert_equal(2, @mpw.search.length)
+
+		assert(@mpw.import(import_file, :csv))
+		assert_equal(2, @mpw.list.length)
 		assert(@mpw.export(export_file, :csv))
 		export = CSV.parse(File.read(export_file), headers: true)
 		assert_equal(2, export.length)
 
 		result = export[0]
-		assert_equal(@fixtures['add']['name'],     result['name'])
-		assert_equal(@fixtures['add']['group'],    result['group'])
-		assert_equal(@fixtures['add']['host'],     result['host'])
-		assert_equal(@fixtures['add']['protocol'], result['protocol'])
-		assert_equal(@fixtures['add']['login'],    result['login'])
-		assert_equal(@fixtures['add']['password'], result['password'])
-		assert_equal(@fixtures['add']['port'],     result['port'])
-		assert_equal(@fixtures['add']['comment'],  result['comment'])
+		assert_equal(@fixtures['add_new']['name'],     result['name'])
+		assert_equal(@fixtures['add_new']['group'],    result['group'])
+		assert_equal(@fixtures['add_new']['host'],     result['host'])
+		assert_equal(@fixtures['add_new']['protocol'], result['protocol'])
+		assert_equal(@fixtures['add_new']['user'],     result['user'])
+		assert_equal(@fixtures['add_new']['password'], result['password'])
+		assert_equal(@fixtures['add_new']['port'],     result['port'])
+		assert_equal(@fixtures['add_new']['comment'],  result['comment'])
 
 		File.unlink(export_file)
 	end
 
-	def test_05_add
-		assert(@mpw.update(@fixtures['add']['name'], 
-		                   @fixtures['add']['group'], 
-		                   @fixtures['add']['host'],
-		                   @fixtures['add']['protocol'],
-		                   @fixtures['add']['login'],
-		                   @fixtures['add']['password'],
-		                   @fixtures['add']['port'],
-		                   @fixtures['add']['comment']))
+	def test_05_add_item
+		data = {name:     @fixtures['add_new']['name'],
+		        group:    @fixtures['add_new']['group'],
+		        host:     @fixtures['add_new']['host'],
+		        protocol: @fixtures['add_new']['protocol'],
+		        user:     @fixtures['add_new']['user'],
+		        password: @fixtures['add_new']['password'],
+		        port:     @fixtures['add_new']['port'],
+		        comment:  @fixtures['add_new']['comment'],
+		       }
+		
+		item = MPW::Item.new(data)
 
-		assert_equal(1, @mpw.search.length)
+		assert(!item.nil?)
+		assert(!item.empty?)
 
-		result = @mpw.search[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
+		assert(@mpw.add(item))
 
-		assert(@mpw.update(@fixtures['add']['name'], 
-		                   @fixtures['add']['group'], 
-		                   @fixtures['add']['host'],
-		                   @fixtures['add']['protocol'],
-		                   @fixtures['add']['login'],
-		                   @fixtures['add']['password'],
-		                   @fixtures['add']['port'],
-		                   @fixtures['add']['comment']))
+		assert_equal(1, @mpw.list.length)
 
-
-		assert_equal(2, @mpw.search.length)
-	end
-
-	def test_06_add_empty_name
-		assert(!@mpw.update('', 
-		                    @fixtures['add']['group'], 
-		                    @fixtures['add']['host'],
-		                    @fixtures['add']['protocol'],
-		                    @fixtures['add']['login'],
-		                    @fixtures['add']['password'],
-		                    @fixtures['add']['port'],
-		                    @fixtures['add']['comment']))
-
-		assert_equal(0, @mpw.search.length)
-	end
-
-	def test_07_update_empty
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
-
-		id = @mpw.search[0]['id']
-
-		assert(@mpw.update('','', '','','','','', '', id))
-
-		result = @mpw.search_by_id(id)
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
-	end
-
-	def test_08_update
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
-
-		id = @mpw.search[0]['id']
-
-		assert(@mpw.update(@fixtures['update']['name'], 
-		                   @fixtures['update']['group'], 
-		                   @fixtures['update']['host'],
-		                   @fixtures['update']['protocol'],
-		                   @fixtures['update']['login'],
-		                   @fixtures['update']['password'],
-		                   @fixtures['update']['port'],
-		                   @fixtures['update']['comment'],
-		                   id))
-
-		assert_equal(2, @mpw.search.length)
-
-		result = @mpw.search_by_id(id)
-		assert_equal(@fixtures['update']['name'],      result['name'])
-		assert_equal(@fixtures['update']['group'],     result['group'])
-		assert_equal(@fixtures['update']['host'],      result['host'])
-		assert_equal(@fixtures['update']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['update']['login'],     result['login'])
-		assert_equal(@fixtures['update']['password'],  result['password'])
-		assert_equal(@fixtures['update']['port'].to_i, result['port'])
-		assert_equal(@fixtures['update']['comment'],   result['comment'])
-	end
-
-	def test_09_remove
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
-
-		id = @mpw.search[0]['id']
-		assert(@mpw.remove(id)) 
-
-		assert_equal(1, @mpw.search.length)
-	end
-
-	def test_10_remove_noexistent
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
-
-		assert(!@mpw.remove('TEST_NOEXISTENT_ID')) 
-
-		assert_equal(2, @mpw.search.length)
+		item = @mpw.list[0]
+		assert_equal(@fixtures['add_new']['name'],      item.name)
+		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']['password'],  item.password)
+		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
+		assert_equal(@fixtures['add_new']['comment'],   item.comment)
 	end
 
 	def test_11_encrypt_empty_file
@@ -222,75 +143,38 @@ class TestMPW < Test::Unit::TestCase
 	end
 
 	def test_12_encrypt
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
+		import_file = 'files/test_import.yml'
+
+		assert(@mpw.import(import_file, :yaml))
+		assert_equal(2, @mpw.list.length)
 
 		assert(@mpw.encrypt)	
 	end
 
 	def test_13_decrypt_empty_file
 		assert(@mpw.decrypt)
-		assert_equal(0, @mpw.search.length)
+		assert_equal(0, @mpw.list.length)
 	end
 
 	def test_14_decrypt
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
+		import_file = 'files/test_import.yml'
+
+		assert(@mpw.import(import_file, :yaml))
+		assert_equal(2, @mpw.list.length)
 
 		assert(@mpw.encrypt)	
 
 		assert(@mpw.decrypt)
-		assert_equal(2, @mpw.search.length)
+		assert_equal(2, @mpw.list.length)
 
-		result = @mpw.search[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
-	end
-
-	def test_15_sync_local_empty
-		import_file = 'files/test_import.yml'
-
-		data = []
-		YAML.load_file(import_file).each_value { |v| data.push(v) }
-
-		@mpw.sync(data, 0)
-
-		result = @mpw.search[0]
-		assert_equal(@fixtures['add']['name'],      result['name'])
-		assert_equal(@fixtures['add']['group'],     result['group'])
-		assert_equal(@fixtures['add']['host'],      result['host'])
-		assert_equal(@fixtures['add']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add']['login'],     result['login'])
-		assert_equal(@fixtures['add']['password'],  result['password'])
-		assert_equal(@fixtures['add']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add']['comment'],   result['comment'])
-
-		assert_equal(2, @mpw.search.length)
-	end
-
-	def test_16_sync_remote_outdated_and_local_empty
-		import_file = 'files/test_import.yml'
-
-		data = []
-		YAML.load_file(import_file).each_value { |v| data.push(v) }
-
-		assert(@mpw.sync(data, Time.now.to_i))
-
-		assert_equal(0, @mpw.search.length)
-	end
-
-	def test_17_sync_empty_data
-		assert(@mpw.import(@fixture_file, :yaml))
-		assert_equal(2, @mpw.search.length)
-	
-		assert(@mpw.sync([], 0))
-
-		assert_equal(2, @mpw.search.length)
+		item = @mpw.list[0]
+		assert_equal(@fixtures['add_new']['name'],      item.name)
+		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']['password'],  item.password)
+		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
+		assert_equal(@fixtures['add_new']['comment'],   item.comment)
 	end
 end

From 59c91bfb8fcab63703aa48dacb2dc0de254625d5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 Feb 2015 19:28:51 +0100
Subject: [PATCH 167/531] add comment

---
 lib/Item.rb | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/lib/Item.rb b/lib/Item.rb
index 33ab1bc..c14d0bd 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -22,10 +22,14 @@ 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 
 		def initialize(options={})
 			if not options.has_key?(:name) or options[:name].to_s.empty?
 				@error_msg = I18n.t('error.update.name_empty')
-				raise ExceptionMPW @error_msg
+				raise @error_msg
 			end
 
 			if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
@@ -39,6 +43,9 @@ module MPW
 			update(options)
 		end
 
+		# Update the item
+		# @args: options -> a hash of parameter
+		# @rtrn: true if the item is update
 		def update(options={})
 			if options.has_key?(:name) and options[:name].to_s.empty?
 				@error_msg = I18n.t('error.update.name_empty')
@@ -58,6 +65,8 @@ module MPW
 			return true
 		end
 
+		# Delete all data
+		# @rtrn: true
 		def delete
 			@id        = nil
 			@name      = nil
@@ -82,6 +91,7 @@ module MPW
 			return false
 		end
 
+		# Generate an random id
 		private
 		def generate_id
 			return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join

From bca86d4c0624c7a1df35b10a8a1e03535d70c10a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 Feb 2015 19:28:59 +0100
Subject: [PATCH 168/531] add comment

---
 lib/Sync.rb | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 984181d..35c516a 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -16,6 +16,8 @@ module MPW
 
 		attr_accessor :error_msg
 
+		# Constructor
+		# raise an exception if there is a bad parameter
 		def initialize(config, local, password=nil)
 			@error_msg = nil
 			@config    = config
@@ -25,6 +27,8 @@ module MPW
 			raise I18n.t('error.class') if not @local.instance_of?(MPW)
 		end
 
+		# Get the data on remote host
+		# @rtrn: true if get the date, else false
 		def get_remote
 			case @config.sync_type
 			when 'mpw'
@@ -62,9 +66,7 @@ module MPW
 		end
 
 		# Sync remote data and local data
-		# @args: data_remote -> array with the data remote
-		#        last_update -> last update
-		# @rtrn: false if data_remote is nil
+		# raise an exception if there is a problem
 		def sync
 			if not @remote.to_s.empty?
 				@local.list.each do |item|

From 66405d0eb911b1fa1f7e19509abe933e6543ea63 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 Feb 2015 19:31:01 +0100
Subject: [PATCH 169/531] update comment

---
 lib/MPW.rb | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 1b1b81a..8d770d3 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -121,8 +121,7 @@ module MPW
 		end
 
 		# Search in some csv data
-		# @args: search -> the string to search
-		#        protocol -> the connection protocol (ssh, web, other)
+		# @args: options -> a hash with paramaters
 		# @rtrn: a list with the resultat of the search
 		def list(options={})
 			result = []
@@ -148,7 +147,7 @@ module MPW
 	
 		# Search in some csv data
 		# @args: id -> the id item
-		# @rtrn: a row with the resultat of the search
+		# @rtrn: a row with the result of the search
 		def search_by_id(id)
 			@data.each do |item|
 				return item if item.id == id
@@ -278,7 +277,7 @@ module MPW
 			return false
 		end
 	
-	# Generate a random password
+		# Generate a random password
 		# @args: length -> the length password
 		# @rtrn: a random string
 		def self.password(length=8)

From ed2d7e624dee677b10f01d1e4723e89a4c04acfb Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 Feb 2015 23:55:14 +0100
Subject: [PATCH 170/531] fix test item

---
 test/test_item.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test_item.rb b/test/test_item.rb
index 32d84f3..923d7af 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -21,7 +21,7 @@ class TestItem < Test::Unit::TestCase
 	end
 
 	def test_00_add_without_name
-		assert_raise {MPW::Item.new}
+		assert_raise(RuntimeError){MPW::Item.new}
 	end
 
 	def test_01_add_new

From 7f45037366650d4d1c076a39155a76eea9216b6e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 Feb 2015 10:42:03 +0100
Subject: [PATCH 171/531] improve search options

---
 lib/MPW.rb    | 27 +++++++++++++++------------
 lib/UI/Cli.rb |  4 ++--
 mpw           | 11 ++++++++---
 3 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 8d770d3..c948606 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -126,20 +126,23 @@ module MPW
 		def list(options={})
 			result = []
 	
-			if not defined?(options[:search]) or options[:search].nil?
-				search = ''
-			else
-				search = options[:search].downcase
-			end
-	
+			search    = options[:search].to_s.downcase
+			group     = options[:group].to_s.downcase
+			protocol  = options[:protocol].to_s.downcase
+
 			@data.each do |item|
-				name    = item.name.nil?    ? nil : item.name.downcase
-				host    = item.host.nil?    ? nil : item.host.downcase
-				comment = item.comment.nil? ? nil : item.comment.downcase
-	
-				if name =~ /^.*#{search}.*$/ or host =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/ 
-					result.push(item)
+				next if not group.empty?    and not group.eql?(item.group.downcase)
+				next if not protocol.empty? and not protocol.eql?(item.protocol.downcase)
+				
+				name    = item.name.to_s.downcase
+				host    = item.host.to_s.downcase
+				comment = item.comment.to_s.downcase
+
+				if not name =~ /^.*#{search}.*$/ and not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/ 
+					next
 				end
+
+				result.push(item)
 			end
 	
 			return result
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index eba7273..9352ed9 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -134,8 +134,8 @@ class Cli
 	# Display the query's result
 	# @args: search -> the string to search
 	#        protocol -> search from a particular protocol
-	def display(search, protocol=nil, group=nil)
-		result = @mpw.list(search: search)
+	def display(options={})
+		result = @mpw.list(options)
 
 		case result.length
 		when 0
diff --git a/mpw b/mpw
index bc63313..ad632b1 100755
--- a/mpw
+++ b/mpw
@@ -77,8 +77,8 @@ OptionParser.new do |opts|
 		options[:setup] = true
 	end
 
-	opts.on('-p', '--protocol PROTOCOL', I18n.t('option.protocol')) do |type|
-		options[:type] = type
+	opts.on('-p', '--protocol PROTOCOL', I18n.t('option.protocol')) do |protocol|
+		options[:protocol] = protocol
 	end
 
 	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
@@ -130,7 +130,12 @@ cli.sync
 
 # Display the item's informations
 if not options[:show].nil?
-	cli.display(options[:show], options[:group], options[:type])
+	opts = {search:    options[:show],
+	        group:     options[:group],
+	        protocol:  options[:protocol],
+	       }
+
+	cli.display(opts)
 
 # Remove an item
 elsif not options[:delete].nil?

From 4cc134afb477c4b4ee63f2a1214fb1955f26e0a0 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 19 Mar 2015 22:52:33 +0100
Subject: [PATCH 172/531] fix bug in preview import

---
 lib/MPW.rb    | 45 ++++++++++++++++++++++++++++++++++++---------
 lib/UI/Cli.rb |  2 ++
 2 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index c948606..45f355e 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -126,9 +126,9 @@ module MPW
 		def list(options={})
 			result = []
 	
-			search    = options[:search].to_s.downcase
-			group     = options[:group].to_s.downcase
-			protocol  = options[:protocol].to_s.downcase
+			search   = options[:search].to_s.downcase
+			group    = options[:group].to_s.downcase
+			protocol = options[:protocol].to_s.downcase
 
 			@data.each do |item|
 				next if not group.empty?    and not group.eql?(item.group.downcase)
@@ -259,22 +259,49 @@ module MPW
 		# @args: file -> path to file import
 		# @rtrn: a hash with the items to import, if there is an error return false
 		def import_preview(file, type=:yaml)
-			result = []
+			data = []
+
 			case type
 			when :csv
 				CSV.foreach(file, {headers: true}) do |row|
-					result << row
+					item = Item.new(name:     row['name'], 
+					                group:    row['group'],
+					                host:     row['host'],
+					                protocol: row['protocol'],
+					                user:     row['user'],
+					                password: row['password'],
+					                port:     row['port'],
+					                comment:  row['comment'],
+					               )
+
+					return false if item.empty?
+
+					data.push(item)
 				end
+
 			when :yaml
-				YAML::load_file(file).each do |k, row| 
-					result << row
+				YAML::load_file(file).each_value do |row| 
+					item = Item.new(name:     row['name'], 
+					                group:    row['group'],
+					                host:     row['host'],
+					                protocol: row['protocol'],
+					                user:     row['user'],
+					                password: row['password'],
+					                port:     row['port'],
+					                comment:  row['comment'],
+					               )
+
+					return false if item.empty?
+
+					data.push(item)
 				end
+
 			else
 				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
 				return false
 			end
-
-			return result
+	
+			return data
 		rescue Exception => e 
 			@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
 			return false
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 9352ed9..35f3399 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -298,8 +298,10 @@ class Cli
 
 		if not force
 			result = @mpw.import_preview(file, type)
+			puts result
 			if result.is_a?(Array) and not result.empty?
 				result.each do |r|
+					puts r.class
 					display_item(r)
 				end
 

From b5230f2cfe437ef6878dc6849d4da3e34c0b874b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 19 Mar 2015 23:03:15 +0100
Subject: [PATCH 173/531] fix translation

---
 i18n/cli/en.yml | 1 +
 i18n/cli/fr.yml | 1 +
 2 files changed, 2 insertions(+)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index b8c1bd7..dfa5835 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -30,6 +30,7 @@ en:
       not_authorized: "You haven't the access to remote file!"
       upload: "Can't upload the file on the server!"
       unknown: "An unknown error is occured!" 
+    unknown_type: "The sync type is unknown" 
 
   warning:
     select: 'Your choice is not a valid element!'
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index ea9b9f0..b354e85 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -30,6 +30,7 @@ fr:
       not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
       upload: "Impossible d'envoyer le fichier sur le serveur!"
       unknown: "Une erreur inconnue est survenue!" 
+    unknown_type: "Le type de synchronisation est inconnu" 
 
   warning:
     select: "Votre choix n'est pas un élément valide!"

From 724fc1752af4661f978a0194fd03d6942bef8020 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 19 Mar 2015 23:17:01 +0100
Subject: [PATCH 174/531] lib/Sync.rb

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

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 35c516a..e054330 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -121,9 +121,9 @@ module MPW
 					raise @local.error_msg if not @local.add(item)
 				end
 			end
-	
-			raise @sync.error_msg if not @sync.update(@config.file_gpg)
+
 			raise @mpw.error_msg  if not @local.encrypt 
+			raise @sync.error_msg if not @sync.update(@config.file_gpg)
 
 			return true
 		rescue Exception => e

From eb040310c34fb8a833c4ab29da75b807877ef55d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 19 Mar 2015 23:17:40 +0100
Subject: [PATCH 175/531] update translation

---
 i18n/cli/en.yml | 2 ++
 i18n/cli/fr.yml | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index dfa5835..6ba6c08 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -1,6 +1,8 @@
 ---
 en:
   error:
+    client:
+      no_authorized: "You aren't authorized."
     config:
       write: "Can't write the config file!"
       check: "Checkconfig failed!"
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index b354e85..1410993 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -1,6 +1,8 @@
 ---
 fr:
   error:
+    client:
+      no_authorized: "Vous n'êtes pas autorisé!"
     config:
       write: "Impossible d'écrire le fichier de configuration!"
       check: "Le fichier de configuration est invalide!"

From ddbc20f635d5019fb9c619654c43a1c60a5d87b3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 22 Mar 2015 22:38:35 +0100
Subject: [PATCH 176/531] no return empty result

---
 lib/MPW.rb | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lib/MPW.rb b/lib/MPW.rb
index 45f355e..0677af9 100644
--- a/lib/MPW.rb
+++ b/lib/MPW.rb
@@ -131,6 +131,8 @@ module MPW
 			protocol = options[:protocol].to_s.downcase
 
 			@data.each do |item|
+				next if item.empty?
+
 				next if not group.empty?    and not group.eql?(item.group.downcase)
 				next if not protocol.empty? and not protocol.eql?(item.protocol.downcase)
 				

From 465b60fd41138721096d600009fdccd29834ac60 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 22 Mar 2015 22:43:18 +0100
Subject: [PATCH 177/531] fix multiple bug in sync

---
 lib/Item.rb |  8 +++++---
 lib/Sync.rb | 30 ++++++++++++++++--------------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/lib/Item.rb b/lib/Item.rb
index c14d0bd..ca4555e 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -36,8 +36,10 @@ module MPW
 				@id      = generate_id
 				@created = Time.now.to_i
 			else
-				@id      = options[:id]
-				@created = options[:created]
+				@id        = options[:id]
+				@created   = options[:created]
+				@last_edit = options[:last_edit]
+				options[:no_update_last_edit] = true
 			end
 
 			update(options)
@@ -60,7 +62,7 @@ module MPW
 			@password  = options[:password]   if options.has_key?(:password)
 			@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
 			@comment   = options[:comment]    if options.has_key?(:comment)
-			@last_edit = Time.now.to_i
+			@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
 
 			return true
 		end
diff --git a/lib/Sync.rb b/lib/Sync.rb
index e054330..beb7100 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -70,7 +70,6 @@ module MPW
 		def sync
 			if not @remote.to_s.empty?
 				@local.list.each do |item|
-					j      = 0
 					update = false
 		
 					# Update item
@@ -89,12 +88,10 @@ module MPW
 							end
 
 							update = true
-							item.delete
+							r.delete
 
 							break
 						end
-
-						j += 1
 					end
 		
 					# Delete an old item
@@ -106,24 +103,29 @@ module MPW
 	
 			# Add item
 			@remote.list.each do |r|
+				puts r.last_edit
 				if r.last_edit > @config.last_update
-					item = Item.new(id:       r.id,
-					                name:     r.name,
-					                group:    r.group,
-					                host:     r.host,
-					                protocol: r.protocol,
-					                user:     r.user,
-					                password: r.password,
-					                port:     r.port,
-					                comment:  r.comment,
-					                created:  r.created
+					item = Item.new(id:        r.id,
+					                name:      r.name,
+					                group:     r.group,
+					                host:      r.host,
+					                protocol:  r.protocol,
+					                user:      r.user,
+					                password:  r.password,
+					                port:      r.port,
+					                comment:   r.comment,
+					                created:   r.created
+					                last_edit: r.last_edit
 					               )
+					puts item.last_edit
 					raise @local.error_msg if not @local.add(item)
 				end
 			end
 
 			raise @mpw.error_msg  if not @local.encrypt 
 			raise @sync.error_msg if not @sync.update(@config.file_gpg)
+			
+			@config.set_last_update
 
 			return true
 		rescue Exception => e

From 9225e360b9d65de903790020716ec45086befbc3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 22 Mar 2015 22:45:43 +0100
Subject: [PATCH 178/531] fix syntax error

---
 lib/Sync.rb | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index beb7100..2c6db1b 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -103,7 +103,6 @@ module MPW
 	
 			# Add item
 			@remote.list.each do |r|
-				puts r.last_edit
 				if r.last_edit > @config.last_update
 					item = Item.new(id:        r.id,
 					                name:      r.name,
@@ -114,7 +113,7 @@ module MPW
 					                password:  r.password,
 					                port:      r.port,
 					                comment:   r.comment,
-					                created:   r.created
+					                created:   r.created,
 					                last_edit: r.last_edit
 					               )
 					puts item.last_edit

From 7a222798c5b0ee7cb02199ff88965b9a363f8695 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 19:15:33 +0200
Subject: [PATCH 179/531] fix tests

---
 test/test_mpw.rb | 11 +++++++++++
 test/tests.rb    |  4 ++++
 2 files changed, 15 insertions(+)
 create mode 100644 test/tests.rb

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index e8b51bf..899b366 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -177,4 +177,15 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
 		assert_equal(@fixtures['add_new']['comment'],   item.comment)
 	end
+
+	def test_15_search
+		import_file = 'files/test_import.yml'
+
+		assert(@mpw.import(import_file, :yaml))
+		assert_equal(2, @mpw.list.length)
+
+		assert_equal(1, @mpw.list(group:    @fixtures['add_new']['group']).length)
+		assert_equal(1, @mpw.list(protocol: @fixtures['add_new']['protocol']).length)
+		assert_equal(2, @mpw.list(search:   @fixtures['add_new']['name'][0..-2]).length)
+	end
 end
diff --git a/test/tests.rb b/test/tests.rb
new file mode 100644
index 0000000..d3735f5
--- /dev/null
+++ b/test/tests.rb
@@ -0,0 +1,4 @@
+#!/usr/bin/ruby
+ 
+require_relative 'test_mpw.rb'
+require_relative 'test_item.rb'

From 2d7dacb02c88c866726a19524b089efff1b87ec8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 19:17:12 +0200
Subject: [PATCH 180/531] fix bug sync when a device add an iten whitout sync

---
 lib/Config.rb | 30 +++++++++++++++---------------
 lib/Item.rb   |  7 +++++++
 lib/Sync.rb   | 37 +++++++++++++++++++------------------
 3 files changed, 41 insertions(+), 33 deletions(-)

diff --git a/lib/Config.rb b/lib/Config.rb
index 7663809..698efe1 100644
--- a/lib/Config.rb
+++ b/lib/Config.rb
@@ -23,7 +23,7 @@ module MPW
 		attr_accessor :sync_user
 		attr_accessor :sync_pwd
 		attr_accessor :sync_path
-		attr_accessor :last_update
+		attr_accessor :last_sync
 		attr_accessor :dir_config
 	
 		# Constructor
@@ -82,7 +82,7 @@ module MPW
 			                       'sync_user'   => sync_user,
 			                       'sync_pwd'    => sync_pwd,
 			                       'sync_path'   => sync_path,
-			                       'last_update' => 0 
+			                       'last_sync' => 0 
 			                      }
 			         }
 	
@@ -139,17 +139,17 @@ module MPW
 		# @rtrn: true if the config file is correct
 		def checkconfig
 			config = YAML::load_file(@file_config)
-			@key         = config['config']['key']
-			@share_keys  = config['config']['share_keys']
-			@lang        = config['config']['lang']
-			@file_gpg    = config['config']['file_gpg']
-			@sync_type   = config['config']['sync_type']
-			@sync_host   = config['config']['sync_host']
-			@sync_port   = config['config']['sync_port']
-			@sync_user   = config['config']['sync_user']
-			@sync_pwd    = config['config']['sync_pwd']
-			@sync_path   = config['config']['sync_path']
-			@last_update = config['config']['last_update'].to_i
+			@key        = config['config']['key']
+			@share_keys = config['config']['share_keys']
+			@lang       = config['config']['lang']
+			@file_gpg   = config['config']['file_gpg']
+			@sync_type  = config['config']['sync_type']
+			@sync_host  = config['config']['sync_host']
+			@sync_port  = config['config']['sync_port']
+			@sync_user  = config['config']['sync_user']
+			@sync_pwd   = config['config']['sync_pwd']
+			@sync_path  = config['config']['sync_path']
+			@last_sync  = config['config']['last_sync'].to_i
 
 			if @key.empty? or @file_gpg.empty? 
 				@error_msg = I18n.t('error.config.check')
@@ -203,7 +203,7 @@ module MPW
 	
 		# Set the last update when there is a sync
 		# @rtrn: true is the file has been updated
-		def set_last_update
+		def set_last_sync
 			config = {'config' => {'key'         => @key,
 			                       'share_keys'  => @share_keys,
 			                       'lang'        => @lang,
@@ -214,7 +214,7 @@ module MPW
 			                       'sync_user'   => @sync_user,
 			                       'sync_pwd'    => @sync_pwd,
 			                       'sync_path'   => @sync_path,
-			                       'last_update' => Time.now.to_i
+			                       'last_sync' => Time.now.to_i
 			                      }
 		           }
 	
diff --git a/lib/Item.rb b/lib/Item.rb
index ca4555e..666d5b3 100644
--- a/lib/Item.rb
+++ b/lib/Item.rb
@@ -20,6 +20,7 @@ module MPW
 		attr_accessor :port
 		attr_accessor :comment
 		attr_accessor :last_edit
+		attr_accessor :last_sync
 		attr_accessor :created
 
 		# Constructor
@@ -67,6 +68,11 @@ module MPW
 			return true
 		end
 
+		# Update last_sync
+		def set_last_sync
+			@last_sync = Time.now.to_i
+		end
+
 		# Delete all data
 		# @rtrn: true
 		def delete
@@ -81,6 +87,7 @@ module MPW
 			@comment   = nil
 			@created   = nil
 			@last_edit = nil
+			@last_sync = nil
 
 			return true
 		end
diff --git a/lib/Sync.rb b/lib/Sync.rb
index 2c6db1b..ce98596 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -68,34 +68,32 @@ module MPW
 		# Sync remote data and local data
 		# raise an exception if there is a problem
 		def sync
+			
 			if not @remote.to_s.empty?
 				@local.list.each do |item|
-					update = false
-		
-					# Update item
 					@remote.list.each do |r|
+
+						# Update item
 						if item.id == r.id
 							if item.last_edit < r.last_edit
-								raise item.error_msg if not item.update(name:     r.name,
-								                                        group:    r.group,
-								                                        host:     r.host,
-								                                        protocol: r.protocol,
-								                                        user:     r.user,
-								                                        password: r.password,
-								                                        port:     r.port,
-								                                        comment:  r.comment
+								raise item.error_msg if not item.update(name:      r.name,
+								                                        group:     r.group,
+								                                        host:      r.host,
+								                                        protocol:  r.protocol,
+								                                        user:      r.user,
+								                                        password:  r.password,
+								                                        port:      r.port,
+								                                        comment:   r.comment
 								                                       )
 							end
 
-							update = true
 							r.delete
-
 							break
 						end
 					end
-		
-					# Delete an old item
-					if not update and item.last_edit < @config.last_update
+
+					# Remove an old item
+					if item.last_sync < @config.last_sync
 						item.delete
 					end
 				end
@@ -116,15 +114,18 @@ module MPW
 					                created:   r.created,
 					                last_edit: r.last_edit
 					               )
-					puts item.last_edit
 					raise @local.error_msg if not @local.add(item)
 				end
 			end
 
+			@local.list.each do |item|
+				item.set_last_sync
+			end
+
 			raise @mpw.error_msg  if not @local.encrypt 
 			raise @sync.error_msg if not @sync.update(@config.file_gpg)
 			
-			@config.set_last_update
+			@config.set_last_sync
 
 			return true
 		rescue Exception => e

From b9ebe9de3f7f03429c30393f9f578d6975e459c4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 19:23:59 +0200
Subject: [PATCH 181/531] fix bug

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

diff --git a/lib/Sync.rb b/lib/Sync.rb
index ce98596..792de3d 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -93,7 +93,7 @@ module MPW
 					end
 
 					# Remove an old item
-					if item.last_sync < @config.last_sync
+					if item.last_sync.to_i < @config.last_sync
 						item.delete
 					end
 				end
@@ -128,7 +128,7 @@ module MPW
 			@config.set_last_sync
 
 			return true
-		rescue Exception => e
+#		rescue Exception => e
 			@error_msg = "#{I18n.t('error.sync.unknown')} #{e}"
 			return false
 		end

From 9bd5024fef459c6d984ddbf90c46b3f9eb43ddf8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 19:25:25 +0200
Subject: [PATCH 182/531] fix bug~

---
 lib/Sync.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 792de3d..4bff9f0 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -101,7 +101,7 @@ module MPW
 	
 			# Add item
 			@remote.list.each do |r|
-				if r.last_edit > @config.last_update
+				if r.last_edit > @config.last_sync
 					item = Item.new(id:        r.id,
 					                name:      r.name,
 					                group:     r.group,

From 3da68967f977de56317a42cb06860180364ec5ea Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 19:30:17 +0200
Subject: [PATCH 183/531] fix import default yaml

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

diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 35f3399..7c5fc74 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -32,7 +32,7 @@ class Cli
 		raise(@sync.error_msg) if not @sync.sync
 
 		return true
-	rescue Exception => e
+#	rescue Exception => e
 		puts "#{I18n.t('display.error')} #7: #{e}".red
 		return false
 	end
@@ -298,10 +298,8 @@ class Cli
 
 		if not force
 			result = @mpw.import_preview(file, type)
-			puts result
 			if result.is_a?(Array) and not result.empty?
 				result.each do |r|
-					puts r.class
 					display_item(r)
 				end
 
diff --git a/mpw b/mpw
index ad632b1..bda314c 100755
--- a/mpw
+++ b/mpw
@@ -92,6 +92,7 @@ OptionParser.new do |opts|
 
 	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
 		options[:import] = file
+		options[:type]   = :yaml
 	end
 
 	opts.on('-f', '--force', I18n.t('option.force')) do

From 59395ce7db68ddeb648edf516ecacbd1b69b1b87 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 19:31:51 +0200
Subject: [PATCH 184/531] fix bug in sync

---
 lib/Sync.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 4bff9f0..bfb4d88 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -93,7 +93,7 @@ module MPW
 					end
 
 					# Remove an old item
-					if item.last_sync.to_i < @config.last_sync
+					if item.last_sync.to_i < @config.last_sync and item.last_edit < @config.last_sync
 						item.delete
 					end
 				end

From 6e9b4a50875bb08bc0cc7e9a1393ec90d174edd1 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 20:12:23 +0200
Subject: [PATCH 185/531] fix bug in sync

---
 lib/Sync.rb | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index bfb4d88..051a0c2 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -71,6 +71,7 @@ module MPW
 			
 			if not @remote.to_s.empty?
 				@local.list.each do |item|
+					update = true
 					@remote.list.each do |r|
 
 						# Update item
@@ -88,12 +89,14 @@ module MPW
 							end
 
 							r.delete
+							update = true
+
 							break
 						end
 					end
 
 					# Remove an old item
-					if item.last_sync.to_i < @config.last_sync and item.last_edit < @config.last_sync
+					if not update and item.last_sync.to_i < @config.last_sync and item.last_edit < @config.last_sync
 						item.delete
 					end
 				end

From 1f4509597baf74e4f994e44ee8e145313cd75f18 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 20:20:18 +0200
Subject: [PATCH 186/531] remove test

---
 lib/Sync.rb   | 2 +-
 lib/UI/Cli.rb | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 051a0c2..78fcce8 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -131,7 +131,7 @@ module MPW
 			@config.set_last_sync
 
 			return true
-#		rescue Exception => e
+		rescue Exception => e
 			@error_msg = "#{I18n.t('error.sync.unknown')} #{e}"
 			return false
 		end
diff --git a/lib/UI/Cli.rb b/lib/UI/Cli.rb
index 7c5fc74..9352ed9 100644
--- a/lib/UI/Cli.rb
+++ b/lib/UI/Cli.rb
@@ -32,7 +32,7 @@ class Cli
 		raise(@sync.error_msg) if not @sync.sync
 
 		return true
-#	rescue Exception => e
+	rescue Exception => e
 		puts "#{I18n.t('display.error')} #7: #{e}".red
 		return false
 	end

From e22ebecf36ef3c8f52311ffac2960f780c8126b9 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 20:26:58 +0200
Subject: [PATCH 187/531] remove puts

---
 lib/Config.rb   | 1 -
 lib/Sync/SSH.rb | 2 --
 2 files changed, 3 deletions(-)

diff --git a/lib/Config.rb b/lib/Config.rb
index 698efe1..d2b02a9 100644
--- a/lib/Config.rb
+++ b/lib/Config.rb
@@ -159,7 +159,6 @@ module MPW
 
 			return true
 		rescue Exception => e 
-			puts e
 			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
 			return false
 		end
diff --git a/lib/Sync/SSH.rb b/lib/Sync/SSH.rb
index ddb00b5..632e292 100644
--- a/lib/Sync/SSH.rb
+++ b/lib/Sync/SSH.rb
@@ -48,8 +48,6 @@ module MPW
 		def get(file_tmp)
 			return false if not @enable
 			
-			puts @path
-			puts file_tmp
 			Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
 				sftp.lstat(@path) do |response|
 					sftp.download!(@path, file_tmp) if response.ok?

From 107e9047dd946c1fd03905398c6af1e13197b95b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 May 2015 21:24:21 +0200
Subject: [PATCH 188/531] fix bug

---
 lib/Sync.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Sync.rb b/lib/Sync.rb
index 78fcce8..22a289e 100644
--- a/lib/Sync.rb
+++ b/lib/Sync.rb
@@ -71,7 +71,7 @@ module MPW
 			
 			if not @remote.to_s.empty?
 				@local.list.each do |item|
-					update = true
+					update = false
 					@remote.list.each do |r|
 
 						# Update item

From ccd188bc2f5c6aa6c5df59efae7563e27e3bdfc2 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 21 Jun 2015 23:45:25 +0200
Subject: [PATCH 189/531] add install with gem

---
 CHANGELOG                                |  6 ++++++
 Makefile                                 | 23 -----------------------
 VERSION                                  |  2 +-
 mpw => bin/mpw                           |  9 ++++-----
 mpw-server => bin/mpw-server             |  4 ++--
 mpw-ssh => bin/mpw-ssh                   | 13 ++++++-------
 lib/{Config.rb => mpw/config.rb}         |  0
 lib/{Item.rb => mpw/item.rb}             |  0
 lib/{MPW.rb => mpw/mpw.rb}               |  3 +--
 lib/{Server.rb => mpw/server.rb}         |  0
 lib/{Sync.rb => mpw/sync.rb}             | 11 +++++------
 lib/{Sync/FTP.rb => mpw/sync/ftp.rb}     |  0
 lib/{Sync/SyncMPW.rb => mpw/sync/mpw.rb} |  0
 lib/{Sync/SSH.rb => mpw/sync/ssh.rb}     |  0
 lib/{UI/Cli.rb => mpw/ui/cli.rb}         |  7 +++----
 lib/{UI/CliSSH.rb => mpw/ui/clissh.rb}   |  2 +-
 mpw.gemspec                              | 19 +++++++++++++++++++
 17 files changed, 48 insertions(+), 51 deletions(-)
 delete mode 100644 Makefile
 rename mpw => bin/mpw (96%)
 rename mpw-server => bin/mpw-server (95%)
 rename mpw-ssh => bin/mpw-ssh (94%)
 rename lib/{Config.rb => mpw/config.rb} (100%)
 rename lib/{Item.rb => mpw/item.rb} (100%)
 rename lib/{MPW.rb => mpw/mpw.rb} (99%)
 rename lib/{Server.rb => mpw/server.rb} (100%)
 rename lib/{Sync.rb => mpw/sync.rb} (95%)
 rename lib/{Sync/FTP.rb => mpw/sync/ftp.rb} (100%)
 rename lib/{Sync/SyncMPW.rb => mpw/sync/mpw.rb} (100%)
 rename lib/{Sync/SSH.rb => mpw/sync/ssh.rb} (100%)
 rename lib/{UI/Cli.rb => mpw/ui/cli.rb} (99%)
 rename lib/{UI/CliSSH.rb => mpw/ui/clissh.rb} (96%)
 create mode 100644 mpw.gemspec

diff --git a/CHANGELOG b/CHANGELOG
index 21cf7ae..1ce4809 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,10 @@
 = CHANGELOG =
+== v2.0.0 ==
+
+* change format csv to yaml
+* easy install with gem
+* add sync with ftp and ssh
+* many improvement
 
 == v1.1.0 ==
 
diff --git a/Makefile b/Makefile
deleted file mode 100644
index cb1fa7a..0000000
--- a/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-all:
-	$(info 'Nothing todo!')
-	$(info 'Use make install or make uninstall')
-
-dep-ubuntu:
-	apt-get install ruby ruby-gpgme ruby-highline ruby-i18n ruby-locale
-
-install:
-	mkdir -p /usr/local/mpw
-	cp -r ./lib /usr/local/mpw/
-	cp -r ./i18n /usr/local/mpw/
-	cp ./mpw /usr/local/mpw/
-	ln -snf /usr/local/mpw/mpw /usr/local/bin/
-	cp ./mpw-server /usr/local/mpw/
-	ln -snf /usr/local/mpw/mpw-server /usr/local/bin/mpw-server
-	cp ./mpw-ssh /usr/local/mpw/
-	ln -snf /usr/local/mpw/mpw-ssh /usr/local/bin/mpw-ssh
-
-uninstall:
-	rm /usr/local/bin/mpw-server
-	rm /usr/local/bin/mpw
-	rm /usr/local/bin/mpw-ssh
-	rm -rf /usr/local/mpw
diff --git a/VERSION b/VERSION
index 5d32997..227cea2 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.0 - dev
+2.0.0
diff --git a/mpw b/bin/mpw
similarity index 96%
rename from mpw
rename to bin/mpw
index bda314c..d8a76c7 100755
--- a/mpw
+++ b/bin/mpw
@@ -9,10 +9,9 @@ require 'pathname'
 require 'locale'
 require 'set'
 require 'i18n'
-
-require_relative 'lib/UI/Cli'
-require_relative 'lib/Config'
-require_relative 'lib/MPW'
+require 'mpw/mpw'
+require 'mpw/config'
+require 'mpw/ui/cli'
 
 # --------------------------------------------------------- #
 # Set local
@@ -27,7 +26,7 @@ end
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
+I18n.load_path      = Dir["#{APP_ROOT}/../i18n/cli/*.yml"]
 I18n.default_locale = :en
 I18n.locale         = lang.to_sym
 
diff --git a/mpw-server b/bin/mpw-server
similarity index 95%
rename from mpw-server
rename to bin/mpw-server
index 30f6105..3a7ce5a 100755
--- a/mpw-server
+++ b/bin/mpw-server
@@ -8,9 +8,9 @@ require 'optparse'
 require 'pathname'
 require 'locale'
 require 'i18n'
+require 'mpw/server'
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/lib/Server.rb"
 
 # --------------------------------------------------------- #
 # Set local
@@ -23,7 +23,7 @@ if defined?(I18n.enforce_available_locales)
 end
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/i18n/server/*.yml"]
+I18n.load_path = Dir["#{APP_ROOT}/../i18n/server/*.yml"]
 I18n.default_locale = :en
 I18n.locale = lang.to_sym
 
diff --git a/mpw-ssh b/bin/mpw-ssh
similarity index 94%
rename from mpw-ssh
rename to bin/mpw-ssh
index 89a8c99..b5f1252 100755
--- a/mpw-ssh
+++ b/bin/mpw-ssh
@@ -8,23 +8,22 @@ require 'optparse'
 require 'pathname'
 require 'locale'
 require 'i18n'
-
-APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-require "#{APP_ROOT}/lib/UI/CliSSH"
-require "#{APP_ROOT}/lib/Config"
-
-lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+require 'mpw/ui/clissh'
+require 'mpw/config'
 
 # --------------------------------------------------------- #
 # Set local
 # --------------------------------------------------------- #
 
+APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
+lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+
 if defined?(I18n.enforce_available_locales)
 	I18n.enforce_available_locales = true
 end
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{APP_ROOT}/i18n/cli/*.yml"]
+I18n.load_path      = Dir["#{APP_ROOT}/../i18n/cli/*.yml"]
 I18n.default_locale = :en
 I18n.locale         = lang.to_sym
 
diff --git a/lib/Config.rb b/lib/mpw/config.rb
similarity index 100%
rename from lib/Config.rb
rename to lib/mpw/config.rb
diff --git a/lib/Item.rb b/lib/mpw/item.rb
similarity index 100%
rename from lib/Item.rb
rename to lib/mpw/item.rb
diff --git a/lib/MPW.rb b/lib/mpw/mpw.rb
similarity index 99%
rename from lib/MPW.rb
rename to lib/mpw/mpw.rb
index 0677af9..57a6bce 100644
--- a/lib/MPW.rb
+++ b/lib/mpw/mpw.rb
@@ -8,8 +8,7 @@ require 'csv'
 require 'i18n'
 require 'fileutils'
 require 'yaml'
-
-require_relative './Item'
+require 'mpw/item'
 	
 module MPW
 	class MPW
diff --git a/lib/Server.rb b/lib/mpw/server.rb
similarity index 100%
rename from lib/Server.rb
rename to lib/mpw/server.rb
diff --git a/lib/Sync.rb b/lib/mpw/sync.rb
similarity index 95%
rename from lib/Sync.rb
rename to lib/mpw/sync.rb
index 22a289e..3e18d32 100644
--- a/lib/Sync.rb
+++ b/lib/mpw/sync.rb
@@ -7,9 +7,8 @@ require 'rubygems'
 require 'i18n'
 require 'yaml'
 require 'tempfile'
-
-require_relative './MPW'
-require_relative './Item'
+require 'mpw/mpw'
+require 'mpw/item'
 	
 module MPW
 	class Sync
@@ -32,13 +31,13 @@ module MPW
 		def get_remote
 			case @config.sync_type
 			when 'mpw'
-				require "#{APP_ROOT}/lib/Sync/SyncMPW"
+				require 'mpw/sync/mpw'
 				@sync = SyncMPW.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 			when 'sftp', 'scp', 'ssh'
-				require "#{APP_ROOT}/lib/Sync/SSH"
+				require 'mpw/sync/ssh'
 				@sync = SyncSSH.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 			when 'ftp'
-				require "#{APP_ROOT}/lib/Sync/FTP"
+				require 'mpw/sync/ftp'
 				@sync = SyncFTP.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 			else
 				@error_msg =  I18n.t('error.unknown_type')
diff --git a/lib/Sync/FTP.rb b/lib/mpw/sync/ftp.rb
similarity index 100%
rename from lib/Sync/FTP.rb
rename to lib/mpw/sync/ftp.rb
diff --git a/lib/Sync/SyncMPW.rb b/lib/mpw/sync/mpw.rb
similarity index 100%
rename from lib/Sync/SyncMPW.rb
rename to lib/mpw/sync/mpw.rb
diff --git a/lib/Sync/SSH.rb b/lib/mpw/sync/ssh.rb
similarity index 100%
rename from lib/Sync/SSH.rb
rename to lib/mpw/sync/ssh.rb
diff --git a/lib/UI/Cli.rb b/lib/mpw/ui/cli.rb
similarity index 99%
rename from lib/UI/Cli.rb
rename to lib/mpw/ui/cli.rb
index 9352ed9..3875be5 100644
--- a/lib/UI/Cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -9,10 +9,9 @@ require 'pathname'
 require 'readline'
 require 'i18n'
 require 'colorize'
-
-require_relative '../Sync'
-require_relative '../MPW'
-require_relative '../Item'
+require 'mpw/sync'
+require 'mpw/mpw'
+require 'mpw/item'
 
 class Cli
 
diff --git a/lib/UI/CliSSH.rb b/lib/mpw/ui/clissh.rb
similarity index 96%
rename from lib/UI/CliSSH.rb
rename to lib/mpw/ui/clissh.rb
index b32097e..83d1d3b 100644
--- a/lib/UI/CliSSH.rb
+++ b/lib/mpw/ui/clissh.rb
@@ -3,7 +3,7 @@
 # mail: nishiki@yaegashi.fr
 # info: a simple script who manage your passwords
 
-require "#{APP_ROOT}/lib/UI/Cli"
+require 'mpw/ui/cli'
 
 class CliSSH < Cli
 
diff --git a/mpw.gemspec b/mpw.gemspec
new file mode 100644
index 0000000..b930a24
--- /dev/null
+++ b/mpw.gemspec
@@ -0,0 +1,19 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+
+Gem::Specification.new do |spec|
+  spec.name          = 'mpw'
+  spec.version       = File.open('VERSION').read
+  spec.authors       = ['nishiki']
+  spec.email         = ['gems@yae.im']
+  spec.summary       = 'Manage your password'
+  spec.description   = 'Save and read your password with gpg'
+  spec.homepage      = 'https://github.com/nishiki/checkupdate'
+  spec.license       = 'GPL'
+
+  spec.files         = `git ls-files -z`.split("\x0")
+  spec.executables   = ['mpw', 'mpw-server']
+  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
+  spec.require_paths = ['lib']
+end

From d367efe13d4fc215c0431f14ea84256bc67e5143 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 21 Jun 2015 23:48:14 +0200
Subject: [PATCH 190/531] fix readme

---
 README.md | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/README.md b/README.md
index 1fc4d7e..c5edca4 100644
--- a/README.md
+++ b/README.md
@@ -8,20 +8,7 @@ MPW can synchronize your password with a MPW Server or via SSH or via FTP.
 You must generate a GPG Key with GPG or with Seahorse (GUI on linux).
 This program work with ruby >= 1.9
 
-* install ruby on your computer
-* gem install bundler
-* bundle install 
-or
-* bundle install --without ssh # if you don't want synchronize your password via SSH
-
-## On Debian/Ubuntu:
-
-* apt-get install ruby ruby-gpgme ruby-highline ruby-i18n ruby-locale
-
-If you want to synchronize your passwords via SSH/SCP:
-* apt-get install ruby-net-ssh ruby-net-scp
-
-For mpw-ssh:
-* apt-get install sshpass
+* install ruby and rubygems on your computer
+* gem install mpw
 
 

From 7d71dded68fd2e2a69c191c70e58e627a5a5a81b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 22 Jun 2015 20:46:43 +0200
Subject: [PATCH 191/531] fix homepage

---
 mpw.gemspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mpw.gemspec b/mpw.gemspec
index b930a24..21c33ed 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
   spec.email         = ['gems@yae.im']
   spec.summary       = 'Manage your password'
   spec.description   = 'Save and read your password with gpg'
-  spec.homepage      = 'https://github.com/nishiki/checkupdate'
+  spec.homepage      = 'https://github.com/nishiki/manage-password'
   spec.license       = 'GPL'
 
   spec.files         = `git ls-files -z`.split("\x0")

From eb0847b16bebf5a0f6e3cfc6af687d74edf87b55 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 23 Jun 2015 22:10:08 +0200
Subject: [PATCH 192/531] fix mpw-ssh

---
 README.md            |  4 ++++
 VERSION              |  2 +-
 lib/mpw/ui/clissh.rb | 12 ++++++------
 mpw.gemspec          |  2 +-
 4 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index c5edca4..cbf6e64 100644
--- a/README.md
+++ b/README.md
@@ -11,4 +11,8 @@ This program work with ruby >= 1.9
 * install ruby and rubygems on your computer
 * gem install mpw
 
+If you want use mpw-ssh, you must install sshpass
 
+# How to use
+
+mpw -h 
diff --git a/VERSION b/VERSION
index 227cea2..38f77a6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.0
+2.0.1
diff --git a/lib/mpw/ui/clissh.rb b/lib/mpw/ui/clissh.rb
index 83d1d3b..0bf7bb0 100644
--- a/lib/mpw/ui/clissh.rb
+++ b/lib/mpw/ui/clissh.rb
@@ -12,15 +12,15 @@ class CliSSH < Cli
 	# Connect to SSH
 	# args: search -> string to search
 	def ssh(search)
-		result = @mpw.search(search, nil, 'ssh')
+		result = @mpw.list(search: search, protocol: 'ssh')
 
 		if result.length > 0
-			result.each do |r|
-				server = @server.nil? ? r[:host]  : @server
-				port   = @port.nil?   ? r[:port]  : @port
-				login  = @login.nil?  ? r[:login] : @login
+			result.each do |item|
+				server = @server.nil? ? item.host : @server
+				port   = @port.nil?   ? item.port : @port
+				login  = @login.nil?  ? item.user : @login
 
-				passwd = r[:password]
+				passwd = item.password
 
 				if port.nil? and port.empty?
 					port = 22
diff --git a/mpw.gemspec b/mpw.gemspec
index 21c33ed..aaf249a 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
   spec.license       = 'GPL'
 
   spec.files         = `git ls-files -z`.split("\x0")
-  spec.executables   = ['mpw', 'mpw-server']
+  spec.executables   = ['mpw', 'mpw-server', 'mpw-ssh']
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ['lib']
 end

From 631f693d06d4e5821f98a2d0f3623fd6137095ca Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 19 Jul 2015 21:51:05 +0200
Subject: [PATCH 193/531] fix dependancy

---
 VERSION     | 2 +-
 mpw.gemspec | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/VERSION b/VERSION
index 38f77a6..e9307ca 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.1
+2.0.2
diff --git a/mpw.gemspec b/mpw.gemspec
index aaf249a..d004d34 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -16,4 +16,11 @@ Gem::Specification.new do |spec|
   spec.executables   = ['mpw', 'mpw-server', 'mpw-ssh']
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ['lib']
+
+  spec.add_dependency "i18n", "~> 0.6", ">= 0.6.9"
+  spec.add_dependency "gpgme"
+  spec.add_dependency "highline"
+  spec.add_dependency "locale"
+  spec.add_dependency "colorize"
+  spec.add_dependency "net-sftp"
 end

From 6b14e8f881092fb5915759a43e9a06340ed59b3c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@believedigital.com>
Date: Sun, 27 Sep 2015 09:13:43 +0200
Subject: [PATCH 194/531] add no-sync option

---
 bin/mpw           | 7 ++++++-
 i18n/cli/en.yml   | 1 +
 i18n/cli/fr.yml   | 1 +
 lib/mpw/ui/cli.rb | 9 ++++++++-
 4 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index d8a76c7..c14c5af 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -37,6 +37,7 @@ I18n.locale         = lang.to_sym
 options = {}
 options[:force]  = false
 options[:format] = false
+options[:sync]   = true
 options[:group]  = nil
 options[:config] = nil
 
@@ -98,6 +99,10 @@ OptionParser.new do |opts|
 		options[:force] = true
 	end
 
+	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
 		puts MPW::MPW::password(length)
 		exit 0
@@ -126,7 +131,7 @@ elsif not config.check_gpg_key?
 end
 
 cli.decrypt
-cli.sync
+cli.sync(options[:sync])
 
 # Display the item's informations
 if not options[:show].nil?
diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index 6ba6c08..bd84171 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -52,6 +52,7 @@ en:
     type: "Data's type export file [csv|yaml]"
     import: "Import item since a yaml or csv file"
     force: "Force an action"
+    no_sync: "Disable synchronization with the server"
     format: "Change the display items format by an alternative format"
     generate_password: "Generate a random password (default 8 characters)"
     help: "Show this help message"
diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 1410993..e9790c0 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -52,6 +52,7 @@ fr:
     type: "Format des données du fichier d'export [csv|yaml]"
     import: "Importe des éléments depuis un fichier yaml ou csv"
     force: "Force une action, l'action ne demandera pas de confirmation"
+    no_sync: "Désactive la synchronisation avec le serveur"
     format: "Change le format d'affichage des éléments par un alternatif"
     generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
     help: "Affiche ce message d'aide"
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 3875be5..3bf9c51 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -23,8 +23,15 @@ class Cli
 	end
 
 	# Sync the data with the server
+	# @args: allow_sync -> allow or disable sync (boolean)
 	# @rtnr: true if the synchro is finish
-	def sync
+	def sync(allow_sync=nil)
+		if not allow_sync.nil?
+			@allow_sync = allow_sync
+		end
+
+		return true if not @allow_sync
+
 		@sync = MPW::Sync.new(@config, @mpw, @password) 
 
 		raise(@sync.error_msg) if not @sync.get_remote

From 0268c604843d9c2f2798b98a8296ea6e14ee5f99 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@believedigital.com>
Date: Sun, 27 Sep 2015 09:16:56 +0200
Subject: [PATCH 195/531] add no-sync option

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

diff --git a/VERSION b/VERSION
index e9307ca..50ffc5a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.2
+2.0.3

From 408ee95bcce7db8a9b0c8263e3f0623b9e886c86 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 5 May 2016 20:19:25 +0200
Subject: [PATCH 196/531] rewrite mpw for use a new file storage format

---
 bin/mpw           |  22 +-
 lib/mpw/config.rb | 356 ++++++++++++-----------------
 lib/mpw/item.rb   | 174 +++++++-------
 lib/mpw/mpw.rb    | 569 +++++++++++++++++++++-------------------------
 lib/mpw/ui/cli.rb | 150 ++++--------
 5 files changed, 532 insertions(+), 739 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index c14c5af..f36bc87 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -1,7 +1,6 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
 
 require 'rubygems'
 require 'optparse'
@@ -11,7 +10,7 @@ require 'set'
 require 'i18n'
 require 'mpw/mpw'
 require 'mpw/config'
-require 'mpw/ui/cli'
+require 'mpw/cli'
 
 # --------------------------------------------------------- #
 # Set local
@@ -77,26 +76,12 @@ OptionParser.new do |opts|
 		options[:setup] = true
 	end
 
-	opts.on('-p', '--protocol PROTOCOL', I18n.t('option.protocol')) do |protocol|
-		options[:protocol] = protocol
-	end
-
 	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
 		options[:export] = file
-		options[:type]   = :yaml
-	end
-
-	opts.on('-t', '--type TYPE', I18n.t('option.type')) do |type|
-		options[:type] = type.to_sym
 	end
 
 	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
 		options[:import] = file
-		options[:type]   = :yaml
-	end
-
-	opts.on('-f', '--force', I18n.t('option.force')) do
-		options[:force] = true
 	end
 
 	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
@@ -131,7 +116,6 @@ elsif not config.check_gpg_key?
 end
 
 cli.decrypt
-cli.sync(options[:sync])
 
 # Display the item's informations
 if not options[:show].nil?
@@ -156,11 +140,11 @@ elsif not options[:add].nil?
 
 # Export
 elsif not options[:export].nil?
-	cli.export(options[:export], options[:type])
+	cli.export(options[:export])
 
 # Add a new item
 elsif not options[:import].nil?
-	cli.import(options[:import], options[:type], options[:force])
+	cli.import(options[:import])
 
 # Interactive mode
 end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index d2b02a9..dd797d2 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -2,230 +2,158 @@
 # author: nishiki
 # mail: nishiki@yaegashi.fr
 
-require 'rubygems'
 require 'gpgme'
 require 'yaml'
 require 'i18n'
 	
 module MPW
-	class Config
-		
-		attr_accessor :error_msg
+class Config
 	
-		attr_accessor :key
-		attr_accessor :share_keys
-		attr_accessor :lang
-		attr_accessor :file_gpg
-		attr_accessor :last_update
-		attr_accessor :sync_type
-		attr_accessor :sync_host
-		attr_accessor :sync_port
-		attr_accessor :sync_user
-		attr_accessor :sync_pwd
-		attr_accessor :sync_path
-		attr_accessor :last_sync
-		attr_accessor :dir_config
-	
-		# Constructor
-		# @args: file_config -> the specify config file
-		def initialize(file_config=nil)
-			@error_msg  = nil
+	attr_accessor :error_msg
 
-			if /darwin/ =~ RUBY_PLATFORM
-				@dir_config = "#{Dir.home}/Library/Preferences/mpw"
-			elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
-				@dir_config = "#{Dir.home}/AppData/Local/mpw"
-			else 
-				@dir_config = "#{Dir.home}/.config/mpw"
-			end
-			
-			@file_config = "#{@dir_config}/conf/default.cfg"
-			if not file_config.nil? and not file_config.empty?
-				@file_config = file_config
-			end
-		end
-	
-		# Create a new config file
-		# @args: key -> the gpg key to encrypt
-		#        share_keys -> multiple keys to share the password with other people
-		#        lang -> the software language
-		#        file_gpg -> the file who is encrypted
-		#        sync_type -> the type to synchronization
-		#        sync_host -> the server host for synchronization
-		#        sync_port -> the server port for synchronization
-		#        sync_user -> the user for synchronization
-		#        sync_pwd -> the password for synchronization
-		#        sync_suffix -> the suffix file (optionnal) 
-		# @rtrn: true if le config file is create
-		def setup(key, share_keys, lang, file_gpg, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
-	
-			if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-				@error_msg = I18n.t('error.config.key_bad_format')
-				return false
-			end
+	attr_accessor :key
+	attr_accessor :lang
+	attr_accessor :config_dir
+	attr_accessor :wallet_dir
 
-			if not check_public_gpg_key(share_keys)
-				return false
-			end
-			
-			if file_gpg.empty?
-				file_gpg = "#{@dir_config}/db/default.gpg"
-			end
-	
-			config = {'config' => {'key'         => key,
-			                       'share_keys'  => share_keys,
-			                       'lang'        => lang,
-			                       'file_gpg'    => file_gpg,
-			                       'sync_type'   => sync_type,
-			                       'sync_host'   => sync_host,
-			                       'sync_port'   => sync_port,
-			                       'sync_user'   => sync_user,
-			                       'sync_pwd'    => sync_pwd,
-			                       'sync_path'   => sync_path,
-			                       'last_sync' => 0 
-			                      }
-			         }
-	
-			Dir.mkdir("#{@config_dir}/conf", 700)
-			Dir.mkdir("#{@config_dir}/db", 700)
-			File.open(@file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-			
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-			return false
-		end
+	# Constructor
+	# @args: config_file -> the specify config file
+	def initialize(config_file=nil)
+		@error_msg   = nil
+		@config_file = config_file
 
-		# 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
-		def setup_gpg_key(password, name, length = 2048, expire = 0)
-			if name.nil? or name.empty?
-				@error_msg = "#{I18n.t('error.config.genkey_gpg.name')}"
-				return false
-			elsif password.nil? or password.empty?
-				@error_msg = "#{I18n.t('error.config.genkey_gpg.password')}"
-				return false
-			end
-
-			param = ''
-			param << '<GnupgKeyParms format="internal">' + "\n"
-			param << "Key-Type: DSA\n"  
-			param << "Key-Length: #{length}\n"
-			param << "Subkey-Type: ELG-E\n"
-			param << "Subkey-Length: #{length}\n"
-			param << "Name-Real: #{name}\n"
-			param << "Name-Comment: #{name}\n"
-			param << "Name-Email: #{@key}\n"
-			param << "Expire-Date: #{expire}\n"
-			param << "Passphrase: #{password}\n"
-			param << "</GnupgKeyParms>\n"
-
-			ctx = GPGME::Ctx.new
-			ctx.genkey(param, nil, nil)
-
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
-			return false
-		end
-	
-		# Check the config file
-		# @rtrn: true if the config file is correct
-		def checkconfig
-			config = YAML::load_file(@file_config)
-			@key        = config['config']['key']
-			@share_keys = config['config']['share_keys']
-			@lang       = config['config']['lang']
-			@file_gpg   = config['config']['file_gpg']
-			@sync_type  = config['config']['sync_type']
-			@sync_host  = config['config']['sync_host']
-			@sync_port  = config['config']['sync_port']
-			@sync_user  = config['config']['sync_user']
-			@sync_pwd   = config['config']['sync_pwd']
-			@sync_path  = config['config']['sync_path']
-			@last_sync  = config['config']['last_sync'].to_i
-
-			if @key.empty? or @file_gpg.empty? 
-				@error_msg = I18n.t('error.config.check')
-				return false
-			end
-			I18n.locale = @lang.to_sym
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
-			return false
-		end
-
-		# Check if private key exist
-		# @rtrn: true if the key exist, else false
-		def check_gpg_key?
-			ctx = GPGME::Ctx.new
-			ctx.each_key(@key, true) do
-				return true
-			end
-
-			return false
-		end
-
-		# Check if private key exist
-		# @args: share_keys -> string with all public keys
-		# @rtrn: true if the key exist, else false
-		def check_public_gpg_key(share_keys = @share_keys)
-			ctx = GPGME::Ctx.new
-
-			share_keys = share_keys.nil? ? '' : share_keys
-			if not share_keys.empty?
-				share_keys.split.each do |k|
-					if not k =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-						@error_msg = I18n.t('error.config.key_bad_format')
-						return false
-					end
-					
-					ctx.each_key(key, false) do
-						next
-					end
-
-					@error_msg = I18n.t('error.config.no_key_public', key: k)
-					return false
-				end
-			end
-
-			return true
-		end
-	
-		# Set the last update when there is a sync
-		# @rtrn: true is the file has been updated
-		def set_last_sync
-			config = {'config' => {'key'         => @key,
-			                       'share_keys'  => @share_keys,
-			                       'lang'        => @lang,
-			                       'file_gpg'    => @file_gpg,
-			                       'sync_type'   => @sync_type,
-			                       'sync_host'   => @sync_host,
-			                       'sync_port'   => @sync_port,
-			                       'sync_user'   => @sync_user,
-			                       'sync_pwd'    => @sync_pwd,
-			                       'sync_path'   => @sync_path,
-			                       'last_sync' => Time.now.to_i
-			                      }
-		           }
-	
-			File.open(@file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-			return false
+		if /darwin/ =~ RUBY_PLATFORM
+			@config_dir = "#{Dir.home}/Library/Preferences/mpw"
+		elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+			@config_dir = "#{Dir.home}/AppData/Local/mpw"
+		else 
+			@config_dir = "#{Dir.home}/.config/mpw"
 		end
 		
+		if @config_file.nil? or @config_file.empty?
+			@config_file = "#{@config_dir}/mpw.cfg"
+		end
 	end
+
+	# Create a new config file
+	# @args: key -> the gpg key to encrypt
+	#        lang -> the software language
+	#        wallet_dir -> the  directory where are the wallets password
+	# @rtrn: true if le config file is create
+	def setup(key, lang, wallet_dir)
+
+		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+			@error_msg = I18n.t('error.config.key_bad_format')
+			return false
+		end
+
+		if wallet_dir.empty?
+			wallet_dir = "#{@config_dir}/wallets"
+		end
+
+		config = {'config' => {'key'        => key,
+		                       'lang'       => lang,
+		                       'wallet_dir' => wallet_dir,
+		                      }
+		         }
+
+		Dir.mkdir(wallet_dir, 0700)
+		File.open(@config_file, 'w') do |file|
+			file << config.to_yaml
+		end
+		
+		return true
+	rescue Exception => e 
+		@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+		return false
+	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
+	def setup_gpg_key(password, name, length = 4096, expire = 0)
+		if name.nil? or name.empty?
+			@error_msg = "#{I18n.t('error.config.genkey_gpg.name')}"
+			return false
+		elsif password.nil? or password.empty?
+			@error_msg = "#{I18n.t('error.config.genkey_gpg.password')}"
+			return false
+		end
+
+		param = ''
+		param << '<GnupgKeyParms format="internal">' + "\n"
+		param << "Key-Type: DSA\n"  
+		param << "Key-Length: #{length}\n"
+		param << "Subkey-Type: ELG-E\n"
+		param << "Subkey-Length: #{length}\n"
+		param << "Name-Real: #{name}\n"
+		param << "Name-Comment: #{name}\n"
+		param << "Name-Email: #{@key}\n"
+		param << "Expire-Date: #{expire}\n"
+		param << "Passphrase: #{password}\n"
+		param << "</GnupgKeyParms>\n"
+
+		ctx = GPGME::Ctx.new
+		ctx.genkey(param, nil, nil)
+
+		return true
+	rescue Exception => e
+		@error_msg = "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
+		return false
+	end
+
+	# Check the config file
+	# @rtrn: true if the config file is correct
+	def checkconfig
+		config = YAML::load_file(@config_file)
+		@key        = config['config']['key']
+		@lang       = config['config']['lang']
+		@wallet_dir = config['config']['wallet_dir']
+
+		if @key.empty? or @wallet_dir.empty? 
+			@error_msg = I18n.t('error.config.check')
+			return false
+		end
+		I18n.locale = @lang.to_sym
+
+		return true
+	rescue Exception => e 
+		@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
+		return false
+	end
+
+	# Check if private key exist
+	# @rtrn: true if the key exist, else false
+	def check_gpg_key?
+		ctx = GPGME::Ctx.new
+		ctx.each_key(@key, true) do
+			return true
+		end
+
+		return false
+	end
+
+	# Set the last update when there is a sync
+	# @rtrn: true is the file has been updated
+	def set_last_sync
+		config = {'config' => {'key'        => @key,
+		                       'lang'       => @lang,
+		                       'wallet_dir' => @wallet_dir,
+		                      }
+	           }
+
+		File.open(@config_file, 'w') do |file|
+			file << config.to_yaml
+		end
+
+		return true
+	rescue Exception => e 
+		@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
+		return false
+	end
+	
+end
 end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 666d5b3..660f74a 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -2,108 +2,104 @@
 # author: nishiki
 # mail: nishiki@yaegashi.fr
 
-require 'rubygems'
 require 'i18n'
 	
 module MPW
-	class Item
+class Item
 
-		attr_accessor :error_msg
+	attr_accessor :error_msg
 
-		attr_accessor :id
-		attr_accessor :name
-		attr_accessor :group
-		attr_accessor :host
-		attr_accessor :protocol
-		attr_accessor :user
-		attr_accessor :password
-		attr_accessor :port
-		attr_accessor :comment
-		attr_accessor :last_edit
-		attr_accessor :last_sync
-		attr_accessor :created
+	attr_accessor :id
+	attr_accessor :name
+	attr_accessor :group
+	attr_accessor :host
+	attr_accessor :protocol
+	attr_accessor :user
+	attr_accessor :port
+	attr_accessor :comment
+	attr_accessor :last_edit
+	attr_accessor :last_sync
+	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 
-		def initialize(options={})
-			if not options.has_key?(:name) or options[:name].to_s.empty?
-				@error_msg = I18n.t('error.update.name_empty')
-				raise @error_msg
-			end
-
-			if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
-				@id      = generate_id
-				@created = Time.now.to_i
-			else
-				@id        = options[:id]
-				@created   = options[:created]
-				@last_edit = options[:last_edit]
-				options[:no_update_last_edit] = true
-			end
-
-			update(options)
+	# Constructor
+	# Create a new item
+	# @args: options -> a hash of parameter
+	# raise an error if the hash hasn't the key name 
+	def initialize(options={})
+		if not options.has_key?(:name) or options[:name].to_s.empty?
+			@error_msg = I18n.t('error.update.name_empty')
+			raise @error_msg
 		end
 
-		# Update the item
-		# @args: options -> a hash of parameter
-		# @rtrn: true if the item is update
-		def update(options={})
-			if options.has_key?(:name) and options[:name].to_s.empty?
-				@error_msg = I18n.t('error.update.name_empty')
-				return false
-			end
-
-			@name      = options[:name]       if options.has_key?(:name)
-			@group     = options[:group]      if options.has_key?(:group)
-			@host      = options[:host]       if options.has_key?(:host)
-			@protocol  = options[:protocol]   if options.has_key?(:protocol)
-			@user      = options[:user]       if options.has_key?(:user)
-			@password  = options[:password]   if options.has_key?(:password)
-			@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
-			@comment   = options[:comment]    if options.has_key?(:comment)
-			@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
-
-			return true
+		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
+			@id      = generate_id
+			@created = Time.now.to_i
+		else
+			@id        = options[:id]
+			@created   = options[:created]
+			@last_edit = options[:last_edit]
+			options[:no_update_last_edit] = true
 		end
 
-		# Update last_sync
-		def set_last_sync
-			@last_sync = Time.now.to_i
-		end
+		update(options)
+	end
 
-		# Delete all data
-		# @rtrn: true
-		def delete
-			@id        = nil
-			@name      = nil
-			@group     = nil
-			@host      = nil
-			@protocol  = nil
-			@user      = nil
-			@password  = nil
-			@port      = nil
-			@comment   = nil
-			@created   = nil
-			@last_edit = nil
-			@last_sync = nil
-
-			return true
-		end
-
-		def empty?
-			return @name.to_s.empty?
-		end
-
-		def nil?
+	# Update the item
+	# @args: options -> a hash of parameter
+	# @rtrn: true if the item is update
+	def update(options={})
+		if options.has_key?(:name) and options[:name].to_s.empty?
+			@error_msg = I18n.t('error.update.name_empty')
 			return false
 		end
 
-		# Generate an random id
-		private
-		def generate_id
-			return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
-		end
+		@name      = options[:name]       if options.has_key?(:name)
+		@group     = options[:group]      if options.has_key?(:group)
+		@host      = options[:host]       if options.has_key?(:host)
+		@protocol  = options[:protocol]   if options.has_key?(:protocol)
+		@user      = options[:user]       if options.has_key?(:user)
+		@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
+		@comment   = options[:comment]    if options.has_key?(:comment)
+		@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
+
+		return true
 	end
+
+	# Update last_sync
+	def set_last_sync
+		@last_sync = Time.now.to_i
+	end
+
+	# Delete all data
+	# @rtrn: true
+	def delete
+		@id        = nil
+		@name      = nil
+		@group     = nil
+		@host      = nil
+		@protocol  = nil
+		@user      = nil
+		@port      = nil
+		@comment   = nil
+		@created   = nil
+		@last_edit = nil
+		@last_sync = nil
+
+		return true
+	end
+
+	def empty?
+		return @name.to_s.empty?
+	end
+
+	def nil?
+		return false
+	end
+
+	# Generate an random id
+	private
+	def generate_id
+		return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
+	end
+end
 end	
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 57a6bce..9fad675 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -2,331 +2,288 @@
 # author: nishiki
 # mail: nishiki@yaegashi.fr
 
-require 'rubygems'
+require 'rubygems/package'
 require 'gpgme'
-require 'csv'
 require 'i18n'
 require 'fileutils'
 require 'yaml'
 require 'mpw/item'
 	
 module MPW
-	class MPW
-	
-		attr_accessor :error_msg
-		
-		# Constructor
-		def initialize(file_gpg, key, share_keys='')
-			@error_msg  = nil
-			@file_gpg   = file_gpg
-			@key        = key
-			@share_keys = share_keys
-			@data       = []
-		end
-	
-		# Decrypt a gpg file
-		# @args: password -> the GPG key password
-		# @rtrn: true if data has been decrypted
-		def decrypt(password=nil)
-			@data = []
+class MPW
 
-			if File.exist?(@file_gpg) and not File.zero?(@file_gpg)
-				crypto       = GPGME::Crypto.new(armor: true)
-				data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: password).read.force_encoding('utf-8')
+	attr_accessor :error_msg
+	
+	# Constructor
+	def initialize(key, wallet_file, gpg_pass=nil)
+		@error_msg   = nil
+		@key         = key
+		@gpg_pass    = gpg_pass
+		@wallet_file = wallet_file
+	end
 
-				if not data_decrypt.to_s.empty?
-					YAML.load(data_decrypt).each_value do |d|
-						@data.push(Item.new(id:        d['id'],
-						                    name:      d['name'],
-						                    group:     d['group'],
-						                    host:      d['host'],
-						                    protocol:  d['protocol'],
-						                    user:      d['user'],
-						                    password:  d['password'],
-						                    port:      d['port'],
-						                    comment:   d['comment'],
-						                    last_edit: d['last_edit'],
-						                    created:   d['created'],
-						                   )
-						          )
-					end
+	# Decrypt a gpg file
+	# @args: password -> the GPG key password
+	# @rtrn: true if data has been decrypted
+	def read_data
+		@config    = nil
+		@keys      = []
+		@data      = []
+		@passwords = {}
+
+		data       = nil
+
+		return if not File.exists?(@wallet_file)
+
+		Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
+			tar.each do |f| 
+				case f.full_name
+					when 'wallet/config.yml'
+						@config = YAML.load(f.read)
+						check_config
+
+					when 'wallet/meta.gpg'
+						data = decrypt(f.read)
+
+					when /^wallet\/keys\/(?<key>.+)\.pub$/
+						@keys[match['key']] = f.read
+
+					when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+						@passwords[Regexp.last_match('id')] = f.read
+					else
+						next
 				end
 			end
-	
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
-			return false
-		end
-	
-		# Encrypt a file
-		# @rtrn: true if the file has been encrypted
-		def encrypt
-			FileUtils.cp(@file_gpg, "#{@file_gpg}.bk") if File.exist?(@file_gpg)
-
-			data_to_encrypt = {}
-	
-			@data.each do |item|
-				next if item.empty?
-
-				data_to_encrypt.merge!(item.id => {'id'        => item.id,
-				                                   'name'      => item.name,
-				                                   'group'     => item.group,
-				                                   'host'      => item.host,
-				                                   'protocol'  => item.protocol,
-				                                   'user'      => item.user,
-				                                   'password'  => item.password,
-				                                   'port'      => item.port,
-				                                   'comment'   => item.comment,
-				                                   'last_edit' => item.last_edit,
-				                                   'created'   => item.created,
-				                                  }
-				                      )
-			end
-	
-			recipients = []
-			recipients.push(@key)
-			if not @share_keys.nil?
-				@share_keys.split.each { |k| recipients.push(k) }
-			end
-
-			crypto = GPGME::Crypto.new(armor: true)
-			file_gpg = File.open(@file_gpg, 'w+')
-			crypto.encrypt(data_to_encrypt.to_yaml, recipients: recipients, output: file_gpg)
-			file_gpg.close
-	
-			FileUtils.rm("#{@file_gpg}.bk") if File.exist?("#{@file_gpg}.bk")
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
-			FileUtils.mv("#{@file_gpg}.bk", @file_gpg) if File.exist?("#{@file_gpg}.bk")
-			return false
-		end
-		
-		# Add a new item
-		# @args: item -> Object MPW::Item
-		# @rtrn: true if add item
-		def add(item)
-			if not item.instance_of?(Item)
-				@error_msg = I18n.t('error.bad_class')
-				return false
-			elsif item.empty?
-				@error_msg = I18n.t('error.add.empty')
-				return false
-			else
-				@data.push(item)
-				return true
-			end
 		end
 
-		# Search in some csv data
-		# @args: options -> a hash with paramaters
-		# @rtrn: a list with the resultat of the search
-		def list(options={})
-			result = []
-	
-			search   = options[:search].to_s.downcase
-			group    = options[:group].to_s.downcase
-			protocol = options[:protocol].to_s.downcase
+		if not data.nil? and not data.empty?
+			YAML.load(data).each_value do |d|
+				@data.push(Item.new(id:        d['id'],
+				                    name:      d['name'],
+				                    group:     d['group'],
+				                    host:      d['host'],
+				                    protocol:  d['protocol'],
+				                    user:      d['user'],
+				                    port:      d['port'],
+				                    comment:   d['comment'],
+				                    last_edit: d['last_edit'],
+				                    created:   d['created'],
+				                   )
+				          )
+			end
+		end
+	end
 
-			@data.each do |item|
-				next if item.empty?
+	# Encrypt a file
+	# @rtrn: true if the file has been encrypted
+	# TODO export key pub
+	def write_data
+		data = {}
 
-				next if not group.empty?    and not group.eql?(item.group.downcase)
-				next if not protocol.empty? and not protocol.eql?(item.protocol.downcase)
-				
-				name    = item.name.to_s.downcase
-				host    = item.host.to_s.downcase
-				comment = item.comment.to_s.downcase
+		@data.each do |item|
+			next if item.empty?
 
-				if not name =~ /^.*#{search}.*$/ and not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/ 
-					next
+			data.merge!(item.id => {'id'        => item.id,
+			                        'name'      => item.name,
+		                            'group'     => item.group,
+			                        'host'      => item.host,
+			                        'protocol'  => item.protocol,
+			                        'user'      => item.user,
+			                        'port'      => item.port,
+			                        'comment'   => item.comment,
+			                        'last_edit' => item.last_edit,
+			                        'created'   => item.created,
+			                       }
+			           )
+		end
+
+
+
+		Gem::Package::TarWriter.new(File.open(@wallet_file, 'w+')) do |tar|
+			data_encrypt = encrypt(YAML::dump(data))
+			tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
+				io.write(data_encrypt)
+			end
+
+			@passwords.each do |id, password|
+				tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
+					io.write(password)
 				end
-
-				result.push(item)
 			end
-	
-			return result
-		end
-	
-		# Search in some csv data
-		# @args: id -> the id item
-		# @rtrn: a row with the result of the search
-		def search_by_id(id)
-			@data.each do |item|
-				return item if item.id == id
-			end
-	
-			return nil
-		end
-		
-		# Export to csv
-		# @args: file -> file where you export the data
-		#        type -> udata type
-		# @rtrn: true if export work
-		def export(file, type=:yaml)
-			case type
-			when :csv
-				CSV.open(file, 'w', write_headers: true,
-									headers: ['name', 'group', 'protocol', 'host', 'user', 'password', 'port', 'comment']) do |csv|
-					@data.each do |item|
-						csv << [item.name, item.group, item.protocol, item.host, item.user, item.password, item.port, item.comment]
-					end
-				end
-
-			when :yaml
-				data = {}
-				@data.each do |item|
-					data.merge!(item.id => {'id'        => item.id,
-					                        'name'      => item.name,
-					                        'group'     => item.group,
-					                        'host'      => item.host,
-					                        'protocol'  => item.protocol,
-					                        'user'      => item.user,
-					                        'password'  => item.password,
-					                        'port'      => item.port,
-					                        'comment'   => item.comment,
-					                        'last_edit' => item.last_edit,
-					                        'created'   => item.created,
-					                       }
-					            )
-				end
-
-				File.open(file, 'w') {|f| f << data.to_yaml}
-
-			else
-				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
-				return false
-			end
-
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.export.write', file: file)}\n#{e}"
-			return false
-		end
-	
-		# Import to csv
-		# @args: file -> path to file import
-		#        type -> udata type
-		# @rtrn: true if the import work
-		def import(file, type=:yaml)
-			case type
-			when :csv
-				CSV.foreach(file, {headers: true}) do |row|
-					item = Item.new(name:     row['name'], 
-					                group:    row['group'],
-					                host:     row['host'],
-					                protocol: row['protocol'],
-					                user:     row['user'],
-					                password: row['password'],
-					                port:     row['port'],
-					                comment:  row['comment'],
-					               )
-
-					return false if item.empty?
-
-					@data.push(item)
-				end
-
-			when :yaml
-				YAML::load_file(file).each_value do |row| 
-					item = Item.new(name:     row['name'], 
-					                group:    row['group'],
-					                host:     row['host'],
-					                protocol: row['protocol'],
-					                user:     row['user'],
-					                password: row['password'],
-					                port:     row['port'],
-					                comment:  row['comment'],
-					               )
-
-					return false if item.empty?
-
-					@data.push(item)
-				end
-
-			else
-				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
-				return false
-			end
-	
-			return true
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
-			return false
-		end
-	
-		# Return a preview import 
-		# @args: file -> path to file import
-		# @rtrn: a hash with the items to import, if there is an error return false
-		def import_preview(file, type=:yaml)
-			data = []
-
-			case type
-			when :csv
-				CSV.foreach(file, {headers: true}) do |row|
-					item = Item.new(name:     row['name'], 
-					                group:    row['group'],
-					                host:     row['host'],
-					                protocol: row['protocol'],
-					                user:     row['user'],
-					                password: row['password'],
-					                port:     row['port'],
-					                comment:  row['comment'],
-					               )
-
-					return false if item.empty?
-
-					data.push(item)
-				end
-
-			when :yaml
-				YAML::load_file(file).each_value do |row| 
-					item = Item.new(name:     row['name'], 
-					                group:    row['group'],
-					                host:     row['host'],
-					                protocol: row['protocol'],
-					                user:     row['user'],
-					                password: row['password'],
-					                port:     row['port'],
-					                comment:  row['comment'],
-					               )
-
-					return false if item.empty?
-
-					data.push(item)
-				end
-
-			else
-				@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
-				return false
-			end
-	
-			return data
-		rescue Exception => e 
-			@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
-			return false
-		end
-	
-		# Generate a random password
-		# @args: length -> the length password
-		# @rtrn: a random string
-		def self.password(length=8)
-			if length.to_i <= 0
-				length = 8
-			else
-				length = length.to_i
-			end
-	
-			result = ''
-			while length > 62 do
-				result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
-				length -= 62
-			end
-			result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
-	
-			return result
 		end
 
 	end
+
+	# TODO comment
+	def get_password(id)
+		return decrypt(@passwords[id])
+	end
+
+	# TODO comment
+	def set_password(id, password)
+		@passwords[id] = encrypt(password)
+	end
+
+	# TODO
+	def check_config
+		if false
+			raise 'ERROR'
+		end
+	end
+
+	# Add a new item
+	# @args: item -> Object MPW::Item
+	# @rtrn: true if add item
+	# TODO add password
+	def add(item)
+		if not item.instance_of?(Item)
+			raise I18n.t('error.bad_class')
+		elsif item.empty?
+			raise I18n.t('error.add.empty')
+		else
+			@data.push(item)
+		end
+	end
+
+
+	# Search in some csv data
+	# @args: options -> a hash with paramaters
+	# @rtrn: a list with the resultat of the search
+	def list(options={})
+		result = []
+
+		search   = options[:search].to_s.downcase
+		group    = options[:group].to_s.downcase
+		protocol = options[:protocol].to_s.downcase
+
+		@data.each do |item|
+			next if item.empty?
+
+			next if not group.empty?    and not group.eql?(item.group.downcase)
+			next if not protocol.empty? and not protocol.eql?(item.protocol.downcase)
+			
+			name    = item.name.to_s.downcase
+			host    = item.host.to_s.downcase
+			comment = item.comment.to_s.downcase
+
+			if not name =~ /^.*#{search}.*$/ and not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/ 
+				next
+			end
+
+			result.push(item)
+		end
+
+		return result
+	end
+
+	# Search in some csv data
+	# @args: id -> the id item
+	# @rtrn: a row with the result of the search
+	def search_by_id(id)
+		@data.each do |item|
+			return item if item.id == id
+		end
+
+		return nil
+	end
+
+	# Export to csv
+	# @args: file -> file where you export the data
+	def export(file)
+			data = {}
+			@data.each do |item|
+				data.merge!(item.id => {'id'        => item.id,
+				                        'name'      => item.name,
+				                        'group'     => item.group,
+				                        'host'      => item.host,
+				                        'protocol'  => item.protocol,
+				                        'user'      => item.user,
+				                        'password'  => get_password(item.id),
+				                        'port'      => item.port,
+				                        'comment'   => item.comment,
+				                        'last_edit' => item.last_edit,
+				                        'created'   => item.created,
+				                       }
+				            )
+			end
+
+			File.open(file, 'w') {|f| f << data.to_yaml}
+	rescue Exception => e 
+		raise "#{I18n.t('error.export.write', file: file)}\n#{e}"
+	end
+
+	# Import to yaml
+	# @args: file -> path to file import
+	# TODO raise
+	def import(file)
+		YAML::load_file(file).each_value do |row| 
+			item = Item.new(name:     row['name'], 
+			                group:    row['group'],
+			                host:     row['host'],
+			                protocol: row['protocol'],
+			                user:     row['user'],
+			                port:     row['port'],
+			                comment:  row['comment'],
+			               )
+
+			raise 'Item is empty' if item.empty?
+
+			@data.push(item)
+			set_password(item.id, row['password'])
+		end
+	rescue Exception => e 
+		raise "#{I18n.t('error.import.read', file: file)}\n#{e}"
+	end
+
+	# Generate a random password
+	# @args: length -> the length password
+	# @rtrn: a random string
+	def self.password(length=8)
+		if length.to_i <= 0
+			length = 8
+		else
+			length = length.to_i
+		end
+
+		result = ''
+		while length > 62 do
+			result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
+			length -= 62
+		end
+		result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
+
+		return result
+	end
+
+	# Decrypt a gpg file
+	# @args: password -> the GPG key password
+	# @rtrn: true if data has been decrypted
+	private
+	def decrypt(data)
+		crypto = GPGME::Crypto.new(armor: true)
+		
+		return crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
+	rescue Exception => e 
+		raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
+	end
+
+	# Encrypt a file
+	# @rtrn: true if the file has been encrypted
+	private
+	def encrypt(data)
+		recipients = []
+		crypto     = GPGME::Crypto.new(armor: true)
+
+#		@config['keys'].each do |key|
+#			recipients.push(key)
+#		end
+
+		recipients.push(@key)
+
+		return crypto.encrypt(data, recipients: recipients).read
+	rescue Exception => e 
+		raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+	end
+
+end
 end
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 3bf9c51..bb6768e 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -1,15 +1,11 @@
 #!/usr/bin/ruby
 # author: nishiki
 # mail: nishiki@yaegashi.fr
-# info: a simple script who m your passwords
 
-require 'rubygems'
-require 'highline/import'
-require 'pathname'
 require 'readline'
 require 'i18n'
 require 'colorize'
-require 'mpw/sync'
+require 'highline/import'
 require 'mpw/mpw'
 require 'mpw/item'
 
@@ -18,29 +14,10 @@ class Cli
 	# Constructor
 	# @args: lang -> the operating system language
 	#        config_file -> a specify config file
+	# TODO
 	def initialize(config)
 		@config = config
-	end
-
-	# Sync the data with the server
-	# @args: allow_sync -> allow or disable sync (boolean)
-	# @rtnr: true if the synchro is finish
-	def sync(allow_sync=nil)
-		if not allow_sync.nil?
-			@allow_sync = allow_sync
-		end
-
-		return true if not @allow_sync
-
-		@sync = MPW::Sync.new(@config, @mpw, @password) 
-
-		raise(@sync.error_msg) if not @sync.get_remote
-		raise(@sync.error_msg) if not @sync.sync
-
-		return true
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #7: #{e}".red
-		return false
+		@wallet_file = "#{@config.wallet_dir}/test.mpw"
 	end
 
 	# Create a new config file
@@ -48,33 +25,16 @@ class Cli
 	def setup(lang)
 		puts I18n.t('form.setup.title')
 		puts '--------------------'
-		language    = ask(I18n.t('form.setup.lang', lang: lang)).to_s
-		key         = ask(I18n.t('form.setup.gpg_key')).to_s
-		share_keys  = ask(I18n.t('form.setup.share_gpg_keys')).to_s
-		file_gpg    = ask(I18n.t('form.setup.gpg_file', home: @conf.dir_config)).to_s
-		sync_type   = ask(I18n.t('form.setup.sync_type')).to_s
+		language   = ask(I18n.t('form.setup.lang', lang: lang)).to_s
+		key        = ask(I18n.t('form.setup.gpg_key')).to_s
+		wallet_dir = ask(I18n.t('form.setup.wallet_dir')).to_s
 
-		if ['ssh', 'ftp', 'mpw'].include?(sync_type)
-			sync_host   = ask(I18n.t('form.setup.sync_host')).to_s
-			sync_port   = ask(I18n.t('form.setup.sync_port')).to_s
-			sync_user   = ask(I18n.t('form.setup.sync_user')).to_s
-			sync_pwd    = ask(I18n.t('form.setup.sync_pwd')).to_s
-			sync_path   = ask(I18n.t('form.setup.sync_path')).to_s
-		end
-		
 		if language.nil? or language.empty?
 			language = lang
 		end
 		I18n.locale = language.to_sym
 
-		sync_type = sync_type.nil? or sync_type.empty? ? nil : sync_type
-		sync_host = sync_host.nil? or sync_host.empty? ? nil : sync_host
-		sync_port = sync_port.nil? or sync_port.empty? ? nil : sync_port.to_i
-		sync_user = sync_user.nil? or sync_user.empty? ? nil : sync_user
-		sync_pwd  = sync_pwd.nil?  or sync_pwd.empty?  ? nil : sync_pwd
-		sync_path = sync_path.nil? or sync_path.empty? ? nil : sync_path
-
-		if @config.setup(key, share_keys, language, file_gpg, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
+		if @config.setup(key, lang, wallet_dir)
 			puts "#{I18n.t('form.setup.valid')}".green
 		else
 			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}".red
@@ -86,7 +46,7 @@ class Cli
 			exit 2
 		end
 	end
-
+	
 	# Setup a new GPG key
 	def setup_gpg_key
 		puts I18n.t('form.setup_gpg_key.title')
@@ -124,17 +84,15 @@ class Cli
 		end
 	end
 
+	
 	# Request the GPG password and decrypt the file
 	def decrypt
 		if not defined?(@mpw)
-			@mpw = MPW::MPW.new(@config.file_gpg, @config.key, @config.share_keys)
+			@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
+			@mpw = MPW::MPW.new(@config.key, @wallet_file, @password)
 		end
 
-		@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
-		if not @mpw.decrypt(@password)
-			puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}".red
-			exit 2
-		end
+		@mpw.read_data
 	end
 
 	# Display the query's result
@@ -185,7 +143,7 @@ class Cli
 		print "#{I18n.t('display.login')}: ".cyan
 		puts  item.user
 		print "#{I18n.t('display.password')}: ".cyan
-		puts  item.password
+		puts  @mpw.get_password(item.id)
 		print "#{I18n.t('display.port')}: ".cyan
 		puts  item.port
 		print "#{I18n.t('display.comment')}: ".cyan
@@ -203,21 +161,17 @@ class Cli
 		options[:host]     = ask(I18n.t('form.add.server')).to_s
 		options[:protocol] = ask(I18n.t('form.add.protocol')).to_s
 		options[:user]     = ask(I18n.t('form.add.login')).to_s
-		options[:password] = ask(I18n.t('form.add.password')).to_s
+		password           = ask(I18n.t('form.add.password')).to_s
 		options[:port]     = ask(I18n.t('form.add.port')).to_s
 		options[:comment]  = ask(I18n.t('form.add.comment')).to_s
 
 		item = MPW::Item.new(options)
-		if @mpw.add(item)
-			if @mpw.encrypt
-				sync
-				puts "#{I18n.t('form.add.valid')}".green
-			else
-				puts "#{I18n.t('display.error')} #12: #{@mpw.error_msg}".red
-			end
-		else
-			puts "#{I18n.t('display.error')} #13: #{item.error_msg}".red
-		end
+
+		@mpw.add(item)
+		@mpw.set_password(item.id, password)
+		@mpw.write_data
+
+		puts "#{I18n.t('form.add.valid')}".green
 	end
 
 	# Update an item
@@ -235,22 +189,17 @@ class Cli
 			options[:host]     = ask(I18n.t('form.update.server'  , server:   item.host)).to_s
 			options[:protocol] = ask(I18n.t('form.update.protocol', protocol: item.protocol)).to_s
 			options[:user]     = ask(I18n.t('form.update.login'   , login:    item.user)).to_s
-			options[:password] = ask(I18n.t('form.update.password')).to_s
+			password           = ask(I18n.t('form.update.password')).to_s
 			options[:port]     = ask(I18n.t('form.update.port'    , port:     item.port)).to_s
 			options[:comment]  = ask(I18n.t('form.update.comment' , comment:  item.comment)).to_s
 
 			options.delete_if { |k,v| v.empty? }
 				
-			if item.update(options)
-				if @mpw.encrypt
-					sync
-					puts "#{I18n.t('form.update.valid')}".green
-				else
-					puts "#{I18n.t('display.error')} #14: #{@mpw.error_msg}".red
-				end
-			else
-				puts "#{I18n.t('display.error')} #15: #{item.error_msg}".red
-			end
+			item.update(options)
+			@mpw.encrypt
+			@mpw.write_data
+
+			puts "#{I18n.t('form.update.valid')}".green
 		else
 			puts I18n.t('display.nothing')
 		end
@@ -289,43 +238,22 @@ class Cli
 
 	# Export the items in a CSV file
 	# @args: file -> the destination file
-	def export(file, type=:yaml)
-		if @mpw.export(file, type)
-			puts "#{I18n.t('export.valid', file)}".green
-		else
-			puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}".red
-		end
+	def export(file)
+		@mpw.export(file)
+
+		puts "#{I18n.t('export.valid', file)}".green
+	rescue Exception => e
+			puts "#{I18n.t('display.error')} #17: #{e}".red
 	end
 
-	# Import items from a CSV file
+	# Import items from a YAML file
 	# @args: file -> the import file
-	#        force -> no resquest a validation
-	def import(file, type=:yaml, force=false)
+	def import(file)
+		@mpw.import(file)
+		@mpw.write_data
 
-		if not force
-			result = @mpw.import_preview(file, type)
-			if result.is_a?(Array) and not result.empty?
-				result.each do |r|
-					display_item(r)
-				end
-
-				confirm = ask("#{I18n.t('form.import.ask', file: file)} (y/N) ").to_s
-				if confirm =~ /^(y|yes|YES|Yes|Y)$/
-					force = true
-				end
-			else
-				puts I18n.t('form.import.not_valid')
-			end
-		end
-
-		if force
-			if @mpw.import(file, type) and @mpw.encrypt
-				sync
-				puts "#{I18n.t('form.import.valid')}".green
-			else
-				puts "#{I18n.t('display.error')} #18: #{@mpw.error_msg}".red
-			end
-		end
+		puts "#{I18n.t('form.import.valid')}".green
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #18: #{e}".red
 	end
-
 end

From b86e095a603059a1bf53b26c3a8dfb629b1a51ea Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 5 May 2016 23:07:37 +0200
Subject: [PATCH 197/531] add relative path for development and test

---
 bin/mpw           | 8 +++++---
 lib/mpw/mpw.rb    | 4 +++-
 lib/mpw/ui/cli.rb | 6 ++++--
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index f36bc87..f385cb7 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -8,9 +8,6 @@ require 'pathname'
 require 'locale'
 require 'set'
 require 'i18n'
-require 'mpw/mpw'
-require 'mpw/config'
-require 'mpw/cli'
 
 # --------------------------------------------------------- #
 # Set local
@@ -24,6 +21,11 @@ end
 
 APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
 
+# TODO
+require "#{APP_ROOT}/../lib/mpw/mpw.rb"
+require "#{APP_ROOT}/../lib/mpw/config.rb"
+require "#{APP_ROOT}/../lib/mpw/ui/cli.rb"
+
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
 I18n.load_path      = Dir["#{APP_ROOT}/../i18n/cli/*.yml"]
 I18n.default_locale = :en
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 9fad675..654cd25 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -7,7 +7,9 @@ require 'gpgme'
 require 'i18n'
 require 'fileutils'
 require 'yaml'
-require 'mpw/item'
+
+#TODO
+require "#{APP_ROOT}/../lib/mpw/item.rb"
 	
 module MPW
 class MPW
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index bb6768e..1d05c8b 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -6,8 +6,10 @@ require 'readline'
 require 'i18n'
 require 'colorize'
 require 'highline/import'
-require 'mpw/mpw'
-require 'mpw/item'
+
+#TODO
+require "#{APP_ROOT}/../lib/mpw/item.rb"
+require "#{APP_ROOT}/../lib/mpw/mpw.rb"
 
 class Cli
 

From 38beb7ad5357f5343cf554d8540a064a17f2b82f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 5 May 2016 23:14:28 +0200
Subject: [PATCH 198/531] cli: fix update function

---
 lib/mpw/ui/cli.rb | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 1d05c8b..cfa731c 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -198,13 +198,15 @@ class Cli
 			options.delete_if { |k,v| v.empty? }
 				
 			item.update(options)
-			@mpw.encrypt
+			@mpw.set_password(item.id, password) if not password.empty?
 			@mpw.write_data
 
 			puts "#{I18n.t('form.update.valid')}".green
 		else
 			puts I18n.t('display.nothing')
 		end
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #14: #{e}".red
 	end
 
 	# Remove an item

From 163ef46076bb79f401e3b5caa296f8268a6907a8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 5 May 2016 23:24:08 +0200
Subject: [PATCH 199/531] cli: fix delete function

---
 lib/mpw/ui/cli.rb | 35 ++++++++++++++++-------------------
 1 file changed, 16 insertions(+), 19 deletions(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index cfa731c..95e7326 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -213,31 +213,28 @@ class Cli
 	# @args: id -> the item's id
 	#        force -> no resquest a validation
 	def delete(id, force=false)
+		item = @mpw.search_by_id(id)
+
+		if item.nil?
+			puts I18n.t('display.nothing')
+			return
+		end
+
 		if not force
-			item = @mpw.search_by_id(id)
+			display_item(item)
 
-			if not item.nil?
-				display_item(item)
-
-				confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
-				if confirm =~ /^(y|yes|YES|Yes|Y)$/
-					force = true
-				end
-			else
-				puts I18n.t('display.nothing')
+			confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
+			if not confirm =~ /^(y|yes|YES|Yes|Y)$/
+				return
 			end
 		end
 
-		if force
-			item.delete
+		item.delete
+		@mpw.write_data
 
-			if @mpw.encrypt
-				sync
-				puts "#{I18n.t('form.delete.valid', id: id)}".green
-			else
-				puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
-			end
-		end
+		puts "#{I18n.t('form.delete.valid', id: id)}".green
+	rescue
+		puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
 	end
 
 	# Export the items in a CSV file

From afb197aff5a7df1db3d80bc32ba48011081eb3be Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 5 May 2016 23:43:34 +0200
Subject: [PATCH 200/531] add option for choice the wallet

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

diff --git a/bin/mpw b/bin/mpw
index f385cb7..e0f5f16 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -41,6 +41,7 @@ options[:format] = false
 options[:sync]   = true
 options[:group]  = nil
 options[:config] = nil
+options[:wallet] = nil
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
@@ -90,6 +91,10 @@ OptionParser.new do |opts|
 		options[:sync] = false
 	end
 
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
 		puts MPW::MPW::password(length)
 		exit 0
@@ -108,7 +113,7 @@ end.parse!
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig
 
-cli = Cli.new(config)
+cli = Cli.new(config, options[:wallet])
 	
 # Setup a new config 
 if not check_error or not options[:setup].nil?
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 95e7326..f4aba00 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -17,9 +17,9 @@ class Cli
 	# @args: lang -> the operating system language
 	#        config_file -> a specify config file
 	# TODO
-	def initialize(config)
+	def initialize(config, wallet)
 		@config = config
-		@wallet_file = "#{@config.wallet_dir}/test.mpw"
+		@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
 	end
 
 	# Create a new config file

From 797efa2b93e5434759ce43b080ca15c1041ca9cb Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 5 May 2016 23:46:48 +0200
Subject: [PATCH 201/531] cli: catch exception in decrypt

---
 lib/mpw/ui/cli.rb | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index f4aba00..868ccfe 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -95,6 +95,9 @@ class Cli
 		end
 
 		@mpw.read_data
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #11: #{e}".red
+		exit 2
 	end
 
 	# Display the query's result

From c1cab93dce65fa3091bd2e786bab4c6b79b25a96 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 6 May 2016 10:53:32 +0200
Subject: [PATCH 202/531] write pub key in tarball

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 654cd25..d022849 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -99,8 +99,6 @@ class MPW
 			           )
 		end
 
-
-
 		Gem::Package::TarWriter.new(File.open(@wallet_file, 'w+')) do |tar|
 			data_encrypt = encrypt(YAML::dump(data))
 			tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
@@ -112,8 +110,13 @@ class MPW
 					io.write(password)
 				end
 			end
-		end
 
+			@keys.each do |id, key|
+				tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
+					io.write(password)
+				end
+			end
+		end
 	end
 
 	# TODO comment

From f724f09957d7e707ed24891c522860cffb072347 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 09:40:39 +0200
Subject: [PATCH 203/531] mpw: add raise for read data

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index d022849..669df3c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -24,9 +24,7 @@ class MPW
 		@wallet_file = wallet_file
 	end
 
-	# Decrypt a gpg file
-	# @args: password -> the GPG key password
-	# @rtrn: true if data has been decrypted
+	# Read mpw file
 	def read_data
 		@config    = nil
 		@keys      = []
@@ -74,6 +72,8 @@ class MPW
 				          )
 			end
 		end
+	rescue Exception => e
+		raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
 	end
 
 	# Encrypt a file

From 6a1d3a8921388aa371732b6750e9ddfd1f2318d7 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 10:04:07 +0200
Subject: [PATCH 204/531] add choice wallet

---
 bin/mpw           |  3 ++-
 lib/mpw/ui/cli.rb | 41 ++++++++++++++++++++++++++++++++++++-----
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index e0f5f16..088f173 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -113,7 +113,7 @@ end.parse!
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig
 
-cli = Cli.new(config, options[:wallet])
+cli = Cli.new(config)
 	
 # Setup a new config 
 if not check_error or not options[:setup].nil?
@@ -122,6 +122,7 @@ elsif not config.check_gpg_key?
 	cli.setup_gpg_key
 end
 
+cli.get_wallet(options[:wallet])
 cli.decrypt
 
 # Display the item's informations
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 868ccfe..57063de 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -14,12 +14,9 @@ require "#{APP_ROOT}/../lib/mpw/mpw.rb"
 class Cli
 
 	# Constructor
-	# @args: lang -> the operating system language
-	#        config_file -> a specify config file
-	# TODO
-	def initialize(config, wallet)
+	# @args: config_file -> a specify config file
+	def initialize(config)
 		@config = config
-		@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
 	end
 
 	# Create a new config file
@@ -155,6 +152,40 @@ class Cli
 		puts  item.comment
 	end
 
+	# Display the wallet
+	# @args: wallet -> the wallet name
+	def get_wallet(wallet=nil)
+		if wallet.to_s.empty?
+			wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
+			puts wallets
+
+			case wallets.length
+			when 0
+				puts I18n.t('display.nothing')
+			when 1
+				@wallet_file = wallets[0]
+			else
+				i = 1
+				wallets.each do |wallet|
+						print "#{i}: ".cyan
+						puts File.basename(wallet, '.mpw')
+
+						i += 1
+				end
+
+				choice = ask(I18n.t('form.select')).to_i
+
+				if choice >= 1 and choice < i
+					@wallet_file = wallets[choice-1]
+				else
+					puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+				end
+			end
+		else
+			@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
+		end
+	end
+
 	# Form to add a new item
 	def add
 		options = {}

From 09f53a3c85883c0c3cf4559f2ce92535237490c2 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 12:00:19 +0200
Subject: [PATCH 205/531] add multiple key for encrypt

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 669df3c..799bf5b 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -27,8 +27,8 @@ class MPW
 	# Read mpw file
 	def read_data
 		@config    = nil
-		@keys      = []
 		@data      = []
+		@keys      = {}
 		@passwords = {}
 
 		data       = nil
@@ -46,7 +46,7 @@ class MPW
 						data = decrypt(f.read)
 
 					when /^wallet\/keys\/(?<key>.+)\.pub$/
-						@keys[match['key']] = f.read
+						@keys[Regexp.last_match('key')] = f.read
 
 					when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
 						@passwords[Regexp.last_match('id')] = f.read
@@ -279,11 +279,11 @@ class MPW
 		recipients = []
 		crypto     = GPGME::Crypto.new(armor: true)
 
-#		@config['keys'].each do |key|
-#			recipients.push(key)
-#		end
+		@keys.each_key do |key|
+			recipients.push(key)
+		end
 
-		recipients.push(@key)
+		recipients.push(@key) if not recipients.index(@key).nil?
 
 		return crypto.encrypt(data, recipients: recipients).read
 	rescue Exception => e 

From d56ab48797cddd462d2333ec42ce10e8c9445eed Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 12:03:55 +0200
Subject: [PATCH 206/531] fix indent for export function

---
 lib/mpw/mpw.rb | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 799bf5b..0d1af7a 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -195,24 +195,24 @@ class MPW
 	# Export to csv
 	# @args: file -> file where you export the data
 	def export(file)
-			data = {}
-			@data.each do |item|
-				data.merge!(item.id => {'id'        => item.id,
-				                        'name'      => item.name,
-				                        'group'     => item.group,
-				                        'host'      => item.host,
-				                        'protocol'  => item.protocol,
-				                        'user'      => item.user,
-				                        'password'  => get_password(item.id),
-				                        'port'      => item.port,
-				                        'comment'   => item.comment,
-				                        'last_edit' => item.last_edit,
-				                        'created'   => item.created,
-				                       }
-				            )
-			end
+		data = {}
+		@data.each do |item|
+			data.merge!(item.id => {'id'        => item.id,
+			                        'name'      => item.name,
+			                        'group'     => item.group,
+			                        'host'      => item.host,
+			                        'protocol'  => item.protocol,
+			                        'user'      => item.user,
+			                        'password'  => get_password(item.id),
+			                        'port'      => item.port,
+			                        'comment'   => item.comment,
+			                        'last_edit' => item.last_edit,
+			                        'created'   => item.created,
+			                       }
+			            )
+		end
 
-			File.open(file, 'w') {|f| f << data.to_yaml}
+		File.open(file, 'w') {|f| f << data.to_yaml}
 	rescue Exception => e 
 		raise "#{I18n.t('error.export.write', file: file)}\n#{e}"
 	end

From 8f29eb8d58b629e6668667ad86cf6559bb248e61 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 12:08:36 +0200
Subject: [PATCH 207/531] fix comments

---
 lib/mpw/mpw.rb | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 0d1af7a..230ff53 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -77,8 +77,7 @@ class MPW
 	end
 
 	# Encrypt a file
-	# @rtrn: true if the file has been encrypted
-	# TODO export key pub
+	# TODO backup and restore file with raise
 	def write_data
 		data = {}
 
@@ -119,12 +118,15 @@ class MPW
 		end
 	end
 
-	# TODO comment
+	# Get a password
+	# args: id -> the item id
 	def get_password(id)
 		return decrypt(@passwords[id])
 	end
 
-	# TODO comment
+	# Set a password
+	# args: id -> the item id
+	#       password -> the new password
 	def set_password(id, password)
 		@passwords[id] = encrypt(password)
 	end
@@ -138,8 +140,6 @@ class MPW
 
 	# Add a new item
 	# @args: item -> Object MPW::Item
-	# @rtrn: true if add item
-	# TODO add password
 	def add(item)
 		if not item.instance_of?(Item)
 			raise I18n.t('error.bad_class')
@@ -192,7 +192,7 @@ class MPW
 		return nil
 	end
 
-	# Export to csv
+	# Export to yaml
 	# @args: file -> file where you export the data
 	def export(file)
 		data = {}
@@ -261,8 +261,7 @@ class MPW
 	end
 
 	# Decrypt a gpg file
-	# @args: password -> the GPG key password
-	# @rtrn: true if data has been decrypted
+	# @args: data -> string to decrypt
 	private
 	def decrypt(data)
 		crypto = GPGME::Crypto.new(armor: true)
@@ -273,7 +272,7 @@ class MPW
 	end
 
 	# Encrypt a file
-	# @rtrn: true if the file has been encrypted
+	# args: data -> string to encrypt
 	private
 	def encrypt(data)
 		recipients = []

From 2d35e19fd0e0e65112ba5be8b5acdcd07342e496 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 12:20:46 +0200
Subject: [PATCH 208/531] fix alphabetic order for options

---
 bin/mpw | 61 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 29 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 088f173..ee201ba 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -37,7 +37,6 @@ I18n.locale         = lang.to_sym
 
 options = {}
 options[:force]  = false
-options[:format] = false
 options[:sync]   = true
 options[:group]  = nil
 options[:config] = nil
@@ -46,8 +45,8 @@ options[:wallet] = nil
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
 
-	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
-		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
+	opts.on('-a', '--add', I18n.t('option.add')) do
+		options[:add] = true
 	end
 
 	opts.on('-A', '--show-all', I18n.t('option.show_all')) do
@@ -55,44 +54,24 @@ OptionParser.new do |opts|
 		options[:show] = ''
 	end
 
-	opts.on('-u', '--update ID', I18n.t('option.update')) do |id|
-		options[:update] = id
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
 	end
 
 	opts.on('-d', '--delete ID', I18n.t('option.remove')) do |id|
 		options[:delete] = id
 	end
 
-	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
-		options[:group] = group
-	end
-
-	opts.on('-a', '--add', I18n.t('option.add')) do
-		options[:add] = true
-	end
-
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
-
-	opts.on('-S', '--setup', I18n.t('option.setup')) do
-		options[:setup] = true
-	end
-
 	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
 		options[:export] = file
 	end
 
-	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
-		options[:import] = file
+	opts.on('-f', '--force', I18n.t('option.force')) do
+		options[:force] = true
 	end
 
-	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
+	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
+		options[:group] = group
 	end
 
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
@@ -104,6 +83,30 @@ OptionParser.new do |opts|
 		puts opts
 		exit 0
 	end
+
+	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
+		options[:import] = file
+	end
+
+	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
+	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
+		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
+	end
+
+	opts.on('-S', '--setup', I18n.t('option.setup')) do
+		options[:setup] = true
+	end
+
+	opts.on('-u', '--update ID', I18n.t('option.update')) do |id|
+		options[:update] = id
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
 end.parse!
 
 # --------------------------------------------------------- #

From 27c4162aa959f0da73781bc96e5259d84f1b0c1a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 13:03:23 +0200
Subject: [PATCH 209/531] add options id

---
 bin/mpw | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index ee201ba..6d92958 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -58,8 +58,8 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
-	opts.on('-d', '--delete ID', I18n.t('option.remove')) do |id|
-		options[:delete] = id
+	opts.on('-d', '--delete', I18n.t('option.remove')) do
+		options[:delete] = true
 	end
 
 	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
@@ -84,7 +84,11 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-i', '--import FILE', I18n.t('option.import')) do |file|
+	opts.on('-i', '--id ID', I18n.t('option.id')) do |id|
+		options[:id] = id
+	end
+
+	opts.on('-I', '--import FILE', I18n.t('option.import')) do |file|
 		options[:import] = file
 	end
 
@@ -100,8 +104,8 @@ OptionParser.new do |opts|
 		options[:setup] = true
 	end
 
-	opts.on('-u', '--update ID', I18n.t('option.update')) do |id|
-		options[:update] = id
+	opts.on('-u', '--update', I18n.t('option.update')) do
+		options[:update] = true
 	end
 
 	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
@@ -138,12 +142,12 @@ if not options[:show].nil?
 	cli.display(opts)
 
 # Remove an item
-elsif not options[:delete].nil?
-	cli.delete(options[:delete], options[:force])
+elsif not options[:delete].nil? and not options[:id].nil?
+	cli.delete(options[:id], options[:force])
 
 # Update an item
-elsif not options[:update].nil?
-	cli.update(options[:update])
+elsif not options[:update].nil? and not options[:id].nil?
+	cli.update(options[:id])
 
 # Add a new item
 elsif not options[:add].nil?

From 22676a8b79b8480b8c32798378d94befdbb698c9 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 14:36:57 +0200
Subject: [PATCH 210/531] add public key in wallet

---
 bin/mpw           | 10 +++++++++-
 lib/mpw/mpw.rb    | 15 +++++++++++++--
 lib/mpw/ui/cli.rb |  9 ++++++++-
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 6d92958..0663c6a 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -92,6 +92,10 @@ OptionParser.new do |opts|
 		options[:import] = file
 	end
 
+	opts.on('-k', '--key KEY', I18n.t('option.import')) do |key|
+		options[:key] = key
+	end
+
 	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
 		options[:sync] = false
 	end
@@ -150,9 +154,13 @@ elsif not options[:update].nil? and not options[:id].nil?
 	cli.update(options[:id])
 
 # Add a new item
-elsif not options[:add].nil?
+elsif not options[:add].nil? and options[:key].nil?
 	cli.add
 
+# Add a new public key in wallet
+elsif not options[:add].nil? and not options[:key].nil?
+	cli.add_key(options[:key])
+
 # Export
 elsif not options[:export].nil?
 	cli.export(options[:export])
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 230ff53..127da7f 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -112,7 +112,7 @@ class MPW
 
 			@keys.each do |id, key|
 				tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
-					io.write(password)
+					io.write(key)
 				end
 			end
 		end
@@ -131,6 +131,18 @@ class MPW
 		@passwords[id] = encrypt(password)
 	end
 
+	# Add public key
+	# args: key ->  new public key
+	def add_key(key)
+		data = GPGME::Key.export(key).read
+
+		if data.to_s.empty?
+			raise I18n.t('error.export_key')
+		end
+
+		@keys[key] = data
+	end
+
 	# TODO
 	def check_config
 		if false
@@ -150,7 +162,6 @@ class MPW
 		end
 	end
 
-
 	# Search in some csv data
 	# @args: options -> a hash with paramaters
 	# @rtrn: a list with the resultat of the search
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 57063de..4d36cd8 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -157,7 +157,6 @@ class Cli
 	def get_wallet(wallet=nil)
 		if wallet.to_s.empty?
 			wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
-			puts wallets
 
 			case wallets.length
 			when 0
@@ -186,6 +185,14 @@ class Cli
 		end
 	end
 
+	# Add new public key
+	def add_key(key)
+		@mpw.add_key(key)
+		@mpw.write_data
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #13: #{e}".red
+	end
+
 	# Form to add a new item
 	def add
 		options = {}

From 812a02d60e5aa1e5a23fc853f30907d0856b7246 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 14:53:37 +0200
Subject: [PATCH 211/531] add backup when write data

---
 lib/mpw/mpw.rb | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 127da7f..29f8bb6 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -77,9 +77,9 @@ class MPW
 	end
 
 	# Encrypt a file
-	# TODO backup and restore file with raise
 	def write_data
-		data = {}
+		data     = {}
+		tmp_file = "#{@wallet_file}.tmp"
 
 		@data.each do |item|
 			next if item.empty?
@@ -98,7 +98,7 @@ class MPW
 			           )
 		end
 
-		Gem::Package::TarWriter.new(File.open(@wallet_file, 'w+')) do |tar|
+		Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
 			data_encrypt = encrypt(YAML::dump(data))
 			tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
 				io.write(data_encrypt)
@@ -116,6 +116,12 @@ class MPW
 				end
 			end
 		end
+
+		File.rename(tmp_file, @wallet_file)
+	rescue Exception => e
+		File.unlink(tmp_file)
+
+		raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
 	end
 
 	# Get a password

From ad1713edcbe2c9fc5058b27859c76be321a8b3a9 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 15:05:14 +0200
Subject: [PATCH 212/531] add class cli in module mpw

---
 bin/mpw           | 2 +-
 lib/mpw/ui/cli.rb | 6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 0663c6a..669a0f0 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -124,7 +124,7 @@ end.parse!
 config      = MPW::Config.new(options[:config])
 check_error = config.checkconfig
 
-cli = Cli.new(config)
+cli = MPW::Cli.new(config)
 	
 # Setup a new config 
 if not check_error or not options[:setup].nil?
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 4d36cd8..3dd85a1 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -11,6 +11,7 @@ require 'highline/import'
 require "#{APP_ROOT}/../lib/mpw/item.rb"
 require "#{APP_ROOT}/../lib/mpw/mpw.rb"
 
+module MPW
 class Cli
 
 	# Constructor
@@ -88,7 +89,7 @@ class Cli
 	def decrypt
 		if not defined?(@mpw)
 			@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
-			@mpw = MPW::MPW.new(@config.key, @wallet_file, @password)
+			@mpw = MPW.new(@config.key, @wallet_file, @password)
 		end
 
 		@mpw.read_data
@@ -208,7 +209,7 @@ class Cli
 		options[:port]     = ask(I18n.t('form.add.port')).to_s
 		options[:comment]  = ask(I18n.t('form.add.comment')).to_s
 
-		item = MPW::Item.new(options)
+		item = Item.new(options)
 
 		@mpw.add(item)
 		@mpw.set_password(item.id, password)
@@ -299,3 +300,4 @@ class Cli
 		puts "#{I18n.t('display.error')} #18: #{e}".red
 	end
 end
+end

From 7759c3e1ebfa435357df641b6f9c6e177a970065 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 16:27:55 +0200
Subject: [PATCH 213/531] minor fix for add key pub

---
 lib/mpw/mpw.rb    | 2 +-
 lib/mpw/ui/cli.rb | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 29f8bb6..9d9d14f 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -140,7 +140,7 @@ class MPW
 	# Add public key
 	# args: key ->  new public key
 	def add_key(key)
-		data = GPGME::Key.export(key).read
+		data = GPGME::Key.export(key, armor: true).read
 
 		if data.to_s.empty?
 			raise I18n.t('error.export_key')
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 3dd85a1..378c290 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -190,6 +190,8 @@ class Cli
 	def add_key(key)
 		@mpw.add_key(key)
 		@mpw.write_data
+
+		puts "#{I18n.t('key.add.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #13: #{e}".red
 	end

From 22239c5db456805c4e214a36f24704360f1d7e79 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 17:47:24 +0200
Subject: [PATCH 214/531] remove a pub key

---
 bin/mpw           |  5 ++++-
 lib/mpw/mpw.rb    |  8 +++++++-
 lib/mpw/ui/cli.rb | 14 +++++++++++++-
 3 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 669a0f0..d74f20a 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -161,6 +161,10 @@ elsif not options[:add].nil? and options[:key].nil?
 elsif not options[:add].nil? and not options[:key].nil?
 	cli.add_key(options[:key])
 
+# Delete a public key in wallet
+elsif not options[:delete].nil? and not options[:key].nil?
+	cli.delete_key(options[:key])
+
 # Export
 elsif not options[:export].nil?
 	cli.export(options[:export])
@@ -169,7 +173,6 @@ elsif not options[:export].nil?
 elsif not options[:import].nil?
 	cli.import(options[:import])
 
-# Interactive mode
 end
 
 cli = nil
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 9d9d14f..f5dc2a2 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -137,7 +137,7 @@ class MPW
 		@passwords[id] = encrypt(password)
 	end
 
-	# Add public key
+	# Add a public key
 	# args: key ->  new public key
 	def add_key(key)
 		data = GPGME::Key.export(key, armor: true).read
@@ -149,6 +149,12 @@ class MPW
 		@keys[key] = data
 	end
 
+	# Delete a public key
+	# args: key ->  public key to delete
+	def delete_key(key)
+		@keys.delete(key)
+	end
+
 	# TODO
 	def check_config
 		if false
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 378c290..682747f 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -186,7 +186,8 @@ class Cli
 		end
 	end
 
-	# Add new public key
+	# Add a new public key
+	# args: key -> the key name to add
 	def add_key(key)
 		@mpw.add_key(key)
 		@mpw.write_data
@@ -196,6 +197,17 @@ class Cli
 		puts "#{I18n.t('display.error')} #13: #{e}".red
 	end
 
+	# Add new public key
+	# args: key -> the key name to delete
+	def delete_key(key)
+		@mpw.delete_key(key)
+		@mpw.write_data
+
+		puts "#{I18n.t('key.delete.valid')}".green
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #15: #{e}".red
+	end
+
 	# Form to add a new item
 	def add
 		options = {}

From 731017e223cf47d353ceeb9cb8187e2d02d333ec Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 7 May 2016 18:47:21 +0200
Subject: [PATCH 215/531] remove unused lib

---
 lib/mpw/mpw.rb | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f5dc2a2..c66fa3b 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -5,7 +5,6 @@
 require 'rubygems/package'
 require 'gpgme'
 require 'i18n'
-require 'fileutils'
 require 'yaml'
 
 #TODO

From 0878155bcf18e981727a4ca9316f24f8568e322f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 09:35:02 +0200
Subject: [PATCH 216/531] add option -f for file

---
 bin/mpw | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index d74f20a..cb3bdf4 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -62,11 +62,15 @@ OptionParser.new do |opts|
 		options[:delete] = true
 	end
 
-	opts.on('-e', '--export FILE', I18n.t('option.export')) do |file|
-		options[:export] = file
+	opts.on('-e', '--export', I18n.t('option.export')) do
+		options[:export] = true
 	end
 
-	opts.on('-f', '--force', I18n.t('option.force')) do
+	opts.on('-f', '--file', I18n.t('option.file')) do |file|
+		options[:file] = file
+	end
+
+	opts.on('-F', '--force', I18n.t('option.force')) do
 		options[:force] = true
 	end
 
@@ -88,11 +92,11 @@ OptionParser.new do |opts|
 		options[:id] = id
 	end
 
-	opts.on('-I', '--import FILE', I18n.t('option.import')) do |file|
-		options[:import] = file
+	opts.on('-I', '--import', I18n.t('option.import')) do
+		options[:import] = true
 	end
 
-	opts.on('-k', '--key KEY', I18n.t('option.import')) do |key|
+	opts.on('-k', '--key KEY', I18n.t('option.key')) do |key|
 		options[:key] = key
 	end
 
@@ -166,12 +170,12 @@ elsif not options[:delete].nil? and not options[:key].nil?
 	cli.delete_key(options[:key])
 
 # Export
-elsif not options[:export].nil?
-	cli.export(options[:export])
+elsif not options[:export].nil? and not options[:file].nil?
+	cli.export(options[:file])
 
 # Add a new item
-elsif not options[:import].nil?
-	cli.import(options[:import])
+elsif not options[:import].nil? and not options[:file].nil?
+	cli.import(options[:file])
 
 end
 

From 0bf8d4e4ba498e4992da48894409d268296d8963 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 10:13:25 +0200
Subject: [PATCH 217/531] add import gpg file

---
 bin/mpw           |  4 ++--
 lib/mpw/mpw.rb    | 13 ++++++++++---
 lib/mpw/ui/cli.rb |  5 +++--
 3 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index cb3bdf4..a185b49 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -66,7 +66,7 @@ OptionParser.new do |opts|
 		options[:export] = true
 	end
 
-	opts.on('-f', '--file', I18n.t('option.file')) do |file|
+	opts.on('-f', '--file FILE', I18n.t('option.file')) do |file|
 		options[:file] = file
 	end
 
@@ -163,7 +163,7 @@ elsif not options[:add].nil? and options[:key].nil?
 
 # Add a new public key in wallet
 elsif not options[:add].nil? and not options[:key].nil?
-	cli.add_key(options[:key])
+	cli.add_key(options[:key], options[:file])
 
 # Delete a public key in wallet
 elsif not options[:delete].nil? and not options[:key].nil?
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index c66fa3b..2c988c5 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -138,8 +138,15 @@ class MPW
 
 	# Add a public key
 	# args: key ->  new public key
-	def add_key(key)
-		data = GPGME::Key.export(key, armor: true).read
+	#       file -> public gpg file to import
+	def add_key(key, file=nil)
+		if not file.nil? and File.exists?(file)
+			data = File.open(file).read
+			GPGME::Key.import(data, armor: true)
+			puts GPGME::Key.find(key)[0].trust
+		else
+			data = GPGME::Key.export(key, armor: true).read
+		end
 
 		if data.to_s.empty?
 			raise I18n.t('error.export_key')
@@ -298,7 +305,7 @@ class MPW
 	private
 	def encrypt(data)
 		recipients = []
-		crypto     = GPGME::Crypto.new(armor: true)
+		crypto     = GPGME::Crypto.new(armor: true, always_trust: true)
 
 		@keys.each_key do |key|
 			recipients.push(key)
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 682747f..a3b846b 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -188,8 +188,9 @@ class Cli
 
 	# Add a new public key
 	# args: key -> the key name to add
-	def add_key(key)
-		@mpw.add_key(key)
+	#       file -> gpg public file to import
+	def add_key(key, file=nil)
+		@mpw.add_key(key, file)
 		@mpw.write_data
 
 		puts "#{I18n.t('key.add.valid')}".green

From 855640c93252dfef8ce69790fb8f4468d0fc6f20 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 10:25:16 +0200
Subject: [PATCH 218/531] auto import public key in mpw file

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 2c988c5..f99153b 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -45,7 +45,13 @@ class MPW
 						data = decrypt(f.read)
 
 					when /^wallet\/keys\/(?<key>.+)\.pub$/
-						@keys[Regexp.last_match('key')] = f.read
+						key = Regexp.last_match('key')
+
+						if GPGME::Key.find(:public, key).length == 0
+							GPGME::Key.import(f.read, armor: true)
+						end
+
+						@keys[key] = f.read
 
 					when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
 						@passwords[Regexp.last_match('id')] = f.read
@@ -143,7 +149,6 @@ class MPW
 		if not file.nil? and File.exists?(file)
 			data = File.open(file).read
 			GPGME::Key.import(data, armor: true)
-			puts GPGME::Key.find(key)[0].trust
 		else
 			data = GPGME::Key.export(key, armor: true).read
 		end

From dd0e236fc56922cb71fed89e70d602da3ac01009 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 11:22:08 +0200
Subject: [PATCH 219/531] remove mpw server protocol

---
 bin/mpw-server      |  78 ----------------------------
 lib/mpw/sync.rb     |   3 --
 lib/mpw/sync/mpw.rb | 124 --------------------------------------------
 3 files changed, 205 deletions(-)
 delete mode 100755 bin/mpw-server
 delete mode 100644 lib/mpw/sync/mpw.rb

diff --git a/bin/mpw-server b/bin/mpw-server
deleted file mode 100755
index 3a7ce5a..0000000
--- a/bin/mpw-server
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'rubygems'
-require 'optparse'
-require 'pathname'
-require 'locale'
-require 'i18n'
-require 'mpw/server'
-
-APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-
-# --------------------------------------------------------- #
-# Set local
-# --------------------------------------------------------- #
-
-lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-
-if defined?(I18n.enforce_available_locales)
-	I18n.enforce_available_locales = true
-end
-
-I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path = Dir["#{APP_ROOT}/../i18n/server/*.yml"]
-I18n.default_locale = :en
-I18n.locale = lang.to_sym
-
-# --------------------------------------------------------- #
-# Options
-# --------------------------------------------------------- #
-
-options = {}
-OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
-
-	opts.on("-c", "--config CONFIG", I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
-
-	opts.on("-t", "--checkconfig", I18n.t('option.checkconfig')) do |b|
-		options[:checkconfig] = b
-	end
-
-	opts.on("-s", "--setup", I18n.t('option.setup')) do |b|
-		options[:setup] = b
-	end
-
-	opts.on("-h", "--help", I18n.t('option.help')) do |b|
-		puts opts
-		exit 0
-	end
-end.parse!
-
-# --------------------------------------------------------- #
-# Main
-# --------------------------------------------------------- #
-
-if options[:config].nil? or options[:config].empty?
-	puts "#{I18n.t('option.usage')}: mpw-server -c CONFIG [options]"
-	exit 2
-end
-
-server = MPW::Server.new
-
-if options[:checkconfig]
-	server.checkconfig(options[:config])
-elsif options[:setup]
-	server.setup(options[:config])
-else
-	if server.checkconfig(options[:config])
-		server.start
-	end
-end
-
-server = nil
-exit 0
diff --git a/lib/mpw/sync.rb b/lib/mpw/sync.rb
index 3e18d32..f271d79 100644
--- a/lib/mpw/sync.rb
+++ b/lib/mpw/sync.rb
@@ -30,9 +30,6 @@ module MPW
 		# @rtrn: true if get the date, else false
 		def get_remote
 			case @config.sync_type
-			when 'mpw'
-				require 'mpw/sync/mpw'
-				@sync = SyncMPW.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
 			when 'sftp', 'scp', 'ssh'
 				require 'mpw/sync/ssh'
 				@sync = SyncSSH.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
diff --git a/lib/mpw/sync/mpw.rb b/lib/mpw/sync/mpw.rb
deleted file mode 100644
index 596414d..0000000
--- a/lib/mpw/sync/mpw.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-
-require 'rubygems'
-require 'i18n'
-require 'socket'
-require 'json'
-require 'timeout'
-	
-module MPW
-	class SyncMPW
-	
-		attr_accessor :error_msg
-		attr_accessor :enable
-	
-		# Constructor
-		# @args: host -> the server host
-		#        port -> ther connection port
-		#        gpg_key -> the gpg key
-		#        password -> the remote password
-		#        suffix -> the suffix file
-		def initialize(host, user, password, suffix, port=nil)
-			@error_msg = nil
-			@enable    = false
-
-			@host     = host
-			@port     = !port.instance_of?(Integer) ? 2201 : port
-			@gpg_key  = user
-			@password = password
-			@suffix   = suffix
-		end
-	
-		# Connect to server
-		# @rtrn: false if the connection fail
-		def connect
-			Timeout.timeout(10) do
-				begin
-					TCPSocket.open(@host, @port) do 
-						@enable = true
-					end
-                rescue Errno::ENETUNREACH
-						retry
-				end
-			end
-		rescue Timeout::Error
-			@error_msg = "#{I18n.t('error.timeout')}\n#{e}"
-			@enable    = false
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-			@enable    = false
-		else
-			return @enable
-		end
-	
-		# Get data on server
-		# @args: gpg_password -> the gpg password
-		# @rtrn: nil if nothing data or error
-		def get(file_tmp)
-			return false if not @enable
-		
-			msg = nil
-			TCPSocket.open(@host, @port) do |socket|
-				send_msg = {action:  'get',
-				            gpg_key:  @gpg_key,
-				            password: @password,
-				            suffix:   @suffix
-				           }
-				
-				socket.puts send_msg.to_json
-				msg = JSON.parse(socket.gets)
-			end
-			
-			if not defined?(msg['error'])
-				@error_msg = I18n.t('error.sync.communication')
-				return false
-			elsif not msg['error'].nil?
-				@error_msg = I18n.t(msg['error'])
-				return false
-			end
-
-			File.open(file_tmp, 'w') do |file|
-				file << msg['data']
-			end
-
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-			return false
-		end
-	
-		# Update the remote data
-		# @args: data -> the data to send on server
-		# @rtrn: false if there is a problem
-		def update(file_gpg)
-			return true if not @enable
-	
-			data = File.open(file_gpg, 'r').read
-
-			msg = nil
-			TCPSocket.open(@host, @port) do |socket|
-				send_msg = {action:   'update',
-				            gpg_key:  @gpg_key,
-				            password: @password,
-				            suffix:   @suffix,
-				            data:     data
-				           }
-				
-				socket.puts send_msg.to_json
-				msg = JSON.parse(socket.gets)
-			end
-	
-			if not defined?(msg['error'])
-				@error_msg = I18n.t('error.sync.communication')
-				return false
-			elsif msg['error'].nil?
-				return true
-			else
-				@error_msg = I18n.t(msg['error'])
-				return false
-			end
-		end
-	
-	end
-end

From 85c034b8b3a0ca193ffa45bc76437841328a5cd1 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 11:34:29 +0200
Subject: [PATCH 220/531] remove mpw-ssh

---
 bin/mpw-ssh          | 89 --------------------------------------------
 lib/mpw/ui/clissh.rb | 42 ---------------------
 2 files changed, 131 deletions(-)
 delete mode 100755 bin/mpw-ssh
 delete mode 100644 lib/mpw/ui/clissh.rb

diff --git a/bin/mpw-ssh b/bin/mpw-ssh
deleted file mode 100755
index b5f1252..0000000
--- a/bin/mpw-ssh
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'rubygems'
-require 'optparse'
-require 'pathname'
-require 'locale'
-require 'i18n'
-require 'mpw/ui/clissh'
-require 'mpw/config'
-
-# --------------------------------------------------------- #
-# Set local
-# --------------------------------------------------------- #
-
-APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-
-if defined?(I18n.enforce_available_locales)
-	I18n.enforce_available_locales = true
-end
-
-I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{APP_ROOT}/../i18n/cli/*.yml"]
-I18n.default_locale = :en
-I18n.locale         = lang.to_sym
-
-# --------------------------------------------------------- #
-# Options
-# --------------------------------------------------------- #
-
-options = {}
-OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
-
-	opts.on("-l", "--login LOGIN", I18n.t('ssh.option.login')) do |login|
-		options[:login] = login
-	end
-
-	opts.on("-s", "--server SERVER", I18n.t('ssh.option.server')) do |server|
-		options[:server] = server
-	end
-
-	opts.on("-p", "--port PORT", I18n.t('ssh.option.port')) do |port|
-		options[:port] = port	
-	end
-
-	opts.on('-c', '--config CONFIG', I18n.t('cli.option.config')) do |config|
-		options[:config] = config
-	end
-
-	opts.on("-h", "--help", I18n.t('ssh.option.help')) do
-		puts opts
-		exit 0
-	end
-end.parse!
-
-# --------------------------------------------------------- #
-# Main
-# --------------------------------------------------------- #
-
-config      = MPW::Config.new(options[:config])
-check_error = config.checkconfig
-
-cli         = CliSSH.new(config)
-cli.login   = options[:login]
-cli.server  = options[:server]
-cli.port    = options[:port]
-
-search     = ARGV[0]
-
-# Setup a new config 
-if not check_error 
-	cli.setup(lang)
-
-elsif ARGV.length < 1
-	puts "#{I18n.t('ssh.option.usage')}: mpw-ssh SEARCH [options]"
-	exit 2
-else
-	cli.decrypt
-	cli.sync
-	cli.ssh(search)
-end
-
-cli = nil
-
-exit 0
diff --git a/lib/mpw/ui/clissh.rb b/lib/mpw/ui/clissh.rb
deleted file mode 100644
index 0bf7bb0..0000000
--- a/lib/mpw/ui/clissh.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'mpw/ui/cli'
-
-class CliSSH < Cli
-
-	attr_accessor :server, :port, :login
-
-	# Connect to SSH
-	# args: search -> string to search
-	def ssh(search)
-		result = @mpw.list(search: search, protocol: 'ssh')
-
-		if result.length > 0
-			result.each do |item|
-				server = @server.nil? ? item.host : @server
-				port   = @port.nil?   ? item.port : @port
-				login  = @login.nil?  ? item.user : @login
-
-				passwd = item.password
-
-				if port.nil? and port.empty?
-					port = 22
-				end
-
-				puts "#{I18n.t('ssh.display.connect')} ssh #{login}@#{server} -p #{port}"
-				if passwd.empty?
-					system("ssh #{login}@#{server} -p #{port}")
-				else
-					system("sshpass -p '#{passwd}' ssh #{login}@#{server} -p #{port}")
-				end
-			end
-
-		else
-			puts I18n.t('ssh.display.nothing')
-		end
-	end
-end
-

From 923977ba04e4acccaffc13b2bcbb1f9e5c01129c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 12:06:06 +0200
Subject: [PATCH 221/531] add salt for password

---
 lib/mpw/mpw.rb | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f99153b..1dde6f4 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -132,13 +132,22 @@ class MPW
 	# Get a password
 	# args: id -> the item id
 	def get_password(id)
-		return decrypt(@passwords[id])
+		password = decrypt(@passwords[id])
+		
+		if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
+			return Regexp.last_match('password')
+		else
+			return password
+		end
 	end
 
 	# Set a password
 	# args: id -> the item id
 	#       password -> the new password
 	def set_password(id, password)
+		salt     = MPW::password(Random.rand(4..9))
+		password = "$#{salt}::#{password}"
+
 		@passwords[id] = encrypt(password)
 	end
 

From b6fb5cd486f35d61cf3ec2987f03f12b3dc2c88e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 22:06:25 +0200
Subject: [PATCH 222/531] encrypt config wallet

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 1dde6f4..f9d8fa6 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -37,8 +37,8 @@ class MPW
 		Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
 			tar.each do |f| 
 				case f.full_name
-					when 'wallet/config.yml'
-						@config = YAML.load(f.read)
+					when 'wallet/config.gpg'
+						@config = YAML.load(decrypt(f.read))
 						check_config
 
 					when 'wallet/meta.gpg'
@@ -109,6 +109,11 @@ class MPW
 				io.write(data_encrypt)
 			end
 
+			config = @config.to_yaml
+			tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
+				io.write(config)
+			end
+
 			@passwords.each do |id, password|
 				tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
 					io.write(password)

From 0a56019794e645cff3670ba1d5d2cab5b6d5da36 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 8 May 2016 22:24:39 +0200
Subject: [PATCH 223/531] remove error_msg

---
 lib/mpw/mpw.rb | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f9d8fa6..4b1bddc 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -13,11 +13,8 @@ require "#{APP_ROOT}/../lib/mpw/item.rb"
 module MPW
 class MPW
 
-	attr_accessor :error_msg
-	
 	# Constructor
 	def initialize(key, wallet_file, gpg_pass=nil)
-		@error_msg   = nil
 		@key         = key
 		@gpg_pass    = gpg_pass
 		@wallet_file = wallet_file

From b85d5109a1316e57c05c2f92b3779e3d6d350084 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 May 2016 16:48:57 +0200
Subject: [PATCH 224/531] add function get_last_sync

---
 lib/mpw/mpw.rb | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 4b1bddc..850be44 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -285,6 +285,13 @@ class MPW
 		raise "#{I18n.t('error.import.read', file: file)}\n#{e}"
 	end
 
+	# Get last sync
+	def get_last_sync
+		return @config['sync']['last_sync'].to_i
+	rescue
+		return 0
+	end
+
 	# Generate a random password
 	# @args: length -> the length password
 	# @rtrn: a random string

From 9b853a371a8436b0e82eaba27a5437e0d4773e66 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 May 2016 22:42:20 +0200
Subject: [PATCH 225/531] fix write config

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 850be44..2c671a8 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -101,12 +101,12 @@ class MPW
 		end
 
 		Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
-			data_encrypt = encrypt(YAML::dump(data))
+			data_encrypt = encrypt(data.to_yaml)
 			tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
 				io.write(data_encrypt)
 			end
 
-			config = @config.to_yaml
+			config = encrypt(@config.to_yaml)
 			tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
 				io.write(config)
 			end

From ec982ce4d80cb164350b1b45c8c1a0b2f0c899a3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 May 2016 22:43:57 +0200
Subject: [PATCH 226/531] add function for setup wallet config

---
 bin/mpw           |  9 ++++++++-
 lib/mpw/mpw.rb    |  6 ++++++
 lib/mpw/ui/cli.rb | 20 ++++++++++++++++++++
 3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/bin/mpw b/bin/mpw
index a185b49..830b2c2 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -2,7 +2,6 @@
 # author: nishiki
 # mail: nishiki@yaegashi.fr
 
-require 'rubygems'
 require 'optparse'
 require 'pathname'
 require 'locale'
@@ -119,6 +118,10 @@ OptionParser.new do |opts|
 	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
+
+	opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
+		options[:setup_wallet] = true
+	end
 end.parse!
 
 # --------------------------------------------------------- #
@@ -177,6 +180,10 @@ elsif not options[:export].nil? and not options[:file].nil?
 elsif not options[:import].nil? and not options[:file].nil?
 	cli.import(options[:file])
 
+# Setup wallet config
+elsif not options[:setup_wallet].nil?
+	cli.setup_wallet_config
+
 end
 
 cli = nil
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 2c671a8..9ae896e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -184,6 +184,12 @@ class MPW
 		end
 	end
 
+	# Set config
+	# args: config -> a hash with config options
+	def set_config(config)
+		@config = config
+	end
+
 	# Add a new item
 	# @args: item -> Object MPW::Item
 	def add(item)
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index a3b846b..ec99a7c 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -84,6 +84,26 @@ class Cli
 		end
 	end
 
+	# Setup wallet config for sync
+	def setup_wallet_config
+		config         = {}
+		config['sync'] = {}
+
+		puts I18n.t('form.setup.title')
+		puts '--------------------'
+		config['sync']['type'] = ask(I18n.t('form.setup.sync_type')).to_s
+		config['sync']['host'] = ask(I18n.t('form.setup.sync_host')).to_s
+		config['sync']['port'] = ask(I18n.t('form.setup.sync_port')).to_s
+		config['sync']['user'] = ask(I18n.t('form.setup.sync_user')).to_s
+		config['sync']['pwd']  = ask(I18n.t('form.setup.sync_pwd')).to_s
+		config['sync']['path'] = ask(I18n.t('form.setup.sync_path')).to_s
+
+		@mpw.set_config(config)
+		@mpw.write_data
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #10: #{e}".red
+		exit 2
+	end
 	
 	# Request the GPG password and decrypt the file
 	def decrypt

From e1de7b7704cd77ae002a19fb4749e0e4e7067f26 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 May 2016 22:44:57 +0200
Subject: [PATCH 227/531] rewrite class sync ssh

---
 lib/mpw/sync/ssh.rb | 113 ++++++++++++++++++--------------------------
 1 file changed, 45 insertions(+), 68 deletions(-)

diff --git a/lib/mpw/sync/ssh.rb b/lib/mpw/sync/ssh.rb
index 632e292..89a7fd5 100644
--- a/lib/mpw/sync/ssh.rb
+++ b/lib/mpw/sync/ssh.rb
@@ -2,79 +2,56 @@
 # author: nishiki
 # mail: nishiki@yaegashi.fr
 
-require 'rubygems'
 require 'i18n'
 require 'net/ssh'
 require 'net/sftp'
 	
 module MPW
-	class SyncSSH
-	
-		attr_accessor :error_msg
-		attr_accessor :enable
-	
-		# Constructor
-		# @args: host -> the server host
-		#        port -> ther connection port
-		#        gpg_key -> the gpg key
-		#        password -> the remote password
-		def initialize(host, user, password, path, port=nil)
-			@error_msg = nil
-			@enable    = false
-
-			@host      = host
-			@user      = user
-			@password  = password
-			@path      = path
-			@port      = port.instance_of?(Integer) ? 22 : port
-		end
-	
-		# Connect to server
-		# @rtrn: false if the connection fail
-		def connect
-			Net::SSH.start(@host, @user, password: @password, port: @port) do |ssh|
-				@enable = true
-			end
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-			@enable    = false
-		else
-			return @enable
-		end
-	
-		# Get data on server
-		# @args: gpg_password -> the gpg password
-		# @rtrn: nil if nothing data or error
-		def get(file_tmp)
-			return false if not @enable
-			
-			Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
-				sftp.lstat(@path) do |response|
-					sftp.download!(@path, file_tmp) if response.ok?
-				end
-			end
-
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-			return false
-		end
-	
-		# Update the remote data
-		# @args: file_gpg -> the data to send on server
-		# @rtrn: false if there is a problem
-		def update(file_gpg)
-			return true if not @enable
-	
-			Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
-				sftp.upload!(file_gpg, @path)
-			end
-
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
-			return false
-		end
+class SyncSSH
 
+	# Constructor
+	# @args: config -> the config
+	def initialize(config)
+		@host      = config['host']
+		@user      = config['user']
+		@password  = config['password']
+		@path      = config['path']
+		@port      = config['port'].instance_of?(Integer) ? 22 : config['port']
 	end
+
+	# Connect to server
+	# @rtrn: false if the connection fail
+	def connect
+		Net::SSH.start(@host, @user, password: @password, port: @port) do
+			break
+		end
+	rescue Exception => e
+		raise "#{I18n.t('error.sync.connection')}\n#{e}"
+	end
+
+	# Get data on server
+	# @args: gpg_password -> the gpg password
+	# @rtrn: nil if nothing data or error
+	def get(file_tmp)
+		Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
+			sftp.lstat(@path) do |response|
+				sftp.download!(@path, file_tmp) if response.ok?
+			end
+		end
+	rescue Exception => e
+		raise "#{I18n.t('error.sync.download')}\n#{e}"
+	end
+
+	# Update the remote data
+	# @args: file_gpg -> the data to send on server
+	# @rtrn: false if there is a problem
+	def update(file_gpg)
+		Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
+			sftp.upload!(file_gpg, @path)
+		end
+	rescue Exception => e
+		raise "#{I18n.t('error.sync.upload')}\n#{e}"
+	end
+
+end
 end

From 50422e07390f7b926b076c44e8bdf2ada7f28a20 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 May 2016 22:47:02 +0200
Subject: [PATCH 228/531] rewrite sync

---
 lib/mpw/mpw.rb    |  95 ++++++++++++++++++++++++++++++++
 lib/mpw/sync.rb   | 135 ----------------------------------------------
 lib/mpw/ui/cli.rb |   1 +
 3 files changed, 96 insertions(+), 135 deletions(-)
 delete mode 100644 lib/mpw/sync.rb

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 9ae896e..e488805 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -298,6 +298,101 @@ class MPW
 		return 0
 	end
 
+	# Sync data with remote file
+	def sync
+		return if @config['sync'].nil?
+		
+		tmp_file  = "#{@wallet_file}.sync"
+		last_sync = @config['last_sync'].to_i
+
+		case @config['sync']['type']
+		when 'sftp', 'scp', 'ssh'
+			require "#{APP_ROOT}/../lib/mpw/sync/ssh.rb"
+			sync = SyncSSH.new(@config['sync'])
+		when 'ftp'
+			require 'mpw/sync/ftp'
+			sync = SyncFTP.new(@config['sync'])
+		else
+			raise I18n.t('error.unknown_type')
+		end
+
+		sync.connect
+		sync.get(tmp_file)
+
+		remote = MPW.new(@key, @wallet_file, @gpg_pass)
+		remote.read_data
+
+		File.unlink(tmp_file) if File.exist?(tmp_file)
+
+		if not remote.to_s.empty?
+			@data.each do |item|
+				update = false
+
+				remote.list.each do |r|
+					next if item.id != r.id
+
+					# Update item
+					if item.last_edit < r.last_edit
+						item.update(name:      r.name,
+						            group:     r.group,
+						            host:      r.host,
+						            protocol:  r.protocol,
+						            user:      r.user,
+						            port:      r.port,
+						            comment:   r.comment
+						           )
+						set_password(item.id, r.get_password(item.id))
+					end
+
+					r.delete
+					update = true
+
+					break
+				end
+
+				# Remove an old item
+				if not update and item.last_sync.to_i < last_sync and item.last_edit < last_sync
+					item.delete
+				end
+			end
+		end
+
+		# Add item
+		remote.list.each do |r|
+			next if r.last_edit <= last_sync
+
+			item = Item.new(id:        r.id,
+			                name:      r.name,
+			                group:     r.group,
+			                host:      r.host,
+			                protocol:  r.protocol,
+			                user:      r.user,
+			                port:      r.port,
+			                comment:   r.comment,
+			                created:   r.created,
+			                last_edit: r.last_edit
+			               )
+
+			set_password(item.id, r.get_password(item.id))
+			add(item)
+		end
+
+		remote = nil
+
+		@data.each do |item|
+			item.set_last_sync
+		end
+
+		@config['sync']['last_sync'] = Time.now.to_i
+
+		write_data
+		sync.update(@wallet_file)
+	rescue Exception => e
+		File.unlink(tmp_file) if File.exist?(tmp_file)
+
+		raise "#{I18n.t('error.sync.unknown')}\n#{e}"
+	end
+
 	# Generate a random password
 	# @args: length -> the length password
 	# @rtrn: a random string
diff --git a/lib/mpw/sync.rb b/lib/mpw/sync.rb
deleted file mode 100644
index f271d79..0000000
--- a/lib/mpw/sync.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
-# info: a simple script who manage your passwords
-
-require 'rubygems'
-require 'i18n'
-require 'yaml'
-require 'tempfile'
-require 'mpw/mpw'
-require 'mpw/item'
-	
-module MPW
-	class Sync
-
-		attr_accessor :error_msg
-
-		# Constructor
-		# raise an exception if there is a bad parameter
-		def initialize(config, local, password=nil)
-			@error_msg = nil
-			@config    = config
-			@local     = local
-			@password  = password
-
-			raise I18n.t('error.class') if not @local.instance_of?(MPW)
-		end
-
-		# Get the data on remote host
-		# @rtrn: true if get the date, else false
-		def get_remote
-			case @config.sync_type
-			when 'sftp', 'scp', 'ssh'
-				require 'mpw/sync/ssh'
-				@sync = SyncSSH.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
-			when 'ftp'
-				require 'mpw/sync/ftp'
-				@sync = SyncFTP.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
-			else
-				@error_msg =  I18n.t('error.unknown_type')
-				return false
-			end
-
-			if not @sync.connect
-				@error_msg = @sync.error_msg
-				return false
-			end
-
-			
-			file_tmp = Tempfile.new('mpw-')
-			raise @sync.error_msg if not @sync.get(file_tmp.path)	
-
-			@remote = MPW.new(file_tmp.path, @config.key)
-			raise @remote.error_msg if not @remote.decrypt(@password)
-
-			file_tmp.close(true)
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.download')} #{e}"
-			file_tmp.close(true)
-			return false
-		end
-
-		# Sync remote data and local data
-		# raise an exception if there is a problem
-		def sync
-			
-			if not @remote.to_s.empty?
-				@local.list.each do |item|
-					update = false
-					@remote.list.each do |r|
-
-						# Update item
-						if item.id == r.id
-							if item.last_edit < r.last_edit
-								raise item.error_msg if not item.update(name:      r.name,
-								                                        group:     r.group,
-								                                        host:      r.host,
-								                                        protocol:  r.protocol,
-								                                        user:      r.user,
-								                                        password:  r.password,
-								                                        port:      r.port,
-								                                        comment:   r.comment
-								                                       )
-							end
-
-							r.delete
-							update = true
-
-							break
-						end
-					end
-
-					# Remove an old item
-					if not update and item.last_sync.to_i < @config.last_sync and item.last_edit < @config.last_sync
-						item.delete
-					end
-				end
-			end
-	
-			# Add item
-			@remote.list.each do |r|
-				if r.last_edit > @config.last_sync
-					item = Item.new(id:        r.id,
-					                name:      r.name,
-					                group:     r.group,
-					                host:      r.host,
-					                protocol:  r.protocol,
-					                user:      r.user,
-					                password:  r.password,
-					                port:      r.port,
-					                comment:   r.comment,
-					                created:   r.created,
-					                last_edit: r.last_edit
-					               )
-					raise @local.error_msg if not @local.add(item)
-				end
-			end
-
-			@local.list.each do |item|
-				item.set_last_sync
-			end
-
-			raise @mpw.error_msg  if not @local.encrypt 
-			raise @sync.error_msg if not @sync.update(@config.file_gpg)
-			
-			@config.set_last_sync
-
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.unknown')} #{e}"
-			return false
-		end
-	end
-end
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index ec99a7c..59da85b 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -113,6 +113,7 @@ class Cli
 		end
 
 		@mpw.read_data
+		@mpw.sync
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #11: #{e}".red
 		exit 2

From 684e0e32f038497ee1f8acd950383d6bda14771f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 12 May 2016 23:39:50 +0200
Subject: [PATCH 229/531] fix password for wallet config

---
 lib/mpw/ui/cli.rb | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 59da85b..686e897 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -91,12 +91,12 @@ class Cli
 
 		puts I18n.t('form.setup.title')
 		puts '--------------------'
-		config['sync']['type'] = ask(I18n.t('form.setup.sync_type')).to_s
-		config['sync']['host'] = ask(I18n.t('form.setup.sync_host')).to_s
-		config['sync']['port'] = ask(I18n.t('form.setup.sync_port')).to_s
-		config['sync']['user'] = ask(I18n.t('form.setup.sync_user')).to_s
-		config['sync']['pwd']  = ask(I18n.t('form.setup.sync_pwd')).to_s
-		config['sync']['path'] = ask(I18n.t('form.setup.sync_path')).to_s
+		config['sync']['type']      = ask(I18n.t('form.setup.sync_type')).to_s
+		config['sync']['host']      = ask(I18n.t('form.setup.sync_host')).to_s
+		config['sync']['port']      = ask(I18n.t('form.setup.sync_port')).to_s
+		config['sync']['user']      = ask(I18n.t('form.setup.sync_user')).to_s
+		config['sync']['password']  = ask(I18n.t('form.setup.sync_pwd')).to_s
+		config['sync']['path']      = ask(I18n.t('form.setup.sync_path')).to_s
 
 		@mpw.set_config(config)
 		@mpw.write_data

From 31751455c59c34e44b202793b6a29aafe3761e49 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 May 2016 15:51:17 +0200
Subject: [PATCH 230/531] secure the wallet config

---
 lib/mpw/mpw.rb | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index e488805..2408515 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -22,7 +22,7 @@ class MPW
 
 	# Read mpw file
 	def read_data
-		@config    = nil
+		@config    = {}
 		@data      = []
 		@keys      = {}
 		@passwords = {}
@@ -187,7 +187,15 @@ class MPW
 	# Set config
 	# args: config -> a hash with config options
 	def set_config(config)
-		@config = config
+		@config['sync'] = {} if @config['sync'].nil?
+
+		@config['sync']['type']      = config['sync']['type']
+		@config['sync']['host']      = config['sync']['host']
+		@config['sync']['port']      = config['sync']['port']
+		@config['sync']['user']      = config['sync']['user']
+		@config['sync']['password']  = config['sync']['password']
+		@config['sync']['path']      = config['sync']['path']
+		@config['sync']['last_sync'] = @config['sync']['last_sync'].nil? ? 0 : @config['sync']['last_sync']
 	end
 
 	# Add a new item
@@ -300,7 +308,7 @@ class MPW
 
 	# Sync data with remote file
 	def sync
-		return if @config['sync'].nil?
+		return if @config.empty? or @config['sync'].nil?
 		
 		tmp_file  = "#{@wallet_file}.sync"
 		last_sync = @config['last_sync'].to_i

From 1399a17c8e23a3dff8f53c9feab8571927104a5f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 May 2016 15:51:49 +0200
Subject: [PATCH 231/531] mutliple fixes for sync

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 2408515..9197307 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -327,7 +327,7 @@ class MPW
 		sync.connect
 		sync.get(tmp_file)
 
-		remote = MPW.new(@key, @wallet_file, @gpg_pass)
+		remote = MPW.new(@key, tmp_file, @gpg_pass)
 		remote.read_data
 
 		File.unlink(tmp_file) if File.exist?(tmp_file)
@@ -349,7 +349,7 @@ class MPW
 						            port:      r.port,
 						            comment:   r.comment
 						           )
-						set_password(item.id, r.get_password(item.id))
+						set_password(item.id, remote.get_password(item.id))
 					end
 
 					r.delete
@@ -381,7 +381,7 @@ class MPW
 			                last_edit: r.last_edit
 			               )
 
-			set_password(item.id, r.get_password(item.id))
+			set_password(item.id, remote.get_password(item.id))
 			add(item)
 		end
 

From b51144758dd1f9a3a8bbd3c4262ae1c910b29555 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 May 2016 16:05:09 +0200
Subject: [PATCH 232/531] automaticly add our public key in wallet

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 9197307..b8f315c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -74,6 +74,8 @@ class MPW
 				          )
 			end
 		end
+
+		add_key(@key) if @keys[@key].nil?
 	rescue Exception => e
 		raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
 	end

From 460450ce082464ef3914698799c1cd2c3121c0d3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 13 May 2016 16:13:14 +0200
Subject: [PATCH 233/531] add sync after add, update or delete action

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

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 686e897..6e301f4 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -213,6 +213,7 @@ class Cli
 	def add_key(key, file=nil)
 		@mpw.add_key(key, file)
 		@mpw.write_data
+		@mpw.sync
 
 		puts "#{I18n.t('key.add.valid')}".green
 	rescue Exception => e
@@ -224,6 +225,7 @@ class Cli
 	def delete_key(key)
 		@mpw.delete_key(key)
 		@mpw.write_data
+		@mpw.sync
 
 		puts "#{I18n.t('key.delete.valid')}".green
 	rescue Exception => e
@@ -250,6 +252,7 @@ class Cli
 		@mpw.add(item)
 		@mpw.set_password(item.id, password)
 		@mpw.write_data
+		@mpw.sync
 
 		puts "#{I18n.t('form.add.valid')}".green
 	end
@@ -278,6 +281,7 @@ class Cli
 			item.update(options)
 			@mpw.set_password(item.id, password) if not password.empty?
 			@mpw.write_data
+			@mpw.sync
 
 			puts "#{I18n.t('form.update.valid')}".green
 		else
@@ -309,6 +313,7 @@ class Cli
 
 		item.delete
 		@mpw.write_data
+		@mpw.sync
 
 		puts "#{I18n.t('form.delete.valid', id: id)}".green
 	rescue

From 81da0407d02193a16a9acc96875eb799f5b904d8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 12:10:24 +0200
Subject: [PATCH 234/531] remove search with protocol

---
 bin/mpw        | 1 -
 lib/mpw/mpw.rb | 3 ---
 2 files changed, 4 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 830b2c2..97b846d 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -147,7 +147,6 @@ cli.decrypt
 if not options[:show].nil?
 	opts = {search:    options[:show],
 	        group:     options[:group],
-	        protocol:  options[:protocol],
 	       }
 
 	cli.display(opts)
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index b8f315c..767522a 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -220,13 +220,10 @@ class MPW
 
 		search   = options[:search].to_s.downcase
 		group    = options[:group].to_s.downcase
-		protocol = options[:protocol].to_s.downcase
 
 		@data.each do |item|
 			next if item.empty?
-
 			next if not group.empty?    and not group.eql?(item.group.downcase)
-			next if not protocol.empty? and not protocol.eql?(item.protocol.downcase)
 			
 			name    = item.name.to_s.downcase
 			host    = item.host.to_s.downcase

From e29346cbb3a316e3ffb461e93417e5cd0182ca99 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 12:13:04 +0200
Subject: [PATCH 235/531] translation fr option

---
 i18n/cli/fr.yml | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index e9790c0..38a39d4 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -38,24 +38,26 @@ fr:
     select: "Votre choix n'est pas un élément valide!"
 
   option:
-    usage: "Utilisation"
+    add: "Ajoute un élément ou une clé"
+    config: "Spécifie le fichier de configuration à utiliser"
+    export: "Exporte un portefeuille dans un fichier yaml"
+    file: "Spécifie un fichier, à utiliser avec les options [--import | --export | --add]"
+    force: "Ne demande pas de confirmation pour la supprésion d'un élément"
+    generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
+    group: "Recherche les éléments appartenant au groupe spécifié"
+    help: "Affiche ce message d'aide"
+    id: "Spécifie un identifiant, à utiliser avec les options [--delete | --update]"
+    import: "Importe des éléments depuis un fichier yaml"
+    key: "Spécifie le nom d'une clé, à utiliser avec les options [--add | --delete | --update]"
+    no_sync: "Désactive la synchronisation avec le serveur"
+    setup: "Création d'un nouveau fichier de configuration"
+    setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
     show: "Recherche et affiche les éléments"
     show_all: "Affiche tous les éléments"
-    update: "Met à jour un élément"
     remove: "Supprime un élément"
-    group: "Recherche les éléments appartenant au groupe spécifié"
-    add: "Ajoute un élément"
-    config: "Spécifie le fichier de configuration à utiliser"
-    setup: "Création d'un nouveau fichier de configuration" 
-    protocol: "Sélectionne les éléments ayant le protocole spécifié"
-    export: "Exporte tous les éléments dans un fichier"
-    type: "Format des données du fichier d'export [csv|yaml]"
-    import: "Importe des éléments depuis un fichier yaml ou csv"
-    force: "Force une action, l'action ne demandera pas de confirmation"
-    no_sync: "Désactive la synchronisation avec le serveur"
-    format: "Change le format d'affichage des éléments par un alternatif"
-    generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
-    help: "Affiche ce message d'aide"
+    update: "Met à jour un élément"
+    usage: "Utilisation"
+    wallet: "Spécifie le portefeuille à utiliser"
 
   form:
     select: "Sélectionner l'élément: "

From ea4c657036667be3262c0a963fd7908807e701fb Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 13:19:50 +0200
Subject: [PATCH 236/531] translation en option

---
 i18n/cli/en.yml | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/i18n/cli/en.yml b/i18n/cli/en.yml
index bd84171..52d00cf 100644
--- a/i18n/cli/en.yml
+++ b/i18n/cli/en.yml
@@ -38,24 +38,28 @@ en:
     select: 'Your choice is not a valid element!'
 
   option:
-    usage: "Usage"
+    add: "Add an item or key"
+    config: "Specify the configuration file to use"
+    export: "Export a wallet in an yaml file"
+    file: "Specify a file, to use with the options [--import | --export | --add]"
+    force: "No ask to confirm when you delete an item"
+    generate_password: "Generate a random password (default 8 characters)"
+    group: "Search the items with specified group"
+    help: "Affiche ce message d'aide"
+    id: "Specify an id, to use with the options [--delete | --update]"
+    import: "Import item since a yaml file"
+    key: "Specify the key name, to use with the options [--add | --delete | --update]"
+    no_sync: "Disable synchronization with the server"
+    setup: "Create a new configuration file" 
+    setup_wallet: "Create a new configuration file for a wallet"
     show: "Search and show the items"
     show_all: "Show all items"
-    update: "Update an item"
     remove: "Delete an item"
-    group: "Search the items with specified group"
-    add: "Add an item"
-    config: "Specify the configuration file to use"
-    setup: "Create a new configuration file" 
-    protocol: "Select the items with the specified protocol"
-    export: "Export all items in a file"
-    type: "Data's type export file [csv|yaml]"
-    import: "Import item since a yaml or csv file"
-    force: "Force an action"
-    no_sync: "Disable synchronization with the server"
-    format: "Change the display items format by an alternative format"
-    generate_password: "Generate a random password (default 8 characters)"
-    help: "Show this help message"
+    update: "Update an item"
+    usage: "Usage"
+    wallet: "Specify a wallet to use"
+
+   help: "Show this help message"
 
   form:
     select: "Select the item: "

From 122f2e3393dac3e5ab4a7e20bcfb9507f057014c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 17:01:44 +0200
Subject: [PATCH 237/531] new name for translation in cli

---
 lib/mpw/ui/cli.rb | 82 ++++++++++++++++++++++++-----------------------
 1 file changed, 42 insertions(+), 40 deletions(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 6e301f4..9167c4d 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -23,11 +23,11 @@ class Cli
 	# Create a new config file
 	# @args: lang -> the software language
 	def setup(lang)
-		puts I18n.t('form.setup.title')
+		puts I18n.t('form.setup_config.title')
 		puts '--------------------'
-		language   = ask(I18n.t('form.setup.lang', lang: lang)).to_s
-		key        = ask(I18n.t('form.setup.gpg_key')).to_s
-		wallet_dir = ask(I18n.t('form.setup.wallet_dir')).to_s
+		language   = ask(I18n.t('form.setup_config.lang', lang: lang)).to_s
+		key        = ask(I18n.t('form.setup_config.gpg_key')).to_s
+		wallet_dir = ask(I18n.t('form.setup_config.wallet_dir', home: "#{@config.config_dir}")).to_s
 
 		if language.nil? or language.empty?
 			language = lang
@@ -35,7 +35,7 @@ class Cli
 		I18n.locale = language.to_sym
 
 		if @config.setup(key, lang, wallet_dir)
-			puts "#{I18n.t('form.setup.valid')}".green
+			puts "#{I18n.t('form.setup_config.valid')}".green
 		else
 			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}".red
 			exit 2
@@ -89,17 +89,19 @@ class Cli
 		config         = {}
 		config['sync'] = {}
 
-		puts I18n.t('form.setup.title')
+		puts I18n.t('form.setup_wallet.title')
 		puts '--------------------'
-		config['sync']['type']      = ask(I18n.t('form.setup.sync_type')).to_s
-		config['sync']['host']      = ask(I18n.t('form.setup.sync_host')).to_s
-		config['sync']['port']      = ask(I18n.t('form.setup.sync_port')).to_s
-		config['sync']['user']      = ask(I18n.t('form.setup.sync_user')).to_s
-		config['sync']['password']  = ask(I18n.t('form.setup.sync_pwd')).to_s
-		config['sync']['path']      = ask(I18n.t('form.setup.sync_path')).to_s
+		config['sync']['type']      = ask(I18n.t('form.setup_wallet.sync_type')).to_s
+		config['sync']['host']      = ask(I18n.t('form.setup_wallet.sync_host')).to_s
+		config['sync']['port']      = ask(I18n.t('form.setup_wallet.sync_port')).to_s
+		config['sync']['user']      = ask(I18n.t('form.setup_wallet.sync_user')).to_s
+		config['sync']['password']  = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
+		config['sync']['path']      = ask(I18n.t('form.setup_wallet.sync_path')).to_s
 
 		@mpw.set_config(config)
 		@mpw.write_data
+
+		puts "#{I18n.t('form.setup_wallet.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #10: #{e}".red
 		exit 2
@@ -215,7 +217,7 @@ class Cli
 		@mpw.write_data
 		@mpw.sync
 
-		puts "#{I18n.t('key.add.valid')}".green
+		puts "#{I18n.t('form.add_key.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #13: #{e}".red
 	end
@@ -227,7 +229,7 @@ class Cli
 		@mpw.write_data
 		@mpw.sync
 
-		puts "#{I18n.t('key.delete.valid')}".green
+		puts "#{I18n.t('form.delete_key.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #15: #{e}".red
 	end
@@ -236,16 +238,16 @@ class Cli
 	def add
 		options = {}
 
-		puts I18n.t('form.add.title')
+		puts I18n.t('form.add_item.title')
 		puts '--------------------'
-		options[:name]     = ask(I18n.t('form.add.name')).to_s
-		options[:group]    = ask(I18n.t('form.add.group')).to_s
-		options[:host]     = ask(I18n.t('form.add.server')).to_s
-		options[:protocol] = ask(I18n.t('form.add.protocol')).to_s
-		options[:user]     = ask(I18n.t('form.add.login')).to_s
-		password           = ask(I18n.t('form.add.password')).to_s
-		options[:port]     = ask(I18n.t('form.add.port')).to_s
-		options[:comment]  = ask(I18n.t('form.add.comment')).to_s
+		options[:name]     = ask(I18n.t('form.add_item.name')).to_s
+		options[:group]    = ask(I18n.t('form.add_item.group')).to_s
+		options[:host]     = ask(I18n.t('form.add_item.server')).to_s
+		options[:protocol] = ask(I18n.t('form.add_item.protocol')).to_s
+		options[:user]     = ask(I18n.t('form.add_item.login')).to_s
+		password           = ask(I18n.t('form.add_item.password')).to_s
+		options[:port]     = ask(I18n.t('form.add_item.port')).to_s
+		options[:comment]  = ask(I18n.t('form.add_item.comment')).to_s
 
 		item = Item.new(options)
 
@@ -254,7 +256,7 @@ class Cli
 		@mpw.write_data
 		@mpw.sync
 
-		puts "#{I18n.t('form.add.valid')}".green
+		puts "#{I18n.t('form.add_item.valid')}".green
 	end
 
 	# Update an item
@@ -265,16 +267,16 @@ class Cli
 		if not item.nil?
 			options = {}
 
-			puts I18n.t('form.update.title')
+			puts I18n.t('form.update_item.title')
 			puts '--------------------'
-			options[:name]     = ask(I18n.t('form.update.name'    , name:     item.name)).to_s
-			options[:group]    = ask(I18n.t('form.update.group'   , group:    item.group)).to_s
-			options[:host]     = ask(I18n.t('form.update.server'  , server:   item.host)).to_s
-			options[:protocol] = ask(I18n.t('form.update.protocol', protocol: item.protocol)).to_s
-			options[:user]     = ask(I18n.t('form.update.login'   , login:    item.user)).to_s
-			password           = ask(I18n.t('form.update.password')).to_s
-			options[:port]     = ask(I18n.t('form.update.port'    , port:     item.port)).to_s
-			options[:comment]  = ask(I18n.t('form.update.comment' , comment:  item.comment)).to_s
+			options[:name]     = ask(I18n.t('form.update_item.name'    , name:     item.name)).to_s
+			options[:group]    = ask(I18n.t('form.update_item.group'   , group:    item.group)).to_s
+			options[:host]     = ask(I18n.t('form.update_item.server'  , server:   item.host)).to_s
+			options[:protocol] = ask(I18n.t('form.update_item.protocol', protocol: item.protocol)).to_s
+			options[:user]     = ask(I18n.t('form.update_item.login'   , login:    item.user)).to_s
+			password           = ask(I18n.t('form.update_item.password')).to_s
+			options[:port]     = ask(I18n.t('form.update_item.port'    , port:     item.port)).to_s
+			options[:comment]  = ask(I18n.t('form.update_item.comment' , comment:  item.comment)).to_s
 
 			options.delete_if { |k,v| v.empty? }
 				
@@ -283,7 +285,7 @@ class Cli
 			@mpw.write_data
 			@mpw.sync
 
-			puts "#{I18n.t('form.update.valid')}".green
+			puts "#{I18n.t('form.update_item.valid')}".green
 		else
 			puts I18n.t('display.nothing')
 		end
@@ -298,14 +300,14 @@ class Cli
 		item = @mpw.search_by_id(id)
 
 		if item.nil?
-			puts I18n.t('display.nothing')
+			puts I18n.t('form.delete_item.not_valid', id: id)
 			return
 		end
 
 		if not force
 			display_item(item)
 
-			confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
+			confirm = ask("#{I18n.t('form.delete_item.ask', id: id)} (y/N) ").to_s
 			if not confirm =~ /^(y|yes|YES|Yes|Y)$/
 				return
 			end
@@ -315,9 +317,9 @@ class Cli
 		@mpw.write_data
 		@mpw.sync
 
-		puts "#{I18n.t('form.delete.valid', id: id)}".green
-	rescue
-		puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
+		puts "#{I18n.t('form.delete_item.valid', id: id)}".green
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #16: #{e}".red
 	end
 
 	# Export the items in a CSV file
@@ -327,7 +329,7 @@ class Cli
 
 		puts "#{I18n.t('export.valid', file)}".green
 	rescue Exception => e
-			puts "#{I18n.t('display.error')} #17: #{e}".red
+		puts "#{I18n.t('display.error')} #17: #{e}".red
 	end
 
 	# Import items from a YAML file

From 83d96bf26cfe70f67e91182740c3e503a0c6ed49 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 17:02:37 +0200
Subject: [PATCH 238/531] fix translate with new name

---
 i18n/cli/fr.yml | 33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/i18n/cli/fr.yml b/i18n/cli/fr.yml
index 38a39d4..5564be4 100644
--- a/i18n/cli/fr.yml
+++ b/i18n/cli/fr.yml
@@ -61,7 +61,9 @@ fr:
 
   form:
     select: "Sélectionner l'élément: "
-    add:
+    add_key:
+      valid: "La clé a bien été ajoutée!"
+    add_item:
       title: "Ajout d'un nouvel élément"
       name: "Entrez le nom: "
       group: "Entrez le groupe (optionnel): "
@@ -72,7 +74,9 @@ fr:
       port: "Entrez le port de connexion (optionnel): "
       comment: "Entrez un commentaire (optionnel): "
       valid: "L'élément a bien été ajouté!"
-    delete:
+    delete_key:
+      valid: "La clé a bien été supprimée!"
+    delete_item:
       ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?"
       valid: "L'élément %{id} a bien été supprimé!"
       not_valid: "L'élément %{id} n'a pu être supprimé, car il n'existe pas!"
@@ -80,22 +84,23 @@ fr:
       ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
       valid: "L'import est un succès!"
       not_valid: "Aucune donnée à importer!"
-    setup:
+    setup_config:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
       gpg_key: "Entrez la clé GPG: " 
-      share_gpg_keys: "Entrez les clés GPG avec qui vous voulez partager les mots de passe: " 
-      gpg_file: "Entrez le chemin du fichier qui sera chiffré [défaut=%{home}/db/default.gpg]: "
-      timeout: "Entrez le temps (en seconde) du mot de passe GPG [défaut=60]: "
-      sync_type: "Type de synchronisation (mpw, ssh, ftp, or nil): "
-      sync_host: "Serveur de synchronisation: "
-      sync_port: "Port du serveur de synchronisation: "
-      sync_user: "Utilisateur pour la synchronisation: "
-      sync_pwd: "Mot de passe pour la synchronisation: "
-      sync_path: "Chemin du fichier pour la synchronisation: "
+      wallet_dir: "Entrez le chemin du répertoire qui contiendra les porte-feuilles de mot de passe [défaut=%{home}/wallets]: "
       valid: "Le fichier de configuration a bien été créé!"
+    setup_wallet:
+      title: "Configuration du porte-feuille"
+      sync_type: "Type de synchronisation (ssh, ftp): "
+      sync_host: "Serveur: "
+      sync_port: "Port: "
+      sync_user: "Utilisateur: "
+      sync_pwd: "Mot de passe: "
+      sync_path: "Chemin du fichier: "
+      valid: "Le fichier de configuration du porte-feuille a bien été créé!"
     setup_gpg_key:
-      title: "Configuration d'une clé GPG"
+      title: "Configuration d'une nouvelle clé GPG"
       ask: "Voulez vous créer votre clé GPG ? (O/n)"
       no_create: "Veuillez créer manuellement votre clé GPG ou relancer le logiciel."
       name: "Votre nom et prénom: "
@@ -106,7 +111,7 @@ fr:
       expire: "Expiration de la clé GPG [défaut=0 (illimité)]: "
       wait: "Veuillez patienter durant la génération de votre clé GPG, ce processus peut prendre quelques minutes."
       valid: "Votre clé GPG a bien été créée ;-)"
-    update:
+    update_item:
       title: "Mis à jour d'un élément"
       name: "Entrez le nom [%{name}]: "
       group: "Entrez le groupe [%{group}]: "

From 7e630954479e4b85e8b3b3e9d2e7ca84eb16182b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 17:03:36 +0200
Subject: [PATCH 239/531] remove mpw server code

---
 i18n/server/en.yml |  26 ----
 i18n/server/fr.yml |  26 ----
 lib/mpw/server.rb  | 343 ---------------------------------------------
 3 files changed, 395 deletions(-)
 delete mode 100644 i18n/server/en.yml
 delete mode 100644 i18n/server/fr.yml
 delete mode 100644 lib/mpw/server.rb

diff --git a/i18n/server/en.yml b/i18n/server/en.yml
deleted file mode 100644
index df88dfe..0000000
--- a/i18n/server/en.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-en:
-  option:
-    usage: "Usage"
-    config: "Specifie the configuration file"
-    checkconfig: "Check the configuration"
-    setup: "Setup a new configuration file"
-    help: "Show this message help"
-  checkconfig:
-    fail: "Checkconfig failed:!"
-    empty: "ERROR: an importe option is missing!"
-    datadir: "ERROR: le data directory doesn't exist!"
-  form:
-    setup:
-      title: "Serveur configuration" 
-      host: "IP listen: "
-      port: "Port listen: "
-      data_dir: "Data directory: "
-      timeout: "Timeout to second: "
-      log_file: "Log file path: "
-      not_valid: "ERROR: Impossible to write the configuration file!"
-  formats:
-    default: ! '%Y-%m-%d'
-    long: ! '%B %d, %Y'
-    short: ! '%b %d'
-    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/i18n/server/fr.yml b/i18n/server/fr.yml
deleted file mode 100644
index 667757c..0000000
--- a/i18n/server/fr.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-fr:
-  option:
-    usage: "Utilisation"
-    config: "Spécifie le fichier de configuration"
-    checkconfig: "Vérifie le fichier de configuration"
-    setup: "Permet de générer un nouveau fichier de configuration"
-    help: "Affiche ce message d'aide"
-  checkconfig:
-    fail: "Le fichier de configuration est invalide!"
-    empty: "ERREUR: Une option importante est manquante!"
-    datadir: "ERREUR: Le répertoire des données n'existe pas!"
-  form:
-    setup:
-      title: "Configuration du serveur" 
-      host: "IP d'écoute: "
-      port: "Port d'écoute: "
-      data_dir: "Répertoire des données: "
-      log_file: "Chemin du ficier de log: "
-      timeout: "Timeout en seconde: "
-      not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
-  formats:
-    default: ! '%Y-%m-%d'
-    long: ! '%B %d, %Y'
-    short: ! '%b %d'
-    custom: ! '%A, %M %B, %Y @ %l:%M%P'
diff --git a/lib/mpw/server.rb b/lib/mpw/server.rb
deleted file mode 100644
index 415bb26..0000000
--- a/lib/mpw/server.rb
+++ /dev/null
@@ -1,343 +0,0 @@
-#!/usr/bin/ruby
-
-module MPW
-
-	require 'socket'
-	require 'json'
-	require 'highline/import'
-	require 'digest'
-	require 'logger'
-
-
-	class Server
-
-		attr_accessor :error_msg
-
-		# Constructor
-		def initialize
-			YAML::ENGINE.yamler='syck'
-		end
-
-		# Start the server
-		def start
-			server = TCPServer.open(@host, @port)
-			@log.info("The server is started on #{@host}:#{@port}")
-
-			loop do
-				Thread.start(server.accept) do |client|
-					@log.info("#{client.peeraddr[3]} is connected")
-
-					while true do
-						msg = get_client_msg(client)
-
-						next if not msg
-						
-						if msg['gpg_key'].nil? or msg['gpg_key'].empty? or msg['password'].nil? or msg['password'].empty?
-							@log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
-							close_connection(client)
-							next
-						end
-
-						case msg['action']
-						when 'get'
-							@log.debug("#{client.peeraddr[3]} GET gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-							client.puts get_file(msg)
-						when 'update'
-							@log.debug("#{client.peeraddr[3]} UPDATE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-							client.puts update_file(msg)
-						when 'delete'
-							@log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
-							client.puts delete_file(msg)
-						else
-							@log.warning("#{client.peeraddr[3]} is disconnected because unkwnow command")
-							send_msg = {action: 'unknown',
-							            gpg_key: msg['gpg_key'],
-							            error:  'server.error.client.unknown'
-							           }
-							client.puts send_msg 
-							close_connection(client)
-						end
-					end
-				end
-			end
-
-		rescue Exception => e
-			puts "Impossible to start the server: #{e}"
-			@log.error("Impossible to start the server: #{e}")
-			exit 2
-		end
-
-		# Get a gpg file
-		# @args: msg -> message puts by the client
-		# @rtrn: json message
-		def get_file(msg)
-			gpg_key = msg['gpg_key'].sub('@', '_')
-
-			if msg['suffix'].nil? or msg['suffix'].empty?
-				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
-			else
-				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
-			end
-
-			if File.exist?(file_gpg)
-				gpg_data    = YAML::load_file(file_gpg)
-				salt        = gpg_data['gpg']['salt']
-				hash        = gpg_data['gpg']['hash']
-				data        = gpg_data['gpg']['data']
-
-				if is_authorized?(msg['password'], salt, hash)
-					send_msg = {action:  'get',
-					            gpg_key: msg['gpg_key'],
-					            data:    data,
-					            error:   nil
-					           }
-				else
-					send_msg = {action:  'get',
-					            gpg_key: msg['gpg_key'],
-					            error:   'server.error.client.no_authorized'
-					           }
-				end
-			else
-				send_msg = {action:  'get',
-				            gpg_key: msg['gpg_key'],
-				            data:    '',
-				            error:   nil
-				           }
-			end
-
-			return send_msg.to_json
-		end
-
-		# Update a file
-		# @args: msg -> message puts by the client
-		# @rtrn: json message
-		def update_file(msg)
-			gpg_key = msg['gpg_key'].sub('@', '_')
-			data    = msg['data']
-
-			if data.nil? or data.empty?
-				send_msg = {action:  'update',
-				            gpg_key: msg['gpg_key'],
-				            error:   'server.error.client.no_data'
-				           }
-				
-				return send_msg.to_json
-			end
-
-			if msg['suffix'].nil? or msg['suffix'].empty?
-				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
-			else
-				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
-			end
-
-			if File.exist?(file_gpg)
-				gpg_data  = YAML::load_file(file_gpg)
-				salt      = gpg_data['gpg']['salt']
-				hash      = gpg_data['gpg']['hash']
-
-			else
-				salt = generate_salt
-				hash = Digest::SHA256.hexdigest(salt + msg['password'])
-			end
-
-			if is_authorized?(msg['password'], salt, hash)
-				begin
-					config = {'gpg' => {'salt' => salt,
-					                    'hash' => hash,
-					                    'data' => data
-					                   }
-					         }
-
-					File.open(file_gpg, 'w+') do |file|
-						file << config.to_yaml
-					end
-
-					send_msg = {action:  'update',
-					            gpg_key: msg['gpg_key'],
-					            error:   nil
-					           }
-				rescue Exception => e
-					send_msg = {action:  'update',
-					            gpg_key: msg['gpg_key'],
-					            error:   'server.error.client.unknown'
-					           }
-				end
-			else
-				send_msg = {action:  'update',
-				            gpg_key: msg['gpg_key'],
-				            error:   'server.error.client.no_authorized'
-				           }
-			end
-			
-			return send_msg.to_json
-		end
-
-		# Remove a gpg file
-		# @args: msg -> message puts by the client
-		# @rtrn: json message
-		def delete_file(msg)
-			gpg_key = msg['gpg_key'].sub('@', '_')
-
-			if msg['suffix'].nil? or msg['suffix'].empty?
-				file_gpg = "#{@data_dir}/#{gpg_key}.yml"
-			else
-				file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
-			end
-
-			if not File.exist?(file_gpg)
-				send_msg = {:action  => 'delete',
-				            :gpg_key => msg['gpg_key'],
-				            :error   => nil
-				           }
-
-				return send_msg.to_json
-			end
-
-			gpg_data = YAML::load_file(file_gpg)
-			salt     = gpg_data['gpg']['salt']
-			hash     = gpg_data['gpg']['hash']
-
-			if is_authorized?(msg['password'], salt, hash)
-				begin
-					File.unlink(file_gpg)
-
-					send_msg = {action:  'delete',
-					            gpg_key: msg['gpg_key'],
-					            error:   nil
-					           }
-				rescue Exception => e
-					send_msg = {action:  'delete',
-					            gpg_key: msg['gpg_key'],
-					            error:   'server.error.client.unknown'
-					           }
-				end
-			else
-				send_msg = {action:  'delete',
-				            gpg_key: msg['gpg_key'],
-				            error:   'server.error.client.no_authorized'
-				           }
-			end
-			
-			return send_msg.to_json
-		end
-
-		# Check is the hash equal the password with the salt
-		# @args: password -> the user password
-		#        salt -> the salt
-		#        hash -> the hash of the password with the salt
-		# @rtrn: true is is good, else false
-		def is_authorized?(password, salt, hash)
-			if hash == Digest::SHA256.hexdigest(salt + password)
-				return true
-			else
-				return false
-			end
-		end
-
-		# Get message to client
-		# @args: client -> client connection
-		# @rtrn: array of the json string, or false if isn't json message
-		def get_client_msg(client)
-			msg = client.gets
-			return JSON.parse(msg)
-		rescue
-			closeConnection(client)
-			return false
-		end
-
-		# Close the client connection
-		# @args: client -> client connection
-		def close_connection(client)
-				client.puts "Closing the connection. Bye!"
-				client.close
-		end
-
-		# Check the config file
-		# @args: file_config -> the configuration file
-		# @rtrn: true if the config file is correct
-		def checkconfig(file_config)
-			config    = YAML::load_file(file_config)
-			@host     = config['config']['host']
-			@port     = config['config']['port'].to_i
-			@data_dir = config['config']['data_dir']
-			@log_file = config['config']['log_file']
-			@timeout  = config['config']['timeout'].to_i
-
-			if @host.empty? or @port <= 0 or @data_dir.empty? 
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.empty')
-				return false
-			end
-
-			if not Dir.exist?(@data_dir)
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.datadir')
-				return false
-			end
-
-			if @log_file.nil? or @log_file.empty?
-				puts I18n.t('checkconfig.fail')
-				puts I18n.t('checkconfig.log_file_empty')
-				return false
-			else
-				begin
-					@log = Logger.new(@log_file)
-				rescue
-					puts I18n.t('checkconfig.fail')
-					puts I18n.t('checkconfig.log_file_create')
-					return false
-				end
-			end
-
-			return true
-		rescue Exception => e 
-			puts "#{I18n.t('checkconfig.fail')}\n#{e}"
-			return false
-		end
-
-		# Create a new config file
-		# @args: file_config -> the configuration file
-		# @rtrn: true if le config file is create
-		def setup(file_config)
-			puts I18n.t('form.setup.title')
-			puts '--------------------'
-			host     = ask(I18n.t('form.setup.host')).to_s
-			port     = ask(I18n.t('form.setup.port')).to_s
-			data_dir = ask(I18n.t('form.setup.data_dir')).to_s
-			log_file = ask(I18n.t('form.setup.log_file')).to_s
-			timeout  = ask(I18n.t('form.setup.timeout')).to_s
-
-			config = {'config' => {'host'     => host,
-			                       'port'     => port,
-			                       'data_dir' => data_dir,
-			                       'log_file' => log_file,
-			                       'timeout'  => timeout
-			                      }
-			         }
-
-			File.open(file_config, 'w') do |file|
-				file << config.to_yaml
-			end
-				
-			return true
-		rescue Exception => e 
-			puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
-			return false
-		end
-
-		# Generate a random salt
-		# @args: length -> the length salt
-		# @rtrn: a random string
-		def generate_salt(length=4)
-			if length.to_i <= 0 or length.to_i > 16
-				length = 4
-			else
-				length = length.to_i
-			end
-
-			return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
-		end
-		
-	end
-
-end

From 25214873edecc63369353ddacc82b5d1a4b88be3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 17:05:17 +0200
Subject: [PATCH 240/531] remove folder cli in folder i18n

---
 bin/mpw               | 2 +-
 i18n/{cli => }/en.yml | 0
 i18n/{cli => }/fr.yml | 0
 3 files changed, 1 insertion(+), 1 deletion(-)
 rename i18n/{cli => }/en.yml (100%)
 rename i18n/{cli => }/fr.yml (100%)

diff --git a/bin/mpw b/bin/mpw
index 97b846d..63868ed 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -26,7 +26,7 @@ require "#{APP_ROOT}/../lib/mpw/config.rb"
 require "#{APP_ROOT}/../lib/mpw/ui/cli.rb"
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{APP_ROOT}/../i18n/cli/*.yml"]
+I18n.load_path      = Dir["#{APP_ROOT}/../i18n/*.yml"]
 I18n.default_locale = :en
 I18n.locale         = lang.to_sym
 
diff --git a/i18n/cli/en.yml b/i18n/en.yml
similarity index 100%
rename from i18n/cli/en.yml
rename to i18n/en.yml
diff --git a/i18n/cli/fr.yml b/i18n/fr.yml
similarity index 100%
rename from i18n/cli/fr.yml
rename to i18n/fr.yml

From 68a0fddde8c8475891c723400a028f6eb8800172 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 14 May 2016 17:13:47 +0200
Subject: [PATCH 241/531] fix en translate with new name

---
 i18n/en.yml | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 52d00cf..6576691 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -45,7 +45,7 @@ en:
     force: "No ask to confirm when you delete an item"
     generate_password: "Generate a random password (default 8 characters)"
     group: "Search the items with specified group"
-    help: "Affiche ce message d'aide"
+    help: "Show this help message"
     id: "Specify an id, to use with the options [--delete | --update]"
     import: "Import item since a yaml file"
     key: "Specify the key name, to use with the options [--add | --delete | --update]"
@@ -59,11 +59,11 @@ en:
     usage: "Usage"
     wallet: "Specify a wallet to use"
 
-   help: "Show this help message"
-
   form:
     select: "Select the item: "
-    add:
+    add_key:
+      valid: "Key has been added!"
+    add_item:
       title: "Add a new item"
       name: "Enter the name: "
       group: "Enter the group (optional): "
@@ -74,7 +74,9 @@ en:
       port: "Enter the connection port (optional): "
       comment: "Enter a comment (optional): "
       valid: "Item has been added!"
-    delete:
+    delete_key:
+      valid: "Key has been deleted!"
+    delete_item:
       ask: "Are you sure you want to remove the item %{id} ?"
       valid: "The item %{id} has been removed!"
       not_valid: "The item %{id} hasn't been removed, because it doesn't exist!"
@@ -82,20 +84,21 @@ en:
       ask: "Are you sure you want to import this file %{file} ?"
       valid: "The import is succesfull!"
       not_valid: "No data to import!"
-    setup:
+    setup_config:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...): "
       gpg_key: "Enter the GPG key: " 
-      share_gpg_keys: "Enter the GPG keys with who you want to share the passwords: " 
-      gpg_file: "Enter the path to encrypt file [default=%{home}/db/default.gpg]: "
-      timeout: "Enter the timeout (in seconde) to GPG password [default=60]: "
+      wallet_dir: "Enter the wallets's folder path [défaut=%{home}/wallets]: "
+      valid: "The config file has been created!"
+    setup_wallet:
+      title: "Wallet setup"
       sync_type: "Synchronization type (mpw, ssh, ftp, or nil): "
       sync_host: "Synchronization server: "
       sync_port: "Port of the synchronization server: "
       sync_user: "Username for the synchronization: "
       sync_pwd: "Password for the synchronization: "
       sync_path: "File path for the synchronization : "
-      valid: "The config file has been created!"
+      valid: "The wallet config file has been created!"
     setup_gpg_key:
       title: "Setup a GPG key"
       ask: "Do you want create your GPG key ? (Y/n)"
@@ -108,7 +111,7 @@ en:
       expire: "Expire time of the GPG key [default=0 (unlimited)]: "
       wait: "Please waiting during the GPG key generate, this process can take few minutes."
       valid: "Your GPG key has been created ;-)"
-    update:
+    updatei_item:
       title: "Update an item"
       name: "Enter the name [%{name}]: "
       group: "Enter the group [%{group}]: "

From a4fe06370322188b3e0abaad58ff8542d884cbf0 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 18:30:33 +0200
Subject: [PATCH 242/531] remove error_msg in config class

---
 lib/mpw/config.rb | 34 +++++++++-------------------------
 lib/mpw/ui/cli.rb | 34 ++++++++++++++--------------------
 2 files changed, 23 insertions(+), 45 deletions(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index dd797d2..11f1e5c 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -19,7 +19,6 @@ class Config
 	# Constructor
 	# @args: config_file -> the specify config file
 	def initialize(config_file=nil)
-		@error_msg   = nil
 		@config_file = config_file
 
 		if /darwin/ =~ RUBY_PLATFORM
@@ -43,8 +42,7 @@ class Config
 	def setup(key, lang, wallet_dir)
 
 		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-			@error_msg = I18n.t('error.config.key_bad_format')
-			return false
+			raise I18n.t('error.config.key_bad_format')
 		end
 
 		if wallet_dir.empty?
@@ -62,10 +60,8 @@ class Config
 			file << config.to_yaml
 		end
 		
-		return true
 	rescue Exception => e 
-		@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-		return false
+		raise "#{I18n.t('error.config.write')}\n#{e}"
 	end
 
 	# Setup a new gpg key
@@ -76,11 +72,9 @@ class Config
 	# @rtrn: true if the GPG key is create, else false
 	def setup_gpg_key(password, name, length = 4096, expire = 0)
 		if name.nil? or name.empty?
-			@error_msg = "#{I18n.t('error.config.genkey_gpg.name')}"
-			return false
+			raise "#{I18n.t('error.config.genkey_gpg.name')}"
 		elsif password.nil? or password.empty?
-			@error_msg = "#{I18n.t('error.config.genkey_gpg.password')}"
-			return false
+			raise "#{I18n.t('error.config.genkey_gpg.password')}"
 		end
 
 		param = ''
@@ -98,31 +92,24 @@ class Config
 
 		ctx = GPGME::Ctx.new
 		ctx.genkey(param, nil, nil)
-
-		return true
 	rescue Exception => e
-		@error_msg = "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
-		return false
+		raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
 	end
 
 	# Check the config file
 	# @rtrn: true if the config file is correct
 	def checkconfig
-		config = YAML::load_file(@config_file)
+		config      = YAML::load_file(@config_file)
 		@key        = config['config']['key']
 		@lang       = config['config']['lang']
 		@wallet_dir = config['config']['wallet_dir']
 
 		if @key.empty? or @wallet_dir.empty? 
-			@error_msg = I18n.t('error.config.check')
-			return false
+			raise I18n.t('error.config.check')
 		end
 		I18n.locale = @lang.to_sym
-
-		return true
 	rescue Exception => e 
-		@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
-		return false
+		raise "#{I18n.t('error.config.check')}\n#{e}"
 	end
 
 	# Check if private key exist
@@ -148,11 +135,8 @@ class Config
 		File.open(@config_file, 'w') do |file|
 			file << config.to_yaml
 		end
-
-		return true
 	rescue Exception => e 
-		@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
-		return false
+		raise "#{I18n.t('error.config.write')}\n#{e}"
 	end
 	
 end
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 9167c4d..957005b 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -34,17 +34,13 @@ class Cli
 		end
 		I18n.locale = language.to_sym
 
-		if @config.setup(key, lang, wallet_dir)
-			puts "#{I18n.t('form.setup_config.valid')}".green
-		else
-			puts "#{I18n.t('display.error')} #8: #{@config.error_msg}".red
-			exit 2
-		end
+		@config.setup(key, lang, wallet_dir)
+		@config.checkconfig
 
-		if not @config.checkconfig
-			puts "#{I18n.t('display.error')} #9: #{@config.error_msg}".red
-			exit 2
-		end
+		puts "#{I18n.t('form.setup_config.valid')}".green
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #8: #{e}".red
+		exit 2
 	end
 	
 	# Setup a new GPG key
@@ -54,8 +50,7 @@ class Cli
 		ask      = ask(I18n.t('form.setup_gpg_key.ask')).to_s
 		
 		if not ['Y', 'y', 'O', 'o'].include?(ask)
-			puts I18n.t('form.setup_gpg_key.no_create')
-			exit 2
+			raise I18n.t('form.setup_gpg_key.no_create')
 		end
 
 		name     = ask(I18n.t('form.setup_gpg_key.name')).to_s
@@ -63,8 +58,7 @@ class Cli
 		confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
 
 		if password != confirm 
-			puts I18n.t('form.setup_gpg_key.error_password')
-			exit 2
+			raise I18n.t('form.setup_gpg_key.error_password')
 		end
 
 		length   = ask(I18n.t('form.setup_gpg_key.length')).to_s
@@ -76,12 +70,12 @@ class Cli
 
 		puts I18n.t('form.setup_gpg_key.wait')
 		
-		if @config.setup_gpg_key(password, name, length, expire)
-			puts "#{I18n.t('form.setup_gpg_key.valid')}".green
-		else
-			puts "#{I18n.t('display.error')} #10: #{@config.error_msg}".red
-			exit 2
-		end
+		@config.setup_gpg_key(password, name, length, expire)
+
+		puts "#{I18n.t('form.setup_gpg_key.valid')}".green
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #8: #{e}".red
+		exit 2
 	end
 
 	# Setup wallet config for sync

From 4060e2b105e446dfc09f49f801d13dc39a8581ee Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 18:31:56 +0200
Subject: [PATCH 243/531] remove set_last_sync function in config because it is
 obsolete

---
 lib/mpw/config.rb | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 11f1e5c..951f400 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -122,22 +122,5 @@ class Config
 
 		return false
 	end
-
-	# Set the last update when there is a sync
-	# @rtrn: true is the file has been updated
-	def set_last_sync
-		config = {'config' => {'key'        => @key,
-		                       'lang'       => @lang,
-		                       'wallet_dir' => @wallet_dir,
-		                      }
-	           }
-
-		File.open(@config_file, 'w') do |file|
-			file << config.to_yaml
-		end
-	rescue Exception => e 
-		raise "#{I18n.t('error.config.write')}\n#{e}"
-	end
-	
 end
 end

From 84ff0e8ae26679fb9559fbb590446356e07957c7 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 23:22:55 +0200
Subject: [PATCH 244/531] fix comments

---
 lib/mpw/sync/ssh.rb | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/lib/mpw/sync/ssh.rb b/lib/mpw/sync/ssh.rb
index 89a7fd5..45dae3a 100644
--- a/lib/mpw/sync/ssh.rb
+++ b/lib/mpw/sync/ssh.rb
@@ -20,7 +20,6 @@ class SyncSSH
 	end
 
 	# Connect to server
-	# @rtrn: false if the connection fail
 	def connect
 		Net::SSH.start(@host, @user, password: @password, port: @port) do
 			break
@@ -30,8 +29,7 @@ class SyncSSH
 	end
 
 	# Get data on server
-	# @args: gpg_password -> the gpg password
-	# @rtrn: nil if nothing data or error
+	# @args: file_tmp -> the path where download the file
 	def get(file_tmp)
 		Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
 			sftp.lstat(@path) do |response|
@@ -44,7 +42,6 @@ class SyncSSH
 
 	# Update the remote data
 	# @args: file_gpg -> the data to send on server
-	# @rtrn: false if there is a problem
 	def update(file_gpg)
 		Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
 			sftp.upload!(file_gpg, @path)
@@ -52,6 +49,5 @@ class SyncSSH
 	rescue Exception => e
 		raise "#{I18n.t('error.sync.upload')}\n#{e}"
 	end
-
 end
 end

From 47430ce611eb83fe0ba157d85ccdab73ea07e29d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 23:23:26 +0200
Subject: [PATCH 245/531] fix ftp sync for mpw 3.0

---
 lib/mpw/sync/ftp.rb | 109 +++++++++++++++++---------------------------
 1 file changed, 41 insertions(+), 68 deletions(-)

diff --git a/lib/mpw/sync/ftp.rb b/lib/mpw/sync/ftp.rb
index 0d324e7..71d1624 100644
--- a/lib/mpw/sync/ftp.rb
+++ b/lib/mpw/sync/ftp.rb
@@ -2,80 +2,53 @@
 # author: nishiki
 # mail: nishiki@yaegashi.fr
 
-require 'rubygems'
 require 'i18n'
 require 'net/ftp'
 		
 module MPW
-	class FTP
-	
-		attr_accessor :error_msg
-		attr_accessor :enable
-	
-		# Constructor
-		# @args: host -> the server host
-		#        port -> ther connection port
-		#        gpg_key -> the gpg key
-		#        password -> the remote password
-		#        suffix -> the suffix file
-		def initialize(host, user, password, path, port=nil)
-			@error_msg = nil
-			@enable    = false
+class FTP
 
-			@host     = host
-			@user     = user
-			@password = password
-			@path     = path
-			@port     = port.instance_of?(Integer) ? 21 : port
-		end
-	
-		# Connect to server
-		# @rtrn: false if the connection fail
-		def connect
-			Net::FTP.open(@host) do |ftp|
-				ftp.login(@user, @password)
-				@enable = true
-			end
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
-			@enable    = false
-		else
-			return @enable
-		end
-	
-		# Get data on server
-		# @args: gpg_password -> the gpg password
-		# @rtrn: nil if nothing data or error
-		def get(file_tmp)
-			return false if not @enable
-			
-			Net::FTP.open(@host) do |ftp|
-				ftp.login(@user, @password)
-				ftp.gettextfile(@path, file_tmp)
-			end
-		
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
-			return false
-		end
-	
-		# Update the remote data
-		# @args: data -> the data to send on server
-		# @rtrn: false if there is a problem
-		def update(file_gpg)
-			return true if not @enable
-	
-			Net::FTP.open(@host) do |ftp|
-				ftp.login(@user, @password)
-				ftp.puttextfile(file_gpg, @path)
-			end
+	# Constructor
+	# @args: config -> the config
+	def initialize(config)
+		@host      = config['host']
+		@user      = config['user']
+		@password  = config['password']
+		@path      = config['path']
+		@port      = config['port'].instance_of?(Integer) ? 22 : config['port']
+	end
 
-			return true
-		rescue Exception => e
-			@error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
-			return false
-		end
 
+	# Connect to server
+	def connect
+		Net::FTP.open(@host) do |ftp|
+			ftp.login(@user, @password)
+			break
+		end
+	rescue Exception => e
+		raise "#{I18n.t('error.sync.connection')}\n#{e}"
+	end
+
+	# Get data on server
+	# @args: file_tmp -> the path where download the file
+	def get(file_tmp)
+		Net::FTP.open(@host) do |ftp|
+			ftp.login(@user, @password)
+			ftp.gettextfile(@path, file_tmp)
+		end
+	rescue Exception => e
+		raise "#{I18n.t('error.sync.download')}\n#{e}"
+	end
+
+	# Update the remote data
+	# @args: file_gpg -> the data to send on server
+	def update(file_gpg)
+		Net::FTP.open(@host) do |ftp|
+			ftp.login(@user, @password)
+			ftp.puttextfile(file_gpg, @path)
+		end
+	rescue Exception => e
+		raise "#{I18n.t('error.sync.upload')}\n#{e}"
 	end
 end
+end

From 7ea4f19698817d9566e7eb1ba0cb71674ffbf896 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 23:37:57 +0200
Subject: [PATCH 246/531] remove useless function check_config

---
 lib/mpw/mpw.rb | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 767522a..64e7972 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -179,13 +179,6 @@ class MPW
 		@keys.delete(key)
 	end
 
-	# TODO
-	def check_config
-		if false
-			raise 'ERROR'
-		end
-	end
-
 	# Set config
 	# args: config -> a hash with config options
 	def set_config(config)
@@ -277,7 +270,6 @@ class MPW
 
 	# Import to yaml
 	# @args: file -> path to file import
-	# TODO raise
 	def import(file)
 		YAML::load_file(file).each_value do |row| 
 			item = Item.new(name:     row['name'], 

From 4108f397d872da7cbc783813fb236d89633cf62b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 23:52:12 +0200
Subject: [PATCH 247/531] remove translate client ssh

---
 i18n/en.yml | 11 -----------
 i18n/fr.yml | 11 -----------
 2 files changed, 22 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 6576691..c6298aa 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -139,17 +139,6 @@ en:
     server: "Server"
     warning: "Warning"
 
-  ssh:
-    option:
-      usage: "Usage"
-      login: "Change the login"
-      server: "Change the host or the ip"
-      port: "Change the port"
-      help: "Show this help message"
-    display:
-      connect: "Connection to:"
-      nothing: "Nothing result!"
-
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 5564be4..11dde2b 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -139,17 +139,6 @@ fr:
     server: "Serveur"
     warning: "Warning"
 
-  ssh:
-    option:
-      usage: "Utilisation"
-      login: "Change l'identifiant de connexion"
-      server: "Change le nom de domaine ou l'ip du serveur"
-      port: "Change le port de connexion"
-      help: "Affiche ce message d'aide"
-    display:
-      connect: "Connexion à:"
-      nothing: "Aucun résultat!"
-
   formats:
     default: ! '%Y-%m-%d'
     long: ! '%B %d, %Y'

From f423774c2ca7ea9abc6235f32c127dc990b8938c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 15 May 2016 23:55:01 +0200
Subject: [PATCH 248/531] fix translate for class mpw

---
 i18n/en.yml    | 20 +++++++++-----------
 i18n/fr.yml    | 18 ++++++++----------
 lib/mpw/mpw.rb |  8 ++++----
 3 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index c6298aa..a81fced 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -1,8 +1,6 @@
 ---
 en:
   error:
-    client:
-      no_authorized: "You aren't authorized."
     config:
       write: "Can't write the config file!"
       check: "Checkconfig failed!"
@@ -14,25 +12,25 @@ en:
         password: "You must define a password for your GPG key!"
     delete:
       id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
-    export:
-      unknown_type: "The data type %{type} is unknown!"
-      write: "Can't export, unable to write in %{file}!"
+    export: "Can't export, unable to write in %{file}!"
     gpg_file: 
       decrypt: "Can't decrypt file!"
       encrypt: "Can't encrypt the GPG file!"
-    import:
-      bad_format: "Can't import, the file is badly formated!"
-      read: "Can't import, unable to read %{file}!"
+    mpw_file: 
+      read_data: "Can't to read the MPW file!"
+      write_data: "Can't to write the MPW file!"
+    import: "Can't import, unable to read %{file}!"
     update:
       name_empty: "You must define a name!"
     sync: 
+      general: "An error is appeared durint the sync"
       connection: "Connection fail!"
       communication: "A communication problem with the server is appeared!"
       download: "Can't download the file!"
       not_authorized: "You haven't the access to remote file!"
       upload: "Can't upload the file on the server!"
-      unknown: "An unknown error is occured!" 
-    unknown_type: "The sync type is unknown" 
+      unknown: "An unknown error is occured!"
+      unknown_type: "The sync type is unknown"
 
   warning:
     select: 'Your choice is not a valid element!'
@@ -111,7 +109,7 @@ en:
       expire: "Expire time of the GPG key [default=0 (unlimited)]: "
       wait: "Please waiting during the GPG key generate, this process can take few minutes."
       valid: "Your GPG key has been created ;-)"
-    updatei_item:
+    update_item:
       title: "Update an item"
       name: "Enter the name [%{name}]: "
       group: "Enter the group [%{group}]: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 11dde2b..a6172ea 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -1,8 +1,6 @@
 ---
 fr:
   error:
-    client:
-      no_authorized: "Vous n'êtes pas autorisé!"
     config:
       write: "Impossible d'écrire le fichier de configuration!"
       check: "Le fichier de configuration est invalide!"
@@ -14,25 +12,25 @@ fr:
         password: "Vous devez définir un mot de passe pour votre clé GPG!"
     delete:
       id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
-    export:
-      unknown_type: "Le type de donnée %{type} est inconnu!"
-      write: "Impossible d'exporter les données dans le fichier %{file}!"
+    export: "Impossible d'exporter les données dans le fichier %{file}!"
     gpg_file: 
       decrypt: "Impossible de déchiffrer le fichier GPG!"
       encrypt: "Impossible de chiffrer le fichier GPG!"
-    import:
-      bad_format: "Impossible d'importer le fichier car son format est incorrect!"
-      read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
+    mpw_file: 
+      read_data: "Impossible de lire le fichier MPW!"
+      write_data: "Impossible d'écrire le fichier MPW!"
+    import: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
     update:
       name_empty: "Vous devez définir un nom!"
     sync: 
+      general: "Une erreur est survenue durant la synchronisation"
       connection: "La connexion n'a pu être établie!"
       communication: "Un problème de communication avec le serveur est apparu!"
       download: "Impossible de télécharger le fichier!"
       not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
       upload: "Impossible d'envoyer le fichier sur le serveur!"
-      unknown: "Une erreur inconnue est survenue!" 
-    unknown_type: "Le type de synchronisation est inconnu" 
+      unknown: "Une erreur inconnue est survenue!"
+      unknown_type: "Le type de synchronisation est inconnu"
 
   warning:
     select: "Votre choix n'est pas un élément valide!"
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 64e7972..011bf3e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -265,7 +265,7 @@ class MPW
 
 		File.open(file, 'w') {|f| f << data.to_yaml}
 	rescue Exception => e 
-		raise "#{I18n.t('error.export.write', file: file)}\n#{e}"
+		raise "#{I18n.t('error.export', file: file)}\n#{e}"
 	end
 
 	# Import to yaml
@@ -287,7 +287,7 @@ class MPW
 			set_password(item.id, row['password'])
 		end
 	rescue Exception => e 
-		raise "#{I18n.t('error.import.read', file: file)}\n#{e}"
+		raise "#{I18n.t('error.import', file: file)}\n#{e}"
 	end
 
 	# Get last sync
@@ -312,7 +312,7 @@ class MPW
 			require 'mpw/sync/ftp'
 			sync = SyncFTP.new(@config['sync'])
 		else
-			raise I18n.t('error.unknown_type')
+			raise I18n.t('error.sync.unknown_type')
 		end
 
 		sync.connect
@@ -389,7 +389,7 @@ class MPW
 	rescue Exception => e
 		File.unlink(tmp_file) if File.exist?(tmp_file)
 
-		raise "#{I18n.t('error.sync.unknown')}\n#{e}"
+		raise "#{I18n.t('error.sync.general')}\n#{e}"
 	end
 
 	# Generate a random password

From 47e84eb451a1b4c0e131ff9b1742638d4714e998 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 19:51:59 +0200
Subject: [PATCH 249/531] minot fix spelling

---
 i18n/fr.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/i18n/fr.yml b/i18n/fr.yml
index a6172ea..22309f9 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -40,7 +40,7 @@ fr:
     config: "Spécifie le fichier de configuration à utiliser"
     export: "Exporte un portefeuille dans un fichier yaml"
     file: "Spécifie un fichier, à utiliser avec les options [--import | --export | --add]"
-    force: "Ne demande pas de confirmation pour la supprésion d'un élément"
+    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)"
     group: "Recherche les éléments appartenant au groupe spécifié"
     help: "Affiche ce message d'aide"

From 5f7e1ff8004c15d7d7d7643005f84ab8aca0a329 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 19:55:25 +0200
Subject: [PATCH 250/531] update Gemfile

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

diff --git a/Gemfile b/Gemfile
index 1ccb9c5..14a22b1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,10 +1,7 @@
 source 'https://rubygems.org'
 gem 'highline'
-gem 'i18n', '0.6.9'
+gem 'i18n'
 gem 'locale'
 gem 'gpgme'
 gem 'colorize'
-
-group :ssh do
-	gem 'net-sftp'
-end
+gem 'net-sftp'

From f842bd63ee05dd4fc11ad04524b8c7a98de16b88 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 20:14:03 +0200
Subject: [PATCH 251/531] remove check_config in read_data

---
 lib/mpw/mpw.rb | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 011bf3e..cb7e57d 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -36,7 +36,6 @@ class MPW
 				case f.full_name
 					when 'wallet/config.gpg'
 						@config = YAML.load(decrypt(f.read))
-						check_config
 
 					when 'wallet/meta.gpg'
 						data = decrypt(f.read)

From 2a0decd21578634e96cf4e0df2e383c833951c1e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 20:15:42 +0200
Subject: [PATCH 252/531] rename function checkconfig to is_valid?

---
 bin/mpw           |  8 +++-----
 lib/mpw/config.rb | 13 +++++++------
 lib/mpw/ui/cli.rb |  3 ++-
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 63868ed..1b8d552 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -128,13 +128,11 @@ end.parse!
 # Main
 # --------------------------------------------------------- #
 
-config      = MPW::Config.new(options[:config])
-check_error = config.checkconfig
-
-cli = MPW::Cli.new(config)
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config)
 	
 # Setup a new config 
-if not check_error or not options[:setup].nil?
+if not config.is_valid? or not options[:setup].nil?
 	cli.setup(lang)
 elsif not config.check_gpg_key?
 	cli.setup_gpg_key
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 951f400..1eaaa0e 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -98,18 +98,19 @@ class Config
 
 	# Check the config file
 	# @rtrn: true if the config file is correct
-	def checkconfig
+	def is_valid?
 		config      = YAML::load_file(@config_file)
 		@key        = config['config']['key']
 		@lang       = config['config']['lang']
 		@wallet_dir = config['config']['wallet_dir']
 
-		if @key.empty? or @wallet_dir.empty? 
-			raise I18n.t('error.config.check')
-		end
+		raise if @key.empty? or @wallet_dir.empty?
+			
 		I18n.locale = @lang.to_sym
-	rescue Exception => e 
-		raise "#{I18n.t('error.config.check')}\n#{e}"
+
+		return true
+	rescue
+		return false
 	end
 
 	# Check if private key exist
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 957005b..a41657f 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -35,7 +35,8 @@ class Cli
 		I18n.locale = language.to_sym
 
 		@config.setup(key, lang, wallet_dir)
-		@config.checkconfig
+
+		raise I18n.t('error.config.check') if not @config.is_valid?
 
 		puts "#{I18n.t('form.setup_config.valid')}".green
 	rescue Exception => e

From 95675ce3ff21e7923a86e07871ddade5a0cad202 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 20:41:51 +0200
Subject: [PATCH 253/531] update readme

---
 README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 75 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index cbf6e64..cad2fc2 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,87 @@
 # Manage your passwords!
 
 MPW is a little software which stores your passwords in an GPG encrypted file.
-MPW can synchronize your password with a MPW Server or via SSH or via FTP.
+MPW can synchronize your password with via SSH or FTP.
 
 # Installation
 
-You must generate a GPG Key with GPG or with Seahorse (GUI on linux).
-This program work with ruby >= 1.9
+This program work with ruby >= 2.0
 
 * install ruby and rubygems on your computer
 * gem install mpw
 
-If you want use mpw-ssh, you must install sshpass
-
 # How to use
 
-mpw -h 
+* Show help
+mpw --help
+
+* Setup a new config file
+mpw --setup
+mpw --setup --config /path/conf/file.cfg
+
+* Create and setup a new wallet
+mpw --setup-wallet --wallet new_wallet_name
+mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
+
+* Add a new item in wallet
+mpw --add 
+mpw --add --config /path/conf/file.cfg
+mpw --add --wallet wallet_name
+mpw --add --config /path/conf/file.cfg --wallet wallet_name
+
+* Update an item
+mpw --update --id uniq_id
+mpw --update --id uniq_id --config /path/conf/file.cfg
+mpw --update --id uniq_id --wallet wallet_name
+mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+
+* Delete an item
+mpw --delete --id uniq_id
+mpw --delete --id uniq_id --config /path/conf/file.cfg
+mpw --delete --id uniq_id --wallet wallet_name
+mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+
+* Show an item
+mpw --show 'string to search'
+mpw --show 'string to search' --config /path/conf/file.cfg
+mpw --show 'string to search' --wallet wallet_name
+mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
+mpw --show 'string to search' --group group_name
+mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
+mpw --show 'string to search' --group group_name --wallet wallet_name
+mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
+
+* Export data in YAML file
+mpw --export --file /path/file/to/export.yml
+mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
+mpw --export --file /path/file/to/export.yml --wallet wallet_name
+mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+
+* Import data from YAML file
+mpw --import --file /path/file/to/export.yml
+mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
+mpw --import --file /path/file/to/export.yml --wallet wallet_name
+mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+
+Format file to import:
+
+1:
+  name: Website perso
+  group: Perso
+  host: localhost.local
+  protocol: ftp
+  user: test
+  password: letoortue
+  port: 21
+  comment: Mysuper website
+2:
+  name: Linuxfr
+  group: Pro
+  host: Linuxfr.org
+  protocol: https
+  user: test
+  password: coucou 
+  port: 
+  comment: 
+
+

From f1814c518ed5b851bbd1fe61bc8e696d898e1035 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 20:47:44 +0200
Subject: [PATCH 254/531] update readme

---
 README.md | 104 ++++++++++++++++++++++++++----------------------------
 1 file changed, 51 insertions(+), 53 deletions(-)

diff --git a/README.md b/README.md
index cad2fc2..5d79fb1 100644
--- a/README.md
+++ b/README.md
@@ -13,75 +13,73 @@ This program work with ruby >= 2.0
 # How to use
 
 * Show help
-mpw --help
+	mpw --help
 
 * Setup a new config file
-mpw --setup
-mpw --setup --config /path/conf/file.cfg
+	mpw --setup
+	mpw --setup --config /path/conf/file.cfg
 
 * Create and setup a new wallet
-mpw --setup-wallet --wallet new_wallet_name
-mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
+	mpw --setup-wallet --wallet new_wallet_name
+	mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
 
 * Add a new item in wallet
-mpw --add 
-mpw --add --config /path/conf/file.cfg
-mpw --add --wallet wallet_name
-mpw --add --config /path/conf/file.cfg --wallet wallet_name
+	mpw --add 
+	mpw --add --config /path/conf/file.cfg
+	mpw --add --wallet wallet_name
+	mpw --add --config /path/conf/file.cfg --wallet wallet_name
 
 * Update an item
-mpw --update --id uniq_id
-mpw --update --id uniq_id --config /path/conf/file.cfg
-mpw --update --id uniq_id --wallet wallet_name
-mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+	mpw --update --id uniq_id
+	mpw --update --id uniq_id --config /path/conf/file.cfg
+	mpw --update --id uniq_id --wallet wallet_name
+	mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Delete an item
-mpw --delete --id uniq_id
-mpw --delete --id uniq_id --config /path/conf/file.cfg
-mpw --delete --id uniq_id --wallet wallet_name
-mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+	mpw --delete --id uniq_id
+	mpw --delete --id uniq_id --config /path/conf/file.cfg
+	mpw --delete --id uniq_id --wallet wallet_name
+	mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Show an item
-mpw --show 'string to search'
-mpw --show 'string to search' --config /path/conf/file.cfg
-mpw --show 'string to search' --wallet wallet_name
-mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
-mpw --show 'string to search' --group group_name
-mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
-mpw --show 'string to search' --group group_name --wallet wallet_name
-mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
+	mpw --show 'string to search'
+	mpw --show 'string to search' --config /path/conf/file.cfg
+	mpw --show 'string to search' --wallet wallet_name
+	mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
+	mpw --show 'string to search' --group group_name
+	mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
+	mpw --show 'string to search' --group group_name --wallet wallet_name
+	mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
 
 * Export data in YAML file
-mpw --export --file /path/file/to/export.yml
-mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
-mpw --export --file /path/file/to/export.yml --wallet wallet_name
-mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+	mpw --export --file /path/file/to/export.yml
+	mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
+	mpw --export --file /path/file/to/export.yml --wallet wallet_name
+	mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 * Import data from YAML file
-mpw --import --file /path/file/to/export.yml
-mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
-mpw --import --file /path/file/to/export.yml --wallet wallet_name
-mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+	mpw --import --file /path/file/to/export.yml
+	mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
+	mpw --import --file /path/file/to/export.yml --wallet wallet_name
+	mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 Format file to import:
 
-1:
-  name: Website perso
-  group: Perso
-  host: localhost.local
-  protocol: ftp
-  user: test
-  password: letoortue
-  port: 21
-  comment: Mysuper website
-2:
-  name: Linuxfr
-  group: Pro
-  host: Linuxfr.org
-  protocol: https
-  user: test
-  password: coucou 
-  port: 
-  comment: 
-
-
+	1:
+	  name: Website perso
+	  group: Perso
+	  host: localhost.local
+	  protocol: ftp
+	  user: test
+	  password: letoortue
+	  port: 21
+	  comment: Mysuper website
+	2:
+	  name: Linuxfr
+	  group: Pro
+	  host: Linuxfr.org
+	  protocol: https
+	  user: test
+	  password: coucou 
+	  port: 
+	  comment: 

From 56eaae86b745feb93653d898bbf73737be2af3a8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 20:48:57 +0200
Subject: [PATCH 255/531] update readme

---
 README.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/README.md b/README.md
index 5d79fb1..74bf554 100644
--- a/README.md
+++ b/README.md
@@ -16,32 +16,38 @@ This program work with ruby >= 2.0
 	mpw --help
 
 * Setup a new config file
+
 	mpw --setup
 	mpw --setup --config /path/conf/file.cfg
 
 * Create and setup a new wallet
+
 	mpw --setup-wallet --wallet new_wallet_name
 	mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
 
 * Add a new item in wallet
+
 	mpw --add 
 	mpw --add --config /path/conf/file.cfg
 	mpw --add --wallet wallet_name
 	mpw --add --config /path/conf/file.cfg --wallet wallet_name
 
 * Update an item
+
 	mpw --update --id uniq_id
 	mpw --update --id uniq_id --config /path/conf/file.cfg
 	mpw --update --id uniq_id --wallet wallet_name
 	mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Delete an item
+
 	mpw --delete --id uniq_id
 	mpw --delete --id uniq_id --config /path/conf/file.cfg
 	mpw --delete --id uniq_id --wallet wallet_name
 	mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Show an item
+
 	mpw --show 'string to search'
 	mpw --show 'string to search' --config /path/conf/file.cfg
 	mpw --show 'string to search' --wallet wallet_name
@@ -52,12 +58,14 @@ This program work with ruby >= 2.0
 	mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
 
 * Export data in YAML file
+
 	mpw --export --file /path/file/to/export.yml
 	mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
 	mpw --export --file /path/file/to/export.yml --wallet wallet_name
 	mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 * Import data from YAML file
+
 	mpw --import --file /path/file/to/export.yml
 	mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
 	mpw --import --file /path/file/to/export.yml --wallet wallet_name

From 32ba65a558e3f0dc932e45d7a0987327fe35062a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 20:51:11 +0200
Subject: [PATCH 256/531] update readme

---
 README.md | 102 +++++++++++++++++++++++++++---------------------------
 1 file changed, 51 insertions(+), 51 deletions(-)

diff --git a/README.md b/README.md
index 74bf554..c75f0dc 100644
--- a/README.md
+++ b/README.md
@@ -13,81 +13,81 @@ This program work with ruby >= 2.0
 # How to use
 
 * Show help
-	mpw --help
+    mpw --help
 
 * Setup a new config file
 
-	mpw --setup
-	mpw --setup --config /path/conf/file.cfg
+    mpw --setup
+    mpw --setup --config /path/conf/file.cfg
 
 * Create and setup a new wallet
 
-	mpw --setup-wallet --wallet new_wallet_name
-	mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
+    mpw --setup-wallet --wallet new_wallet_name
+    mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
 
 * Add a new item in wallet
 
-	mpw --add 
-	mpw --add --config /path/conf/file.cfg
-	mpw --add --wallet wallet_name
-	mpw --add --config /path/conf/file.cfg --wallet wallet_name
+    mpw --add 
+    mpw --add --config /path/conf/file.cfg
+    mpw --add --wallet wallet_name
+    mpw --add --config /path/conf/file.cfg --wallet wallet_name
 
 * Update an item
 
-	mpw --update --id uniq_id
-	mpw --update --id uniq_id --config /path/conf/file.cfg
-	mpw --update --id uniq_id --wallet wallet_name
-	mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+    mpw --update --id uniq_id
+    mpw --update --id uniq_id --config /path/conf/file.cfg
+    mpw --update --id uniq_id --wallet wallet_name
+    mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Delete an item
 
-	mpw --delete --id uniq_id
-	mpw --delete --id uniq_id --config /path/conf/file.cfg
-	mpw --delete --id uniq_id --wallet wallet_name
-	mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+    mpw --delete --id uniq_id
+    mpw --delete --id uniq_id --config /path/conf/file.cfg
+    mpw --delete --id uniq_id --wallet wallet_name
+    mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Show an item
 
-	mpw --show 'string to search'
-	mpw --show 'string to search' --config /path/conf/file.cfg
-	mpw --show 'string to search' --wallet wallet_name
-	mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
-	mpw --show 'string to search' --group group_name
-	mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
-	mpw --show 'string to search' --group group_name --wallet wallet_name
-	mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
+    mpw --show 'string to search'
+    mpw --show 'string to search' --config /path/conf/file.cfg
+    mpw --show 'string to search' --wallet wallet_name
+    mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
+    mpw --show 'string to search' --group group_name
+    mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
+    mpw --show 'string to search' --group group_name --wallet wallet_name
+    mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
 
 * Export data in YAML file
 
-	mpw --export --file /path/file/to/export.yml
-	mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
-	mpw --export --file /path/file/to/export.yml --wallet wallet_name
-	mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+    mpw --export --file /path/file/to/export.yml
+    mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
+    mpw --export --file /path/file/to/export.yml --wallet wallet_name
+    mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 * Import data from YAML file
 
-	mpw --import --file /path/file/to/export.yml
-	mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
-	mpw --import --file /path/file/to/export.yml --wallet wallet_name
-	mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+    mpw --import --file /path/file/to/export.yml
+    mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
+    mpw --import --file /path/file/to/export.yml --wallet wallet_name
+    mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 Format file to import:
 
-	1:
-	  name: Website perso
-	  group: Perso
-	  host: localhost.local
-	  protocol: ftp
-	  user: test
-	  password: letoortue
-	  port: 21
-	  comment: Mysuper website
-	2:
-	  name: Linuxfr
-	  group: Pro
-	  host: Linuxfr.org
-	  protocol: https
-	  user: test
-	  password: coucou 
-	  port: 
-	  comment: 
+    1:
+      name: Website perso
+      group: Perso
+      host: localhost.local
+      protocol: ftp
+      user: test
+      password: letoortue
+      port: 21
+      comment: Mysuper website
+    2:
+      name: Linuxfr
+      group: Pro
+      host: Linuxfr.org
+      protocol: https
+      user: test
+      password: coucou 
+      port: 
+      comment: 

From d7c8aa314299004d7c8a9f7109fd20963e04d5f3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 21:04:49 +0200
Subject: [PATCH 257/531] update readme

---
 README.md | 102 +++++++++++++++++++++++++++---------------------------
 1 file changed, 51 insertions(+), 51 deletions(-)

diff --git a/README.md b/README.md
index c75f0dc..b3c9942 100644
--- a/README.md
+++ b/README.md
@@ -13,81 +13,81 @@ This program work with ruby >= 2.0
 # How to use
 
 * Show help
-    mpw --help
+> mpw --help
 
 * Setup a new config file
 
-    mpw --setup
-    mpw --setup --config /path/conf/file.cfg
+> mpw --setup
+> mpw --setup --config /path/conf/file.cfg
 
 * Create and setup a new wallet
 
-    mpw --setup-wallet --wallet new_wallet_name
-    mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
+> mpw --setup-wallet --wallet new_wallet_name
+> mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
 
 * Add a new item in wallet
 
-    mpw --add 
-    mpw --add --config /path/conf/file.cfg
-    mpw --add --wallet wallet_name
-    mpw --add --config /path/conf/file.cfg --wallet wallet_name
+> mpw --add 
+> mpw --add --config /path/conf/file.cfg
+> mpw --add --wallet wallet_name
+> mpw --add --config /path/conf/file.cfg --wallet wallet_name
 
 * Update an item
 
-    mpw --update --id uniq_id
-    mpw --update --id uniq_id --config /path/conf/file.cfg
-    mpw --update --id uniq_id --wallet wallet_name
-    mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+> mpw --update --id uniq_id
+> mpw --update --id uniq_id --config /path/conf/file.cfg
+> mpw --update --id uniq_id --wallet wallet_name
+> mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Delete an item
 
-    mpw --delete --id uniq_id
-    mpw --delete --id uniq_id --config /path/conf/file.cfg
-    mpw --delete --id uniq_id --wallet wallet_name
-    mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+> mpw --delete --id uniq_id
+> mpw --delete --id uniq_id --config /path/conf/file.cfg
+> mpw --delete --id uniq_id --wallet wallet_name
+> mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
 
 * Show an item
 
-    mpw --show 'string to search'
-    mpw --show 'string to search' --config /path/conf/file.cfg
-    mpw --show 'string to search' --wallet wallet_name
-    mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
-    mpw --show 'string to search' --group group_name
-    mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
-    mpw --show 'string to search' --group group_name --wallet wallet_name
-    mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
+> mpw --show 'string to search'
+> mpw --show 'string to search' --config /path/conf/file.cfg
+> mpw --show 'string to search' --wallet wallet_name
+> mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
+> mpw --show 'string to search' --group group_name
+> mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
+> mpw --show 'string to search' --group group_name --wallet wallet_name
+> mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
 
 * Export data in YAML file
 
-    mpw --export --file /path/file/to/export.yml
-    mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
-    mpw --export --file /path/file/to/export.yml --wallet wallet_name
-    mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+> mpw --export --file /path/file/to/export.yml
+> mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
+> mpw --export --file /path/file/to/export.yml --wallet wallet_name
+> mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 * Import data from YAML file
 
-    mpw --import --file /path/file/to/export.yml
-    mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
-    mpw --import --file /path/file/to/export.yml --wallet wallet_name
-    mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+> mpw --import --file /path/file/to/export.yml
+> mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
+> mpw --import --file /path/file/to/export.yml --wallet wallet_name
+> mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
 
 Format file to import:
 
-    1:
-      name: Website perso
-      group: Perso
-      host: localhost.local
-      protocol: ftp
-      user: test
-      password: letoortue
-      port: 21
-      comment: Mysuper website
-    2:
-      name: Linuxfr
-      group: Pro
-      host: Linuxfr.org
-      protocol: https
-      user: test
-      password: coucou 
-      port: 
-      comment: 
+> 1:
+>   name: Website perso
+>   group: Perso
+>   host: localhost.local
+>   protocol: ftp
+>   user: test
+>   password: letoortue
+>   port: 21
+>   comment: Mysuper website
+> 2:
+>   name: Linuxfr
+>   group: Pro
+>   host: Linuxfr.org
+>   protocol: https
+>   user: test
+>   password: coucou 
+>   port: 
+>   comment: 

From 6ca9e8bffe982b415e33b034fe9c05646501a417 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 21:39:05 +0200
Subject: [PATCH 258/531] update readme

---
 README.md | 123 +++++++++++++++++++++++++++++-------------------------
 1 file changed, 65 insertions(+), 58 deletions(-)

diff --git a/README.md b/README.md
index b3c9942..7958e71 100644
--- a/README.md
+++ b/README.md
@@ -13,81 +13,88 @@ This program work with ruby >= 2.0
 # How to use
 
 * Show help
-> mpw --help
+mpw --help
 
 * Setup a new config file
 
-> mpw --setup
-> mpw --setup --config /path/conf/file.cfg
+mpw --setup
+mpw --setup --config /path/conf/file.cfg
 
 * Create and setup a new wallet
 
-> mpw --setup-wallet --wallet new_wallet_name
-> mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
+mpw --setup-wallet --wallet new_wallet_name
+mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
 
 * Add a new item in wallet
-
-> mpw --add 
-> mpw --add --config /path/conf/file.cfg
-> mpw --add --wallet wallet_name
-> mpw --add --config /path/conf/file.cfg --wallet wallet_name
+```
+mpw --add 
+mpw --add --config /path/conf/file.cfg
+mpw --add --wallet wallet_name
+mpw --add --config /path/conf/file.cfg --wallet wallet_name
+```
 
 * Update an item
-
-> mpw --update --id uniq_id
-> mpw --update --id uniq_id --config /path/conf/file.cfg
-> mpw --update --id uniq_id --wallet wallet_name
-> mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+```
+mpw --update --id uniq_id
+mpw --update --id uniq_id --config /path/conf/file.cfg
+mpw --update --id uniq_id --wallet wallet_name
+mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+```
 
 * Delete an item
-
-> mpw --delete --id uniq_id
-> mpw --delete --id uniq_id --config /path/conf/file.cfg
-> mpw --delete --id uniq_id --wallet wallet_name
-> mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+```
+mpw --delete --id uniq_id
+mpw --delete --id uniq_id --config /path/conf/file.cfg
+mpw --delete --id uniq_id --wallet wallet_name
+mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
+```
 
 * Show an item
-
-> mpw --show 'string to search'
-> mpw --show 'string to search' --config /path/conf/file.cfg
-> mpw --show 'string to search' --wallet wallet_name
-> mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
-> mpw --show 'string to search' --group group_name
-> mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
-> mpw --show 'string to search' --group group_name --wallet wallet_name
-> mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
+```
+mpw --show 'string to search'
+mpw --show 'string to search' --config /path/conf/file.cfg
+mpw --show 'string to search' --wallet wallet_name
+mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
+mpw --show 'string to search' --group group_name
+mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
+mpw --show 'string to search' --group group_name --wallet wallet_name
+mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
+```
 
 * Export data in YAML file
-
-> mpw --export --file /path/file/to/export.yml
-> mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
-> mpw --export --file /path/file/to/export.yml --wallet wallet_name
-> mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+```
+mpw --export --file /path/file/to/export.yml
+mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
+mpw --export --file /path/file/to/export.yml --wallet wallet_name
+mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+```
 
 * Import data from YAML file
-
-> mpw --import --file /path/file/to/export.yml
-> mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
-> mpw --import --file /path/file/to/export.yml --wallet wallet_name
-> mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+```
+mpw --import --file /path/file/to/export.yml
+mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
+mpw --import --file /path/file/to/export.yml --wallet wallet_name
+mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
+```
 
 Format file to import:
-
-> 1:
->   name: Website perso
->   group: Perso
->   host: localhost.local
->   protocol: ftp
->   user: test
->   password: letoortue
->   port: 21
->   comment: Mysuper website
-> 2:
->   name: Linuxfr
->   group: Pro
->   host: Linuxfr.org
->   protocol: https
->   user: test
->   password: coucou 
->   port: 
->   comment: 
+```
+1:
+  name: Website perso
+  group: Perso
+  host: localhost.local
+  protocol: ftp
+  user: test
+  password: letoortue
+  port: 21
+  comment: Mysuper website
+2:
+  name: Linuxfr
+  group: Pro
+  host: Linuxfr.org
+  protocol: https
+  user: test
+  password: coucou 
+  port: 
+  comment: 
+```

From 240af672abb052bdd1c210fda0805cfe3da07738 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 16 May 2016 21:53:24 +0200
Subject: [PATCH 259/531] update readme

---
 README.md | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 7958e71..a6b4dee 100644
--- a/README.md
+++ b/README.md
@@ -13,17 +13,44 @@ This program work with ruby >= 2.0
 # How to use
 
 * Show help
+```
 mpw --help
+```
 
 * Setup a new config file
-
+```
 mpw --setup
 mpw --setup --config /path/conf/file.cfg
+```
 
 * Create and setup a new wallet
-
+```
 mpw --setup-wallet --wallet new_wallet_name
 mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
+```
+
+* Add a GPG key in wallet
+```
+mpw --add --key root@localhost.local
+mpw --add --key root@localhost.local --config /path/conf/file.cfg 
+mpw --add --key root@localhost.local --wallet wallet_name
+mpw --add --key root@localhost.local --config /path/conf/file.cfg --wallet wallet_name
+```
+
+* Add a new  GPG key in wallet
+```
+mpw --add --key root@localhost.local --file /path/gpg/file.pub
+mpw --add --key root@localhost.local --file /path/gpg/file.pub --config /path/conf/file.cfg 
+mpw --add --key root@localhost.local --file /path/gpg/file.pub --wallet wallet_name
+mpw --add --key root@localhost.local --file /path/gpg/file.pub --config /path/conf/file.cfg --wallet wallet_name
+```
+
+* Delete a GPG key in wallet
+```
+mpw --delete --key root@localhost.local
+mpw --delete --key root@localhost.local --wallet wallet_name
+mpw --delete --key root@localhost.local --wallet wallet_name --config /path/conf/file.cfg 
+```
 
 * Add a new item in wallet
 ```

From 53cb745eb4b2568a521a07d2fe88404e793e9b77 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 17 May 2016 19:25:30 +0200
Subject: [PATCH 260/531] fix bug when create recursive config folder

---
 lib/mpw/config.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 1eaaa0e..85b863b 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -5,6 +5,7 @@
 require 'gpgme'
 require 'yaml'
 require 'i18n'
+require 'fileutils'
 	
 module MPW
 class Config
@@ -55,7 +56,7 @@ class Config
 		                      }
 		         }
 
-		Dir.mkdir(wallet_dir, 0700)
+		FileUtils.mkdir_p(wallet_dir, mode: 0700)
 		File.open(@config_file, 'w') do |file|
 			file << config.to_yaml
 		end

From 8a89339691fe66ec7e36f7a32920da9b32102b1a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 17 May 2016 19:34:05 +0200
Subject: [PATCH 261/531] fix translate

---
 i18n/en.yml       | 12 ++++++------
 i18n/fr.yml       |  2 +-
 lib/mpw/ui/cli.rb |  2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index a81fced..5c36344 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -84,13 +84,13 @@ en:
       not_valid: "No data to import!"
     setup_config:
       title: "Setup a new config file"
-      lang: "Choose your language (en, fr, ...): "
-      gpg_key: "Enter the GPG key: " 
-      wallet_dir: "Enter the wallets's folder path [défaut=%{home}/wallets]: "
+      lang: "Choose your language (en, fr, ...) [default=%{lang}]: "
+      gpg_key: "Enter the GPG key [ex: test@host.local]: " 
+      wallet_dir: "Enter the wallets's folder path [default=%{home}/wallets]: "
       valid: "The config file has been created!"
     setup_wallet:
       title: "Wallet setup"
-      sync_type: "Synchronization type (mpw, ssh, ftp, or nil): "
+      sync_type: "Synchronization type (ssh, ftp, or nil): "
       sync_host: "Synchronization server: "
       sync_port: "Port of the synchronization server: "
       sync_user: "Username for the synchronization: "
@@ -126,11 +126,11 @@ en:
   display:
     comment: "Comment"
     error: "ERROR"
-    gpg_password: "Password GPG: "
+    gpg_password: "GPG passphrase: "
     group: "Group"
     login: "Login"
     name: "Name"
-    nothing: "Nothing result!"
+    nothing: "No matches!"
     password: "Password"
     port: "Port"
     protocol: "Protocol"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 22309f9..f273aff 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -85,7 +85,7 @@ fr:
     setup_config:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
-      gpg_key: "Entrez la clé GPG: " 
+      gpg_key: "Entrez la clé GPG [ex: test@host.local]: " 
       wallet_dir: "Entrez le chemin du répertoire qui contiendra les porte-feuilles de mot de passe [défaut=%{home}/wallets]: "
       valid: "Le fichier de configuration a bien été créé!"
     setup_wallet:
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index a41657f..a0d5b29 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -322,7 +322,7 @@ class Cli
 	def export(file)
 		@mpw.export(file)
 
-		puts "#{I18n.t('export.valid', file)}".green
+		puts "#{I18n.t('export.export.valid', file)}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #17: #{e}".red
 	end

From 5287c4f122d82b7a36c8b034f3e895674ec45fa5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 30 Jun 2016 22:02:04 +0200
Subject: [PATCH 262/531] fix Gemfile

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

diff --git a/Gemfile b/Gemfile
index 14a22b1..ef153b8 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,4 +4,5 @@ gem 'i18n'
 gem 'locale'
 gem 'gpgme'
 gem 'colorize'
-gem 'net-sftp'
+gem 'net-ssh'
+gem 'net-scp'

From 4ea4376149fd784896daa0a1e4d7a95bac7582d5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 30 Jun 2016 22:02:45 +0200
Subject: [PATCH 263/531] add license in source file

---
 bin/mpw             | 18 ++++++++++++++++--
 lib/mpw/config.rb   | 18 ++++++++++++++++--
 lib/mpw/item.rb     | 18 ++++++++++++++++--
 lib/mpw/mpw.rb      | 18 ++++++++++++++++--
 lib/mpw/sync/ftp.rb | 18 ++++++++++++++++--
 lib/mpw/sync/ssh.rb | 18 ++++++++++++++++--
 lib/mpw/ui/cli.rb   | 18 ++++++++++++++++--
 7 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 1b8d552..245c8ab 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
 require 'pathname'
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 85b863b..49345d7 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'gpgme'
 require 'yaml'
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 660f74a..7f0459a 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'i18n'
 	
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index cb7e57d..14f7ab8 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'rubygems/package'
 require 'gpgme'
diff --git a/lib/mpw/sync/ftp.rb b/lib/mpw/sync/ftp.rb
index 71d1624..d64e629 100644
--- a/lib/mpw/sync/ftp.rb
+++ b/lib/mpw/sync/ftp.rb
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'i18n'
 require 'net/ftp'
diff --git a/lib/mpw/sync/ssh.rb b/lib/mpw/sync/ssh.rb
index 45dae3a..4471949 100644
--- a/lib/mpw/sync/ssh.rb
+++ b/lib/mpw/sync/ssh.rb
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'i18n'
 require 'net/ssh'
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index a0d5b29..bccb9be 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -1,6 +1,20 @@
 #!/usr/bin/ruby
-# author: nishiki
-# mail: nishiki@yaegashi.fr
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'readline'
 require 'i18n'

From 8d73f9cf474f47eaeec791a646d65c3906ded55e Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 30 Jun 2016 22:24:51 +0200
Subject: [PATCH 264/531] update gemspec file

---
 mpw.gemspec | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/mpw.gemspec b/mpw.gemspec
index d004d34..5024cd1 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -5,22 +5,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
 Gem::Specification.new do |spec|
   spec.name          = 'mpw'
   spec.version       = File.open('VERSION').read
-  spec.authors       = ['nishiki']
-  spec.email         = ['gems@yae.im']
-  spec.summary       = 'Manage your password'
-  spec.description   = 'Save and read your password with gpg'
+  spec.authors       = ['Adrien Waksberg']
+  spec.email         = ['mpw@yae.im']
+  spec.summary       = 'MPW is a software to crypt and manage your passwords'
+  spec.description   = 'Manage your passwords in all security with MPW, we use GPG to crypt your passwords'
   spec.homepage      = 'https://github.com/nishiki/manage-password'
-  spec.license       = 'GPL'
+  spec.license       = 'GPL-2.0'
 
   spec.files         = `git ls-files -z`.split("\x0")
-  spec.executables   = ['mpw', 'mpw-server', 'mpw-ssh']
+  spec.executables   = ['mpw']
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ['lib']
 
-  spec.add_dependency "i18n", "~> 0.6", ">= 0.6.9"
+  spec.add_dependency "i18n"
   spec.add_dependency "gpgme"
   spec.add_dependency "highline"
   spec.add_dependency "locale"
   spec.add_dependency "colorize"
-  spec.add_dependency "net-sftp"
+  spec.add_dependency "net-ssh"
+  spec.add_dependency "net-scp"
 end

From 19d0f749c82ca00e1c7c1ef0ce9110638f2e5430 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 30 Jun 2016 22:25:20 +0200
Subject: [PATCH 265/531] change version for 3.0-beta

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

diff --git a/VERSION b/VERSION
index 50ffc5a..0f982ff 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.3
+3.0.0-beta

From 0238e75d36c611ff3f0f00918d02d91236587b8a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 1 Jul 2016 09:15:39 +0200
Subject: [PATCH 266/531] no sync if last_sync == remote.last_sync

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 14f7ab8..a07061c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -336,6 +336,8 @@ class MPW
 
 		File.unlink(tmp_file) if File.exist?(tmp_file)
 
+		return if remote.get_last_sync == get_last_sync
+
 		if not remote.to_s.empty?
 			@data.each do |item|
 				update = false

From e12b701c4b200713c72903f7571cf7648371e2d9 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 1 Jul 2016 09:22:28 +0200
Subject: [PATCH 267/531] add option no sync

---
 bin/mpw           |  2 +-
 lib/mpw/ui/cli.rb | 18 ++++++++++--------
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 245c8ab..080c58a 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -143,7 +143,7 @@ end.parse!
 # --------------------------------------------------------- #
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config)
+cli    = MPW::Cli.new(config, options[:sync])
 	
 # Setup a new config 
 if not config.is_valid? or not options[:setup].nil?
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index bccb9be..5965dfb 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -29,9 +29,11 @@ module MPW
 class Cli
 
 	# Constructor
-	# @args: config_file -> a specify config file
-	def initialize(config)
+	# @args: config -> the config
+	#        sync -> boolean for sync or not
+	def initialize(config, sync=true)
 		@config = config
+		@sync   = sync
 	end
 
 	# Create a new config file
@@ -124,7 +126,7 @@ class Cli
 		end
 
 		@mpw.read_data
-		@mpw.sync
+		@mpw.sync if @sync
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #11: #{e}".red
 		exit 2
@@ -224,7 +226,7 @@ class Cli
 	def add_key(key, file=nil)
 		@mpw.add_key(key, file)
 		@mpw.write_data
-		@mpw.sync
+		@mpw.sync if @sync
 
 		puts "#{I18n.t('form.add_key.valid')}".green
 	rescue Exception => e
@@ -236,7 +238,7 @@ class Cli
 	def delete_key(key)
 		@mpw.delete_key(key)
 		@mpw.write_data
-		@mpw.sync
+		@mpw.sync if @sync
 
 		puts "#{I18n.t('form.delete_key.valid')}".green
 	rescue Exception => e
@@ -263,7 +265,7 @@ class Cli
 		@mpw.add(item)
 		@mpw.set_password(item.id, password)
 		@mpw.write_data
-		@mpw.sync
+		@mpw.sync if @sync
 
 		puts "#{I18n.t('form.add_item.valid')}".green
 	end
@@ -292,7 +294,7 @@ class Cli
 			item.update(options)
 			@mpw.set_password(item.id, password) if not password.empty?
 			@mpw.write_data
-			@mpw.sync
+			@mpw.sync if @sync
 
 			puts "#{I18n.t('form.update_item.valid')}".green
 		else
@@ -324,7 +326,7 @@ class Cli
 
 		item.delete
 		@mpw.write_data
-		@mpw.sync
+		@mpw.sync if @sync
 
 		puts "#{I18n.t('form.delete_item.valid', id: id)}".green
 	rescue Exception => e

From 0733bdbd123ef7299254e0d98a94ca041f8c74a7 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Fri, 1 Jul 2016 09:28:56 +0200
Subject: [PATCH 268/531] minor syntax fix

---
 lib/mpw/ui/cli.rb | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 5965dfb..3bfe30a 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -102,12 +102,12 @@ class Cli
 
 		puts I18n.t('form.setup_wallet.title')
 		puts '--------------------'
-		config['sync']['type']      = ask(I18n.t('form.setup_wallet.sync_type')).to_s
-		config['sync']['host']      = ask(I18n.t('form.setup_wallet.sync_host')).to_s
-		config['sync']['port']      = ask(I18n.t('form.setup_wallet.sync_port')).to_s
-		config['sync']['user']      = ask(I18n.t('form.setup_wallet.sync_user')).to_s
-		config['sync']['password']  = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
-		config['sync']['path']      = ask(I18n.t('form.setup_wallet.sync_path')).to_s
+		config['sync']['type']     = ask(I18n.t('form.setup_wallet.sync_type')).to_s
+		config['sync']['host']     = ask(I18n.t('form.setup_wallet.sync_host')).to_s
+		config['sync']['port']     = ask(I18n.t('form.setup_wallet.sync_port')).to_s
+		config['sync']['user']     = ask(I18n.t('form.setup_wallet.sync_user')).to_s
+		config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
+		config['sync']['path']     = ask(I18n.t('form.setup_wallet.sync_path')).to_s
 
 		@mpw.set_config(config)
 		@mpw.write_data

From 8d01c21806a82c09dff5644929fe33a11a0833f3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 13:44:04 +0200
Subject: [PATCH 269/531] release version 3.0

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

diff --git a/VERSION b/VERSION
index 0f982ff..4a36342 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.0-beta
+3.0.0

From e9eb9b0909051f283badd51f75d795a289acf3ae Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 13:59:54 +0200
Subject: [PATCH 270/531] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a6b4dee..6532a53 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # Manage your passwords!
 
 MPW is a little software which stores your passwords in an GPG encrypted file.
-MPW can synchronize your password with via SSH or FTP.
+MPW can synchronize your password with SSH or FTP.
 
 # Installation
 

From ac3b7b1dde1fa421c73b2fd56985c02baeec3f16 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 14:35:10 +0200
Subject: [PATCH 271/531] Update mpw.gemspec

---
 mpw.gemspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mpw.gemspec b/mpw.gemspec
index 5024cd1..31b851f 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
   spec.authors       = ['Adrien Waksberg']
   spec.email         = ['mpw@yae.im']
   spec.summary       = 'MPW is a software to crypt and manage your passwords'
-  spec.description   = 'Manage your passwords in all security with MPW, we use GPG to crypt your passwords'
+  spec.description   = 'Manage your passwords in all security with MPW, we use GPG to encrypt your passwords'
   spec.homepage      = 'https://github.com/nishiki/manage-password'
   spec.license       = 'GPL-2.0'
 

From 8e4027c82216e304f11a4f60b43ade4fe0b54a53 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 22:03:55 +0200
Subject: [PATCH 272/531] switch gpg2 to gpg1

---
 i18n/en.yml       | 1 +
 i18n/fr.yml       | 1 +
 lib/mpw/config.rb | 6 +++++-
 lib/mpw/mpw.rb    | 9 +++++++--
 lib/mpw/ui/cli.rb | 5 +++--
 5 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 5c36344..9121813 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -86,6 +86,7 @@ en:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...) [default=%{lang}]: "
       gpg_key: "Enter the GPG key [ex: test@host.local]: " 
+      gpg_exe: "Enter the executable GPG path (optional): "
       wallet_dir: "Enter the wallets's folder path [default=%{home}/wallets]: "
       valid: "The config file has been created!"
     setup_wallet:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index f273aff..155a177 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -86,6 +86,7 @@ fr:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
       gpg_key: "Entrez la clé GPG [ex: test@host.local]: " 
+      gpg_exe: "Entrez le chemin de l'exécutable GPG (optionnel): "
       wallet_dir: "Entrez le chemin du répertoire qui contiendra les porte-feuilles de mot de passe [défaut=%{home}/wallets]: "
       valid: "Le fichier de configuration a bien été créé!"
     setup_wallet:
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 49345d7..90df409 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -30,6 +30,7 @@ class Config
 	attr_accessor :lang
 	attr_accessor :config_dir
 	attr_accessor :wallet_dir
+	attr_accessor :gpg_exe
 
 	# Constructor
 	# @args: config_file -> the specify config file
@@ -53,8 +54,9 @@ class Config
 	# @args: key -> the gpg key to encrypt
 	#        lang -> the software language
 	#        wallet_dir -> the  directory where are the wallets password
+	#        gpg_exe -> the  path of gpg executable
 	# @rtrn: true if le config file is create
-	def setup(key, lang, wallet_dir)
+	def setup(key, lang, wallet_dir, gpg_exe)
 
 		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 			raise I18n.t('error.config.key_bad_format')
@@ -67,6 +69,7 @@ class Config
 		config = {'config' => {'key'        => key,
 		                       'lang'       => lang,
 		                       'wallet_dir' => wallet_dir,
+		                       'gpg_exe'    => gpg_exe,
 		                      }
 		         }
 
@@ -118,6 +121,7 @@ class Config
 		@key        = config['config']['key']
 		@lang       = config['config']['lang']
 		@wallet_dir = config['config']['wallet_dir']
+		@gpg_exe    = config['config']['gpg_exe']
 
 		raise if @key.empty? or @wallet_dir.empty?
 			
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index a07061c..f2b2ddc 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -28,10 +28,15 @@ module MPW
 class MPW
 
 	# Constructor
-	def initialize(key, wallet_file, gpg_pass=nil)
+	def initialize(key, wallet_file, gpg_pass=nil, gpg_exe=nil)
 		@key         = key
 		@gpg_pass    = gpg_pass
+		@gpg_exe     = gpg_exe
 		@wallet_file = wallet_file
+
+		if @gpg_exe
+			GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
+		end
 	end
 
 	# Read mpw file
@@ -331,7 +336,7 @@ class MPW
 		sync.connect
 		sync.get(tmp_file)
 
-		remote = MPW.new(@key, tmp_file, @gpg_pass)
+		remote = MPW.new(@key, tmp_file, @gpg_pass, @gpg_exe)
 		remote.read_data
 
 		File.unlink(tmp_file) if File.exist?(tmp_file)
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 3bfe30a..d72d597 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -44,13 +44,14 @@ class Cli
 		language   = ask(I18n.t('form.setup_config.lang', lang: lang)).to_s
 		key        = ask(I18n.t('form.setup_config.gpg_key')).to_s
 		wallet_dir = ask(I18n.t('form.setup_config.wallet_dir', home: "#{@config.config_dir}")).to_s
+		gpg_exe    = ask(I18n.t('form.setup_config.gpg_exe')).to_s
 
 		if language.nil? or language.empty?
 			language = lang
 		end
 		I18n.locale = language.to_sym
 
-		@config.setup(key, lang, wallet_dir)
+		@config.setup(key, lang, wallet_dir, gpg_exe)
 
 		raise I18n.t('error.config.check') if not @config.is_valid?
 
@@ -122,7 +123,7 @@ class Cli
 	def decrypt
 		if not defined?(@mpw)
 			@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
-			@mpw = MPW.new(@config.key, @wallet_file, @password)
+			@mpw      = MPW.new(@config.key, @wallet_file, @password, @config.gpg_exe)
 		end
 
 		@mpw.read_data

From 91242936074bd36fdff67b5e29beaf55afbd68ed Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 22:39:10 +0200
Subject: [PATCH 273/531] fix translate

---
 i18n/en.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 9121813..6e20e46 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -23,7 +23,7 @@ en:
     update:
       name_empty: "You must define a name!"
     sync: 
-      general: "An error is appeared durint the sync"
+      general: "An error is appeared during the sync"
       connection: "Connection fail!"
       communication: "A communication problem with the server is appeared!"
       download: "Can't download the file!"

From 25a64d0303fdbdc4cdb823ff755fcbe1859fea28 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 22:47:35 +0200
Subject: [PATCH 274/531] fix bug try sync but sync is nil

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f2b2ddc..4ee0b8c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -317,7 +317,7 @@ class MPW
 
 	# Sync data with remote file
 	def sync
-		return if @config.empty? or @config['sync'].nil?
+		return if @config.empty? or @config['sync']['type'].to_s.empty?
 		
 		tmp_file  = "#{@wallet_file}.sync"
 		last_sync = @config['last_sync'].to_i

From 9565d40668f859ce0cf4a3173e2ab1dcd8e93f6f Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 22:48:21 +0200
Subject: [PATCH 275/531] fix translate

---
 i18n/en.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 6e20e46..d18ecee 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -91,7 +91,7 @@ en:
       valid: "The config file has been created!"
     setup_wallet:
       title: "Wallet setup"
-      sync_type: "Synchronization type (ssh, ftp, or nil): "
+      sync_type: "Synchronization type (ssh, ftp): "
       sync_host: "Synchronization server: "
       sync_port: "Port of the synchronization server: "
       sync_user: "Username for the synchronization: "

From 6bbee4d2cee37417944e7dcc217981be27673b99 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 22:59:52 +0200
Subject: [PATCH 276/531] ui: no show wallet setup form if sync is nil

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

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index d72d597..433df6f 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -104,11 +104,14 @@ class Cli
 		puts I18n.t('form.setup_wallet.title')
 		puts '--------------------'
 		config['sync']['type']     = ask(I18n.t('form.setup_wallet.sync_type')).to_s
-		config['sync']['host']     = ask(I18n.t('form.setup_wallet.sync_host')).to_s
-		config['sync']['port']     = ask(I18n.t('form.setup_wallet.sync_port')).to_s
-		config['sync']['user']     = ask(I18n.t('form.setup_wallet.sync_user')).to_s
-		config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
-		config['sync']['path']     = ask(I18n.t('form.setup_wallet.sync_path')).to_s
+
+		if ['ftp', 'ssh'].include?(config['sync']['type'].downcase)
+			config['sync']['host']     = ask(I18n.t('form.setup_wallet.sync_host')).to_s
+			config['sync']['port']     = ask(I18n.t('form.setup_wallet.sync_port')).to_s
+			config['sync']['user']     = ask(I18n.t('form.setup_wallet.sync_user')).to_s
+			config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
+			config['sync']['path']     = ask(I18n.t('form.setup_wallet.sync_path')).to_s
+		end
 
 		@mpw.set_config(config)
 		@mpw.write_data

From 50ea811b612c2cb07a669e75cc37edc139514cc7 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 23:01:27 +0200
Subject: [PATCH 277/531] fix translate

---
 i18n/en.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index d18ecee..e6b7a78 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -23,7 +23,7 @@ en:
     update:
       name_empty: "You must define a name!"
     sync: 
-      general: "An error is appeared during the sync"
+      general: "An error has appeared during the sync"
       connection: "Connection fail!"
       communication: "A communication problem with the server is appeared!"
       download: "Can't download the file!"

From 4502c43424c5ebd4f8bc8d0a5d739307088ff811 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 5 Jul 2016 23:14:12 +0200
Subject: [PATCH 278/531] catch backstrace when interrupt

---
 bin/mpw | 117 +++++++++++++++++++++++++++++---------------------------
 1 file changed, 61 insertions(+), 56 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 080c58a..8e72668 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -142,61 +142,66 @@ end.parse!
 # Main
 # --------------------------------------------------------- #
 
-config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+begin
+	config = MPW::Config.new(options[:config])
+	cli    = MPW::Cli.new(config, options[:sync])
+		
+	# Setup a new config 
+	if not config.is_valid? or not options[:setup].nil?
+		cli.setup(lang)
+	elsif not config.check_gpg_key?
+		cli.setup_gpg_key
+	end
 	
-# Setup a new config 
-if not config.is_valid? or not options[:setup].nil?
-	cli.setup(lang)
-elsif not config.check_gpg_key?
-	cli.setup_gpg_key
+	cli.get_wallet(options[:wallet])
+	cli.decrypt
+	
+	# Display the item's informations
+	if not options[:show].nil?
+		opts = {search:    options[:show],
+		        group:     options[:group],
+		       }
+	
+		cli.display(opts)
+	
+	# Remove an item
+	elsif not options[:delete].nil? and not options[:id].nil?
+		cli.delete(options[:id], options[:force])
+	
+	# Update an item
+	elsif not options[:update].nil? and not options[:id].nil?
+		cli.update(options[:id])
+	
+	# Add a new item
+	elsif not options[:add].nil? and options[:key].nil?
+		cli.add
+	
+	# Add a new public key in wallet
+	elsif not options[:add].nil? and not options[:key].nil?
+		cli.add_key(options[:key], options[:file])
+	
+	# Delete a public key in wallet
+	elsif not options[:delete].nil? and not options[:key].nil?
+		cli.delete_key(options[:key])
+	
+	# Export
+	elsif not options[:export].nil? and not options[:file].nil?
+		cli.export(options[:file])
+	
+	# Add a new item
+	elsif not options[:import].nil? and not options[:file].nil?
+		cli.import(options[:file])
+	
+	# Setup wallet config
+	elsif not options[:setup_wallet].nil?
+		cli.setup_wallet_config
+	
+	end
+	
+	cli = nil
+	
+	exit 0
+
+rescue SystemExit, Interrupt
+	exit 3
 end
-
-cli.get_wallet(options[:wallet])
-cli.decrypt
-
-# Display the item's informations
-if not options[:show].nil?
-	opts = {search:    options[:show],
-	        group:     options[:group],
-	       }
-
-	cli.display(opts)
-
-# Remove an item
-elsif not options[:delete].nil? and not options[:id].nil?
-	cli.delete(options[:id], options[:force])
-
-# Update an item
-elsif not options[:update].nil? and not options[:id].nil?
-	cli.update(options[:id])
-
-# Add a new item
-elsif not options[:add].nil? and options[:key].nil?
-	cli.add
-
-# Add a new public key in wallet
-elsif not options[:add].nil? and not options[:key].nil?
-	cli.add_key(options[:key], options[:file])
-
-# Delete a public key in wallet
-elsif not options[:delete].nil? and not options[:key].nil?
-	cli.delete_key(options[:key])
-
-# Export
-elsif not options[:export].nil? and not options[:file].nil?
-	cli.export(options[:file])
-
-# Add a new item
-elsif not options[:import].nil? and not options[:file].nil?
-	cli.import(options[:file])
-
-# Setup wallet config
-elsif not options[:setup_wallet].nil?
-	cli.setup_wallet_config
-
-end
-
-cli = nil
-
-exit 0

From 8bfcb29bdf817154d9413b871970c5facebff031 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 6 Jul 2016 21:40:51 +0200
Subject: [PATCH 279/531] no error after config

---
 bin/mpw | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 8e72668..ce73305 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -147,11 +147,13 @@ begin
 	cli    = MPW::Cli.new(config, options[:sync])
 		
 	# Setup a new config 
-	if not config.is_valid? or not options[:setup].nil?
+	if not options[:setup].nil?
 		cli.setup(lang)
-	elsif not config.check_gpg_key?
-		cli.setup_gpg_key
+		exit 0
 	end
+
+	cli.setup(lang)   if not config.is_valid?
+	cli.setup_gpg_key if not config.check_gpg_key?
 	
 	cli.get_wallet(options[:wallet])
 	cli.decrypt

From 10363047274447fe0c799b51c5da4632b3131e8c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 6 Jul 2016 21:48:49 +0200
Subject: [PATCH 280/531] no sync if last sync < 5min

---
 lib/mpw/mpw.rb    |  9 +++++----
 lib/mpw/ui/cli.rb | 10 +++++-----
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 4ee0b8c..69a4938 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -316,12 +316,13 @@ class MPW
 	end
 
 	# Sync data with remote file
-	def sync
+	# @args: force -> force the sync
+	def sync(force=false)
 		return if @config.empty? or @config['sync']['type'].to_s.empty?
+		return if get_last_sync < Time.now.to_i + 300 and not force
 		
 		tmp_file  = "#{@wallet_file}.sync"
-		last_sync = @config['last_sync'].to_i
-
+		
 		case @config['sync']['type']
 		when 'sftp', 'scp', 'ssh'
 			require "#{APP_ROOT}/../lib/mpw/sync/ssh.rb"
@@ -370,7 +371,7 @@ class MPW
 				end
 
 				# Remove an old item
-				if not update and item.last_sync.to_i < last_sync and item.last_edit < last_sync
+				if not update and item.last_sync.to_i < get_last_sync and item.last_edit < get_last_sync
 					item.delete
 				end
 			end
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 433df6f..bed2d31 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -230,7 +230,7 @@ class Cli
 	def add_key(key, file=nil)
 		@mpw.add_key(key, file)
 		@mpw.write_data
-		@mpw.sync if @sync
+		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_key.valid')}".green
 	rescue Exception => e
@@ -242,7 +242,7 @@ class Cli
 	def delete_key(key)
 		@mpw.delete_key(key)
 		@mpw.write_data
-		@mpw.sync if @sync
+		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.delete_key.valid')}".green
 	rescue Exception => e
@@ -269,7 +269,7 @@ class Cli
 		@mpw.add(item)
 		@mpw.set_password(item.id, password)
 		@mpw.write_data
-		@mpw.sync if @sync
+		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_item.valid')}".green
 	end
@@ -298,7 +298,7 @@ class Cli
 			item.update(options)
 			@mpw.set_password(item.id, password) if not password.empty?
 			@mpw.write_data
-			@mpw.sync if @sync
+			@mpw.sync(true) if @sync
 
 			puts "#{I18n.t('form.update_item.valid')}".green
 		else
@@ -330,7 +330,7 @@ class Cli
 
 		item.delete
 		@mpw.write_data
-		@mpw.sync if @sync
+		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.delete_item.valid', id: id)}".green
 	rescue Exception => e

From bbeeb680653e9fbaab6c1c64fb0a118f350f09c4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 6 Jul 2016 22:04:37 +0200
Subject: [PATCH 281/531] change version to 3.1.0 beta

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

diff --git a/VERSION b/VERSION
index 4a36342..b6919fe 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.0
+3.1.0-beta

From e068a2758c867e7e634fe21a67887bf5279592bd Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 9 Jul 2016 14:06:49 +0200
Subject: [PATCH 282/531] add login and password in clipboard

---
 Gemfile           |  1 +
 README.md         |  1 +
 bin/mpw           | 19 ++++++++++++-------
 i18n/en.yml       |  5 +++++
 i18n/fr.yml       |  5 +++++
 lib/mpw/ui/cli.rb | 33 +++++++++++++++++++++++++++++----
 mpw.gemspec       |  1 +
 7 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/Gemfile b/Gemfile
index ef153b8..b897ea2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,3 +6,4 @@ gem 'gpgme'
 gem 'colorize'
 gem 'net-ssh'
 gem 'net-scp'
+gem 'clipboard'
diff --git a/README.md b/README.md
index 6532a53..e3ca84c 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ MPW can synchronize your password with SSH or FTP.
 This program work with ruby >= 2.0
 
 * install ruby and rubygems on your computer
+* install xclip
 * gem install mpw
 
 # How to use
diff --git a/bin/mpw b/bin/mpw
index ce73305..52033a3 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -48,12 +48,13 @@ I18n.locale         = lang.to_sym
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-options[:force]  = false
-options[:sync]   = true
-options[:group]  = nil
-options[:config] = nil
-options[:wallet] = nil
+options             = {}
+options[:force]     = false
+options[:sync]      = true
+options[:clipboard] = true
+options[:group]     = nil
+options[:config]    = nil
+options[:wallet]    = nil
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
@@ -71,6 +72,10 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
+	opts.on('-C', '--no-clipboard', I18n.t('option.clipboard')) do
+		options[:clipboard] = false
+	end
+
 	opts.on('-d', '--delete', I18n.t('option.remove')) do
 		options[:delete] = true
 	end
@@ -144,7 +149,7 @@ end.parse!
 
 begin
 	config = MPW::Config.new(options[:config])
-	cli    = MPW::Cli.new(config, options[:sync])
+	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync])
 		
 	# Setup a new config 
 	if not options[:setup].nil?
diff --git a/i18n/en.yml b/i18n/en.yml
index e6b7a78..5426128 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -38,6 +38,7 @@ en:
   option:
     add: "Add an item or key"
     config: "Specify the configuration file to use"
+    clipboard: "Disable the clipboard feature"
     export: "Export a wallet in an yaml file"
     file: "Specify a file, to use with the options [--import | --export | --add]"
     force: "No ask to confirm when you delete an item"
@@ -72,6 +73,10 @@ en:
       port: "Enter the connection port (optional): "
       comment: "Enter a comment (optional): "
       valid: "Item has been added!"
+    clipboard:
+      clean: "The clipboard has been cleaned."
+      login: "The login has been copied in clipboard, press any key to continue."
+      password: "The password has been copied in clipboard for 30s!"
     delete_key:
       valid: "Key has been deleted!"
     delete_item:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 155a177..f371eef 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -38,6 +38,7 @@ fr:
   option:
     add: "Ajoute un élément ou une clé"
     config: "Spécifie le fichier de configuration à utiliser"
+    clipboard: "Désactive la fonction presse papier"
     export: "Exporte un portefeuille dans un fichier yaml"
     file: "Spécifie un fichier, à utiliser avec les options [--import | --export | --add]"
     force: "Ne demande pas de confirmation pour la suppression d'un élément"
@@ -72,6 +73,10 @@ fr:
       port: "Entrez le port de connexion (optionnel): "
       comment: "Entrez un commentaire (optionnel): "
       valid: "L'élément a bien été ajouté!"
+    clipboard:
+      clean: "Le presse papier a été nettoyé."
+      login: "L'identifiant a été copié dans le presse papier, pressez n'importe quelle touche pour continuer."
+      password: "Le mot de passe a été copié dans le presse papier pour 30s!"
     delete_key:
       valid: "La clé a bien été supprimée!"
     delete_item:
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index bed2d31..d2980cf 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -20,6 +20,7 @@ require 'readline'
 require 'i18n'
 require 'colorize'
 require 'highline/import'
+require 'clipboard'
 
 #TODO
 require "#{APP_ROOT}/../lib/mpw/item.rb"
@@ -31,9 +32,10 @@ class Cli
 	# Constructor
 	# @args: config -> the config
 	#        sync -> boolean for sync or not
-	def initialize(config, sync=true)
-		@config = config
-		@sync   = sync
+	def initialize(config, clipboard=true, sync=true)
+		@config    = config
+		@clipboard = clipboard
+		@sync      = sync
 	end
 
 	# Create a new config file
@@ -184,11 +186,34 @@ class Cli
 		print "#{I18n.t('display.login')}: ".cyan
 		puts  item.user
 		print "#{I18n.t('display.password')}: ".cyan
-		puts  @mpw.get_password(item.id)
+		if @clipboard
+			puts '***********'
+		else
+			puts  @mpw.get_password(item.id)
+		end
 		print "#{I18n.t('display.port')}: ".cyan
 		puts  item.port
 		print "#{I18n.t('display.comment')}: ".cyan
 		puts  item.comment
+
+		clipboard(item) if @clipboard
+	end
+
+	# Copy in clipboard the login and password
+	def clipboard(item)
+		Clipboard.copy(item.user)
+		print "\n#{I18n.t('form.clipboard.login')}".green
+		gets
+
+		Clipboard.copy(@mpw.get_password(item.id))
+		puts I18n.t('form.clipboard.password').yellow
+
+		sleep(30)
+
+		Clipboard.clear
+		puts I18n.t('form.clipboard.clean').green
+	rescue SystemExit, Interrupt
+		Clipboard.clear
 	end
 
 	# Display the wallet
diff --git a/mpw.gemspec b/mpw.gemspec
index 31b851f..bf6f3db 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
   spec.add_dependency "colorize"
   spec.add_dependency "net-ssh"
   spec.add_dependency "net-scp"
+  spec.add_dependency "clipboard"
 end

From 2ff967cded5dec7364037329859a845d8affba33 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 9 Jul 2016 17:11:18 +0200
Subject: [PATCH 283/531] minor change when list items

---
 lib/mpw/ui/cli.rb | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index d2980cf..3c8e5a5 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -147,11 +147,23 @@ class Cli
 		case result.length
 		when 0
 			puts I18n.t('display.nothing')
+
 		when 1
 			display_item(result.first)
+
 		else
-			i = 1
+			group = nil
+			i     = 1
+
+			result.sort! { |a,b| a.group.downcase <=> b.group.downcase }
+
 			result.each do |item|
+				if group != item.group
+					group = item.group
+					puts "#{I18n.t('display.group')}: #{group}".yellow
+				end
+
+				print " |_ ".yellow
 				print "#{i}: ".cyan
 				print item.name
 				print " -> #{item.comment}".magenta if not item.comment.to_s.empty?

From 2883c0d51c170c5dab10765caa965ca6fec2708c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 9 Jul 2016 17:12:10 +0200
Subject: [PATCH 284/531] fix translate

---
 i18n/en.yml | 2 +-
 i18n/fr.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 5426128..42b6f37 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -52,7 +52,7 @@ en:
     setup: "Create a new configuration file" 
     setup_wallet: "Create a new configuration file for a wallet"
     show: "Search and show the items"
-    show_all: "Show all items"
+    show_all: "List all items"
     remove: "Delete an item"
     update: "Update an item"
     usage: "Usage"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index f371eef..882a123 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -52,7 +52,7 @@ fr:
     setup: "Création d'un nouveau fichier de configuration"
     setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
     show: "Recherche et affiche les éléments"
-    show_all: "Affiche tous les éléments"
+    show_all: "Liste tous les éléments"
     remove: "Supprime un élément"
     update: "Met à jour un élément"
     usage: "Utilisation"

From b233186e20919beeb58c598c1abdcfdfbd7befb8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 9 Jul 2016 17:15:03 +0200
Subject: [PATCH 285/531] update version 3.1.0

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

diff --git a/VERSION b/VERSION
index b6919fe..fd2a018 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.1.0-beta
+3.1.0

From 7cde7de1ed426a20f335b1cb15db6658ce03c7bd Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 10 Jul 2016 11:00:04 +0200
Subject: [PATCH 286/531] update version 3.2.0-beta

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

diff --git a/VERSION b/VERSION
index fd2a018..db8f8da 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.1.0
+3.2.0-beta

From 712ac9d99829ec91339a031d95fa5522f715a763 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 10 Jul 2016 11:00:53 +0200
Subject: [PATCH 287/531] new password generator with special chars

---
 bin/mpw        | 22 ++++++++++++++++++++--
 i18n/en.yml    |  4 ++++
 i18n/fr.yml    |  4 ++++
 lib/mpw/mpw.rb | 18 ++++++++++++------
 4 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 52033a3..60b0a07 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -48,6 +48,7 @@ I18n.locale         = lang.to_sym
 # Options
 # --------------------------------------------------------- #
 
+options_password    = {}
 options             = {}
 options[:force]     = false
 options[:sync]      = true
@@ -97,8 +98,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
-		puts MPW::MPW::password(length)
-		exit 0
+		options_password[:length] = length
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -118,6 +118,10 @@ OptionParser.new do |opts|
 		options[:key] = key
 	end
 
+	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
+		options_password[:numeric] = true
+	end
+
 	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
 		options[:sync] = false
 	end
@@ -141,12 +145,26 @@ OptionParser.new do |opts|
 	opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
 		options[:setup_wallet] = true
 	end
+
+	opts.on('-x', '--special-chars', I18n.t('option.special_chars')) do
+		options_password[:special] = true
+	end
+
+	opts.on('-y', '--alpha', I18n.t('option.alpha')) do
+		options_password[:alpha] = true
+	end
 end.parse!
 
 # --------------------------------------------------------- #
 # Main
 # --------------------------------------------------------- #
 
+# Generate password
+if not options_password.empty?
+	puts MPW::MPW::password(options_password)
+	exit 0
+end
+
 begin
 	config = MPW::Config.new(options[:config])
 	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync])
diff --git a/i18n/en.yml b/i18n/en.yml
index 42b6f37..2f1f476 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -37,6 +37,7 @@ en:
 
   option:
     add: "Add an item or key"
+    alpha: "Use letter to generate a password"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
     export: "Export a wallet in an yaml file"
@@ -49,8 +50,10 @@ en:
     import: "Import item since a yaml file"
     key: "Specify the key name, to use with the options [--add | --delete | --update]"
     no_sync: "Disable synchronization with the server"
+    numeric: "Use number to generate a 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"
     remove: "Delete an item"
@@ -136,6 +139,7 @@ en:
     group: "Group"
     login: "Login"
     name: "Name"
+    no_group: "Without group"
     nothing: "No matches!"
     password: "Password"
     port: "Port"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 882a123..da91af0 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -37,6 +37,7 @@ fr:
 
   option:
     add: "Ajoute un élément ou une clé"
+    alpha: "Utilise des lettres dans la génération d'un mot de passe"
     config: "Spécifie le fichier de configuration à utiliser"
     clipboard: "Désactive la fonction presse papier"
     export: "Exporte un portefeuille dans un fichier yaml"
@@ -49,8 +50,10 @@ fr:
     import: "Importe des éléments depuis un fichier yaml"
     key: "Spécifie le nom d'une clé, à utiliser avec les options [--add | --delete | --update]"
     no_sync: "Désactive la synchronisation avec le serveur"
+    numeric: "Utilise des chiffre dans la génération d'un mot de passe"
     setup: "Création d'un nouveau fichier de configuration"
     setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
+    special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
     show: "Recherche et affiche les éléments"
     show_all: "Liste tous les éléments"
     remove: "Supprime un élément"
@@ -136,6 +139,7 @@ fr:
     group: "Groupe"
     login: "Identifiant"
     name: "Nom"
+    no_group: "Sans groupe"
     nothing: "Aucun résultat!"
     password: "Mot de passe"
     port: "Port"
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 69a4938..ab2c1ce 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -414,21 +414,27 @@ class MPW
 	end
 
 	# Generate a random password
-	# @args: length -> the length password
+	# @args: options -> :length, :special, :alpha, :numeric
 	# @rtrn: a random string
-	def self.password(length=8)
-		if length.to_i <= 0
+	def self.password(options={})
+		if not options.include?(:length) or options[:length].to_i <= 0
 			length = 8
 		else
-			length = length.to_i
+			length = options[:length].to_i
 		end
 
+		chars = []
+		chars += [*('!'..'?')] - [*('0'..'9')]        if options.include?(:special)
+		chars += [*('A'..'Z'),*('a'..'z')]            if options.include?(:alpha)
+		chars += [*('0'..'9')]                        if options.include?(:numeric)
+		chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
+
 		result = ''
 		while length > 62 do
-			result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
+			result << chars.sample(62).join
 			length -= 62
 		end
-		result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
+		result << chars.sample(length).join
 
 		return result
 	end

From 55342597ac4538799db25b41bd49296c91cdf153 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 10 Jul 2016 11:01:11 +0200
Subject: [PATCH 288/531] improve list interface

---
 lib/mpw/ui/cli.rb | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/ui/cli.rb
index 3c8e5a5..5a0eb8e 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/ui/cli.rb
@@ -160,7 +160,12 @@ class Cli
 			result.each do |item|
 				if group != item.group
 					group = item.group
-					puts "#{I18n.t('display.group')}: #{group}".yellow
+
+					if group.empty?
+						puts I18n.t('display.no_group').yellow
+					else
+						puts "\n#{group}".yellow
+					end
 				end
 
 				print " |_ ".yellow
@@ -171,6 +176,8 @@ class Cli
 
 				i += 1
 			end
+
+			print "\n"
 			choice = ask(I18n.t('form.select')).to_i
 
 			if choice >= 1 and choice < i 

From 13b025df2ba915ee738b69a28310ef6ea84ed81c Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 10 Jul 2016 18:55:18 +0200
Subject: [PATCH 289/531] fix path for require lib

---
 bin/mpw                 | 15 ++++++---------
 lib/mpw/{ui => }/cli.rb |  6 ++----
 lib/mpw/mpw.rb          |  6 ++----
 3 files changed, 10 insertions(+), 17 deletions(-)
 rename lib/mpw/{ui => }/cli.rb (99%)

diff --git a/bin/mpw b/bin/mpw
index 60b0a07..cbc6e4d 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -16,11 +16,15 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
+$: << File.expand_path('../../lib', __FILE__)
+
 require 'optparse'
-require 'pathname'
 require 'locale'
 require 'set'
 require 'i18n'
+require 'mpw/mpw'
+require 'mpw/config'
+require 'mpw/cli'
 
 # --------------------------------------------------------- #
 # Set local
@@ -32,15 +36,8 @@ if defined?(I18n.enforce_available_locales)
 	I18n.enforce_available_locales = true
 end
 
-APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
-
-# TODO
-require "#{APP_ROOT}/../lib/mpw/mpw.rb"
-require "#{APP_ROOT}/../lib/mpw/config.rb"
-require "#{APP_ROOT}/../lib/mpw/ui/cli.rb"
-
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{APP_ROOT}/../i18n/*.yml"]
+I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
 I18n.default_locale = :en
 I18n.locale         = lang.to_sym
 
diff --git a/lib/mpw/ui/cli.rb b/lib/mpw/cli.rb
similarity index 99%
rename from lib/mpw/ui/cli.rb
rename to lib/mpw/cli.rb
index 5a0eb8e..0b06724 100644
--- a/lib/mpw/ui/cli.rb
+++ b/lib/mpw/cli.rb
@@ -21,10 +21,8 @@ require 'i18n'
 require 'colorize'
 require 'highline/import'
 require 'clipboard'
-
-#TODO
-require "#{APP_ROOT}/../lib/mpw/item.rb"
-require "#{APP_ROOT}/../lib/mpw/mpw.rb"
+require 'mpw/item'
+require 'mpw/mpw'
 
 module MPW
 class Cli
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index ab2c1ce..fc4e5da 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -20,9 +20,7 @@ require 'rubygems/package'
 require 'gpgme'
 require 'i18n'
 require 'yaml'
-
-#TODO
-require "#{APP_ROOT}/../lib/mpw/item.rb"
+require 'mpw/item'
 	
 module MPW
 class MPW
@@ -325,7 +323,7 @@ class MPW
 		
 		case @config['sync']['type']
 		when 'sftp', 'scp', 'ssh'
-			require "#{APP_ROOT}/../lib/mpw/sync/ssh.rb"
+			require "mpw/sync/ssh"
 			sync = SyncSSH.new(@config['sync'])
 		when 'ftp'
 			require 'mpw/sync/ftp'

From f68882181d6f86e9a8d55df7aede7f7b330adff2 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 10 Jul 2016 19:04:34 +0200
Subject: [PATCH 290/531] minor clean code

---
 lib/mpw/item.rb | 14 ++------------
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 7f0459a..ef1309e 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -21,8 +21,6 @@ require 'i18n'
 module MPW
 class Item
 
-	attr_accessor :error_msg
-
 	attr_accessor :id
 	attr_accessor :name
 	attr_accessor :group
@@ -41,8 +39,7 @@ class Item
 	# raise an error if the hash hasn't the key name 
 	def initialize(options={})
 		if not options.has_key?(:name) or options[:name].to_s.empty?
-			@error_msg = I18n.t('error.update.name_empty')
-			raise @error_msg
+			raise I18n.t('error.update.name_empty')
 		end
 
 		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
@@ -60,11 +57,9 @@ class Item
 
 	# Update the item
 	# @args: options -> a hash of parameter
-	# @rtrn: true if the item is update
 	def update(options={})
 		if options.has_key?(:name) and options[:name].to_s.empty?
-			@error_msg = I18n.t('error.update.name_empty')
-			return false
+			raise I18n.t('error.update.name_empty')
 		end
 
 		@name      = options[:name]       if options.has_key?(:name)
@@ -75,8 +70,6 @@ class Item
 		@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
 		@comment   = options[:comment]    if options.has_key?(:comment)
 		@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
-
-		return true
 	end
 
 	# Update last_sync
@@ -85,7 +78,6 @@ class Item
 	end
 
 	# Delete all data
-	# @rtrn: true
 	def delete
 		@id        = nil
 		@name      = nil
@@ -98,8 +90,6 @@ class Item
 		@created   = nil
 		@last_edit = nil
 		@last_sync = nil
-
-		return true
 	end
 
 	def empty?

From 8a1003f1bdac9d2b20351cbd79382d8b5c7f58f3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sun, 10 Jul 2016 19:25:21 +0200
Subject: [PATCH 291/531] fix max length to 32768 for a password

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index fc4e5da..68a2292 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -417,6 +417,8 @@ class MPW
 	def self.password(options={})
 		if not options.include?(:length) or options[:length].to_i <= 0
 			length = 8
+		elsif options[:length].to_i >= 32768
+			length = 32768
 		else
 			length = options[:length].to_i
 		end

From d55291cc147ce7239f9e7c9b16d474c176e4f8b4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Mon, 11 Jul 2016 23:56:58 +0200
Subject: [PATCH 292/531] dynamic menu for clipboard

---
 i18n/en.yml    |  3 ++-
 i18n/fr.yml    |  3 ++-
 lib/mpw/cli.rb | 62 +++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 60 insertions(+), 8 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 2f1f476..c97ac5c 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -77,8 +77,9 @@ en:
       comment: "Enter a comment (optional): "
       valid: "Item has been added!"
     clipboard:
+      choice: "What do you want to copy ? [q = quit, p = password, l = login]: "
       clean: "The clipboard has been cleaned."
-      login: "The login has been copied in clipboard, press any key to continue."
+      login: "The login has been copied in clipboard."
       password: "The password has been copied in clipboard for 30s!"
     delete_key:
       valid: "Key has been deleted!"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index da91af0..993a809 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -77,8 +77,9 @@ fr:
       comment: "Entrez un commentaire (optionnel): "
       valid: "L'élément a bien été ajouté!"
     clipboard:
+      choice: "Que voulez-vous copier ? [q = quitter, p = mot de passe, l = identifiant]: "
       clean: "Le presse papier a été nettoyé."
-      login: "L'identifiant a été copié dans le presse papier, pressez n'importe quelle touche pour continuer."
+      login: "L'identifiant a été copié dans le presse papier"
       password: "Le mot de passe a été copié dans le presse papier pour 30s!"
     delete_key:
       valid: "La clé a bien été supprimée!"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 0b06724..ce1b9ec 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -217,20 +217,69 @@ class Cli
 	end
 
 	# Copy in clipboard the login and password
+	# @args: item -> the item
 	def clipboard(item)
-		Clipboard.copy(item.user)
-		print "\n#{I18n.t('form.clipboard.login')}".green
-		gets
+		pid = nil
 
-		Clipboard.copy(@mpw.get_password(item.id))
-		puts I18n.t('form.clipboard.password').yellow
+		# Security: force quit after 90s
+		pid_s = Process.fork do
+			begin
+				sleep 90
+				Process.kill(3, Process.ppid)
+			rescue Interrupt
+				exit
+			end
+		end
+		
+		while true
+			choice = ask(I18n.t('form.clipboard.choice')).to_s
+			
+			if not pid.nil?
+				Clipboard.clear
+				Process.kill(9, pid)
 
-		sleep(30)
+				pid = nil
+			end
+
+			case choice
+			when 'q', 'quit'
+				break
+
+			when 'l', 'login'
+				Clipboard.copy(item.user)
+				puts I18n.t('form.clipboard.login').green
+
+			when 'p', 'password'
+				Clipboard.copy(@mpw.get_password(item.id))
+				puts I18n.t('form.clipboard.password').yellow
+
+				pid = Process.fork do
+					begin
+						sleep 30
+
+						Clipboard.clear
+						puts I18n.t('form.clipboard.clean').green
+					rescue Interrupt
+						exit
+					end
+				end
+
+			else
+				puts I18n.t('warning.select').yellow
+				next
+			end
+		end
 
 		Clipboard.clear
 		puts I18n.t('form.clipboard.clean').green
+
 	rescue SystemExit, Interrupt
 		Clipboard.clear
+		puts I18n.t('form.clipboard.clean').green
+
+	ensure
+		Process.kill('HUP', pid)  if not pid.nil?
+		Process.kill('HUP', pid_s)
 	end
 
 	# Display the wallet
@@ -259,6 +308,7 @@ class Cli
 					@wallet_file = wallets[choice-1]
 				else
 					puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+					exit 2
 				end
 			end
 		else

From 05dbba4da612e90bda4ae5aa1a746a1f6dbb6c63 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 12 Jul 2016 20:31:49 +0200
Subject: [PATCH 293/531] fix sync bug

---
 lib/mpw/mpw.rb | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 68a2292..d906359 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -118,6 +118,8 @@ class MPW
 			           )
 		end
 
+		@config['last_update'] = Time.now.to_i
+
 		Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
 			data_encrypt = encrypt(data.to_yaml)
 			tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
@@ -317,8 +319,8 @@ class MPW
 	# @args: force -> force the sync
 	def sync(force=false)
 		return if @config.empty? or @config['sync']['type'].to_s.empty?
-		return if get_last_sync < Time.now.to_i + 300 and not force
-		
+		return if get_last_sync + 300 > Time.now.to_i and not force
+
 		tmp_file  = "#{@wallet_file}.sync"
 		
 		case @config['sync']['type']
@@ -340,7 +342,7 @@ class MPW
 
 		File.unlink(tmp_file) if File.exist?(tmp_file)
 
-		return if remote.get_last_sync == get_last_sync
+		return if remote.get_last_sync == @config['last_update']
 
 		if not remote.to_s.empty?
 			@data.each do |item|

From a18793d8d67e00b82a62c30ca15aeb9c30ab48e4 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 12 Jul 2016 20:32:21 +0200
Subject: [PATCH 294/531] fix salt for password

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index d906359..b15f95c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -167,7 +167,7 @@ class MPW
 	# args: id -> the item id
 	#       password -> the new password
 	def set_password(id, password)
-		salt     = MPW::password(Random.rand(4..9))
+		salt     = MPW::password(length: Random.rand(4..9))
 		password = "$#{salt}::#{password}"
 
 		@passwords[id] = encrypt(password)

From 4d783e88a4d9128d0c880ad27cf696ce919e3b48 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 12 Jul 2016 20:34:54 +0200
Subject: [PATCH 295/531] clipboard: remove clean text

---
 lib/mpw/cli.rb | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index ce1b9ec..ca2ee53 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -258,7 +258,6 @@ class Cli
 						sleep 30
 
 						Clipboard.clear
-						puts I18n.t('form.clipboard.clean').green
 					rescue Interrupt
 						exit
 					end
@@ -271,11 +270,9 @@ class Cli
 		end
 
 		Clipboard.clear
-		puts I18n.t('form.clipboard.clean').green
 
 	rescue SystemExit, Interrupt
 		Clipboard.clear
-		puts I18n.t('form.clipboard.clean').green
 
 	ensure
 		Process.kill('HUP', pid)  if not pid.nil?

From 1edb10ae49bcfa2f59e299a552f19cdfa5143fc5 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 14 Jul 2016 00:12:21 +0200
Subject: [PATCH 296/531] replace fork to thread

---
 lib/mpw/cli.rb | 32 ++++++--------------------------
 1 file changed, 6 insertions(+), 26 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index ca2ee53..430c671 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -222,25 +222,14 @@ class Cli
 		pid = nil
 
 		# Security: force quit after 90s
-		pid_s = Process.fork do
-			begin
-				sleep 90
-				Process.kill(3, Process.ppid)
-			rescue Interrupt
-				exit
-			end
+		Thread.new do
+			sleep 90
+			exit
 		end
 		
 		while true
 			choice = ask(I18n.t('form.clipboard.choice')).to_s
 			
-			if not pid.nil?
-				Clipboard.clear
-				Process.kill(9, pid)
-
-				pid = nil
-			end
-
 			case choice
 			when 'q', 'quit'
 				break
@@ -253,14 +242,10 @@ class Cli
 				Clipboard.copy(@mpw.get_password(item.id))
 				puts I18n.t('form.clipboard.password').yellow
 
-				pid = Process.fork do
-					begin
-						sleep 30
+				Thread.new do
+					sleep 30
 
-						Clipboard.clear
-					rescue Interrupt
-						exit
-					end
+					Clipboard.clear
 				end
 
 			else
@@ -270,13 +255,8 @@ class Cli
 		end
 
 		Clipboard.clear
-
 	rescue SystemExit, Interrupt
 		Clipboard.clear
-
-	ensure
-		Process.kill('HUP', pid)  if not pid.nil?
-		Process.kill('HUP', pid_s)
 	end
 
 	# Display the wallet

From 8bcbffec66f60ad0ba2147841b0a298ce2858560 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 2 Aug 2016 21:59:05 +0200
Subject: [PATCH 297/531] add option for otp

---
 bin/mpw | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/bin/mpw b/bin/mpw
index cbc6e4d..f5e717b 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -48,6 +48,7 @@ I18n.locale         = lang.to_sym
 options_password    = {}
 options             = {}
 options[:force]     = false
+options[:otp]       = false
 options[:sync]      = true
 options[:clipboard] = true
 options[:group]     = nil
@@ -123,6 +124,10 @@ OptionParser.new do |opts|
 		options[:sync] = false
 	end
 
+	opts.on('-O', '--otp', I18n.t('option.otp')) do
+		options[:otp] = true
+	end
+
 	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
 		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
 	end
@@ -164,7 +169,7 @@ end
 
 begin
 	config = MPW::Config.new(options[:config])
-	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync])
+	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
 		
 	# Setup a new config 
 	if not options[:setup].nil?

From cd0dd2d0aab50f5ce72dac3d3c6e4670c3d1df68 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 2 Aug 2016 21:59:22 +0200
Subject: [PATCH 298/531] add translate for otp

---
 i18n/en.yml |  8 ++++++++
 i18n/fr.yml | 10 +++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index c97ac5c..79cf4b2 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -75,12 +75,19 @@ en:
       password: "Enter the the password: "
       port: "Enter the connection port (optional): "
       comment: "Enter a comment (optional): "
+      otp_key: "Enter the otp secret (base32 secret key): "
       valid: "Item has been added!"
     clipboard:
       choice: "What do you want to copy ? [q = quit, p = password, l = login]: "
       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!"
+      help:
+         name: "Help"
+         login: "Press <l> to copy the login"
+         password: "Press <p> to copy the password"
+         otp_code: "Press <o> to copy the otp code"
     delete_key:
       valid: "Key has been deleted!"
     delete_item:
@@ -129,6 +136,7 @@ en:
       password: "Enter the the password: "
       port: "Enter the connection port [%{port}]: "
       comment: "Enter a comment [%{comment}]: "
+      otp_key: "Enter the otp secret (base32 secret key): "
       valid: "Item has been updated!" 
     export:
       valid: "The export in %{file} is succesfull!"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 993a809..caf1e05 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -75,12 +75,19 @@ fr:
       password: "Entrez le mot de passe: "
       port: "Entrez le port de connexion (optionnel): "
       comment: "Entrez un commentaire (optionnel): "
+      otp_key: "Entrez le secret OTP: "
       valid: "L'élément a bien été ajouté!"
     clipboard:
-      choice: "Que voulez-vous copier ? [q = quitter, p = mot de passe, l = identifiant]: "
+      choice: "Que voulez-vous copier ? : "
       clean: "Le presse papier a été nettoyé."
       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!"
+      help:
+         name: "Aide"
+         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"
     delete_key:
       valid: "La clé a bien été supprimée!"
     delete_item:
@@ -129,6 +136,7 @@ fr:
       password: "Entrez le mot de passe: "
       port: "Entrez un port de connexion [%{port}]: "
       comment: "Entrez un commentaire [%{comment}]: "
+      otp_key: "Entrez le secret OTP: "
       valid: "L'élément a bien été mis à jour!" 
     export:
       valid: "L'export dans %{file} est un succès!"

From b200b564695da09f581d1102b55f6982ad6f9183 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 2 Aug 2016 21:59:46 +0200
Subject: [PATCH 299/531] add manage otp code

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 430c671..03224e5 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -30,10 +30,13 @@ class Cli
 	# Constructor
 	# @args: config -> the config
 	#        sync -> boolean for sync or not
-	def initialize(config, clipboard=true, sync=true)
+	#        clipboard -> enable clopboard
+	#        otp -> enable otp
+	def initialize(config, clipboard=true, sync=true, otp=false)
 		@config    = config
 		@clipboard = clipboard
 		@sync      = sync
+		@otp       = otp
 	end
 
 	# Create a new config file
@@ -203,11 +206,13 @@ class Cli
 		print "#{I18n.t('display.login')}: ".cyan
 		puts  item.user
 		print "#{I18n.t('display.password')}: ".cyan
+
 		if @clipboard
 			puts '***********'
 		else
 			puts  @mpw.get_password(item.id)
 		end
+
 		print "#{I18n.t('display.port')}: ".cyan
 		puts  item.port
 		print "#{I18n.t('display.comment')}: ".cyan
@@ -248,8 +253,15 @@ class Cli
 					Clipboard.clear
 				end
 
+			when 'o', 'otp'
+				Clipboard.copy(@mpw.get_otp_code(item.id))
+				puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
+
 			else
-				puts I18n.t('warning.select').yellow
+				puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
+				puts I18n.t('form.clipboard.help.login')
+				puts I18n.t('form.clipboard.help.password')
+				puts I18n.t('form.clipboard.help.otp_code')
 				next
 			end
 		end
@@ -333,10 +345,15 @@ class Cli
 		options[:port]     = ask(I18n.t('form.add_item.port')).to_s
 		options[:comment]  = ask(I18n.t('form.add_item.comment')).to_s
 
+		if @otp
+			otp_key = ask(I18n.t('form.add_item.otp_key')).to_s
+		end
+
 		item = Item.new(options)
 
 		@mpw.add(item)
 		@mpw.set_password(item.id, password)
+		@mpw.set_otp_key(item.id, otp_key)
 		@mpw.write_data
 		@mpw.sync(true) if @sync
 
@@ -362,10 +379,15 @@ class Cli
 			options[:port]     = ask(I18n.t('form.update_item.port'    , port:     item.port)).to_s
 			options[:comment]  = ask(I18n.t('form.update_item.comment' , comment:  item.comment)).to_s
 
+			if @otp
+				otp_key = ask(I18n.t('form.update_item.otp_key')).to_s
+			end
+
 			options.delete_if { |k,v| v.empty? }
-				
+
 			item.update(options)
 			@mpw.set_password(item.id, password) if not password.empty?
+			@mpw.set_otp_key(item.id, otp_key)   if not otp_key.empty?
 			@mpw.write_data
 			@mpw.sync(true) if @sync
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index b15f95c..c4c34ef 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -20,6 +20,7 @@ require 'rubygems/package'
 require 'gpgme'
 require 'i18n'
 require 'yaml'
+require 'rotp'
 require 'mpw/item'
 	
 module MPW
@@ -43,6 +44,7 @@ class MPW
 		@data      = []
 		@keys      = {}
 		@passwords = {}
+		@otp_keys  = {}
 
 		data       = nil
 
@@ -68,6 +70,10 @@ class MPW
 
 					when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
 						@passwords[Regexp.last_match('id')] = f.read
+
+					when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+						@otp_keys[Regexp.last_match('id')] = f.read
+
 					else
 						next
 				end
@@ -137,6 +143,12 @@ class MPW
 				end
 			end
 
+			@otp_keys.each do |id, key|
+				tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
+					io.write(key)
+				end
+			end
+
 			@keys.each do |id, key|
 				tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
 					io.write(key)
@@ -413,6 +425,30 @@ class MPW
 		raise "#{I18n.t('error.sync.general')}\n#{e}"
 	end
 
+	# Set an opt key
+	# args: id -> the item id
+	#       key -> the new key
+	def set_otp_key(id, key)
+		@otp_keys[id] = encrypt(key)
+	end
+
+	# Get an otp code
+	# @args: id -> the item id
+	# @rtrn: an otp code
+	def	get_otp_code(id)
+		if not @otp_keys.has_key?(id)
+			return 0
+		else
+			return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
+		end
+	end
+
+	# Get remaining time before expire otp code
+	# @rtrn: return time in seconde
+	def get_otp_remaining_time
+		return (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

From e8b572ae2e04757585d9294dac2ab4e1ca77895d Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 2 Aug 2016 22:03:09 +0200
Subject: [PATCH 300/531] add dep rotp

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

diff --git a/Gemfile b/Gemfile
index b897ea2..557dec5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,3 +7,4 @@ gem 'colorize'
 gem 'net-ssh'
 gem 'net-scp'
 gem 'clipboard'
+gem 'rotp'
diff --git a/mpw.gemspec b/mpw.gemspec
index bf6f3db..fa4afc7 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
   spec.add_dependency "net-ssh"
   spec.add_dependency "net-scp"
   spec.add_dependency "clipboard"
+  spec.add_dependency "rotp"
 end

From 2dcff8d9ef3b7a44eb0a1cc4401ca41f036b9da8 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 2 Aug 2016 22:10:50 +0200
Subject: [PATCH 301/531] fix bug when otp is empty

---
 lib/mpw/cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 03224e5..53ebe89 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -387,7 +387,7 @@ class Cli
 
 			item.update(options)
 			@mpw.set_password(item.id, password) if not password.empty?
-			@mpw.set_otp_key(item.id, otp_key)   if not otp_key.empty?
+			@mpw.set_otp_key(item.id, otp_key)   if not otp_key.to_s.empty?
 			@mpw.write_data
 			@mpw.sync(true) if @sync
 

From d51c4733c052b15f808406982f042dc69b14ad03 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 3 Aug 2016 21:26:04 +0200
Subject: [PATCH 302/531] show otp code whitout clipboard

---
 i18n/en.yml    | 1 +
 i18n/fr.yml    | 1 +
 lib/mpw/cli.rb | 5 ++++-
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 79cf4b2..c704849 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -150,6 +150,7 @@ en:
     name: "Name"
     no_group: "Without group"
     nothing: "No matches!"
+    otp_code: "OTP code"
     password: "Password"
     port: "Port"
     protocol: "Protocol"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index caf1e05..41cac4d 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -150,6 +150,7 @@ fr:
     name: "Nom"
     no_group: "Sans groupe"
     nothing: "Aucun résultat!"
+    otp_code: "Code OTP"
     password: "Mot de passe"
     port: "Port"
     protocol: "Protocol"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 53ebe89..b0e413d 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -205,12 +205,15 @@ class Cli
 		puts  item.protocol
 		print "#{I18n.t('display.login')}: ".cyan
 		puts  item.user
-		print "#{I18n.t('display.password')}: ".cyan
 
 		if @clipboard
+			print "#{I18n.t('display.password')}: ".cyan
 			puts '***********'
 		else
+			print "#{I18n.t('display.password')}: ".cyan
 			puts  @mpw.get_password(item.id)
+			print "#{I18n.t('display.otp_code')}: ".cyan
+			puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
 		end
 
 		print "#{I18n.t('display.port')}: ".cyan

From 1104706692ad02c2187366c43d15999ca07f20ed Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 3 Aug 2016 21:26:36 +0200
Subject: [PATCH 303/531] update version

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

diff --git a/VERSION b/VERSION
index db8f8da..944880f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.2.0-beta
+3.2.0

From fbc291c177087e3d00d54cb7681eb8db4fea4e7b Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Wed, 3 Aug 2016 21:37:49 +0200
Subject: [PATCH 304/531] no show otp code field if otp code is nil

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index b0e413d..144ed38 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -212,8 +212,11 @@ class Cli
 		else
 			print "#{I18n.t('display.password')}: ".cyan
 			puts  @mpw.get_password(item.id)
-			print "#{I18n.t('display.otp_code')}: ".cyan
-			puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
+
+			if @mpw.get_otp_code(item.id) > 0
+				print "#{I18n.t('display.otp_code')}: ".cyan
+				puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
+			end
 		end
 
 		print "#{I18n.t('display.port')}: ".cyan

From b64514c55762cf081c81fabbb3a9578fb4195f6a Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 4 Aug 2016 22:20:12 +0200
Subject: [PATCH 305/531] if delete disable clipboard

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 144ed38..392e7bd 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -409,7 +409,8 @@ class Cli
 	# @args: id -> the item's id
 	#        force -> no resquest a validation
 	def delete(id, force=false)
-		item = @mpw.search_by_id(id)
+		@clipboard = false
+		item       = @mpw.search_by_id(id)
 
 		if item.nil?
 			puts I18n.t('form.delete_item.not_valid', id: id)

From 110cbe151042afa9553dac01b87373edb45d3ae9 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Thu, 4 Aug 2016 22:20:32 +0200
Subject: [PATCH 306/531] fix bug sync

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index c4c34ef..baa83f5 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -391,7 +391,7 @@ class MPW
 
 		# Add item
 		remote.list.each do |r|
-			next if r.last_edit <= last_sync
+			next if r.last_edit <= get_last_sync
 
 			item = Item.new(id:        r.id,
 			                name:      r.name,

From 08d72e8d44e0c1b5653833c6cef9e78d7503bf65 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Sat, 6 Aug 2016 23:35:54 +0200
Subject: [PATCH 307/531] update Version

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

diff --git a/VERSION b/VERSION
index 944880f..e4604e3 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.2.0
+3.2.1

From 2f627f271c9ae398018069caa35f70fcb2ccdf64 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 9 Aug 2016 22:06:47 +0200
Subject: [PATCH 308/531] add and update with vim editor

---
 bin/mpw                   |  22 +-------
 i18n/en.yml               |   6 --
 i18n/fr.yml               |   6 --
 lib/mpw/cli.rb            | 114 ++++++++++++++++----------------------
 templates/add_form.erb    |  10 ++++
 templates/update_form.erb |  10 ++++
 6 files changed, 70 insertions(+), 98 deletions(-)
 create mode 100644 templates/add_form.erb
 create mode 100644 templates/update_form.erb

diff --git a/bin/mpw b/bin/mpw
index f5e717b..5f0d64d 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -75,10 +75,6 @@ OptionParser.new do |opts|
 		options[:clipboard] = false
 	end
 
-	opts.on('-d', '--delete', I18n.t('option.remove')) do
-		options[:delete] = true
-	end
-
 	opts.on('-e', '--export', I18n.t('option.export')) do
 		options[:export] = true
 	end
@@ -104,10 +100,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-i', '--id ID', I18n.t('option.id')) do |id|
-		options[:id] = id
-	end
-
 	opts.on('-I', '--import', I18n.t('option.import')) do
 		options[:import] = true
 	end
@@ -136,10 +128,6 @@ OptionParser.new do |opts|
 		options[:setup] = true
 	end
 
-	opts.on('-u', '--update', I18n.t('option.update')) do
-		options[:update] = true
-	end
-
 	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
@@ -191,14 +179,6 @@ begin
 	
 		cli.display(opts)
 	
-	# Remove an item
-	elsif not options[:delete].nil? and not options[:id].nil?
-		cli.delete(options[:id], options[:force])
-	
-	# Update an item
-	elsif not options[:update].nil? and not options[:id].nil?
-		cli.update(options[:id])
-	
 	# Add a new item
 	elsif not options[:add].nil? and options[:key].nil?
 		cli.add
@@ -215,7 +195,7 @@ begin
 	elsif not options[:export].nil? and not options[:file].nil?
 		cli.export(options[:file])
 	
-	# Add a new item
+	# Import
 	elsif not options[:import].nil? and not options[:file].nil?
 		cli.import(options[:file])
 	
diff --git a/i18n/en.yml b/i18n/en.yml
index c704849..52fbf67 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -10,8 +10,6 @@ en:
         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!"
-    delete:
-      id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
     export: "Can't export, unable to write in %{file}!"
     gpg_file: 
       decrypt: "Can't decrypt file!"
@@ -46,7 +44,6 @@ en:
     generate_password: "Generate a random password (default 8 characters)"
     group: "Search the items with specified group"
     help: "Show this help message"
-    id: "Specify an id, to use with the options [--delete | --update]"
     import: "Import item since a yaml file"
     key: "Specify the key name, to use with the options [--add | --delete | --update]"
     no_sync: "Disable synchronization with the server"
@@ -56,8 +53,6 @@ en:
     special_chars: "Use special char to generate a password"
     show: "Search and show the items"
     show_all: "List all items"
-    remove: "Delete an item"
-    update: "Update an item"
     usage: "Usage"
     wallet: "Specify a wallet to use"
 
@@ -93,7 +88,6 @@ en:
     delete_item:
       ask: "Are you sure you want to remove the item %{id} ?"
       valid: "The item %{id} has been removed!"
-      not_valid: "The item %{id} hasn't been removed, because it doesn't exist!"
     import:
       ask: "Are you sure you want to import this file %{file} ?"
       valid: "The import is succesfull!"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 41cac4d..b07a9e2 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -10,8 +10,6 @@ fr:
         exception: "La création de la clé GPG n'a pas pu aboutir!"
         name: "Vous devez définir un nom pour votre clé GPG!"
         password: "Vous devez définir un mot de passe pour votre clé GPG!"
-    delete:
-      id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
     export: "Impossible d'exporter les données dans le fichier %{file}!"
     gpg_file: 
       decrypt: "Impossible de déchiffrer le fichier GPG!"
@@ -46,7 +44,6 @@ fr:
     generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
     group: "Recherche les éléments appartenant au groupe spécifié"
     help: "Affiche ce message d'aide"
-    id: "Spécifie un identifiant, à utiliser avec les options [--delete | --update]"
     import: "Importe des éléments depuis un fichier yaml"
     key: "Spécifie le nom d'une clé, à utiliser avec les options [--add | --delete | --update]"
     no_sync: "Désactive la synchronisation avec le serveur"
@@ -56,8 +53,6 @@ fr:
     special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
     show: "Recherche et affiche les éléments"
     show_all: "Liste tous les éléments"
-    remove: "Supprime un élément"
-    update: "Met à jour un élément"
     usage: "Utilisation"
     wallet: "Spécifie le portefeuille à utiliser"
 
@@ -93,7 +88,6 @@ fr:
     delete_item:
       ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?"
       valid: "L'élément %{id} a bien été supprimé!"
-      not_valid: "L'élément %{id} n'a pu être supprimé, car il n'existe pas!"
     import:
       ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
       valid: "L'import est un succès!"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 392e7bd..50fad4d 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -21,6 +21,7 @@ require 'i18n'
 require 'colorize'
 require 'highline/import'
 require 'clipboard'
+require 'tmpdir'
 require 'mpw/item'
 require 'mpw/mpw'
 
@@ -156,7 +157,7 @@ class Cli
 			group = nil
 			i     = 1
 
-			result.sort! { |a,b| a.group.downcase <=> b.group.downcase }
+			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
 
 			result.each do |item|
 				if group != item.group
@@ -263,6 +264,12 @@ class Cli
 				Clipboard.copy(@mpw.get_otp_code(item.id))
 				puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
 
+			when 'd', 'delete'
+				delete(item)
+
+			when 'u', 'update', 'e', 'edit'
+				update(item)
+
 			else
 				puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
 				puts I18n.t('form.clipboard.help.login')
@@ -336,30 +343,41 @@ class Cli
 		puts "#{I18n.t('display.error')} #15: #{e}".red
 	end
 
-	# Form to add a new item
-	def add
-		options = {}
+	def text_editor(template_name, item=nil)
+		options       = {}
+		opts          = {}
+		template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
+		template      = ERB.new(IO.read(template_file))
 
-		puts I18n.t('form.add_item.title')
-		puts '--------------------'
-		options[:name]     = ask(I18n.t('form.add_item.name')).to_s
-		options[:group]    = ask(I18n.t('form.add_item.group')).to_s
-		options[:host]     = ask(I18n.t('form.add_item.server')).to_s
-		options[:protocol] = ask(I18n.t('form.add_item.protocol')).to_s
-		options[:user]     = ask(I18n.t('form.add_item.login')).to_s
-		password           = ask(I18n.t('form.add_item.password')).to_s
-		options[:port]     = ask(I18n.t('form.add_item.port')).to_s
-		options[:comment]  = ask(I18n.t('form.add_item.comment')).to_s
+		Dir.mktmpdir do |dir|
+			tmp_file = "#{dir}/#{template_name}.yml"
 
-		if @otp
-			otp_key = ask(I18n.t('form.add_item.otp_key')).to_s
+			File.open(tmp_file, 'w') do |f|
+				f << template.result(binding)
+			end
+
+			system("vim #{tmp_file}")
+
+			opts = YAML::load_file(tmp_file)
 		end
 
-		item = Item.new(options)
+		opts.delete_if { |k,v| v.to_s.empty? }
+
+		opts.each do |k,v|
+			options[k.to_sym] = v
+		end
+
+		return options
+	end
+
+	# Form to add a new item
+	def add
+		options = text_editor('add_form')	
+		item    = Item.new(options)
 
 		@mpw.add(item)
-		@mpw.set_password(item.id, password)
-		@mpw.set_otp_key(item.id, otp_key)
+		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
+		@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
 		@mpw.write_data
 		@mpw.sync(true) if @sync
 
@@ -368,39 +386,16 @@ class Cli
 
 	# Update an item
 	# @args: id -> the item's id
-	def update(id)
-		item = @mpw.search_by_id(id)
+	def update(item)
+		options = text_editor('update_form', item)
 
-		if not item.nil?
-			options = {}
+		item.update(options)
+		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
+		@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
+		@mpw.write_data
+		@mpw.sync(true) if @sync
 
-			puts I18n.t('form.update_item.title')
-			puts '--------------------'
-			options[:name]     = ask(I18n.t('form.update_item.name'    , name:     item.name)).to_s
-			options[:group]    = ask(I18n.t('form.update_item.group'   , group:    item.group)).to_s
-			options[:host]     = ask(I18n.t('form.update_item.server'  , server:   item.host)).to_s
-			options[:protocol] = ask(I18n.t('form.update_item.protocol', protocol: item.protocol)).to_s
-			options[:user]     = ask(I18n.t('form.update_item.login'   , login:    item.user)).to_s
-			password           = ask(I18n.t('form.update_item.password')).to_s
-			options[:port]     = ask(I18n.t('form.update_item.port'    , port:     item.port)).to_s
-			options[:comment]  = ask(I18n.t('form.update_item.comment' , comment:  item.comment)).to_s
-
-			if @otp
-				otp_key = ask(I18n.t('form.update_item.otp_key')).to_s
-			end
-
-			options.delete_if { |k,v| v.empty? }
-
-			item.update(options)
-			@mpw.set_password(item.id, password) if not password.empty?
-			@mpw.set_otp_key(item.id, otp_key)   if not otp_key.to_s.empty?
-			@mpw.write_data
-			@mpw.sync(true) if @sync
-
-			puts "#{I18n.t('form.update_item.valid')}".green
-		else
-			puts I18n.t('display.nothing')
-		end
+		puts "#{I18n.t('form.update_item.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #14: #{e}".red
 	end
@@ -408,29 +403,18 @@ class Cli
 	# Remove an item
 	# @args: id -> the item's id
 	#        force -> no resquest a validation
-	def delete(id, force=false)
-		@clipboard = false
-		item       = @mpw.search_by_id(id)
+	def delete(item)
+		confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
 
-		if item.nil?
-			puts I18n.t('form.delete_item.not_valid', id: id)
+		if not confirm =~ /^(y|yes|YES|Yes|Y)$/
 			return
 		end
 
-		if not force
-			display_item(item)
-
-			confirm = ask("#{I18n.t('form.delete_item.ask', id: id)} (y/N) ").to_s
-			if not confirm =~ /^(y|yes|YES|Yes|Y)$/
-				return
-			end
-		end
-
 		item.delete
 		@mpw.write_data
 		@mpw.sync(true) if @sync
 
-		puts "#{I18n.t('form.delete_item.valid', id: id)}".green
+		puts "#{I18n.t('form.delete_item.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #16: #{e}".red
 	end
diff --git a/templates/add_form.erb b/templates/add_form.erb
new file mode 100644
index 0000000..9cb9d1a
--- /dev/null
+++ b/templates/add_form.erb
@@ -0,0 +1,10 @@
+---
+name: 
+group: 
+host: 
+protocol: 
+user: 
+password: 
+port: 
+comment: 
+otp_secret: 
diff --git a/templates/update_form.erb b/templates/update_form.erb
new file mode 100644
index 0000000..13ce1d6
--- /dev/null
+++ b/templates/update_form.erb
@@ -0,0 +1,10 @@
+---
+name: <%= item.name %> 
+group: <%= item.group %>
+host: <%= item.host %>
+protocol: <%= item.protocol %>
+user: <%= item.user %>
+password: 
+port: <%= item.port %>
+comment: <%= item.comment %>
+otp_secret: 

From 4b8c87c17f0ecdb0a30b4cda305bde4aa5afbfd3 Mon Sep 17 00:00:00 2001
From: nishiki <nishiki@yaegashi.fr>
Date: Tue, 9 Aug 2016 22:34:59 +0200
Subject: [PATCH 309/531] change editor with var EDITOR

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 50fad4d..3319168 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -344,6 +344,7 @@ class Cli
 	end
 
 	def text_editor(template_name, item=nil)
+		editor        = ENV['EDITOR'] || 'nano'
 		options       = {}
 		opts          = {}
 		template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
@@ -356,7 +357,7 @@ class Cli
 				f << template.result(binding)
 			end
 
-			system("vim #{tmp_file}")
+			system("#{editor} #{tmp_file}")
 
 			opts = YAML::load_file(tmp_file)
 		end
@@ -382,6 +383,8 @@ class Cli
 		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_item.valid')}".green
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #13: #{e}".red
 	end
 
 	# Update an item

From 166d1f141accf93ff810011dfdba2e03e617a7dc Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 Aug 2016 09:18:32 +0200
Subject: [PATCH 310/531] fix translate for add template

---
 i18n/en.yml            | 19 +++++++++----------
 i18n/fr.yml            | 19 +++++++++----------
 templates/add_form.erb | 18 +++++++++---------
 3 files changed, 27 insertions(+), 29 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 52fbf67..f226203 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -61,16 +61,15 @@ en:
     add_key:
       valid: "Key has been added!"
     add_item:
-      title: "Add a new item"
-      name: "Enter the name: "
-      group: "Enter the group (optional): "
-      server: "Enter the hostname or ip: "
-      protocol: "Enter the protocol of the connection (ssh, http, other): "
-      login: "Enter the login connection: "
-      password: "Enter the the password: "
-      port: "Enter the connection port (optional): "
-      comment: "Enter a comment (optional): "
-      otp_key: "Enter the otp secret (base32 secret key): "
+      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"
+      comment: "A comment"
+      otp_key: "The OTP secret"
       valid: "Item has been added!"
     clipboard:
       choice: "What do you want to copy ? [q = quit, p = password, l = login]: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index b07a9e2..54da62b 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -61,16 +61,15 @@ fr:
     add_key:
       valid: "La clé a bien été ajoutée!"
     add_item:
-      title: "Ajout d'un nouvel élément"
-      name: "Entrez le nom: "
-      group: "Entrez le groupe (optionnel): "
-      server: "Entrez le nom de domaine ou l'ip: "
-      protocol: "Entrez le protocole de connexion (ssh, http, other): "
-      login: "Entrez l'identifiant de connexion: "
-      password: "Entrez le mot de passe: "
-      port: "Entrez le port de connexion (optionnel): "
-      comment: "Entrez un commentaire (optionnel): "
-      otp_key: "Entrez le secret OTP: "
+      name: "Le nom de l'élément (obligatoire)"
+      group: "Le nom du groupe"
+      host: "Le nom de domaine ou l'ip"
+      protocol: "Le protocole de connexion (ssh, http, ...)"
+      login: "L'identifiant de connexion"
+      password: "Le mot de passe"
+      port: "Le port de connexion"
+      comment: "Un commentaire"
+      otp_key: "Le secret OTP"
       valid: "L'élément a bien été ajouté!"
     clipboard:
       choice: "Que voulez-vous copier ? : "
diff --git a/templates/add_form.erb b/templates/add_form.erb
index 9cb9d1a..53c0333 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -1,10 +1,10 @@
 ---
-name: 
-group: 
-host: 
-protocol: 
-user: 
-password: 
-port: 
-comment: 
-otp_secret: 
+name:           # <%= I18n.t('form.add_item.name') %>
+group:          # <%= I18n.t('form.add_item.group') %> 
+host:           # <%= I18n.t('form.add_item.host') %>
+protocol:       # <%= I18n.t('form.add_item.protocol') %>
+user:           # <%= I18n.t('form.add_item.login') %>
+password:       # <%= I18n.t('form.add_item.password') %>
+port:           # <%= I18n.t('form.add_item.port') %>
+comment:        # <%= I18n.t('form.add_item.comment') %>
+otp_secret:     # <%= I18n.t('form.add_item.otp_key') %>

From 2a89d8718c7a0add94503d16f6a01ad1f86830ac Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 Aug 2016 09:19:02 +0200
Subject: [PATCH 311/531] add missing gem net-sftp

---
 mpw.gemspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mpw.gemspec b/mpw.gemspec
index fa4afc7..09fe6d9 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
   spec.add_dependency "locale"
   spec.add_dependency "colorize"
   spec.add_dependency "net-ssh"
-  spec.add_dependency "net-scp"
+  spec.add_dependency "net-sftp"
   spec.add_dependency "clipboard"
   spec.add_dependency "rotp"
 end

From 973f36248580b9485291da1468fb1361de461414 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 Aug 2016 10:03:12 +0200
Subject: [PATCH 312/531] fix translate for update template

---
 i18n/en.yml               | 19 +++++++++----------
 i18n/fr.yml               | 19 +++++++++----------
 templates/update_form.erb | 13 +++++++++++--
 3 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index f226203..c9b26eb 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -120,16 +120,15 @@ en:
       wait: "Please waiting during the GPG key generate, this process can take few minutes."
       valid: "Your GPG key has been created ;-)"
     update_item:
-      title: "Update an item"
-      name: "Enter the name [%{name}]: "
-      group: "Enter the group [%{group}]: "
-      server: "Enter the hostname or ip [%{server}]: "
-      protocol: "Enter the protocol of the connection [%{protocol}]: "
-      login: "Enter the login connection [%{login}]: "
-      password: "Enter the the password: "
-      port: "Enter the connection port [%{port}]: "
-      comment: "Enter a comment [%{comment}]: "
-      otp_key: "Enter the otp secret (base32 secret key): "
+      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"
+      comment: "A comment"
+      otp_key: "The OTP secret (leave empty if you don't want change"
       valid: "Item has been updated!" 
     export:
       valid: "The export in %{file} is succesfull!"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 54da62b..5715fe2 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -120,16 +120,15 @@ fr:
       wait: "Veuillez patienter durant la génération de votre clé GPG, ce processus peut prendre quelques minutes."
       valid: "Votre clé GPG a bien été créée ;-)"
     update_item:
-      title: "Mis à jour d'un élément"
-      name: "Entrez le nom [%{name}]: "
-      group: "Entrez le groupe [%{group}]: "
-      server: "Entrez le nom de domaine ou l'ip du serveur [%{server}]: "
-      protocol: "Entrez le protocole de connexion [%{protocol}]: "
-      login: "Entrez votre identifiant de connexion [%{login}]: "
-      password: "Entrez le mot de passe: "
-      port: "Entrez un port de connexion [%{port}]: "
-      comment: "Entrez un commentaire [%{comment}]: "
-      otp_key: "Entrez le secret OTP: "
+      name: "Le nom de l'élément (obligatoire)"
+      group: "Le nom du groupe"
+      host: "Le nom de domaine ou l'ip"
+      protocol: "Le protocole de connexion (ssh, http, ...)"
+      login: "L'identifiant de connexion"
+      password: "Le mot de passe (laissez vide si vous ne voulez pas le changer)"
+      port: "Le port de connexion"
+      comment: "Un commentaire"
+      otp_key: "Le secret OTP (laissez vide si vous ne voulez pas le changer)"
       valid: "L'élément a bien été mis à jour!" 
     export:
       valid: "L'export dans %{file} est un succès!"
diff --git a/templates/update_form.erb b/templates/update_form.erb
index 13ce1d6..57178cc 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -1,10 +1,19 @@
 ---
-name: <%= item.name %> 
+# <%= I18n.t('form.update_item.name') %>
+name: <%= item.name %>
+# <%= I18n.t('form.update_item.group') %>
 group: <%= item.group %>
+# <%= I18n.t('form.update_item.host') %>
 host: <%= item.host %>
+# <%= I18n.t('form.update_item.protocol') %>
 protocol: <%= item.protocol %>
+# <%= I18n.t('form.update_item.login') %>
 user: <%= item.user %>
+# <%= I18n.t('form.update_item.password') %>
 password: 
+# <%= I18n.t('form.update_item.port') %>
 port: <%= item.port %>
+# <%= I18n.t('form.update_item.comment') %>
 comment: <%= item.comment %>
-otp_secret: 
+# <%= I18n.t('form.update_item.otp_key') %>
+opt_code: 

From 126b896c24a80e452751b230eb360f90c4301246 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 20 Aug 2016 16:09:34 +0200
Subject: [PATCH 313/531] add otp_key for import and export

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index baa83f5..6c3fc00 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -287,6 +287,7 @@ class MPW
 			                        'password'  => get_password(item.id),
 			                        'port'      => item.port,
 			                        'comment'   => item.comment,
+			                        'otp_key'   => get_otp_code(item.id),
 			                        'last_edit' => item.last_edit,
 			                        'created'   => item.created,
 			                       }
@@ -314,7 +315,8 @@ class MPW
 			raise 'Item is empty' if item.empty?
 
 			@data.push(item)
-			set_password(item.id, row['password'])
+			set_password(item.id, row['password']) if not row['password'].to_s.empty?
+			set_otp_code(item.id, row['otp_key'])  if not row['otp_key'].to_s.empty?
 		end
 	rescue Exception => e 
 		raise "#{I18n.t('error.import', file: file)}\n#{e}"
@@ -435,7 +437,7 @@ class MPW
 	# Get an otp code
 	# @args: id -> the item id
 	# @rtrn: an otp code
-	def	get_otp_code(id)
+	def get_otp_code(id)
 		if not @otp_keys.has_key?(id)
 			return 0
 		else

From dd9f2853ce330375a923f0b0daaac9aac84dc690 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 26 Sep 2016 21:53:17 +0200
Subject: [PATCH 314/531] fix translate

---
 i18n/en.yml | 6 ++++--
 i18n/fr.yml | 6 ++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index c9b26eb..7c7bcc5 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -82,11 +82,13 @@ en:
          login: "Press <l> to copy the login"
          password: "Press <p> to copy the password"
          otp_code: "Press <o> to copy the otp code"
+         update: "Press <u> to update the item"
+         delete: "Press <d> to delete the item"
     delete_key:
       valid: "Key has been deleted!"
     delete_item:
-      ask: "Are you sure you want to remove the item %{id} ?"
-      valid: "The item %{id} has been removed!"
+      ask: "Are you sure you want to remove the item ?"
+      valid: "The item has been removed!"
     import:
       ask: "Are you sure you want to import this file %{file} ?"
       valid: "The import is succesfull!"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 5715fe2..a5d7aff 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -82,11 +82,13 @@ fr:
          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"
+         update: "Pressez <u> pour mettre à jour l'élément"
+         delete: "Pressez <d> pour supprimer l'élément"
     delete_key:
       valid: "La clé a bien été supprimée!"
     delete_item:
-      ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?"
-      valid: "L'élément %{id} a bien été supprimé!"
+      ask: "Êtes vous sûre de vouloir supprimer l'élément ?"
+      valid: "L'élément a bien été supprimé!"
     import:
       ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
       valid: "L'import est un succès!"

From e7956b20068ff503cd6eddc0eece7b0d65efebeb Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 26 Sep 2016 21:55:38 +0200
Subject: [PATCH 315/531] new setup form

---
 lib/mpw/cli.rb | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 3319168..337b95e 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -41,21 +41,16 @@ class Cli
 	end
 
 	# Create a new config file
-	# @args: lang -> the software language
-	def setup(lang)
-		puts I18n.t('form.setup_config.title')
-		puts '--------------------'
-		language   = ask(I18n.t('form.setup_config.lang', lang: lang)).to_s
-		key        = ask(I18n.t('form.setup_config.gpg_key')).to_s
-		wallet_dir = ask(I18n.t('form.setup_config.wallet_dir', home: "#{@config.config_dir}")).to_s
-		gpg_exe    = ask(I18n.t('form.setup_config.gpg_exe')).to_s
+	# @args: language -> the software language
+	def setup(language)
+		@config.is_valid?
+
+		options  = text_editor('setup_form', language)
+		language = options[:language] || language
 
-		if language.nil? or language.empty?
-			language = lang
-		end
 		I18n.locale = language.to_sym
 
-		@config.setup(key, lang, wallet_dir, gpg_exe)
+		@config.setup(options[:gpg_key], language, options[:wallet_dir], options[:gpg_exe])
 
 		raise I18n.t('error.config.check') if not @config.is_valid?
 

From a8a083250b2d0fa00e4b6635249738373b0d215e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 26 Sep 2016 21:56:00 +0200
Subject: [PATCH 316/531] quit if delete item

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 337b95e..cb061c7 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -260,7 +260,7 @@ class Cli
 				puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
 
 			when 'd', 'delete'
-				delete(item)
+				break if delete(item)
 
 			when 'u', 'update', 'e', 'edit'
 				update(item)
@@ -270,6 +270,8 @@ class Cli
 				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.update')
+				puts I18n.t('form.clipboard.help.delete')
 				next
 			end
 		end
@@ -405,7 +407,7 @@ class Cli
 		confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
 
 		if not confirm =~ /^(y|yes|YES|Yes|Y)$/
-			return
+			return false
 		end
 
 		item.delete
@@ -413,8 +415,11 @@ class Cli
 		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.delete_item.valid')}".green
+
+		return true
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #16: #{e}".red
+		return false
 	end
 
 	# Export the items in a CSV file

From 4b36fa9081733cfe36b285e3929d8d5727166a20 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 26 Sep 2016 22:13:11 +0200
Subject: [PATCH 317/531] add template setup form

---
 templates/setup_form.erb | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 templates/setup_form.erb

diff --git a/templates/setup_form.erb b/templates/setup_form.erb
new file mode 100644
index 0000000..f7e6d7b
--- /dev/null
+++ b/templates/setup_form.erb
@@ -0,0 +1,9 @@
+---
+# <%= I18n.t('form.setup_config.lang') %>
+language: <%= @config.lang %>
+# <%= I18n.t('form.setup_config.gpg_key') %>
+gpg_key: <%= @config.key %>
+# <%= I18n.t('form.setup_config.wallet_dir') %>
+wallet_dir: <%= @config.config_dir %>
+# <%= I18n.t('form.setup_config.gpg_exe') %>
+gpg_exe: <%= @config.gpg_exe %>

From d8ac5ed35a0723c35ab40019030f9081c8ca5db4 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 12 Oct 2016 23:23:03 +0200
Subject: [PATCH 318/531] first cut binary

---
 bin/mpw        | 173 +--------------------------------------
 bin/mpw-old    | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
 bin/mpw-passwd |  50 ++++++++++++
 3 files changed, 268 insertions(+), 169 deletions(-)
 create mode 100755 bin/mpw-old
 create mode 100644 bin/mpw-passwd

diff --git a/bin/mpw b/bin/mpw
index 5f0d64d..2d32700 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -18,13 +18,9 @@
 
 $: << File.expand_path('../../lib', __FILE__)
 
-require 'optparse'
 require 'locale'
 require 'set'
 require 'i18n'
-require 'mpw/mpw'
-require 'mpw/config'
-require 'mpw/cli'
 
 # --------------------------------------------------------- #
 # Set local
@@ -45,170 +41,9 @@ I18n.locale         = lang.to_sym
 # Options
 # --------------------------------------------------------- #
 
-options_password    = {}
-options             = {}
-options[:force]     = false
-options[:otp]       = false
-options[:sync]      = true
-options[:clipboard] = true
-options[:group]     = nil
-options[:config]    = nil
-options[:wallet]    = nil
+bin_dir = File.dirname(__FILE__)
+command = "#{bin_dir}/mpw-#{ARGV[0]}"
 
-OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
-
-	opts.on('-a', '--add', I18n.t('option.add')) do
-		options[:add] = true
-	end
-
-	opts.on('-A', '--show-all', I18n.t('option.show_all')) do
-		options[:type] = nil
-		options[:show] = ''
-	end
-
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
-
-	opts.on('-C', '--no-clipboard', I18n.t('option.clipboard')) do
-		options[:clipboard] = false
-	end
-
-	opts.on('-e', '--export', I18n.t('option.export')) do
-		options[:export] = true
-	end
-
-	opts.on('-f', '--file FILE', I18n.t('option.file')) do |file|
-		options[:file] = file
-	end
-
-	opts.on('-F', '--force', I18n.t('option.force')) do
-		options[:force] = true
-	end
-
-	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
-		options[:group] = group
-	end
-
-	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
-		options_password[:length] = length
-	end
-
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
-
-	opts.on('-I', '--import', I18n.t('option.import')) do
-		options[:import] = true
-	end
-
-	opts.on('-k', '--key KEY', I18n.t('option.key')) do |key|
-		options[:key] = key
-	end
-
-	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
-		options_password[:numeric] = true
-	end
-
-	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
-	opts.on('-O', '--otp', I18n.t('option.otp')) do
-		options[:otp] = true
-	end
-
-	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
-		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
-	end
-
-	opts.on('-S', '--setup', I18n.t('option.setup')) do
-		options[:setup] = true
-	end
-
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
-
-	opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
-		options[:setup_wallet] = true
-	end
-
-	opts.on('-x', '--special-chars', I18n.t('option.special_chars')) do
-		options_password[:special] = true
-	end
-
-	opts.on('-y', '--alpha', I18n.t('option.alpha')) do
-		options_password[:alpha] = true
-	end
-end.parse!
-
-# --------------------------------------------------------- #
-# Main
-# --------------------------------------------------------- #
-
-# Generate password
-if not options_password.empty?
-	puts MPW::MPW::password(options_password)
-	exit 0
-end
-
-begin
-	config = MPW::Config.new(options[:config])
-	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
-		
-	# Setup a new config 
-	if not options[:setup].nil?
-		cli.setup(lang)
-		exit 0
-	end
-
-	cli.setup(lang)   if not config.is_valid?
-	cli.setup_gpg_key if not config.check_gpg_key?
-	
-	cli.get_wallet(options[:wallet])
-	cli.decrypt
-	
-	# Display the item's informations
-	if not options[:show].nil?
-		opts = {search:    options[:show],
-		        group:     options[:group],
-		       }
-	
-		cli.display(opts)
-	
-	# Add a new item
-	elsif not options[:add].nil? and options[:key].nil?
-		cli.add
-	
-	# Add a new public key in wallet
-	elsif not options[:add].nil? and not options[:key].nil?
-		cli.add_key(options[:key], options[:file])
-	
-	# Delete a public key in wallet
-	elsif not options[:delete].nil? and not options[:key].nil?
-		cli.delete_key(options[:key])
-	
-	# Export
-	elsif not options[:export].nil? and not options[:file].nil?
-		cli.export(options[:file])
-	
-	# Import
-	elsif not options[:import].nil? and not options[:file].nil?
-		cli.import(options[:file])
-	
-	# Setup wallet config
-	elsif not options[:setup_wallet].nil?
-		cli.setup_wallet_config
-	
-	end
-	
-	cli = nil
-	
-	exit 0
-
-rescue SystemExit, Interrupt
-	exit 3
+if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
+	Kernel.load(command)
 end
diff --git a/bin/mpw-old b/bin/mpw-old
new file mode 100755
index 0000000..5f0d64d
--- /dev/null
+++ b/bin/mpw-old
@@ -0,0 +1,214 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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.
+
+$: << File.expand_path('../../lib', __FILE__)
+
+require 'optparse'
+require 'locale'
+require 'set'
+require 'i18n'
+require 'mpw/mpw'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Set local
+# --------------------------------------------------------- #
+
+lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+
+if defined?(I18n.enforce_available_locales)
+	I18n.enforce_available_locales = true
+end
+
+I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
+I18n.default_locale = :en
+I18n.locale         = lang.to_sym
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options_password    = {}
+options             = {}
+options[:force]     = false
+options[:otp]       = false
+options[:sync]      = true
+options[:clipboard] = true
+options[:group]     = nil
+options[:config]    = nil
+options[:wallet]    = nil
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+
+	opts.on('-a', '--add', I18n.t('option.add')) do
+		options[:add] = true
+	end
+
+	opts.on('-A', '--show-all', I18n.t('option.show_all')) do
+		options[:type] = nil
+		options[:show] = ''
+	end
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-C', '--no-clipboard', I18n.t('option.clipboard')) do
+		options[:clipboard] = false
+	end
+
+	opts.on('-e', '--export', I18n.t('option.export')) do
+		options[:export] = true
+	end
+
+	opts.on('-f', '--file FILE', I18n.t('option.file')) do |file|
+		options[:file] = file
+	end
+
+	opts.on('-F', '--force', I18n.t('option.force')) do
+		options[:force] = true
+	end
+
+	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
+		options[:group] = group
+	end
+
+	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
+		options_password[:length] = length
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-I', '--import', I18n.t('option.import')) do
+		options[:import] = true
+	end
+
+	opts.on('-k', '--key KEY', I18n.t('option.key')) do |key|
+		options[:key] = key
+	end
+
+	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
+		options_password[:numeric] = true
+	end
+
+	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
+	opts.on('-O', '--otp', I18n.t('option.otp')) do
+		options[:otp] = true
+	end
+
+	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
+		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
+	end
+
+	opts.on('-S', '--setup', I18n.t('option.setup')) do
+		options[:setup] = true
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+
+	opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
+		options[:setup_wallet] = true
+	end
+
+	opts.on('-x', '--special-chars', I18n.t('option.special_chars')) do
+		options_password[:special] = true
+	end
+
+	opts.on('-y', '--alpha', I18n.t('option.alpha')) do
+		options_password[:alpha] = true
+	end
+end.parse!
+
+# --------------------------------------------------------- #
+# Main
+# --------------------------------------------------------- #
+
+# Generate password
+if not options_password.empty?
+	puts MPW::MPW::password(options_password)
+	exit 0
+end
+
+begin
+	config = MPW::Config.new(options[:config])
+	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
+		
+	# Setup a new config 
+	if not options[:setup].nil?
+		cli.setup(lang)
+		exit 0
+	end
+
+	cli.setup(lang)   if not config.is_valid?
+	cli.setup_gpg_key if not config.check_gpg_key?
+	
+	cli.get_wallet(options[:wallet])
+	cli.decrypt
+	
+	# Display the item's informations
+	if not options[:show].nil?
+		opts = {search:    options[:show],
+		        group:     options[:group],
+		       }
+	
+		cli.display(opts)
+	
+	# Add a new item
+	elsif not options[:add].nil? and options[:key].nil?
+		cli.add
+	
+	# Add a new public key in wallet
+	elsif not options[:add].nil? and not options[:key].nil?
+		cli.add_key(options[:key], options[:file])
+	
+	# Delete a public key in wallet
+	elsif not options[:delete].nil? and not options[:key].nil?
+		cli.delete_key(options[:key])
+	
+	# Export
+	elsif not options[:export].nil? and not options[:file].nil?
+		cli.export(options[:file])
+	
+	# Import
+	elsif not options[:import].nil? and not options[:file].nil?
+		cli.import(options[:file])
+	
+	# Setup wallet config
+	elsif not options[:setup_wallet].nil?
+		cli.setup_wallet_config
+	
+	end
+	
+	cli = nil
+	
+	exit 0
+
+rescue SystemExit, Interrupt
+	exit 3
+end
diff --git a/bin/mpw-passwd b/bin/mpw-passwd
new file mode 100644
index 0000000..c99d002
--- /dev/null
+++ b/bin/mpw-passwd
@@ -0,0 +1,50 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/mpw'
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw-passwd [options]"
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
+		options[:length] = length.to_i
+	end
+
+	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
+		options[:numeric] = true
+	end
+
+	opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
+		options[:special] = true
+	end
+
+	opts.on('-a', '--alpha', I18n.t('option.alpha')) do
+		options[:alpha] = true
+	end
+end.parse!
+
+puts MPW::MPW::password(options)
+exit 0

From 4085d43f973d7b48d3c5480ed5878c32c3dd1746 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 13 Oct 2016 18:26:56 +0200
Subject: [PATCH 319/531] rename mpw-passwd to mpw-genpwd

---
 bin/{mpw-passwd => mpw-genpwd} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename bin/{mpw-passwd => mpw-genpwd} (100%)

diff --git a/bin/mpw-passwd b/bin/mpw-genpwd
similarity index 100%
rename from bin/mpw-passwd
rename to bin/mpw-genpwd

From 3d99ac80deb10dcd836f0a9c3c3b1bc35c8a8d46 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 13 Oct 2016 21:59:51 +0200
Subject: [PATCH 320/531] new binary for config

---
 bin/mpw-config    | 91 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/mpw/cli.rb    | 44 ++++++++++++-----------
 lib/mpw/config.rb |  3 +-
 3 files changed, 116 insertions(+), 22 deletions(-)
 create mode 100644 bin/mpw-config

diff --git a/bin/mpw-config b/bin/mpw-config
new file mode 100644
index 0000000..ebde9e2
--- /dev/null
+++ b/bin/mpw-config
@@ -0,0 +1,91 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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.
+
+$: << File.expand_path('../../lib', __FILE__)
+
+require 'optparse'
+require 'locale'
+require 'set'
+require 'i18n'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Set local
+# --------------------------------------------------------- #
+
+lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+
+if defined?(I18n.enforce_available_locales)
+	I18n.enforce_available_locales = true
+end
+
+I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
+I18n.default_locale = :en
+I18n.locale         = lang.to_sym
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-g', '--gpg-exe PATH', I18n.t('option.lang')) do |gpg_exe|
+		options[:gpg_exe] = gpg_exe
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('--init', I18n.t('option.init')) do
+		options[:init] = true
+	end
+
+	opts.on('-k', '--key GPG_KEY', I18n.t('option.lang')) do |gpg_key|
+		options[:gpg_key] = gpg_key
+	end
+
+	opts.on('-l', '--lang LANG', I18n.t('option.lang')) do |lang|
+		options[:lang] = lang
+	end
+
+	opts.on('-w', '--wallet-dir LANG', I18n.t('option.lang')) do |wallet_dir|
+		options[:wallet_dir] = wallet_dir
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
+
+if not options[:init].nil?
+	cli.setup(options)
+	cli.setup_gpg_key(options[:gpg_key]) if not config.check_gpg_key?
+	exit 0
+end
+
+cli.set_config(options)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index cb061c7..0547ac4 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -17,6 +17,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 require 'readline'
+require 'locale'
 require 'i18n'
 require 'colorize'
 require 'highline/import'
@@ -40,17 +41,32 @@ class Cli
 		@otp       = otp
 	end
 
+	# Change a parameter int the config after init
+	# @args: options -> param to change
+	def set_config(options)
+		raise I18n.t('error.config.check') if not @config.is_valid?
+		
+		gpg_key    = options[:gpg_key]    || @config.key
+		lang       = options[:lang]       || @config.lang
+		wallet_dir = options[:wallet_dir] || @config.wallet_dir
+		gpg_exe    = options[:gpg_exe]    || @config.gpg_exe
+
+		@config.setup(gpg_key, lang, wallet_dir, gpg_exe)
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #15: #{e}".red
+		exit 2
+	end
+
 	# Create a new config file
 	# @args: language -> the software language
-	def setup(language)
+	def setup(options)
 		@config.is_valid?
 
-		options  = text_editor('setup_form', language)
-		language = options[:language] || language
+		lang = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
-		I18n.locale = language.to_sym
+		I18n.locale = lang.to_sym
 
-		@config.setup(options[:gpg_key], language, options[:wallet_dir], options[:gpg_exe])
+		@config.setup(options[:gpg_key], lang, options[:wallet_dir], options[:gpg_exe])
 
 		raise I18n.t('error.config.check') if not @config.is_valid?
 
@@ -61,16 +77,11 @@ class Cli
 	end
 	
 	# Setup a new GPG key
-	def setup_gpg_key
+	# @args: gpg_key -> the key name
+	def setup_gpg_key(gpg_key)
 		puts I18n.t('form.setup_gpg_key.title')
 		puts '--------------------'
 		ask      = ask(I18n.t('form.setup_gpg_key.ask')).to_s
-		
-		if not ['Y', 'y', 'O', 'o'].include?(ask)
-			raise I18n.t('form.setup_gpg_key.no_create')
-		end
-
-		name     = ask(I18n.t('form.setup_gpg_key.name')).to_s
 		password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
 		confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
 
@@ -78,16 +89,9 @@ class Cli
 			raise I18n.t('form.setup_gpg_key.error_password')
 		end
 
-		length   = ask(I18n.t('form.setup_gpg_key.length')).to_s
-		expire   = ask(I18n.t('form.setup_gpg_key.expire')).to_s
-		password = password.to_s
-
-		length = length.nil? or length.empty? ? 2048 : length.to_i
-		expire = expire.nil? or expire.empty? ? 0    : expire.to_i
-
 		puts I18n.t('form.setup_gpg_key.wait')
 		
-		@config.setup_gpg_key(password, name, length, expire)
+		@config.setup_gpg_key(password.to_s, gpg_key)
 
 		puts "#{I18n.t('form.setup_gpg_key.valid')}".green
 	rescue Exception => e
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 90df409..b504c2a 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -57,12 +57,11 @@ class Config
 	#        gpg_exe -> the  path of gpg executable
 	# @rtrn: true if le config file is create
 	def setup(key, lang, wallet_dir, gpg_exe)
-
 		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 			raise I18n.t('error.config.key_bad_format')
 		end
 
-		if wallet_dir.empty?
+		if wallet_dir.to_s.empty?
 			wallet_dir = "#{@config_dir}/wallets"
 		end
 

From 422dacd29d805ec397c877933c3c34c65e99cb8e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Fri, 14 Oct 2016 18:43:19 +0200
Subject: [PATCH 321/531] remove unused code

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

diff --git a/bin/mpw-config b/bin/mpw-config
index ebde9e2..41223c6 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -16,30 +16,10 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-$: << File.expand_path('../../lib', __FILE__)
-
 require 'optparse'
-require 'locale'
-require 'set'
-require 'i18n'
 require 'mpw/config'
 require 'mpw/cli'
 
-# --------------------------------------------------------- #
-# Set local
-# --------------------------------------------------------- #
-
-lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-
-if defined?(I18n.enforce_available_locales)
-	I18n.enforce_available_locales = true
-end
-
-I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
-I18n.default_locale = :en
-I18n.locale         = lang.to_sym
-
 # --------------------------------------------------------- #
 # Options
 # --------------------------------------------------------- #

From 3fd70721f339b64ef97b5e6a9c9e18436e220c91 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Fri, 14 Oct 2016 21:48:23 +0200
Subject: [PATCH 322/531] new binary for add

---
 bin/mpw-add | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 bin/mpw-add

diff --git a/bin/mpw-add b/bin/mpw-add
new file mode 100644
index 0000000..d959c19
--- /dev/null
+++ b/bin/mpw-add
@@ -0,0 +1,48 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli.add

From e45c528a3fd8a0b0c279885776e57dbf7c2e57ca Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Fri, 14 Oct 2016 23:20:55 +0200
Subject: [PATCH 323/531] init: iniatilize a default wallet

---
 bin/mpw-config |  3 ++-
 lib/mpw/cli.rb | 38 ++++++++++++++++++++++----------------
 lib/mpw/mpw.rb |  9 ++++++---
 3 files changed, 30 insertions(+), 20 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 41223c6..d29a93c 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -60,11 +60,12 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
+cli    = MPW::Cli.new(config, nil, nil, nil)
 
 if not options[:init].nil?
 	cli.setup(options)
 	cli.setup_gpg_key(options[:gpg_key]) if not config.check_gpg_key?
+	cli.setup_wallet_config
 	exit 0
 end
 
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 0547ac4..2f9ad31 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -89,9 +89,11 @@ class Cli
 			raise I18n.t('form.setup_gpg_key.error_password')
 		end
 
+		@password = password.to_s 
+
 		puts I18n.t('form.setup_gpg_key.wait')
 		
-		@config.setup_gpg_key(password.to_s, gpg_key)
+		@config.setup_gpg_key(@password, gpg_key)
 
 		puts "#{I18n.t('form.setup_gpg_key.valid')}".green
 	rescue Exception => e
@@ -100,23 +102,27 @@ class Cli
 	end
 
 	# Setup wallet config for sync
-	def setup_wallet_config
-		config         = {}
-		config['sync'] = {}
+	# @args: wallet -> the wallet name
+	def setup_wallet_config(wallet = nil)
+		#config         = {}
+		#config['sync'] = {}
 
-		puts I18n.t('form.setup_wallet.title')
-		puts '--------------------'
-		config['sync']['type']     = ask(I18n.t('form.setup_wallet.sync_type')).to_s
+		#puts '--------------------'
+		#config['sync']['type']     = ask(I18n.t('form.setup_wallet.sync_type')).to_s
 
-		if ['ftp', 'ssh'].include?(config['sync']['type'].downcase)
-			config['sync']['host']     = ask(I18n.t('form.setup_wallet.sync_host')).to_s
-			config['sync']['port']     = ask(I18n.t('form.setup_wallet.sync_port')).to_s
-			config['sync']['user']     = ask(I18n.t('form.setup_wallet.sync_user')).to_s
-			config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
-			config['sync']['path']     = ask(I18n.t('form.setup_wallet.sync_path')).to_s
-		end
+		#if ['ftp', 'ssh'].include?(config['sync']['type'].downcase)
+		#	config['sync']['host']     = ask(I18n.t('form.setup_wallet.sync_host')).to_s
+		#	config['sync']['port']     = ask(I18n.t('form.setup_wallet.sync_port')).to_s
+		#	config['sync']['user']     = ask(I18n.t('form.setup_wallet.sync_user')).to_s
+		#	config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
+		#	config['sync']['path']     = ask(I18n.t('form.setup_wallet.sync_path')).to_s
+		#end
 
-		@mpw.set_config(config)
+		wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
+
+		@mpw = MPW.new(@config.key, wallet_file, @password, @config.gpg_exe)
+		@mpw.read_data
+		@mpw.set_config
 		@mpw.write_data
 
 		puts "#{I18n.t('form.setup_wallet.valid')}".green
@@ -384,7 +390,7 @@ class Cli
 		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_item.valid')}".green
-	rescue Exception => e
+	#rescue Exception => e
 		puts "#{I18n.t('display.error')} #13: #{e}".red
 	end
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 6c3fc00..f4e8ce3 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -112,7 +112,7 @@ class MPW
 
 			data.merge!(item.id => {'id'        => item.id,
 			                        'name'      => item.name,
-		                            'group'     => item.group,
+			                        'group'     => item.group,
 			                        'host'      => item.host,
 			                        'protocol'  => item.protocol,
 			                        'user'      => item.user,
@@ -158,7 +158,7 @@ class MPW
 
 		File.rename(tmp_file, @wallet_file)
 	rescue Exception => e
-		File.unlink(tmp_file)
+		File.unlink(tmp_file) if File.exist?(tmp_file)
 
 		raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
 	end
@@ -211,9 +211,12 @@ class MPW
 
 	# Set config
 	# args: config -> a hash with config options
-	def set_config(config)
+	def set_config(config=nil)
+		@config         = {} if @config.nil?
 		@config['sync'] = {} if @config['sync'].nil?
 
+		return if config.to_s.empty?
+
 		@config['sync']['type']      = config['sync']['type']
 		@config['sync']['host']      = config['sync']['host']
 		@config['sync']['port']      = config['sync']['port']

From 1681db0edb10e4391332a60c97eaf4266ddf6e4a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 15 Oct 2016 09:30:41 +0200
Subject: [PATCH 324/531] fix recipients for encrypt

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 2f9ad31..c3a953f 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -79,9 +79,6 @@ class Cli
 	# Setup a new GPG key
 	# @args: gpg_key -> the key name
 	def setup_gpg_key(gpg_key)
-		puts I18n.t('form.setup_gpg_key.title')
-		puts '--------------------'
-		ask      = ask(I18n.t('form.setup_gpg_key.ask')).to_s
 		password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
 		confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
 
@@ -294,6 +291,8 @@ class Cli
 	# Display the wallet
 	# @args: wallet -> the wallet name
 	def get_wallet(wallet=nil)
+		@config.is_valid?
+
 		if wallet.to_s.empty?
 			wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
 
@@ -390,7 +389,7 @@ class Cli
 		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_item.valid')}".green
-	#rescue Exception => e
+	rescue Exception => e
 		puts "#{I18n.t('display.error')} #13: #{e}".red
 	end
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f4e8ce3..11c1293 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -500,12 +500,12 @@ class MPW
 		recipients = []
 		crypto     = GPGME::Crypto.new(armor: true, always_trust: true)
 
+		recipients.push(@key)
 		@keys.each_key do |key|
+			next if key == @key
 			recipients.push(key)
 		end
 
-		recipients.push(@key) if not recipients.index(@key).nil?
-
 		return crypto.encrypt(data, recipients: recipients).read
 	rescue Exception => e 
 		raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"

From ca2225ce711a4cc5fd68c0ef0df99074ac4a1728 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 15 Oct 2016 09:31:20 +0200
Subject: [PATCH 325/531] mpw-add: fix problem with get wallet

---
 bin/mpw-add | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/bin/mpw-add b/bin/mpw-add
index d959c19..52537c0 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -45,4 +45,7 @@ end.parse!
 
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
 cli.add

From 77a8a7695a20f59923f534fbfd2cffbea297a349 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 15 Oct 2016 13:28:48 +0200
Subject: [PATCH 326/531] no try to decrypt if data is empty

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 11c1293..5adeb0c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -486,6 +486,8 @@ class MPW
 	# @args: data -> string to decrypt
 	private
 	def decrypt(data)
+		return nil if data.to_s.empty?
+
 		crypto = GPGME::Crypto.new(armor: true)
 		
 		return crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')

From d5d4091bc23a83c47950af08d4d80c062c415a0b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 15 Oct 2016 17:40:03 +0200
Subject: [PATCH 327/531] remove id and name for an item

---
 lib/mpw/item.rb        | 19 +++----------
 lib/mpw/mpw.rb         | 61 +++++++++++++++++-------------------------
 templates/add_form.erb |  5 ++--
 3 files changed, 30 insertions(+), 55 deletions(-)

diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index ef1309e..626db72 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -21,8 +21,6 @@ require 'i18n'
 module MPW
 class Item
 
-	attr_accessor :id
-	attr_accessor :name
 	attr_accessor :group
 	attr_accessor :host
 	attr_accessor :protocol
@@ -38,15 +36,13 @@ class Item
 	# @args: options -> a hash of parameter
 	# raise an error if the hash hasn't the key name 
 	def initialize(options={})
-		if not options.has_key?(:name) or options[:name].to_s.empty?
+		if not options.has_key?(:host) or options[:host].to_s.empty?
 			raise I18n.t('error.update.name_empty')
 		end
 
 		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
-			@id      = generate_id
 			@created = Time.now.to_i
 		else
-			@id        = options[:id]
 			@created   = options[:created]
 			@last_edit = options[:last_edit]
 			options[:no_update_last_edit] = true
@@ -58,11 +54,10 @@ class Item
 	# Update the item
 	# @args: options -> a hash of parameter
 	def update(options={})
-		if options.has_key?(:name) and options[:name].to_s.empty?
+		if options.has_key?(:host) and options[:host].to_s.empty?
 			raise I18n.t('error.update.name_empty')
 		end
 
-		@name      = options[:name]       if options.has_key?(:name)
 		@group     = options[:group]      if options.has_key?(:group)
 		@host      = options[:host]       if options.has_key?(:host)
 		@protocol  = options[:protocol]   if options.has_key?(:protocol)
@@ -79,8 +74,6 @@ class Item
 
 	# Delete all data
 	def delete
-		@id        = nil
-		@name      = nil
 		@group     = nil
 		@host      = nil
 		@protocol  = nil
@@ -93,17 +86,11 @@ class Item
 	end
 
 	def empty?
-		return @name.to_s.empty?
+		return @host.to_s.empty?
 	end
 
 	def nil?
 		return false
 	end
-
-	# Generate an random id
-	private
-	def generate_id
-		return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
-	end
 end
 end	
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 5adeb0c..fe80f40 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -82,9 +82,7 @@ class MPW
 
 		if not data.nil? and not data.empty?
 			YAML.load(data).each_value do |d|
-				@data.push(Item.new(id:        d['id'],
-				                    name:      d['name'],
-				                    group:     d['group'],
+				@data.push(Item.new(group:     d['group'],
 				                    host:      d['host'],
 				                    protocol:  d['protocol'],
 				                    user:      d['user'],
@@ -110,17 +108,15 @@ class MPW
 		@data.each do |item|
 			next if item.empty?
 
-			data.merge!(item.id => {'id'        => item.id,
-			                        'name'      => item.name,
-			                        'group'     => item.group,
-			                        'host'      => item.host,
-			                        'protocol'  => item.protocol,
-			                        'user'      => item.user,
-			                        'port'      => item.port,
-			                        'comment'   => item.comment,
-			                        'last_edit' => item.last_edit,
-			                        'created'   => item.created,
-			                       }
+			data.merge!("#{item.user}@#{item.host}" => {'group'     => item.group,
+			                                            'host'      => item.host,
+			                                            'protocol'  => item.protocol,
+			                                            'user'      => item.user,
+			                                            'port'      => item.port,
+			                                            'comment'   => item.comment,
+			                                            'last_edit' => item.last_edit,
+			                                            'created'   => item.created,
+			                                           }
 			           )
 		end
 
@@ -251,11 +247,10 @@ class MPW
 			next if item.empty?
 			next if not group.empty?    and not group.eql?(item.group.downcase)
 			
-			name    = item.name.to_s.downcase
 			host    = item.host.to_s.downcase
 			comment = item.comment.to_s.downcase
 
-			if not name =~ /^.*#{search}.*$/ and not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/ 
+			if not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/ 
 				next
 			end
 
@@ -281,19 +276,17 @@ class MPW
 	def export(file)
 		data = {}
 		@data.each do |item|
-			data.merge!(item.id => {'id'        => item.id,
-			                        'name'      => item.name,
-			                        'group'     => item.group,
-			                        'host'      => item.host,
-			                        'protocol'  => item.protocol,
-			                        'user'      => item.user,
-			                        'password'  => get_password(item.id),
-			                        'port'      => item.port,
-			                        'comment'   => item.comment,
-			                        'otp_key'   => get_otp_code(item.id),
-			                        'last_edit' => item.last_edit,
-			                        'created'   => item.created,
-			                       }
+			data.merge!("#{item.login}@#{item.host}" => {'group'     => item.group,
+			                                             'host'      => item.host,
+			                                             'protocol'  => item.protocol,
+			                                             'user'      => item.user,
+			                                             'password'  => get_password(item.id),
+			                                             'port'      => item.port,
+			                                             'comment'   => item.comment,
+			                                             'otp_key'   => get_otp_code(item.id),
+			                                             'last_edit' => item.last_edit,
+			                                             'created'   => item.created,
+			                                            }
 			            )
 		end
 
@@ -306,8 +299,7 @@ class MPW
 	# @args: file -> path to file import
 	def import(file)
 		YAML::load_file(file).each_value do |row| 
-			item = Item.new(name:     row['name'], 
-			                group:    row['group'],
+			item = Item.new(group:    row['group'],
 			                host:     row['host'],
 			                protocol: row['protocol'],
 			                user:     row['user'],
@@ -370,8 +362,7 @@ class MPW
 
 					# Update item
 					if item.last_edit < r.last_edit
-						item.update(name:      r.name,
-						            group:     r.group,
+						item.update(group:     r.group,
 						            host:      r.host,
 						            protocol:  r.protocol,
 						            user:      r.user,
@@ -398,9 +389,7 @@ class MPW
 		remote.list.each do |r|
 			next if r.last_edit <= get_last_sync
 
-			item = Item.new(id:        r.id,
-			                name:      r.name,
-			                group:     r.group,
+			item = Item.new(group:     r.group,
 			                host:      r.host,
 			                protocol:  r.protocol,
 			                user:      r.user,
diff --git a/templates/add_form.erb b/templates/add_form.erb
index 53c0333..447788e 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -1,9 +1,8 @@
 ---
-name:           # <%= I18n.t('form.add_item.name') %>
-group:          # <%= I18n.t('form.add_item.group') %> 
 host:           # <%= I18n.t('form.add_item.host') %>
-protocol:       # <%= I18n.t('form.add_item.protocol') %>
 user:           # <%= I18n.t('form.add_item.login') %>
+group:          # <%= I18n.t('form.add_item.group') %> 
+protocol:       # <%= I18n.t('form.add_item.protocol') %>
 password:       # <%= I18n.t('form.add_item.password') %>
 port:           # <%= I18n.t('form.add_item.port') %>
 comment:        # <%= I18n.t('form.add_item.comment') %>

From dc8a8d76fc639a0d074085cb021aa287b6066d0f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 15 Oct 2016 17:40:35 +0200
Subject: [PATCH 328/531] new binary for list

---
 bin/mpw-list   | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/mpw/cli.rb | 32 +++++++++++++++++++++++++
 2 files changed, 95 insertions(+)
 create mode 100644 bin/mpw-list

diff --git a/bin/mpw-list b/bin/mpw-list
new file mode 100644
index 0000000..9bde1ff
--- /dev/null
+++ b/bin/mpw-list
@@ -0,0 +1,63 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+		options[:group] = group
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+		options[:pattern] = pattern
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+opts = { search: options[:pattern],
+         group:  options[:group],
+       }
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
+cli.list(opts)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index c3a953f..f2bf72a 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -142,6 +142,38 @@ class Cli
 		exit 2
 	end
 
+	# Display the query's result
+	# @args: options -> the option to search
+	def list(options={})
+		result = @mpw.list(options)
+
+		if result.length == 0
+			puts I18n.t('display.nothing')
+
+		else
+			group = '.'
+
+			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+
+			result.each do |item|
+				if group != item.group
+					group = item.group
+
+					if group.to_s.empty?
+						puts I18n.t('display.no_group').yellow
+					else
+						puts "\n#{group}".yellow
+					end
+				end
+
+				print " |_ ".yellow
+				print "#{item.user}@#{item.host}"
+				print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
+				print "\n"
+			end
+		end
+	end
+
 	# Display the query's result
 	# @args: search -> the string to search
 	#        protocol -> search from a particular protocol

From fbac3df19341042212116ef2162cae6dbba2dacf Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 16 Oct 2016 09:41:13 +0200
Subject: [PATCH 329/531] re add id for item

---
 lib/mpw/item.rb | 16 +++++++++++++---
 lib/mpw/mpw.rb  |  9 ++++++---
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 626db72..2e2459b 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -21,6 +21,7 @@ require 'i18n'
 module MPW
 class Item
 
+	attr_accessor :id
 	attr_accessor :group
 	attr_accessor :host
 	attr_accessor :protocol
@@ -40,9 +41,11 @@ class Item
 			raise I18n.t('error.update.name_empty')
 		end
 
-		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?  
+		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
+			@id = generate_id
 			@created = Time.now.to_i
 		else
+			@id = options[:id]
 			@created   = options[:created]
 			@last_edit = options[:last_edit]
 			options[:no_update_last_edit] = true
@@ -74,6 +77,7 @@ class Item
 
 	# Delete all data
 	def delete
+		@id        = nil
 		@group     = nil
 		@host      = nil
 		@protocol  = nil
@@ -86,11 +90,17 @@ class Item
 	end
 
 	def empty?
-		return @host.to_s.empty?
+		return @id.to_s.empty?
 	end
 
 	def nil?
 		return false
 	end
+
+	# Generate an random id
+	private
+	def generate_id
+		return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
+	end 
+end
 end
-end	
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index fe80f40..0c1609e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -82,7 +82,8 @@ class MPW
 
 		if not data.nil? and not data.empty?
 			YAML.load(data).each_value do |d|
-				@data.push(Item.new(group:     d['group'],
+				@data.push(Item.new(id:        d['id'],
+				                    group:     d['group'],
 				                    host:      d['host'],
 				                    protocol:  d['protocol'],
 				                    user:      d['user'],
@@ -108,7 +109,8 @@ class MPW
 		@data.each do |item|
 			next if item.empty?
 
-			data.merge!("#{item.user}@#{item.host}" => {'group'     => item.group,
+			data.merge!("#{item.user}@#{item.host}" => {'id'        => item.id,
+			                                            'group'     => item.group,
 			                                            'host'      => item.host,
 			                                            'protocol'  => item.protocol,
 			                                            'user'      => item.user,
@@ -389,7 +391,8 @@ class MPW
 		remote.list.each do |r|
 			next if r.last_edit <= get_last_sync
 
-			item = Item.new(group:     r.group,
+			item = Item.new(id:        r.id,
+			                group:     r.group,
 			                host:      r.host,
 			                protocol:  r.protocol,
 			                user:      r.user,

From 4c9c4e21f67fc48c4039e2ee1cdd34d5eac6c760 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 17 Oct 2016 00:06:27 +0200
Subject: [PATCH 330/531] list under table format

---
 lib/mpw/cli.rb | 49 +++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 43 insertions(+), 6 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index f2bf72a..1575a06 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -154,23 +154,60 @@ class Cli
 			group = '.'
 
 			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+			size  = {host: 10,
+			         user: 8,
+			         comment: 15,
+			         otp: 5,
+			        }
+
+			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+
+			result.each do |item|
+				size[:host]    = item.host.length + 3    if item.host.to_s.length > size[:host]
+				size[:user]    = item.user.length + 3    if item.user.to_s.length > size[:user]
+				size[:comment] = item.comment.length + 3 if item.comment.to_s.length > size[:comment]
+			end
+			size[:max] = size[:host] + size[:user] + size[:comment] + size[:otp]
 
 			result.each do |item|
 				if group != item.group
 					group = item.group
 
 					if group.to_s.empty?
-						puts I18n.t('display.no_group').yellow
+						puts I18n.t('display.no_group').red
 					else
-						puts "\n#{group}".yellow
+						puts "\n#{group}".red
 					end
+
+					(size[:max] + 8).times { print '=' }
+					print "\n"
+					print '| Host'
+					(size[:host] - 'Host'.length).times { print ' ' }
+					print '| User'
+					(size[:user] - 'User'.length).times { print ' ' }
+					print '| OTP '
+					print '| Comment'
+					(size[:comment] - 'Comment'.length).times { print ' ' }
+					print "|\n"
+					(size[:max] + 8).times { print '=' }
+					print "\n"
 				end
 
-				print " |_ ".yellow
-				print "#{item.user}@#{item.host}"
-				print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
-				print "\n"
+				print '| '
+				print "#{item.host}".yellow
+				(size[:host] - item.host.to_s.length).times { print ' ' }
+				print '| '
+				print "#{item.user}".green
+				(size[:user] - item.user.to_s.length).times { print ' ' }
+				print '| '
+				4.times { print ' ' }
+				print '| '
+				print "#{item.comment}".magenta
+				(size[:comment] - item.comment.to_s.length).times { print ' ' }
+				print "|\n"
 			end
+
+			print "\n"
 		end
 	end
 

From fc8e5e71151c4aee3027071b8e09449328e5436a Mon Sep 17 00:00:00 2001
From: nishiki <git@yae.im>
Date: Mon, 17 Oct 2016 18:32:34 +0200
Subject: [PATCH 331/531] fix bug in search with group option

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 0c1609e..872c1aa 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -242,12 +242,12 @@ class MPW
 	def list(options={})
 		result = []
 
-		search   = options[:search].to_s.downcase
-		group    = options[:group].to_s.downcase
+		search = options[:search].to_s.downcase
+		group  = options[:group].to_s.downcase
 
 		@data.each do |item|
 			next if item.empty?
-			next if not group.empty?    and not group.eql?(item.group.downcase)
+			next if not group.empty? and not group.eql?(item.group.to_s.downcase)
 			
 			host    = item.host.to_s.downcase
 			comment = item.comment.to_s.downcase

From aeae6e861c539329a8eca5022384c30112ade8e9 Mon Sep 17 00:00:00 2001
From: nishiki <git@yae.im>
Date: Mon, 17 Oct 2016 18:34:43 +0200
Subject: [PATCH 332/531] add attribute otp in item

---
 lib/mpw/item.rb |  3 +++
 lib/mpw/mpw.rb  | 27 ++++++++++++++++-----------
 2 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 2e2459b..4beb49f 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -27,6 +27,7 @@ class Item
 	attr_accessor :protocol
 	attr_accessor :user
 	attr_accessor :port
+	attr_accessor :otp
 	attr_accessor :comment
 	attr_accessor :last_edit
 	attr_accessor :last_sync
@@ -66,6 +67,7 @@ class Item
 		@protocol  = options[:protocol]   if options.has_key?(:protocol)
 		@user      = options[:user]       if options.has_key?(:user)
 		@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
+		@otp       = options[:otp]        if options.has_key?(:otp)
 		@comment   = options[:comment]    if options.has_key?(:comment)
 		@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
 	end
@@ -83,6 +85,7 @@ class Item
 		@protocol  = nil
 		@user      = nil
 		@port      = nil
+		@otp       = nil
 		@comment   = nil
 		@created   = nil
 		@last_edit = nil
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 872c1aa..1572923 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -88,6 +88,7 @@ class MPW
 				                    protocol:  d['protocol'],
 				                    user:      d['user'],
 				                    port:      d['port'],
+				                    otp:       @otp_keys.has_key?(d['id']),
 				                    comment:   d['comment'],
 				                    last_edit: d['last_edit'],
 				                    created:   d['created'],
@@ -277,19 +278,23 @@ class MPW
 	# @args: file -> file where you export the data
 	def export(file)
 		data = {}
+		i    = 1
+
 		@data.each do |item|
-			data.merge!("#{item.login}@#{item.host}" => {'group'     => item.group,
-			                                             'host'      => item.host,
-			                                             'protocol'  => item.protocol,
-			                                             'user'      => item.user,
-			                                             'password'  => get_password(item.id),
-			                                             'port'      => item.port,
-			                                             'comment'   => item.comment,
-			                                             'otp_key'   => get_otp_code(item.id),
-			                                             'last_edit' => item.last_edit,
-			                                             'created'   => item.created,
-			                                            }
+			data.merge!(i => { 'group'     => item.group,
+			                   'host'      => item.host,
+			                   'protocol'  => item.protocol,
+			                   'user'      => item.user,
+			                   'password'  => get_password(item.id),
+			                   'port'      => item.port,
+			                   'comment'   => item.comment,
+			                   'otp_key'   => get_otp_code(item.id),
+			                   'last_edit' => item.last_edit,
+			                   'created'   => item.created,
+			                 }
 			            )
+
+			i += 1
 		end
 
 		File.open(file, 'w') {|f| f << data.to_yaml}

From 2ffaa5114b3bef5c12c117ba54cb8da3ed7bc8ba Mon Sep 17 00:00:00 2001
From: nishiki <git@yae.im>
Date: Mon, 17 Oct 2016 18:35:13 +0200
Subject: [PATCH 333/531] fix template for otp

---
 templates/add_form.erb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/add_form.erb b/templates/add_form.erb
index 447788e..f4f8cae 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -6,4 +6,4 @@ protocol:       # <%= I18n.t('form.add_item.protocol') %>
 password:       # <%= I18n.t('form.add_item.password') %>
 port:           # <%= I18n.t('form.add_item.port') %>
 comment:        # <%= I18n.t('form.add_item.comment') %>
-otp_secret:     # <%= I18n.t('form.add_item.otp_key') %>
+otp_key:        # <%= I18n.t('form.add_item.otp_key') %>

From cc339bbe98cd22ab554bddeb95d500a2a21e9061 Mon Sep 17 00:00:00 2001
From: nishiki <git@yae.im>
Date: Mon, 17 Oct 2016 18:35:30 +0200
Subject: [PATCH 334/531] minor fix id

---
 lib/mpw/mpw.rb | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 1572923..f1ae35e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -110,16 +110,16 @@ class MPW
 		@data.each do |item|
 			next if item.empty?
 
-			data.merge!("#{item.user}@#{item.host}" => {'id'        => item.id,
-			                                            'group'     => item.group,
-			                                            'host'      => item.host,
-			                                            'protocol'  => item.protocol,
-			                                            'user'      => item.user,
-			                                            'port'      => item.port,
-			                                            'comment'   => item.comment,
-			                                            'last_edit' => item.last_edit,
-			                                            'created'   => item.created,
-			                                           }
+			data.merge!(item.id => { 'id'        => item.id,
+			                         'group'     => item.group,
+			                         'host'      => item.host,
+			                         'protocol'  => item.protocol,
+			                         'user'      => item.user,
+			                         'port'      => item.port,
+			                         'comment'   => item.comment,
+			                         'last_edit' => item.last_edit,
+			                         'created'   => item.created,
+			                       }
 			           )
 		end
 

From d49b305f697dd7ea7b5aca4a14493e639010e93d Mon Sep 17 00:00:00 2001
From: nishiki <git@yae.im>
Date: Mon, 17 Oct 2016 18:35:57 +0200
Subject: [PATCH 335/531] cli: add table function

---
 lib/mpw/cli.rb | 131 ++++++++++++++++++++++++++++---------------------
 1 file changed, 74 insertions(+), 57 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 1575a06..5f356ad 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -142,6 +142,79 @@ class Cli
 		exit 2
 	end
 
+	# Format items on a table
+	def table(items=[])
+		group        = '.'
+		i            = 1
+		length_total = 10
+		length       = { id: 3,
+		                 host: 10,
+		                 user: 8,
+		                 comment: 15,
+		                 otp: 5,
+		               }
+
+		items.each do |item|
+			length[:host]    = item.host.length + 3    if item.host.to_s.length > length[:host]
+			length[:user]    = item.user.length + 3    if item.user.to_s.length > length[:user]
+			length[:comment] = item.comment.length + 3 if item.comment.to_s.length > length[:comment]
+		end
+		length[:id]  = items.length.to_s.length + 2 if items.length.to_s.length > length[:id]
+		
+		length.each_value { |v| length_total += v }
+		items.sort!       { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+
+		items.each do |item|
+			if group != item.group
+				group = item.group
+
+				if group.to_s.empty?
+					puts "\n#{I18n.t('display.no_group')}".red
+				else
+					puts "\n#{group}".red
+				end
+
+				print ' '
+				length_total.times { print '=' }
+				print "\n "
+				print ' ID '
+				print '| Host'
+				(length[:host] - 'Host'.length).times { print ' ' }
+				print '| User'
+				(length[:user] - 'User'.length).times { print ' ' }
+				print '| OTP '
+				print '| Comment'
+				(length[:comment] - 'Comment'.length).times { print ' ' }
+				print "\n "
+				length_total.times { print '=' }
+				print "\n"
+			end
+
+			print "  #{i}".cyan
+			(length[:id] - i.to_s.length).times { print ' ' }
+			print '| '
+			print "#{item.host}".yellow
+			(length[:host] - item.host.to_s.length).times { print ' ' }
+			print '| '
+			print "#{item.user}".green
+			(length[:user] - item.user.to_s.length).times { print ' ' }
+			print '| '
+			if item.otp
+				print ' X  '
+			else
+				4.times { print ' ' }
+			end
+			print '| '
+			print "#{item.comment}".magenta
+			(length[:comment] - item.comment.to_s.length).times { print ' ' }
+			print "\n"
+
+			i += 1
+		end
+
+		print "\n"
+	end
+
 	# Display the query's result
 	# @args: options -> the option to search
 	def list(options={})
@@ -151,63 +224,7 @@ class Cli
 			puts I18n.t('display.nothing')
 
 		else
-			group = '.'
-
-			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
-			size  = {host: 10,
-			         user: 8,
-			         comment: 15,
-			         otp: 5,
-			        }
-
-			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
-
-			result.each do |item|
-				size[:host]    = item.host.length + 3    if item.host.to_s.length > size[:host]
-				size[:user]    = item.user.length + 3    if item.user.to_s.length > size[:user]
-				size[:comment] = item.comment.length + 3 if item.comment.to_s.length > size[:comment]
-			end
-			size[:max] = size[:host] + size[:user] + size[:comment] + size[:otp]
-
-			result.each do |item|
-				if group != item.group
-					group = item.group
-
-					if group.to_s.empty?
-						puts I18n.t('display.no_group').red
-					else
-						puts "\n#{group}".red
-					end
-
-					(size[:max] + 8).times { print '=' }
-					print "\n"
-					print '| Host'
-					(size[:host] - 'Host'.length).times { print ' ' }
-					print '| User'
-					(size[:user] - 'User'.length).times { print ' ' }
-					print '| OTP '
-					print '| Comment'
-					(size[:comment] - 'Comment'.length).times { print ' ' }
-					print "|\n"
-					(size[:max] + 8).times { print '=' }
-					print "\n"
-				end
-
-				print '| '
-				print "#{item.host}".yellow
-				(size[:host] - item.host.to_s.length).times { print ' ' }
-				print '| '
-				print "#{item.user}".green
-				(size[:user] - item.user.to_s.length).times { print ' ' }
-				print '| '
-				4.times { print ' ' }
-				print '| '
-				print "#{item.comment}".magenta
-				(size[:comment] - item.comment.to_s.length).times { print ' ' }
-				print "|\n"
-			end
-
-			print "\n"
+			table(result)
 		end
 	end
 

From c75f169b0ca7d28691ac73e7db30d47a3c3347e9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 17 Oct 2016 20:48:20 +0200
Subject: [PATCH 336/531] new binary for update

---
 bin/mpw-update            | 63 ++++++++++++++++++++++++++++++
 lib/mpw/cli.rb            | 81 ++++++++++++++-------------------------
 templates/update_form.erb | 14 +++----
 3 files changed, 97 insertions(+), 61 deletions(-)
 create mode 100644 bin/mpw-update

diff --git a/bin/mpw-update b/bin/mpw-update
new file mode 100644
index 0000000..e521ffc
--- /dev/null
+++ b/bin/mpw-update
@@ -0,0 +1,63 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+		options[:group] = group
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+		options[:pattern] = pattern
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+opts = { search: options[:pattern],
+         group:  options[:group],
+       }
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
+cli.update(opts)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 5f356ad..67bf9a2 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -228,53 +228,19 @@ class Cli
 		end
 	end
 
-	# Display the query's result
-	# @args: search -> the string to search
-	#        protocol -> search from a particular protocol
-	def display(options={})
-		result = @mpw.list(options)
+	# Get an item when multiple choice
+	# @args: items -> array of items
+	# @rtrn: item
+	def get_item(items)
+		return items[0] if items.length == 1
 
-		case result.length
-		when 0
-			puts I18n.t('display.nothing')
-
-		when 1
-			display_item(result.first)
+		items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+		choice = ask(I18n.t('form.select')).to_i
 
+		if choice >= 1 and choice <= items.length
+			return items[choice-1]
 		else
-			group = nil
-			i     = 1
-
-			result.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
-
-			result.each do |item|
-				if group != item.group
-					group = item.group
-
-					if group.empty?
-						puts I18n.t('display.no_group').yellow
-					else
-						puts "\n#{group}".yellow
-					end
-				end
-
-				print " |_ ".yellow
-				print "#{i}: ".cyan
-				print item.name
-				print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
-				print "\n"
-
-				i += 1
-			end
-
-			print "\n"
-			choice = ask(I18n.t('form.select')).to_i
-
-			if choice >= 1 and choice < i 
-				display_item(result[choice-1])
-			else
-				puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
-			end
+			return nil
 		end
 	end
 
@@ -480,17 +446,26 @@ class Cli
 	end
 
 	# Update an item
-	# @args: id -> the item's id
-	def update(item)
-		options = text_editor('update_form', item)
+	# @args: options -> the option to search
+	def update(options={})
+		items = @mpw.list(options)
+		
+		if items.length == 0
+			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+		else
+			table(items) if items.length > 1
 
-		item.update(options)
-		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
-		@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
-		@mpw.write_data
-		@mpw.sync(true) if @sync
+			item    = get_item(items)
+			options = text_editor('update_form', item)
 
-		puts "#{I18n.t('form.update_item.valid')}".green
+			item.update(options)
+			@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
+			@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
+			@mpw.write_data
+			@mpw.sync(true) if @sync
+
+			puts "#{I18n.t('form.update_item.valid')}".green
+		end
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #14: #{e}".red
 	end
diff --git a/templates/update_form.erb b/templates/update_form.erb
index 57178cc..f4a380b 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -1,19 +1,17 @@
 ---
-# <%= I18n.t('form.update_item.name') %>
-name: <%= item.name %>
-# <%= I18n.t('form.update_item.group') %>
-group: <%= item.group %>
 # <%= I18n.t('form.update_item.host') %>
 host: <%= item.host %>
-# <%= I18n.t('form.update_item.protocol') %>
-protocol: <%= item.protocol %>
 # <%= I18n.t('form.update_item.login') %>
 user: <%= item.user %>
 # <%= I18n.t('form.update_item.password') %>
 password: 
+# <%= I18n.t('form.update_item.group') %>
+group: <%= item.group %>
+# <%= I18n.t('form.update_item.protocol') %>
+protocol: <%= item.protocol %>
 # <%= I18n.t('form.update_item.port') %>
 port: <%= item.port %>
+# <%= I18n.t('form.update_item.otp_key') %>
+opt_key: 
 # <%= I18n.t('form.update_item.comment') %>
 comment: <%= item.comment %>
-# <%= I18n.t('form.update_item.otp_key') %>
-opt_code: 

From e196cbf93d4a57768abe69393b635214a4dc547e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 17 Oct 2016 21:17:15 +0200
Subject: [PATCH 337/531] new binary for delete

---
 bin/mpw-delete | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/mpw/cli.rb | 35 ++++++++++++++++------------
 2 files changed, 83 insertions(+), 15 deletions(-)
 create mode 100644 bin/mpw-delete

diff --git a/bin/mpw-delete b/bin/mpw-delete
new file mode 100644
index 0000000..a7e95e8
--- /dev/null
+++ b/bin/mpw-delete
@@ -0,0 +1,63 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+		options[:group] = group
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+		options[:pattern] = pattern
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+opts = { search: options[:pattern],
+         group:  options[:group],
+       }
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
+cli.delete(opts)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 67bf9a2..749007a 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -471,25 +471,30 @@ class Cli
 	end
 
 	# Remove an item
-	# @args: id -> the item's id
-	#        force -> no resquest a validation
-	def delete(item)
-		confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
+	# @args: options -> the option to search
+	def delete(options={})
+		items = @mpw.list(options)
+		
+		if items.length == 0
+			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+		else
+			table(items)
 
-		if not confirm =~ /^(y|yes|YES|Yes|Y)$/
-			return false
+			item    = get_item(items)
+			confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
+	
+			if not confirm =~ /^(y|yes|YES|Yes|Y)$/
+				return false
+			end
+	
+			item.delete
+			@mpw.write_data
+			@mpw.sync(true) if @sync
+	
+			puts "#{I18n.t('form.delete_item.valid')}".green
 		end
-
-		item.delete
-		@mpw.write_data
-		@mpw.sync(true) if @sync
-
-		puts "#{I18n.t('form.delete_item.valid')}".green
-
-		return true
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #16: #{e}".red
-		return false
 	end
 
 	# Export the items in a CSV file

From 985de310fb4f11f4f00c92a196e9a7e2632c07c7 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 19 Oct 2016 12:56:55 +0200
Subject: [PATCH 338/531] new binary for wallet

---
 bin/mpw-wallet | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/mpw/cli.rb | 28 ++++++++++++----------------
 2 files changed, 61 insertions(+), 16 deletions(-)
 create mode 100644 bin/mpw-wallet

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
new file mode 100644
index 0000000..c5fdc7a
--- /dev/null
+++ b/bin/mpw-wallet
@@ -0,0 +1,49 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
+
+	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+cli.list_wallet
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 749007a..b92d5fb 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -340,6 +340,17 @@ class Cli
 		Clipboard.clear
 	end
 
+	# List all wallets
+	def list_wallet
+		@config.is_valid?
+
+		wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
+
+		wallets.each do |wallet|
+			puts File.basename(wallet, '.mpw')
+		end
+	end
+
 	# Display the wallet
 	# @args: wallet -> the wallet name
 	def get_wallet(wallet=nil)
@@ -354,22 +365,7 @@ class Cli
 			when 1
 				@wallet_file = wallets[0]
 			else
-				i = 1
-				wallets.each do |wallet|
-						print "#{i}: ".cyan
-						puts File.basename(wallet, '.mpw')
-
-						i += 1
-				end
-
-				choice = ask(I18n.t('form.select')).to_i
-
-				if choice >= 1 and choice < i
-					@wallet_file = wallets[choice-1]
-				else
-					puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
-					exit 2
-				end
+				@wallet_file = "#{@config.wallet_dir}/default.mpw"
 			end
 		else
 			@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"

From 2f686ca1631219ff4aea958bb6969377241e9160 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 19 Oct 2016 15:08:08 +0200
Subject: [PATCH 339/531] new binary for export

---
 bin/mpw-export | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/mpw/cli.rb | 27 ++++++++++++++++++--
 lib/mpw/mpw.rb | 28 ---------------------
 3 files changed, 92 insertions(+), 30 deletions(-)
 create mode 100644 bin/mpw-export

diff --git a/bin/mpw-export b/bin/mpw-export
new file mode 100644
index 0000000..700d067
--- /dev/null
+++ b/bin/mpw-export
@@ -0,0 +1,67 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
+
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-f', '--file PATH', I18n.t('option.file')) do |file|
+		options[:file] = file
+	end
+
+	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+		options[:group] = group
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+		options[:pattern] = pattern
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+opts = { search: options[:pattern],
+         group:  options[:group],
+       }
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
+cli.export(options[:file], opts)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index b92d5fb..daf8543 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -495,8 +495,31 @@ class Cli
 
 	# Export the items in a CSV file
 	# @args: file -> the destination file
-	def export(file)
-		@mpw.export(file)
+	#        options -> option to search
+	def export(file, options)
+		file  = 'export-mpw.yml' if file.to_s.empty?
+		items = @mpw.list(options)
+		data  = {}
+		i     = 1
+
+		items.each do |item|
+			data.merge!(i => { '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_code(item.id),
+			                   'comment'   => item.comment,
+			                   'last_edit' => item.last_edit,
+			                   'created'   => item.created,
+			                 }
+			            )
+
+			i += 1
+		end
+
+		File.open(file, 'w') {|f| f << data.to_yaml}
 
 		puts "#{I18n.t('export.export.valid', file)}".green
 	rescue Exception => e
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f1ae35e..10c57d0 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -274,34 +274,6 @@ class MPW
 		return nil
 	end
 
-	# Export to yaml
-	# @args: file -> file where you export the data
-	def export(file)
-		data = {}
-		i    = 1
-
-		@data.each do |item|
-			data.merge!(i => { 'group'     => item.group,
-			                   'host'      => item.host,
-			                   'protocol'  => item.protocol,
-			                   'user'      => item.user,
-			                   'password'  => get_password(item.id),
-			                   'port'      => item.port,
-			                   'comment'   => item.comment,
-			                   'otp_key'   => get_otp_code(item.id),
-			                   'last_edit' => item.last_edit,
-			                   'created'   => item.created,
-			                 }
-			            )
-
-			i += 1
-		end
-
-		File.open(file, 'w') {|f| f << data.to_yaml}
-	rescue Exception => e 
-		raise "#{I18n.t('error.export', file: file)}\n#{e}"
-	end
-
 	# Import to yaml
 	# @args: file -> path to file import
 	def import(file)

From 7c24853e5e1f43bc31c4ef85bea090138bb6a8c5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 19 Oct 2016 16:11:50 +0200
Subject: [PATCH 340/531] fix export otp_key

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index daf8543..8e95297 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -509,7 +509,7 @@ class Cli
 			                   'password'  => @mpw.get_password(item.id),
 			                   'protocol'  => item.protocol,
 			                   'port'      => item.port,
-			                   'otp_key'   => @mpw.get_otp_code(item.id),
+			                   'otp_key'   => @mpw.get_otp_key(item.id),
 			                   'comment'   => item.comment,
 			                   'last_edit' => item.last_edit,
 			                   'created'   => item.created,
@@ -521,7 +521,7 @@ class Cli
 
 		File.open(file, 'w') {|f| f << data.to_yaml}
 
-		puts "#{I18n.t('export.export.valid', file)}".green
+		puts "#{I18n.t('export.valid', file)}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #17: #{e}".red
 	end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 10c57d0..c2befb1 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -403,9 +403,23 @@ class MPW
 	# args: id -> the item id
 	#       key -> the new key
 	def set_otp_key(id, key)
-		@otp_keys[id] = encrypt(key)
+		if not key.to_s.empty?
+			@otp_keys[id] = encrypt(key.to_s)
+		end
 	end
 
+	# Get an opt key
+	# args: id -> the item id
+	#       key -> the new key
+	def get_otp_key(id)
+		if @otp_keys.has_key?(id)
+			return decrypt(@otp_keys[id])
+		else
+			return nil
+		end
+	end
+
+
 	# Get an otp code
 	# @args: id -> the item id
 	# @rtrn: an otp code

From 92168dd654dfc55b9ec5380f8d8a4fbb15e2c5f3 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 19 Oct 2016 16:12:13 +0200
Subject: [PATCH 341/531] new binary for import

---
 bin/mpw-import | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/mpw/cli.rb | 21 +++++++++++++++++-
 lib/mpw/mpw.rb | 22 -------------------
 3 files changed, 79 insertions(+), 23 deletions(-)
 create mode 100644 bin/mpw-import

diff --git a/bin/mpw-import b/bin/mpw-import
new file mode 100644
index 0000000..61b5d8c
--- /dev/null
+++ b/bin/mpw-import
@@ -0,0 +1,59 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
+
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-f', '--file PATH', I18n.t('option.file')) do |file|
+		options[:file] = file
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+
+opts = { search: options[:pattern],
+         group:  options[:group],
+       }
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
+cli.import(options[:file])
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 8e95297..8fc109b 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -529,7 +529,26 @@ class Cli
 	# Import items from a YAML file
 	# @args: file -> the import file
 	def import(file)
-		@mpw.import(file)
+		raise I18n.t('import.file_empty')     if file.to_s.empty?
+		raise I18n.t('import.file_not_exist') if not 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'],
+			               )
+
+			next if item.empty?
+
+			@mpw.add(item)
+			@mpw.set_password(item.id, row['password']) if not row['password'].to_s.empty?
+			@mpw.set_otp_key(item.id, row['otp_key'])   if not row['otp_key'].to_s.empty?
+		end
+
 		@mpw.write_data
 
 		puts "#{I18n.t('form.import.valid')}".green
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index c2befb1..a0c4c6e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -274,28 +274,6 @@ class MPW
 		return nil
 	end
 
-	# Import to yaml
-	# @args: file -> path to file import
-	def import(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'],
-			               )
-
-			raise 'Item is empty' if item.empty?
-
-			@data.push(item)
-			set_password(item.id, row['password']) if not row['password'].to_s.empty?
-			set_otp_code(item.id, row['otp_key'])  if not row['otp_key'].to_s.empty?
-		end
-	rescue Exception => e 
-		raise "#{I18n.t('error.import', file: file)}\n#{e}"
-	end
-
 	# Get last sync
 	def get_last_sync
 		return @config['sync']['last_sync'].to_i

From 38b5b5ada5fec453ddd9a9d2c99c7e6a9fc09442 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 08:32:26 +0200
Subject: [PATCH 342/531] fix translation options

---
 bin/mpw-add    |  6 +++---
 bin/mpw-config | 21 ++++++++++-----------
 bin/mpw-delete |  8 ++++----
 bin/mpw-export |  8 ++++----
 bin/mpw-genpwd |  2 +-
 bin/mpw-import |  6 +++---
 bin/mpw-list   |  8 ++++----
 bin/mpw-update | 10 +++++-----
 bin/mpw-wallet |  4 ++--
 i18n/en.yml    | 11 +++++++++--
 i18n/fr.yml    | 11 +++++++++--
 11 files changed, 54 insertions(+), 41 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index 52537c0..883f75d 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -27,9 +27,9 @@ require 'mpw/cli'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
 
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
@@ -38,7 +38,7 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/bin/mpw-config b/bin/mpw-config
index d29a93c..a215150 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -27,13 +27,13 @@ require 'mpw/cli'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw config [options]"
 
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on('-g', '--gpg-exe PATH', I18n.t('option.lang')) do |gpg_exe|
+	opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
 		options[:gpg_exe] = gpg_exe
 	end
 
@@ -42,11 +42,11 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('--init', I18n.t('option.init')) do
-		options[:init] = true
+	opts.on('-i', '--init GPG_KEY', I18n.t('option.init')) do |gpg_key|
+		options[:init] = gpg_key
 	end
 
-	opts.on('-k', '--key GPG_KEY', I18n.t('option.lang')) do |gpg_key|
+	opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
 		options[:gpg_key] = gpg_key
 	end
 
@@ -54,7 +54,7 @@ OptionParser.new do |opts|
 		options[:lang] = lang
 	end
 
-	opts.on('-w', '--wallet-dir LANG', I18n.t('option.lang')) do |wallet_dir|
+	opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
 		options[:wallet_dir] = wallet_dir
 	end
 end.parse!
@@ -64,9 +64,8 @@ cli    = MPW::Cli.new(config, nil, nil, nil)
 
 if not options[:init].nil?
 	cli.setup(options)
-	cli.setup_gpg_key(options[:gpg_key]) if not config.check_gpg_key?
+	cli.setup_gpg_key(options[:init])
 	cli.setup_wallet_config
-	exit 0
+else
+	cli.set_config(options)
 end
-
-cli.set_config(options)
diff --git a/bin/mpw-delete b/bin/mpw-delete
index a7e95e8..7974ff6 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -29,11 +29,11 @@ options = {}
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
 
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
 		options[:group] = group
 	end
 
@@ -42,11 +42,11 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		options[:pattern] = pattern
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/bin/mpw-export b/bin/mpw-export
index 700d067..15a9f9b 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -33,11 +33,11 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
-	opts.on('-f', '--file PATH', I18n.t('option.file')) do |file|
+	opts.on('-f', '--file PATH', I18n.t('option.file_export')) do |file|
 		options[:file] = file
 	end
 
-	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
 		options[:group] = group
 	end
 
@@ -46,11 +46,11 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		options[:pattern] = pattern
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd
index c99d002..8af4290 100644
--- a/bin/mpw-genpwd
+++ b/bin/mpw-genpwd
@@ -22,7 +22,7 @@ require 'mpw/mpw'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw-passwd [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw passwd [options]"
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
 		puts opts
diff --git a/bin/mpw-import b/bin/mpw-import
index 61b5d8c..335ebf4 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -27,13 +27,13 @@ require 'mpw/cli'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
 
 	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on('-f', '--file PATH', I18n.t('option.file')) do |file|
+	opts.on('-f', '--file PATH', I18n.t('option.file_import')) do |file|
 		options[:file] = file
 	end
 
@@ -42,7 +42,7 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/bin/mpw-list b/bin/mpw-list
index 9bde1ff..984a3b0 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -29,11 +29,11 @@ options = {}
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
 
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
 		options[:group] = group
 	end
 
@@ -42,11 +42,11 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		options[:pattern] = pattern
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/bin/mpw-update b/bin/mpw-update
index e521ffc..cab7cbf 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -27,13 +27,13 @@ require 'mpw/cli'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
+	opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
 
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
-	opts.on('-g', '--group GROUP', I18n.t('option.config')) do |group|
+	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
 		options[:group] = group
 	end
 
@@ -42,11 +42,11 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.config')) do |pattern|
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		options[:pattern] = pattern
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index c5fdc7a..7c52f1b 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -29,7 +29,7 @@ options = {}
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
 
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
 
@@ -38,7 +38,7 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
diff --git a/i18n/en.yml b/i18n/en.yml
index 7c7bcc5..b4d2b15 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -39,15 +39,21 @@ en:
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
     export: "Export a wallet in an yaml file"
-    file: "Specify a file, to use with the options [--import | --export | --add]"
+    file_export: "Specify the file where 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)"
+    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"
+    init: "Initialize mpw"
     import: "Import item since a yaml file"
-    key: "Specify the key name, to use with the options [--add | --delete | --update]"
+    key: "Specify the key name"
+    lang: "Set the software language"
     no_sync: "Disable synchronization with the server"
     numeric: "Use number to generate a password"
+    pattern: "Given search pattern"
     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"
@@ -55,6 +61,7 @@ en:
     show_all: "List all items"
     usage: "Usage"
     wallet: "Specify a wallet to use"
+    wallet_dir: "Set the wallets folder"
 
   form:
     select: "Select the item: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index a5d7aff..f7233af 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -39,15 +39,21 @@ fr:
     config: "Spécifie le fichier de configuration à utiliser"
     clipboard: "Désactive la fonction presse papier"
     export: "Exporte un portefeuille dans un fichier yaml"
-    file: "Spécifie un fichier, à utiliser avec les options [--import | --export | --add]"
+    file_export: "Spécifie le fichier où exporter les données"
+    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)"
+    gpg_exe: "Spécifie le chemin du binaire gpg à utiliser"
+    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"
     import: "Importe des éléments depuis un fichier yaml"
-    key: "Spécifie le nom d'une clé, à utiliser avec les options [--add | --delete | --update]"
+    init: "Initialise mpw"
+    key: "Spécifie le nom d'une clé"
+    lang: "Spécifie la langue du logiciel (ex: fr)"
     no_sync: "Désactive la synchronisation avec le serveur"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
+    pattern: "Motif de donnée à chercher"
     setup: "Création d'un nouveau fichier de configuration"
     setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
     special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
@@ -55,6 +61,7 @@ fr:
     show_all: "Liste tous les éléments"
     usage: "Utilisation"
     wallet: "Spécifie le portefeuille à utiliser"
+    wallet_dir: "Spécifie le répertoire des portefeuilles"
 
   form:
     select: "Sélectionner l'élément: "

From 1745585fb8aa17f61b297c5e1da63ad52ac83c40 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 08:51:59 +0200
Subject: [PATCH 343/531] remove unused contructor variables

---
 bin/mpw-add    | 2 +-
 bin/mpw-config | 2 +-
 bin/mpw-delete | 2 +-
 bin/mpw-export | 2 +-
 bin/mpw-import | 2 +-
 bin/mpw-list   | 2 +-
 bin/mpw-update | 2 +-
 bin/mpw-wallet | 2 +-
 lib/mpw/cli.rb | 6 +-----
 9 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index 883f75d..1e09e07 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -44,7 +44,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 cli.get_wallet(options[:wallet])
 cli.decrypt
diff --git a/bin/mpw-config b/bin/mpw-config
index a215150..a1b0743 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -60,7 +60,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, nil, nil)
+cli    = MPW::Cli.new(config, nil)
 
 if not options[:init].nil?
 	cli.setup(options)
diff --git a/bin/mpw-delete b/bin/mpw-delete
index 7974ff6..3dc0cd7 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -52,7 +52,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 opts = { search: options[:pattern],
          group:  options[:group],
diff --git a/bin/mpw-export b/bin/mpw-export
index 15a9f9b..4a174aa 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -56,7 +56,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 opts = { search: options[:pattern],
          group:  options[:group],
diff --git a/bin/mpw-import b/bin/mpw-import
index 335ebf4..8881441 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -48,7 +48,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 opts = { search: options[:pattern],
          group:  options[:group],
diff --git a/bin/mpw-list b/bin/mpw-list
index 984a3b0..2b503cf 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -52,7 +52,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 opts = { search: options[:pattern],
          group:  options[:group],
diff --git a/bin/mpw-update b/bin/mpw-update
index cab7cbf..7e6519d 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -52,7 +52,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 opts = { search: options[:pattern],
          group:  options[:group],
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 7c52f1b..790509f 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -44,6 +44,6 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil, options[:sync], nil)
+cli    = MPW::Cli.new(config, options[:sync])
 
 cli.list_wallet
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 8fc109b..e7764f2 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -32,13 +32,9 @@ class Cli
 	# Constructor
 	# @args: config -> the config
 	#        sync -> boolean for sync or not
-	#        clipboard -> enable clopboard
-	#        otp -> enable otp
-	def initialize(config, clipboard=true, sync=true, otp=false)
+	def initialize(config,sync=true)
 		@config    = config
-		@clipboard = clipboard
 		@sync      = sync
-		@otp       = otp
 	end
 
 	# Change a parameter int the config after init

From fdc1a6f3ec0115867bac58249210b19e8519f2c0 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 09:23:26 +0200
Subject: [PATCH 344/531] new binary for copy

---
 bin/mpw-copy   | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
 i18n/en.yml    |  3 +--
 i18n/fr.yml    |  3 +--
 lib/mpw/cli.rb | 26 ++++++++++++++-------
 4 files changed, 83 insertions(+), 12 deletions(-)
 create mode 100644 bin/mpw-copy

diff --git a/bin/mpw-copy b/bin/mpw-copy
new file mode 100644
index 0000000..c49d361
--- /dev/null
+++ b/bin/mpw-copy
@@ -0,0 +1,63 @@
+#!/usr/bin/ruby
+# MPW is a software to crypt and manage your passwords
+# Copyright (C) 2016  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 'optparse'
+require 'mpw/config'
+require 'mpw/cli'
+
+# --------------------------------------------------------- #
+# Options
+# --------------------------------------------------------- #
+
+options = {}
+
+OptionParser.new do |opts|
+	opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
+
+	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+		options[:config] = config
+	end
+
+	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
+		options[:group] = group
+	end
+
+	opts.on('-h', '--help', I18n.t('option.help')) do
+		puts opts
+		exit 0
+	end
+
+	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
+		options[:pattern] = pattern
+	end
+
+	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+		options[:wallet] = wallet
+	end
+end.parse!
+
+config = MPW::Config.new(options[:config])
+cli    = MPW::Cli.new(config, options[:sync])
+
+opts = { search: options[:pattern],
+         group:  options[:group],
+       }
+
+cli.get_wallet(options[:wallet])
+cli.decrypt
+cli.copy(opts)
diff --git a/i18n/en.yml b/i18n/en.yml
index b4d2b15..cf10200 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -89,8 +89,7 @@ en:
          login: "Press <l> to copy the login"
          password: "Press <p> to copy the password"
          otp_code: "Press <o> to copy the otp code"
-         update: "Press <u> to update the item"
-         delete: "Press <d> to delete the item"
+         quit: "Press <q> to quit"
     delete_key:
       valid: "Key has been deleted!"
     delete_item:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index f7233af..8e878a1 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -89,8 +89,7 @@ fr:
          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"
-         update: "Pressez <u> pour mettre à jour l'élément"
-         delete: "Pressez <d> pour supprimer l'élément"
+         quit: "Pressez <q> pour quitter"
     delete_key:
       valid: "La clé a bien été supprimée!"
     delete_item:
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index e7764f2..8bfa7bd 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -314,19 +314,12 @@ class Cli
 				Clipboard.copy(@mpw.get_otp_code(item.id))
 				puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
 
-			when 'd', 'delete'
-				break if delete(item)
-
-			when 'u', 'update', 'e', 'edit'
-				update(item)
-
 			else
 				puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
 				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.update')
-				puts I18n.t('form.clipboard.help.delete')
+				puts I18n.t('form.clipboard.help.quit')
 				next
 			end
 		end
@@ -489,6 +482,23 @@ class Cli
 		puts "#{I18n.t('display.error')} #16: #{e}".red
 	end
 
+	# Copy a password, otp, login
+	# @args: options -> the option to search
+	def copy(options={})
+		items = @mpw.list(options)
+		
+		if items.length == 0
+			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+		else
+			table(items)
+
+			item = get_item(items)
+			clipboard(item)
+		end
+	rescue Exception => e
+		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

From f5746d6af66fdd17e152cee4cb543da9080040ee Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 10:24:04 +0200
Subject: [PATCH 345/531] improve table

---
 lib/mpw/cli.rb | 77 +++++++++++++++++++++++++++-----------------------
 1 file changed, 42 insertions(+), 35 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 8bfa7bd..3779148 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -143,22 +143,26 @@ class Cli
 		group        = '.'
 		i            = 1
 		length_total = 10
-		length       = { id: 3,
-		                 host: 10,
-		                 user: 8,
-		                 comment: 15,
-		                 otp: 5,
+		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|
-			length[:host]    = item.host.length + 3    if item.host.to_s.length > length[:host]
-			length[:user]    = item.user.length + 3    if item.user.to_s.length > length[:user]
-			length[:comment] = item.comment.length + 3 if item.comment.to_s.length > length[:comment]
+			data.each do |k, v|
+				next if k == :id or k == :otp
+
+				v[:length] = item.send(k.to_s).length + 3 if item.send(k.to_s).to_s.length > v[:length]
+			end
 		end
-		length[:id]  = items.length.to_s.length + 2 if items.length.to_s.length > length[:id]
+		data[:id][:length]  = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
 		
-		length.each_value { |v| length_total += v }
-		items.sort!       { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+		data.each_value { |v| length_total += v[:length] }
+		items.sort!     { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
 
 		items.each do |item|
 			if group != item.group
@@ -173,36 +177,39 @@ class Cli
 				print ' '
 				length_total.times { print '=' }
 				print "\n "
-				print ' ID '
-				print '| Host'
-				(length[:host] - 'Host'.length).times { print ' ' }
-				print '| User'
-				(length[:user] - 'User'.length).times { print ' ' }
-				print '| OTP '
-				print '| Comment'
-				(length[:comment] - 'Comment'.length).times { print ' ' }
+				data.each do |k, v|
+					case k
+					when :id
+						print ' ID'
+					when :otp
+						print '| OTP'
+					else
+						print "| #{k.to_s.capitalize}"
+					end
+
+					(v[:length] - k.to_s.length).times { print ' ' }
+				end
 				print "\n "
 				length_total.times { print '=' }
 				print "\n"
 			end
 
-			print "  #{i}".cyan
-			(length[:id] - i.to_s.length).times { print ' ' }
-			print '| '
-			print "#{item.host}".yellow
-			(length[:host] - item.host.to_s.length).times { print ' ' }
-			print '| '
-			print "#{item.user}".green
-			(length[:user] - item.user.to_s.length).times { print ' ' }
-			print '| '
-			if item.otp
-				print ' X  '
-			else
-				4.times { print ' ' }
+			print "  #{i}".send(data[:id][:color])
+			(data[:id][:length] - i.to_s.length).times { print ' ' }
+			data.each do |k, v|
+				next if k == :id
+
+				if k == :otp
+					print '| '
+					if item.otp;  print ' X  ' else 4.times { print ' ' } end
+
+					next
+				end
+
+				print '| '
+				print "#{item.send(k.to_s)}".send(v[:color])
+				(v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
 			end
-			print '| '
-			print "#{item.comment}".magenta
-			(length[:comment] - item.comment.to_s.length).times { print ' ' }
 			print "\n"
 
 			i += 1

From 73312b933ffaf182a66a0176e1643099679f5208 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 13:49:17 +0200
Subject: [PATCH 346/531] copy: disable clipboard option

---
 bin/mpw-copy   |  7 ++++++-
 lib/mpw/cli.rb | 38 ++++++++++++++++++++++++++------------
 2 files changed, 32 insertions(+), 13 deletions(-)

diff --git a/bin/mpw-copy b/bin/mpw-copy
index c49d361..b4d770c 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+options[:clipboard] = true
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
@@ -33,6 +34,10 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
+	opts.on('-d', '--disable-clipboard', I18n.t('option.clipboard')) do
+		options[:clipboard] = false
+	end
+
 	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
 		options[:group] = group
 	end
@@ -60,4 +65,4 @@ opts = { search: options[:pattern],
 
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.copy(opts)
+cli.copy(options[:clipboard], opts)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 3779148..617b58d 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -287,7 +287,8 @@ class Cli
 
 	# Copy in clipboard the login and password
 	# @args: item -> the item
-	def clipboard(item)
+	#        clipboard -> enable clipboard
+	def clipboard(item, clipboard=true)
 		pid = nil
 
 		# Security: force quit after 90s
@@ -304,21 +305,33 @@ class Cli
 				break
 
 			when 'l', 'login'
-				Clipboard.copy(item.user)
-				puts I18n.t('form.clipboard.login').green
+				if clipboard
+					Clipboard.copy(item.user)
+					puts I18n.t('form.clipboard.login').green
+				else
+					puts item.user
+				end
 
 			when 'p', 'password'
-				Clipboard.copy(@mpw.get_password(item.id))
-				puts I18n.t('form.clipboard.password').yellow
+				if clipboard
+					Clipboard.copy(@mpw.get_password(item.id))
+					puts I18n.t('form.clipboard.password').yellow
 
-				Thread.new do
-					sleep 30
+					Thread.new do
+						sleep 30
 
-					Clipboard.clear
+						Clipboard.clear
+					end
+				else
+					puts @mpw.get_password(item.id)
 				end
 
 			when 'o', 'otp'
-				Clipboard.copy(@mpw.get_otp_code(item.id))
+				if clipboard
+					Clipboard.copy(@mpw.get_otp_code(item.id))
+				else
+					puts @mpw.get_otp_code(item.id)
+				end
 				puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
 
 			else
@@ -490,8 +503,9 @@ class Cli
 	end
 
 	# Copy a password, otp, login
-	# @args: options -> the option to search
-	def copy(options={})
+	# @args: clipboard -> enable clipboard
+	#        options -> the option to search
+	def copy(clipboard=true, options={})
 		items = @mpw.list(options)
 		
 		if items.length == 0
@@ -500,7 +514,7 @@ class Cli
 			table(items)
 
 			item = get_item(items)
-			clipboard(item)
+			clipboard(item, clipboard)
 		end
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #14: #{e}".red

From 2e32b223b0e97fa6b87204d56cb80252b2d06565 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 14:25:42 +0200
Subject: [PATCH 347/531] remove old function display

---
 lib/mpw/cli.rb | 38 --------------------------------------
 1 file changed, 38 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 617b58d..3420bc1 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -247,44 +247,6 @@ class Cli
 		end
 	end
 
-	# Display an item in the default format
-	# @args: item -> an array with the item information
-	def display_item(item)
-		puts '--------------------'.cyan
-		print 'Id: '.cyan
-		puts  item.id
-		print "#{I18n.t('display.name')}: ".cyan
-		puts  item.name
-		print "#{I18n.t('display.group')}: ".cyan
-		puts  item.group
-		print "#{I18n.t('display.server')}: ".cyan
-		puts  item.host
-		print "#{I18n.t('display.protocol')}: ".cyan
-		puts  item.protocol
-		print "#{I18n.t('display.login')}: ".cyan
-		puts  item.user
-
-		if @clipboard
-			print "#{I18n.t('display.password')}: ".cyan
-			puts '***********'
-		else
-			print "#{I18n.t('display.password')}: ".cyan
-			puts  @mpw.get_password(item.id)
-
-			if @mpw.get_otp_code(item.id) > 0
-				print "#{I18n.t('display.otp_code')}: ".cyan
-				puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
-			end
-		end
-
-		print "#{I18n.t('display.port')}: ".cyan
-		puts  item.port
-		print "#{I18n.t('display.comment')}: ".cyan
-		puts  item.comment
-
-		clipboard(item) if @clipboard
-	end
-
 	# Copy in clipboard the login and password
 	# @args: item -> the item
 	#        clipboard -> enable clipboard

From 580c5b419388d1cd7cdb169e4d050ecf8e8e036f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 14:31:25 +0200
Subject: [PATCH 348/531] add random password when create new item

---
 bin/mpw-add            |  6 +++++-
 lib/mpw/cli.rb         | 15 ++++++++++++---
 templates/add_form.erb |  4 ++--
 3 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index 1e09e07..542b884 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -38,6 +38,10 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-r', '--random', I18n.t('option.random_password')) do
+		options[:password] = true
+	end
+
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
@@ -48,4 +52,4 @@ cli    = MPW::Cli.new(config, options[:sync])
 
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.add
+cli.add(options[:password])
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 3420bc1..b52a63b 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -368,7 +368,11 @@ class Cli
 		puts "#{I18n.t('display.error')} #15: #{e}".red
 	end
 
-	def text_editor(template_name, item=nil)
+	# Text editor interface
+	# @args: template -> template name
+	#        item -> the item to edit
+	#        password -> disable field password
+	def text_editor(template_name, item=nil, password=false)
 		editor        = ENV['EDITOR'] || 'nano'
 		options       = {}
 		opts          = {}
@@ -397,10 +401,15 @@ class Cli
 	end
 
 	# Form to add a new item
-	def add
-		options = text_editor('add_form')	
+	# @args: password -> generate a random password
+	def add(password=false)
+		options = text_editor('add_form', nil, password)	
 		item    = Item.new(options)
 
+		if password
+			options[:password] = MPW::password(length: 24)
+		end
+		
 		@mpw.add(item)
 		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
 		@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
diff --git a/templates/add_form.erb b/templates/add_form.erb
index f4f8cae..c2d6d73 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -2,8 +2,8 @@
 host:           # <%= I18n.t('form.add_item.host') %>
 user:           # <%= I18n.t('form.add_item.login') %>
 group:          # <%= I18n.t('form.add_item.group') %> 
-protocol:       # <%= I18n.t('form.add_item.protocol') %>
-password:       # <%= I18n.t('form.add_item.password') %>
+protocol:       # <%= I18n.t('form.add_item.protocol') %><% if not password %>
+password:       # <%= I18n.t('form.add_item.password') %><% end %>
 port:           # <%= I18n.t('form.add_item.port') %>
 comment:        # <%= I18n.t('form.add_item.comment') %>
 otp_key:        # <%= I18n.t('form.add_item.otp_key') %>

From 337f1e2314e147319fef2fe03858c59e73e69af6 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 18:25:30 +0200
Subject: [PATCH 349/531] set wallet config

---
 bin/mpw-wallet | 37 ++++++++++++++++++++++++++++++++++++-
 i18n/en.yml    |  7 +++++++
 i18n/fr.yml    |  7 +++++++
 lib/mpw/cli.rb | 26 ++++++++------------------
 lib/mpw/mpw.rb | 29 +++++++++++++----------------
 5 files changed, 71 insertions(+), 35 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 790509f..0184d85 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -38,6 +39,34 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('--host NAME', I18n.t('option.host')) do |host|
+		values[:host] = host
+	end
+
+	opts.on('-l', '--list', I18n.t('option.list')) do |list|
+		options[:list] = true
+	end
+
+	opts.on('--password', I18n.t('option.password')) do
+		values[:password] = true
+	end
+
+	opts.on('--path PATH', I18n.t('option.path')) do |path|
+		values[:path] = path
+	end
+
+	opts.on('--port NUMBER', I18n.t('option.port')) do |port|
+		values[:port] = port
+	end
+
+	opts.on('--protocol NAME', I18n.t('option.protocol')) do |protocol|
+		values[:protocol] = protocol
+	end
+
+	opts.on('--user NAME', I18n.t('option.user')) do |user|
+		values[:user] = user
+	end
+
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
@@ -46,4 +75,10 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-cli.list_wallet
+if not options[:list].nil?
+	cli.list_wallet
+else
+	cli.get_wallet(options[:wallet])
+	cli.decrypt
+	cli.setup_wallet_config(values)
+end
diff --git a/i18n/en.yml b/i18n/en.yml
index cf10200..e88f360 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -47,19 +47,26 @@ 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 the server for the synchronization"
     init: "Initialize mpw"
     import: "Import item since a yaml file"
     key: "Specify the key name"
     lang: "Set the software language"
+    list: "List the wallets"
     no_sync: "Disable synchronization with the server"
     numeric: "Use number to generate a password"
+    password: "Change the password for the synchronization"
+    path: "Specify the remote path"
     pattern: "Given search pattern"
+    port: "Specify the connection port"
+    protocol: "Specify the protocol for the connection"
     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"
     usage: "Usage"
+    user: "Specify the user for the connection"
     wallet: "Specify a wallet to use"
     wallet_dir: "Set the wallets folder"
 
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 8e878a1..903cba6 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -47,19 +47,26 @@ 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 serveur pour la synchronisation"
     import: "Importe des éléments depuis un fichier yaml"
     init: "Initialise mpw"
     key: "Spécifie le nom d'une clé"
     lang: "Spécifie la langue du logiciel (ex: fr)"
+    list: "Liste les portefeuilles"
     no_sync: "Désactive la synchronisation avec le serveur"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
+    password: "Changer le mot de passe de connexion"
+    path: "Spécifie le chemin distant"
     pattern: "Motif de donnée à chercher"
+    port: "Spécifie le port de connexion"
+    protocol: "Spécifie le protocol utilisé pour la connexion"
     setup: "Création d'un nouveau fichier de configuration"
     setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
     special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
     show: "Recherche et affiche les éléments"
     show_all: "Liste tous les éléments"
     usage: "Utilisation"
+    user: "Spécifie l'identifiant de connection"
     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 b52a63b..464d098 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -95,27 +95,17 @@ class Cli
 	end
 
 	# Setup wallet config for sync
-	# @args: wallet -> the wallet name
-	def setup_wallet_config(wallet = nil)
-		#config         = {}
-		#config['sync'] = {}
+	# @args: options -> value to change
+	def setup_wallet_config(options={})
+		if not options[:password].nil?
+			options[:password] = ask(I18n.t('form.setup_wallet.password')) {|q| q.echo = false}
+		end
 
-		#puts '--------------------'
-		#config['sync']['type']     = ask(I18n.t('form.setup_wallet.sync_type')).to_s
+		#wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
 
-		#if ['ftp', 'ssh'].include?(config['sync']['type'].downcase)
-		#	config['sync']['host']     = ask(I18n.t('form.setup_wallet.sync_host')).to_s
-		#	config['sync']['port']     = ask(I18n.t('form.setup_wallet.sync_port')).to_s
-		#	config['sync']['user']     = ask(I18n.t('form.setup_wallet.sync_user')).to_s
-		#	config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
-		#	config['sync']['path']     = ask(I18n.t('form.setup_wallet.sync_path')).to_s
-		#end
-
-		wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
-
-		@mpw = MPW.new(@config.key, wallet_file, @password, @config.gpg_exe)
+		@mpw = MPW.new(@config.key, @wallet_file, @password, @config.gpg_exe)
 		@mpw.read_data
-		@mpw.set_config
+		@mpw.set_config(options)
 		@mpw.write_data
 
 		puts "#{I18n.t('form.setup_wallet.valid')}".green
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index a0c4c6e..0727636 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -210,19 +210,16 @@ class MPW
 
 	# Set config
 	# args: config -> a hash with config options
-	def set_config(config=nil)
-		@config         = {} if @config.nil?
-		@config['sync'] = {} if @config['sync'].nil?
+	def set_config(options={})
+		@config              = {} if @config.nil?
 
-		return if config.to_s.empty?
-
-		@config['sync']['type']      = config['sync']['type']
-		@config['sync']['host']      = config['sync']['host']
-		@config['sync']['port']      = config['sync']['port']
-		@config['sync']['user']      = config['sync']['user']
-		@config['sync']['password']  = config['sync']['password']
-		@config['sync']['path']      = config['sync']['path']
-		@config['sync']['last_sync'] = @config['sync']['last_sync'].nil? ? 0 : @config['sync']['last_sync']
+		@config['protocol']  = options[:protocol] if options.has_key?(:protocol)
+		@config['host']      = options[:host]     if options.has_key?(:host)
+		@config['port']      = options[:port]     if options.has_key?(:port)
+		@config['user']      = options[:user]     if options.has_key?(:user)
+		@config['password']  = options[:password] if options.has_key?(:password)
+		@config['path']      = options[:path]     if options.has_key?(:path)
+		@config['last_sync'] = @config['last_sync'].nil? ? 0 : @config['last_sync']
 	end
 
 	# Add a new item
@@ -276,7 +273,7 @@ class MPW
 
 	# Get last sync
 	def get_last_sync
-		return @config['sync']['last_sync'].to_i
+		return @config['last_sync'].to_i
 	rescue
 		return 0
 	end
@@ -284,12 +281,12 @@ class MPW
 	# Sync data with remote file
 	# @args: force -> force the sync
 	def sync(force=false)
-		return if @config.empty? or @config['sync']['type'].to_s.empty?
+		return if @config.empty? or @config['protocol'].to_s.empty?
 		return if get_last_sync + 300 > Time.now.to_i and not force
 
 		tmp_file  = "#{@wallet_file}.sync"
 		
-		case @config['sync']['type']
+		case @config['protocol']
 		when 'sftp', 'scp', 'ssh'
 			require "mpw/sync/ssh"
 			sync = SyncSSH.new(@config['sync'])
@@ -367,7 +364,7 @@ class MPW
 			item.set_last_sync
 		end
 
-		@config['sync']['last_sync'] = Time.now.to_i
+		@config['last_sync'] = Time.now.to_i
 
 		write_data
 		sync.update(@wallet_file)

From 7b301519b64213b9765c1e7e7bbefaeefc680e67 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 21:25:43 +0200
Subject: [PATCH 350/531] config: fix init with existing gpg key

---
 bin/mpw-config | 17 ++++++++++-------
 lib/mpw/cli.rb | 11 +++++------
 2 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index a1b0743..b0a1863 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw config [options]"
@@ -34,7 +35,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
-		options[:gpg_exe] = gpg_exe
+		values[:gpg_exe] = gpg_exe
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -43,19 +44,20 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-i', '--init GPG_KEY', I18n.t('option.init')) do |gpg_key|
-		options[:init] = gpg_key
+		options[:init]   = true
+		values[:gpg_key] = gpg_key
 	end
 
 	opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
-		options[:gpg_key] = gpg_key
+		values[:gpg_key] = gpg_key
 	end
 
 	opts.on('-l', '--lang LANG', I18n.t('option.lang')) do |lang|
-		options[:lang] = lang
+		values[:lang] = lang
 	end
 
 	opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
-		options[:wallet_dir] = wallet_dir
+		values[:wallet_dir] = wallet_dir
 	end
 end.parse!
 
@@ -63,9 +65,10 @@ config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil)
 
 if not options[:init].nil?
-	cli.setup(options)
+	cli.get_wallet
+	cli.setup(values)
 	cli.setup_gpg_key(options[:init])
 	cli.setup_wallet_config
 else
-	cli.set_config(options)
+	cli.set_config(values)
 end
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 464d098..b0161a1 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -32,7 +32,7 @@ class Cli
 	# Constructor
 	# @args: config -> the config
 	#        sync -> boolean for sync or not
-	def initialize(config,sync=true)
+	def initialize(config, sync=true)
 		@config    = config
 		@sync      = sync
 	end
@@ -54,7 +54,7 @@ class Cli
 	end
 
 	# Create a new config file
-	# @args: language -> the software language
+	# @args: options -> set param
 	def setup(options)
 		@config.is_valid?
 
@@ -75,6 +75,8 @@ class Cli
 	# Setup a new GPG key
 	# @args: gpg_key -> the key name
 	def setup_gpg_key(gpg_key)
+		return if @config.check_gpg_key?
+
 		password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
 		confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
 
@@ -320,10 +322,7 @@ class Cli
 		if wallet.to_s.empty?
 			wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
 
-			case wallets.length
-			when 0
-				puts I18n.t('display.nothing')
-			when 1
+			if wallets.length == 1
 				@wallet_file = wallets[0]
 			else
 				@wallet_file = "#{@config.wallet_dir}/default.mpw"

From e4c03619b380725e19fd0e2767450970b709ef1e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Oct 2016 21:25:58 +0200
Subject: [PATCH 351/531] minor fix

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

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index b504c2a..148ecf1 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -88,9 +88,9 @@ class Config
 	#        expire -> the time of expire to GPG key
 	# @rtrn: true if the GPG key is create, else false
 	def setup_gpg_key(password, name, length = 4096, expire = 0)
-		if name.nil? or name.empty?
+		if name.to_s.empty?
 			raise "#{I18n.t('error.config.genkey_gpg.name')}"
-		elsif password.nil? or password.empty?
+		elsif password.to_s.empty?
 			raise "#{I18n.t('error.config.genkey_gpg.password')}"
 		end
 

From 6d2a45217c8aeda5594065daaf1d36d6c1bb52dc Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 23 Oct 2016 21:54:46 +0200
Subject: [PATCH 352/531] fix load config

---
 bin/mpw-add       |  1 +
 bin/mpw-config    |  1 +
 bin/mpw-copy      |  1 +
 bin/mpw-delete    |  1 +
 bin/mpw-export    |  1 +
 bin/mpw-import    |  1 +
 bin/mpw-list      |  1 +
 bin/mpw-update    |  1 +
 bin/mpw-wallet    |  2 ++
 i18n/en.yml       |  2 +-
 i18n/fr.yml       |  2 +-
 lib/mpw/cli.rb    | 19 ++++++++++---------
 lib/mpw/config.rb | 27 ++++++++++++---------------
 13 files changed, 34 insertions(+), 26 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index 542b884..a0e450f 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -50,6 +50,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.add(options[:password])
diff --git a/bin/mpw-config b/bin/mpw-config
index b0a1863..133f3c4 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -65,6 +65,7 @@ config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil)
 
 if not options[:init].nil?
+	cli.load_config
 	cli.get_wallet
 	cli.setup(values)
 	cli.setup_gpg_key(options[:init])
diff --git a/bin/mpw-copy b/bin/mpw-copy
index b4d770c..e9ae66b 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -63,6 +63,7 @@ opts = { search: options[:pattern],
          group:  options[:group],
        }
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.copy(options[:clipboard], opts)
diff --git a/bin/mpw-delete b/bin/mpw-delete
index 3dc0cd7..e2d1195 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -58,6 +58,7 @@ opts = { search: options[:pattern],
          group:  options[:group],
        }
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.delete(opts)
diff --git a/bin/mpw-export b/bin/mpw-export
index 4a174aa..be15bb5 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -62,6 +62,7 @@ opts = { search: options[:pattern],
          group:  options[:group],
        }
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.export(options[:file], opts)
diff --git a/bin/mpw-import b/bin/mpw-import
index 8881441..ad52f57 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -54,6 +54,7 @@ opts = { search: options[:pattern],
          group:  options[:group],
        }
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.import(options[:file])
diff --git a/bin/mpw-list b/bin/mpw-list
index 2b503cf..1c729cc 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -58,6 +58,7 @@ opts = { search: options[:pattern],
          group:  options[:group],
        }
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.list(opts)
diff --git a/bin/mpw-update b/bin/mpw-update
index 7e6519d..cdd3af1 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -58,6 +58,7 @@ opts = { search: options[:pattern],
          group:  options[:group],
        }
 
+cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
 cli.update(opts)
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 0184d85..8e33426 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -75,6 +75,8 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
+cli.load_config
+
 if not options[:list].nil?
 	cli.list_wallet
 else
diff --git a/i18n/en.yml b/i18n/en.yml
index e88f360..9a964d2 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -3,7 +3,7 @@ en:
   error:
     config:
       write: "Can't write the config file!"
-      check: "Checkconfig failed!"
+      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}!"
       genkey_gpg:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 903cba6..d2cae5b 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -3,7 +3,7 @@ fr:
   error:
     config:
       write: "Impossible d'écrire le fichier de configuration!"
-      check: "Le fichier de configuration est invalide!"
+      load: "Le fichier de configuration est invalide!"
       key_bad_format: "La clé GPG est invalide!"
       no_key_public: "Vous ne possédez pas la clé publique de %{key}!"
       genkey_gpg:
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index b0161a1..b25cd18 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -40,8 +40,6 @@ class Cli
 	# Change a parameter int the config after init
 	# @args: options -> param to change
 	def set_config(options)
-		raise I18n.t('error.config.check') if not @config.is_valid?
-		
 		gpg_key    = options[:gpg_key]    || @config.key
 		lang       = options[:lang]       || @config.lang
 		wallet_dir = options[:wallet_dir] || @config.wallet_dir
@@ -56,15 +54,13 @@ class Cli
 	# Create a new config file
 	# @args: options -> set param
 	def setup(options)
-		@config.is_valid?
-
 		lang = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
 		I18n.locale = lang.to_sym
 
 		@config.setup(options[:gpg_key], lang, options[:wallet_dir], options[:gpg_exe])
 
-		raise I18n.t('error.config.check') if not @config.is_valid?
+		load_config
 
 		puts "#{I18n.t('form.setup_config.valid')}".green
 	rescue Exception => e
@@ -116,6 +112,15 @@ class Cli
 		exit 2
 	end
 	
+	# Load config
+	def load_config
+		@config.load_config
+
+	rescue Exception => e
+		puts "#{I18n.t('display.error')} #10: #{e}".red
+		exit 2
+	end
+
 	# Request the GPG password and decrypt the file
 	def decrypt
 		if not defined?(@mpw)
@@ -305,8 +310,6 @@ class Cli
 
 	# List all wallets
 	def list_wallet
-		@config.is_valid?
-
 		wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
 
 		wallets.each do |wallet|
@@ -317,8 +320,6 @@ class Cli
 	# Display the wallet
 	# @args: wallet -> the wallet name
 	def get_wallet(wallet=nil)
-		@config.is_valid?
-
 		if wallet.to_s.empty?
 			wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
 
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 148ecf1..2c1c496 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -65,11 +65,10 @@ class Config
 			wallet_dir = "#{@config_dir}/wallets"
 		end
 
-		config = {'config' => {'key'        => key,
-		                       'lang'       => lang,
-		                       'wallet_dir' => wallet_dir,
-		                       'gpg_exe'    => gpg_exe,
-		                      }
+		config = { 'key'        => key,
+		           'lang'       => lang,
+		           'wallet_dir' => wallet_dir,
+		           'gpg_exe'    => gpg_exe,
 		         }
 
 		FileUtils.mkdir_p(wallet_dir, mode: 0700)
@@ -113,22 +112,20 @@ class Config
 		raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
 	end
 
-	# Check the config file
-	# @rtrn: true if the config file is correct
-	def is_valid?
+	# Load the config file
+	def load_config
 		config      = YAML::load_file(@config_file)
-		@key        = config['config']['key']
-		@lang       = config['config']['lang']
-		@wallet_dir = config['config']['wallet_dir']
-		@gpg_exe    = config['config']['gpg_exe']
+		@key        = config['key']
+		@lang       = config['lang']
+		@wallet_dir = config['wallet_dir']
+		@gpg_exe    = config['gpg_exe']
 
 		raise if @key.empty? or @wallet_dir.empty?
 			
 		I18n.locale = @lang.to_sym
 
-		return true
-	rescue
-		return false
+	rescue Exception => e
+		raise "#{I18n.t('error.config.load')}\n#{e}"
 	end
 
 	# Check if private key exist

From f1f92d5658db11a79bf20b6fe091da13ad915980 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 23 Oct 2016 22:10:58 +0200
Subject: [PATCH 353/531] replace opts by value for options to search

---
 bin/mpw-delete | 11 ++++-------
 bin/mpw-export | 11 ++++-------
 bin/mpw-list   | 11 ++++-------
 bin/mpw-update | 11 ++++-------
 lib/mpw/mpw.rb |  2 +-
 5 files changed, 17 insertions(+), 29 deletions(-)

diff --git a/bin/mpw-delete b/bin/mpw-delete
index e2d1195..d0e76d5 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
@@ -34,7 +35,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		options[:group] = group
+		values[:group] = group
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -43,7 +44,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		options[:pattern] = pattern
+		values[:pattern] = pattern
 	end
 
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
@@ -54,11 +55,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-opts = { search: options[:pattern],
-         group:  options[:group],
-       }
-
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.delete(opts)
+cli.delete(values)
diff --git a/bin/mpw-export b/bin/mpw-export
index be15bb5..f5a2145 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -38,7 +39,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
-		options[:group] = group
+		values[:group] = group
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -47,7 +48,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		options[:pattern] = pattern
+		values[:pattern] = pattern
 	end
 
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
@@ -58,11 +59,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-opts = { search: options[:pattern],
-         group:  options[:group],
-       }
-
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.export(options[:file], opts)
+cli.export(options[:file], values)
diff --git a/bin/mpw-list b/bin/mpw-list
index 1c729cc..839e00f 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
@@ -34,7 +35,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		options[:group] = group
+		values[:group] = group
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -43,7 +44,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		options[:pattern] = pattern
+		values[:pattern] = pattern
 	end
 
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
@@ -54,11 +55,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-opts = { search: options[:pattern],
-         group:  options[:group],
-       }
-
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.list(opts)
+cli.list(values)
diff --git a/bin/mpw-update b/bin/mpw-update
index cdd3af1..598920b 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -25,6 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
@@ -34,7 +35,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		options[:group] = group
+		values[:group] = group
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -43,7 +44,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		options[:pattern] = pattern
+		values[:pattern] = pattern
 	end
 
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
@@ -54,11 +55,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-opts = { search: options[:pattern],
-         group:  options[:group],
-       }
-
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.update(opts)
+cli.update(values)
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 0727636..884f752 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -240,7 +240,7 @@ class MPW
 	def list(options={})
 		result = []
 
-		search = options[:search].to_s.downcase
+		search = options[:pattern].to_s.downcase
 		group  = options[:group].to_s.downcase
 
 		@data.each do |item|

From 90e8b658bf96dd4b7befa6f7f194005abf94b0eb Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 23 Oct 2016 22:36:50 +0200
Subject: [PATCH 354/531] replace opts by value for options to search

---
 bin/mpw-copy | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/bin/mpw-copy b/bin/mpw-copy
index e9ae66b..aa42c9a 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -26,6 +26,7 @@ require 'mpw/cli'
 
 options = {}
 options[:clipboard] = true
+values              = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
@@ -39,7 +40,7 @@ OptionParser.new do |opts|
 	end
 
 	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		options[:group] = group
+		values[:group] = group
 	end
 
 	opts.on('-h', '--help', I18n.t('option.help')) do
@@ -47,8 +48,12 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		options[:pattern] = pattern
+		values[:pattern] = pattern
 	end
 
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
@@ -59,11 +64,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-opts = { search: options[:pattern],
-         group:  options[:group],
-       }
-
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.copy(options[:clipboard], opts)
+cli.copy(options[:clipboard], values)

From 5bcf26d25193799ba8835006cb1ccae8cd31ebd6 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 23 Oct 2016 22:39:22 +0200
Subject: [PATCH 355/531] enable sync

---
 bin/mpw-add    |  7 ++++++-
 bin/mpw-config |  2 +-
 bin/mpw-copy   |  3 ++-
 bin/mpw-delete |  9 +++++++--
 bin/mpw-export |  9 +++++++--
 bin/mpw-import | 11 ++++++-----
 bin/mpw-list   |  9 +++++++--
 bin/mpw-update |  9 +++++++--
 bin/mpw-wallet |  9 +++++++--
 lib/mpw/mpw.rb |  4 ++--
 10 files changed, 52 insertions(+), 20 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index a0e450f..5322cc1 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -24,7 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
+options        = {}
+options[:sync] = true
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
@@ -38,6 +39,10 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-r', '--random', I18n.t('option.random_password')) do
 		options[:password] = true
 	end
diff --git a/bin/mpw-config b/bin/mpw-config
index 133f3c4..120da5b 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -65,9 +65,9 @@ config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil)
 
 if not options[:init].nil?
+	cli.setup(values)
 	cli.load_config
 	cli.get_wallet
-	cli.setup(values)
 	cli.setup_gpg_key(options[:init])
 	cli.setup_wallet_config
 else
diff --git a/bin/mpw-copy b/bin/mpw-copy
index aa42c9a..0a14ed7 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -24,7 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
+options             = {}
+options[:sync]      = true
 options[:clipboard] = true
 values              = {}
 
diff --git a/bin/mpw-delete b/bin/mpw-delete
index d0e76d5..2d79e6a 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -24,8 +24,9 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-values  = {}
+options        = {}
+options[:sync] = true
+values         = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
@@ -43,6 +44,10 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
diff --git a/bin/mpw-export b/bin/mpw-export
index f5a2145..f891873 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -24,8 +24,9 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-values  = {}
+options        = {}
+options[:sync] = true
+values         = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -47,6 +48,10 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
diff --git a/bin/mpw-import b/bin/mpw-import
index ad52f57..c740252 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -24,7 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
+options        = {}
+options[:sync] = true
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
@@ -42,6 +43,10 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
@@ -50,10 +55,6 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, options[:sync])
 
-opts = { search: options[:pattern],
-         group:  options[:group],
-       }
-
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
diff --git a/bin/mpw-list b/bin/mpw-list
index 839e00f..9d18b0d 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -24,8 +24,9 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-values  = {}
+options        = {}
+options[:sync] = true
+values         = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
@@ -47,6 +48,10 @@ OptionParser.new do |opts|
 		values[:pattern] = pattern
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
diff --git a/bin/mpw-update b/bin/mpw-update
index 598920b..301b876 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -24,8 +24,9 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-values  = {}
+options        = {}
+options[:sync] = true
+values         = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
@@ -43,6 +44,10 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 8e33426..b81e6b2 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -24,8 +24,9 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-values  = {}
+options        = {}
+options[:sync] = {}
+values         = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -47,6 +48,10 @@ OptionParser.new do |opts|
 		options[:list] = true
 	end
 
+	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
+		options[:sync] = false
+	end
+
 	opts.on('--password', I18n.t('option.password')) do
 		values[:password] = true
 	end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 884f752..0215a70 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -289,10 +289,10 @@ class MPW
 		case @config['protocol']
 		when 'sftp', 'scp', 'ssh'
 			require "mpw/sync/ssh"
-			sync = SyncSSH.new(@config['sync'])
+			sync = SyncSSH.new(@config)
 		when 'ftp'
 			require 'mpw/sync/ftp'
-			sync = SyncFTP.new(@config['sync'])
+			sync = SyncFTP.new(@config)
 		else
 			raise I18n.t('error.sync.unknown_type')
 		end

From fb64bfbbdf0397949febd0ef8335e6ca6d72751a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 23 Oct 2016 22:39:52 +0200
Subject: [PATCH 356/531] remove olw mpw binary

---
 bin/mpw-old | 214 ----------------------------------------------------
 1 file changed, 214 deletions(-)
 delete mode 100755 bin/mpw-old

diff --git a/bin/mpw-old b/bin/mpw-old
deleted file mode 100755
index 5f0d64d..0000000
--- a/bin/mpw-old
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/ruby
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  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.
-
-$: << File.expand_path('../../lib', __FILE__)
-
-require 'optparse'
-require 'locale'
-require 'set'
-require 'i18n'
-require 'mpw/mpw'
-require 'mpw/config'
-require 'mpw/cli'
-
-# --------------------------------------------------------- #
-# Set local
-# --------------------------------------------------------- #
-
-lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-
-if defined?(I18n.enforce_available_locales)
-	I18n.enforce_available_locales = true
-end
-
-I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
-I18n.default_locale = :en
-I18n.locale         = lang.to_sym
-
-# --------------------------------------------------------- #
-# Options
-# --------------------------------------------------------- #
-
-options_password    = {}
-options             = {}
-options[:force]     = false
-options[:otp]       = false
-options[:sync]      = true
-options[:clipboard] = true
-options[:group]     = nil
-options[:config]    = nil
-options[:wallet]    = nil
-
-OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
-
-	opts.on('-a', '--add', I18n.t('option.add')) do
-		options[:add] = true
-	end
-
-	opts.on('-A', '--show-all', I18n.t('option.show_all')) do
-		options[:type] = nil
-		options[:show] = ''
-	end
-
-	opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
-
-	opts.on('-C', '--no-clipboard', I18n.t('option.clipboard')) do
-		options[:clipboard] = false
-	end
-
-	opts.on('-e', '--export', I18n.t('option.export')) do
-		options[:export] = true
-	end
-
-	opts.on('-f', '--file FILE', I18n.t('option.file')) do |file|
-		options[:file] = file
-	end
-
-	opts.on('-F', '--force', I18n.t('option.force')) do
-		options[:force] = true
-	end
-
-	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
-		options[:group] = group
-	end
-
-	opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
-		options_password[:length] = length
-	end
-
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
-
-	opts.on('-I', '--import', I18n.t('option.import')) do
-		options[:import] = true
-	end
-
-	opts.on('-k', '--key KEY', I18n.t('option.key')) do |key|
-		options[:key] = key
-	end
-
-	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
-		options_password[:numeric] = true
-	end
-
-	opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
-	opts.on('-O', '--otp', I18n.t('option.otp')) do
-		options[:otp] = true
-	end
-
-	opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
-		search.nil? ? (options[:show]  = '')  : (options[:show] = search)
-	end
-
-	opts.on('-S', '--setup', I18n.t('option.setup')) do
-		options[:setup] = true
-	end
-
-	opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
-
-	opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
-		options[:setup_wallet] = true
-	end
-
-	opts.on('-x', '--special-chars', I18n.t('option.special_chars')) do
-		options_password[:special] = true
-	end
-
-	opts.on('-y', '--alpha', I18n.t('option.alpha')) do
-		options_password[:alpha] = true
-	end
-end.parse!
-
-# --------------------------------------------------------- #
-# Main
-# --------------------------------------------------------- #
-
-# Generate password
-if not options_password.empty?
-	puts MPW::MPW::password(options_password)
-	exit 0
-end
-
-begin
-	config = MPW::Config.new(options[:config])
-	cli    = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
-		
-	# Setup a new config 
-	if not options[:setup].nil?
-		cli.setup(lang)
-		exit 0
-	end
-
-	cli.setup(lang)   if not config.is_valid?
-	cli.setup_gpg_key if not config.check_gpg_key?
-	
-	cli.get_wallet(options[:wallet])
-	cli.decrypt
-	
-	# Display the item's informations
-	if not options[:show].nil?
-		opts = {search:    options[:show],
-		        group:     options[:group],
-		       }
-	
-		cli.display(opts)
-	
-	# Add a new item
-	elsif not options[:add].nil? and options[:key].nil?
-		cli.add
-	
-	# Add a new public key in wallet
-	elsif not options[:add].nil? and not options[:key].nil?
-		cli.add_key(options[:key], options[:file])
-	
-	# Delete a public key in wallet
-	elsif not options[:delete].nil? and not options[:key].nil?
-		cli.delete_key(options[:key])
-	
-	# Export
-	elsif not options[:export].nil? and not options[:file].nil?
-		cli.export(options[:file])
-	
-	# Import
-	elsif not options[:import].nil? and not options[:file].nil?
-		cli.import(options[:file])
-	
-	# Setup wallet config
-	elsif not options[:setup_wallet].nil?
-		cli.setup_wallet_config
-	
-	end
-	
-	cli = nil
-	
-	exit 0
-
-rescue SystemExit, Interrupt
-	exit 3
-end

From c25b31201375287a38ca7db2d9719ed64ecec0f3 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 23 Oct 2016 22:54:36 +0200
Subject: [PATCH 357/531] update version file: 4.0.0-beta

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

diff --git a/VERSION b/VERSION
index 944880f..c0de572 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.2.0
+4.0.0-beta

From 94f6e4e61eea74671271153b6d13c2bcd429f56e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 25 Oct 2016 23:17:04 +0200
Subject: [PATCH 358/531] fix bug when gpg path is empty

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 0215a70..533077e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -33,7 +33,7 @@ class MPW
 		@gpg_exe     = gpg_exe
 		@wallet_file = wallet_file
 
-		if @gpg_exe
+		if not @gpg_exe.to_s.empty?
 			GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
 		end
 	end

From f775146ffd3aef0c705e15fb50f70fff79ff0bc2 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 5 Nov 2016 10:13:33 +0100
Subject: [PATCH 359/531] new readme

---
 README.md  | 128 -----------------------------------------------------
 README.rst |  53 ++++++++++++++++++++++
 2 files changed, 53 insertions(+), 128 deletions(-)
 delete mode 100644 README.md
 create mode 100644 README.rst

diff --git a/README.md b/README.md
deleted file mode 100644
index e3ca84c..0000000
--- a/README.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Manage your passwords!
-
-MPW is a little software which stores your passwords in an GPG encrypted file.
-MPW can synchronize your password with SSH or FTP.
-
-# Installation
-
-This program work with ruby >= 2.0
-
-* install ruby and rubygems on your computer
-* install xclip
-* gem install mpw
-
-# How to use
-
-* Show help
-```
-mpw --help
-```
-
-* Setup a new config file
-```
-mpw --setup
-mpw --setup --config /path/conf/file.cfg
-```
-
-* Create and setup a new wallet
-```
-mpw --setup-wallet --wallet new_wallet_name
-mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg 
-```
-
-* Add a GPG key in wallet
-```
-mpw --add --key root@localhost.local
-mpw --add --key root@localhost.local --config /path/conf/file.cfg 
-mpw --add --key root@localhost.local --wallet wallet_name
-mpw --add --key root@localhost.local --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Add a new  GPG key in wallet
-```
-mpw --add --key root@localhost.local --file /path/gpg/file.pub
-mpw --add --key root@localhost.local --file /path/gpg/file.pub --config /path/conf/file.cfg 
-mpw --add --key root@localhost.local --file /path/gpg/file.pub --wallet wallet_name
-mpw --add --key root@localhost.local --file /path/gpg/file.pub --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Delete a GPG key in wallet
-```
-mpw --delete --key root@localhost.local
-mpw --delete --key root@localhost.local --wallet wallet_name
-mpw --delete --key root@localhost.local --wallet wallet_name --config /path/conf/file.cfg 
-```
-
-* Add a new item in wallet
-```
-mpw --add 
-mpw --add --config /path/conf/file.cfg
-mpw --add --wallet wallet_name
-mpw --add --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Update an item
-```
-mpw --update --id uniq_id
-mpw --update --id uniq_id --config /path/conf/file.cfg
-mpw --update --id uniq_id --wallet wallet_name
-mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Delete an item
-```
-mpw --delete --id uniq_id
-mpw --delete --id uniq_id --config /path/conf/file.cfg
-mpw --delete --id uniq_id --wallet wallet_name
-mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Show an item
-```
-mpw --show 'string to search'
-mpw --show 'string to search' --config /path/conf/file.cfg
-mpw --show 'string to search' --wallet wallet_name
-mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
-mpw --show 'string to search' --group group_name
-mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
-mpw --show 'string to search' --group group_name --wallet wallet_name
-mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Export data in YAML file
-```
-mpw --export --file /path/file/to/export.yml
-mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
-mpw --export --file /path/file/to/export.yml --wallet wallet_name
-mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
-```
-
-* Import data from YAML file
-```
-mpw --import --file /path/file/to/export.yml
-mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
-mpw --import --file /path/file/to/export.yml --wallet wallet_name
-mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
-```
-
-Format file to import:
-```
-1:
-  name: Website perso
-  group: Perso
-  host: localhost.local
-  protocol: ftp
-  user: test
-  password: letoortue
-  port: 21
-  comment: Mysuper website
-2:
-  name: Linuxfr
-  group: Pro
-  host: Linuxfr.org
-  protocol: https
-  user: test
-  password: coucou 
-  port: 
-  comment: 
-```
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..a62c1e2
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,53 @@
+MPW: Manage your passwords!
+*******************************************************
+
+mpw is a little software which stores your passwords in `GnuPG <http://www.gnupg.org/>` encrypted files.
+
+Features
+========
+
+* generate OTP code
+* synchronize your passwords with SSH or FTP.
+* copy your login, password or otp in clipboard
+
+Install
+=======
+
+On debian or ubuntu::
+
+	apt install ruby ruby-dev xclip
+	gem install mpw
+
+
+How to use
+==========
+
+A simple mpw usage::
+
+	mpw config --init user@host.com
+	mpw add
+	mpw copy
+	mpw add
+	mpw list
+
+Output::
+
+	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
+
+
+Licence |License|
+=================
+
+Full license here: `LICENSE <https://github.com/nishiki/manage-password/blob/master/LICENSE>`
+
+.. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg

From 56b01bcc8633abbad6f55019a521bbf880e14b34 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 5 Nov 2016 10:14:08 +0100
Subject: [PATCH 360/531] minor fix in table format

---
 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 b25cd18..722b16d 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -141,7 +141,7 @@ class Cli
 		i            = 1
 		length_total = 10
 		data         = { id:       { length: 3,  color: 'cyan' },
-		                 host:     { length: 9, color: 'yellow' },
+		                 host:     { length: 9,  color: 'yellow' },
 		                 user:     { length: 7,  color: 'green' },
 		                 protocol: { length: 9,  color: 'white' },
 		                 port:     { length: 5,  color: 'white' },
@@ -153,7 +153,7 @@ class Cli
 			data.each do |k, v|
 				next if k == :id or k == :otp
 
-				v[:length] = item.send(k.to_s).length + 3 if item.send(k.to_s).to_s.length > v[:length]
+				v[:length] = item.send(k.to_s).length + 3 if item.send(k.to_s).to_s.length >= v[:length]
 			end
 		end
 		data[:id][:length]  = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]

From 167123d7111db4029aba4856905484125756abaf Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 5 Nov 2016 10:27:19 +0100
Subject: [PATCH 361/531] update changelog

---
 CHANGELOG | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/CHANGELOG b/CHANGELOG
index 1ce4809..261d083 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,34 @@
 = CHANGELOG =
+== v4.0.0 (beta) ==
+
+* new interface with a table
+* new command line interface
+* use text editor for add or update an item
+* several bugs fix
+
+== v3.2.1 ==
+
+* fix bug when add a new item
+
+== v3.2.0 ==
+
+* add support OTP
+* fix bug in synchronize
+* improve interface
+
+== v3.1.0 ==
+
+* add clipboard
+* can change gpg version
+* minor change in interface
+* several bugs fix
+
+== v3.0.0 ==
+
+* new storage format
+* new share system
+* remove MPW server
+
 == v2.0.0 ==
 
 * change format csv to yaml

From 3d48ff95b90470accdc04b35cd675c24a8ae29ed Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 5 Nov 2016 10:50:10 +0100
Subject: [PATCH 362/531] fix dependency version

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

diff --git a/Gemfile b/Gemfile
index 557dec5..a588d17 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,10 +1,10 @@
 source 'https://rubygems.org'
-gem 'highline'
-gem 'i18n'
-gem 'locale'
-gem 'gpgme'
-gem 'colorize'
-gem 'net-ssh'
-gem 'net-scp'
-gem 'clipboard'
-gem 'rotp'
+gem "i18n",      "~> 0.7", ">= 0.7.0"
+gem "gpgme",     "~> 2.0", ">= 2.0.12"
+gem "highline",  "~> 1.7", ">= 1.7.8"
+gem "locale",    "~> 2.1", ">= 2.1.2"
+gem "colorize",  "~> 0.8", ">= 0.8.1"
+gem "net-ssh",   "~> 3.2", ">= 3.2.0"
+gem "net-sftp",  "~> 2.1", ">= 2.1.2"
+gem "clipboard", "~> 1.1", ">= 1.1.1"
+gem "rotp",      "~> 3.1", ">= 3.1.0"
diff --git a/mpw.gemspec b/mpw.gemspec
index 09fe6d9..80d9344 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -17,13 +17,13 @@ Gem::Specification.new do |spec|
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ['lib']
 
-  spec.add_dependency "i18n"
-  spec.add_dependency "gpgme"
-  spec.add_dependency "highline"
-  spec.add_dependency "locale"
-  spec.add_dependency "colorize"
-  spec.add_dependency "net-ssh"
-  spec.add_dependency "net-sftp"
-  spec.add_dependency "clipboard"
-  spec.add_dependency "rotp"
+  spec.add_dependency "i18n",      "~> 0.7", ">= 0.7.0"
+  spec.add_dependency "gpgme",     "~> 2.0", ">= 2.0.12"
+  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 "net-ssh",   "~> 3.2", ">= 3.2.0"
+  spec.add_dependency "net-sftp",  "~> 2.1", ">= 2.1.2"
+  spec.add_dependency "clipboard", "~> 1.1", ">= 1.1.1"
+  spec.add_dependency "rotp",      "~> 3.1", ">= 3.1.0"
 end

From 0e86be546150b48ca60d8a6a5179a061630e4d31 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 5 Nov 2016 15:34:11 +0100
Subject: [PATCH 363/531] add help command

---
 bin/mpw     | 15 +++++++++++++++
 i18n/en.yml | 12 ++++++++++++
 i18n/fr.yml | 12 ++++++++++++
 3 files changed, 39 insertions(+)

diff --git a/bin/mpw b/bin/mpw
index 2d32700..8e578f8 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -46,4 +46,19 @@ command = "#{bin_dir}/mpw-#{ARGV[0]}"
 
 if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
 	Kernel.load(command)
+else
+	puts "#{I18n.t('option.usage')}: mpw COMMAND [options]\n\n"
+	puts 'Commands:'
+	puts "  add       #{I18n.t('command.add')}"
+	puts "  config    #{I18n.t('command.config')}"
+	puts "  copy      #{I18n.t('command.copy')}"
+	puts "  delete    #{I18n.t('command.delete')}"
+	puts "  export    #{I18n.t('command.export')}"
+	puts "  genpwd    #{I18n.t('command.genpwd')}"
+	puts "  import    #{I18n.t('command.import')}"
+	puts "  list      #{I18n.t('command.list')}"
+	puts "  update    #{I18n.t('command.update')}"
+	puts "  wallet    #{I18n.t('command.wallet')}"
+
+	exit 3
 end
diff --git a/i18n/en.yml b/i18n/en.yml
index 9a964d2..93955d5 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -33,6 +33,18 @@ en:
   warning:
     select: 'Your choice is not a valid element!'
 
+  command:
+    add: "Add a new item"
+    config: "Manage the general config"
+    copy: "Copy a login, password or OTP code"
+    delete: "Delete an item"
+    export: "Export the data in plain text"
+    genpwd: "Generate a password"
+    import: "Import data from a file"
+    list: "Print the items"
+    update: "Update an item"
+    wallet: "Manage the wallet config"
+
   option:
     add: "Add an item or key"
     alpha: "Use letter to generate a password"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index d2cae5b..4f8b7ac 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -33,6 +33,18 @@ fr:
   warning:
     select: "Votre choix n'est pas un élément valide!"
 
+  command:
+    add: "Ajoute un nouvel élément"
+    config: "Gère la configuration générale"
+    copy: "Copie un identifiant,  un mot de passe ou un code OTP"
+    delete: "Supprimer un élément d'un portefeuille"
+    export: "Exporte les données"
+    genpwd: "Génére un mot de passe"
+    import: "Importe des données"
+    list: "Liste les éléments d'un portefeuille"
+    update: "Met à jour un élément"
+    wallet: "Gére la configuration d'un portefeuille"
+
   option:
     add: "Ajoute un élément ou une clé"
     alpha: "Utilise des lettres dans la génération d'un mot de passe"

From 0c93fd9eeaee4fad2ed0042d056cb1cc7f0f3056 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 6 Nov 2016 10:30:37 +0100
Subject: [PATCH 364/531] fix create config folder

---
 lib/mpw/config.rb | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 2c1c496..e8911fa 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -71,7 +71,9 @@ class Config
 		           'gpg_exe'    => gpg_exe,
 		         }
 
-		FileUtils.mkdir_p(wallet_dir, mode: 0700)
+		FileUtils.mkdir_p(@config_dir, mode: 0700)
+		FileUtils.mkdir_p(wallet_dir,  mode: 0700)
+
 		File.open(@config_file, 'w') do |file|
 			file << config.to_yaml
 		end

From 59e850cfeb4c7bcd36ccbbc08d665524d34c16bf Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 10 Nov 2016 23:57:16 +0100
Subject: [PATCH 365/531] add test with travis

---
 .travis.yml                |  13 +++
 README.rst                 |   7 +-
 test/files/fixtures.yml    |   4 -
 test/files/test_import.csv |   3 -
 test/files/test_import.yml |  23 ----
 test/test.sh               |   1 +
 test/test2.rb              |  11 ++
 test/test_config.rb        |  40 +++++++
 test/test_item.rb          | 130 +++++++--------------
 test/test_mpw.rb           | 226 +++++++++++++------------------------
 test/tests.rb              |   3 +-
 11 files changed, 192 insertions(+), 269 deletions(-)
 create mode 100644 .travis.yml
 delete mode 100644 test/files/test_import.csv
 delete mode 100644 test/files/test_import.yml
 create mode 100644 test/test.sh
 create mode 100644 test/test2.rb
 create mode 100644 test/test_config.rb

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..141eab1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: ruby
+rvm:
+  - 2.3.1
+  - 2.2.5
+  - 2.1.10
+install:
+  - bundle install
+  - gem install 'test-unit'
+  - echo 9999 > VERSION
+  - gem build mpw.gemspec
+  - gem install mpw-9999.gem
+script:
+  - ruby ./test/tests.rb 
diff --git a/README.rst b/README.rst
index a62c1e2..432b4d8 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,8 @@
 MPW: Manage your passwords!
 *******************************************************
+|Build Status| |License|
 
-mpw is a little software which stores your passwords in `GnuPG <http://www.gnupg.org/>` encrypted files.
+mpw is a little software which stores your passwords in `GnuPG <http://www.gnupg.org/>`_ encrypted files.
 
 Features
 ========
@@ -48,6 +49,8 @@ Output::
 Licence |License|
 =================
 
-Full license here: `LICENSE <https://github.com/nishiki/manage-password/blob/master/LICENSE>`
+Full license here: `LICENSE <https://github.com/nishiki/manage-password/blob/master/LICENSE>`_
 
 .. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg
+.. |Build Status| image:: https://travis-ci.org/nishiki/manage-password.svg?branch=master
+   :target: https://travis-ci.org/nishiki/manage-password
diff --git a/test/files/fixtures.yml b/test/files/fixtures.yml
index 73eacf7..5651911 100644
--- a/test/files/fixtures.yml
+++ b/test/files/fixtures.yml
@@ -1,5 +1,4 @@
 add_new:
-  name:	'test_name'
   group: 'test_group'
   host: 'test_host'
   protocol: 'test_protocol'
@@ -10,7 +9,6 @@ add_new:
 
 add_existing:
   id: 'TEST-ID-XXXXX'
-  name:	'test_name_existing'
   group: 'test_group_existing'
   host: 'test_host_existing'
   protocol: 'test_protocol_existing'
@@ -21,7 +19,6 @@ add_existing:
   created: 1386752948
 
 update:
-  name:	'test_name_update'
   group: 'test_group_update'
   host: 'test_host_update'
   protocol: 'test_protocol_update'
@@ -29,4 +26,3 @@ update:
   password: 'test_password_update'
   port: '43'
   comment: 'test_comment_update'
-
diff --git a/test/files/test_import.csv b/test/files/test_import.csv
deleted file mode 100644
index 74c1844..0000000
--- a/test/files/test_import.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-name,group,protocol,host,user,password,port,comment
-test_name,test_group,test_protocol,test_host,test_user,test_password,42,test_comment
-test_name_update,test_group_update,test_protocol_update,test_host_update,test_user_update,test_password_update,43,test_comment_update
diff --git a/test/files/test_import.yml b/test/files/test_import.yml
deleted file mode 100644
index 889a264..0000000
--- a/test/files/test_import.yml
+++ /dev/null
@@ -1,23 +0,0 @@
----
-XWas7vpy0HerhOYd:
-  id: XWas7vpy0HerhOYd
-  name: test_name
-  group: test_group
-  host: test_host
-  protocol: test_protocol
-  user: test_user
-  password: test_password
-  port: 42
-  comment: test_comment
-  date: 1419858983
-D7URyJENLa91jt0b:
-  id: D7URyJENLa91jt0b
-  name: test_name_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
-  date: 1419858983
diff --git a/test/test.sh b/test/test.sh
new file mode 100644
index 0000000..ab37885
--- /dev/null
+++ b/test/test.sh
@@ -0,0 +1 @@
+echo "test\ntest\n" | ruby ./bin/mpw config --init test@test.com
diff --git a/test/test2.rb b/test/test2.rb
new file mode 100644
index 0000000..17af2da
--- /dev/null
+++ b/test/test2.rb
@@ -0,0 +1,11 @@
+require 'open3'
+
+Open3.popen3("./bin/mpw config --init test@test.com") do |stdin, stdout, stderr, thread|
+	stdin.puts 'test'
+	stdin.puts 'test'
+end
+
+Open3.popen3("./bin/mpw list") do |stdin, stdout, stderr, thread|
+	stdin.puts 'test'
+	puts stdout
+end
diff --git a/test/test_config.rb b/test/test_config.rb
new file mode 100644
index 0000000..3de438c
--- /dev/null
+++ b/test/test_config.rb
@@ -0,0 +1,40 @@
+#!/usr/bin/ruby
+
+require 'mpw/config'
+require 'test/unit'
+require 'locale'
+require 'i18n'
+
+class TestConfig < Test::Unit::TestCase
+	def setup
+		lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+		
+		if defined?(I18n.enforce_available_locales)
+			I18n.enforce_available_locales = true
+		end
+		
+		I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+		I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
+		I18n.default_locale = :en
+		I18n.locale         = lang.to_sym
+	end
+
+	def test_00_config
+		data = { key: 'test@example.com',
+		         lang: 'en',
+		         wallet_dir: '/tmp/test',
+		         gpg_exe: '',
+		       }
+
+		@config = MPW::Config.new
+		@config.setup(data[:key], data[:lang], data[:wallet_dir], data[:gpg_exe])
+		@config.load_config
+
+		data.each do |k,v|
+			assert_equal(v, @config.send(k))
+		end
+
+		@config.setup_gpg_key('password', 'test@example.com', 2048)
+		assert(@config.check_gpg_key?)
+	end
+end
diff --git a/test/test_item.rb b/test/test_item.rb
index 923d7af..b9f5961 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -1,19 +1,19 @@
 #!/usr/bin/ruby
  
-require_relative '../lib/Item'
+require 'mpw/item'
 require 'test/unit'
 require 'yaml'
  
 class TestItem < Test::Unit::TestCase
 	def setup
-		@fixture_file = 'files/fixtures.yml'
+		@fixture_file = 'test/files/fixtures.yml'
 		@fixtures = YAML.load_file(@fixture_file)
 		
 		if defined?(I18n.enforce_available_locales)
 			I18n.enforce_available_locales = false
 		end
 
-		I18n.load_path      = Dir['../i18n/cli/*.yml']
+		I18n.load_path      = Dir['./i18n/cli/*.yml']
 		I18n.default_locale = :en
 
 
@@ -25,14 +25,12 @@ class TestItem < Test::Unit::TestCase
 	end
 
 	def test_01_add_new
-		data = {name:     @fixtures['add_new']['name'],
-		        group:    @fixtures['add_new']['group'],
-		        host:     @fixtures['add_new']['host'],
-		        protocol: @fixtures['add_new']['protocol'],
-		        user:     @fixtures['add_new']['user'],
-		        password: @fixtures['add_new']['password'],
-		        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)
@@ -40,27 +38,23 @@ class TestItem < Test::Unit::TestCase
 		assert(!item.nil?)
 		assert(!item.empty?)
 
-		assert_equal(@fixtures['add_new']['name'],      item.name)
 		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']['password'],  item.password)
 		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
 		assert_equal(@fixtures['add_new']['comment'],   item.comment)
 	end
 
 	def test_02_add_existing
-		data = {id:       @fixtures['add_existing']['id'],
-		        name:     @fixtures['add_existing']['name'],
-		        group:    @fixtures['add_existing']['group'],
-		        host:     @fixtures['add_existing']['host'],
-		        protocol: @fixtures['add_existing']['protocol'],
-		        user:     @fixtures['add_existing']['user'],
-		        password: @fixtures['add_existing']['password'],
-		        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)
@@ -69,26 +63,22 @@ class TestItem < Test::Unit::TestCase
 		assert(!item.empty?)
 
 		assert_equal(@fixtures['add_existing']['id'],        item.id)
-		assert_equal(@fixtures['add_existing']['name'],      item.name)
 		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']['password'],  item.password)
 		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)
 	end
 
 	def test_03_update
-		data = {name:     @fixtures['add_new']['name'],
-		        group:    @fixtures['add_new']['group'],
-		        host:     @fixtures['add_new']['host'],
-		        protocol: @fixtures['add_new']['protocol'],
-		        user:     @fixtures['add_new']['user'],
-		        password: @fixtures['add_new']['password'],
-		        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)
@@ -99,14 +89,12 @@ class TestItem < Test::Unit::TestCase
 		created   = item.created
 		last_edit = item.last_edit
 
-		data = {name:     @fixtures['update']['name'],
-		        group:    @fixtures['update']['group'],
-		        host:     @fixtures['update']['host'],
-		        protocol: @fixtures['update']['protocol'],
-		        user:     @fixtures['update']['user'],
-		        password: @fixtures['update']['password'],
-		        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)
@@ -114,12 +102,10 @@ class TestItem < Test::Unit::TestCase
 
 		assert(!item.empty?)
 
-		assert_equal(@fixtures['update']['name'],      item.name)
 		assert_equal(@fixtures['update']['group'],     item.group)
 		assert_equal(@fixtures['update']['host'],      item.host)
 		assert_equal(@fixtures['update']['protocol'],  item.protocol)
 		assert_equal(@fixtures['update']['user'],      item.user)
-		assert_equal(@fixtures['update']['password'],  item.password)
 		assert_equal(@fixtures['update']['port'].to_i, item.port)
 		assert_equal(@fixtures['update']['comment'],   item.comment)
 
@@ -127,39 +113,13 @@ class TestItem < Test::Unit::TestCase
 		assert_not_equal(last_edit, item.last_edit)
 	end
 
-	def test_04_update_with_empty_name
-		data = {name:     @fixtures['add_new']['name'],
-		        group:    @fixtures['add_new']['group'],
-		        host:     @fixtures['add_new']['host'],
-		        protocol: @fixtures['add_new']['protocol'],
-		        user:     @fixtures['add_new']['user'],
-		        password: @fixtures['add_new']['password'],
-		        port:     @fixtures['add_new']['port'],
-		        comment:  @fixtures['add_new']['comment'],
-		       }
-		
-		item = MPW::Item.new(data)
-
-		assert(!item.nil?)
-		assert(!item.empty?)
-
-		last_edit = item.last_edit
-
-		sleep(1)
-		assert(!item.update({name: ''}))
-
-		assert_equal(last_edit, item.last_edit)
-	end
-
 	def test_05_update_one_element
-		data = {name:     @fixtures['add_new']['name'],
-		        group:    @fixtures['add_new']['group'],
-		        host:     @fixtures['add_new']['host'],
-		        protocol: @fixtures['add_new']['protocol'],
-		        user:     @fixtures['add_new']['user'],
-		        password: @fixtures['add_new']['password'],
-		        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)
@@ -172,12 +132,10 @@ class TestItem < Test::Unit::TestCase
 		sleep(1)
 		assert(item.update({comment: @fixtures['update']['comment']}))
 
-		assert_equal(@fixtures['add_new']['name'],      item.name)
 		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']['password'],  item.password)
 		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
 		assert_equal(@fixtures['update']['comment'],    item.comment)
 	
@@ -185,14 +143,12 @@ class TestItem < Test::Unit::TestCase
 	end
 
 	def test_05_delete
-		data = {name:     @fixtures['add_new']['name'],
-		        group:    @fixtures['add_new']['group'],
-		        host:     @fixtures['add_new']['host'],
-		        protocol: @fixtures['add_new']['protocol'],
-		        user:     @fixtures['add_new']['user'],
-		        password: @fixtures['add_new']['password'],
-		        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)
@@ -200,17 +156,15 @@ class TestItem < Test::Unit::TestCase
 		assert(!item.nil?)
 		assert(!item.empty?)
 
-		assert(item.delete)
+		item.delete
 		assert(!item.nil?)
 		assert(item.empty?)
 
 		assert_equal(nil, item.id)
-		assert_equal(nil, item.name)
 		assert_equal(nil, item.group)
 		assert_equal(nil, item.host)
 		assert_equal(nil, item.protocol)
 		assert_equal(nil, item.user)
-		assert_equal(nil, item.password)
 		assert_equal(nil, item.port)
 		assert_equal(nil, item.comment)
 		assert_equal(nil, item.created)
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 899b366..0c6a25b 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -1,121 +1,44 @@
 #!/usr/bin/ruby
  
-require_relative '../lib/MPW'
-require_relative '../lib/Item'
+require 'mpw/mpw'
+require 'mpw/item'
 require 'test/unit'
 require 'yaml'
 require 'csv'
  
 class TestMPW < Test::Unit::TestCase
 	def setup
-		fixture_file = 'files/fixtures.yml'
+		fixture_file = './test/files/fixtures.yml'
 
-		file_gpg = 'test.gpg'
-		key      = ENV['MPW_TEST_KEY']
-
-		puts
+		wallet_file = 'default.gpg'
+		key         = 'test@example.com'
+		password    = 'password'
 
 		if defined?(I18n.enforce_available_locales)
 			I18n.enforce_available_locales = false
 		end
 
-		File.delete(file_gpg) if File.exist?(file_gpg)
-
-		@mpw      = MPW::MPW.new(file_gpg, key)
+		@mpw      = MPW::MPW.new(key, wallet_file, password)
 		@fixtures = YAML.load_file(fixture_file)
 	end
  
- 	def test_01_import_yaml
-		import_file = 'files/test_import.yml'
-
-		assert(@mpw.import(import_file, :yaml))
-		assert_equal(2, @mpw.list.length)
-
-		item = @mpw.list[0]
-		assert_equal(@fixtures['add_new']['name'],      item.name)
-		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']['password'],  item.password)
-		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
-		assert_equal(@fixtures['add_new']['comment'],   item.comment)
+	def test_00_decrypt_empty_file
+		@mpw.read_data
+		assert_equal(0, @mpw.list.length)
 	end
 
-	def test_02_export_yaml
-		import_file = 'files/test_import.yml'
-		export_file = 'test_export.yml'
-
-		assert(@mpw.import(import_file))
-		assert_equal(2, @mpw.list.length)
-		assert(@mpw.export(export_file, :yaml))
-		export = YAML::load_file(export_file)
-		assert_equal(2, export.length)
-
-		result = export.values[0]
-		assert_equal(@fixtures['add_new']['name'],      result['name'])
-		assert_equal(@fixtures['add_new']['group'],     result['group'])
-		assert_equal(@fixtures['add_new']['host'],      result['host'])
-		assert_equal(@fixtures['add_new']['protocol'],  result['protocol'])
-		assert_equal(@fixtures['add_new']['user'],      result['user'])
-		assert_equal(@fixtures['add_new']['password'],  result['password'])
-		assert_equal(@fixtures['add_new']['port'].to_i, result['port'])
-		assert_equal(@fixtures['add_new']['comment'],   result['comment'])
-
-		File.unlink(export_file)
+	def test_01_encrypt_empty_file
+		@mpw.read_data
+		@mpw.write_data
 	end
 
-	def test_03_import_csv
-		import_file = 'files/test_import.csv'
-
-		assert(@mpw.import(import_file, :csv))
-		assert_equal(2, @mpw.list.length)
-
-		import = CSV.parse(File.read(import_file), headers: true)
-
-		item = @mpw.list[0]
-		assert_equal(import[0]['name'],      item.name)
-		assert_equal(import[0]['group'],     item.group)
-		assert_equal(import[0]['host'],      item.host)
-		assert_equal(import[0]['protocol'],  item.protocol)
-		assert_equal(import[0]['user'],      item.user)
-		assert_equal(import[0]['password'],  item.password)
-		assert_equal(import[0]['port'].to_i, item.port)
-		assert_equal(import[0]['comment'],   item.comment)
-	end
-
-	def test_04_export_csv
-		import_file = 'files/test_import.csv'
-		export_file = 'test_export.csv'
-
-		assert(@mpw.import(import_file, :csv))
-		assert_equal(2, @mpw.list.length)
-		assert(@mpw.export(export_file, :csv))
-		export = CSV.parse(File.read(export_file), headers: true)
-		assert_equal(2, export.length)
-
-		result = export[0]
-		assert_equal(@fixtures['add_new']['name'],     result['name'])
-		assert_equal(@fixtures['add_new']['group'],    result['group'])
-		assert_equal(@fixtures['add_new']['host'],     result['host'])
-		assert_equal(@fixtures['add_new']['protocol'], result['protocol'])
-		assert_equal(@fixtures['add_new']['user'],     result['user'])
-		assert_equal(@fixtures['add_new']['password'], result['password'])
-		assert_equal(@fixtures['add_new']['port'],     result['port'])
-		assert_equal(@fixtures['add_new']['comment'],  result['comment'])
-
-		File.unlink(export_file)
-	end
-
-	def test_05_add_item
-		data = {name:     @fixtures['add_new']['name'],
-		        group:    @fixtures['add_new']['group'],
-		        host:     @fixtures['add_new']['host'],
-		        protocol: @fixtures['add_new']['protocol'],
-		        user:     @fixtures['add_new']['user'],
-		        password: @fixtures['add_new']['password'],
-		        port:     @fixtures['add_new']['port'],
-		        comment:  @fixtures['add_new']['comment'],
+	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'],
 		       }
 		
 		item = MPW::Item.new(data)
@@ -123,69 +46,76 @@ class TestMPW < Test::Unit::TestCase
 		assert(!item.nil?)
 		assert(!item.empty?)
 
-		assert(@mpw.add(item))
+		@mpw.read_data
+		@mpw.add(item)
+		@mpw.set_password(item.id, @fixtures['add_new']['password'])
 
 		assert_equal(1, @mpw.list.length)
 
 		item = @mpw.list[0]
-		assert_equal(@fixtures['add_new']['name'],      item.name)
-		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']['password'],  item.password)
-		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
-		assert_equal(@fixtures['add_new']['comment'],   item.comment)
+		@fixtures['add_new'].each do |k,v|
+			if k == 'password'
+				assert_equal(v, @mpw.get_password(item.id))
+			else
+				assert_equal(v, item.send(k).to_s)
+			end
+		end
+
+		@mpw.write_data
 	end
 
-	def test_11_encrypt_empty_file
-		assert(@mpw.encrypt)	
-	end
-
-	def test_12_encrypt
-		import_file = 'files/test_import.yml'
-
-		assert(@mpw.import(import_file, :yaml))
-		assert_equal(2, @mpw.list.length)
-
-		assert(@mpw.encrypt)	
-	end
-
-	def test_13_decrypt_empty_file
-		assert(@mpw.decrypt)
-		assert_equal(0, @mpw.list.length)
-	end
-
-	def test_14_decrypt
-		import_file = 'files/test_import.yml'
-
-		assert(@mpw.import(import_file, :yaml))
-		assert_equal(2, @mpw.list.length)
-
-		assert(@mpw.encrypt)	
-
-		assert(@mpw.decrypt)
-		assert_equal(2, @mpw.list.length)
+	def test_03_decrypt_file
+		@mpw.read_data
+		assert_equal(1, @mpw.list.length)
 
 		item = @mpw.list[0]
-		assert_equal(@fixtures['add_new']['name'],      item.name)
-		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']['password'],  item.password)
-		assert_equal(@fixtures['add_new']['port'].to_i, item.port)
-		assert_equal(@fixtures['add_new']['comment'],   item.comment)
+		@fixtures['add_new'].each do |k,v|
+			if k == 'password'
+				assert_equal(v, @mpw.get_password(item.id))
+			else
+				assert_equal(v, item.send(k).to_s)
+			end
+		end
 	end
 
-	def test_15_search
-		import_file = 'files/test_import.yml'
+	def test_04_delete_item
+		@mpw.read_data
 
-		assert(@mpw.import(import_file, :yaml))
-		assert_equal(2, @mpw.list.length)
+		assert_equal(1, @mpw.list.length)
 
+		@mpw.list.each do |item|
+			item.delete
+		end
+
+		assert_equal(0, @mpw.list.length)
+
+		@mpw.write_data
+	end
+
+	def test_05_search
+		@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'],
+			       }
+			
+			item = MPW::Item.new(data)
+	
+			assert(!item.nil?)
+			assert(!item.empty?)
+	
+			@mpw.add(item)
+			@mpw.set_password(item.id, v['password'])
+		end
+
+		assert_equal(3, @mpw.list.length)
 		assert_equal(1, @mpw.list(group:    @fixtures['add_new']['group']).length)
-		assert_equal(1, @mpw.list(protocol: @fixtures['add_new']['protocol']).length)
-		assert_equal(2, @mpw.list(search:   @fixtures['add_new']['name'][0..-2]).length)
+		assert_equal(1, @mpw.list(pattern:  'existing').length)
+		assert_equal(2, @mpw.list(pattern:  'host_[eu]').length)
 	end
 end
diff --git a/test/tests.rb b/test/tests.rb
index d3735f5..b85efc0 100644
--- a/test/tests.rb
+++ b/test/tests.rb
@@ -1,4 +1,5 @@
 #!/usr/bin/ruby
  
-require_relative 'test_mpw.rb'
+require_relative 'test_config.rb'
 require_relative 'test_item.rb'
+require_relative 'test_mpw.rb'

From c01c795ecaa55a4fe91d94dffe9ebc07ccc7b951 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Fri, 11 Nov 2016 00:24:38 +0100
Subject: [PATCH 366/531] add version

---
 README.rst | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/README.rst b/README.rst
index 432b4d8..17725d2 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,6 @@
 MPW: Manage your passwords!
 *******************************************************
-|Build Status| |License|
+|Version| |Build Status| |License|
 
 mpw is a little software which stores your passwords in `GnuPG <http://www.gnupg.org/>`_ encrypted files.
 
@@ -46,11 +46,9 @@ Output::
 	  2  | linuxfr.org   | example   | https    |      |     | Da Linux French Site
 
 
-Licence |License|
-=================
-
-Full license here: `LICENSE <https://github.com/nishiki/manage-password/blob/master/LICENSE>`_
-
+.. |Version| image:: https://img.shields.io/badge/latest_version-4.0.0--beta-yellow.svg
+   :target: https://github.com/nishiki/manage-password/releases
 .. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg
+   :target: https://github.com/nishiki/manage-password/blob/master/LICENSE
 .. |Build Status| image:: https://travis-ci.org/nishiki/manage-password.svg?branch=master
    :target: https://travis-ci.org/nishiki/manage-password

From 04e8a2cd0bc40d26a444e65ae5e02300db6e615e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 12 Nov 2016 16:54:41 +0100
Subject: [PATCH 367/531] add test translate

---
 test/test_translate.rb | 31 +++++++++++++++++++++++++++++++
 test/tests.rb          |  1 +
 2 files changed, 32 insertions(+)
 create mode 100644 test/test_translate.rb

diff --git a/test/test_translate.rb b/test/test_translate.rb
new file mode 100644
index 0000000..fe9a575
--- /dev/null
+++ b/test/test_translate.rb
@@ -0,0 +1,31 @@
+#!/usr/bin/ruby
+
+require 'yaml'
+require 'test/unit'
+
+class TestTranslate < Test::Unit::TestCase
+	def test_00_check_translate
+		missing = 0
+		
+		Dir.glob('i18n/*.yml').each do |yaml|
+			lang      = File.basename(yaml, '.yml')
+			translate = YAML.load_file(yaml)
+		
+			`grep -r -o "I18n.t('.*')" bin/ lib/ | cut -d"'" -f2`.each_line do |line|
+				begin
+					t = translate[lang]
+					line.strip.split('.').each do |v|
+						t = t[v]
+					end
+
+					assert(!t.to_s.empty?)
+				rescue
+					puts "#{lang}.#{line}"
+					missing = 1
+				end
+			end
+		end
+		
+		assert_equal(0, missing)
+	end
+end
diff --git a/test/tests.rb b/test/tests.rb
index b85efc0..e4efc5d 100644
--- a/test/tests.rb
+++ b/test/tests.rb
@@ -3,3 +3,4 @@
 require_relative 'test_config.rb'
 require_relative 'test_item.rb'
 require_relative 'test_mpw.rb'
+require_relative 'test_translate.rb'

From d8339b53deb810ff24cdeb455cfd18dc0c240409 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 12 Nov 2016 17:16:57 +0100
Subject: [PATCH 368/531] fix missing translate

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

diff --git a/i18n/en.yml b/i18n/en.yml
index 93955d5..e1ffcf7 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -1,6 +1,7 @@
 ---
 en:
   error:
+    bad_class: "The object class isn't valid!"
     config:
       write: "Can't write the config file!"
       load: "Checkconfig failed!"
@@ -10,7 +11,9 @@ en:
         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"
     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!"
@@ -64,6 +67,7 @@ en:
     import: "Import item since a yaml file"
     key: "Specify the key name"
     lang: "Set the software language"
+    length: "Size of the password"
     list: "List the wallets"
     no_sync: "Disable synchronization with the server"
     numeric: "Use number to generate a password"
@@ -72,6 +76,7 @@ en:
     pattern: "Given search pattern"
     port: "Specify the connection port"
     protocol: "Specify the protocol for the connection"
+    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"
@@ -116,6 +121,8 @@ en:
       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!"
       not_valid: "No data to import!"
     setup_config:
@@ -126,6 +133,7 @@ en:
       wallet_dir: "Enter the wallets's folder path [default=%{home}/wallets]: "
       valid: "The config file has been created!"
     setup_wallet:
+      password: "Sync password: " 
       title: "Wallet setup"
       sync_type: "Synchronization type (ssh, ftp): "
       sync_host: "Synchronization server: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 4f8b7ac..82e5af3 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -1,6 +1,7 @@
 ---
 fr:
   error:
+    bad_class: "La classe de l'objet n'est pas celle attendue!"
     config:
       write: "Impossible d'écrire le fichier de configuration!"
       load: "Le fichier de configuration est invalide!"
@@ -10,7 +11,9 @@ fr:
         exception: "La création de la clé GPG n'a pas pu aboutir!"
         name: "Vous devez définir un nom pour votre clé GPG!"
         password: "Vous devez définir un mot de passe pour votre clé GPG!"
+    empty: "La classe est vide"
     export: "Impossible d'exporter les données dans le fichier %{file}!"
+    export_key: "Impossible d'exporter la clé GPG"
     gpg_file: 
       decrypt: "Impossible de déchiffrer le fichier GPG!"
       encrypt: "Impossible de chiffrer le fichier GPG!"
@@ -64,6 +67,7 @@ fr:
     init: "Initialise mpw"
     key: "Spécifie le nom d'une clé"
     lang: "Spécifie la langue du logiciel (ex: fr)"
+    length: "Taille du mot de passe"
     list: "Liste les portefeuilles"
     no_sync: "Désactive la synchronisation avec le serveur"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
@@ -72,6 +76,7 @@ fr:
     pattern: "Motif de donnée à chercher"
     port: "Spécifie le port de connexion"
     protocol: "Spécifie le protocol utilisé pour la 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"
     special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
@@ -116,6 +121,8 @@ fr:
       valid: "L'élément a bien été supprimé!"
     import:
       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"
       valid: "L'import est un succès!"
       not_valid: "Aucune donnée à importer!"
     setup_config:
@@ -126,6 +133,7 @@ fr:
       wallet_dir: "Entrez le chemin du répertoire qui contiendra les porte-feuilles de mot de passe [défaut=%{home}/wallets]: "
       valid: "Le fichier de configuration a bien été créé!"
     setup_wallet:
+      password: "Mot de passe de synchronisation: "
       title: "Configuration du porte-feuille"
       sync_type: "Type de synchronisation (ssh, ftp): "
       sync_host: "Serveur: "
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 722b16d..8cd700a 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -517,8 +517,8 @@ class Cli
 	# Import items from a YAML file
 	# @args: file -> the import file
 	def import(file)
-		raise I18n.t('import.file_empty')     if file.to_s.empty?
-		raise I18n.t('import.file_not_exist') if not File.exist?(file)
+		raise I18n.t('form.import.file_empty')     if file.to_s.empty?
+		raise I18n.t('form.import.file_not_exist') if not File.exist?(file)
 
 		YAML::load_file(file).each_value do |row|
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 533077e..91bd04a 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -228,7 +228,7 @@ class MPW
 		if not item.instance_of?(Item)
 			raise I18n.t('error.bad_class')
 		elsif item.empty?
-			raise I18n.t('error.add.empty')
+			raise I18n.t('error.empty')
 		else
 			@data.push(item)
 		end

From 36e0f83175e1bc440af907166430ccb095be5ecb Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 12 Dec 2016 22:15:09 +0100
Subject: [PATCH 369/531] fix gpg key name

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

diff --git a/bin/mpw-config b/bin/mpw-config
index 120da5b..fb6b372 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -68,7 +68,7 @@ if not options[:init].nil?
 	cli.setup(values)
 	cli.load_config
 	cli.get_wallet
-	cli.setup_gpg_key(options[:init])
+	cli.setup_gpg_key(values[:gpg_key])
 	cli.setup_wallet_config
 else
 	cli.set_config(values)

From 3575cd9bd7095c09253492f314acff8f699d3a45 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 12 Dec 2016 23:09:52 +0100
Subject: [PATCH 370/531] feat change add key

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 8cd700a..43256d1 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -334,10 +334,9 @@ class Cli
 	end
 
 	# Add a new public key
-	# args: key -> the key name to add
-	#       file -> gpg public file to import
-	def add_key(key, file=nil)
-		@mpw.add_key(key, file)
+	# args: key -> the key name or key file to add
+	def add_key(key)
+		@mpw.add_key(key)
 		@mpw.write_data
 		@mpw.sync(true) if @sync
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 91bd04a..7e50547 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -185,12 +185,12 @@ class MPW
 	end
 
 	# Add a public key
-	# args: key ->  new public key
-	#       file -> public gpg file to import
-	def add_key(key, file=nil)
-		if not file.nil? and File.exists?(file)
-			data = File.open(file).read
-			GPGME::Key.import(data, armor: true)
+	# args: key ->  new public key file or name
+	def add_key(key)
+		if File.exists?(key)
+			data       = File.open(key).read
+			key_import = GPGME::Key.import(data, armor: true)
+			key        = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
 		else
 			data = GPGME::Key.export(key, armor: true).read
 		end

From bf356f4d6f9dbcbab6ba150d898f99db0cdfa5fb Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 12 Dec 2016 23:13:32 +0100
Subject: [PATCH 371/531] add an option for add a share gpg key

---
 bin/mpw-wallet | 6 ++++++
 i18n/en.yml    | 1 +
 i18n/fr.yml    | 1 +
 3 files changed, 8 insertions(+)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index b81e6b2..f6ea562 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -31,6 +31,10 @@ values         = {}
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
 
+	opts.on('-a', '--add-gpg-key NAME', I18n.t('option.add_gpg_key')) do |gpg_key|
+		options[:gpg_key] = gpg_key
+	end
+
 	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
 		options[:config] = config
 	end
@@ -84,6 +88,8 @@ cli.load_config
 
 if not options[:list].nil?
 	cli.list_wallet
+elsif not options[:gpg_key].nil?
+	cli.add_key(options[:gpg_key])
 else
 	cli.get_wallet(options[:wallet])
 	cli.decrypt
diff --git a/i18n/en.yml b/i18n/en.yml
index e1ffcf7..eb45c86 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -50,6 +50,7 @@ en:
 
   option:
     add: "Add an item or key"
+    add_gpg_key: "Share the wallet with an other GPG key (name or file path)"
     alpha: "Use letter to generate a password"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 82e5af3..5a0ebc0 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -50,6 +50,7 @@ fr:
 
   option:
     add: "Ajoute un élément ou une clé"
+    add_gpg_key: "Partage le portefeuille avec une autre clé GPG (nom ou fichier)"
     alpha: "Utilise des lettres dans la génération d'un mot de passe"
     config: "Spécifie le fichier de configuration à utiliser"
     clipboard: "Désactive la fonction presse papier"

From cb409b1545bb3a2394c071f16a8a4e947c066ce3 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 24 Jan 2017 22:58:03 +0100
Subject: [PATCH 372/531] fix bug when port is > 4 number

---
 lib/mpw/cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 8cd700a..47dfa04 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -153,7 +153,7 @@ class Cli
 			data.each do |k, v|
 				next if k == :id or k == :otp
 
-				v[:length] = item.send(k.to_s).length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+				v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
 			end
 		end
 		data[:id][:length]  = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]

From 50aa7713f47fd3cd7765190fe2c30eab843656ed Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 30 Jan 2017 20:29:42 +0100
Subject: [PATCH 373/531] fix error message when host is empty

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

diff --git a/i18n/en.yml b/i18n/en.yml
index e1ffcf7..252efee 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -22,7 +22,7 @@ en:
       write_data: "Can't to write the MPW file!"
     import: "Can't import, unable to read %{file}!"
     update:
-      name_empty: "You must define a name!"
+      host_empty: "You must define a host!"
     sync: 
       general: "An error has appeared during the sync"
       connection: "Connection fail!"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 82e5af3..16160cd 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:
-      name_empty: "Vous devez définir un nom!"
+      host_empty: "Vous devez définir un host!"
     sync: 
       general: "Une erreur est survenue durant la synchronisation"
       connection: "La connexion n'a pu être établie!"
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 4beb49f..d3783b9 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -39,7 +39,7 @@ class Item
 	# raise an error if the hash hasn't the key name 
 	def initialize(options={})
 		if not options.has_key?(:host) or options[:host].to_s.empty?
-			raise I18n.t('error.update.name_empty')
+			raise I18n.t('error.update.host_empty')
 		end
 
 		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
@@ -59,7 +59,7 @@ class Item
 	# @args: options -> a hash of parameter
 	def update(options={})
 		if options.has_key?(:host) and options[:host].to_s.empty?
-			raise I18n.t('error.update.name_empty')
+			raise I18n.t('error.update.host_empty')
 		end
 
 		@group     = options[:group]      if options.has_key?(:group)

From 8c5be6b795a784f5407e34268a39c15b151daac4 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 30 Jan 2017 22:10:56 +0100
Subject: [PATCH 374/531] wallet: add remove gpg key option

---
 bin/mpw-wallet | 18 ++++++++++++++----
 i18n/en.yml    |  3 ++-
 i18n/fr.yml    |  3 ++-
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index f6ea562..8bdfb1c 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -24,9 +24,10 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = {}
-values         = {}
+options          = {}
+options[:sync]   = {}
+options[:delete] = false
+values           = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -39,6 +40,11 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
+	opts.on('-d', '--delete-gpg-key NAME', I18n.t('option.delete_gpg_key')) do |gpg_key|
+		options[:gpg_key] = gpg_key
+		options[:delete]  = true
+	end
+
 	opts.on('-h', '--help', I18n.t('option.help')) do
 		puts opts
 		exit 0
@@ -89,7 +95,11 @@ cli.load_config
 if not options[:list].nil?
 	cli.list_wallet
 elsif not options[:gpg_key].nil?
-	cli.add_key(options[:gpg_key])
+	if options[:delete]
+		cli.delete_key(options[:gpg_key])
+	else
+		cli.add_key(options[:gpg_key])
+	end
 else
 	cli.get_wallet(options[:wallet])
 	cli.decrypt
diff --git a/i18n/en.yml b/i18n/en.yml
index eb45c86..6b1f23f 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -50,10 +50,11 @@ en:
 
   option:
     add: "Add an item or key"
-    add_gpg_key: "Share the wallet with an other GPG key (name or file path)"
+    add_gpg_key: "Share the wallet with an other GPG key"
     alpha: "Use letter to generate a password"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
+    add_gpg_key: "Delete the wallet's share with an other GPG key"
     export: "Export a wallet in an yaml file"
     file_export: "Specify the file where export data"
     file_import: "Specify the file to import"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 5a0ebc0..8dc6d21 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -50,10 +50,11 @@ fr:
 
   option:
     add: "Ajoute un élément ou une clé"
-    add_gpg_key: "Partage le portefeuille avec une autre clé GPG (nom ou fichier)"
+    add_gpg_key: "Partage le portefeuille avec une autre clé GPG"
     alpha: "Utilise des lettres dans la génération d'un mot de passe"
     config: "Spécifie le fichier de configuration à utiliser"
     clipboard: "Désactive la fonction presse papier"
+    delete_gpg_key: "Supprime le partage le portefeuille avec une autre clé GPG"
     export: "Exporte un portefeuille dans un fichier yaml"
     file_export: "Spécifie le fichier où exporter les données"
     file_import: "Spécifie le fichier à importer"

From d6d7074341ad199eeca20fe31f6acc5c543adf26 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 30 Jan 2017 22:20:22 +0100
Subject: [PATCH 375/531] fix error message when there is a bad option

---
 bin/mpw | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/bin/mpw b/bin/mpw
index 8e578f8..464980c 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -21,6 +21,7 @@ $: << File.expand_path('../../lib', __FILE__)
 require 'locale'
 require 'set'
 require 'i18n'
+require 'colorize'
 
 # --------------------------------------------------------- #
 # Set local
@@ -45,7 +46,11 @@ bin_dir = File.dirname(__FILE__)
 command = "#{bin_dir}/mpw-#{ARGV[0]}"
 
 if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
-	Kernel.load(command)
+	begin
+		Kernel.load(command)
+	rescue Exception => e
+		puts "#{I18n.t('display.error')}: #{e}".red
+	end
 else
 	puts "#{I18n.t('option.usage')}: mpw COMMAND [options]\n\n"
 	puts 'Commands:'

From 06c876861b7d246db798218bcf7ea555c2c96ad4 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 30 Jan 2017 22:26:18 +0100
Subject: [PATCH 376/531] fix english translation

---
 i18n/en.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index 6b1f23f..6f3d4c8 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -54,7 +54,7 @@ en:
     alpha: "Use letter to generate a password"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
-    add_gpg_key: "Delete the wallet's share with an other GPG key"
+    delete_gpg_key: "Delete the wallet's share with an other GPG key"
     export: "Export a wallet in an yaml file"
     file_export: "Specify the file where export data"
     file_import: "Specify the file to import"

From a1a21f96179f09107770e37e0d72fbc60eb56094 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 11:24:12 +0100
Subject: [PATCH 377/531] mpw: add function list_keys

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 7e50547..c5023e6 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -184,6 +184,12 @@ class MPW
 		@passwords[id] = encrypt(password)
 	end
 
+	# Return the list of all gpg keys
+	# rtrn: an array with the gpg keys name
+	def list_keys
+		return @keys.keys
+	end
+
 	# Add a public key
 	# args: key ->  new public key file or name
 	def add_key(key)

From b287b719fee1f3b47d07ced3ef30ffb9d97ff02a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 11:24:49 +0100
Subject: [PATCH 378/531] add test to check add and delete gpg key

---
 test/test_mpw.rb | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index 0c6a25b..b1c80c9 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -118,4 +118,26 @@ class TestMPW < Test::Unit::TestCase
 		assert_equal(1, @mpw.list(pattern:  'existing').length)
 		assert_equal(2, @mpw.list(pattern:  'host_[eu]').length)
 	end
+
+	def test_06_add_gpg_key
+		@config = MPW::Config.new
+		@config.setup_gpg_key('password', 'test2@example.com', 2048)
+
+		@mpw.read_data
+
+		@mpw.add_key('test2@example.com')
+		assert_equal(2, @mpw.keys.length)
+
+		@mpw.write_data
+	end
+
+	def test07_delete_gpg_key
+		@mpw.read_data
+		assert_equal(2, @mpw.keys.length)
+
+		@mpw.delete_key('test2@example.com')
+		assert_equal(1, @mpw.keys.length)
+
+		@mpw.write_data
+	end
 end

From 4bd538fa7c4d6768094fb959924d8a33718fa237 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 13:43:29 +0100
Subject: [PATCH 379/531] generate a gpg key for the test

---
 test/init.rb     | 19 +++++++++++++++++++
 test/test_mpw.rb |  5 +----
 test/tests.rb    |  1 +
 3 files changed, 21 insertions(+), 4 deletions(-)
 create mode 100644 test/init.rb

diff --git a/test/init.rb b/test/init.rb
new file mode 100644
index 0000000..be7a73a
--- /dev/null
+++ b/test/init.rb
@@ -0,0 +1,19 @@
+#!/usr/bin/ruby
+
+require 'gpgme'
+
+param = ''
+param << '<GnupgKeyParms format="internal">' + "\n"
+param << "Key-Type: RSA\n"
+param << "Key-Length: 2048\n"
+param << "Subkey-Type: ELG-E\n"
+param << "Subkey-Length: 2048\n"
+param << "Name-Real: test\n"
+param << "Name-Comment: test\n"
+param << "Name-Email: test2@example.com\n"
+param << "Expire-Date: 0\n"
+param << "Passphrase: password\n"
+param << "</GnupgKeyParms>\n"
+
+ctx = GPGME::Ctx.new
+ctx.genkey(param, nil, nil)
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index b1c80c9..d2de795 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -120,9 +120,6 @@ class TestMPW < Test::Unit::TestCase
 	end
 
 	def test_06_add_gpg_key
-		@config = MPW::Config.new
-		@config.setup_gpg_key('password', 'test2@example.com', 2048)
-
 		@mpw.read_data
 
 		@mpw.add_key('test2@example.com')
@@ -131,7 +128,7 @@ class TestMPW < Test::Unit::TestCase
 		@mpw.write_data
 	end
 
-	def test07_delete_gpg_key
+	def test_07_delete_gpg_key
 		@mpw.read_data
 		assert_equal(2, @mpw.keys.length)
 
diff --git a/test/tests.rb b/test/tests.rb
index e4efc5d..904f64e 100644
--- a/test/tests.rb
+++ b/test/tests.rb
@@ -1,5 +1,6 @@
 #!/usr/bin/ruby
  
+require_relative 'init.rb'
 require_relative 'test_config.rb'
 require_relative 'test_item.rb'
 require_relative 'test_mpw.rb'

From e6238341cd6436cb4f12ed50996b3614f402ebef Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 13:47:22 +0100
Subject: [PATCH 380/531] fix test

---
 test/test_mpw.rb | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index d2de795..c4748f6 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -123,17 +123,17 @@ class TestMPW < Test::Unit::TestCase
 		@mpw.read_data
 
 		@mpw.add_key('test2@example.com')
-		assert_equal(2, @mpw.keys.length)
+		assert_equal(2, @mpw.list_keys.length)
 
 		@mpw.write_data
 	end
 
 	def test_07_delete_gpg_key
 		@mpw.read_data
-		assert_equal(2, @mpw.keys.length)
+		assert_equal(2, @mpw.list_keys.length)
 
 		@mpw.delete_key('test2@example.com')
-		assert_equal(1, @mpw.keys.length)
+		assert_equal(1, @mpw.list_keys.length)
 
 		@mpw.write_data
 	end

From adf34a006c625d1b772414823819afe498416f56 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 15:09:48 +0100
Subject: [PATCH 381/531] config: generate gpg key with rsa

---
 lib/mpw/config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index e8911fa..867ff35 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -97,7 +97,7 @@ class Config
 
 		param = ''
 		param << '<GnupgKeyParms format="internal">' + "\n"
-		param << "Key-Type: DSA\n"  
+		param << "Key-Type: RSA\n"  
 		param << "Key-Length: #{length}\n"
 		param << "Subkey-Type: ELG-E\n"
 		param << "Subkey-Length: #{length}\n"

From 501ae5ab2e243d73720383e89e3afa74bcafb5e1 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 15:11:08 +0100
Subject: [PATCH 382/531] remove unused test files

---
 test/test.sh  |  1 -
 test/test2.rb | 11 -----------
 2 files changed, 12 deletions(-)
 delete mode 100644 test/test.sh
 delete mode 100644 test/test2.rb

diff --git a/test/test.sh b/test/test.sh
deleted file mode 100644
index ab37885..0000000
--- a/test/test.sh
+++ /dev/null
@@ -1 +0,0 @@
-echo "test\ntest\n" | ruby ./bin/mpw config --init test@test.com
diff --git a/test/test2.rb b/test/test2.rb
deleted file mode 100644
index 17af2da..0000000
--- a/test/test2.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require 'open3'
-
-Open3.popen3("./bin/mpw config --init test@test.com") do |stdin, stdout, stderr, thread|
-	stdin.puts 'test'
-	stdin.puts 'test'
-end
-
-Open3.popen3("./bin/mpw list") do |stdin, stdout, stderr, thread|
-	stdin.puts 'test'
-	puts stdout
-end

From f251b2ebe098158fa6cad4180039fd2eda4be858 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 11 Feb 2017 19:08:26 +0100
Subject: [PATCH 383/531] test with ruby 2.4

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

diff --git a/.travis.yml b/.travis.yml
index 141eab1..1a18023 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,8 @@
 language: ruby
 rvm:
-  - 2.3.1
-  - 2.2.5
+  - 2.4.0
+  - 2.3.3
+  - 2.2.6
   - 2.1.10
 install:
   - bundle install

From ec3abbe88a006757ab7ef3b9f7fd780b398a0b9f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 14 Feb 2017 21:30:26 +0100
Subject: [PATCH 384/531] fix bug share key

---
 bin/mpw-wallet | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 8bdfb1c..5222d81 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -94,14 +94,17 @@ cli.load_config
 
 if not options[:list].nil?
 	cli.list_wallet
-elsif not options[:gpg_key].nil?
-	if options[:delete]
-		cli.delete_key(options[:gpg_key])
-	else
-		cli.add_key(options[:gpg_key])
-	end
 else
 	cli.get_wallet(options[:wallet])
 	cli.decrypt
-	cli.setup_wallet_config(values)
+
+        if not options[:gpg_key].nil?
+		if options[:delete]
+			cli.delete_key(options[:gpg_key])
+		else
+			cli.add_key(options[:gpg_key])
+		end
+	else
+		cli.setup_wallet_config(values)
+	end
 end

From 362253c63b6f847d54a2627427e0888eaaee9f8e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 16 Feb 2017 20:47:39 +0100
Subject: [PATCH 385/531] cli: add list gpg keys in wallet

---
 bin/mpw-wallet | 10 ++++++++--
 i18n/en.yml    |  2 ++
 i18n/fr.yml    |  2 ++
 lib/mpw/cli.rb | 24 ++++++++++++++++++++++++
 4 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 5222d81..221bb38 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -54,10 +54,14 @@ OptionParser.new do |opts|
 		values[:host] = host
 	end
 
-	opts.on('-l', '--list', I18n.t('option.list')) do |list|
+	opts.on('-l', '--list', I18n.t('option.list')) do
 		options[:list] = true
 	end
 
+	opts.on('-L', '--list-keys', I18n.t('option.list_keys')) do
+		options[:list_keys] = true
+	end
+
 	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
 		options[:sync] = false
 	end
@@ -98,7 +102,9 @@ else
 	cli.get_wallet(options[:wallet])
 	cli.decrypt
 
-        if not options[:gpg_key].nil?
+	if not options[:list_keys].nil?
+		cli.list_keys
+	elsif not options[:gpg_key].nil?
 		if options[:delete]
 			cli.delete_key(options[:gpg_key])
 		else
diff --git a/i18n/en.yml b/i18n/en.yml
index 16a8c69..abf32bd 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -71,6 +71,7 @@ en:
     lang: "Set the software language"
     length: "Size of the password"
     list: "List the wallets"
+    list_keys: "List the GPG keys in wallet"
     no_sync: "Disable synchronization with the server"
     numeric: "Use number to generate a password"
     password: "Change the password for the synchronization"
@@ -173,6 +174,7 @@ en:
   display:
     comment: "Comment"
     error: "ERROR"
+    keys: "GPG keys"
     gpg_password: "GPG passphrase: "
     group: "Group"
     login: "Login"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 0e4e9f6..9b6a1ea 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -71,6 +71,7 @@ fr:
     lang: "Spécifie la langue du logiciel (ex: fr)"
     length: "Taille du mot de passe"
     list: "Liste les portefeuilles"
+    list_keys: "Liste les clés GPG dans le portefeuille"
     no_sync: "Désactive la synchronisation avec le serveur"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
     password: "Changer le mot de passe de connexion"
@@ -173,6 +174,7 @@ fr:
   display:
     comment: "Commentaire"
     error: "ERREUR"
+    keys: "Clés GPG"
     gpg_password: "Mot de passe GPG: "
     group: "Groupe"
     login: "Identifiant"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 09e3ebd..15915af 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -112,6 +112,30 @@ class Cli
 		exit 2
 	end
 	
+	# List gpg keys in wallet
+	def list_keys
+		i      = 1
+		length = 0
+
+		@mpw.list_keys.each do |key|
+			length = key.length if length < key.length
+		end
+		length += 7
+
+		puts "\n#{I18n.t('display.keys')}".red
+		length.times { print '=' }
+		print "\n"
+
+		@mpw.list_keys.each do |key|
+			print " #{i}".cyan
+			(3 - i.to_s.length).times { print ' ' }
+			puts "| #{key}"
+			i += 1
+		end
+
+		print "\n"
+	end
+
 	# Load config
 	def load_config
 		@config.load_config

From e17219c77210ec2dbd0ba6e9edb9f4a2b1238e31 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 16 Feb 2017 23:25:54 +0100
Subject: [PATCH 386/531] minor fix in list_keys interface

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 15915af..99f79e3 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -123,11 +123,12 @@ class Cli
 		length += 7
 
 		puts "\n#{I18n.t('display.keys')}".red
+		print ' '
 		length.times { print '=' }
 		print "\n"
 
 		@mpw.list_keys.each do |key|
-			print " #{i}".cyan
+			print "  #{i}".cyan
 			(3 - i.to_s.length).times { print ' ' }
 			puts "| #{key}"
 			i += 1

From dbffea6b6e626e7a0778c890f15afab2f38e42ce Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 16 Feb 2017 23:32:24 +0100
Subject: [PATCH 387/531] upgrade version 4.0.0-beta1

---
 README.rst | 2 +-
 VERSION    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.rst b/README.rst
index 17725d2..f449383 100644
--- a/README.rst
+++ b/README.rst
@@ -46,7 +46,7 @@ Output::
 	  2  | linuxfr.org   | example   | https    |      |     | Da Linux French Site
 
 
-.. |Version| image:: https://img.shields.io/badge/latest_version-4.0.0--beta-yellow.svg
+.. |Version| image:: https://img.shields.io/badge/latest_version-4.0.0--beta1-yellow.svg
    :target: https://github.com/nishiki/manage-password/releases
 .. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg
    :target: https://github.com/nishiki/manage-password/blob/master/LICENSE
diff --git a/VERSION b/VERSION
index c0de572..817c97b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0.0-beta
+4.0.0-beta1

From 795343b66980d4738b7e651730ecf4fd3dfc1ee0 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 16 Feb 2017 23:39:50 +0100
Subject: [PATCH 388/531] upgrade changelog for version 4.0.0-beta1

---
 CHANGELOG | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG b/CHANGELOG
index 261d083..e154a0c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,10 +1,16 @@
 = CHANGELOG =
-== v4.0.0 (beta) ==
+== v4.0.0-beta1 ==
+
+* add manage share key with new interface
+
+== v4.0.0-beta ==
 
 * new interface with a table
 * new command line interface
 * use text editor for add or update an item
+* fix generate gpg key with RSA
 * several bugs fix
+* add unit tests
 
 == v3.2.1 ==
 

From a3dd83e63c5d23708d78c975bfb6f125319611e5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Fri, 17 Feb 2017 00:04:25 +0100
Subject: [PATCH 389/531] fix update otp key

---
 templates/update_form.erb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/update_form.erb b/templates/update_form.erb
index f4a380b..b942cf5 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -12,6 +12,6 @@ protocol: <%= item.protocol %>
 # <%= I18n.t('form.update_item.port') %>
 port: <%= item.port %>
 # <%= I18n.t('form.update_item.otp_key') %>
-opt_key: 
+otp_key: 
 # <%= I18n.t('form.update_item.comment') %>
 comment: <%= item.comment %>

From afdbb05bc51c4ad2c277e6cfd08e8e7c2ef4d0be Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 20 Feb 2017 22:26:57 +0100
Subject: [PATCH 390/531] fix nothing item message in copy mode

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 99f79e3..331b250 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -247,7 +247,6 @@ class Cli
 
 		if result.length == 0
 			puts I18n.t('display.nothing')
-
 		else
 			table(result)
 		end
@@ -494,7 +493,7 @@ class Cli
 		items = @mpw.list(options)
 		
 		if items.length == 0
-			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+			puts I18n.t('display.nothing')
 		else
 			table(items)
 

From 35a4cd47e4436f9fff27ebbf3d0bf2e92e91c018 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 21 Feb 2017 20:32:26 +0100
Subject: [PATCH 391/531] change readme syntax

---
 README.md  | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 README.rst | 54 ------------------------------------------------------
 2 files changed, 49 insertions(+), 54 deletions(-)
 create mode 100644 README.md
 delete mode 100644 README.rst

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..563c592
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# MPW: Manage your passwords!
+[![Version](https://img.shields.io/badge/latest_version-4.0.0--beta1-yellow.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)
+
+
+mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg.org/) encrypted files.
+
+## Features
+
+ * generate OTP code
+ * synchronize your passwords with SSH or FTP.
+ * copy your login, password or otp in clipboard
+
+## Install
+
+On debian or ubuntu:
+```
+apt install ruby ruby-dev xclip
+gem install mpw
+```
+
+
+# How to use
+
+A simple mpw usage:
+```
+mpw config --init user@host.com
+mpw add
+mpw copy
+mpw add
+mpw list
+```
+
+Output:
+```
+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
+
+```
diff --git a/README.rst b/README.rst
deleted file mode 100644
index f449383..0000000
--- a/README.rst
+++ /dev/null
@@ -1,54 +0,0 @@
-MPW: Manage your passwords!
-*******************************************************
-|Version| |Build Status| |License|
-
-mpw is a little software which stores your passwords in `GnuPG <http://www.gnupg.org/>`_ encrypted files.
-
-Features
-========
-
-* generate OTP code
-* synchronize your passwords with SSH or FTP.
-* copy your login, password or otp in clipboard
-
-Install
-=======
-
-On debian or ubuntu::
-
-	apt install ruby ruby-dev xclip
-	gem install mpw
-
-
-How to use
-==========
-
-A simple mpw usage::
-
-	mpw config --init user@host.com
-	mpw add
-	mpw copy
-	mpw add
-	mpw list
-
-Output::
-
-	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
-
-
-.. |Version| image:: https://img.shields.io/badge/latest_version-4.0.0--beta1-yellow.svg
-   :target: https://github.com/nishiki/manage-password/releases
-.. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg
-   :target: https://github.com/nishiki/manage-password/blob/master/LICENSE
-.. |Build Status| image:: https://travis-ci.org/nishiki/manage-password.svg?branch=master
-   :target: https://travis-ci.org/nishiki/manage-password

From 4b6de3575a3643e1f8294eaf47b8c06c75971c73 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 21 Feb 2017 21:05:28 +0100
Subject: [PATCH 392/531] fix interface for wallets list

---
 i18n/en.yml    |  1 +
 i18n/fr.yml    |  1 +
 lib/mpw/cli.rb | 66 +++++++++++++++++++++++++++-----------------------
 3 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/i18n/en.yml b/i18n/en.yml
index abf32bd..536a5e4 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -186,6 +186,7 @@ en:
     port: "Port"
     protocol: "Protocol"
     server: "Server"
+    wallets: "Wallets"
     warning: "Warning"
 
   formats:
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 9b6a1ea..09f060a 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -186,6 +186,7 @@ fr:
     port: "Port"
     protocol: "Protocol"
     server: "Serveur"
+    wallets: "Porte-feuilles"
     warning: "Warning"
 
   formats:
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 331b250..cf0cc28 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -114,27 +114,7 @@ class Cli
 	
 	# List gpg keys in wallet
 	def list_keys
-		i      = 1
-		length = 0
-
-		@mpw.list_keys.each do |key|
-			length = key.length if length < key.length
-		end
-		length += 7
-
-		puts "\n#{I18n.t('display.keys')}".red
-		print ' '
-		length.times { print '=' }
-		print "\n"
-
-		@mpw.list_keys.each do |key|
-			print "  #{i}".cyan
-			(3 - i.to_s.length).times { print ' ' }
-			puts "| #{key}"
-			i += 1
-		end
-
-		print "\n"
+		table_list('keys', @mpw.list_keys)
 	end
 
 	# Load config
@@ -160,8 +140,33 @@ class Cli
 		exit 2
 	end
 
+	# Format list on a table
+	def table_list(title, list)
+		i      = 1
+		length = 0
+
+		list.each do |item|
+			length = item.length if length < item.length
+		end
+		length += 7
+
+		puts "\n#{I18n.t("display.#{title}")}".red
+		print ' '
+		length.times { print '=' }
+		print "\n"
+
+		list.each do |item|
+			print "  #{i}".cyan
+			(3 - i.to_s.length).times { print ' ' }
+			puts "| #{item}"
+			i += 1
+		end
+
+		print "\n"
+	end
+
 	# Format items on a table
-	def table(items=[])
+	def table_items(items=[])
 		group        = '.'
 		i            = 1
 		length_total = 10
@@ -248,7 +253,7 @@ class Cli
 		if result.length == 0
 			puts I18n.t('display.nothing')
 		else
-			table(result)
+			table_items(result)
 		end
 	end
 
@@ -334,11 +339,12 @@ class Cli
 
 	# List all wallets
 	def list_wallet
-		wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
-
-		wallets.each do |wallet|
-			puts File.basename(wallet, '.mpw')
+		wallets = []
+		Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f| 
+			wallets << File.basename(f, '.mpw')
 		end
+
+		table_list('wallets', wallets)
 	end
 
 	# Display the wallet
@@ -442,7 +448,7 @@ class Cli
 		if items.length == 0
 			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
 		else
-			table(items) if items.length > 1
+			table_items(items) if items.length > 1
 
 			item    = get_item(items)
 			options = text_editor('update_form', item)
@@ -467,7 +473,7 @@ class Cli
 		if items.length == 0
 			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
 		else
-			table(items)
+			table_items(items)
 
 			item    = get_item(items)
 			confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
@@ -495,7 +501,7 @@ class Cli
 		if items.length == 0
 			puts I18n.t('display.nothing')
 		else
-			table(items)
+			table_items(items)
 
 			item = get_item(items)
 			clipboard(item, clipboard)

From a117e8a6188251580ec5b8e36d8f8c320834b83c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 21 Feb 2017 21:08:14 +0100
Subject: [PATCH 393/531] nothing to do if no values

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

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 221bb38..fbf0389 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -110,7 +110,7 @@ else
 		else
 			cli.add_key(options[:gpg_key])
 		end
-	else
+	elsif not values.empty?
 		cli.setup_wallet_config(values)
 	end
 end

From c63d91faccb2fb2e1a99c15340d464466df6234f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 21 Feb 2017 22:38:55 +0100
Subject: [PATCH 394/531] fix bug when exit with help message

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

diff --git a/bin/mpw b/bin/mpw
index 464980c..a69b568 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -48,7 +48,7 @@ command = "#{bin_dir}/mpw-#{ARGV[0]}"
 if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
 	begin
 		Kernel.load(command)
-	rescue Exception => e
+	rescue OptionParser::ParseError => e
 		puts "#{I18n.t('display.error')}: #{e}".red
 	end
 else

From 50d88fc970b207b4f4aebc389d872e244091a0ab Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 22 Feb 2017 19:23:55 +0100
Subject: [PATCH 395/531] fix translation test

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index cf0cc28..94fa9c3 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -538,7 +538,7 @@ class Cli
 
 		File.open(file, 'w') {|f| f << data.to_yaml}
 
-		puts "#{I18n.t('export.valid', file)}".green
+		puts "#{I18n.t('form.export.valid', file)}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #17: #{e}".red
 	end
diff --git a/test/test_translate.rb b/test/test_translate.rb
index fe9a575..55f09df 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|
+			`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 c841123ac0549f0ab6310da55623dd470a48944f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 22 Feb 2017 21:51:06 +0100
Subject: [PATCH 396/531] feat: add option to set default wallet

---
 bin/mpw-config    |  7 ++++++-
 i18n/en.yml       |  3 +++
 i18n/fr.yml       |  3 +++
 lib/mpw/cli.rb    | 19 +++++++++++++------
 lib/mpw/config.rb | 41 +++++++++++++++++++----------------------
 5 files changed, 44 insertions(+), 29 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index fb6b372..74309ce 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -34,6 +34,10 @@ OptionParser.new do |opts|
 		options[:config] = config
 	end
 
+	opts.on('-d', '--default-wallet NAME', I18n.t('option.default_wallet')) do |default_wallet|
+		values[:default_wallet] = default_wallet
+	end
+
 	opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
 		values[:gpg_exe] = gpg_exe
 	end
@@ -64,12 +68,13 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil)
 
-if not options[:init].nil?
+if options.has_key?(:init)
 	cli.setup(values)
 	cli.load_config
 	cli.get_wallet
 	cli.setup_gpg_key(values[:gpg_key])
 	cli.setup_wallet_config
 else
+	cli.load_config
 	cli.set_config(values)
 end
diff --git a/i18n/en.yml b/i18n/en.yml
index 536a5e4..6dafacf 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -54,6 +54,7 @@ en:
     alpha: "Use letter to generate a password"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
+    default_wallet: "Specify the default wallet to use"
     delete_gpg_key: "Delete the wallet's share with an other GPG key"
     export: "Export a wallet in an yaml file"
     file_export: "Specify the file where export data"
@@ -128,6 +129,8 @@ en:
       file_not_exist: "The import file doesn't exist!"
       valid: "The import is succesfull!"
       not_valid: "No data to import!"
+    set_config:
+      valid: "The config file has been edited!"
     setup_config:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...) [default=%{lang}]: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 09f060a..a20e58f 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -54,6 +54,7 @@ fr:
     alpha: "Utilise des lettres dans la génération d'un mot de passe"
     config: "Spécifie le fichier de configuration à utiliser"
     clipboard: "Désactive la fonction presse papier"
+    default_wallet: "Spécifie le porte-feuille à utiliser par défaut"
     delete_gpg_key: "Supprime le partage le portefeuille avec une autre clé GPG"
     export: "Exporte un portefeuille dans un fichier yaml"
     file_export: "Spécifie le fichier où exporter les données"
@@ -128,6 +129,8 @@ fr:
       file_not_exist: "Le fichier d'import n'existe pas"
       valid: "L'import est un succès!"
       not_valid: "Aucune donnée à importer!"
+    set_config:
+      valid: "Le fichier de configuration a bien été modifié!"
     setup_config:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 94fa9c3..0a5dcde 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -40,12 +40,15 @@ class Cli
 	# Change a parameter int the config after init
 	# @args: options -> param to change
 	def set_config(options)
-		gpg_key    = options[:gpg_key]    || @config.key
-		lang       = options[:lang]       || @config.lang
-		wallet_dir = options[:wallet_dir] || @config.wallet_dir
-		gpg_exe    = options[:gpg_exe]    || @config.gpg_exe
+		gpg_key        = options[:gpg_key]        || @config.key
+		lang           = options[:lang]           || @config.lang
+		wallet_dir     = options[:wallet_dir]     || @config.wallet_dir
+		default_wallet = options[:default_wallet] || @config.default_wallet
+		gpg_exe        = options[:gpg_exe]        || @config.gpg_exe
 
-		@config.setup(gpg_key, lang, wallet_dir, gpg_exe)
+		@config.setup(gpg_key, lang, wallet_dir, default_wallet, gpg_exe)
+
+		puts "#{I18n.t('form.set_config.valid')}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #15: #{e}".red
 		exit 2
@@ -341,7 +344,9 @@ class Cli
 	def list_wallet
 		wallets = []
 		Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f| 
-			wallets << File.basename(f, '.mpw')
+			wallet = File.basename(f, '.mpw')
+			wallet += ' *'.green if wallet == @config.default_wallet
+			wallets << wallet
 		end
 
 		table_list('wallets', wallets)
@@ -355,6 +360,8 @@ class Cli
 
 			if wallets.length == 1
 				@wallet_file = wallets[0]
+			elsif not @config.default_wallet.to_s.empty?
+				@wallet_file = "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
 			else
 				@wallet_file = "#{@config.wallet_dir}/default.mpw"
 			end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 867ff35..c1e883c 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -29,6 +29,7 @@ class Config
 	attr_accessor :key
 	attr_accessor :lang
 	attr_accessor :config_dir
+	attr_accessor :default_wallet
 	attr_accessor :wallet_dir
 	attr_accessor :gpg_exe
 
@@ -45,31 +46,28 @@ class Config
 			@config_dir = "#{Dir.home}/.config/mpw"
 		end
 		
-		if @config_file.nil? or @config_file.empty?
-			@config_file = "#{@config_dir}/mpw.cfg"
-		end
+		@config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? or @config_file.empty?
 	end
 
 	# Create a new config file
 	# @args: key -> the gpg key to encrypt
 	#        lang -> the software language
-	#        wallet_dir -> the  directory where are the wallets password
-	#        gpg_exe -> the  path of gpg executable
+	#        wallet_dir -> the directory where are the wallets password
+	#        default_wallet -> the default wallet
+	#        gpg_exe -> the path of gpg executable
 	# @rtrn: true if le config file is create
-	def setup(key, lang, wallet_dir, gpg_exe)
+	def setup(key, lang, wallet_dir, default_wallet, gpg_exe)
 		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 			raise I18n.t('error.config.key_bad_format')
 		end
 
-		if wallet_dir.to_s.empty?
-			wallet_dir = "#{@config_dir}/wallets"
-		end
-
-		config = { 'key'        => key,
-		           'lang'       => lang,
-		           'wallet_dir' => wallet_dir,
-		           'gpg_exe'    => gpg_exe,
-		         }
+		wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
+		config     = { 'key'            => key,
+		               'lang'           => lang,
+		               'wallet_dir'     => wallet_dir,
+		               'default_wallet' => default_wallet,
+		               'gpg_exe'        => gpg_exe,
+		             }
 
 		FileUtils.mkdir_p(@config_dir, mode: 0700)
 		FileUtils.mkdir_p(wallet_dir,  mode: 0700)
@@ -77,7 +75,6 @@ class Config
 		File.open(@config_file, 'w') do |file|
 			file << config.to_yaml
 		end
-		
 	rescue Exception => e 
 		raise "#{I18n.t('error.config.write')}\n#{e}"
 	end
@@ -116,16 +113,16 @@ class Config
 
 	# Load the config file
 	def load_config
-		config      = YAML::load_file(@config_file)
-		@key        = config['key']
-		@lang       = config['lang']
-		@wallet_dir = config['wallet_dir']
-		@gpg_exe    = config['gpg_exe']
+		config          = YAML::load_file(@config_file)
+		@key            = config['key']
+		@lang           = config['lang']
+		@wallet_dir     = config['wallet_dir']
+		@default_wallet = config['default_wallet']
+		@gpg_exe        = config['gpg_exe']
 
 		raise if @key.empty? or @wallet_dir.empty?
 			
 		I18n.locale = @lang.to_sym
-
 	rescue Exception => e
 		raise "#{I18n.t('error.config.load')}\n#{e}"
 	end

From 31574751ffd58ffb048043b6bcde051f593e51c5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 22 Feb 2017 21:59:49 +0100
Subject: [PATCH 397/531] fix bug after add default wallet

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 0a5dcde..12bbe22 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -61,7 +61,7 @@ class Cli
 
 		I18n.locale = lang.to_sym
 
-		@config.setup(options[:gpg_key], lang, options[:wallet_dir], options[:gpg_exe])
+		@config.setup(options[:gpg_key], lang, options[:wallet_dir], options[:default_wallet], options[:gpg_exe])
 
 		load_config
 
diff --git a/test/test_config.rb b/test/test_config.rb
index 3de438c..5e4e721 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -27,7 +27,7 @@ class TestConfig < Test::Unit::TestCase
 		       }
 
 		@config = MPW::Config.new
-		@config.setup(data[:key], data[:lang], data[:wallet_dir], data[:gpg_exe])
+		@config.setup(data[:key], data[:lang], data[:wallet_dir], nil, data[:gpg_exe])
 		@config.load_config
 
 		data.each do |k,v|

From 000bc3aaa55c807b5e34cf7594491eca10c9e678 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sat, 25 Feb 2017 18:43:30 +0100
Subject: [PATCH 398/531] add config option for password parameters

---
 bin/mpw-config    | 30 +++++++++++++++++++++++++++++-
 i18n/en.yml       |  3 +++
 i18n/fr.yml       |  3 +++
 lib/mpw/cli.rb    | 10 ++--------
 lib/mpw/config.rb | 34 ++++++++++++++++++++++++++--------
 lib/mpw/mpw.rb    |  6 +++---
 6 files changed, 66 insertions(+), 20 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 74309ce..1034f04 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -56,13 +56,41 @@ OptionParser.new do |opts|
 		values[:gpg_key] = gpg_key
 	end
 
-	opts.on('-l', '--lang LANG', I18n.t('option.lang')) do |lang|
+	opts.on('-L', '--lang LANG', I18n.t('option.lang')) do |lang|
 		values[:lang] = lang
 	end
 
 	opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
 		values[:wallet_dir] = wallet_dir
 	end
+
+	opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
+		values[:pwd_length] = length.to_i
+	end
+
+	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
+		values[:pwd_numeric] = true
+	end
+
+	opts.on('-N', '--disable-numeric', I18n.t('option.disable_numeric')) do
+		values[:pwd_numeric] = false
+	end
+
+	opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
+		values[:pwd_special] = true
+	end
+
+	opts.on('-S', '--disable_special-chars', I18n.t('option.special_chars')) do
+		values[:pwd_special] = false
+	end
+
+	opts.on('-a', '--alpha', I18n.t('option.alpha')) do
+		values[:pwd_alpha] = true
+	end
+
+	opts.on('-A', '--disable-alpha', I18n.t('option.disable_alpha')) do
+		values[:pwd_alpha] = false
+	end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/i18n/en.yml b/i18n/en.yml
index 6dafacf..5ee9868 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -56,6 +56,9 @@ en:
     clipboard: "Disable the clipboard feature"
     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"
+    disable_special_chars: "Don't use special char to generate a password"
     export: "Export a wallet in an yaml file"
     file_export: "Specify the file where export data"
     file_import: "Specify the file to import"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index a20e58f..d9bd279 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -56,6 +56,9 @@ fr:
     clipboard: "Désactive la fonction presse papier"
     default_wallet: "Spécifie le porte-feuille à utiliser par défaut"
     delete_gpg_key: "Supprime le partage le portefeuille avec une autre clé GPG"
+    disable_alpha: "Désactive l'utilisation des lettres dans la génération d'un mot de passe"
+    disable_numeric: "Désactive l'utilisation des chiffre dans la génération d'un mot de passe"
+    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_import: "Spécifie le fichier à importer"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 12bbe22..97e7e56 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -40,13 +40,7 @@ class Cli
 	# Change a parameter int the config after init
 	# @args: options -> param to change
 	def set_config(options)
-		gpg_key        = options[:gpg_key]        || @config.key
-		lang           = options[:lang]           || @config.lang
-		wallet_dir     = options[:wallet_dir]     || @config.wallet_dir
-		default_wallet = options[:default_wallet] || @config.default_wallet
-		gpg_exe        = options[:gpg_exe]        || @config.gpg_exe
-
-		@config.setup(gpg_key, lang, wallet_dir, default_wallet, gpg_exe)
+		@config.setup(options)
 
 		puts "#{I18n.t('form.set_config.valid')}".green
 	rescue Exception => e
@@ -433,7 +427,7 @@ class Cli
 		item    = Item.new(options)
 
 		if password
-			options[:password] = MPW::password(length: 24)
+			options[:password] = MPW::password(@config.password)
 		end
 		
 		@mpw.add(item)
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index c1e883c..94b9eb8 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -32,6 +32,7 @@ class Config
 	attr_accessor :default_wallet
 	attr_accessor :wallet_dir
 	attr_accessor :gpg_exe
+	attr_accessor :password
 
 	# Constructor
 	# @args: config_file -> the specify config file
@@ -50,23 +51,39 @@ class Config
 	end
 
 	# Create a new config file
-	# @args: key -> the gpg key to encrypt
-	#        lang -> the software language
-	#        wallet_dir -> the directory where are the wallets password
-	#        default_wallet -> the default wallet
-	#        gpg_exe -> the path of gpg executable
+	# @args: options -> hash with values
 	# @rtrn: true if le config file is create
-	def setup(key, lang, wallet_dir, default_wallet, gpg_exe)
-		if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+	def setup(options)
+		gpg_key        = options[:gpg_key]        || @key
+		lang           = options[:lang]           || @lang
+		wallet_dir     = options[:wallet_dir]     || @wallet_dir
+		default_wallet = options[:default_wallet] || @default_wallet
+		gpg_exe        = options[:gpg_exe]        || @gpg_exe
+		password       = { numeric: true,
+		                   alpha:   true,
+		                   special: false,
+		                   length:  16,
+		                 }
+
+		['numeric', 'special', 'alpha', 'length'].each do |k|
+			if options.has_key?("pwd_#{k}".to_sym)
+				password[k.to_sym] = options["pwd_#{k}".to_sym]
+			elsif not @password.nil? and @password.has_key?(k.to_sym)
+				password[k.to_sym] = @password[k.to_sym]
+			end
+		end
+
+		if not gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
 			raise I18n.t('error.config.key_bad_format')
 		end
 
 		wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
-		config     = { 'key'            => key,
+		config     = { 'key'            => gpg_key,
 		               'lang'           => lang,
 		               'wallet_dir'     => wallet_dir,
 		               'default_wallet' => default_wallet,
 		               'gpg_exe'        => gpg_exe,
+		               'password'       => password,
 		             }
 
 		FileUtils.mkdir_p(@config_dir, mode: 0700)
@@ -119,6 +136,7 @@ class Config
 		@wallet_dir     = config['wallet_dir']
 		@default_wallet = config['default_wallet']
 		@gpg_exe        = config['gpg_exe']
+		@password       = config['password']
 
 		raise if @key.empty? or @wallet_dir.empty?
 			
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index c5023e6..761127c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -431,9 +431,9 @@ class MPW
 		end
 
 		chars = []
-		chars += [*('!'..'?')] - [*('0'..'9')]        if options.include?(:special)
-		chars += [*('A'..'Z'),*('a'..'z')]            if options.include?(:alpha)
-		chars += [*('0'..'9')]                        if options.include?(:numeric)
+		chars += [*('!'..'?')] - [*('0'..'9')]        if options[:special]
+		chars += [*('A'..'Z'),*('a'..'z')]            if options[:alpha]
+		chars += [*('0'..'9')]                        if options[:numeric]
 		chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
 
 		result = ''

From cedfe9cef72d94da87e556f1aadf69d46815553a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 09:35:46 +0100
Subject: [PATCH 399/531] fix bug encrypt with share key

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index c5023e6..3f9986f 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -206,12 +206,16 @@ class MPW
 		end
 
 		@keys[key] = data
+		@password.each_keys { |id| set_password(id, get_password(id)) }
+		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 
 	# Delete a public key
 	# args: key ->  public key to delete
 	def delete_key(key)
 		@keys.delete(key)
+		@password.each_keys { |id| set_password(id, get_password(id)) }
+		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 
 	# Set config

From a737ee94d1b1a1666235ea82628883a6b22e0b93 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 09:45:19 +0100
Subject: [PATCH 400/531] add unit test

---
 test/test_config.rb | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/test/test_config.rb b/test/test_config.rb
index 5e4e721..9d4b930 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -27,7 +27,7 @@ class TestConfig < Test::Unit::TestCase
 		       }
 
 		@config = MPW::Config.new
-		@config.setup(data[:key], data[:lang], data[:wallet_dir], nil, data[:gpg_exe])
+		@config.setup(data)
 		@config.load_config
 
 		data.each do |k,v|
@@ -37,4 +37,28 @@ class TestConfig < Test::Unit::TestCase
 		@config.setup_gpg_key('password', 'test@example.com', 2048)
 		assert(@config.check_gpg_key?)
 	end
+
+	def test_01_password
+		data = { password: { alpha: false,
+		                     numeric: false,
+		                     special: true,
+		                     length: 32,
+		                   }
+		       }
+
+		@config.load_config
+
+		assert_equal(@config.password[:length], 16)
+		assert(@config.password[:alpha])
+		assert(@config.password[:numeric])
+		assert(!@config.password[:special])
+
+		@config.setup(data)
+		@config.load_config
+
+		assert_equal(@config.password[:length], 32)
+		assert(!@config.password[:alpha])
+		assert(!@config.password[:numeric])
+		assert(@config.password[:special])
+	end
 end

From 980adacc2c861cfe8faf298f9aab15783df9d892 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 21:17:35 +0100
Subject: [PATCH 401/531] fix missing change

---
 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 97e7e56..7bce420 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -51,11 +51,11 @@ class Cli
 	# Create a new config file
 	# @args: options -> set param
 	def setup(options)
-		lang = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+		options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
 		I18n.locale = lang.to_sym
 
-		@config.setup(options[:gpg_key], lang, options[:wallet_dir], options[:default_wallet], options[:gpg_exe])
+		@config.setup(options)
 
 		load_config
 

From e486be5ba853b90355f91ff080a08172e9034cfb Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 21:22:25 +0100
Subject: [PATCH 402/531] fix bug

---
 lib/mpw/cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 7bce420..409bf5a 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -53,7 +53,7 @@ class Cli
 	def setup(options)
 		options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
-		I18n.locale = lang.to_sym
+		I18n.locale = options[:lang].to_sym
 
 		@config.setup(options)
 

From d6f7c78eb16345a5ff72d9d2b1aeb5ad90dceaf5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 21:26:23 +0100
Subject: [PATCH 403/531] fix test

---
 test/test_config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test_config.rb b/test/test_config.rb
index 9d4b930..3dd0d92 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -20,7 +20,7 @@ class TestConfig < Test::Unit::TestCase
 	end
 
 	def test_00_config
-		data = { key: 'test@example.com',
+		data = { gpg_key: 'test@example.com',
 		         lang: 'en',
 		         wallet_dir: '/tmp/test',
 		         gpg_exe: '',

From 5d267a486580ada4ec0da6581deda75e42e8f075 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 21:34:54 +0100
Subject: [PATCH 404/531] config: rename key to gpg_key

---
 lib/mpw/cli.rb           |  4 ++--
 lib/mpw/config.rb        | 14 +++++++-------
 templates/setup_form.erb |  9 ---------
 3 files changed, 9 insertions(+), 18 deletions(-)
 delete mode 100644 templates/setup_form.erb

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 409bf5a..3ad32be 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -98,7 +98,7 @@ class Cli
 
 		#wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
 
-		@mpw = MPW.new(@config.key, @wallet_file, @password, @config.gpg_exe)
+		@mpw = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe)
 		@mpw.read_data
 		@mpw.set_config(options)
 		@mpw.write_data
@@ -127,7 +127,7 @@ class Cli
 	def decrypt
 		if not defined?(@mpw)
 			@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
-			@mpw      = MPW.new(@config.key, @wallet_file, @password, @config.gpg_exe)
+			@mpw      = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe)
 		end
 
 		@mpw.read_data
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 94b9eb8..2cf5481 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -26,7 +26,7 @@ class Config
 	
 	attr_accessor :error_msg
 
-	attr_accessor :key
+	attr_accessor :gpg_key
 	attr_accessor :lang
 	attr_accessor :config_dir
 	attr_accessor :default_wallet
@@ -54,7 +54,7 @@ class Config
 	# @args: options -> hash with values
 	# @rtrn: true if le config file is create
 	def setup(options)
-		gpg_key        = options[:gpg_key]        || @key
+		gpg_key        = options[:gpg_key]        || @gpg_key
 		lang           = options[:lang]           || @lang
 		wallet_dir     = options[:wallet_dir]     || @wallet_dir
 		default_wallet = options[:default_wallet] || @default_wallet
@@ -78,7 +78,7 @@ class Config
 		end
 
 		wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
-		config     = { 'key'            => gpg_key,
+		config     = { 'gpg_key'        => gpg_key,
 		               'lang'           => lang,
 		               'wallet_dir'     => wallet_dir,
 		               'default_wallet' => default_wallet,
@@ -117,7 +117,7 @@ class Config
 		param << "Subkey-Length: #{length}\n"
 		param << "Name-Real: #{name}\n"
 		param << "Name-Comment: #{name}\n"
-		param << "Name-Email: #{@key}\n"
+		param << "Name-Email: #{@gpg_key}\n"
 		param << "Expire-Date: #{expire}\n"
 		param << "Passphrase: #{password}\n"
 		param << "</GnupgKeyParms>\n"
@@ -131,14 +131,14 @@ class Config
 	# Load the config file
 	def load_config
 		config          = YAML::load_file(@config_file)
-		@key            = config['key']
+		@gpg_key        = config['gpg_key']
 		@lang           = config['lang']
 		@wallet_dir     = config['wallet_dir']
 		@default_wallet = config['default_wallet']
 		@gpg_exe        = config['gpg_exe']
 		@password       = config['password']
 
-		raise if @key.empty? or @wallet_dir.empty?
+		raise if @gpg_key.empty? or @wallet_dir.empty?
 			
 		I18n.locale = @lang.to_sym
 	rescue Exception => e
@@ -149,7 +149,7 @@ class Config
 	# @rtrn: true if the key exist, else false
 	def check_gpg_key?
 		ctx = GPGME::Ctx.new
-		ctx.each_key(@key, true) do
+		ctx.each_key(@gpg_key, true) do
 			return true
 		end
 
diff --git a/templates/setup_form.erb b/templates/setup_form.erb
deleted file mode 100644
index f7e6d7b..0000000
--- a/templates/setup_form.erb
+++ /dev/null
@@ -1,9 +0,0 @@
----
-# <%= I18n.t('form.setup_config.lang') %>
-language: <%= @config.lang %>
-# <%= I18n.t('form.setup_config.gpg_key') %>
-gpg_key: <%= @config.key %>
-# <%= I18n.t('form.setup_config.wallet_dir') %>
-wallet_dir: <%= @config.config_dir %>
-# <%= I18n.t('form.setup_config.gpg_exe') %>
-gpg_exe: <%= @config.gpg_exe %>

From 21c3732ad371f70db839bd188b8379c7ab9cb905 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 21:39:33 +0100
Subject: [PATCH 405/531] fix test

---
 test/test_config.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/test_config.rb b/test/test_config.rb
index 3dd0d92..4d94fcd 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -46,6 +46,7 @@ class TestConfig < Test::Unit::TestCase
 		                   }
 		       }
 
+		@config = MPW::Config.new
 		@config.load_config
 
 		assert_equal(@config.password[:length], 16)

From cced11f6c4247b7779ae5b3126f6fbc823ab82e7 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 21:45:42 +0100
Subject: [PATCH 406/531] fix test

---
 test/test_config.rb | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/test/test_config.rb b/test/test_config.rb
index 4d94fcd..143b3ed 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -39,11 +39,10 @@ class TestConfig < Test::Unit::TestCase
 	end
 
 	def test_01_password
-		data = { password: { alpha: false,
-		                     numeric: false,
-		                     special: true,
-		                     length: 32,
-		                   }
+		data = { pwd_alpha: false,
+		         pwd_numeric: false,
+		         pwd_special: true,
+		         pwd_length: 32,
 		       }
 
 		@config = MPW::Config.new
@@ -57,7 +56,7 @@ class TestConfig < Test::Unit::TestCase
 		@config.setup(data)
 		@config.load_config
 
-		assert_equal(@config.password[:length], 32)
+		assert_equal(@config.password[:length], data[:pwd_length])
 		assert(!@config.password[:alpha])
 		assert(!@config.password[:numeric])
 		assert(@config.password[:special])

From e03614fb724481ce34cb72bf7de152973079cc83 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 09:35:46 +0100
Subject: [PATCH 407/531] fix bug encrypt with share key

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 761127c..f8ab415 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -206,12 +206,16 @@ class MPW
 		end
 
 		@keys[key] = data
+		@password.each_keys { |id| set_password(id, get_password(id)) }
+		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 
 	# Delete a public key
 	# args: key ->  public key to delete
 	def delete_key(key)
 		@keys.delete(key)
+		@password.each_keys { |id| set_password(id, get_password(id)) }
+		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 
 	# Set config

From da81f34e07cbe8dad6c9a683f37bb34977f61d11 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 22:07:18 +0100
Subject: [PATCH 408/531] fix add_key

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index f8ab415..bf03347 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -206,7 +206,7 @@ class MPW
 		end
 
 		@keys[key] = data
-		@password.each_keys { |id| set_password(id, get_password(id)) }
+		@passwords.each_keys { |id| set_password(id, get_password(id)) }
 		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 

From 52577425a6e16c4a60eb1e6662fc0ddb2a79215e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 26 Feb 2017 22:11:14 +0100
Subject: [PATCH 409/531] fix each_key

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index bf03347..1e86c08 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -206,16 +206,16 @@ class MPW
 		end
 
 		@keys[key] = data
-		@passwords.each_keys { |id| set_password(id, get_password(id)) }
-		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
+		@passwords.each_key { |id| set_password(id, get_password(id)) }
+		@otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 
 	# Delete a public key
 	# args: key ->  public key to delete
 	def delete_key(key)
 		@keys.delete(key)
-		@password.each_keys { |id| set_password(id, get_password(id)) }
-		@otp_keys.each_keys { |id| set_otp_key(id, get_otp_key(id)) }
+		@passwords.each_key { |id| set_password(id, get_password(id)) }
+		@otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
 	end
 
 	# Set config

From e4fb7800163f8643a8e64ec1a76fd79371d027a3 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 26 Feb 2017 23:11:07 +0100
Subject: [PATCH 410/531] fix minor bug when config password is empty

---
 lib/mpw/config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 2cf5481..7fbd778 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -136,7 +136,7 @@ class Config
 		@wallet_dir     = config['wallet_dir']
 		@default_wallet = config['default_wallet']
 		@gpg_exe        = config['gpg_exe']
-		@password       = config['password']
+		@password       = config['password'] || {}
 
 		raise if @gpg_key.empty? or @wallet_dir.empty?
 			

From 6b8dce2eff12bc4527fb2b4c2ff3242b947ec145 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 26 Feb 2017 23:12:49 +0100
Subject: [PATCH 411/531] add random password for update

---
 bin/mpw-update            |  6 +++++-
 lib/mpw/cli.rb            | 19 +++++++++----------
 templates/update_form.erb |  4 ++--
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/bin/mpw-update b/bin/mpw-update
index 301b876..221c632 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -52,6 +52,10 @@ OptionParser.new do |opts|
 		values[:pattern] = pattern
 	end
 
+	opts.on('-r', '--random', I18n.t('option.random_password')) do
+		options[:password] = true
+	end
+
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
@@ -63,4 +67,4 @@ cli    = MPW::Cli.new(config, options[:sync])
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.update(values)
+cli.update(options[:password], values)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 3ad32be..9e8040e 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -423,12 +423,9 @@ class Cli
 	# Form to add a new item
 	# @args: password -> generate a random password
 	def add(password=false)
-		options = text_editor('add_form', nil, password)	
-		item    = Item.new(options)
-
-		if password
-			options[:password] = MPW::password(@config.password)
-		end
+		options            = text_editor('add_form', nil, password)
+		item               = Item.new(options)
+		options[:password] = MPW::password(@config.password) if password
 		
 		@mpw.add(item)
 		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
@@ -442,8 +439,9 @@ class Cli
 	end
 
 	# Update an item
-	# @args: options -> the option to search
-	def update(options={})
+	# @args: password -> generate a random password
+	#        options -> the option to search
+	def update(password=false, options={})
 		items = @mpw.list(options)
 		
 		if items.length == 0
@@ -451,8 +449,9 @@ class Cli
 		else
 			table_items(items) if items.length > 1
 
-			item    = get_item(items)
-			options = text_editor('update_form', item)
+			item               = get_item(items)
+			options            = text_editor('update_form', item, password)
+		        options[:password] = MPW::password(@config.password) if password
 
 			item.update(options)
 			@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
diff --git a/templates/update_form.erb b/templates/update_form.erb
index b942cf5..e5363f8 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -2,9 +2,9 @@
 # <%= I18n.t('form.update_item.host') %>
 host: <%= item.host %>
 # <%= I18n.t('form.update_item.login') %>
-user: <%= item.user %>
+user: <%= item.user %><% if not password %>
 # <%= I18n.t('form.update_item.password') %>
-password: 
+password: <% end %>
 # <%= I18n.t('form.update_item.group') %>
 group: <%= item.group %>
 # <%= I18n.t('form.update_item.protocol') %>

From ba745ccc0c148da7902fb5ed4c4ea74f783bd74b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 2 Mar 2017 23:43:19 +0100
Subject: [PATCH 412/531] update README

---
 README.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 67 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 563c592..fa81df6 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
 [![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)
 
-
 mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg.org/) encrypted files.
 
 ## Features
@@ -11,6 +10,8 @@ mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg
  * generate OTP code
  * synchronize your passwords with SSH or FTP.
  * copy your login, password or otp in clipboard
+ * manage many wallets
+ * share a wallet with others GPG keys
 
 ## Install
 
@@ -20,17 +21,28 @@ apt install ruby ruby-dev xclip
 gem install mpw
 ```
 
+## How to use
+### First steps
 
-# How to use
-
-A simple mpw usage:
+Initialize your first wallet:
 ```
 mpw config --init user@host.com
+```
+
+Add your first item:
+```
 mpw add
-mpw copy
-mpw add
+```
+
+And list your items:
+```
 mpw list
 ```
+or search an item with
+```
+mpw list --pattern Da
+mpw list --group bank
+```
 
 Output:
 ```
@@ -47,3 +59,52 @@ Linux
   2  | linuxfr.org   | example   | https    |      |     | Da Linux French Site
 
 ```
+
+Copy a password, login or OTP code:
+```
+mpw copy -p linuxfr
+```
+
+Update an item:
+```
+mpw update -p linuxfr
+```
+
+Delete an item:
+```
+mpw delete -p linuxfr
+```
+
+### Manage wallets
+
+List all available wallets:
+```
+mpw wallet --list
+```
+
+Create an other wallet:
+```
+mpw config --wallet work --init user@host.com
+```
+
+List all GPG keys in wallet:
+```
+mpw wallet --list-keys [--wallet NAME]
+```
+
+Share with an other GPG key:
+```
+mpw wallet --add-gpg-key test42@localhost.com
+ or
+mpw wallet --add-gpg-key /path/to/file
+```
+
+Remove a GPG key:
+```
+mpw wallet --delete-gpg-key test42@localhost.com
+```
+
+Add synchronize:
+```
+mpw wallet --protocol ssh --host example.com --user test --path /remote/path --password
+```

From 55e46e1afadfaca42fad2e39456ed0067a221856 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 2 Mar 2017 23:47:24 +0100
Subject: [PATCH 413/531] convert changelog to markdown

---
 CHANGELOG    | 49 -------------------------------------------------
 CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 49 deletions(-)
 delete mode 100644 CHANGELOG
 create mode 100644 CHANGELOG.md

diff --git a/CHANGELOG b/CHANGELOG
deleted file mode 100644
index e154a0c..0000000
--- a/CHANGELOG
+++ /dev/null
@@ -1,49 +0,0 @@
-= CHANGELOG =
-== v4.0.0-beta1 ==
-
-* add manage share key with new interface
-
-== v4.0.0-beta ==
-
-* new interface with a table
-* new command line interface
-* use text editor for add or update an item
-* fix generate gpg key with RSA
-* several bugs fix
-* add unit tests
-
-== v3.2.1 ==
-
-* fix bug when add a new item
-
-== v3.2.0 ==
-
-* add support OTP
-* fix bug in synchronize
-* improve interface
-
-== v3.1.0 ==
-
-* add clipboard
-* can change gpg version
-* minor change in interface
-* several bugs fix
-
-== v3.0.0 ==
-
-* new storage format
-* new share system
-* remove MPW server
-
-== v2.0.0 ==
-
-* change format csv to yaml
-* easy install with gem
-* add sync with ftp and ssh
-* many improvement
-
-== v1.1.0 ==
-
-* Add sync with MPW Server
-* Add MPW Server
-* Fix minors bugs
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f00a846
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,49 @@
+# CHANGELOG
+## v4.0.0-beta1
+
+ * add manage share key with new interface
+
+## v4.0.0-beta
+
+ * new interface with a table
+ * new command line interface
+ * use text editor for add or update an item
+ * fix generate gpg key with RSA
+ * several bugs fix
+ * add unit tests
+
+## v3.2.1
+
+ * fix bug when add a new item
+
+## v3.2.0
+
+ * add support OTP
+ * fix bug in synchronize
+ * improve interface
+
+## v3.1.0
+
+ * add clipboard
+ * can change gpg version
+ * minor change in interface
+ * several bugs fix
+
+## v3.0.0
+
+ * new storage format
+ * new share system
+ * remove MPW server
+
+## v2.0.0
+
+ * change format csv to yaml
+ * easy install with gem
+ * add sync with ftp and ssh
+ * many improvement
+
+## v1.1.0
+
+ * Add sync with MPW Server
+ * Add MPW Server
+ * Fix minors bugs

From 8fb83bd3910f321ce34cbee6d4ed6e6ac9d59664 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Fri, 3 Mar 2017 21:57:21 +0100
Subject: [PATCH 414/531] fix message in export function

---
 lib/mpw/cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 9e8040e..1eb2b94 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -538,7 +538,7 @@ class Cli
 
 		File.open(file, 'w') {|f| f << data.to_yaml}
 
-		puts "#{I18n.t('form.export.valid', file)}".green
+		puts "#{I18n.t('form.export.valid', file: file)}".green
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #17: #{e}".red
 	end

From de4beba3dbb2f487c62f98f4a8380d4ad51d04fc Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Fri, 3 Mar 2017 22:00:15 +0100
Subject: [PATCH 415/531] export items with id

---
 lib/mpw/cli.rb | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 1eb2b94..90fe00d 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -517,23 +517,20 @@ class Cli
 		file  = 'export-mpw.yml' if file.to_s.empty?
 		items = @mpw.list(options)
 		data  = {}
-		i     = 1
 
 		items.each do |item|
-			data.merge!(i => { '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,
-			                 }
+			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,
+			                       }
 			            )
-
-			i += 1
 		end
 
 		File.open(file, 'w') {|f| f << data.to_yaml}
@@ -550,7 +547,6 @@ class Cli
 		raise I18n.t('form.import.file_not_exist') if not File.exist?(file)
 
 		YAML::load_file(file).each_value do |row|
-
 			item = Item.new(group:    row['group'],
 			                host:     row['host'],
 			                protocol: row['protocol'],

From 44c1c2b4764c87b6ff3513e47aadf9f59ccdd166 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Fri, 3 Mar 2017 22:42:30 +0100
Subject: [PATCH 416/531] update readme

---
 README.md | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index fa81df6..6b32418 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,11 @@ mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg
 
 ## Features
 
+ * generate random password
  * generate OTP code
- * synchronize your passwords with SSH or FTP.
  * copy your login, password or otp in clipboard
  * manage many wallets
+ * synchronize your passwords with SSH or FTP.
  * share a wallet with others GPG keys
 
 ## Install
@@ -108,3 +109,39 @@ Add synchronize:
 ```
 mpw wallet --protocol ssh --host example.com --user test --path /remote/path --password
 ```
+
+### Export and import data
+
+You can export your data in yaml file with your passwords in clear text:
+```
+mpw export --file export.yml
+```
+
+Import data from an yaml file:
+```
+mpw import --file import.yml
+```
+
+Example yaml file for mpw:
+
+```
+---
+1:
+  host: bank.com
+  user: 123456
+  group: Bank
+  password: secret
+  protocol: https
+  port: 
+  otp_key: 1afg34
+  comment: 
+2:
+  host: linuxfr.org
+  user: example
+  group: 
+  password: 'complex %- password'
+  protocol: https
+  port: 
+  otp_key: 
+  comment: Da Linux French Site
+```

From 58215d4e01ab133e8324c21be9b5d0220e88ddf1 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Fri, 3 Mar 2017 22:57:03 +0100
Subject: [PATCH 417/531] ignore *.gem

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index b844b14..7ae6fcf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 Gemfile.lock
+*.gem

From ffedd5b88bae5ecd214d2a1a111cd2f2d7d782e4 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 8 Mar 2017 21:23:15 +0100
Subject: [PATCH 418/531] fix error when delete an item with sync

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 1e86c08..fe46108 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -319,6 +319,8 @@ class MPW
 
 		if not remote.to_s.empty?
 			@data.each do |item|
+				next if item.empty?
+
 				update = false
 
 				remote.list.each do |r|

From 5a1e0f6aa8a1f07e0c5fb54be8d6d1760cacc190 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 9 Mar 2017 21:06:05 +0100
Subject: [PATCH 419/531] release version 4.0.0

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f00a846..452ac98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,11 @@
 # CHANGELOG
+## v4.0.0
+
+ * 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
 
  * add manage share key with new interface
diff --git a/README.md b/README.md
index 6b32418..7ac7d47 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # MPW: Manage your passwords!
-[![Version](https://img.shields.io/badge/latest_version-4.0.0--beta1-yellow.svg)](https://github.com/nishiki/manage-password/releases)
+[![Version](https://img.shields.io/badge/latest_version-4.0.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 817c97b..fcdb2e1 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0.0-beta1
+4.0.0

From 2e6d111b69f2153afdf34c39503a15229a91ec30 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 27 Mar 2017 21:27:50 +0200
Subject: [PATCH 420/531] begind version 4.1.0

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

diff --git a/VERSION b/VERSION
index fcdb2e1..4aa925d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0.0
+4.1.0-dev

From 871b1dcbed1490d47b8bab40cd22e12aae6eb879 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 27 Mar 2017 21:47:43 +0200
Subject: [PATCH 421/531] remove all sync

---
 README.md           |   6 ---
 bin/mpw-add         |   9 +---
 bin/mpw-copy        |   7 +--
 bin/mpw-delete      |  11 ++---
 bin/mpw-export      |  11 ++---
 bin/mpw-import      |   9 +---
 bin/mpw-list        |  11 ++---
 bin/mpw-update      |  11 ++---
 bin/mpw-wallet      |  34 +-------------
 i18n/en.yml         |  26 -----------
 i18n/fr.yml         |  26 -----------
 lib/mpw/cli.rb      |  32 +------------
 lib/mpw/item.rb     |   7 ---
 lib/mpw/mpw.rb      | 106 --------------------------------------------
 lib/mpw/sync/ftp.rb |  68 ----------------------------
 lib/mpw/sync/ssh.rb |  67 ----------------------------
 16 files changed, 20 insertions(+), 421 deletions(-)
 delete mode 100644 lib/mpw/sync/ftp.rb
 delete mode 100644 lib/mpw/sync/ssh.rb

diff --git a/README.md b/README.md
index 7ac7d47..fd2534a 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,6 @@ mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg
  * generate OTP code
  * copy your login, password or otp in clipboard
  * manage many wallets
- * synchronize your passwords with SSH or FTP.
  * share a wallet with others GPG keys
 
 ## Install
@@ -105,11 +104,6 @@ Remove a GPG key:
 mpw wallet --delete-gpg-key test42@localhost.com
 ```
 
-Add synchronize:
-```
-mpw wallet --protocol ssh --host example.com --user test --path /remote/path --password
-```
-
 ### Export and import data
 
 You can export your data in yaml file with your passwords in clear text:
diff --git a/bin/mpw-add b/bin/mpw-add
index 5322cc1..fc688a4 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -24,8 +24,7 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = true
+options = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
@@ -39,10 +38,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-r', '--random', I18n.t('option.random_password')) do
 		options[:password] = true
 	end
@@ -53,7 +48,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-copy b/bin/mpw-copy
index 0a14ed7..1e5804b 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -25,7 +25,6 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options             = {}
-options[:sync]      = true
 options[:clipboard] = true
 values              = {}
 
@@ -49,10 +48,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
@@ -63,7 +58,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-delete b/bin/mpw-delete
index 2d79e6a..53c8074 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -24,9 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = true
-values         = {}
+options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
@@ -44,10 +43,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
@@ -58,7 +53,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-export b/bin/mpw-export
index f891873..7913040 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -24,9 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = true
-values         = {}
+options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -48,10 +47,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
@@ -62,7 +57,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-import b/bin/mpw-import
index c740252..a827783 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -24,8 +24,7 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = true
+options = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
@@ -43,17 +42,13 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-list b/bin/mpw-list
index 9d18b0d..3ebc2b8 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -24,9 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = true
-values         = {}
+options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
@@ -48,17 +47,13 @@ OptionParser.new do |opts|
 		values[:pattern] = pattern
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-update b/bin/mpw-update
index 221c632..fd207c5 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -24,9 +24,8 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options        = {}
-options[:sync] = true
-values         = {}
+options = {}
+values  = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
@@ -44,10 +43,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
 	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
 		values[:pattern] = pattern
 	end
@@ -62,7 +57,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 cli.get_wallet(options[:wallet])
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index fbf0389..56e96ba 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -25,9 +25,7 @@ require 'mpw/cli'
 # --------------------------------------------------------- #
 
 options          = {}
-options[:sync]   = {}
 options[:delete] = false
-values           = {}
 
 OptionParser.new do |opts|
 	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
@@ -50,10 +48,6 @@ OptionParser.new do |opts|
 		exit 0
 	end
 
-	opts.on('--host NAME', I18n.t('option.host')) do |host|
-		values[:host] = host
-	end
-
 	opts.on('-l', '--list', I18n.t('option.list')) do
 		options[:list] = true
 	end
@@ -62,37 +56,13 @@ OptionParser.new do |opts|
 		options[:list_keys] = true
 	end
 
-	opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
-		options[:sync] = false
-	end
-
-	opts.on('--password', I18n.t('option.password')) do
-		values[:password] = true
-	end
-
-	opts.on('--path PATH', I18n.t('option.path')) do |path|
-		values[:path] = path
-	end
-
-	opts.on('--port NUMBER', I18n.t('option.port')) do |port|
-		values[:port] = port
-	end
-
-	opts.on('--protocol NAME', I18n.t('option.protocol')) do |protocol|
-		values[:protocol] = protocol
-	end
-
-	opts.on('--user NAME', I18n.t('option.user')) do |user|
-		values[:user] = user
-	end
-
 	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
 		options[:wallet] = wallet
 	end
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, options[:sync])
+cli    = MPW::Cli.new(config)
 
 cli.load_config
 
@@ -110,7 +80,5 @@ else
 		else
 			cli.add_key(options[:gpg_key])
 		end
-	elsif not values.empty?
-		cli.setup_wallet_config(values)
 	end
 end
diff --git a/i18n/en.yml b/i18n/en.yml
index 5ee9868..162f6b3 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -23,15 +23,6 @@ en:
     import: "Can't import, unable to read %{file}!"
     update:
       host_empty: "You must define a host!"
-    sync: 
-      general: "An error has appeared during the sync"
-      connection: "Connection fail!"
-      communication: "A communication problem with the server is appeared!"
-      download: "Can't download the file!"
-      not_authorized: "You haven't the access to remote file!"
-      upload: "Can't upload the file on the server!"
-      unknown: "An unknown error is occured!"
-      unknown_type: "The sync type is unknown"
 
   warning:
     select: 'Your choice is not a valid element!'
@@ -68,7 +59,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 the server for the synchronization"
     init: "Initialize mpw"
     import: "Import item since a yaml file"
     key: "Specify the key name"
@@ -76,13 +66,8 @@ en:
     length: "Size of the password"
     list: "List the wallets"
     list_keys: "List the GPG keys in wallet"
-    no_sync: "Disable synchronization with the server"
     numeric: "Use number to generate a password"
-    password: "Change the password for the synchronization"
-    path: "Specify the remote path"
     pattern: "Given search pattern"
-    port: "Specify the connection port"
-    protocol: "Specify the protocol for the connection"
     random_password: "Generate a random password"
     setup: "Create a new configuration file" 
     setup_wallet: "Create a new configuration file for a wallet"
@@ -90,7 +75,6 @@ en:
     show: "Search and show the items"
     show_all: "List all items"
     usage: "Usage"
-    user: "Specify the user for the connection"
     wallet: "Specify a wallet to use"
     wallet_dir: "Set the wallets folder"
 
@@ -141,16 +125,6 @@ en:
       gpg_exe: "Enter the executable GPG path (optional): "
       wallet_dir: "Enter the wallets's folder path [default=%{home}/wallets]: "
       valid: "The config file has been created!"
-    setup_wallet:
-      password: "Sync password: " 
-      title: "Wallet setup"
-      sync_type: "Synchronization type (ssh, ftp): "
-      sync_host: "Synchronization server: "
-      sync_port: "Port of the synchronization server: "
-      sync_user: "Username for the synchronization: "
-      sync_pwd: "Password for the synchronization: "
-      sync_path: "File path for the synchronization : "
-      valid: "The wallet config file has been created!"
     setup_gpg_key:
       title: "Setup a GPG key"
       ask: "Do you want create your GPG key ? (Y/n)"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index d9bd279..b2e3db4 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -23,15 +23,6 @@ fr:
     import: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
     update:
       host_empty: "Vous devez définir un host!"
-    sync: 
-      general: "Une erreur est survenue durant la synchronisation"
-      connection: "La connexion n'a pu être établie!"
-      communication: "Un problème de communication avec le serveur est apparu!"
-      download: "Impossible de télécharger le fichier!"
-      not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
-      upload: "Impossible d'envoyer le fichier sur le serveur!"
-      unknown: "Une erreur inconnue est survenue!"
-      unknown_type: "Le type de synchronisation est inconnu"
 
   warning:
     select: "Votre choix n'est pas un élément valide!"
@@ -68,7 +59,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 serveur pour la synchronisation"
     import: "Importe des éléments depuis un fichier yaml"
     init: "Initialise mpw"
     key: "Spécifie le nom d'une clé"
@@ -76,13 +66,8 @@ fr:
     length: "Taille du mot de passe"
     list: "Liste les portefeuilles"
     list_keys: "Liste les clés GPG dans le portefeuille"
-    no_sync: "Désactive la synchronisation avec le serveur"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
-    password: "Changer le mot de passe de connexion"
-    path: "Spécifie le chemin distant"
     pattern: "Motif de donnée à chercher"
-    port: "Spécifie le port de connexion"
-    protocol: "Spécifie le protocol utilisé pour la 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"
@@ -90,7 +75,6 @@ fr:
     show: "Recherche et affiche les éléments"
     show_all: "Liste tous les éléments"
     usage: "Utilisation"
-    user: "Spécifie l'identifiant de connection"
     wallet: "Spécifie le portefeuille à utiliser"
     wallet_dir: "Spécifie le répertoire des portefeuilles"
 
@@ -141,16 +125,6 @@ fr:
       gpg_exe: "Entrez le chemin de l'exécutable GPG (optionnel): "
       wallet_dir: "Entrez le chemin du répertoire qui contiendra les porte-feuilles de mot de passe [défaut=%{home}/wallets]: "
       valid: "Le fichier de configuration a bien été créé!"
-    setup_wallet:
-      password: "Mot de passe de synchronisation: "
-      title: "Configuration du porte-feuille"
-      sync_type: "Type de synchronisation (ssh, ftp): "
-      sync_host: "Serveur: "
-      sync_port: "Port: "
-      sync_user: "Utilisateur: "
-      sync_pwd: "Mot de passe: "
-      sync_path: "Chemin du fichier: "
-      valid: "Le fichier de configuration du porte-feuille a bien été créé!"
     setup_gpg_key:
       title: "Configuration d'une nouvelle clé GPG"
       ask: "Voulez vous créer votre clé GPG ? (O/n)"
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 90fe00d..0eb294e 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -31,10 +31,8 @@ class Cli
 
 	# Constructor
 	# @args: config -> the config
-	#        sync -> boolean for sync or not
-	def initialize(config, sync=true)
-		@config    = config
-		@sync      = sync
+	def initialize(config)
+		@config = config
 	end
 
 	# Change a parameter int the config after init
@@ -89,26 +87,6 @@ class Cli
 		exit 2
 	end
 
-	# Setup wallet config for sync
-	# @args: options -> value to change
-	def setup_wallet_config(options={})
-		if not options[:password].nil?
-			options[:password] = ask(I18n.t('form.setup_wallet.password')) {|q| q.echo = false}
-		end
-
-		#wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
-
-		@mpw = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe)
-		@mpw.read_data
-		@mpw.set_config(options)
-		@mpw.write_data
-
-		puts "#{I18n.t('form.setup_wallet.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #10: #{e}".red
-		exit 2
-	end
-	
 	# List gpg keys in wallet
 	def list_keys
 		table_list('keys', @mpw.list_keys)
@@ -131,7 +109,6 @@ class Cli
 		end
 
 		@mpw.read_data
-		@mpw.sync if @sync
 	rescue Exception => e
 		puts "#{I18n.t('display.error')} #11: #{e}".red
 		exit 2
@@ -369,7 +346,6 @@ class Cli
 	def add_key(key)
 		@mpw.add_key(key)
 		@mpw.write_data
-		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_key.valid')}".green
 	rescue Exception => e
@@ -381,7 +357,6 @@ class Cli
 	def delete_key(key)
 		@mpw.delete_key(key)
 		@mpw.write_data
-		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.delete_key.valid')}".green
 	rescue Exception => e
@@ -431,7 +406,6 @@ class Cli
 		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
 		@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
 		@mpw.write_data
-		@mpw.sync(true) if @sync
 
 		puts "#{I18n.t('form.add_item.valid')}".green
 	rescue Exception => e
@@ -457,7 +431,6 @@ class Cli
 			@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
 			@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
 			@mpw.write_data
-			@mpw.sync(true) if @sync
 
 			puts "#{I18n.t('form.update_item.valid')}".green
 		end
@@ -484,7 +457,6 @@ class Cli
 	
 			item.delete
 			@mpw.write_data
-			@mpw.sync(true) if @sync
 	
 			puts "#{I18n.t('form.delete_item.valid')}".green
 		end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index d3783b9..a4f730b 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -30,7 +30,6 @@ class Item
 	attr_accessor :otp
 	attr_accessor :comment
 	attr_accessor :last_edit
-	attr_accessor :last_sync
 	attr_accessor :created
 
 	# Constructor
@@ -72,11 +71,6 @@ class Item
 		@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
 	end
 
-	# Update last_sync
-	def set_last_sync
-		@last_sync = Time.now.to_i
-	end
-
 	# Delete all data
 	def delete
 		@id        = nil
@@ -89,7 +83,6 @@ class Item
 		@comment   = nil
 		@created   = nil
 		@last_edit = nil
-		@last_sync = nil
 	end
 
 	def empty?
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index fe46108..8f4b42e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -229,7 +229,6 @@ class MPW
 		@config['user']      = options[:user]     if options.has_key?(:user)
 		@config['password']  = options[:password] if options.has_key?(:password)
 		@config['path']      = options[:path]     if options.has_key?(:path)
-		@config['last_sync'] = @config['last_sync'].nil? ? 0 : @config['last_sync']
 	end
 
 	# Add a new item
@@ -281,111 +280,6 @@ class MPW
 		return nil
 	end
 
-	# Get last sync
-	def get_last_sync
-		return @config['last_sync'].to_i
-	rescue
-		return 0
-	end
-
-	# Sync data with remote file
-	# @args: force -> force the sync
-	def sync(force=false)
-		return if @config.empty? or @config['protocol'].to_s.empty?
-		return if get_last_sync + 300 > Time.now.to_i and not force
-
-		tmp_file  = "#{@wallet_file}.sync"
-		
-		case @config['protocol']
-		when 'sftp', 'scp', 'ssh'
-			require "mpw/sync/ssh"
-			sync = SyncSSH.new(@config)
-		when 'ftp'
-			require 'mpw/sync/ftp'
-			sync = SyncFTP.new(@config)
-		else
-			raise I18n.t('error.sync.unknown_type')
-		end
-
-		sync.connect
-		sync.get(tmp_file)
-
-		remote = MPW.new(@key, tmp_file, @gpg_pass, @gpg_exe)
-		remote.read_data
-
-		File.unlink(tmp_file) if File.exist?(tmp_file)
-
-		return if remote.get_last_sync == @config['last_update']
-
-		if not remote.to_s.empty?
-			@data.each do |item|
-				next if item.empty?
-
-				update = false
-
-				remote.list.each do |r|
-					next if item.id != r.id
-
-					# Update item
-					if item.last_edit < r.last_edit
-						item.update(group:     r.group,
-						            host:      r.host,
-						            protocol:  r.protocol,
-						            user:      r.user,
-						            port:      r.port,
-						            comment:   r.comment
-						           )
-						set_password(item.id, remote.get_password(item.id))
-					end
-
-					r.delete
-					update = true
-
-					break
-				end
-
-				# Remove an old item
-				if not update and item.last_sync.to_i < get_last_sync and item.last_edit < get_last_sync
-					item.delete
-				end
-			end
-		end
-
-		# Add item
-		remote.list.each do |r|
-			next if r.last_edit <= get_last_sync
-
-			item = Item.new(id:        r.id,
-			                group:     r.group,
-			                host:      r.host,
-			                protocol:  r.protocol,
-			                user:      r.user,
-			                port:      r.port,
-			                comment:   r.comment,
-			                created:   r.created,
-			                last_edit: r.last_edit
-			               )
-
-			set_password(item.id, remote.get_password(item.id))
-			add(item)
-		end
-
-		remote = nil
-
-		@data.each do |item|
-			item.set_last_sync
-		end
-
-		@config['last_sync'] = Time.now.to_i
-
-		write_data
-		sync.update(@wallet_file)
-	rescue Exception => e
-		File.unlink(tmp_file) if File.exist?(tmp_file)
-
-		raise "#{I18n.t('error.sync.general')}\n#{e}"
-	end
-
 	# Set an opt key
 	# args: id -> the item id
 	#       key -> the new key
diff --git a/lib/mpw/sync/ftp.rb b/lib/mpw/sync/ftp.rb
deleted file mode 100644
index d64e629..0000000
--- a/lib/mpw/sync/ftp.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/ruby
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  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 'i18n'
-require 'net/ftp'
-		
-module MPW
-class FTP
-
-	# Constructor
-	# @args: config -> the config
-	def initialize(config)
-		@host      = config['host']
-		@user      = config['user']
-		@password  = config['password']
-		@path      = config['path']
-		@port      = config['port'].instance_of?(Integer) ? 22 : config['port']
-	end
-
-
-	# Connect to server
-	def connect
-		Net::FTP.open(@host) do |ftp|
-			ftp.login(@user, @password)
-			break
-		end
-	rescue Exception => e
-		raise "#{I18n.t('error.sync.connection')}\n#{e}"
-	end
-
-	# Get data on server
-	# @args: file_tmp -> the path where download the file
-	def get(file_tmp)
-		Net::FTP.open(@host) do |ftp|
-			ftp.login(@user, @password)
-			ftp.gettextfile(@path, file_tmp)
-		end
-	rescue Exception => e
-		raise "#{I18n.t('error.sync.download')}\n#{e}"
-	end
-
-	# Update the remote data
-	# @args: file_gpg -> the data to send on server
-	def update(file_gpg)
-		Net::FTP.open(@host) do |ftp|
-			ftp.login(@user, @password)
-			ftp.puttextfile(file_gpg, @path)
-		end
-	rescue Exception => e
-		raise "#{I18n.t('error.sync.upload')}\n#{e}"
-	end
-end
-end
diff --git a/lib/mpw/sync/ssh.rb b/lib/mpw/sync/ssh.rb
deleted file mode 100644
index 4471949..0000000
--- a/lib/mpw/sync/ssh.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/ruby
-# MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  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 'i18n'
-require 'net/ssh'
-require 'net/sftp'
-	
-module MPW
-class SyncSSH
-
-	# Constructor
-	# @args: config -> the config
-	def initialize(config)
-		@host      = config['host']
-		@user      = config['user']
-		@password  = config['password']
-		@path      = config['path']
-		@port      = config['port'].instance_of?(Integer) ? 22 : config['port']
-	end
-
-	# Connect to server
-	def connect
-		Net::SSH.start(@host, @user, password: @password, port: @port) do
-			break
-		end
-	rescue Exception => e
-		raise "#{I18n.t('error.sync.connection')}\n#{e}"
-	end
-
-	# Get data on server
-	# @args: file_tmp -> the path where download the file
-	def get(file_tmp)
-		Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
-			sftp.lstat(@path) do |response|
-				sftp.download!(@path, file_tmp) if response.ok?
-			end
-		end
-	rescue Exception => e
-		raise "#{I18n.t('error.sync.download')}\n#{e}"
-	end
-
-	# Update the remote data
-	# @args: file_gpg -> the data to send on server
-	def update(file_gpg)
-		Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
-			sftp.upload!(file_gpg, @path)
-		end
-	rescue Exception => e
-		raise "#{I18n.t('error.sync.upload')}\n#{e}"
-	end
-end
-end

From 7c72b2d90f7322bae290b71e91fb010743269a70 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 27 Mar 2017 22:18:17 +0200
Subject: [PATCH 422/531] change tab by 2 spaces

change tab by 2 spaces
---
 bin/mpw                |   44 +-
 bin/mpw-add            |   34 +-
 bin/mpw-config         |  116 ++---
 bin/mpw-copy           |   46 +-
 bin/mpw-delete         |   40 +-
 bin/mpw-export         |   46 +-
 bin/mpw-genpwd         |   40 +-
 bin/mpw-import         |   34 +-
 bin/mpw-list           |   40 +-
 bin/mpw-update         |   46 +-
 bin/mpw-wallet         |   78 +--
 lib/mpw/cli.rb         | 1028 ++++++++++++++++++++--------------------
 lib/mpw/config.rb      |  240 +++++-----
 lib/mpw/item.rb        |  144 +++---
 lib/mpw/mpw.rb         |  592 +++++++++++------------
 test/test_config.rb    |   92 ++--
 test/test_item.rb      |  280 +++++------
 test/test_mpw.rb       |  212 ++++-----
 test/test_translate.rb |   46 +-
 test/tests.rb          |    2 +-
 20 files changed, 1600 insertions(+), 1600 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index a69b568..3eb9fd9 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -30,7 +30,7 @@ require 'colorize'
 lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
 if defined?(I18n.enforce_available_locales)
-	I18n.enforce_available_locales = true
+  I18n.enforce_available_locales = true
 end
 
 I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
@@ -46,24 +46,24 @@ bin_dir = File.dirname(__FILE__)
 command = "#{bin_dir}/mpw-#{ARGV[0]}"
 
 if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
-	begin
-		Kernel.load(command)
-	rescue OptionParser::ParseError => e
-		puts "#{I18n.t('display.error')}: #{e}".red
-	end
+  begin
+    Kernel.load(command)
+  rescue OptionParser::ParseError => e
+    puts "#{I18n.t('display.error')}: #{e}".red
+  end
 else
-	puts "#{I18n.t('option.usage')}: mpw COMMAND [options]\n\n"
-	puts 'Commands:'
-	puts "  add       #{I18n.t('command.add')}"
-	puts "  config    #{I18n.t('command.config')}"
-	puts "  copy      #{I18n.t('command.copy')}"
-	puts "  delete    #{I18n.t('command.delete')}"
-	puts "  export    #{I18n.t('command.export')}"
-	puts "  genpwd    #{I18n.t('command.genpwd')}"
-	puts "  import    #{I18n.t('command.import')}"
-	puts "  list      #{I18n.t('command.list')}"
-	puts "  update    #{I18n.t('command.update')}"
-	puts "  wallet    #{I18n.t('command.wallet')}"
+  puts "#{I18n.t('option.usage')}: mpw COMMAND [options]\n\n"
+  puts 'Commands:'
+  puts "  add       #{I18n.t('command.add')}"
+  puts "  config    #{I18n.t('command.config')}"
+  puts "  copy      #{I18n.t('command.copy')}"
+  puts "  delete    #{I18n.t('command.delete')}"
+  puts "  export    #{I18n.t('command.export')}"
+  puts "  genpwd    #{I18n.t('command.genpwd')}"
+  puts "  import    #{I18n.t('command.import')}"
+  puts "  list      #{I18n.t('command.list')}"
+  puts "  update    #{I18n.t('command.update')}"
+  puts "  wallet    #{I18n.t('command.wallet')}"
 
-	exit 3
+  exit 3
 end
diff --git a/bin/mpw-add b/bin/mpw-add
index fc688a4..6376326 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -27,24 +27,24 @@ require 'mpw/cli'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-r', '--random', I18n.t('option.random_password')) do
-		options[:password] = true
-	end
+  opts.on('-r', '--random', I18n.t('option.random_password')) do
+    options[:password] = true
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-config b/bin/mpw-config
index 1034f04..14387d9 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -28,81 +28,81 @@ options = {}
 values  = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw config [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw config [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-d', '--default-wallet NAME', I18n.t('option.default_wallet')) do |default_wallet|
-		values[:default_wallet] = default_wallet
-	end
+  opts.on('-d', '--default-wallet NAME', I18n.t('option.default_wallet')) do |default_wallet|
+    values[:default_wallet] = default_wallet
+  end
 
-	opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
-		values[:gpg_exe] = gpg_exe
-	end
+  opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
+    values[:gpg_exe] = gpg_exe
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-i', '--init GPG_KEY', I18n.t('option.init')) do |gpg_key|
-		options[:init]   = true
-		values[:gpg_key] = gpg_key
-	end
+  opts.on('-i', '--init GPG_KEY', I18n.t('option.init')) do |gpg_key|
+    options[:init]   = true
+    values[:gpg_key] = gpg_key
+  end
 
-	opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
-		values[:gpg_key] = gpg_key
-	end
+  opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
+    values[:gpg_key] = gpg_key
+  end
 
-	opts.on('-L', '--lang LANG', I18n.t('option.lang')) do |lang|
-		values[:lang] = lang
-	end
+  opts.on('-L', '--lang LANG', I18n.t('option.lang')) do |lang|
+    values[:lang] = lang
+  end
 
-	opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
-		values[:wallet_dir] = wallet_dir
-	end
+  opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
+    values[:wallet_dir] = wallet_dir
+  end
 
-	opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
-		values[:pwd_length] = length.to_i
-	end
+  opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
+    values[:pwd_length] = length.to_i
+  end
 
-	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
-		values[:pwd_numeric] = true
-	end
+  opts.on('-n', '--numeric', I18n.t('option.numeric')) do
+    values[:pwd_numeric] = true
+  end
 
-	opts.on('-N', '--disable-numeric', I18n.t('option.disable_numeric')) do
-		values[:pwd_numeric] = false
-	end
+  opts.on('-N', '--disable-numeric', I18n.t('option.disable_numeric')) do
+    values[:pwd_numeric] = false
+  end
 
-	opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
-		values[:pwd_special] = true
-	end
+  opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
+    values[:pwd_special] = true
+  end
 
-	opts.on('-S', '--disable_special-chars', I18n.t('option.special_chars')) do
-		values[:pwd_special] = false
-	end
+  opts.on('-S', '--disable_special-chars', I18n.t('option.special_chars')) do
+    values[:pwd_special] = false
+  end
 
-	opts.on('-a', '--alpha', I18n.t('option.alpha')) do
-		values[:pwd_alpha] = true
-	end
+  opts.on('-a', '--alpha', I18n.t('option.alpha')) do
+    values[:pwd_alpha] = true
+  end
 
-	opts.on('-A', '--disable-alpha', I18n.t('option.disable_alpha')) do
-		values[:pwd_alpha] = false
-	end
+  opts.on('-A', '--disable-alpha', I18n.t('option.disable_alpha')) do
+    values[:pwd_alpha] = false
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil)
 
 if options.has_key?(:init)
-	cli.setup(values)
-	cli.load_config
-	cli.get_wallet
-	cli.setup_gpg_key(values[:gpg_key])
-	cli.setup_wallet_config
+  cli.setup(values)
+  cli.load_config
+  cli.get_wallet
+  cli.setup_gpg_key(values[:gpg_key])
+  cli.setup_wallet_config
 else
-	cli.load_config
-	cli.set_config(values)
+  cli.load_config
+  cli.set_config(values)
 end
diff --git a/bin/mpw-copy b/bin/mpw-copy
index 1e5804b..9436a10 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -29,32 +29,32 @@ options[:clipboard] = true
 values              = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-d', '--disable-clipboard', I18n.t('option.clipboard')) do
-		options[:clipboard] = false
-	end
+  opts.on('-d', '--disable-clipboard', I18n.t('option.clipboard')) do
+    options[:clipboard] = false
+  end
 
-	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		values[:group] = group
-	end
+  opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
+    values[:group] = group
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		values[:pattern] = pattern
-	end
+  opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
+    values[:pattern] = pattern
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-delete b/bin/mpw-delete
index 53c8074..f276785 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -28,28 +28,28 @@ options = {}
 values  = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		values[:group] = group
-	end
+  opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
+    values[:group] = group
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		values[:pattern] = pattern
-	end
+  opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
+    values[:pattern] = pattern
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-export b/bin/mpw-export
index 7913040..982d1ad 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -28,32 +28,32 @@ options = {}
 values  = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-f', '--file PATH', I18n.t('option.file_export')) do |file|
-		options[:file] = file
-	end
+  opts.on('-f', '--file PATH', I18n.t('option.file_export')) do |file|
+    options[:file] = file
+  end
 
-	opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
-		values[:group] = group
-	end
+  opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
+    values[:group] = group
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		values[:pattern] = pattern
-	end
+  opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
+    values[:pattern] = pattern
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd
index 8af4290..0370732 100644
--- a/bin/mpw-genpwd
+++ b/bin/mpw-genpwd
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -22,28 +22,28 @@ require 'mpw/mpw'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw passwd [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw passwd [options]"
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
-		options[:length] = length.to_i
-	end
+  opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
+    options[:length] = length.to_i
+  end
 
-	opts.on('-n', '--numeric', I18n.t('option.numeric')) do
-		options[:numeric] = true
-	end
+  opts.on('-n', '--numeric', I18n.t('option.numeric')) do
+    options[:numeric] = true
+  end
 
-	opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
-		options[:special] = true
-	end
+  opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
+    options[:special] = true
+  end
 
-	opts.on('-a', '--alpha', I18n.t('option.alpha')) do
-		options[:alpha] = true
-	end
+  opts.on('-a', '--alpha', I18n.t('option.alpha')) do
+    options[:alpha] = true
+  end
 end.parse!
 
 puts MPW::MPW::password(options)
diff --git a/bin/mpw-import b/bin/mpw-import
index a827783..d9c868d 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -27,24 +27,24 @@ require 'mpw/cli'
 options = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-f', '--file PATH', I18n.t('option.file_import')) do |file|
-		options[:file] = file
-	end
+  opts.on('-f', '--file PATH', I18n.t('option.file_import')) do |file|
+    options[:file] = file
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-list b/bin/mpw-list
index 3ebc2b8..3571abc 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -28,28 +28,28 @@ options = {}
 values  = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		values[:group] = group
-	end
+  opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
+    values[:group] = group
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		values[:pattern] = pattern
-	end
+  opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
+    values[:pattern] = pattern
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-update b/bin/mpw-update
index fd207c5..c258330 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -28,32 +28,32 @@ options = {}
 values  = {}
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
-		values[:group] = group
-	end
+  opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
+    values[:group] = group
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
-		values[:pattern] = pattern
-	end
+  opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
+    values[:pattern] = pattern
+  end
 
-	opts.on('-r', '--random', I18n.t('option.random_password')) do
-		options[:password] = true
-	end
+  opts.on('-r', '--random', I18n.t('option.random_password')) do
+    options[:password] = true
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 56e96ba..e575c0d 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -28,37 +28,37 @@ options          = {}
 options[:delete] = false
 
 OptionParser.new do |opts|
-	opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
+  opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
 
-	opts.on('-a', '--add-gpg-key NAME', I18n.t('option.add_gpg_key')) do |gpg_key|
-		options[:gpg_key] = gpg_key
-	end
+  opts.on('-a', '--add-gpg-key NAME', I18n.t('option.add_gpg_key')) do |gpg_key|
+    options[:gpg_key] = gpg_key
+  end
 
-	opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
-		options[:config] = config
-	end
+  opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
+    options[:config] = config
+  end
 
-	opts.on('-d', '--delete-gpg-key NAME', I18n.t('option.delete_gpg_key')) do |gpg_key|
-		options[:gpg_key] = gpg_key
-		options[:delete]  = true
-	end
+  opts.on('-d', '--delete-gpg-key NAME', I18n.t('option.delete_gpg_key')) do |gpg_key|
+    options[:gpg_key] = gpg_key
+    options[:delete]  = true
+  end
 
-	opts.on('-h', '--help', I18n.t('option.help')) do
-		puts opts
-		exit 0
-	end
+  opts.on('-h', '--help', I18n.t('option.help')) do
+    puts opts
+    exit 0
+  end
 
-	opts.on('-l', '--list', I18n.t('option.list')) do
-		options[:list] = true
-	end
+  opts.on('-l', '--list', I18n.t('option.list')) do
+    options[:list] = true
+  end
 
-	opts.on('-L', '--list-keys', I18n.t('option.list_keys')) do
-		options[:list_keys] = true
-	end
+  opts.on('-L', '--list-keys', I18n.t('option.list_keys')) do
+    options[:list_keys] = true
+  end
 
-	opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
-		options[:wallet] = wallet
-	end
+  opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
+    options[:wallet] = wallet
+  end
 end.parse!
 
 config = MPW::Config.new(options[:config])
@@ -67,18 +67,18 @@ cli    = MPW::Cli.new(config)
 cli.load_config
 
 if not options[:list].nil?
-	cli.list_wallet
+  cli.list_wallet
 else
-	cli.get_wallet(options[:wallet])
-	cli.decrypt
+  cli.get_wallet(options[:wallet])
+  cli.decrypt
 
-	if not options[:list_keys].nil?
-		cli.list_keys
-	elsif not options[:gpg_key].nil?
-		if options[:delete]
-			cli.delete_key(options[:gpg_key])
-		else
-			cli.add_key(options[:gpg_key])
-		end
-	end
+  if not options[:list_keys].nil?
+    cli.list_keys
+  elsif not options[:gpg_key].nil?
+    if options[:delete]
+      cli.delete_key(options[:gpg_key])
+    else
+      cli.add_key(options[:gpg_key])
+    end
+  end
 end
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 0eb294e..83b9af3 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -29,516 +29,516 @@ require 'mpw/mpw'
 module MPW
 class Cli
 
-	# Constructor
-	# @args: config -> the config
-	def initialize(config)
-		@config = config
-	end
-
-	# Change a parameter int the config after init
-	# @args: options -> param to change
-	def set_config(options)
-		@config.setup(options)
-
-		puts "#{I18n.t('form.set_config.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #15: #{e}".red
-		exit 2
-	end
-
-	# Create a new config file
-	# @args: options -> set param
-	def setup(options)
-		options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-
-		I18n.locale = options[:lang].to_sym
-
-		@config.setup(options)
-
-		load_config
-
-		puts "#{I18n.t('form.setup_config.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #8: #{e}".red
-		exit 2
-	end
-	
-	# Setup a new GPG key
-	# @args: gpg_key -> the key name
-	def setup_gpg_key(gpg_key)
-		return if @config.check_gpg_key?
-
-		password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
-		confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
-
-		if password != confirm 
-			raise I18n.t('form.setup_gpg_key.error_password')
-		end
-
-		@password = password.to_s 
-
-		puts I18n.t('form.setup_gpg_key.wait')
-		
-		@config.setup_gpg_key(@password, gpg_key)
-
-		puts "#{I18n.t('form.setup_gpg_key.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #8: #{e}".red
-		exit 2
-	end
-
-	# List gpg keys in wallet
-	def list_keys
-		table_list('keys', @mpw.list_keys)
-	end
-
-	# Load config
-	def load_config
-		@config.load_config
-
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #10: #{e}".red
-		exit 2
-	end
-
-	# Request the GPG password and decrypt the file
-	def decrypt
-		if not 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)
-		end
-
-		@mpw.read_data
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #11: #{e}".red
-		exit 2
-	end
-
-	# Format list on a table
-	def table_list(title, list)
-		i      = 1
-		length = 0
-
-		list.each do |item|
-			length = item.length if length < item.length
-		end
-		length += 7
-
-		puts "\n#{I18n.t("display.#{title}")}".red
-		print ' '
-		length.times { print '=' }
-		print "\n"
-
-		list.each do |item|
-			print "  #{i}".cyan
-			(3 - i.to_s.length).times { print ' ' }
-			puts "| #{item}"
-			i += 1
-		end
-
-		print "\n"
-	end
-
-	# Format items on a table
-	def table_items(items=[])
-		group        = '.'
-		i            = 1
-		length_total = 10
-		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 or k == :otp
-
-				v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
-			end
-		end
-		data[:id][:length]  = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
-		
-		data.each_value { |v| length_total += v[:length] }
-		items.sort!     { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
-
-		items.each do |item|
-			if group != item.group
-				group = item.group
-
-				if group.to_s.empty?
-					puts "\n#{I18n.t('display.no_group')}".red
-				else
-					puts "\n#{group}".red
-				end
-
-				print ' '
-				length_total.times { print '=' }
-				print "\n "
-				data.each do |k, v|
-					case k
-					when :id
-						print ' ID'
-					when :otp
-						print '| OTP'
-					else
-						print "| #{k.to_s.capitalize}"
-					end
-
-					(v[:length] - k.to_s.length).times { print ' ' }
-				end
-				print "\n "
-				length_total.times { print '=' }
-				print "\n"
-			end
-
-			print "  #{i}".send(data[:id][:color])
-			(data[:id][:length] - i.to_s.length).times { print ' ' }
-			data.each do |k, v|
-				next if k == :id
-
-				if k == :otp
-					print '| '
-					if item.otp;  print ' X  ' else 4.times { print ' ' } end
-
-					next
-				end
-
-				print '| '
-				print "#{item.send(k.to_s)}".send(v[:color])
-				(v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
-			end
-			print "\n"
-
-			i += 1
-		end
-
-		print "\n"
-	end
-
-	# Display the query's result
-	# @args: options -> the option to search
-	def list(options={})
-		result = @mpw.list(options)
-
-		if result.length == 0
-			puts I18n.t('display.nothing')
-		else
-			table_items(result)
-		end
-	end
-
-	# Get an item when multiple choice
-	# @args: items -> array of items
-	# @rtrn: item
-	def get_item(items)
-		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
-
-		if choice >= 1 and choice <= items.length
-			return items[choice-1]
-		else
-			return nil
-		end
-	end
-
-	# Copy in clipboard the login and password
-	# @args: item -> the item
-	#        clipboard -> enable clipboard
-	def clipboard(item, clipboard=true)
-		pid = nil
-
-		# Security: force quit after 90s
-		Thread.new do
-			sleep 90
-			exit
-		end
-		
-		while true
-			choice = ask(I18n.t('form.clipboard.choice')).to_s
-			
-			case choice
-			when 'q', 'quit'
-				break
-
-			when 'l', 'login'
-				if clipboard
-					Clipboard.copy(item.user)
-					puts I18n.t('form.clipboard.login').green
-				else
-					puts item.user
-				end
-
-			when 'p', 'password'
-				if clipboard
-					Clipboard.copy(@mpw.get_password(item.id))
-					puts I18n.t('form.clipboard.password').yellow
-
-					Thread.new do
-						sleep 30
-
-						Clipboard.clear
-					end
-				else
-					puts @mpw.get_password(item.id)
-				end
-
-			when 'o', 'otp'
-				if clipboard
-					Clipboard.copy(@mpw.get_otp_code(item.id))
-				else
-					puts @mpw.get_otp_code(item.id)
-				end
-				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.login')
-				puts I18n.t('form.clipboard.help.password')
-				puts I18n.t('form.clipboard.help.otp_code')
-				puts I18n.t('form.clipboard.help.quit')
-				next
-			end
-		end
-
-		Clipboard.clear
-	rescue SystemExit, Interrupt
-		Clipboard.clear
-	end
-
-	# List all wallets
-	def list_wallet
-		wallets = []
-		Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f| 
-			wallet = File.basename(f, '.mpw')
-			wallet += ' *'.green if wallet == @config.default_wallet
-			wallets << wallet
-		end
-
-		table_list('wallets', wallets)
-	end
-
-	# Display the wallet
-	# @args: wallet -> the wallet name
-	def get_wallet(wallet=nil)
-		if wallet.to_s.empty?
-			wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
-
-			if wallets.length == 1
-				@wallet_file = wallets[0]
-			elsif not @config.default_wallet.to_s.empty?
-				@wallet_file = "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
-			else
-				@wallet_file = "#{@config.wallet_dir}/default.mpw"
-			end
-		else
-			@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
-		end
-	end
-
-	# Add a new public key
-	# args: key -> the key name or key file to add
-	def add_key(key)
-		@mpw.add_key(key)
-		@mpw.write_data
-
-		puts "#{I18n.t('form.add_key.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #13: #{e}".red
-	end
-
-	# Add new public key
-	# args: key -> the key name to delete
-	def delete_key(key)
-		@mpw.delete_key(key)
-		@mpw.write_data
-
-		puts "#{I18n.t('form.delete_key.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #15: #{e}".red
-	end
-
-	# Text editor interface
-	# @args: template -> template name
-	#        item -> the item to edit
-	#        password -> disable field password
-	def text_editor(template_name, item=nil, password=false)
-		editor        = ENV['EDITOR'] || 'nano'
-		options       = {}
-		opts          = {}
-		template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
-		template      = ERB.new(IO.read(template_file))
-
-		Dir.mktmpdir do |dir|
-			tmp_file = "#{dir}/#{template_name}.yml"
-
-			File.open(tmp_file, 'w') do |f|
-				f << template.result(binding)
-			end
-
-			system("#{editor} #{tmp_file}")
-
-			opts = YAML::load_file(tmp_file)
-		end
-
-		opts.delete_if { |k,v| v.to_s.empty? }
-
-		opts.each do |k,v|
-			options[k.to_sym] = v
-		end
-
-		return options
-	end
-
-	# Form to add a new item
-	# @args: password -> generate a random password
-	def add(password=false)
-		options            = text_editor('add_form', nil, password)
-		item               = Item.new(options)
-		options[:password] = MPW::password(@config.password) if password
-		
-		@mpw.add(item)
-		@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
-		@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
-		@mpw.write_data
-
-		puts "#{I18n.t('form.add_item.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #13: #{e}".red
-	end
-
-	# Update an item
-	# @args: password -> generate a random password
-	#        options -> the option to search
-	def update(password=false, options={})
-		items = @mpw.list(options)
-		
-		if items.length == 0
-			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
-		else
-			table_items(items) if items.length > 1
-
-			item               = get_item(items)
-			options            = text_editor('update_form', item, password)
-		        options[:password] = MPW::password(@config.password) if password
-
-			item.update(options)
-			@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
-			@mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
-			@mpw.write_data
-
-			puts "#{I18n.t('form.update_item.valid')}".green
-		end
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #14: #{e}".red
-	end
-
-	# Remove an item
-	# @args: options -> the option to search
-	def delete(options={})
-		items = @mpw.list(options)
-		
-		if items.length == 0
-			puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
-		else
-			table_items(items)
-
-			item    = get_item(items)
-			confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
-	
-			if not confirm =~ /^(y|yes|YES|Yes|Y)$/
-				return false
-			end
-	
-			item.delete
-			@mpw.write_data
-	
-			puts "#{I18n.t('form.delete_item.valid')}".green
-		end
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #16: #{e}".red
-	end
-
-	# Copy a password, otp, login
-	# @args: clipboard -> enable clipboard
-	#        options -> the option to search
-	def copy(clipboard=true, options={})
-		items = @mpw.list(options)
-		
-		if items.length == 0
-			puts I18n.t('display.nothing')
-		else
-			table_items(items)
-
-			item = get_item(items)
-			clipboard(item, clipboard)
-		end
-	rescue Exception => e
-		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
-	def export(file, options)
-		file  = 'export-mpw.yml' if file.to_s.empty?
-		items = @mpw.list(options)
-		data  = {}
-
-		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,
-			                       }
-			            )
-		end
-
-		File.open(file, 'w') {|f| f << data.to_yaml}
-
-		puts "#{I18n.t('form.export.valid', file: file)}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #17: #{e}".red
-	end
-
-	# Import items from a YAML file
-	# @args: file -> the 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') if not 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'],
-			               )
-
-			next if item.empty?
-
-			@mpw.add(item)
-			@mpw.set_password(item.id, row['password']) if not row['password'].to_s.empty?
-			@mpw.set_otp_key(item.id, row['otp_key'])   if not row['otp_key'].to_s.empty?
-		end
-
-		@mpw.write_data
-
-		puts "#{I18n.t('form.import.valid')}".green
-	rescue Exception => e
-		puts "#{I18n.t('display.error')} #18: #{e}".red
-	end
+  # Constructor
+  # @args: config -> the config
+  def initialize(config)
+    @config = config
+  end
+
+  # Change a parameter int the config after init
+  # @args: options -> param to change
+  def set_config(options)
+    @config.setup(options)
+
+    puts "#{I18n.t('form.set_config.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #15: #{e}".red
+    exit 2
+  end
+
+  # Create a new config file
+  # @args: options -> set param
+  def setup(options)
+    options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
+
+    I18n.locale = options[:lang].to_sym
+
+    @config.setup(options)
+
+    load_config
+
+    puts "#{I18n.t('form.setup_config.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #8: #{e}".red
+    exit 2
+  end
+
+  # Setup a new GPG key
+  # @args: gpg_key -> the key name
+  def setup_gpg_key(gpg_key)
+    return if @config.check_gpg_key?
+
+    password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
+    confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
+
+    if password != confirm
+      raise I18n.t('form.setup_gpg_key.error_password')
+    end
+
+    @password = password.to_s
+
+    puts I18n.t('form.setup_gpg_key.wait')
+
+    @config.setup_gpg_key(@password, gpg_key)
+
+    puts "#{I18n.t('form.setup_gpg_key.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #8: #{e}".red
+    exit 2
+  end
+
+  # List gpg keys in wallet
+  def list_keys
+    table_list('keys', @mpw.list_keys)
+  end
+
+  # Load config
+  def load_config
+    @config.load_config
+
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #10: #{e}".red
+    exit 2
+  end
+
+  # Request the GPG password and decrypt the file
+  def decrypt
+    if not 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)
+    end
+
+    @mpw.read_data
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #11: #{e}".red
+    exit 2
+  end
+
+  # Format list on a table
+  def table_list(title, list)
+    i      = 1
+    length = 0
+
+    list.each do |item|
+      length = item.length if length < item.length
+    end
+    length += 7
+
+    puts "\n#{I18n.t("display.#{title}")}".red
+    print ' '
+    length.times { print '=' }
+    print "\n"
+
+    list.each do |item|
+      print "  #{i}".cyan
+      (3 - i.to_s.length).times { print ' ' }
+      puts "| #{item}"
+      i += 1
+    end
+
+    print "\n"
+  end
+
+  # Format items on a table
+  def table_items(items=[])
+    group        = '.'
+    i            = 1
+    length_total = 10
+    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 or k == :otp
+
+        v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+      end
+    end
+    data[:id][:length]  = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
+
+    data.each_value { |v| length_total += v[:length] }
+    items.sort!     { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+
+    items.each do |item|
+      if group != item.group
+        group = item.group
+
+        if group.to_s.empty?
+          puts "\n#{I18n.t('display.no_group')}".red
+        else
+          puts "\n#{group}".red
+        end
+
+        print ' '
+        length_total.times { print '=' }
+        print "\n "
+        data.each do |k, v|
+          case k
+          when :id
+            print ' ID'
+          when :otp
+            print '| OTP'
+          else
+            print "| #{k.to_s.capitalize}"
+          end
+
+          (v[:length] - k.to_s.length).times { print ' ' }
+        end
+        print "\n "
+        length_total.times { print '=' }
+        print "\n"
+      end
+
+      print "  #{i}".send(data[:id][:color])
+      (data[:id][:length] - i.to_s.length).times { print ' ' }
+      data.each do |k, v|
+        next if k == :id
+
+        if k == :otp
+          print '| '
+          if item.otp;  print ' X  ' else 4.times { print ' ' } end
+
+          next
+        end
+
+        print '| '
+        print "#{item.send(k.to_s)}".send(v[:color])
+        (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+      end
+      print "\n"
+
+      i += 1
+    end
+
+    print "\n"
+  end
+
+  # Display the query's result
+  # @args: options -> the option to search
+  def list(options={})
+    result = @mpw.list(options)
+
+    if result.length == 0
+      puts I18n.t('display.nothing')
+    else
+      table_items(result)
+    end
+  end
+
+  # Get an item when multiple choice
+  # @args: items -> array of items
+  # @rtrn: item
+  def get_item(items)
+    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
+
+    if choice >= 1 and choice <= items.length
+      return items[choice-1]
+    else
+      return nil
+    end
+  end
+
+  # Copy in clipboard the login and password
+  # @args: item -> the item
+  #        clipboard -> enable clipboard
+  def clipboard(item, clipboard=true)
+    pid = nil
+
+    # Security: force quit after 90s
+    Thread.new do
+      sleep 90
+      exit
+    end
+
+    while true
+      choice = ask(I18n.t('form.clipboard.choice')).to_s
+
+      case choice
+      when 'q', 'quit'
+        break
+
+      when 'l', 'login'
+        if clipboard
+          Clipboard.copy(item.user)
+          puts I18n.t('form.clipboard.login').green
+        else
+          puts item.user
+        end
+
+      when 'p', 'password'
+        if clipboard
+          Clipboard.copy(@mpw.get_password(item.id))
+          puts I18n.t('form.clipboard.password').yellow
+
+          Thread.new do
+            sleep 30
+
+            Clipboard.clear
+          end
+        else
+          puts @mpw.get_password(item.id)
+        end
+
+      when 'o', 'otp'
+        if clipboard
+          Clipboard.copy(@mpw.get_otp_code(item.id))
+        else
+          puts @mpw.get_otp_code(item.id)
+        end
+        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.login')
+        puts I18n.t('form.clipboard.help.password')
+        puts I18n.t('form.clipboard.help.otp_code')
+        puts I18n.t('form.clipboard.help.quit')
+        next
+      end
+    end
+
+    Clipboard.clear
+  rescue SystemExit, Interrupt
+    Clipboard.clear
+  end
+
+  # List all wallets
+  def list_wallet
+    wallets = []
+    Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f|
+      wallet = File.basename(f, '.mpw')
+      wallet += ' *'.green if wallet == @config.default_wallet
+      wallets << wallet
+    end
+
+    table_list('wallets', wallets)
+  end
+
+  # Display the wallet
+  # @args: wallet -> the wallet name
+  def get_wallet(wallet=nil)
+    if wallet.to_s.empty?
+      wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
+
+      if wallets.length == 1
+        @wallet_file = wallets[0]
+      elsif not @config.default_wallet.to_s.empty?
+        @wallet_file = "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
+      else
+        @wallet_file = "#{@config.wallet_dir}/default.mpw"
+      end
+    else
+      @wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
+    end
+  end
+
+  # Add a new public key
+  # args: key -> the key name or key file to add
+  def add_key(key)
+    @mpw.add_key(key)
+    @mpw.write_data
+
+    puts "#{I18n.t('form.add_key.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #13: #{e}".red
+  end
+
+  # Add new public key
+  # args: key -> the key name to delete
+  def delete_key(key)
+    @mpw.delete_key(key)
+    @mpw.write_data
+
+    puts "#{I18n.t('form.delete_key.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #15: #{e}".red
+  end
+
+  # Text editor interface
+  # @args: template -> template name
+  #        item -> the item to edit
+  #        password -> disable field password
+  def text_editor(template_name, item=nil, password=false)
+    editor        = ENV['EDITOR'] || 'nano'
+    options       = {}
+    opts          = {}
+    template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
+    template      = ERB.new(IO.read(template_file))
+
+    Dir.mktmpdir do |dir|
+      tmp_file = "#{dir}/#{template_name}.yml"
+
+      File.open(tmp_file, 'w') do |f|
+        f << template.result(binding)
+      end
+
+      system("#{editor} #{tmp_file}")
+
+      opts = YAML::load_file(tmp_file)
+    end
+
+    opts.delete_if { |k,v| v.to_s.empty? }
+
+    opts.each do |k,v|
+      options[k.to_sym] = v
+    end
+
+    return options
+  end
+
+  # Form to add a new item
+  # @args: password -> generate a random password
+  def add(password=false)
+    options            = text_editor('add_form', nil, password)
+    item               = Item.new(options)
+    options[:password] = MPW::password(@config.password) if password
+
+    @mpw.add(item)
+    @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
+    @mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
+    @mpw.write_data
+
+    puts "#{I18n.t('form.add_item.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #13: #{e}".red
+  end
+
+  # Update an item
+  # @args: password -> generate a random password
+  #        options -> the option to search
+  def update(password=false, options={})
+    items = @mpw.list(options)
+
+    if items.length == 0
+      puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+    else
+      table_items(items) if items.length > 1
+
+      item               = get_item(items)
+      options            = text_editor('update_form', item, password)
+            options[:password] = MPW::password(@config.password) if password
+
+      item.update(options)
+      @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
+      @mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
+      @mpw.write_data
+
+      puts "#{I18n.t('form.update_item.valid')}".green
+    end
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #14: #{e}".red
+  end
+
+  # Remove an item
+  # @args: options -> the option to search
+  def delete(options={})
+    items = @mpw.list(options)
+
+    if items.length == 0
+      puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+    else
+      table_items(items)
+
+      item    = get_item(items)
+      confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
+
+      if not confirm =~ /^(y|yes|YES|Yes|Y)$/
+        return false
+      end
+
+      item.delete
+      @mpw.write_data
+
+      puts "#{I18n.t('form.delete_item.valid')}".green
+    end
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #16: #{e}".red
+  end
+
+  # Copy a password, otp, login
+  # @args: clipboard -> enable clipboard
+  #        options -> the option to search
+  def copy(clipboard=true, options={})
+    items = @mpw.list(options)
+
+    if items.length == 0
+      puts I18n.t('display.nothing')
+    else
+      table_items(items)
+
+      item = get_item(items)
+      clipboard(item, clipboard)
+    end
+  rescue Exception => e
+    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
+  def export(file, options)
+    file  = 'export-mpw.yml' if file.to_s.empty?
+    items = @mpw.list(options)
+    data  = {}
+
+    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,
+                             }
+                  )
+    end
+
+    File.open(file, 'w') {|f| f << data.to_yaml}
+
+    puts "#{I18n.t('form.export.valid', file: file)}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #17: #{e}".red
+  end
+
+  # Import items from a YAML file
+  # @args: file -> the 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') if not 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'],
+                     )
+
+      next if item.empty?
+
+      @mpw.add(item)
+      @mpw.set_password(item.id, row['password']) if not row['password'].to_s.empty?
+      @mpw.set_otp_key(item.id, row['otp_key'])   if not row['otp_key'].to_s.empty?
+    end
+
+    @mpw.write_data
+
+    puts "#{I18n.t('form.import.valid')}".green
+  rescue Exception => e
+    puts "#{I18n.t('display.error')} #18: #{e}".red
+  end
 end
 end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 7fbd778..298ba61 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -20,140 +20,140 @@ require 'gpgme'
 require 'yaml'
 require 'i18n'
 require 'fileutils'
-	
+
 module MPW
 class Config
-	
-	attr_accessor :error_msg
 
-	attr_accessor :gpg_key
-	attr_accessor :lang
-	attr_accessor :config_dir
-	attr_accessor :default_wallet
-	attr_accessor :wallet_dir
-	attr_accessor :gpg_exe
-	attr_accessor :password
+  attr_accessor :error_msg
 
-	# Constructor
-	# @args: config_file -> the specify config file
-	def initialize(config_file=nil)
-		@config_file = config_file
+  attr_accessor :gpg_key
+  attr_accessor :lang
+  attr_accessor :config_dir
+  attr_accessor :default_wallet
+  attr_accessor :wallet_dir
+  attr_accessor :gpg_exe
+  attr_accessor :password
 
-		if /darwin/ =~ RUBY_PLATFORM
-			@config_dir = "#{Dir.home}/Library/Preferences/mpw"
-		elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
-			@config_dir = "#{Dir.home}/AppData/Local/mpw"
-		else 
-			@config_dir = "#{Dir.home}/.config/mpw"
-		end
-		
-		@config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? or @config_file.empty?
-	end
+  # Constructor
+  # @args: config_file -> the specify config file
+  def initialize(config_file=nil)
+    @config_file = config_file
 
-	# Create a new config file
-	# @args: options -> hash with values
-	# @rtrn: true if le config file is create
-	def setup(options)
-		gpg_key        = options[:gpg_key]        || @gpg_key
-		lang           = options[:lang]           || @lang
-		wallet_dir     = options[:wallet_dir]     || @wallet_dir
-		default_wallet = options[:default_wallet] || @default_wallet
-		gpg_exe        = options[:gpg_exe]        || @gpg_exe
-		password       = { numeric: true,
-		                   alpha:   true,
-		                   special: false,
-		                   length:  16,
-		                 }
+    if /darwin/ =~ RUBY_PLATFORM
+      @config_dir = "#{Dir.home}/Library/Preferences/mpw"
+    elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+      @config_dir = "#{Dir.home}/AppData/Local/mpw"
+    else
+      @config_dir = "#{Dir.home}/.config/mpw"
+    end
 
-		['numeric', 'special', 'alpha', 'length'].each do |k|
-			if options.has_key?("pwd_#{k}".to_sym)
-				password[k.to_sym] = options["pwd_#{k}".to_sym]
-			elsif not @password.nil? and @password.has_key?(k.to_sym)
-				password[k.to_sym] = @password[k.to_sym]
-			end
-		end
+    @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? or @config_file.empty?
+  end
 
-		if not gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-			raise I18n.t('error.config.key_bad_format')
-		end
+  # Create a new config file
+  # @args: options -> hash with values
+  # @rtrn: true if le config file is create
+  def setup(options)
+    gpg_key        = options[:gpg_key]        || @gpg_key
+    lang           = options[:lang]           || @lang
+    wallet_dir     = options[:wallet_dir]     || @wallet_dir
+    default_wallet = options[:default_wallet] || @default_wallet
+    gpg_exe        = options[:gpg_exe]        || @gpg_exe
+    password       = { numeric: true,
+                       alpha:   true,
+                       special: false,
+                       length:  16,
+                     }
 
-		wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
-		config     = { 'gpg_key'        => gpg_key,
-		               'lang'           => lang,
-		               'wallet_dir'     => wallet_dir,
-		               'default_wallet' => default_wallet,
-		               'gpg_exe'        => gpg_exe,
-		               'password'       => password,
-		             }
+    ['numeric', 'special', 'alpha', 'length'].each do |k|
+      if options.has_key?("pwd_#{k}".to_sym)
+        password[k.to_sym] = options["pwd_#{k}".to_sym]
+      elsif not @password.nil? and @password.has_key?(k.to_sym)
+        password[k.to_sym] = @password[k.to_sym]
+      end
+    end
 
-		FileUtils.mkdir_p(@config_dir, mode: 0700)
-		FileUtils.mkdir_p(wallet_dir,  mode: 0700)
+    if not gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+      raise I18n.t('error.config.key_bad_format')
+    end
 
-		File.open(@config_file, 'w') do |file|
-			file << config.to_yaml
-		end
-	rescue Exception => e 
-		raise "#{I18n.t('error.config.write')}\n#{e}"
-	end
+    wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
+    config     = { 'gpg_key'        => gpg_key,
+                   'lang'           => lang,
+                   'wallet_dir'     => wallet_dir,
+                   'default_wallet' => default_wallet,
+                   'gpg_exe'        => gpg_exe,
+                   'password'       => password,
+                 }
 
-	# 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
-	def setup_gpg_key(password, name, length = 4096, expire = 0)
-		if name.to_s.empty?
-			raise "#{I18n.t('error.config.genkey_gpg.name')}"
-		elsif password.to_s.empty?
-			raise "#{I18n.t('error.config.genkey_gpg.password')}"
-		end
+    FileUtils.mkdir_p(@config_dir, mode: 0700)
+    FileUtils.mkdir_p(wallet_dir,  mode: 0700)
 
-		param = ''
-		param << '<GnupgKeyParms format="internal">' + "\n"
-		param << "Key-Type: RSA\n"  
-		param << "Key-Length: #{length}\n"
-		param << "Subkey-Type: ELG-E\n"
-		param << "Subkey-Length: #{length}\n"
-		param << "Name-Real: #{name}\n"
-		param << "Name-Comment: #{name}\n"
-		param << "Name-Email: #{@gpg_key}\n"
-		param << "Expire-Date: #{expire}\n"
-		param << "Passphrase: #{password}\n"
-		param << "</GnupgKeyParms>\n"
+    File.open(@config_file, 'w') do |file|
+      file << config.to_yaml
+    end
+  rescue Exception => e
+    raise "#{I18n.t('error.config.write')}\n#{e}"
+  end
 
-		ctx = GPGME::Ctx.new
-		ctx.genkey(param, nil, nil)
-	rescue Exception => e
-		raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
-	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
+  def setup_gpg_key(password, name, length = 4096, expire = 0)
+    if name.to_s.empty?
+      raise "#{I18n.t('error.config.genkey_gpg.name')}"
+    elsif password.to_s.empty?
+      raise "#{I18n.t('error.config.genkey_gpg.password')}"
+    end
 
-	# Load the config file
-	def load_config
-		config          = YAML::load_file(@config_file)
-		@gpg_key        = config['gpg_key']
-		@lang           = config['lang']
-		@wallet_dir     = config['wallet_dir']
-		@default_wallet = config['default_wallet']
-		@gpg_exe        = config['gpg_exe']
-		@password       = config['password'] || {}
+    param = ''
+    param << '<GnupgKeyParms format="internal">' + "\n"
+    param << "Key-Type: RSA\n"
+    param << "Key-Length: #{length}\n"
+    param << "Subkey-Type: ELG-E\n"
+    param << "Subkey-Length: #{length}\n"
+    param << "Name-Real: #{name}\n"
+    param << "Name-Comment: #{name}\n"
+    param << "Name-Email: #{@gpg_key}\n"
+    param << "Expire-Date: #{expire}\n"
+    param << "Passphrase: #{password}\n"
+    param << "</GnupgKeyParms>\n"
 
-		raise if @gpg_key.empty? or @wallet_dir.empty?
-			
-		I18n.locale = @lang.to_sym
-	rescue Exception => e
-		raise "#{I18n.t('error.config.load')}\n#{e}"
-	end
+    ctx = GPGME::Ctx.new
+    ctx.genkey(param, nil, nil)
+  rescue Exception => e
+    raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
+  end
 
-	# Check if private key exist
-	# @rtrn: true if the key exist, else false
-	def check_gpg_key?
-		ctx = GPGME::Ctx.new
-		ctx.each_key(@gpg_key, true) do
-			return true
-		end
+  # Load the config file
+  def load_config
+    config          = YAML::load_file(@config_file)
+    @gpg_key        = config['gpg_key']
+    @lang           = config['lang']
+    @wallet_dir     = config['wallet_dir']
+    @default_wallet = config['default_wallet']
+    @gpg_exe        = config['gpg_exe']
+    @password       = config['password'] || {}
 
-		return false
-	end
+    raise if @gpg_key.empty? or @wallet_dir.empty?
+
+    I18n.locale = @lang.to_sym
+  rescue Exception => e
+    raise "#{I18n.t('error.config.load')}\n#{e}"
+  end
+
+  # Check if private key exist
+  # @rtrn: true if the key exist, else false
+  def check_gpg_key?
+    ctx = GPGME::Ctx.new
+    ctx.each_key(@gpg_key, true) do
+      return true
+    end
+
+    return false
+  end
 end
 end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index a4f730b..469af15 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -1,102 +1,102 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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 'i18n'
-	
+
 module MPW
 class Item
 
-	attr_accessor :id
-	attr_accessor :group
-	attr_accessor :host
-	attr_accessor :protocol
-	attr_accessor :user
-	attr_accessor :port
-	attr_accessor :otp
-	attr_accessor :comment
-	attr_accessor :last_edit
-	attr_accessor :created
+  attr_accessor :id
+  attr_accessor :group
+  attr_accessor :host
+  attr_accessor :protocol
+  attr_accessor :user
+  attr_accessor :port
+  attr_accessor :otp
+  attr_accessor :comment
+  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 
-	def initialize(options={})
-		if not options.has_key?(:host) or options[:host].to_s.empty?
-			raise I18n.t('error.update.host_empty')
-		end
+  # Constructor
+  # Create a new item
+  # @args: options -> a hash of parameter
+  # raise an error if the hash hasn't the key name
+  def initialize(options={})
+    if not options.has_key?(:host) or options[:host].to_s.empty?
+      raise I18n.t('error.update.host_empty')
+    end
 
-		if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
-			@id = generate_id
-			@created = Time.now.to_i
-		else
-			@id = options[:id]
-			@created   = options[:created]
-			@last_edit = options[:last_edit]
-			options[:no_update_last_edit] = true
-		end
+    if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
+      @id = generate_id
+      @created = Time.now.to_i
+    else
+      @id = options[:id]
+      @created   = options[:created]
+      @last_edit = options[:last_edit]
+      options[:no_update_last_edit] = true
+    end
 
-		update(options)
-	end
+    update(options)
+  end
 
-	# Update the item
-	# @args: options -> a hash of parameter
-	def update(options={})
-		if options.has_key?(:host) and options[:host].to_s.empty?
-			raise I18n.t('error.update.host_empty')
-		end
+  # Update the item
+  # @args: options -> a hash of parameter
+  def update(options={})
+    if options.has_key?(:host) and options[:host].to_s.empty?
+      raise I18n.t('error.update.host_empty')
+    end
 
-		@group     = options[:group]      if options.has_key?(:group)
-		@host      = options[:host]       if options.has_key?(:host)
-		@protocol  = options[:protocol]   if options.has_key?(:protocol)
-		@user      = options[:user]       if options.has_key?(:user)
-		@port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
-		@otp       = options[:otp]        if options.has_key?(:otp)
-		@comment   = options[:comment]    if options.has_key?(:comment)
-		@last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
-	end
+    @group     = options[:group]      if options.has_key?(:group)
+    @host      = options[:host]       if options.has_key?(:host)
+    @protocol  = options[:protocol]   if options.has_key?(:protocol)
+    @user      = options[:user]       if options.has_key?(:user)
+    @port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
+    @otp       = options[:otp]        if options.has_key?(:otp)
+    @comment   = options[:comment]    if options.has_key?(:comment)
+    @last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
+  end
 
-	# Delete all data
-	def delete
-		@id        = nil
-		@group     = nil
-		@host      = nil
-		@protocol  = nil
-		@user      = nil
-		@port      = nil
-		@otp       = nil
-		@comment   = nil
-		@created   = nil
-		@last_edit = nil
-	end
+  # Delete all data
+  def delete
+    @id        = nil
+    @group     = nil
+    @host      = nil
+    @protocol  = nil
+    @user      = nil
+    @port      = nil
+    @otp       = nil
+    @comment   = nil
+    @created   = nil
+    @last_edit = nil
+  end
 
-	def empty?
-		return @id.to_s.empty?
-	end
+  def empty?
+    return @id.to_s.empty?
+  end
 
-	def nil?
-		return false
-	end
+  def nil?
+    return false
+  end
 
-	# Generate an random id
-	private
-	def generate_id
-		return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
-	end 
+  # Generate an random id
+  private
+  def generate_id
+    return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
+  end
 end
 end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 8f4b42e..9147648 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -1,17 +1,17 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
 # Copyright (C) 2016  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.
@@ -22,360 +22,360 @@ require 'i18n'
 require 'yaml'
 require 'rotp'
 require 'mpw/item'
-	
+
 module MPW
 class MPW
 
-	# Constructor
-	def initialize(key, wallet_file, gpg_pass=nil, gpg_exe=nil)
-		@key         = key
-		@gpg_pass    = gpg_pass
-		@gpg_exe     = gpg_exe
-		@wallet_file = wallet_file
+  # Constructor
+  def initialize(key, wallet_file, gpg_pass=nil, gpg_exe=nil)
+    @key         = key
+    @gpg_pass    = gpg_pass
+    @gpg_exe     = gpg_exe
+    @wallet_file = wallet_file
 
-		if not @gpg_exe.to_s.empty?
-			GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
-		end
-	end
+    if not @gpg_exe.to_s.empty?
+      GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
+    end
+  end
 
-	# Read mpw file
-	def read_data
-		@config    = {}
-		@data      = []
-		@keys      = {}
-		@passwords = {}
-		@otp_keys  = {}
+  # Read mpw file
+  def read_data
+    @config    = {}
+    @data      = []
+    @keys      = {}
+    @passwords = {}
+    @otp_keys  = {}
 
-		data       = nil
+    data       = nil
 
-		return if not File.exists?(@wallet_file)
+    return if not File.exists?(@wallet_file)
 
-		Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
-			tar.each do |f| 
-				case f.full_name
-					when 'wallet/config.gpg'
-						@config = YAML.load(decrypt(f.read))
+    Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
+      tar.each do |f|
+        case f.full_name
+          when 'wallet/config.gpg'
+            @config = YAML.load(decrypt(f.read))
 
-					when 'wallet/meta.gpg'
-						data = decrypt(f.read)
+          when 'wallet/meta.gpg'
+            data = decrypt(f.read)
 
-					when /^wallet\/keys\/(?<key>.+)\.pub$/
-						key = Regexp.last_match('key')
+          when /^wallet\/keys\/(?<key>.+)\.pub$/
+            key = Regexp.last_match('key')
 
-						if GPGME::Key.find(:public, key).length == 0
-							GPGME::Key.import(f.read, armor: true)
-						end
+            if GPGME::Key.find(:public, key).length == 0
+              GPGME::Key.import(f.read, armor: true)
+            end
 
-						@keys[key] = f.read
+            @keys[key] = f.read
 
-					when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
-						@passwords[Regexp.last_match('id')] = f.read
+          when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+            @passwords[Regexp.last_match('id')] = f.read
 
-					when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
-						@otp_keys[Regexp.last_match('id')] = f.read
+          when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+            @otp_keys[Regexp.last_match('id')] = f.read
 
-					else
-						next
-				end
-			end
-		end
+          else
+            next
+        end
+      end
+    end
 
-		if not data.nil? and not data.empty?
-			YAML.load(data).each_value do |d|
-				@data.push(Item.new(id:        d['id'],
-				                    group:     d['group'],
-				                    host:      d['host'],
-				                    protocol:  d['protocol'],
-				                    user:      d['user'],
-				                    port:      d['port'],
-				                    otp:       @otp_keys.has_key?(d['id']),
-				                    comment:   d['comment'],
-				                    last_edit: d['last_edit'],
-				                    created:   d['created'],
-				                   )
-				          )
-			end
-		end
+    if not data.nil? and not data.empty?
+      YAML.load(data).each_value do |d|
+        @data.push(Item.new(id:        d['id'],
+                            group:     d['group'],
+                            host:      d['host'],
+                            protocol:  d['protocol'],
+                            user:      d['user'],
+                            port:      d['port'],
+                            otp:       @otp_keys.has_key?(d['id']),
+                            comment:   d['comment'],
+                            last_edit: d['last_edit'],
+                            created:   d['created'],
+                           )
+                  )
+      end
+    end
 
-		add_key(@key) if @keys[@key].nil?
-	rescue Exception => e
-		raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
-	end
+    add_key(@key) if @keys[@key].nil?
+  rescue Exception => e
+    raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
+  end
 
-	# Encrypt a file
-	def write_data
-		data     = {}
-		tmp_file = "#{@wallet_file}.tmp"
+  # Encrypt a file
+  def write_data
+    data     = {}
+    tmp_file = "#{@wallet_file}.tmp"
 
-		@data.each do |item|
-			next if item.empty?
+    @data.each do |item|
+      next if item.empty?
 
-			data.merge!(item.id => { 'id'        => item.id,
-			                         'group'     => item.group,
-			                         'host'      => item.host,
-			                         'protocol'  => item.protocol,
-			                         'user'      => item.user,
-			                         'port'      => item.port,
-			                         'comment'   => item.comment,
-			                         'last_edit' => item.last_edit,
-			                         'created'   => item.created,
-			                       }
-			           )
-		end
+      data.merge!(item.id => { 'id'        => item.id,
+                               'group'     => item.group,
+                               'host'      => item.host,
+                               'protocol'  => item.protocol,
+                               'user'      => item.user,
+                               'port'      => item.port,
+                               'comment'   => item.comment,
+                               'last_edit' => item.last_edit,
+                               'created'   => item.created,
+                             }
+                 )
+    end
 
-		@config['last_update'] = Time.now.to_i
+    @config['last_update'] = Time.now.to_i
 
-		Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
-			data_encrypt = encrypt(data.to_yaml)
-			tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
-				io.write(data_encrypt)
-			end
+    Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
+      data_encrypt = encrypt(data.to_yaml)
+      tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
+        io.write(data_encrypt)
+      end
 
-			config = encrypt(@config.to_yaml)
-			tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
-				io.write(config)
-			end
+      config = encrypt(@config.to_yaml)
+      tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
+        io.write(config)
+      end
 
-			@passwords.each do |id, password|
-				tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
-					io.write(password)
-				end
-			end
+      @passwords.each do |id, password|
+        tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
+          io.write(password)
+        end
+      end
 
-			@otp_keys.each do |id, key|
-				tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
-					io.write(key)
-				end
-			end
+      @otp_keys.each do |id, key|
+        tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
+          io.write(key)
+        end
+      end
 
-			@keys.each do |id, key|
-				tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
-					io.write(key)
-				end
-			end
-		end
+      @keys.each do |id, key|
+        tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
+          io.write(key)
+        end
+      end
+    end
 
-		File.rename(tmp_file, @wallet_file)
-	rescue Exception => e
-		File.unlink(tmp_file) if File.exist?(tmp_file)
+    File.rename(tmp_file, @wallet_file)
+  rescue Exception => e
+    File.unlink(tmp_file) if File.exist?(tmp_file)
 
-		raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
-	end
+    raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
+  end
 
-	# Get a password
-	# args: id -> the item id
-	def get_password(id)
-		password = decrypt(@passwords[id])
-		
-		if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
-			return Regexp.last_match('password')
-		else
-			return password
-		end
-	end
+  # Get a password
+  # args: id -> the item id
+  def get_password(id)
+    password = decrypt(@passwords[id])
 
-	# Set a password
-	# args: id -> the item id
-	#       password -> the new password
-	def set_password(id, password)
-		salt     = MPW::password(length: Random.rand(4..9))
-		password = "$#{salt}::#{password}"
+    if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
+      return Regexp.last_match('password')
+    else
+      return password
+    end
+  end
 
-		@passwords[id] = encrypt(password)
-	end
+  # Set a password
+  # args: id -> the item id
+  #       password -> the new password
+  def set_password(id, password)
+    salt     = MPW::password(length: Random.rand(4..9))
+    password = "$#{salt}::#{password}"
 
-	# Return the list of all gpg keys
-	# rtrn: an array with the gpg keys name
-	def list_keys
-		return @keys.keys
-	end
+    @passwords[id] = encrypt(password)
+  end
 
-	# Add a public key
-	# args: key ->  new public key file or name
-	def add_key(key)
-		if File.exists?(key)
-			data       = File.open(key).read
-			key_import = GPGME::Key.import(data, armor: true)
-			key        = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
-		else
-			data = GPGME::Key.export(key, armor: true).read
-		end
+  # Return the list of all gpg keys
+  # rtrn: an array with the gpg keys name
+  def list_keys
+    return @keys.keys
+  end
 
-		if data.to_s.empty?
-			raise I18n.t('error.export_key')
-		end
+  # Add a public key
+  # args: key ->  new public key file or name
+  def add_key(key)
+    if File.exists?(key)
+      data       = File.open(key).read
+      key_import = GPGME::Key.import(data, armor: true)
+      key        = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
+    else
+      data = GPGME::Key.export(key, armor: true).read
+    end
 
-		@keys[key] = data
-		@passwords.each_key { |id| set_password(id, get_password(id)) }
-		@otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
-	end
+    if data.to_s.empty?
+      raise I18n.t('error.export_key')
+    end
 
-	# Delete a public key
-	# args: key ->  public key to delete
-	def delete_key(key)
-		@keys.delete(key)
-		@passwords.each_key { |id| set_password(id, get_password(id)) }
-		@otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
-	end
+    @keys[key] = data
+    @passwords.each_key { |id| set_password(id, get_password(id)) }
+    @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
+  end
 
-	# Set config
-	# args: config -> a hash with config options
-	def set_config(options={})
-		@config              = {} if @config.nil?
+  # Delete a public key
+  # args: key ->  public key to delete
+  def delete_key(key)
+    @keys.delete(key)
+    @passwords.each_key { |id| set_password(id, get_password(id)) }
+    @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
+  end
 
-		@config['protocol']  = options[:protocol] if options.has_key?(:protocol)
-		@config['host']      = options[:host]     if options.has_key?(:host)
-		@config['port']      = options[:port]     if options.has_key?(:port)
-		@config['user']      = options[:user]     if options.has_key?(:user)
-		@config['password']  = options[:password] if options.has_key?(:password)
-		@config['path']      = options[:path]     if options.has_key?(:path)
-	end
+  # Set config
+  # args: config -> a hash with config options
+  def set_config(options={})
+    @config              = {} if @config.nil?
 
-	# Add a new item
-	# @args: item -> Object MPW::Item
-	def add(item)
-		if not item.instance_of?(Item)
-			raise I18n.t('error.bad_class')
-		elsif item.empty?
-			raise I18n.t('error.empty')
-		else
-			@data.push(item)
-		end
-	end
+    @config['protocol']  = options[:protocol] if options.has_key?(:protocol)
+    @config['host']      = options[:host]     if options.has_key?(:host)
+    @config['port']      = options[:port]     if options.has_key?(:port)
+    @config['user']      = options[:user]     if options.has_key?(:user)
+    @config['password']  = options[:password] if options.has_key?(:password)
+    @config['path']      = options[:path]     if options.has_key?(:path)
+  end
 
-	# Search in some csv data
-	# @args: options -> a hash with paramaters
-	# @rtrn: a list with the resultat of the search
-	def list(options={})
-		result = []
+  # Add a new item
+  # @args: item -> Object MPW::Item
+  def add(item)
+    if not item.instance_of?(Item)
+      raise I18n.t('error.bad_class')
+    elsif item.empty?
+      raise I18n.t('error.empty')
+    else
+      @data.push(item)
+    end
+  end
 
-		search = options[:pattern].to_s.downcase
-		group  = options[:group].to_s.downcase
+  # Search in some csv data
+  # @args: options -> a hash with paramaters
+  # @rtrn: a list with the resultat of the search
+  def list(options={})
+    result = []
 
-		@data.each do |item|
-			next if item.empty?
-			next if not group.empty? and not group.eql?(item.group.to_s.downcase)
-			
-			host    = item.host.to_s.downcase
-			comment = item.comment.to_s.downcase
+    search = options[:pattern].to_s.downcase
+    group  = options[:group].to_s.downcase
 
-			if not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/ 
-				next
-			end
+    @data.each do |item|
+      next if item.empty?
+      next if not group.empty? and not group.eql?(item.group.to_s.downcase)
 
-			result.push(item)
-		end
+      host    = item.host.to_s.downcase
+      comment = item.comment.to_s.downcase
 
-		return result
-	end
+      if not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/
+        next
+      end
 
-	# Search in some csv data
-	# @args: id -> the id item
-	# @rtrn: a row with the result of the search
-	def search_by_id(id)
-		@data.each do |item|
-			return item if item.id == id
-		end
+      result.push(item)
+    end
 
-		return nil
-	end
+    return result
+  end
 
-	# Set an opt key
-	# args: id -> the item id
-	#       key -> the new key
-	def set_otp_key(id, key)
-		if not key.to_s.empty?
-			@otp_keys[id] = encrypt(key.to_s)
-		end
-	end
+  # Search in some csv data
+  # @args: id -> the id item
+  # @rtrn: a row with the result of the search
+  def search_by_id(id)
+    @data.each do |item|
+      return item if item.id == id
+    end
 
-	# Get an opt key
-	# args: id -> the item id
-	#       key -> the new key
-	def get_otp_key(id)
-		if @otp_keys.has_key?(id)
-			return decrypt(@otp_keys[id])
-		else
-			return nil
-		end
-	end
+    return nil
+  end
+
+  # Set an opt key
+  # args: id -> the item id
+  #       key -> the new key
+  def set_otp_key(id, key)
+    if not key.to_s.empty?
+      @otp_keys[id] = encrypt(key.to_s)
+    end
+  end
+
+  # Get an opt key
+  # args: id -> the item id
+  #       key -> the new key
+  def get_otp_key(id)
+    if @otp_keys.has_key?(id)
+      return decrypt(@otp_keys[id])
+    else
+      return nil
+    end
+  end
 
 
-	# Get an otp code
-	# @args: id -> the item id
-	# @rtrn: an otp code
-	def get_otp_code(id)
-		if not @otp_keys.has_key?(id)
-			return 0
-		else
-			return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
-		end
-	end
+  # Get an otp code
+  # @args: id -> the item id
+  # @rtrn: an otp code
+  def get_otp_code(id)
+    if not @otp_keys.has_key?(id)
+      return 0
+    else
+      return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
+    end
+  end
 
-	# Get remaining time before expire otp code
-	# @rtrn: return time in seconde
-	def get_otp_remaining_time
-		return (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
-	end
+  # Get remaining time before expire otp code
+  # @rtrn: return time in seconde
+  def get_otp_remaining_time
+    return (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
-	def self.password(options={})
-		if not options.include?(:length) or options[:length].to_i <= 0
-			length = 8
-		elsif options[:length].to_i >= 32768
-			length = 32768
-		else
-			length = options[:length].to_i
-		end
+  # Generate a random password
+  # @args: options -> :length, :special, :alpha, :numeric
+  # @rtrn: a random string
+  def self.password(options={})
+    if not options.include?(:length) or options[:length].to_i <= 0
+      length = 8
+    elsif options[:length].to_i >= 32768
+      length = 32768
+    else
+      length = options[:length].to_i
+    end
 
-		chars = []
-		chars += [*('!'..'?')] - [*('0'..'9')]        if options[:special]
-		chars += [*('A'..'Z'),*('a'..'z')]            if options[:alpha]
-		chars += [*('0'..'9')]                        if options[:numeric]
-		chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
+    chars = []
+    chars += [*('!'..'?')] - [*('0'..'9')]        if options[:special]
+    chars += [*('A'..'Z'),*('a'..'z')]            if options[:alpha]
+    chars += [*('0'..'9')]                        if options[:numeric]
+    chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
 
-		result = ''
-		while length > 62 do
-			result << chars.sample(62).join
-			length -= 62
-		end
-		result << chars.sample(length).join
+    result = ''
+    while length > 62 do
+      result << chars.sample(62).join
+      length -= 62
+    end
+    result << chars.sample(length).join
 
-		return result
-	end
+    return result
+  end
 
-	# Decrypt a gpg file
-	# @args: data -> string to decrypt
-	private
-	def decrypt(data)
-		return nil if data.to_s.empty?
+  # Decrypt a gpg file
+  # @args: data -> string to decrypt
+  private
+  def decrypt(data)
+    return nil if data.to_s.empty?
 
-		crypto = GPGME::Crypto.new(armor: true)
-		
-		return crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
-	rescue Exception => e 
-		raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
-	end
+    crypto = GPGME::Crypto.new(armor: true)
 
-	# Encrypt a file
-	# args: data -> string to encrypt
-	private
-	def encrypt(data)
-		recipients = []
-		crypto     = GPGME::Crypto.new(armor: true, always_trust: true)
+    return crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
+  rescue Exception => e
+    raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
+  end
 
-		recipients.push(@key)
-		@keys.each_key do |key|
-			next if key == @key
-			recipients.push(key)
-		end
+  # Encrypt a file
+  # args: data -> string to encrypt
+  private
+  def encrypt(data)
+    recipients = []
+    crypto     = GPGME::Crypto.new(armor: true, always_trust: true)
 
-		return crypto.encrypt(data, recipients: recipients).read
-	rescue Exception => e 
-		raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
-	end
+    recipients.push(@key)
+    @keys.each_key do |key|
+      next if key == @key
+      recipients.push(key)
+    end
+
+    return crypto.encrypt(data, recipients: recipients).read
+  rescue Exception => e
+    raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+  end
 
 end
 end
diff --git a/test/test_config.rb b/test/test_config.rb
index 143b3ed..2d1a51b 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -6,59 +6,59 @@ require 'locale'
 require 'i18n'
 
 class TestConfig < Test::Unit::TestCase
-	def setup
-		lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-		
-		if defined?(I18n.enforce_available_locales)
-			I18n.enforce_available_locales = true
-		end
-		
-		I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-		I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
-		I18n.default_locale = :en
-		I18n.locale         = lang.to_sym
-	end
+  def setup
+    lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
-	def test_00_config
-		data = { gpg_key: 'test@example.com',
-		         lang: 'en',
-		         wallet_dir: '/tmp/test',
-		         gpg_exe: '',
-		       }
+    if defined?(I18n.enforce_available_locales)
+      I18n.enforce_available_locales = true
+    end
 
-		@config = MPW::Config.new
-		@config.setup(data)
-		@config.load_config
+    I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
+    I18n.load_path      = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
+    I18n.default_locale = :en
+    I18n.locale         = lang.to_sym
+  end
 
-		data.each do |k,v|
-			assert_equal(v, @config.send(k))
-		end
+  def test_00_config
+    data = { gpg_key: 'test@example.com',
+             lang: 'en',
+             wallet_dir: '/tmp/test',
+             gpg_exe: '',
+           }
 
-		@config.setup_gpg_key('password', 'test@example.com', 2048)
-		assert(@config.check_gpg_key?)
-	end
+    @config = MPW::Config.new
+    @config.setup(data)
+    @config.load_config
 
-	def test_01_password
-		data = { pwd_alpha: false,
-		         pwd_numeric: false,
-		         pwd_special: true,
-		         pwd_length: 32,
-		       }
+    data.each do |k,v|
+      assert_equal(v, @config.send(k))
+    end
 
-		@config = MPW::Config.new
-		@config.load_config
+    @config.setup_gpg_key('password', 'test@example.com', 2048)
+    assert(@config.check_gpg_key?)
+  end
 
-		assert_equal(@config.password[:length], 16)
-		assert(@config.password[:alpha])
-		assert(@config.password[:numeric])
-		assert(!@config.password[:special])
+  def test_01_password
+    data = { pwd_alpha: false,
+             pwd_numeric: false,
+             pwd_special: true,
+             pwd_length: 32,
+           }
 
-		@config.setup(data)
-		@config.load_config
+    @config = MPW::Config.new
+    @config.load_config
 
-		assert_equal(@config.password[:length], data[:pwd_length])
-		assert(!@config.password[:alpha])
-		assert(!@config.password[:numeric])
-		assert(@config.password[:special])
-	end
+    assert_equal(@config.password[:length], 16)
+    assert(@config.password[:alpha])
+    assert(@config.password[:numeric])
+    assert(!@config.password[:special])
+
+    @config.setup(data)
+    @config.load_config
+
+    assert_equal(@config.password[:length], data[:pwd_length])
+    assert(!@config.password[:alpha])
+    assert(!@config.password[:numeric])
+    assert(@config.password[:special])
+  end
 end
diff --git a/test/test_item.rb b/test/test_item.rb
index b9f5961..751e1b0 100644
--- a/test/test_item.rb
+++ b/test/test_item.rb
@@ -1,172 +1,172 @@
 #!/usr/bin/ruby
- 
+
 require 'mpw/item'
 require 'test/unit'
 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
+  def setup
+    @fixture_file = 'test/files/fixtures.yml'
+    @fixtures = YAML.load_file(@fixture_file)
 
-		I18n.load_path      = Dir['./i18n/cli/*.yml']
-		I18n.default_locale = :en
+    if defined?(I18n.enforce_available_locales)
+      I18n.enforce_available_locales = false
+    end
+
+    I18n.load_path      = Dir['./i18n/cli/*.yml']
+    I18n.default_locale = :en
 
 
-		puts
-	end
+    puts
+  end
 
-	def test_00_add_without_name
-		assert_raise(RuntimeError){MPW::Item.new}
-	end
+  def test_00_add_without_name
+    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'],
-		       }
-		
-		item = MPW::Item.new(data)
+  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'],
+           }
 
-		assert(!item.nil?)
-		assert(!item.empty?)
+    item = MPW::Item.new(data)
 
-		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)
-	end
+    assert(!item.nil?)
+    assert(!item.empty?)
 
-	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'],
-		       }
+    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)
+  end
 
-		item = MPW::Item.new(data)
+  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'],
+           }
 
-		assert(!item.nil?)
-		assert(!item.empty?)
+    item = MPW::Item.new(data)
 
-		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)
-	end
+    assert(!item.nil?)
+    assert(!item.empty?)
 
-	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'],
-		       }
-		
-		item = MPW::Item.new(data)
+    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)
+  end
 
-		assert(!item.nil?)
-		assert(!item.empty?)
+  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'],
+           }
 
-		created   = item.created
-		last_edit = item.last_edit
+    item = MPW::Item.new(data)
 
-		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))
+    assert(!item.nil?)
+    assert(!item.empty?)
 
-		assert(!item.empty?)
+    created   = item.created
+    last_edit = item.last_edit
 
-		assert_equal(@fixtures['update']['group'],     item.group)
-		assert_equal(@fixtures['update']['host'],      item.host)
-		assert_equal(@fixtures['update']['protocol'],  item.protocol)
-		assert_equal(@fixtures['update']['user'],      item.user)
-		assert_equal(@fixtures['update']['port'].to_i, item.port)
-		assert_equal(@fixtures['update']['comment'],   item.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'],
+           }
 
-		assert_equal(created, item.created)
-		assert_not_equal(last_edit, item.last_edit)
-	end
+    sleep(1)
+    assert(item.update(data))
 
-	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'],
-		       }
-		
-		item = MPW::Item.new(data)
+    assert(!item.empty?)
 
-		assert(!item.nil?)
-		assert(!item.empty?)
+    assert_equal(@fixtures['update']['group'],     item.group)
+    assert_equal(@fixtures['update']['host'],      item.host)
+    assert_equal(@fixtures['update']['protocol'],  item.protocol)
+    assert_equal(@fixtures['update']['user'],      item.user)
+    assert_equal(@fixtures['update']['port'].to_i, item.port)
+    assert_equal(@fixtures['update']['comment'],   item.comment)
 
-		last_edit = item.last_edit
+    assert_equal(created, item.created)
+    assert_not_equal(last_edit, item.last_edit)
+  end
 
-		sleep(1)
-		assert(item.update({comment: @fixtures['update']['comment']}))
+  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'],
+           }
 
-		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_not_equal(last_edit, item.last_edit)
-	end
+    item = MPW::Item.new(data)
 
-	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'],
-		       }
-		
-		item = MPW::Item.new(data)
+    assert(!item.nil?)
+    assert(!item.empty?)
 
-		assert(!item.nil?)
-		assert(!item.empty?)
+    last_edit = item.last_edit
 
-		item.delete
-		assert(!item.nil?)
-		assert(item.empty?)
+    sleep(1)
+    assert(item.update({comment: @fixtures['update']['comment']}))
 
-		assert_equal(nil, item.id)
-		assert_equal(nil, item.group)
-		assert_equal(nil, item.host)
-		assert_equal(nil, item.protocol)
-		assert_equal(nil, item.user)
-		assert_equal(nil, item.port)
-		assert_equal(nil, item.comment)
-		assert_equal(nil, item.created)
-	end
-end 
+    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_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'],
+           }
+
+    item = MPW::Item.new(data)
+
+    assert(!item.nil?)
+    assert(!item.empty?)
+
+    item.delete
+    assert(!item.nil?)
+    assert(item.empty?)
+
+    assert_equal(nil, item.id)
+    assert_equal(nil, item.group)
+    assert_equal(nil, item.host)
+    assert_equal(nil, item.protocol)
+    assert_equal(nil, item.user)
+    assert_equal(nil, item.port)
+    assert_equal(nil, item.comment)
+    assert_equal(nil, item.created)
+  end
+end
diff --git a/test/test_mpw.rb b/test/test_mpw.rb
index c4748f6..30c9028 100644
--- a/test/test_mpw.rb
+++ b/test/test_mpw.rb
@@ -1,140 +1,140 @@
 #!/usr/bin/ruby
- 
+
 require 'mpw/mpw'
 require 'mpw/item'
 require 'test/unit'
 require 'yaml'
 require 'csv'
- 
+
 class TestMPW < Test::Unit::TestCase
-	def setup
-		fixture_file = './test/files/fixtures.yml'
+  def setup
+    fixture_file = './test/files/fixtures.yml'
 
-		wallet_file = 'default.gpg'
-		key         = 'test@example.com'
-		password    = 'password'
+    wallet_file = 'default.gpg'
+    key         = 'test@example.com'
+    password    = 'password'
 
-		if defined?(I18n.enforce_available_locales)
-			I18n.enforce_available_locales = false
-		end
+    if defined?(I18n.enforce_available_locales)
+      I18n.enforce_available_locales = false
+    end
 
-		@mpw      = MPW::MPW.new(key, wallet_file, password)
-		@fixtures = YAML.load_file(fixture_file)
-	end
- 
-	def test_00_decrypt_empty_file
-		@mpw.read_data
-		assert_equal(0, @mpw.list.length)
-	end
+    @mpw      = MPW::MPW.new(key, wallet_file, password)
+    @fixtures = YAML.load_file(fixture_file)
+  end
 
-	def test_01_encrypt_empty_file
-		@mpw.read_data
-		@mpw.write_data
-	end
+  def test_00_decrypt_empty_file
+    @mpw.read_data
+    assert_equal(0, @mpw.list.length)
+  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'],
-		       }
-		
-		item = MPW::Item.new(data)
+  def test_01_encrypt_empty_file
+    @mpw.read_data
+    @mpw.write_data
+  end
 
-		assert(!item.nil?)
-		assert(!item.empty?)
+  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'],
+           }
 
-		@mpw.read_data
-		@mpw.add(item)
-		@mpw.set_password(item.id, @fixtures['add_new']['password'])
+    item = MPW::Item.new(data)
 
-		assert_equal(1, @mpw.list.length)
+    assert(!item.nil?)
+    assert(!item.empty?)
 
-		item = @mpw.list[0]
-		@fixtures['add_new'].each do |k,v|
-			if k == 'password'
-				assert_equal(v, @mpw.get_password(item.id))
-			else
-				assert_equal(v, item.send(k).to_s)
-			end
-		end
+    @mpw.read_data
+    @mpw.add(item)
+    @mpw.set_password(item.id, @fixtures['add_new']['password'])
 
-		@mpw.write_data
-	end
+    assert_equal(1, @mpw.list.length)
 
-	def test_03_decrypt_file
-		@mpw.read_data
-		assert_equal(1, @mpw.list.length)
+    item = @mpw.list[0]
+    @fixtures['add_new'].each do |k,v|
+      if k == 'password'
+        assert_equal(v, @mpw.get_password(item.id))
+      else
+        assert_equal(v, item.send(k).to_s)
+      end
+    end
 
-		item = @mpw.list[0]
-		@fixtures['add_new'].each do |k,v|
-			if k == 'password'
-				assert_equal(v, @mpw.get_password(item.id))
-			else
-				assert_equal(v, item.send(k).to_s)
-			end
-		end
-	end
+    @mpw.write_data
+  end
 
-	def test_04_delete_item
-		@mpw.read_data
+  def test_03_decrypt_file
+    @mpw.read_data
+    assert_equal(1, @mpw.list.length)
 
-		assert_equal(1, @mpw.list.length)
+    item = @mpw.list[0]
+    @fixtures['add_new'].each do |k,v|
+      if k == 'password'
+        assert_equal(v, @mpw.get_password(item.id))
+      else
+        assert_equal(v, item.send(k).to_s)
+      end
+    end
+  end
 
-		@mpw.list.each do |item|
-			item.delete
-		end
+  def test_04_delete_item
+    @mpw.read_data
 
-		assert_equal(0, @mpw.list.length)
+    assert_equal(1, @mpw.list.length)
 
-		@mpw.write_data
-	end
+    @mpw.list.each do |item|
+      item.delete
+    end
 
-	def test_05_search
-		@mpw.read_data
+    assert_equal(0, @mpw.list.length)
 
-		@fixtures.each_value do |v|
-			data = { group:    v['group'],
-			         host:     v['host'],
-			         protocol: v['protocol'],
-			         user:     v['user'],
-			         port:     v['port'],
-			         comment:  v['comment'],
-			       }
-			
-			item = MPW::Item.new(data)
-	
-			assert(!item.nil?)
-			assert(!item.empty?)
-	
-			@mpw.add(item)
-			@mpw.set_password(item.id, v['password'])
-		end
+    @mpw.write_data
+  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)
-	end
+  def test_05_search
+    @mpw.read_data
 
-	def test_06_add_gpg_key
-		@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'],
+             }
 
-		@mpw.add_key('test2@example.com')
-		assert_equal(2, @mpw.list_keys.length)
+      item = MPW::Item.new(data)
 
-		@mpw.write_data
-	end
+      assert(!item.nil?)
+      assert(!item.empty?)
 
-	def test_07_delete_gpg_key
-		@mpw.read_data
-		assert_equal(2, @mpw.list_keys.length)
+      @mpw.add(item)
+      @mpw.set_password(item.id, v['password'])
+    end
 
-		@mpw.delete_key('test2@example.com')
-		assert_equal(1, @mpw.list_keys.length)
+    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)
+  end
 
-		@mpw.write_data
-	end
+  def test_06_add_gpg_key
+    @mpw.read_data
+
+    @mpw.add_key('test2@example.com')
+    assert_equal(2, @mpw.list_keys.length)
+
+    @mpw.write_data
+  end
+
+  def test_07_delete_gpg_key
+    @mpw.read_data
+    assert_equal(2, @mpw.list_keys.length)
+
+    @mpw.delete_key('test2@example.com')
+    assert_equal(1, @mpw.list_keys.length)
+
+    @mpw.write_data
+  end
 end
diff --git a/test/test_translate.rb b/test/test_translate.rb
index 55f09df..6334a78 100644
--- a/test/test_translate.rb
+++ b/test/test_translate.rb
@@ -4,28 +4,28 @@ require 'yaml'
 require 'test/unit'
 
 class TestTranslate < Test::Unit::TestCase
-	def test_00_check_translate
-		missing = 0
-		
-		Dir.glob('i18n/*.yml').each do |yaml|
-			lang      = File.basename(yaml, '.yml')
-			translate = YAML.load_file(yaml)
-		
-			`grep -r -o "I18n.t('.*)" bin/ lib/ | cut -d"'" -f2`.each_line do |line|
-				begin
-					t = translate[lang]
-					line.strip.split('.').each do |v|
-						t = t[v]
-					end
+  def test_00_check_translate
+    missing = 0
 
-					assert(!t.to_s.empty?)
-				rescue
-					puts "#{lang}.#{line}"
-					missing = 1
-				end
-			end
-		end
-		
-		assert_equal(0, missing)
-	end
+    Dir.glob('i18n/*.yml').each do |yaml|
+      lang      = File.basename(yaml, '.yml')
+      translate = YAML.load_file(yaml)
+
+      `grep -r -o "I18n.t('.*)" bin/ lib/ | cut -d"'" -f2`.each_line do |line|
+        begin
+          t = translate[lang]
+          line.strip.split('.').each do |v|
+            t = t[v]
+          end
+
+          assert(!t.to_s.empty?)
+        rescue
+          puts "#{lang}.#{line}"
+          missing = 1
+        end
+      end
+    end
+
+    assert_equal(0, missing)
+  end
 end
diff --git a/test/tests.rb b/test/tests.rb
index 904f64e..7f3fc7d 100644
--- a/test/tests.rb
+++ b/test/tests.rb
@@ -1,5 +1,5 @@
 #!/usr/bin/ruby
- 
+
 require_relative 'init.rb'
 require_relative 'test_config.rb'
 require_relative 'test_item.rb'

From 178d0e6d1d3d32ad765fc4203cf1ee2e5575024f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 27 Mar 2017 22:31:05 +0200
Subject: [PATCH 423/531] replace if not by unless

---
 bin/mpw-wallet            |  2 +-
 lib/mpw/cli.rb            | 12 +++++-------
 lib/mpw/config.rb         |  2 +-
 lib/mpw/item.rb           |  2 +-
 lib/mpw/mpw.rb            | 18 +++++++-----------
 templates/add_form.erb    |  2 +-
 templates/update_form.erb |  2 +-
 7 files changed, 17 insertions(+), 23 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index e575c0d..bcc3fc2 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -66,7 +66,7 @@ cli    = MPW::Cli.new(config)
 
 cli.load_config
 
-if not options[:list].nil?
+if !options[:list].nil?
   cli.list_wallet
 else
   cli.get_wallet(options[:wallet])
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 83b9af3..9571fec 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -103,7 +103,7 @@ class Cli
 
   # Request the GPG password and decrypt the file
   def decrypt
-    if not defined?(@mpw)
+    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)
     end
@@ -451,9 +451,7 @@ class Cli
       item    = get_item(items)
       confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
 
-      if not confirm =~ /^(y|yes|YES|Yes|Y)$/
-        return false
-      end
+      return false unless confirm =~ /^(y|yes|YES|Yes|Y)$/
 
       item.delete
       @mpw.write_data
@@ -516,7 +514,7 @@ class Cli
   # @args: file -> the 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') if not File.exist?(file)
+    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'],
@@ -530,8 +528,8 @@ class Cli
       next if item.empty?
 
       @mpw.add(item)
-      @mpw.set_password(item.id, row['password']) if not row['password'].to_s.empty?
-      @mpw.set_otp_key(item.id, row['otp_key'])   if not row['otp_key'].to_s.empty?
+      @mpw.set_password(item.id, row['password']) unless row['password'].to_s.empty?
+      @mpw.set_otp_key(item.id, row['otp_key'])   unless row['otp_key'].to_s.empty?
     end
 
     @mpw.write_data
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 298ba61..8ec8e3a 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -73,7 +73,7 @@ class Config
       end
     end
 
-    if not gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+    unless gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
       raise I18n.t('error.config.key_bad_format')
     end
 
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 469af15..7c2547e 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -68,7 +68,7 @@ class Item
     @port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
     @otp       = options[:otp]        if options.has_key?(:otp)
     @comment   = options[:comment]    if options.has_key?(:comment)
-    @last_edit = Time.now.to_i        if not options.has_key?(:no_update_last_edit)
+    @last_edit = Time.now.to_i        unless options.has_key?(:no_update_last_edit)
   end
 
   # Delete all data
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 9147648..bb4e38b 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -33,7 +33,7 @@ class MPW
     @gpg_exe     = gpg_exe
     @wallet_file = wallet_file
 
-    if not @gpg_exe.to_s.empty?
+    unless @gpg_exe.to_s.empty?
       GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
     end
   end
@@ -48,7 +48,7 @@ class MPW
 
     data       = nil
 
-    return if not File.exists?(@wallet_file)
+    return unless File.exists?(@wallet_file)
 
     Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
       tar.each do |f|
@@ -80,7 +80,7 @@ class MPW
       end
     end
 
-    if not data.nil? and not data.empty?
+    unless data.to_s.empty?
       YAML.load(data).each_value do |d|
         @data.push(Item.new(id:        d['id'],
                             group:     d['group'],
@@ -254,14 +254,12 @@ class MPW
 
     @data.each do |item|
       next if item.empty?
-      next if not group.empty? and not group.eql?(item.group.to_s.downcase)
+      next unless group.empty? or group.eql?(item.group.to_s.downcase)
 
       host    = item.host.to_s.downcase
       comment = item.comment.to_s.downcase
 
-      if not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/
-        next
-      end
+      next unless host =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/
 
       result.push(item)
     end
@@ -284,9 +282,7 @@ class MPW
   # args: id -> the item id
   #       key -> the new key
   def set_otp_key(id, key)
-    if not key.to_s.empty?
-      @otp_keys[id] = encrypt(key.to_s)
-    end
+    @otp_keys[id] = encrypt(key.to_s) unless key.to_s.empty?
   end
 
   # Get an opt key
@@ -305,7 +301,7 @@ class MPW
   # @args: id -> the item id
   # @rtrn: an otp code
   def get_otp_code(id)
-    if not @otp_keys.has_key?(id)
+    unless @otp_keys.has_key?(id)
       return 0
     else
       return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
diff --git a/templates/add_form.erb b/templates/add_form.erb
index c2d6d73..bb81787 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -2,7 +2,7 @@
 host:           # <%= I18n.t('form.add_item.host') %>
 user:           # <%= I18n.t('form.add_item.login') %>
 group:          # <%= I18n.t('form.add_item.group') %> 
-protocol:       # <%= I18n.t('form.add_item.protocol') %><% if not password %>
+protocol:       # <%= I18n.t('form.add_item.protocol') %><% unless password %>
 password:       # <%= I18n.t('form.add_item.password') %><% end %>
 port:           # <%= I18n.t('form.add_item.port') %>
 comment:        # <%= I18n.t('form.add_item.comment') %>
diff --git a/templates/update_form.erb b/templates/update_form.erb
index e5363f8..f27da0b 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -2,7 +2,7 @@
 # <%= I18n.t('form.update_item.host') %>
 host: <%= item.host %>
 # <%= I18n.t('form.update_item.login') %>
-user: <%= item.user %><% if not password %>
+user: <%= item.user %><% unless password %>
 # <%= I18n.t('form.update_item.password') %>
 password: <% end %>
 # <%= I18n.t('form.update_item.group') %>

From e71f8e2acf36e74c81ba624405fd29ab94da06e2 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:49:46 +0200
Subject: [PATCH 424/531] replace if not by unless

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index bb4e38b..724dcaf 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -234,13 +234,10 @@ class MPW
   # Add a new item
   # @args: item -> Object MPW::Item
   def add(item)
-    if not item.instance_of?(Item)
-      raise I18n.t('error.bad_class')
-    elsif item.empty?
-      raise I18n.t('error.empty')
-    else
-      @data.push(item)
-    end
+    raise I18n.t('error.bad_class') unless item.instance_of?(Item)
+    raise I18n.t('error.empty')     if item.empty?
+
+    return @data.push(item)
   end
 
   # Search in some csv data

From 8ed90c2ddc7de2eac70d36c528869edd8cb846d5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:50:47 +0200
Subject: [PATCH 425/531] fix

---
 bin/mpw-wallet    | 4 ++--
 lib/mpw/cli.rb    | 6 +++---
 lib/mpw/config.rb | 6 +++---
 lib/mpw/item.rb   | 8 ++++----
 lib/mpw/mpw.rb    | 8 ++++----
 5 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index bcc3fc2..fffeaac 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -72,9 +72,9 @@ else
   cli.get_wallet(options[:wallet])
   cli.decrypt
 
-  if not options[:list_keys].nil?
+  if !options[:list_keys].nil?
     cli.list_keys
-  elsif not options[:gpg_key].nil?
+  elsif !options[:gpg_key].nil?
     if options[:delete]
       cli.delete_key(options[:gpg_key])
     else
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 9571fec..6b56c63 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -155,7 +155,7 @@ class Cli
 
     items.each do |item|
       data.each do |k, v|
-        next if k == :id or k == :otp
+        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]
       end
@@ -240,7 +240,7 @@ class Cli
     items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
     choice = ask(I18n.t('form.select')).to_i
 
-    if choice >= 1 and choice <= items.length
+    if choice >= 1 && choice <= items.length
       return items[choice-1]
     else
       return nil
@@ -331,7 +331,7 @@ class Cli
 
       if wallets.length == 1
         @wallet_file = wallets[0]
-      elsif not @config.default_wallet.to_s.empty?
+      elsif !@config.default_wallet.to_s.empty?
         @wallet_file = "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
       else
         @wallet_file = "#{@config.wallet_dir}/default.mpw"
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 8ec8e3a..cd584b1 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -47,7 +47,7 @@ class Config
       @config_dir = "#{Dir.home}/.config/mpw"
     end
 
-    @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? or @config_file.empty?
+    @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? || @config_file.empty?
   end
 
   # Create a new config file
@@ -68,7 +68,7 @@ class Config
     ['numeric', 'special', 'alpha', 'length'].each do |k|
       if options.has_key?("pwd_#{k}".to_sym)
         password[k.to_sym] = options["pwd_#{k}".to_sym]
-      elsif not @password.nil? and @password.has_key?(k.to_sym)
+      elsif !@password.nil? && @password.has_key?(k.to_sym)
         password[k.to_sym] = @password[k.to_sym]
       end
     end
@@ -138,7 +138,7 @@ class Config
     @gpg_exe        = config['gpg_exe']
     @password       = config['password'] || {}
 
-    raise if @gpg_key.empty? or @wallet_dir.empty?
+    raise if @gpg_key.empty? || @wallet_dir.empty?
 
     I18n.locale = @lang.to_sym
   rescue Exception => e
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 7c2547e..c0ca11e 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -37,11 +37,11 @@ class Item
   # @args: options -> a hash of parameter
   # raise an error if the hash hasn't the key name
   def initialize(options={})
-    if not options.has_key?(:host) or options[:host].to_s.empty?
+    if !options.has_key?(:host) || options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
 
-    if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
+    if !options.has_key?(:id) || options[:id].to_s.empty? || !options.has_key?(:created) || options[:created].to_s.empty?
       @id = generate_id
       @created = Time.now.to_i
     else
@@ -57,7 +57,7 @@ class Item
   # Update the item
   # @args: options -> a hash of parameter
   def update(options={})
-    if options.has_key?(:host) and options[:host].to_s.empty?
+    if options.has_key?(:host) && options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
 
@@ -65,7 +65,7 @@ class Item
     @host      = options[:host]       if options.has_key?(:host)
     @protocol  = options[:protocol]   if options.has_key?(:protocol)
     @user      = options[:user]       if options.has_key?(:user)
-    @port      = options[:port].to_i  if options.has_key?(:port) and not options[:port].to_s.empty?
+    @port      = options[:port].to_i  if options.has_key?(:port) && !options[:port].to_s.empty?
     @otp       = options[:otp]        if options.has_key?(:otp)
     @comment   = options[:comment]    if options.has_key?(:comment)
     @last_edit = Time.now.to_i        unless options.has_key?(:no_update_last_edit)
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 724dcaf..183b888 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -237,7 +237,7 @@ class MPW
     raise I18n.t('error.bad_class') unless item.instance_of?(Item)
     raise I18n.t('error.empty')     if item.empty?
 
-    return @data.push(item)
+    @data.push(item)
   end
 
   # Search in some csv data
@@ -251,12 +251,12 @@ class MPW
 
     @data.each do |item|
       next if item.empty?
-      next unless group.empty? or group.eql?(item.group.to_s.downcase)
+      next unless group.empty? || group.eql?(item.group.to_s.downcase)
 
       host    = item.host.to_s.downcase
       comment = item.comment.to_s.downcase
 
-      next unless host =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/
+      next unless host =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/
 
       result.push(item)
     end
@@ -315,7 +315,7 @@ class MPW
   # @args: options -> :length, :special, :alpha, :numeric
   # @rtrn: a random string
   def self.password(options={})
-    if not options.include?(:length) or options[:length].to_i <= 0
+    if !options.include?(:length) || options[:length].to_i <= 0
       length = 8
     elsif options[:length].to_i >= 32768
       length = 32768

From e7fff4bb87aa97ffac32d9ee5eac50aec813bc7b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 22:17:52 +0200
Subject: [PATCH 426/531] remove return if useless

---
 lib/mpw/cli.rb    | 10 +++-------
 lib/mpw/config.rb |  2 +-
 lib/mpw/item.rb   |  6 +++---
 lib/mpw/mpw.rb    | 32 +++++++++++---------------------
 4 files changed, 18 insertions(+), 32 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 6b56c63..ecd98e3 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -240,11 +240,7 @@ class Cli
     items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
     choice = ask(I18n.t('form.select')).to_i
 
-    if choice >= 1 && choice <= items.length
-      return items[choice-1]
-    else
-      return nil
-    end
+    choice >= 1 && choice <= items.length ? items[choice-1] : nil
   end
 
   # Copy in clipboard the login and password
@@ -392,7 +388,7 @@ class Cli
       options[k.to_sym] = v
     end
 
-    return options
+    options
   end
 
   # Form to add a new item
@@ -451,7 +447,7 @@ class Cli
       item    = get_item(items)
       confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
 
-      return false unless confirm =~ /^(y|yes|YES|Yes|Y)$/
+      return unless confirm =~ /^(y|yes|YES|Yes|Y)$/
 
       item.delete
       @mpw.write_data
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index cd584b1..b23842e 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -153,7 +153,7 @@ class Config
       return true
     end
 
-    return false
+    false
   end
 end
 end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index c0ca11e..bd1445b 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -86,17 +86,17 @@ class Item
   end
 
   def empty?
-    return @id.to_s.empty?
+    @id.to_s.empty?
   end
 
   def nil?
-    return false
+    false
   end
 
   # Generate an random id
   private
   def generate_id
-    return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
+    ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
   end
 end
 end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 183b888..de99440 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -168,9 +168,9 @@ class MPW
     password = decrypt(@passwords[id])
 
     if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
-      return Regexp.last_match('password')
+      Regexp.last_match('password')
     else
-      return password
+      password
     end
   end
 
@@ -187,7 +187,7 @@ class MPW
   # Return the list of all gpg keys
   # rtrn: an array with the gpg keys name
   def list_keys
-    return @keys.keys
+    @keys.keys
   end
 
   # Add a public key
@@ -261,7 +261,7 @@ class MPW
       result.push(item)
     end
 
-    return result
+    result
   end
 
   # Search in some csv data
@@ -272,7 +272,7 @@ class MPW
       return item if item.id == id
     end
 
-    return nil
+    nil
   end
 
   # Set an opt key
@@ -286,29 +286,20 @@ class MPW
   # args: id -> the item id
   #       key -> the new key
   def get_otp_key(id)
-    if @otp_keys.has_key?(id)
-      return decrypt(@otp_keys[id])
-    else
-      return nil
-    end
+    @otp_keys.has_key?(id) ? decrypt(@otp_keys[id]) : nil
   end
 
-
   # Get an otp code
   # @args: id -> the item id
   # @rtrn: an otp code
   def get_otp_code(id)
-    unless @otp_keys.has_key?(id)
-      return 0
-    else
-      return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
-    end
+    @otp_keys.has_key?(id) ? 0 : ROTP::TOTP.new(decrypt(@otp_keys[id])).now
   end
 
   # Get remaining time before expire otp code
   # @rtrn: return time in seconde
   def get_otp_remaining_time
-    return (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
+    (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
   end
 
   # Generate a random password
@@ -336,7 +327,7 @@ class MPW
     end
     result << chars.sample(length).join
 
-    return result
+    result
   end
 
   # Decrypt a gpg file
@@ -347,7 +338,7 @@ class MPW
 
     crypto = GPGME::Crypto.new(armor: true)
 
-    return crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
+    crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
   rescue Exception => e
     raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
   end
@@ -365,10 +356,9 @@ class MPW
       recipients.push(key)
     end
 
-    return crypto.encrypt(data, recipients: recipients).read
+    crypto.encrypt(data, recipients: recipients).read
   rescue Exception => e
     raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
   end
-
 end
 end

From 31aba3b13c4a9d87cbfad1fee8aa16abdec4c0d9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 22:22:09 +0200
Subject: [PATCH 427/531] replace hash method has_key? by key?

---
 bin/mpw-config    |  2 +-
 lib/mpw/cli.rb    |  8 ++++----
 lib/mpw/config.rb |  4 ++--
 lib/mpw/item.rb   | 22 +++++++++++-----------
 lib/mpw/mpw.rb    | 18 +++++++++---------
 5 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 14387d9..4740ed1 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -96,7 +96,7 @@ end.parse!
 config = MPW::Config.new(options[:config])
 cli    = MPW::Cli.new(config, nil)
 
-if options.has_key?(:init)
+if options.key?(:init)
   cli.setup(values)
   cli.load_config
   cli.get_wallet
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index ecd98e3..90f7665 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -399,8 +399,8 @@ class Cli
     options[:password] = MPW::password(@config.password) if password
 
     @mpw.add(item)
-    @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
-    @mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
+    @mpw.set_password(item.id, options[:password]) if options.key?(:password)
+    @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
     @mpw.write_data
 
     puts "#{I18n.t('form.add_item.valid')}".green
@@ -424,8 +424,8 @@ class Cli
             options[:password] = MPW::password(@config.password) if password
 
       item.update(options)
-      @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
-      @mpw.set_otp_key(item.id, options[:otp_key])   if options.has_key?(:otp_key)
+      @mpw.set_password(item.id, options[:password]) if options.key?(:password)
+      @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
       @mpw.write_data
 
       puts "#{I18n.t('form.update_item.valid')}".green
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index b23842e..b7cf5e8 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -66,9 +66,9 @@ class Config
                      }
 
     ['numeric', 'special', 'alpha', 'length'].each do |k|
-      if options.has_key?("pwd_#{k}".to_sym)
+      if options.key?("pwd_#{k}".to_sym)
         password[k.to_sym] = options["pwd_#{k}".to_sym]
-      elsif !@password.nil? && @password.has_key?(k.to_sym)
+      elsif !@password.nil? && @password.key?(k.to_sym)
         password[k.to_sym] = @password[k.to_sym]
       end
     end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index bd1445b..6e99f6a 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -37,11 +37,11 @@ class Item
   # @args: options -> a hash of parameter
   # raise an error if the hash hasn't the key name
   def initialize(options={})
-    if !options.has_key?(:host) || options[:host].to_s.empty?
+    if !options.key?(:host) || options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
 
-    if !options.has_key?(:id) || options[:id].to_s.empty? || !options.has_key?(:created) || options[:created].to_s.empty?
+    if !options.key?(:id) || options[:id].to_s.empty? || !options.key?(:created) || options[:created].to_s.empty?
       @id = generate_id
       @created = Time.now.to_i
     else
@@ -57,18 +57,18 @@ class Item
   # Update the item
   # @args: options -> a hash of parameter
   def update(options={})
-    if options.has_key?(:host) && options[:host].to_s.empty?
+    if options.key?(:host) && options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
 
-    @group     = options[:group]      if options.has_key?(:group)
-    @host      = options[:host]       if options.has_key?(:host)
-    @protocol  = options[:protocol]   if options.has_key?(:protocol)
-    @user      = options[:user]       if options.has_key?(:user)
-    @port      = options[:port].to_i  if options.has_key?(:port) && !options[:port].to_s.empty?
-    @otp       = options[:otp]        if options.has_key?(:otp)
-    @comment   = options[:comment]    if options.has_key?(:comment)
-    @last_edit = Time.now.to_i        unless options.has_key?(:no_update_last_edit)
+    @group     = options[:group]      if options.key?(:group)
+    @host      = options[:host]       if options.key?(:host)
+    @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)
   end
 
   # Delete all data
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index de99440..a957ef0 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -88,7 +88,7 @@ class MPW
                             protocol:  d['protocol'],
                             user:      d['user'],
                             port:      d['port'],
-                            otp:       @otp_keys.has_key?(d['id']),
+                            otp:       @otp_keys.key?(d['id']),
                             comment:   d['comment'],
                             last_edit: d['last_edit'],
                             created:   d['created'],
@@ -223,12 +223,12 @@ class MPW
   def set_config(options={})
     @config              = {} if @config.nil?
 
-    @config['protocol']  = options[:protocol] if options.has_key?(:protocol)
-    @config['host']      = options[:host]     if options.has_key?(:host)
-    @config['port']      = options[:port]     if options.has_key?(:port)
-    @config['user']      = options[:user]     if options.has_key?(:user)
-    @config['password']  = options[:password] if options.has_key?(:password)
-    @config['path']      = options[:path]     if options.has_key?(:path)
+    @config['protocol']  = options[:protocol] if options.key?(:protocol)
+    @config['host']      = options[:host]     if options.key?(:host)
+    @config['port']      = options[:port]     if options.key?(:port)
+    @config['user']      = options[:user]     if options.key?(:user)
+    @config['password']  = options[:password] if options.key?(:password)
+    @config['path']      = options[:path]     if options.key?(:path)
   end
 
   # Add a new item
@@ -286,14 +286,14 @@ class MPW
   # args: id -> the item id
   #       key -> the new key
   def get_otp_key(id)
-    @otp_keys.has_key?(id) ? decrypt(@otp_keys[id]) : nil
+    @otp_keys.key?(id) ? decrypt(@otp_keys[id]) : nil
   end
 
   # Get an otp code
   # @args: id -> the item id
   # @rtrn: an otp code
   def get_otp_code(id)
-    @otp_keys.has_key?(id) ? 0 : ROTP::TOTP.new(decrypt(@otp_keys[id])).now
+    @otp_keys.key?(id) ? 0 : ROTP::TOTP.new(decrypt(@otp_keys[id])).now
   end
 
   # Get remaining time before expire otp code

From 351499aef6c76b99f75f6789c2440b5c387bc39c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 22:56:23 +0200
Subject: [PATCH 428/531] replace MPW::password to MPW.password replace
 YAML::load_file to YAML.load_file replace YAML::load to YAML.safe_load

---
 bin/mpw-genpwd    | 2 +-
 lib/mpw/cli.rb    | 8 ++++----
 lib/mpw/config.rb | 2 +-
 lib/mpw/mpw.rb    | 6 +++---
 4 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd
index 0370732..1c09489 100644
--- a/bin/mpw-genpwd
+++ b/bin/mpw-genpwd
@@ -46,5 +46,5 @@ OptionParser.new do |opts|
   end
 end.parse!
 
-puts MPW::MPW::password(options)
+puts MPW::MPW.password(options)
 exit 0
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 90f7665..7f406b7 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -379,7 +379,7 @@ class Cli
 
       system("#{editor} #{tmp_file}")
 
-      opts = YAML::load_file(tmp_file)
+      opts = YAML.load_file(tmp_file)
     end
 
     opts.delete_if { |k,v| v.to_s.empty? }
@@ -396,7 +396,7 @@ class Cli
   def add(password=false)
     options            = text_editor('add_form', nil, password)
     item               = Item.new(options)
-    options[:password] = MPW::password(@config.password) if password
+    options[:password] = MPW.password(@config.password) if password
 
     @mpw.add(item)
     @mpw.set_password(item.id, options[:password]) if options.key?(:password)
@@ -421,7 +421,7 @@ class Cli
 
       item               = get_item(items)
       options            = text_editor('update_form', item, password)
-            options[:password] = MPW::password(@config.password) if password
+            options[:password] = MPW.password(@config.password) if password
 
       item.update(options)
       @mpw.set_password(item.id, options[:password]) if options.key?(:password)
@@ -512,7 +512,7 @@ class Cli
     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|
+    YAML.load_file(file).each_value do |row|
       item = Item.new(group:    row['group'],
                       host:     row['host'],
                       protocol: row['protocol'],
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index b7cf5e8..6f3632c 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -130,7 +130,7 @@ class Config
 
   # Load the config file
   def load_config
-    config          = YAML::load_file(@config_file)
+    config          = YAML.load_file(@config_file)
     @gpg_key        = config['gpg_key']
     @lang           = config['lang']
     @wallet_dir     = config['wallet_dir']
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index a957ef0..70f3847 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -54,7 +54,7 @@ class MPW
       tar.each do |f|
         case f.full_name
           when 'wallet/config.gpg'
-            @config = YAML.load(decrypt(f.read))
+            @config = YAML.safe_load(decrypt(f.read))
 
           when 'wallet/meta.gpg'
             data = decrypt(f.read)
@@ -81,7 +81,7 @@ class MPW
     end
 
     unless data.to_s.empty?
-      YAML.load(data).each_value do |d|
+      YAML.safe_load(data).each_value do |d|
         @data.push(Item.new(id:        d['id'],
                             group:     d['group'],
                             host:      d['host'],
@@ -178,7 +178,7 @@ class MPW
   # args: id -> the item id
   #       password -> the new password
   def set_password(id, password)
-    salt     = MPW::password(length: Random.rand(4..9))
+    salt     = MPW.password(length: Random.rand(4..9))
     password = "$#{salt}::#{password}"
 
     @passwords[id] = encrypt(password)

From db6bb2b0f6fef4cd97b0223b8bea07b552e5c84d Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 23:04:54 +0200
Subject: [PATCH 429/531] replace  "#{var}" by var.to_s

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 7f406b7..1807f75 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -40,7 +40,7 @@ class Cli
   def set_config(options)
     @config.setup(options)
 
-    puts "#{I18n.t('form.set_config.valid')}".green
+    puts I18n.t('form.set_config.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #15: #{e}".red
     exit 2
@@ -57,7 +57,7 @@ class Cli
 
     load_config
 
-    puts "#{I18n.t('form.setup_config.valid')}".green
+    puts I18n.t('form.setup_config.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #8: #{e}".red
     exit 2
@@ -81,7 +81,7 @@ class Cli
 
     @config.setup_gpg_key(@password, gpg_key)
 
-    puts "#{I18n.t('form.setup_gpg_key.valid')}".green
+    puts I18n.t('form.setup_gpg_key.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #8: #{e}".red
     exit 2
@@ -208,7 +208,7 @@ class Cli
         end
 
         print '| '
-        print "#{item.send(k.to_s)}".send(v[:color])
+        print item.send(k.to_s).to_s.send(v[:color])
         (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
       end
       print "\n"
@@ -343,7 +343,7 @@ class Cli
     @mpw.add_key(key)
     @mpw.write_data
 
-    puts "#{I18n.t('form.add_key.valid')}".green
+    puts I18n.t('form.add_key.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #13: #{e}".red
   end
@@ -354,7 +354,7 @@ class Cli
     @mpw.delete_key(key)
     @mpw.write_data
 
-    puts "#{I18n.t('form.delete_key.valid')}".green
+    puts I18n.t('form.delete_key.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #15: #{e}".red
   end
@@ -403,7 +403,7 @@ class Cli
     @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
     @mpw.write_data
 
-    puts "#{I18n.t('form.add_item.valid')}".green
+    puts I18n.t('form.add_item.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #13: #{e}".red
   end
@@ -428,7 +428,7 @@ class Cli
       @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
       @mpw.write_data
 
-      puts "#{I18n.t('form.update_item.valid')}".green
+      puts I18n.t('form.update_item.valid').to_s.green
     end
   rescue Exception => e
     puts "#{I18n.t('display.error')} #14: #{e}".red
@@ -452,7 +452,7 @@ class Cli
       item.delete
       @mpw.write_data
 
-      puts "#{I18n.t('form.delete_item.valid')}".green
+      puts I18n.t('form.delete_item.valid').to_s.green
     end
   rescue Exception => e
     puts "#{I18n.t('display.error')} #16: #{e}".red
@@ -501,7 +501,7 @@ class Cli
 
     File.open(file, 'w') {|f| f << data.to_yaml}
 
-    puts "#{I18n.t('form.export.valid', file: file)}".green
+    puts I18n.t('form.export.valid', file: file).to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #17: #{e}".red
   end
@@ -530,7 +530,7 @@ class Cli
 
     @mpw.write_data
 
-    puts "#{I18n.t('form.import.valid')}".green
+    puts I18n.t('form.import.valid').to_s.green
   rescue Exception => e
     puts "#{I18n.t('display.error')} #18: #{e}".red
   end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 6f3632c..be99f31 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -104,9 +104,9 @@ class Config
   # @rtrn: true if the GPG key is create, else false
   def setup_gpg_key(password, name, length = 4096, expire = 0)
     if name.to_s.empty?
-      raise "#{I18n.t('error.config.genkey_gpg.name')}"
+      raise I18n.t('error.config.genkey_gpg.name')
     elsif password.to_s.empty?
-      raise "#{I18n.t('error.config.genkey_gpg.password')}"
+      raise I18n.t('error.config.genkey_gpg.password')
     end
 
     param = ''

From e99b18519db246afeccb990959f602e4e6b5b79b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 23:13:45 +0200
Subject: [PATCH 430/531] replace var.length == 0 by var.empty?

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 1807f75..991388c 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -224,7 +224,7 @@ class Cli
   def list(options={})
     result = @mpw.list(options)
 
-    if result.length == 0
+    if result.empty?
       puts I18n.t('display.nothing')
     else
       table_items(result)
@@ -414,7 +414,7 @@ class Cli
   def update(password=false, options={})
     items = @mpw.list(options)
 
-    if items.length == 0
+    if items.empty?
       puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
     else
       table_items(items) if items.length > 1
@@ -439,7 +439,7 @@ class Cli
   def delete(options={})
     items = @mpw.list(options)
 
-    if items.length == 0
+    if items.empty?
       puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
     else
       table_items(items)
@@ -464,7 +464,7 @@ class Cli
   def copy(clipboard=true, options={})
     items = @mpw.list(options)
 
-    if items.length == 0
+    if items.empty?
       puts I18n.t('display.nothing')
     else
       table_items(items)
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 70f3847..811a7a1 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -62,7 +62,7 @@ class MPW
           when /^wallet\/keys\/(?<key>.+)\.pub$/
             key = Regexp.last_match('key')
 
-            if GPGME::Key.find(:public, key).length == 0
+            if GPGME::Key.find(:public, key).empty?
               GPGME::Key.import(f.read, armor: true)
             end
 

From 6a6120ca1448363f0fd21264c05ff2ad9fa9e44a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 23:24:22 +0200
Subject: [PATCH 431/531] use %w for words array

---
 lib/mpw/config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index be99f31..915e603 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -65,7 +65,7 @@ class Config
                        length:  16,
                      }
 
-    ['numeric', 'special', 'alpha', 'length'].each do |k|
+    %w(numeric special alpha length).each do |k|
       if options.key?("pwd_#{k}".to_sym)
         password[k.to_sym] = options["pwd_#{k}".to_sym]
       elsif !@password.nil? && @password.key?(k.to_sym)

From 7a831555f49fe0a2dbdb0d25edb6e64a4d36ed42 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 23:29:33 +0200
Subject: [PATCH 432/531] fix spacing

---
 lib/mpw/cli.rb    | 38 ++++++++++++++++++-------------------
 lib/mpw/config.rb |  2 +-
 lib/mpw/item.rb   |  6 +++---
 lib/mpw/mpw.rb    | 48 +++++++++++++++++++++++------------------------
 4 files changed, 47 insertions(+), 47 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 991388c..300318a 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -68,8 +68,8 @@ class Cli
   def setup_gpg_key(gpg_key)
     return if @config.check_gpg_key?
 
-    password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
-    confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
+    password = ask(I18n.t('form.setup_gpg_key.password')) { |q| q.echo = false }
+    confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) { |q| q.echo = false }
 
     if password != confirm
       raise I18n.t('form.setup_gpg_key.error_password')
@@ -104,7 +104,7 @@ class Cli
   # Request the GPG password and decrypt the file
   def decrypt
     unless defined?(@mpw)
-      @password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
+      @password = ask(I18n.t('display.gpg_password')) { |q| q.echo = false }
       @mpw      = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe)
     end
 
@@ -140,7 +140,7 @@ class Cli
   end
 
   # Format items on a table
-  def table_items(items=[])
+  def table_items(items = [])
     group        = '.'
     i            = 1
     length_total = 10
@@ -160,7 +160,7 @@ class Cli
         v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
       end
     end
-    data[:id][:length]  = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
+    data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
 
     data.each_value { |v| length_total += v[:length] }
     items.sort!     { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
@@ -221,7 +221,7 @@ class Cli
 
   # Display the query's result
   # @args: options -> the option to search
-  def list(options={})
+  def list(options = {})
     result = @mpw.list(options)
 
     if result.empty?
@@ -237,16 +237,16 @@ class Cli
   def get_item(items)
     return items[0] if items.length == 1
 
-    items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+    items.sort! { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
     choice = ask(I18n.t('form.select')).to_i
 
-    choice >= 1 && choice <= items.length ? items[choice-1] : nil
+    choice >= 1 && choice <= items.length ? items[choice - 1] : nil
   end
 
   # Copy in clipboard the login and password
   # @args: item -> the item
   #        clipboard -> enable clipboard
-  def clipboard(item, clipboard=true)
+  def clipboard(item, clipboard = true)
     pid = nil
 
     # Security: force quit after 90s
@@ -321,7 +321,7 @@ class Cli
 
   # Display the wallet
   # @args: wallet -> the wallet name
-  def get_wallet(wallet=nil)
+  def get_wallet(wallet = nil)
     if wallet.to_s.empty?
       wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
 
@@ -363,7 +363,7 @@ class Cli
   # @args: template -> template name
   #        item -> the item to edit
   #        password -> disable field password
-  def text_editor(template_name, item=nil, password=false)
+  def text_editor(template_name, item = nil, password = false)
     editor        = ENV['EDITOR'] || 'nano'
     options       = {}
     opts          = {}
@@ -382,9 +382,9 @@ class Cli
       opts = YAML.load_file(tmp_file)
     end
 
-    opts.delete_if { |k,v| v.to_s.empty? }
+    opts.delete_if { |k, v| v.to_s.empty? }
 
-    opts.each do |k,v|
+    opts.each do |k, v|
       options[k.to_sym] = v
     end
 
@@ -393,7 +393,7 @@ class Cli
 
   # Form to add a new item
   # @args: password -> generate a random password
-  def add(password=false)
+  def add(password = false)
     options            = text_editor('add_form', nil, password)
     item               = Item.new(options)
     options[:password] = MPW.password(@config.password) if password
@@ -411,7 +411,7 @@ class Cli
   # Update an item
   # @args: password -> generate a random password
   #        options -> the option to search
-  def update(password=false, options={})
+  def update(password = false, options = {})
     items = @mpw.list(options)
 
     if items.empty?
@@ -421,7 +421,7 @@ class Cli
 
       item               = get_item(items)
       options            = text_editor('update_form', item, password)
-            options[:password] = MPW.password(@config.password) if password
+      options[:password] = MPW.password(@config.password) if password
 
       item.update(options)
       @mpw.set_password(item.id, options[:password]) if options.key?(:password)
@@ -436,7 +436,7 @@ class Cli
 
   # Remove an item
   # @args: options -> the option to search
-  def delete(options={})
+  def delete(options = {})
     items = @mpw.list(options)
 
     if items.empty?
@@ -461,7 +461,7 @@ class Cli
   # Copy a password, otp, login
   # @args: clipboard -> enable clipboard
   #        options -> the option to search
-  def copy(clipboard=true, options={})
+  def copy(clipboard = true, options = {})
     items = @mpw.list(options)
 
     if items.empty?
@@ -499,7 +499,7 @@ class Cli
                   )
     end
 
-    File.open(file, 'w') {|f| f << data.to_yaml}
+    File.open(file, 'w') { |f| f << data.to_yaml }
 
     puts I18n.t('form.export.valid', file: file).to_s.green
   rescue Exception => e
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 915e603..c722c39 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -36,7 +36,7 @@ class Config
 
   # Constructor
   # @args: config_file -> the specify config file
-  def initialize(config_file=nil)
+  def initialize(config_file = nil)
     @config_file = config_file
 
     if /darwin/ =~ RUBY_PLATFORM
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 6e99f6a..55e0061 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -36,7 +36,7 @@ class Item
   # Create a new item
   # @args: options -> a hash of parameter
   # raise an error if the hash hasn't the key name
-  def initialize(options={})
+  def initialize(options = {})
     if !options.key?(:host) || options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
@@ -56,7 +56,7 @@ class Item
 
   # Update the item
   # @args: options -> a hash of parameter
-  def update(options={})
+  def update(options = {})
     if options.key?(:host) && options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
@@ -96,7 +96,7 @@ class Item
   # Generate an random id
   private
   def generate_id
-    ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
+    ([*('A'..'Z'), *('a'..'z'), *('0'..'9')]).sample(16).join
   end
 end
 end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 811a7a1..678987e 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -27,7 +27,7 @@ module MPW
 class MPW
 
   # Constructor
-  def initialize(key, wallet_file, gpg_pass=nil, gpg_exe=nil)
+  def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
     @key         = key
     @gpg_pass    = gpg_pass
     @gpg_exe     = gpg_exe
@@ -53,29 +53,29 @@ class MPW
     Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
       tar.each do |f|
         case f.full_name
-          when 'wallet/config.gpg'
-            @config = YAML.safe_load(decrypt(f.read))
+        when 'wallet/config.gpg'
+          @config = YAML.safe_load(decrypt(f.read))
 
-          when 'wallet/meta.gpg'
-            data = decrypt(f.read)
+        when 'wallet/meta.gpg'
+          data = decrypt(f.read)
 
-          when /^wallet\/keys\/(?<key>.+)\.pub$/
-            key = Regexp.last_match('key')
+        when /^wallet\/keys\/(?<key>.+)\.pub$/
+          key = Regexp.last_match('key')
 
-            if GPGME::Key.find(:public, key).empty?
-              GPGME::Key.import(f.read, armor: true)
-            end
+          if GPGME::Key.find(:public, key).empty?
+            GPGME::Key.import(f.read, armor: true)
+          end
 
-            @keys[key] = f.read
+          @keys[key] = f.read
 
-          when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
-            @passwords[Regexp.last_match('id')] = f.read
+        when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+          @passwords[Regexp.last_match('id')] = f.read
 
-          when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
-            @otp_keys[Regexp.last_match('id')] = f.read
+        when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+          @otp_keys[Regexp.last_match('id')] = f.read
 
-          else
-            next
+        else
+          next
         end
       end
     end
@@ -220,7 +220,7 @@ class MPW
 
   # Set config
   # args: config -> a hash with config options
-  def set_config(options={})
+  def set_config(options = {})
     @config              = {} if @config.nil?
 
     @config['protocol']  = options[:protocol] if options.key?(:protocol)
@@ -243,7 +243,7 @@ class MPW
   # Search in some csv data
   # @args: options -> a hash with paramaters
   # @rtrn: a list with the resultat of the search
-  def list(options={})
+  def list(options = {})
     result = []
 
     search = options[:pattern].to_s.downcase
@@ -305,7 +305,7 @@ class MPW
   # Generate a random password
   # @args: options -> :length, :special, :alpha, :numeric
   # @rtrn: a random string
-  def self.password(options={})
+  def self.password(options = {})
     if !options.include?(:length) || options[:length].to_i <= 0
       length = 8
     elsif options[:length].to_i >= 32768
@@ -315,10 +315,10 @@ class MPW
     end
 
     chars = []
-    chars += [*('!'..'?')] - [*('0'..'9')]        if options[:special]
-    chars += [*('A'..'Z'),*('a'..'z')]            if options[:alpha]
-    chars += [*('0'..'9')]                        if options[:numeric]
-    chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
+    chars += [*('!'..'?')] - [*('0'..'9')]          if options[:special]
+    chars += [*('A'..'Z'), *('a'..'z')]             if options[:alpha]
+    chars += [*('0'..'9')]                          if options[:numeric]
+    chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
 
     result = ''
     while length > 62 do

From 043e378eab897e63cb0488ebcb1093ca5a23ff86 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 28 Mar 2017 23:34:24 +0200
Subject: [PATCH 433/531] replace File.exists? by File.exist?

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 678987e..13c864f 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -48,7 +48,7 @@ class MPW
 
     data       = nil
 
-    return unless File.exists?(@wallet_file)
+    return unless File.exist?(@wallet_file)
 
     Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
       tar.each do |f|
@@ -193,7 +193,7 @@ class MPW
   # Add a public key
   # args: key ->  new public key file or name
   def add_key(key)
-    if File.exists?(key)
+    if File.exist?(key)
       data       = File.open(key).read
       key_import = GPGME::Key.import(data, armor: true)
       key        = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email

From 05c30200c3b2a2efa6373aa4d0cf72131e699b6f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 08:12:57 +0200
Subject: [PATCH 434/531] add rubocop file

---
 .rubocop.yml | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 .rubocop.yml

diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..8685f88
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,122 @@
+
+AllCops:
+  Exclude:
+    - db/**/*
+    - config/**/*
+    - test/*
+    - Vagrantfile
+  TargetRubyVersion: 2.3
+
+Style/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:
+  Enabled: false
+Metrics/LineLength:
+  Max: 120
+Metrics/AbcSize:
+  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:
+  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:
+  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

From c1baf3e1d4f18e5dee5c93530905281d16624417 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:00:21 +0200
Subject: [PATCH 435/531] remove unused variable

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 300318a..8faab03 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -247,8 +247,6 @@ class Cli
   # @args: item -> the item
   #        clipboard -> enable clipboard
   def clipboard(item, clipboard = true)
-    pid = nil
-
     # Security: force quit after 90s
     Thread.new do
       sleep 90

From b32846ea5edb61842464f798b4b60b78cdab9583 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:12:11 +0200
Subject: [PATCH 436/531] replace while true by Kernel.loop

---
 lib/mpw/cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 8faab03..a334e61 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -253,7 +253,7 @@ class Cli
       exit
     end
 
-    while true
+    Kernel.loop do
       choice = ask(I18n.t('form.clipboard.choice')).to_s
 
       case choice

From c29d2e92d23ea103c66e0107a237d73b60315f6e Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:13:53 +0200
Subject: [PATCH 437/531] replace :? to

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

diff --git a/bin/mpw b/bin/mpw
index 3eb9fd9..8ee1305 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -16,7 +16,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-$: << File.expand_path('../../lib', __FILE__)
+$LOAD_PATH << File.expand_path('../../lib', __FILE__)
 
 require 'locale'
 require 'set'

From 74d08a7d5b9706561ab52b597eb999c3d3fa8ad1 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:24:50 +0200
Subject: [PATCH 438/531] add to_s

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

diff --git a/bin/mpw b/bin/mpw
index 8ee1305..1bdf835 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -45,7 +45,7 @@ I18n.locale         = lang.to_sym
 bin_dir = File.dirname(__FILE__)
 command = "#{bin_dir}/mpw-#{ARGV[0]}"
 
-if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
+if Dir.glob("#{bin_dir}/mpw-*").include?(command.to_s)
   begin
     Kernel.load(command)
   rescue OptionParser::ParseError => e

From da248460da2fe8d674ea4979a33facc249b07ae9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:31:04 +0200
Subject: [PATCH 439/531] use %r{} for regex

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 13c864f..b3a733c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -59,7 +59,7 @@ class MPW
         when 'wallet/meta.gpg'
           data = decrypt(f.read)
 
-        when /^wallet\/keys\/(?<key>.+)\.pub$/
+        when %r{^wallet/keys/(?<key>.+)\.pub$}
           key = Regexp.last_match('key')
 
           if GPGME::Key.find(:public, key).empty?
@@ -68,10 +68,10 @@ class MPW
 
           @keys[key] = f.read
 
-        when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+        when %r{^wallet/passwords/(?<id>[a-zA-Z0-9]+)\.gpg$}
           @passwords[Regexp.last_match('id')] = f.read
 
-        when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
+        when %r{^wallet/otp_keys/(?<id>[a-zA-Z0-9]+)\.gpg$}
           @otp_keys[Regexp.last_match('id')] = f.read
 
         else

From 8cd7b6e583fef8d355b559eeab5c19f2e631cfc0 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:31:48 +0200
Subject: [PATCH 440/531] use iter form

---
 lib/mpw/cli.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index a334e61..7f1c2da 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -202,7 +202,7 @@ class Cli
 
         if k == :otp
           print '| '
-          if item.otp;  print ' X  ' else 4.times { print ' ' } end
+          item.otp ? (print ' X  ') : 4.times { print ' ' }
 
           next
         end

From 18ba72e91bd9fc52381c2a27c42d12444617b121 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:36:46 +0200
Subject: [PATCH 441/531] fix private style

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

diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 55e0061..225a3d7 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -93,8 +93,9 @@ class Item
     false
   end
 
-  # Generate an random id
   private
+
+  # Generate an random id
   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 b3a733c..d09e9bd 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -330,9 +330,10 @@ class MPW
     result
   end
 
+  private
+
   # Decrypt a gpg file
   # @args: data -> string to decrypt
-  private
   def decrypt(data)
     return nil if data.to_s.empty?
 
@@ -345,7 +346,6 @@ class MPW
 
   # Encrypt a file
   # args: data -> string to encrypt
-  private
   def encrypt(data)
     recipients = []
     crypto     = GPGME::Crypto.new(armor: true, always_trust: true)

From 5db52db1a39c7c6c4ccfc2bd7d9e707c3cd53118 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:38:16 +0200
Subject: [PATCH 442/531] remove do in while line

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index d09e9bd..ffdccff 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -321,7 +321,7 @@ class MPW
     chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
 
     result = ''
-    while length > 62 do
+    while length > 62
       result << chars.sample(62).join
       length -= 62
     end

From e04d00d3f477a08c1e69b76bfb27df883a0ec4e2 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 22:45:39 +0200
Subject: [PATCH 443/531] use new form for module

---
 lib/mpw/cli.rb    | 4 +---
 lib/mpw/config.rb | 4 +---
 lib/mpw/item.rb   | 4 +---
 lib/mpw/mpw.rb    | 4 +---
 4 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 7f1c2da..d2dcd0b 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -26,8 +26,7 @@ require 'tmpdir'
 require 'mpw/item'
 require 'mpw/mpw'
 
-module MPW
-class Cli
+class MPW::Cli
 
   # Constructor
   # @args: config -> the config
@@ -533,4 +532,3 @@ class Cli
     puts "#{I18n.t('display.error')} #18: #{e}".red
   end
 end
-end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index c722c39..4c4e259 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -21,8 +21,7 @@ require 'yaml'
 require 'i18n'
 require 'fileutils'
 
-module MPW
-class Config
+class MPW::Config
 
   attr_accessor :error_msg
 
@@ -156,4 +155,3 @@ class Config
     false
   end
 end
-end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 225a3d7..a5528a6 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -18,8 +18,7 @@
 
 require 'i18n'
 
-module MPW
-class Item
+class MPW::Item
 
   attr_accessor :id
   attr_accessor :group
@@ -100,4 +99,3 @@ class Item
     ([*('A'..'Z'), *('a'..'z'), *('0'..'9')]).sample(16).join
   end
 end
-end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index ffdccff..0eac01c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -23,8 +23,7 @@ require 'yaml'
 require 'rotp'
 require 'mpw/item'
 
-module MPW
-class MPW
+class MPW::MPW
 
   # Constructor
   def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
@@ -361,4 +360,3 @@ class MPW
     raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
   end
 end
-end

From 782f3d8b5f88cc7acd16311157406a236c031352 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 23:01:12 +0200
Subject: [PATCH 444/531] remove useless line

---
 lib/mpw/cli.rb    | 1 -
 lib/mpw/config.rb | 1 -
 lib/mpw/item.rb   | 1 -
 lib/mpw/mpw.rb    | 1 -
 4 files changed, 4 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index d2dcd0b..3488b5a 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -27,7 +27,6 @@ require 'mpw/item'
 require 'mpw/mpw'
 
 class MPW::Cli
-
   # Constructor
   # @args: config -> the config
   def initialize(config)
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 4c4e259..4818104 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -22,7 +22,6 @@ require 'i18n'
 require 'fileutils'
 
 class MPW::Config
-
   attr_accessor :error_msg
 
   attr_accessor :gpg_key
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index a5528a6..8c0db4c 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -19,7 +19,6 @@
 require 'i18n'
 
 class MPW::Item
-
   attr_accessor :id
   attr_accessor :group
   attr_accessor :host
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 0eac01c..0aa9aea 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -24,7 +24,6 @@ require 'rotp'
 require 'mpw/item'
 
 class MPW::MPW
-
   # Constructor
   def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
     @key         = key

From b1fd013d921f12f71b93bc1db0a532bd6562f44b Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 23:15:45 +0200
Subject: [PATCH 445/531] fix indentation for multiline method and hash

---
 lib/mpw/cli.rb    | 32 +++++++++++++++---------------
 lib/mpw/config.rb |  6 ++----
 lib/mpw/mpw.rb    | 50 +++++++++++++++++++++++++----------------------
 3 files changed, 45 insertions(+), 43 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 3488b5a..12f18af 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -148,8 +148,7 @@ class MPW::Cli
                      protocol: { length: 9,  color: 'white' },
                      port:     { length: 5,  color: 'white' },
                      otp:      { length: 4,  color: 'white' },
-                     comment:  { length: 14, color: 'magenta' },
-                   }
+                     comment:  { length: 14, color: 'magenta' } }
 
     items.each do |item|
       data.each do |k, v|
@@ -481,18 +480,20 @@ class MPW::Cli
     data  = {}
 
     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,
-                             }
-                  )
+      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,
+        }
+      )
     end
 
     File.open(file, 'w') { |f| f << data.to_yaml }
@@ -514,8 +515,7 @@ class MPW::Cli
                       protocol: row['protocol'],
                       user:     row['user'],
                       port:     row['port'],
-                      comment:  row['comment'],
-                     )
+                      comment:  row['comment'])
 
       next if item.empty?
 
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 4818104..f82fadb 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -60,8 +60,7 @@ class MPW::Config
     password       = { numeric: true,
                        alpha:   true,
                        special: false,
-                       length:  16,
-                     }
+                       length:  16 }
 
     %w(numeric special alpha length).each do |k|
       if options.key?("pwd_#{k}".to_sym)
@@ -81,8 +80,7 @@ class MPW::Config
                    'wallet_dir'     => wallet_dir,
                    'default_wallet' => default_wallet,
                    'gpg_exe'        => gpg_exe,
-                   'password'       => password,
-                 }
+                   'password'       => password }
 
     FileUtils.mkdir_p(@config_dir, mode: 0700)
     FileUtils.mkdir_p(wallet_dir,  mode: 0700)
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 0aa9aea..1e385cb 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -80,18 +80,20 @@ class MPW::MPW
 
     unless data.to_s.empty?
       YAML.safe_load(data).each_value do |d|
-        @data.push(Item.new(id:        d['id'],
-                            group:     d['group'],
-                            host:      d['host'],
-                            protocol:  d['protocol'],
-                            user:      d['user'],
-                            port:      d['port'],
-                            otp:       @otp_keys.key?(d['id']),
-                            comment:   d['comment'],
-                            last_edit: d['last_edit'],
-                            created:   d['created'],
-                           )
-                  )
+        @data.push(
+          Item.new(
+            id:        d['id'],
+            group:     d['group'],
+            host:      d['host'],
+            protocol:  d['protocol'],
+            user:      d['user'],
+            port:      d['port'],
+            otp:       @otp_keys.key?(d['id']),
+            comment:   d['comment'],
+            last_edit: d['last_edit'],
+            created:   d['created'],
+          )
+        )
       end
     end
 
@@ -108,17 +110,19 @@ class MPW::MPW
     @data.each do |item|
       next if item.empty?
 
-      data.merge!(item.id => { 'id'        => item.id,
-                               'group'     => item.group,
-                               'host'      => item.host,
-                               'protocol'  => item.protocol,
-                               'user'      => item.user,
-                               'port'      => item.port,
-                               'comment'   => item.comment,
-                               'last_edit' => item.last_edit,
-                               'created'   => item.created,
-                             }
-                 )
+      data.merge!(
+        item.id => {
+          'id'        => item.id,
+          'group'     => item.group,
+          'host'      => item.host,
+          'protocol'  => item.protocol,
+          'user'      => item.user,
+          'port'      => item.port,
+          'comment'   => item.comment,
+          'last_edit' => item.last_edit,
+          'created'   => item.created,
+        }
+      )
     end
 
     @config['last_update'] = Time.now.to_i

From ac2732d116e56c68fa60873ac70b570bda122156 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 29 Mar 2017 23:24:54 +0200
Subject: [PATCH 446/531] revert new module form

---
 lib/mpw/cli.rb    | 4 +++-
 lib/mpw/config.rb | 4 +++-
 lib/mpw/item.rb   | 4 +++-
 lib/mpw/mpw.rb    | 4 +++-
 4 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 12f18af..b44d770 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -26,7 +26,8 @@ require 'tmpdir'
 require 'mpw/item'
 require 'mpw/mpw'
 
-class MPW::Cli
+module MPW
+class Cli
   # Constructor
   # @args: config -> the config
   def initialize(config)
@@ -531,3 +532,4 @@ class MPW::Cli
     puts "#{I18n.t('display.error')} #18: #{e}".red
   end
 end
+end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index f82fadb..8e2cb6e 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -21,7 +21,8 @@ require 'yaml'
 require 'i18n'
 require 'fileutils'
 
-class MPW::Config
+module MPW
+class Config
   attr_accessor :error_msg
 
   attr_accessor :gpg_key
@@ -152,3 +153,4 @@ class MPW::Config
     false
   end
 end
+end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 8c0db4c..f6d90ef 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -18,7 +18,8 @@
 
 require 'i18n'
 
-class MPW::Item
+module MPW
+class Item
   attr_accessor :id
   attr_accessor :group
   attr_accessor :host
@@ -98,3 +99,4 @@ class MPW::Item
     ([*('A'..'Z'), *('a'..'z'), *('0'..'9')]).sample(16).join
   end
 end
+end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 1e385cb..0c65435 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -23,7 +23,8 @@ require 'yaml'
 require 'rotp'
 require 'mpw/item'
 
-class MPW::MPW
+module MPW
+class MPW
   # Constructor
   def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
     @key         = key
@@ -363,3 +364,4 @@ class MPW::MPW
     raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
   end
 end
+end

From 002ec2175b427ba148dbf803d3fa34f39ca402bd Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Thu, 30 Mar 2017 19:35:50 +0200
Subject: [PATCH 447/531] fix rescue

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index b44d770..a1533b5 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -40,7 +40,7 @@ class Cli
     @config.setup(options)
 
     puts I18n.t('form.set_config.valid').to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #15: #{e}".red
     exit 2
   end
@@ -57,7 +57,7 @@ class Cli
     load_config
 
     puts I18n.t('form.setup_config.valid').to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #8: #{e}".red
     exit 2
   end
@@ -81,7 +81,7 @@ class Cli
     @config.setup_gpg_key(@password, gpg_key)
 
     puts I18n.t('form.setup_gpg_key.valid').to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #8: #{e}".red
     exit 2
   end
@@ -94,8 +94,7 @@ class Cli
   # Load config
   def load_config
     @config.load_config
-
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #10: #{e}".red
     exit 2
   end
@@ -108,7 +107,7 @@ class Cli
     end
 
     @mpw.read_data
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #11: #{e}".red
     exit 2
   end
@@ -340,7 +339,7 @@ class Cli
     @mpw.write_data
 
     puts I18n.t('form.add_key.valid').to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #13: #{e}".red
   end
 
@@ -351,7 +350,7 @@ class Cli
     @mpw.write_data
 
     puts I18n.t('form.delete_key.valid').to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #15: #{e}".red
   end
 
@@ -400,7 +399,7 @@ class Cli
     @mpw.write_data
 
     puts I18n.t('form.add_item.valid').to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #13: #{e}".red
   end
 
@@ -426,7 +425,7 @@ class Cli
 
       puts I18n.t('form.update_item.valid').to_s.green
     end
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #14: #{e}".red
   end
 
@@ -450,7 +449,7 @@ class Cli
 
       puts I18n.t('form.delete_item.valid').to_s.green
     end
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #16: #{e}".red
   end
 
@@ -468,7 +467,7 @@ class Cli
       item = get_item(items)
       clipboard(item, clipboard)
     end
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #14: #{e}".red
   end
 
@@ -500,7 +499,7 @@ class Cli
     File.open(file, 'w') { |f| f << data.to_yaml }
 
     puts I18n.t('form.export.valid', file: file).to_s.green
-  rescue Exception => e
+  rescue => e
     puts "#{I18n.t('display.error')} #17: #{e}".red
   end
 
@@ -528,7 +527,7 @@ class Cli
     @mpw.write_data
 
     puts I18n.t('form.import.valid').to_s.green
-  rescue Exception => 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 8e2cb6e..af746a2 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -89,7 +89,7 @@ class Config
     File.open(@config_file, 'w') do |file|
       file << config.to_yaml
     end
-  rescue Exception => e
+  rescue => e
     raise "#{I18n.t('error.config.write')}\n#{e}"
   end
 
@@ -121,7 +121,7 @@ class Config
 
     ctx = GPGME::Ctx.new
     ctx.genkey(param, nil, nil)
-  rescue Exception => e
+  rescue => e
     raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
   end
 
@@ -138,7 +138,7 @@ class Config
     raise if @gpg_key.empty? || @wallet_dir.empty?
 
     I18n.locale = @lang.to_sym
-  rescue Exception => 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 0c65435..069a123 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -99,7 +99,7 @@ class MPW
     end
 
     add_key(@key) if @keys[@key].nil?
-  rescue Exception => e
+  rescue => e
     raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
   end
 
@@ -159,7 +159,7 @@ class MPW
     end
 
     File.rename(tmp_file, @wallet_file)
-  rescue Exception => e
+  rescue => e
     File.unlink(tmp_file) if File.exist?(tmp_file)
 
     raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
@@ -343,7 +343,7 @@ class MPW
     crypto = GPGME::Crypto.new(armor: true)
 
     crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
-  rescue Exception => e
+  rescue => e
     raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
   end
 
@@ -360,7 +360,7 @@ class MPW
     end
 
     crypto.encrypt(data, recipients: recipients).read
-  rescue Exception => e
+  rescue => e
     raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
   end
 end

From 3c787371b36d0760a0a19645a657807047a084a7 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Thu, 30 Mar 2017 19:40:04 +0200
Subject: [PATCH 448/531] fix syntax for options in method

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index a1533b5..3f4d43d 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -218,7 +218,7 @@ class Cli
 
   # Display the query's result
   # @args: options -> the option to search
-  def list(options = {})
+  def list(**options)
     result = @mpw.list(options)
 
     if result.empty?
@@ -406,7 +406,7 @@ class Cli
   # Update an item
   # @args: password -> generate a random password
   #        options -> the option to search
-  def update(password = false, options = {})
+  def update(password = false, **options)
     items = @mpw.list(options)
 
     if items.empty?
@@ -431,7 +431,7 @@ class Cli
 
   # Remove an item
   # @args: options -> the option to search
-  def delete(options = {})
+  def delete(**options)
     items = @mpw.list(options)
 
     if items.empty?
@@ -456,7 +456,7 @@ class Cli
   # Copy a password, otp, login
   # @args: clipboard -> enable clipboard
   #        options -> the option to search
-  def copy(clipboard = true, options = {})
+  def copy(clipboard = true, **options)
     items = @mpw.list(options)
 
     if items.empty?
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index f6d90ef..1534cc4 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -35,7 +35,7 @@ class Item
   # Create a new item
   # @args: options -> a hash of parameter
   # raise an error if the hash hasn't the key name
-  def initialize(options = {})
+  def initialize(**options)
     if !options.key?(:host) || options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
@@ -55,7 +55,7 @@ class Item
 
   # Update the item
   # @args: options -> a hash of parameter
-  def update(options = {})
+  def update(**options)
     if options.key?(:host) && options[:host].to_s.empty?
       raise I18n.t('error.update.host_empty')
     end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 069a123..46355ec 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -223,7 +223,7 @@ class MPW
 
   # Set config
   # args: config -> a hash with config options
-  def set_config(options = {})
+  def set_config(**options)
     @config              = {} if @config.nil?
 
     @config['protocol']  = options[:protocol] if options.key?(:protocol)
@@ -246,7 +246,7 @@ class MPW
   # Search in some csv data
   # @args: options -> a hash with paramaters
   # @rtrn: a list with the resultat of the search
-  def list(options = {})
+  def list(**options)
     result = []
 
     search = options[:pattern].to_s.downcase

From 755df6c3425663e7bcfd3e07b6a8976e349c703d Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Thu, 30 Mar 2017 19:41:00 +0200
Subject: [PATCH 449/531] fix syntax for variable with multiple conditional

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 3f4d43d..0536108 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -317,19 +317,20 @@ class Cli
   # Display the wallet
   # @args: wallet -> the wallet name
   def get_wallet(wallet = nil)
-    if wallet.to_s.empty?
-      wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
+    @wallet_file =
+      if wallet.to_s.empty?
+        wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
 
-      if wallets.length == 1
-        @wallet_file = wallets[0]
-      elsif !@config.default_wallet.to_s.empty?
-        @wallet_file = "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
+        if wallets.length == 1
+          wallets[0]
+        elsif !@config.default_wallet.to_s.empty?
+          "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
+        else
+          "#{@config.wallet_dir}/default.mpw"
+        end
       else
-        @wallet_file = "#{@config.wallet_dir}/default.mpw"
+        "#{@config.wallet_dir}/#{wallet}.mpw"
       end
-    else
-      @wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
-    end
   end
 
   # Add a new public key
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index af746a2..a6c102e 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -38,13 +38,14 @@ class Config
   def initialize(config_file = nil)
     @config_file = config_file
 
-    if /darwin/ =~ RUBY_PLATFORM
-      @config_dir = "#{Dir.home}/Library/Preferences/mpw"
-    elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
-      @config_dir = "#{Dir.home}/AppData/Local/mpw"
-    else
-      @config_dir = "#{Dir.home}/.config/mpw"
-    end
+    @config_dir =
+      if /darwin/ =~ RUBY_PLATFORM
+        "#{Dir.home}/Library/Preferences/mpw"
+      elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+        "#{Dir.home}/AppData/Local/mpw"
+      else
+        "#{Dir.home}/.config/mpw"
+      end
 
     @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? || @config_file.empty?
   end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 46355ec..557d572 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -308,14 +308,15 @@ class MPW
   # Generate a random password
   # @args: options -> :length, :special, :alpha, :numeric
   # @rtrn: a random string
-  def self.password(options = {})
-    if !options.include?(:length) || options[:length].to_i <= 0
-      length = 8
-    elsif options[:length].to_i >= 32768
-      length = 32768
-    else
-      length = options[:length].to_i
-    end
+  def self.password(**options)
+    length =
+      if !options.include?(:length) || options[:length].to_i <= 0
+        8
+      elsif options[:length].to_i >= 32_768
+        32_768
+      else
+        options[:length].to_i
+      end
 
     chars = []
     chars += [*('!'..'?')] - [*('0'..'9')]          if options[:special]

From 0f7510b6e6b61dfb898328eb6c161b2583dbff83 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Thu, 30 Mar 2017 19:44:32 +0200
Subject: [PATCH 450/531] multiple fix on syntax

---
 lib/mpw/cli.rb    | 6 ++----
 lib/mpw/config.rb | 7 ++-----
 lib/mpw/item.rb   | 2 +-
 lib/mpw/mpw.rb    | 8 ++------
 4 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 0536108..cc452cf 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -70,9 +70,7 @@ class Cli
     password = ask(I18n.t('form.setup_gpg_key.password')) { |q| q.echo = false }
     confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) { |q| q.echo = false }
 
-    if password != confirm
-      raise I18n.t('form.setup_gpg_key.error_password')
-    end
+    raise I18n.t('form.setup_gpg_key.error_password') if password != confirm
 
     @password = password.to_s
 
@@ -378,7 +376,7 @@ class Cli
       opts = YAML.load_file(tmp_file)
     end
 
-    opts.delete_if { |k, v| v.to_s.empty? }
+    opts.delete_if { |_, v| v.to_s.empty? }
 
     opts.each do |k, v|
       options[k.to_sym] = v
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index a6c102e..d691559 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -101,11 +101,8 @@ class Config
   #        expire -> the time of expire to GPG key
   # @rtrn: true if the GPG key is create, else false
   def setup_gpg_key(password, name, length = 4096, expire = 0)
-    if name.to_s.empty?
-      raise I18n.t('error.config.genkey_gpg.name')
-    elsif password.to_s.empty?
-      raise I18n.t('error.config.genkey_gpg.password')
-    end
+    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?
 
     param = ''
     param << '<GnupgKeyParms format="internal">' + "\n"
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 1534cc4..cd24168 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -96,7 +96,7 @@ class Item
 
   # Generate an random id
   def generate_id
-    ([*('A'..'Z'), *('a'..'z'), *('0'..'9')]).sample(16).join
+    [*('A'..'Z'), *('a'..'z'), *('0'..'9')].sample(16).join
   end
 end
 end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 557d572..daf04ff 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -32,9 +32,7 @@ class MPW
     @gpg_exe     = gpg_exe
     @wallet_file = wallet_file
 
-    unless @gpg_exe.to_s.empty?
-      GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
-    end
+    GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg") unless @gpg_exe.to_s.empty?
   end
 
   # Read mpw file
@@ -204,9 +202,7 @@ class MPW
       data = GPGME::Key.export(key, armor: true).read
     end
 
-    if data.to_s.empty?
-      raise I18n.t('error.export_key')
-    end
+    raise I18n.t('error.export_key') if data.to_s.empty?
 
     @keys[key] = data
     @passwords.each_key { |id| set_password(id, get_password(id)) }

From 01745cac2d5ced7a5237b16087b9525974876634 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Thu, 30 Mar 2017 20:04:20 +0200
Subject: [PATCH 451/531] indent class

---
 lib/mpw/cli.rb    | 886 +++++++++++++++++++++++-----------------------
 lib/mpw/config.rb | 228 ++++++------
 lib/mpw/item.rb   | 138 ++++----
 lib/mpw/mpw.rb    | 622 ++++++++++++++++----------------
 4 files changed, 937 insertions(+), 937 deletions(-)

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index cc452cf..d4d4027 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -27,507 +27,507 @@ require 'mpw/item'
 require 'mpw/mpw'
 
 module MPW
-class Cli
-  # Constructor
-  # @args: config -> the config
-  def initialize(config)
-    @config = config
-  end
-
-  # Change a parameter int the config after init
-  # @args: options -> param to change
-  def set_config(options)
-    @config.setup(options)
-
-    puts I18n.t('form.set_config.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #15: #{e}".red
-    exit 2
-  end
-
-  # Create a new config file
-  # @args: options -> set param
-  def setup(options)
-    options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
-
-    I18n.locale = options[:lang].to_sym
-
-    @config.setup(options)
-
-    load_config
-
-    puts I18n.t('form.setup_config.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #8: #{e}".red
-    exit 2
-  end
-
-  # Setup a new GPG key
-  # @args: gpg_key -> the key name
-  def setup_gpg_key(gpg_key)
-    return if @config.check_gpg_key?
-
-    password = ask(I18n.t('form.setup_gpg_key.password')) { |q| q.echo = false }
-    confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) { |q| q.echo = false }
-
-    raise I18n.t('form.setup_gpg_key.error_password') if password != confirm
-
-    @password = password.to_s
-
-    puts I18n.t('form.setup_gpg_key.wait')
-
-    @config.setup_gpg_key(@password, gpg_key)
-
-    puts I18n.t('form.setup_gpg_key.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #8: #{e}".red
-    exit 2
-  end
-
-  # List gpg keys in wallet
-  def list_keys
-    table_list('keys', @mpw.list_keys)
-  end
-
-  # Load config
-  def load_config
-    @config.load_config
-  rescue => e
-    puts "#{I18n.t('display.error')} #10: #{e}".red
-    exit 2
-  end
-
-  # 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)
+  class Cli
+    # Constructor
+    # @args: config -> the config
+    def initialize(config)
+      @config = config
     end
 
-    @mpw.read_data
-  rescue => e
-    puts "#{I18n.t('display.error')} #11: #{e}".red
-    exit 2
-  end
+    # Change a parameter int the config after init
+    # @args: options -> param to change
+    def set_config(options)
+      @config.setup(options)
 
-  # Format list on a table
-  def table_list(title, list)
-    i      = 1
-    length = 0
-
-    list.each do |item|
-      length = item.length if length < item.length
-    end
-    length += 7
-
-    puts "\n#{I18n.t("display.#{title}")}".red
-    print ' '
-    length.times { print '=' }
-    print "\n"
-
-    list.each do |item|
-      print "  #{i}".cyan
-      (3 - i.to_s.length).times { print ' ' }
-      puts "| #{item}"
-      i += 1
+      puts I18n.t('form.set_config.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #15: #{e}".red
+      exit 2
     end
 
-    print "\n"
-  end
+    # Create a new config file
+    # @args: options -> set param
+    def setup(options)
+      options[:lang] = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
 
-  # Format items on a table
-  def table_items(items = [])
-    group        = '.'
-    i            = 1
-    length_total = 10
-    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' } }
+      I18n.locale = options[:lang].to_sym
 
-    items.each do |item|
-      data.each do |k, v|
-        next if k == :id || k == :otp
+      @config.setup(options)
 
-        v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
-      end
+      load_config
+
+      puts I18n.t('form.setup_config.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #8: #{e}".red
+      exit 2
     end
-    data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
 
-    data.each_value { |v| length_total += v[:length] }
-    items.sort!     { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
+    # Setup a new GPG key
+    # @args: gpg_key -> the key name
+    def setup_gpg_key(gpg_key)
+      return if @config.check_gpg_key?
 
-    items.each do |item|
-      if group != item.group
-        group = item.group
+      password = ask(I18n.t('form.setup_gpg_key.password')) { |q| q.echo = false }
+      confirm  = ask(I18n.t('form.setup_gpg_key.confirm_password')) { |q| q.echo = false }
 
-        if group.to_s.empty?
-          puts "\n#{I18n.t('display.no_group')}".red
-        else
-          puts "\n#{group}".red
-        end
+      raise I18n.t('form.setup_gpg_key.error_password') if password != confirm
 
-        print ' '
-        length_total.times { print '=' }
-        print "\n "
-        data.each do |k, v|
-          case k
-          when :id
-            print ' ID'
-          when :otp
-            print '| OTP'
-          else
-            print "| #{k.to_s.capitalize}"
-          end
+      @password = password.to_s
 
-          (v[:length] - k.to_s.length).times { print ' ' }
-        end
-        print "\n "
-        length_total.times { print '=' }
-        print "\n"
+      puts I18n.t('form.setup_gpg_key.wait')
+
+      @config.setup_gpg_key(@password, gpg_key)
+
+      puts I18n.t('form.setup_gpg_key.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #8: #{e}".red
+      exit 2
+    end
+
+    # List gpg keys in wallet
+    def list_keys
+      table_list('keys', @mpw.list_keys)
+    end
+
+    # Load config
+    def load_config
+      @config.load_config
+    rescue => e
+      puts "#{I18n.t('display.error')} #10: #{e}".red
+      exit 2
+    end
+
+    # 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)
       end
 
-      print "  #{i}".send(data[:id][:color])
-      (data[:id][:length] - i.to_s.length).times { print ' ' }
-      data.each do |k, v|
-        next if k == :id
+      @mpw.read_data
+    rescue => e
+      puts "#{I18n.t('display.error')} #11: #{e}".red
+      exit 2
+    end
 
-        if k == :otp
-          print '| '
-          item.otp ? (print ' X  ') : 4.times { print ' ' }
+    # Format list on a table
+    def table_list(title, list)
+      i      = 1
+      length = 0
 
-          next
-        end
-
-        print '| '
-        print item.send(k.to_s).to_s.send(v[:color])
-        (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+      list.each do |item|
+        length = item.length if length < item.length
       end
+      length += 7
+
+      puts "\n#{I18n.t("display.#{title}")}".red
+      print ' '
+      length.times { print '=' }
       print "\n"
 
-      i += 1
+      list.each do |item|
+        print "  #{i}".cyan
+        (3 - i.to_s.length).times { print ' ' }
+        puts "| #{item}"
+        i += 1
+      end
+
+      print "\n"
     end
 
-    print "\n"
-  end
+    # Format items on a table
+    def table_items(items = [])
+      group        = '.'
+      i            = 1
+      length_total = 10
+      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' } }
 
-  # Display the query's result
-  # @args: options -> the option to search
-  def list(**options)
-    result = @mpw.list(options)
+      items.each do |item|
+        data.each do |k, v|
+          next if k == :id || k == :otp
 
-    if result.empty?
-      puts I18n.t('display.nothing')
-    else
-      table_items(result)
-    end
-  end
+          v[:length] = item.send(k.to_s).to_s.length + 3 if item.send(k.to_s).to_s.length >= v[:length]
+        end
+      end
+      data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
 
-  # Get an item when multiple choice
-  # @args: items -> array of items
-  # @rtrn: item
-  def get_item(items)
-    return items[0] if items.length == 1
+      data.each_value { |v| length_total += v[:length] }
+      items.sort!     { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
 
-    items.sort! { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
-    choice = ask(I18n.t('form.select')).to_i
+      items.each do |item|
+        if group != item.group
+          group = item.group
 
-    choice >= 1 && choice <= items.length ? items[choice - 1] : nil
-  end
+          if group.to_s.empty?
+            puts "\n#{I18n.t('display.no_group')}".red
+          else
+            puts "\n#{group}".red
+          end
 
-  # Copy in clipboard the login and password
-  # @args: item -> the item
-  #        clipboard -> enable clipboard
-  def clipboard(item, clipboard = true)
-    # Security: force quit after 90s
-    Thread.new do
-      sleep 90
-      exit
-    end
+          print ' '
+          length_total.times { print '=' }
+          print "\n "
+          data.each do |k, v|
+            case k
+            when :id
+              print ' ID'
+            when :otp
+              print '| OTP'
+            else
+              print "| #{k.to_s.capitalize}"
+            end
 
-    Kernel.loop do
-      choice = ask(I18n.t('form.clipboard.choice')).to_s
-
-      case choice
-      when 'q', 'quit'
-        break
-
-      when 'l', 'login'
-        if clipboard
-          Clipboard.copy(item.user)
-          puts I18n.t('form.clipboard.login').green
-        else
-          puts item.user
+            (v[:length] - k.to_s.length).times { print ' ' }
+          end
+          print "\n "
+          length_total.times { print '=' }
+          print "\n"
         end
 
-      when 'p', 'password'
-        if clipboard
-          Clipboard.copy(@mpw.get_password(item.id))
-          puts I18n.t('form.clipboard.password').yellow
+        print "  #{i}".send(data[:id][:color])
+        (data[:id][:length] - i.to_s.length).times { print ' ' }
+        data.each do |k, v|
+          next if k == :id
 
-          Thread.new do
-            sleep 30
+          if k == :otp
+            print '| '
+            item.otp ? (print ' X  ') : 4.times { print ' ' }
 
-            Clipboard.clear
+            next
+          end
+
+          print '| '
+          print item.send(k.to_s).to_s.send(v[:color])
+          (v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
+        end
+        print "\n"
+
+        i += 1
+      end
+
+      print "\n"
+    end
+
+    # Display the query's result
+    # @args: options -> the option to search
+    def list(**options)
+      result = @mpw.list(options)
+
+      if result.empty?
+        puts I18n.t('display.nothing')
+      else
+        table_items(result)
+      end
+    end
+
+    # Get an item when multiple choice
+    # @args: items -> array of items
+    # @rtrn: item
+    def get_item(items)
+      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 >= 1 && choice <= items.length ? items[choice - 1] : nil
+    end
+
+    # Copy in clipboard the login and password
+    # @args: item -> the item
+    #        clipboard -> enable clipboard
+    def clipboard(item, clipboard = true)
+      # Security: force quit after 90s
+      Thread.new do
+        sleep 90
+        exit
+      end
+
+      Kernel.loop do
+        choice = ask(I18n.t('form.clipboard.choice')).to_s
+
+        case choice
+        when 'q', 'quit'
+          break
+
+        when 'l', 'login'
+          if clipboard
+            Clipboard.copy(item.user)
+            puts I18n.t('form.clipboard.login').green
+          else
+            puts item.user
+          end
+
+        when 'p', 'password'
+          if clipboard
+            Clipboard.copy(@mpw.get_password(item.id))
+            puts I18n.t('form.clipboard.password').yellow
+
+            Thread.new do
+              sleep 30
+
+              Clipboard.clear
+            end
+          else
+            puts @mpw.get_password(item.id)
+          end
+
+        when 'o', 'otp'
+          if clipboard
+            Clipboard.copy(@mpw.get_otp_code(item.id))
+          else
+            puts @mpw.get_otp_code(item.id)
+          end
+          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.login')
+          puts I18n.t('form.clipboard.help.password')
+          puts I18n.t('form.clipboard.help.otp_code')
+          puts I18n.t('form.clipboard.help.quit')
+          next
+        end
+      end
+
+      Clipboard.clear
+    rescue SystemExit, Interrupt
+      Clipboard.clear
+    end
+
+    # List all wallets
+    def list_wallet
+      wallets = []
+      Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f|
+        wallet = File.basename(f, '.mpw')
+        wallet += ' *'.green if wallet == @config.default_wallet
+        wallets << wallet
+      end
+
+      table_list('wallets', wallets)
+    end
+
+    # Display the wallet
+    # @args: wallet -> the wallet name
+    def get_wallet(wallet = nil)
+      @wallet_file =
+        if wallet.to_s.empty?
+          wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
+
+          if wallets.length == 1
+            wallets[0]
+          elsif !@config.default_wallet.to_s.empty?
+            "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
+          else
+            "#{@config.wallet_dir}/default.mpw"
           end
         else
-          puts @mpw.get_password(item.id)
+          "#{@config.wallet_dir}/#{wallet}.mpw"
+        end
+    end
+
+    # Add a new public key
+    # args: key -> the key name or key file to add
+    def add_key(key)
+      @mpw.add_key(key)
+      @mpw.write_data
+
+      puts I18n.t('form.add_key.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #13: #{e}".red
+    end
+
+    # Add new public key
+    # args: key -> the key name to delete
+    def delete_key(key)
+      @mpw.delete_key(key)
+      @mpw.write_data
+
+      puts I18n.t('form.delete_key.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #15: #{e}".red
+    end
+
+    # Text editor interface
+    # @args: template -> template name
+    #        item -> the item to edit
+    #        password -> disable field password
+    def text_editor(template_name, item = nil, password = false)
+      editor        = ENV['EDITOR'] || 'nano'
+      options       = {}
+      opts          = {}
+      template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
+      template      = ERB.new(IO.read(template_file))
+
+      Dir.mktmpdir do |dir|
+        tmp_file = "#{dir}/#{template_name}.yml"
+
+        File.open(tmp_file, 'w') do |f|
+          f << template.result(binding)
         end
 
-      when 'o', 'otp'
-        if clipboard
-          Clipboard.copy(@mpw.get_otp_code(item.id))
-        else
-          puts @mpw.get_otp_code(item.id)
-        end
-        puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
+        system("#{editor} #{tmp_file}")
 
-      else
-        puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
-        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
-      end
-    end
-
-    Clipboard.clear
-  rescue SystemExit, Interrupt
-    Clipboard.clear
-  end
-
-  # List all wallets
-  def list_wallet
-    wallets = []
-    Dir.glob("#{@config.wallet_dir}/*.mpw").each do |f|
-      wallet = File.basename(f, '.mpw')
-      wallet += ' *'.green if wallet == @config.default_wallet
-      wallets << wallet
-    end
-
-    table_list('wallets', wallets)
-  end
-
-  # Display the wallet
-  # @args: wallet -> the wallet name
-  def get_wallet(wallet = nil)
-    @wallet_file =
-      if wallet.to_s.empty?
-        wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
-
-        if wallets.length == 1
-          wallets[0]
-        elsif !@config.default_wallet.to_s.empty?
-          "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
-        else
-          "#{@config.wallet_dir}/default.mpw"
-        end
-      else
-        "#{@config.wallet_dir}/#{wallet}.mpw"
-      end
-  end
-
-  # Add a new public key
-  # args: key -> the key name or key file to add
-  def add_key(key)
-    @mpw.add_key(key)
-    @mpw.write_data
-
-    puts I18n.t('form.add_key.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #13: #{e}".red
-  end
-
-  # Add new public key
-  # args: key -> the key name to delete
-  def delete_key(key)
-    @mpw.delete_key(key)
-    @mpw.write_data
-
-    puts I18n.t('form.delete_key.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #15: #{e}".red
-  end
-
-  # Text editor interface
-  # @args: template -> template name
-  #        item -> the item to edit
-  #        password -> disable field password
-  def text_editor(template_name, item = nil, password = false)
-    editor        = ENV['EDITOR'] || 'nano'
-    options       = {}
-    opts          = {}
-    template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
-    template      = ERB.new(IO.read(template_file))
-
-    Dir.mktmpdir do |dir|
-      tmp_file = "#{dir}/#{template_name}.yml"
-
-      File.open(tmp_file, 'w') do |f|
-        f << template.result(binding)
+        opts = YAML.load_file(tmp_file)
       end
 
-      system("#{editor} #{tmp_file}")
+      opts.delete_if { |_, v| v.to_s.empty? }
 
-      opts = YAML.load_file(tmp_file)
+      opts.each do |k, v|
+        options[k.to_sym] = v
+      end
+
+      options
     end
 
-    opts.delete_if { |_, v| v.to_s.empty? }
-
-    opts.each do |k, v|
-      options[k.to_sym] = v
-    end
-
-    options
-  end
-
-  # Form to add a new item
-  # @args: password -> generate a random password
-  def add(password = false)
-    options            = text_editor('add_form', nil, password)
-    item               = Item.new(options)
-    options[:password] = MPW.password(@config.password) if password
-
-    @mpw.add(item)
-    @mpw.set_password(item.id, options[:password]) if options.key?(:password)
-    @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
-    @mpw.write_data
-
-    puts I18n.t('form.add_item.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #13: #{e}".red
-  end
-
-  # Update an item
-  # @args: password -> generate a random password
-  #        options -> the option to search
-  def update(password = false, **options)
-    items = @mpw.list(options)
-
-    if items.empty?
-      puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
-    else
-      table_items(items) if items.length > 1
-
-      item               = get_item(items)
-      options            = text_editor('update_form', item, password)
+    # Form to add a new item
+    # @args: password -> generate a random password
+    def add(password = false)
+      options            = text_editor('add_form', nil, password)
+      item               = Item.new(options)
       options[:password] = MPW.password(@config.password) if password
 
-      item.update(options)
+      @mpw.add(item)
       @mpw.set_password(item.id, options[:password]) if options.key?(:password)
       @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
       @mpw.write_data
 
-      puts I18n.t('form.update_item.valid').to_s.green
+      puts I18n.t('form.add_item.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #13: #{e}".red
     end
-  rescue => e
-    puts "#{I18n.t('display.error')} #14: #{e}".red
-  end
 
-  # Remove an item
-  # @args: options -> the option to search
-  def delete(**options)
-    items = @mpw.list(options)
+    # Update an item
+    # @args: password -> generate a random password
+    #        options -> the option to search
+    def update(password = false, **options)
+      items = @mpw.list(options)
 
-    if items.empty?
-      puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
-    else
-      table_items(items)
+      if items.empty?
+        puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+      else
+        table_items(items) if items.length > 1
 
-      item    = get_item(items)
-      confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
+        item               = get_item(items)
+        options            = text_editor('update_form', item, password)
+        options[:password] = MPW.password(@config.password) if password
 
-      return unless confirm =~ /^(y|yes|YES|Yes|Y)$/
+        item.update(options)
+        @mpw.set_password(item.id, options[:password]) if options.key?(:password)
+        @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
+        @mpw.write_data
+
+        puts I18n.t('form.update_item.valid').to_s.green
+      end
+    rescue => e
+      puts "#{I18n.t('display.error')} #14: #{e}".red
+    end
+
+    # Remove an item
+    # @args: options -> the option to search
+    def delete(**options)
+      items = @mpw.list(options)
+
+      if items.empty?
+        puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
+      else
+        table_items(items)
+
+        item    = get_item(items)
+        confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
+
+        return unless confirm =~ /^(y|yes|YES|Yes|Y)$/
+
+        item.delete
+        @mpw.write_data
+
+        puts I18n.t('form.delete_item.valid').to_s.green
+      end
+    rescue => e
+      puts "#{I18n.t('display.error')} #16: #{e}".red
+    end
+
+    # Copy a password, otp, login
+    # @args: clipboard -> enable clipboard
+    #        options -> the option to search
+    def copy(clipboard = true, **options)
+      items = @mpw.list(options)
+
+      if items.empty?
+        puts I18n.t('display.nothing')
+      else
+        table_items(items)
+
+        item = get_item(items)
+        clipboard(item, clipboard)
+      end
+    rescue => e
+      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
+    def export(file, options)
+      file  = 'export-mpw.yml' if file.to_s.empty?
+      items = @mpw.list(options)
+      data  = {}
+
+      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,
+          }
+        )
+      end
+
+      File.open(file, 'w') { |f| f << data.to_yaml }
+
+      puts I18n.t('form.export.valid', file: file).to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #17: #{e}".red
+    end
+
+    # Import items from a YAML file
+    # @args: file -> the 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)
+
+      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'])
+
+        next if item.empty?
+
+        @mpw.add(item)
+        @mpw.set_password(item.id, row['password']) unless row['password'].to_s.empty?
+        @mpw.set_otp_key(item.id, row['otp_key'])   unless row['otp_key'].to_s.empty?
+      end
 
-      item.delete
       @mpw.write_data
 
-      puts I18n.t('form.delete_item.valid').to_s.green
+      puts I18n.t('form.import.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #18: #{e}".red
     end
-  rescue => e
-    puts "#{I18n.t('display.error')} #16: #{e}".red
-  end
-
-  # Copy a password, otp, login
-  # @args: clipboard -> enable clipboard
-  #        options -> the option to search
-  def copy(clipboard = true, **options)
-    items = @mpw.list(options)
-
-    if items.empty?
-      puts I18n.t('display.nothing')
-    else
-      table_items(items)
-
-      item = get_item(items)
-      clipboard(item, clipboard)
-    end
-  rescue => e
-    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
-  def export(file, options)
-    file  = 'export-mpw.yml' if file.to_s.empty?
-    items = @mpw.list(options)
-    data  = {}
-
-    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,
-        }
-      )
-    end
-
-    File.open(file, 'w') { |f| f << data.to_yaml }
-
-    puts I18n.t('form.export.valid', file: file).to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #17: #{e}".red
-  end
-
-  # Import items from a YAML file
-  # @args: file -> the 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)
-
-    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'])
-
-      next if item.empty?
-
-      @mpw.add(item)
-      @mpw.set_password(item.id, row['password']) unless row['password'].to_s.empty?
-      @mpw.set_otp_key(item.id, row['otp_key'])   unless row['otp_key'].to_s.empty?
-    end
-
-    @mpw.write_data
-
-    puts I18n.t('form.import.valid').to_s.green
-  rescue => e
-    puts "#{I18n.t('display.error')} #18: #{e}".red
   end
 end
-end
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index d691559..3b88b0a 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -22,133 +22,133 @@ require 'i18n'
 require 'fileutils'
 
 module MPW
-class Config
-  attr_accessor :error_msg
+  class Config
+    attr_accessor :error_msg
 
-  attr_accessor :gpg_key
-  attr_accessor :lang
-  attr_accessor :config_dir
-  attr_accessor :default_wallet
-  attr_accessor :wallet_dir
-  attr_accessor :gpg_exe
-  attr_accessor :password
+    attr_accessor :gpg_key
+    attr_accessor :lang
+    attr_accessor :config_dir
+    attr_accessor :default_wallet
+    attr_accessor :wallet_dir
+    attr_accessor :gpg_exe
+    attr_accessor :password
 
-  # Constructor
-  # @args: config_file -> the specify config file
-  def initialize(config_file = nil)
-    @config_file = config_file
+    # Constructor
+    # @args: config_file -> the specify config file
+    def initialize(config_file = nil)
+      @config_file = config_file
 
-    @config_dir =
-      if /darwin/ =~ RUBY_PLATFORM
-        "#{Dir.home}/Library/Preferences/mpw"
-      elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
-        "#{Dir.home}/AppData/Local/mpw"
-      else
-        "#{Dir.home}/.config/mpw"
+      @config_dir =
+        if /darwin/ =~ RUBY_PLATFORM
+          "#{Dir.home}/Library/Preferences/mpw"
+        elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+          "#{Dir.home}/AppData/Local/mpw"
+        else
+          "#{Dir.home}/.config/mpw"
+        end
+
+      @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? || @config_file.empty?
+    end
+
+    # Create a new config file
+    # @args: options -> hash with values
+    # @rtrn: true if le config file is create
+    def setup(options)
+      gpg_key        = options[:gpg_key]        || @gpg_key
+      lang           = options[:lang]           || @lang
+      wallet_dir     = options[:wallet_dir]     || @wallet_dir
+      default_wallet = options[:default_wallet] || @default_wallet
+      gpg_exe        = options[:gpg_exe]        || @gpg_exe
+      password       = { numeric: true,
+                         alpha:   true,
+                         special: false,
+                         length:  16 }
+
+      %w(numeric special alpha length).each do |k|
+        if options.key?("pwd_#{k}".to_sym)
+          password[k.to_sym] = options["pwd_#{k}".to_sym]
+        elsif !@password.nil? && @password.key?(k.to_sym)
+          password[k.to_sym] = @password[k.to_sym]
+        end
       end
 
-    @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? || @config_file.empty?
-  end
-
-  # Create a new config file
-  # @args: options -> hash with values
-  # @rtrn: true if le config file is create
-  def setup(options)
-    gpg_key        = options[:gpg_key]        || @gpg_key
-    lang           = options[:lang]           || @lang
-    wallet_dir     = options[:wallet_dir]     || @wallet_dir
-    default_wallet = options[:default_wallet] || @default_wallet
-    gpg_exe        = options[:gpg_exe]        || @gpg_exe
-    password       = { numeric: true,
-                       alpha:   true,
-                       special: false,
-                       length:  16 }
-
-    %w(numeric special alpha length).each do |k|
-      if options.key?("pwd_#{k}".to_sym)
-        password[k.to_sym] = options["pwd_#{k}".to_sym]
-      elsif !@password.nil? && @password.key?(k.to_sym)
-        password[k.to_sym] = @password[k.to_sym]
+      unless gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
+        raise I18n.t('error.config.key_bad_format')
       end
+
+      wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
+      config     = { 'gpg_key'        => gpg_key,
+                     'lang'           => lang,
+                     'wallet_dir'     => wallet_dir,
+                     'default_wallet' => default_wallet,
+                     'gpg_exe'        => gpg_exe,
+                     'password'       => password }
+
+      FileUtils.mkdir_p(@config_dir, mode: 0700)
+      FileUtils.mkdir_p(wallet_dir,  mode: 0700)
+
+      File.open(@config_file, 'w') do |file|
+        file << config.to_yaml
+      end
+    rescue => e
+      raise "#{I18n.t('error.config.write')}\n#{e}"
     end
 
-    unless gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
-      raise I18n.t('error.config.key_bad_format')
+    # 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
+    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?
+
+      param = ''
+      param << '<GnupgKeyParms format="internal">' + "\n"
+      param << "Key-Type: RSA\n"
+      param << "Key-Length: #{length}\n"
+      param << "Subkey-Type: ELG-E\n"
+      param << "Subkey-Length: #{length}\n"
+      param << "Name-Real: #{name}\n"
+      param << "Name-Comment: #{name}\n"
+      param << "Name-Email: #{@gpg_key}\n"
+      param << "Expire-Date: #{expire}\n"
+      param << "Passphrase: #{password}\n"
+      param << "</GnupgKeyParms>\n"
+
+      ctx = GPGME::Ctx.new
+      ctx.genkey(param, nil, nil)
+    rescue => e
+      raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
     end
 
-    wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
-    config     = { 'gpg_key'        => gpg_key,
-                   'lang'           => lang,
-                   'wallet_dir'     => wallet_dir,
-                   'default_wallet' => default_wallet,
-                   'gpg_exe'        => gpg_exe,
-                   'password'       => password }
+    # Load the config file
+    def load_config
+      config          = YAML.load_file(@config_file)
+      @gpg_key        = config['gpg_key']
+      @lang           = config['lang']
+      @wallet_dir     = config['wallet_dir']
+      @default_wallet = config['default_wallet']
+      @gpg_exe        = config['gpg_exe']
+      @password       = config['password'] || {}
 
-    FileUtils.mkdir_p(@config_dir, mode: 0700)
-    FileUtils.mkdir_p(wallet_dir,  mode: 0700)
+      raise if @gpg_key.empty? || @wallet_dir.empty?
 
-    File.open(@config_file, 'w') do |file|
-      file << config.to_yaml
-    end
-  rescue => e
-    raise "#{I18n.t('error.config.write')}\n#{e}"
-  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
-  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?
-
-    param = ''
-    param << '<GnupgKeyParms format="internal">' + "\n"
-    param << "Key-Type: RSA\n"
-    param << "Key-Length: #{length}\n"
-    param << "Subkey-Type: ELG-E\n"
-    param << "Subkey-Length: #{length}\n"
-    param << "Name-Real: #{name}\n"
-    param << "Name-Comment: #{name}\n"
-    param << "Name-Email: #{@gpg_key}\n"
-    param << "Expire-Date: #{expire}\n"
-    param << "Passphrase: #{password}\n"
-    param << "</GnupgKeyParms>\n"
-
-    ctx = GPGME::Ctx.new
-    ctx.genkey(param, nil, nil)
-  rescue => e
-    raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
-  end
-
-  # Load the config file
-  def load_config
-    config          = YAML.load_file(@config_file)
-    @gpg_key        = config['gpg_key']
-    @lang           = config['lang']
-    @wallet_dir     = config['wallet_dir']
-    @default_wallet = config['default_wallet']
-    @gpg_exe        = config['gpg_exe']
-    @password       = config['password'] || {}
-
-    raise if @gpg_key.empty? || @wallet_dir.empty?
-
-    I18n.locale = @lang.to_sym
-  rescue => e
-    raise "#{I18n.t('error.config.load')}\n#{e}"
-  end
-
-  # Check if private key exist
-  # @rtrn: true if the key exist, else false
-  def check_gpg_key?
-    ctx = GPGME::Ctx.new
-    ctx.each_key(@gpg_key, true) do
-      return true
+      I18n.locale = @lang.to_sym
+    rescue => e
+      raise "#{I18n.t('error.config.load')}\n#{e}"
     end
 
-    false
+    # Check if private key exist
+    # @rtrn: true if the key exist, else false
+    def check_gpg_key?
+      ctx = GPGME::Ctx.new
+      ctx.each_key(@gpg_key, true) do
+        return true
+      end
+
+      false
+    end
   end
 end
-end
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index cd24168..8b4b867 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -19,84 +19,84 @@
 require 'i18n'
 
 module MPW
-class Item
-  attr_accessor :id
-  attr_accessor :group
-  attr_accessor :host
-  attr_accessor :protocol
-  attr_accessor :user
-  attr_accessor :port
-  attr_accessor :otp
-  attr_accessor :comment
-  attr_accessor :last_edit
-  attr_accessor :created
+  class Item
+    attr_accessor :id
+    attr_accessor :group
+    attr_accessor :host
+    attr_accessor :protocol
+    attr_accessor :user
+    attr_accessor :port
+    attr_accessor :otp
+    attr_accessor :comment
+    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
-  def initialize(**options)
-    if !options.key?(:host) || options[:host].to_s.empty?
-      raise I18n.t('error.update.host_empty')
+    # Constructor
+    # Create a new item
+    # @args: options -> a hash of parameter
+    # raise an error if the hash hasn't the key name
+    def initialize(**options)
+      if !options.key?(:host) || options[:host].to_s.empty?
+        raise I18n.t('error.update.host_empty')
+      end
+
+      if !options.key?(:id) || options[:id].to_s.empty? || !options.key?(:created) || options[:created].to_s.empty?
+        @id = generate_id
+        @created = Time.now.to_i
+      else
+        @id = options[:id]
+        @created   = options[:created]
+        @last_edit = options[:last_edit]
+        options[:no_update_last_edit] = true
+      end
+
+      update(options)
     end
 
-    if !options.key?(:id) || options[:id].to_s.empty? || !options.key?(:created) || options[:created].to_s.empty?
-      @id = generate_id
-      @created = Time.now.to_i
-    else
-      @id = options[:id]
-      @created   = options[:created]
-      @last_edit = options[:last_edit]
-      options[:no_update_last_edit] = true
+    # Update the item
+    # @args: options -> a hash of parameter
+    def update(**options)
+      if options.key?(:host) && options[:host].to_s.empty?
+        raise I18n.t('error.update.host_empty')
+      end
+
+      @group     = options[:group]      if options.key?(:group)
+      @host      = options[:host]       if options.key?(:host)
+      @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)
     end
 
-    update(options)
-  end
-
-  # Update the item
-  # @args: options -> a hash of parameter
-  def update(**options)
-    if options.key?(:host) && options[:host].to_s.empty?
-      raise I18n.t('error.update.host_empty')
+    # Delete all data
+    def delete
+      @id        = nil
+      @group     = nil
+      @host      = nil
+      @protocol  = nil
+      @user      = nil
+      @port      = nil
+      @otp       = nil
+      @comment   = nil
+      @created   = nil
+      @last_edit = nil
     end
 
-    @group     = options[:group]      if options.key?(:group)
-    @host      = options[:host]       if options.key?(:host)
-    @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)
-  end
+    def empty?
+      @id.to_s.empty?
+    end
 
-  # Delete all data
-  def delete
-    @id        = nil
-    @group     = nil
-    @host      = nil
-    @protocol  = nil
-    @user      = nil
-    @port      = nil
-    @otp       = nil
-    @comment   = nil
-    @created   = nil
-    @last_edit = nil
-  end
+    def nil?
+      false
+    end
 
-  def empty?
-    @id.to_s.empty?
-  end
+    private
 
-  def nil?
-    false
-  end
-
-  private
-
-  # Generate an random id
-  def generate_id
-    [*('A'..'Z'), *('a'..'z'), *('0'..'9')].sample(16).join
+    # Generate an random id
+    def generate_id
+      [*('A'..'Z'), *('a'..'z'), *('0'..'9')].sample(16).join
+    end
   end
 end
-end
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index daf04ff..8389be9 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -24,341 +24,341 @@ require 'rotp'
 require 'mpw/item'
 
 module MPW
-class MPW
-  # Constructor
-  def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
-    @key         = key
-    @gpg_pass    = gpg_pass
-    @gpg_exe     = gpg_exe
-    @wallet_file = wallet_file
+  class MPW
+    # Constructor
+    def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
+      @key         = key
+      @gpg_pass    = gpg_pass
+      @gpg_exe     = gpg_exe
+      @wallet_file = wallet_file
 
-    GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg") unless @gpg_exe.to_s.empty?
-  end
-
-  # Read mpw file
-  def read_data
-    @config    = {}
-    @data      = []
-    @keys      = {}
-    @passwords = {}
-    @otp_keys  = {}
-
-    data       = nil
-
-    return unless File.exist?(@wallet_file)
-
-    Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
-      tar.each do |f|
-        case f.full_name
-        when 'wallet/config.gpg'
-          @config = YAML.safe_load(decrypt(f.read))
-
-        when 'wallet/meta.gpg'
-          data = decrypt(f.read)
-
-        when %r{^wallet/keys/(?<key>.+)\.pub$}
-          key = Regexp.last_match('key')
-
-          if GPGME::Key.find(:public, key).empty?
-            GPGME::Key.import(f.read, armor: true)
-          end
-
-          @keys[key] = f.read
-
-        when %r{^wallet/passwords/(?<id>[a-zA-Z0-9]+)\.gpg$}
-          @passwords[Regexp.last_match('id')] = f.read
-
-        when %r{^wallet/otp_keys/(?<id>[a-zA-Z0-9]+)\.gpg$}
-          @otp_keys[Regexp.last_match('id')] = f.read
-
-        else
-          next
-        end
-      end
+      GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg") unless @gpg_exe.to_s.empty?
     end
 
-    unless data.to_s.empty?
-      YAML.safe_load(data).each_value do |d|
-        @data.push(
-          Item.new(
-            id:        d['id'],
-            group:     d['group'],
-            host:      d['host'],
-            protocol:  d['protocol'],
-            user:      d['user'],
-            port:      d['port'],
-            otp:       @otp_keys.key?(d['id']),
-            comment:   d['comment'],
-            last_edit: d['last_edit'],
-            created:   d['created'],
+    # Read mpw file
+    def read_data
+      @config    = {}
+      @data      = []
+      @keys      = {}
+      @passwords = {}
+      @otp_keys  = {}
+
+      data       = nil
+
+      return unless File.exist?(@wallet_file)
+
+      Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
+        tar.each do |f|
+          case f.full_name
+          when 'wallet/config.gpg'
+            @config = YAML.safe_load(decrypt(f.read))
+
+          when 'wallet/meta.gpg'
+            data = decrypt(f.read)
+
+          when %r{^wallet/keys/(?<key>.+)\.pub$}
+            key = Regexp.last_match('key')
+
+            if GPGME::Key.find(:public, key).empty?
+              GPGME::Key.import(f.read, armor: true)
+            end
+
+            @keys[key] = f.read
+
+          when %r{^wallet/passwords/(?<id>[a-zA-Z0-9]+)\.gpg$}
+            @passwords[Regexp.last_match('id')] = f.read
+
+          when %r{^wallet/otp_keys/(?<id>[a-zA-Z0-9]+)\.gpg$}
+            @otp_keys[Regexp.last_match('id')] = f.read
+
+          else
+            next
+          end
+        end
+      end
+
+      unless data.to_s.empty?
+        YAML.safe_load(data).each_value do |d|
+          @data.push(
+            Item.new(
+              id:        d['id'],
+              group:     d['group'],
+              host:      d['host'],
+              protocol:  d['protocol'],
+              user:      d['user'],
+              port:      d['port'],
+              otp:       @otp_keys.key?(d['id']),
+              comment:   d['comment'],
+              last_edit: d['last_edit'],
+              created:   d['created'],
+            )
           )
+        end
+      end
+
+      add_key(@key) if @keys[@key].nil?
+    rescue => e
+      raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
+    end
+
+    # Encrypt a file
+    def write_data
+      data     = {}
+      tmp_file = "#{@wallet_file}.tmp"
+
+      @data.each do |item|
+        next if item.empty?
+
+        data.merge!(
+          item.id => {
+            'id'        => item.id,
+            'group'     => item.group,
+            'host'      => item.host,
+            'protocol'  => item.protocol,
+            'user'      => item.user,
+            'port'      => item.port,
+            'comment'   => item.comment,
+            'last_edit' => item.last_edit,
+            'created'   => item.created,
+          }
         )
       end
-    end
 
-    add_key(@key) if @keys[@key].nil?
-  rescue => e
-    raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
-  end
+      @config['last_update'] = Time.now.to_i
 
-  # Encrypt a file
-  def write_data
-    data     = {}
-    tmp_file = "#{@wallet_file}.tmp"
+      Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
+        data_encrypt = encrypt(data.to_yaml)
+        tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
+          io.write(data_encrypt)
+        end
 
-    @data.each do |item|
-      next if item.empty?
+        config = encrypt(@config.to_yaml)
+        tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
+          io.write(config)
+        end
 
-      data.merge!(
-        item.id => {
-          'id'        => item.id,
-          'group'     => item.group,
-          'host'      => item.host,
-          'protocol'  => item.protocol,
-          'user'      => item.user,
-          'port'      => item.port,
-          'comment'   => item.comment,
-          'last_edit' => item.last_edit,
-          'created'   => item.created,
-        }
-      )
-    end
+        @passwords.each do |id, password|
+          tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
+            io.write(password)
+          end
+        end
 
-    @config['last_update'] = Time.now.to_i
+        @otp_keys.each do |id, key|
+          tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
+            io.write(key)
+          end
+        end
 
-    Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
-      data_encrypt = encrypt(data.to_yaml)
-      tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
-        io.write(data_encrypt)
-      end
-
-      config = encrypt(@config.to_yaml)
-      tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
-        io.write(config)
-      end
-
-      @passwords.each do |id, password|
-        tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
-          io.write(password)
+        @keys.each do |id, key|
+          tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
+            io.write(key)
+          end
         end
       end
 
-      @otp_keys.each do |id, key|
-        tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
-          io.write(key)
-        end
-      end
+      File.rename(tmp_file, @wallet_file)
+    rescue => e
+      File.unlink(tmp_file) if File.exist?(tmp_file)
 
-      @keys.each do |id, key|
-        tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
-          io.write(key)
-        end
-      end
+      raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
     end
 
-    File.rename(tmp_file, @wallet_file)
-  rescue => e
-    File.unlink(tmp_file) if File.exist?(tmp_file)
+    # Get a password
+    # args: id -> the item id
+    def get_password(id)
+      password = decrypt(@passwords[id])
 
-    raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
-  end
-
-  # Get a password
-  # args: id -> the item id
-  def get_password(id)
-    password = decrypt(@passwords[id])
-
-    if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
-      Regexp.last_match('password')
-    else
-      password
-    end
-  end
-
-  # Set a password
-  # args: id -> the item id
-  #       password -> the new password
-  def set_password(id, password)
-    salt     = MPW.password(length: Random.rand(4..9))
-    password = "$#{salt}::#{password}"
-
-    @passwords[id] = encrypt(password)
-  end
-
-  # Return the list of all gpg keys
-  # rtrn: an array with the gpg keys name
-  def list_keys
-    @keys.keys
-  end
-
-  # Add a public key
-  # args: key ->  new public key file or name
-  def add_key(key)
-    if File.exist?(key)
-      data       = File.open(key).read
-      key_import = GPGME::Key.import(data, armor: true)
-      key        = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
-    else
-      data = GPGME::Key.export(key, armor: true).read
-    end
-
-    raise I18n.t('error.export_key') if data.to_s.empty?
-
-    @keys[key] = data
-    @passwords.each_key { |id| set_password(id, get_password(id)) }
-    @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
-  end
-
-  # Delete a public key
-  # args: key ->  public key to delete
-  def delete_key(key)
-    @keys.delete(key)
-    @passwords.each_key { |id| set_password(id, get_password(id)) }
-    @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
-  end
-
-  # Set config
-  # args: config -> a hash with config options
-  def set_config(**options)
-    @config              = {} if @config.nil?
-
-    @config['protocol']  = options[:protocol] if options.key?(:protocol)
-    @config['host']      = options[:host]     if options.key?(:host)
-    @config['port']      = options[:port]     if options.key?(:port)
-    @config['user']      = options[:user]     if options.key?(:user)
-    @config['password']  = options[:password] if options.key?(:password)
-    @config['path']      = options[:path]     if options.key?(:path)
-  end
-
-  # Add a new item
-  # @args: item -> Object MPW::Item
-  def add(item)
-    raise I18n.t('error.bad_class') unless item.instance_of?(Item)
-    raise I18n.t('error.empty')     if item.empty?
-
-    @data.push(item)
-  end
-
-  # Search in some csv data
-  # @args: options -> a hash with paramaters
-  # @rtrn: a list with the resultat of the search
-  def list(**options)
-    result = []
-
-    search = options[:pattern].to_s.downcase
-    group  = options[:group].to_s.downcase
-
-    @data.each do |item|
-      next if item.empty?
-      next unless group.empty? || group.eql?(item.group.to_s.downcase)
-
-      host    = item.host.to_s.downcase
-      comment = item.comment.to_s.downcase
-
-      next unless host =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/
-
-      result.push(item)
-    end
-
-    result
-  end
-
-  # Search in some csv data
-  # @args: id -> the id item
-  # @rtrn: a row with the result of the search
-  def search_by_id(id)
-    @data.each do |item|
-      return item if item.id == id
-    end
-
-    nil
-  end
-
-  # Set an opt key
-  # args: id -> the item id
-  #       key -> 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
-  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
-  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
-  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
-  def self.password(**options)
-    length =
-      if !options.include?(:length) || options[:length].to_i <= 0
-        8
-      elsif options[:length].to_i >= 32_768
-        32_768
+      if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
+        Regexp.last_match('password')
       else
-        options[:length].to_i
+        password
+      end
+    end
+
+    # Set a password
+    # args: id -> the item id
+    #       password -> the new password
+    def set_password(id, password)
+      salt     = MPW.password(length: Random.rand(4..9))
+      password = "$#{salt}::#{password}"
+
+      @passwords[id] = encrypt(password)
+    end
+
+    # Return the list of all gpg keys
+    # rtrn: an array with the gpg keys name
+    def list_keys
+      @keys.keys
+    end
+
+    # Add a public key
+    # args: key ->  new public key file or name
+    def add_key(key)
+      if File.exist?(key)
+        data       = File.open(key).read
+        key_import = GPGME::Key.import(data, armor: true)
+        key        = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
+      else
+        data = GPGME::Key.export(key, armor: true).read
       end
 
-    chars = []
-    chars += [*('!'..'?')] - [*('0'..'9')]          if options[:special]
-    chars += [*('A'..'Z'), *('a'..'z')]             if options[:alpha]
-    chars += [*('0'..'9')]                          if options[:numeric]
-    chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
+      raise I18n.t('error.export_key') if data.to_s.empty?
 
-    result = ''
-    while length > 62
-      result << chars.sample(62).join
-      length -= 62
-    end
-    result << chars.sample(length).join
-
-    result
-  end
-
-  private
-
-  # Decrypt a gpg file
-  # @args: data -> string to decrypt
-  def decrypt(data)
-    return nil if data.to_s.empty?
-
-    crypto = GPGME::Crypto.new(armor: true)
-
-    crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
-  rescue => e
-    raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
-  end
-
-  # Encrypt a file
-  # args: data -> string to encrypt
-  def encrypt(data)
-    recipients = []
-    crypto     = GPGME::Crypto.new(armor: true, always_trust: true)
-
-    recipients.push(@key)
-    @keys.each_key do |key|
-      next if key == @key
-      recipients.push(key)
+      @keys[key] = data
+      @passwords.each_key { |id| set_password(id, get_password(id)) }
+      @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
     end
 
-    crypto.encrypt(data, recipients: recipients).read
-  rescue => e
-    raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+    # Delete a public key
+    # args: key ->  public key to delete
+    def delete_key(key)
+      @keys.delete(key)
+      @passwords.each_key { |id| set_password(id, get_password(id)) }
+      @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
+    end
+
+    # Set config
+    # args: config -> a hash with config options
+    def set_config(**options)
+      @config              = {} if @config.nil?
+
+      @config['protocol']  = options[:protocol] if options.key?(:protocol)
+      @config['host']      = options[:host]     if options.key?(:host)
+      @config['port']      = options[:port]     if options.key?(:port)
+      @config['user']      = options[:user]     if options.key?(:user)
+      @config['password']  = options[:password] if options.key?(:password)
+      @config['path']      = options[:path]     if options.key?(:path)
+    end
+
+    # Add a new item
+    # @args: item -> Object MPW::Item
+    def add(item)
+      raise I18n.t('error.bad_class') unless item.instance_of?(Item)
+      raise I18n.t('error.empty')     if item.empty?
+
+      @data.push(item)
+    end
+
+    # Search in some csv data
+    # @args: options -> a hash with paramaters
+    # @rtrn: a list with the resultat of the search
+    def list(**options)
+      result = []
+
+      search = options[:pattern].to_s.downcase
+      group  = options[:group].to_s.downcase
+
+      @data.each do |item|
+        next if item.empty?
+        next unless group.empty? || group.eql?(item.group.to_s.downcase)
+
+        host    = item.host.to_s.downcase
+        comment = item.comment.to_s.downcase
+
+        next unless host =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/
+
+        result.push(item)
+      end
+
+      result
+    end
+
+    # Search in some csv data
+    # @args: id -> the id item
+    # @rtrn: a row with the result of the search
+    def search_by_id(id)
+      @data.each do |item|
+        return item if item.id == id
+      end
+
+      nil
+    end
+
+    # Set an opt key
+    # args: id -> the item id
+    #       key -> 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
+    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
+    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
+    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
+    def self.password(**options)
+      length =
+        if !options.include?(:length) || options[:length].to_i <= 0
+          8
+        elsif options[:length].to_i >= 32_768
+          32_768
+        else
+          options[:length].to_i
+        end
+
+      chars = []
+      chars += [*('!'..'?')] - [*('0'..'9')]          if options[:special]
+      chars += [*('A'..'Z'), *('a'..'z')]             if options[:alpha]
+      chars += [*('0'..'9')]                          if options[:numeric]
+      chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
+
+      result = ''
+      while length > 62
+        result << chars.sample(62).join
+        length -= 62
+      end
+      result << chars.sample(length).join
+
+      result
+    end
+
+    private
+
+    # Decrypt a gpg file
+    # @args: data -> string to decrypt
+    def decrypt(data)
+      return nil if data.to_s.empty?
+
+      crypto = GPGME::Crypto.new(armor: true)
+
+      crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
+    rescue => e
+      raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
+    end
+
+    # Encrypt a file
+    # args: data -> string to encrypt
+    def encrypt(data)
+      recipients = []
+      crypto     = GPGME::Crypto.new(armor: true, always_trust: true)
+
+      recipients.push(@key)
+      @keys.each_key do |key|
+        next if key == @key
+        recipients.push(key)
+      end
+
+      crypto.encrypt(data, recipients: recipients).read
+    rescue => e
+      raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
+    end
   end
 end
-end

From 7eb585726faf69eb02367f236ed524e903adf39c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Thu, 30 Mar 2017 20:07:47 +0200
Subject: [PATCH 452/531] fix syntax on Genfile and mpw.gemspec

---
 Gemfile     | 18 +++++++++---------
 mpw.gemspec | 21 +++++++++++----------
 2 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/Gemfile b/Gemfile
index a588d17..7e2d83c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,10 +1,10 @@
 source 'https://rubygems.org'
-gem "i18n",      "~> 0.7", ">= 0.7.0"
-gem "gpgme",     "~> 2.0", ">= 2.0.12"
-gem "highline",  "~> 1.7", ">= 1.7.8"
-gem "locale",    "~> 2.1", ">= 2.1.2"
-gem "colorize",  "~> 0.8", ">= 0.8.1"
-gem "net-ssh",   "~> 3.2", ">= 3.2.0"
-gem "net-sftp",  "~> 2.1", ">= 2.1.2"
-gem "clipboard", "~> 1.1", ">= 1.1.1"
-gem "rotp",      "~> 3.1", ">= 3.1.0"
+gem 'clipboard', '~> 1.1', '>= 1.1.1'
+gem 'colorize',  '~> 0.8', '>= 0.8.1'
+gem 'gpgme',     '~> 2.0', '>= 2.0.12'
+gem 'highline',  '~> 1.7', '>= 1.7.8'
+gem 'i18n',      '~> 0.7', '>= 0.7.0'
+gem 'locale',    '~> 2.1', '>= 2.1.2'
+gem 'net-sftp',  '~> 2.1', '>= 2.1.2'
+gem 'net-ssh',   '~> 3.2', '>= 3.2.0'
+gem 'rotp',      '~> 3.1', '>= 3.1.0'
diff --git a/mpw.gemspec b/mpw.gemspec
index 80d9344..7f687ab 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -1,4 +1,5 @@
 # coding: utf-8
+
 lib = File.expand_path('../lib', __FILE__)
 $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
 
@@ -12,18 +13,18 @@ Gem::Specification.new do |spec|
   spec.homepage      = 'https://github.com/nishiki/manage-password'
   spec.license       = 'GPL-2.0'
 
-  spec.files         = `git ls-files -z`.split("\x0")
+  spec.files         = %x(git ls-files -z).split("\x0")
   spec.executables   = ['mpw']
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ['lib']
 
-  spec.add_dependency "i18n",      "~> 0.7", ">= 0.7.0"
-  spec.add_dependency "gpgme",     "~> 2.0", ">= 2.0.12"
-  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 "net-ssh",   "~> 3.2", ">= 3.2.0"
-  spec.add_dependency "net-sftp",  "~> 2.1", ">= 2.1.2"
-  spec.add_dependency "clipboard", "~> 1.1", ">= 1.1.1"
-  spec.add_dependency "rotp",      "~> 3.1", ">= 3.1.0"
+  spec.add_dependency 'i18n',      '~> 0.7', '>= 0.7.0'
+  spec.add_dependency 'gpgme',     '~> 2.0', '>= 2.0.12'
+  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 'net-ssh',   '~> 3.2', '>= 3.2.0'
+  spec.add_dependency 'net-sftp',  '~> 2.1', '>= 2.1.2'
+  spec.add_dependency 'clipboard', '~> 1.1', '>= 1.1.1'
+  spec.add_dependency 'rotp',      '~> 3.1', '>= 3.1.0'
 end

From 2cb8ad4dbe4806bedb64b257cd1bc45d65d85c7a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 30 Mar 2017 20:18:49 +0200
Subject: [PATCH 453/531] add rubocop test in travis

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

diff --git a/.travis.yml b/.travis.yml
index 1a18023..faa5c04 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,8 +7,10 @@ rvm:
 install:
   - bundle install
   - gem install 'test-unit'
+  - gem install rubocop
   - echo 9999 > VERSION
   - gem build mpw.gemspec
   - gem install mpw-9999.gem
 script:
+  - rubocop
   - ruby ./test/tests.rb 

From 1e688d191cd9c45e748ecaa3c7fd51f848dca727 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 30 Mar 2017 22:41:12 +0200
Subject: [PATCH 454/531] remove unused dependancies

---
 Gemfile     | 2 --
 mpw.gemspec | 2 --
 2 files changed, 4 deletions(-)

diff --git a/Gemfile b/Gemfile
index 7e2d83c..71d84d4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,6 +5,4 @@ gem 'gpgme',     '~> 2.0', '>= 2.0.12'
 gem 'highline',  '~> 1.7', '>= 1.7.8'
 gem 'i18n',      '~> 0.7', '>= 0.7.0'
 gem 'locale',    '~> 2.1', '>= 2.1.2'
-gem 'net-sftp',  '~> 2.1', '>= 2.1.2'
-gem 'net-ssh',   '~> 3.2', '>= 3.2.0'
 gem 'rotp',      '~> 3.1', '>= 3.1.0'
diff --git a/mpw.gemspec b/mpw.gemspec
index 7f687ab..9f27023 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -23,8 +23,6 @@ Gem::Specification.new do |spec|
   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 'net-ssh',   '~> 3.2', '>= 3.2.0'
-  spec.add_dependency 'net-sftp',  '~> 2.1', '>= 2.1.2'
   spec.add_dependency 'clipboard', '~> 1.1', '>= 1.1.1'
   spec.add_dependency 'rotp',      '~> 3.1', '>= 3.1.0'
 end

From 92cb89ad33083a917710cafe057d4303c8bc91eb Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.fr>
Date: Sun, 2 Apr 2017 12:14:27 +0200
Subject: [PATCH 455/531] fix gpg password with pinentry

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 8389be9..2444f12 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -339,7 +339,7 @@ module MPW
 
       crypto = GPGME::Crypto.new(armor: true)
 
-      crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
+      crypto.decrypt(data, password: @gpg_pass, pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK).read.force_encoding('utf-8')
     rescue => e
       raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
     end

From 3543b0cf05cfc4fc177dc54a0407a21951747aac Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 2 Apr 2017 22:39:31 +0200
Subject: [PATCH 456/531] remove unused wallet config

---
 lib/mpw/mpw.rb | 24 ------------------------
 1 file changed, 24 deletions(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 8389be9..c52f8db 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -37,7 +37,6 @@ module MPW
 
     # Read mpw file
     def read_data
-      @config    = {}
       @data      = []
       @keys      = {}
       @passwords = {}
@@ -50,9 +49,6 @@ module MPW
       Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
         tar.each do |f|
           case f.full_name
-          when 'wallet/config.gpg'
-            @config = YAML.safe_load(decrypt(f.read))
-
           when 'wallet/meta.gpg'
             data = decrypt(f.read)
 
@@ -124,19 +120,12 @@ module MPW
         )
       end
 
-      @config['last_update'] = Time.now.to_i
-
       Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
         data_encrypt = encrypt(data.to_yaml)
         tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
           io.write(data_encrypt)
         end
 
-        config = encrypt(@config.to_yaml)
-        tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
-          io.write(config)
-        end
-
         @passwords.each do |id, password|
           tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
             io.write(password)
@@ -217,19 +206,6 @@ module MPW
       @otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
     end
 
-    # Set config
-    # args: config -> a hash with config options
-    def set_config(**options)
-      @config              = {} if @config.nil?
-
-      @config['protocol']  = options[:protocol] if options.key?(:protocol)
-      @config['host']      = options[:host]     if options.key?(:host)
-      @config['port']      = options[:port]     if options.key?(:port)
-      @config['user']      = options[:user]     if options.key?(:user)
-      @config['password']  = options[:password] if options.key?(:password)
-      @config['path']      = options[:path]     if options.key?(:path)
-    end
-
     # Add a new item
     # @args: item -> Object MPW::Item
     def add(item)

From d1adfd24c10ab9a4fd80cb3c08ebbfbc069bc585 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 2 Apr 2017 23:44:36 +0200
Subject: [PATCH 457/531] fix pinentry mode with gpg 1.4

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

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 2444f12..88cab5c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -337,9 +337,18 @@ module MPW
     def decrypt(data)
       return nil if data.to_s.empty?
 
-      crypto = GPGME::Crypto.new(armor: true)
+      password =
+        if /^1\.[0-9.]+$/ =~ GPGME::Engine.info.first.version
+          { password: @gpg_pass }
+        else
+          { password: @gpg_pass,
+            pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK }
+        end
 
-      crypto.decrypt(data, password: @gpg_pass, pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK).read.force_encoding('utf-8')
+      crypto = GPGME::Crypto.new(armor: true)
+      crypto
+        .decrypt(data, password)
+        .read.force_encoding('utf-8')
     rescue => e
       raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
     end

From 7ce4ba721bb039f30af6882dca9c55e748b58944 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 4 Apr 2017 23:45:03 +0200
Subject: [PATCH 458/531] add option to enable pinmode with gpg >= 2.1

---
 bin/mpw-config    | 6 +++++-
 lib/mpw/cli.rb    | 2 +-
 lib/mpw/config.rb | 6 +++++-
 lib/mpw/mpw.rb    | 5 +++--
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 4740ed1..1e3480d 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -60,6 +60,10 @@ OptionParser.new do |opts|
     values[:lang] = lang
   end
 
+  opts.on('-P', '--pinmode', I18n.t('option.pinmode')) do
+    values[:pinmode] = true
+  end
+
   opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
     values[:wallet_dir] = wallet_dir
   end
@@ -94,7 +98,7 @@ OptionParser.new do |opts|
 end.parse!
 
 config = MPW::Config.new(options[:config])
-cli    = MPW::Cli.new(config, nil)
+cli    = MPW::Cli.new(config)
 
 if options.key?(:init)
   cli.setup(values)
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index d4d4027..56e77a1 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -101,7 +101,7 @@ module MPW
     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)
+        @mpw      = MPW.new(@config.gpg_key, @wallet_file, @password, @config.gpg_exe, @config.pinmode)
       end
 
       @mpw.read_data
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 3b88b0a..829abc2 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -32,6 +32,7 @@ module MPW
     attr_accessor :wallet_dir
     attr_accessor :gpg_exe
     attr_accessor :password
+    attr_accessor :pinmode
 
     # Constructor
     # @args: config_file -> the specify config file
@@ -59,6 +60,7 @@ 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,
@@ -82,7 +84,8 @@ module MPW
                      'wallet_dir'     => wallet_dir,
                      'default_wallet' => default_wallet,
                      'gpg_exe'        => gpg_exe,
-                     'password'       => password }
+                     'password'       => password,
+                     'pinmode'        => pinmode }
 
       FileUtils.mkdir_p(@config_dir, mode: 0700)
       FileUtils.mkdir_p(wallet_dir,  mode: 0700)
@@ -132,6 +135,7 @@ module MPW
       @default_wallet = config['default_wallet']
       @gpg_exe        = config['gpg_exe']
       @password       = config['password'] || {}
+      @pinmode        = config['pinmode']
 
       raise if @gpg_key.empty? || @wallet_dir.empty?
 
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 88cab5c..7d15b52 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -26,11 +26,12 @@ require 'mpw/item'
 module MPW
   class MPW
     # Constructor
-    def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil)
+    def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil, pinmode = false)
       @key         = key
       @gpg_pass    = gpg_pass
       @gpg_exe     = gpg_exe
       @wallet_file = wallet_file
+      @pinmode     = pinmode
 
       GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg") unless @gpg_exe.to_s.empty?
     end
@@ -338,7 +339,7 @@ module MPW
       return nil if data.to_s.empty?
 
       password =
-        if /^1\.[0-9.]+$/ =~ GPGME::Engine.info.first.version
+        if /^1\.[0-9.]+$/ =~ GPGME::Engine.info.first.version || @pinmode
           { password: @gpg_pass }
         else
           { password: @gpg_pass,

From d8df357993212cc6d43148eaeb3f907f7479fa5c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 4 Apr 2017 23:50:38 +0200
Subject: [PATCH 459/531] not use pinmode if gpg version < 2.1

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 7d15b52..dd12ef6 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -339,7 +339,7 @@ module MPW
       return nil if data.to_s.empty?
 
       password =
-        if /^1\.[0-9.]+$/ =~ GPGME::Engine.info.first.version || @pinmode
+        if /^(1\.[0-9.]+|2\.0)(\.[0-9]+)?/ =~ GPGME::Engine.info.first.version || @pinmode
           { password: @gpg_pass }
         else
           { password: @gpg_pass,

From 45ead1e24e13fda0161dab564198980ae2b57abd Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Tue, 4 Apr 2017 23:52:00 +0200
Subject: [PATCH 460/531] fix syntax for rubocop 0.48.1

---
 lib/mpw/config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 829abc2..591b991 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -66,7 +66,7 @@ module MPW
                          special: false,
                          length:  16 }
 
-      %w(numeric special alpha length).each do |k|
+      %w[numeric special alpha length].each do |k|
         if options.key?("pwd_#{k}".to_sym)
           password[k.to_sym] = options["pwd_#{k}".to_sym]
         elsif !@password.nil? && @password.key?(k.to_sym)

From 621819203f4025c9632f79971383c76b8c0415bf Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 5 Apr 2017 00:03:53 +0200
Subject: [PATCH 461/531] add option to disable pinmode

---
 bin/mpw-config | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 1e3480d..67ffc50 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -60,10 +60,14 @@ OptionParser.new do |opts|
     values[:lang] = lang
   end
 
-  opts.on('-P', '--pinmode', I18n.t('option.pinmode')) do
+  opts.on('-P', '--enable-pinmode', I18n.t('option.pinmode')) do
     values[:pinmode] = true
   end
 
+  opts.on('-p', '--disable-pinmode', I18n.t('option.disable_pinmode')) do
+    values[:pinmode] = false
+  end
+
   opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
     values[:wallet_dir] = wallet_dir
   end

From 3b5bb48e6ba6cdcc9ae60783a62a85440e406761 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Wed, 5 Apr 2017 00:04:15 +0200
Subject: [PATCH 462/531] add translate for pinmode options

---
 i18n/en.yml | 2 ++
 i18n/fr.yml | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/i18n/en.yml b/i18n/en.yml
index 162f6b3..97f1928 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -49,6 +49,7 @@ en:
     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"
+    disable_pinmode: "Disable the pinentry mode"
     disable_special_chars: "Don't use special char to generate a password"
     export: "Export a wallet in an yaml file"
     file_export: "Specify the file where export data"
@@ -68,6 +69,7 @@ en:
     list_keys: "List the GPG keys in wallet"
     numeric: "Use number to generate a password"
     pattern: "Given search pattern"
+    pinmode: "Enable pinentry mode (available with gpg >= 2.1)"
     random_password: "Generate a random password"
     setup: "Create a new configuration file" 
     setup_wallet: "Create a new configuration file for a wallet"
diff --git a/i18n/fr.yml b/i18n/fr.yml
index b2e3db4..0c7319c 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -49,6 +49,7 @@ fr:
     delete_gpg_key: "Supprime le partage le portefeuille avec une autre clé GPG"
     disable_alpha: "Désactive l'utilisation des lettres dans la génération d'un mot de passe"
     disable_numeric: "Désactive l'utilisation des chiffre dans la génération d'un mot de passe"
+    disable_pinmode: "Désactive le mode pinentry"
     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"
@@ -68,6 +69,7 @@ fr:
     list_keys: "Liste les clés GPG dans le portefeuille"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
     pattern: "Motif de donnée à chercher"
+    pinmode: "Active le mode pinentry (valable avec gpg >= 2.1)"
     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"

From 55c07b90af363055bc2131f04030e500e2c56f72 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 8 Apr 2017 10:04:43 +0200
Subject: [PATCH 463/531] minor refacto

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

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index fffeaac..8bbffcf 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -66,19 +66,15 @@ cli    = MPW::Cli.new(config)
 
 cli.load_config
 
-if !options[:list].nil?
+if options.key?(:list)
   cli.list_wallet
 else
   cli.get_wallet(options[:wallet])
   cli.decrypt
 
-  if !options[:list_keys].nil?
+  if options.key?(:list_keys)
     cli.list_keys
-  elsif !options[:gpg_key].nil?
-    if options[:delete]
-      cli.delete_key(options[:gpg_key])
-    else
-      cli.add_key(options[:gpg_key])
-    end
+  elsif options.key?(:gpg_key)
+    options[:delete] ? cli.delete_key(options[:gpg_key]) : cli.add_key(options[:gpg_key])
   end
 end

From 25baa260e33756fc0efeb17c525e2563470c75fa Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 8 Apr 2017 17:15:05 +0200
Subject: [PATCH 464/531] feat: add option to set a specific path for a wallet

---
 bin/mpw-wallet    | 11 +++++++++++
 i18n/en.yml       |  4 ++++
 i18n/fr.yml       |  4 ++++
 lib/mpw/cli.rb    | 29 +++++++++++++++++++++++------
 lib/mpw/config.rb | 38 ++++++++++++++++++++++++++++++++++----
 5 files changed, 76 insertions(+), 10 deletions(-)

diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 8bbffcf..5b3b7f9 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -56,6 +56,14 @@ OptionParser.new do |opts|
     options[:list_keys] = true
   end
 
+  opts.on('-p', '--path PATH', I18n.t('option.path')) do |path|
+    options[:path] = path
+  end
+
+  opts.on('-P', '--default-path', I18n.t('option.default_path')) do
+    options[:path] = 'default'
+  end
+
   opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
     options[:wallet] = wallet
   end
@@ -68,6 +76,9 @@ cli.load_config
 
 if options.key?(:list)
   cli.list_wallet
+elsif options.key?(:path)
+  cli.get_wallet(options[:wallet])
+  cli.set_wallet_path(options[:path])
 else
   cli.get_wallet(options[:wallet])
   cli.decrypt
diff --git a/i18n/en.yml b/i18n/en.yml
index 97f1928..1fa2618 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -45,6 +45,7 @@ en:
     alpha: "Use letter to generate a password"
     config: "Specify the configuration file to use"
     clipboard: "Disable the clipboard feature"
+    default_path: "Move the wallet in 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"
@@ -68,6 +69,7 @@ en:
     list: "List the wallets"
     list_keys: "List the GPG keys in wallet"
     numeric: "Use number to generate a password"
+    path: "Move the wallet in new specify directory"
     pattern: "Given search pattern"
     pinmode: "Enable pinentry mode (available with gpg >= 2.1)"
     random_password: "Generate a random password"
@@ -120,6 +122,8 @@ en:
       not_valid: "No data to import!"
     set_config:
       valid: "The config file has been edited!"
+    set_wallet_path:
+      valid: "The wallet has well moved!"
     setup_config:
       title: "Setup a new config file"
       lang: "Choose your language (en, fr, ...) [default=%{lang}]: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 0c7319c..76b07ef 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -45,6 +45,7 @@ fr:
     alpha: "Utilise des lettres dans la génération d'un mot de passe"
     config: "Spécifie le fichier de configuration à utiliser"
     clipboard: "Désactive la fonction presse papier"
+    default_path: "Déplace le portefeuille dans le dossier par défaut"
     default_wallet: "Spécifie le porte-feuille à utiliser par défaut"
     delete_gpg_key: "Supprime le partage le portefeuille avec une autre clé GPG"
     disable_alpha: "Désactive l'utilisation des lettres dans la génération d'un mot de passe"
@@ -68,6 +69,7 @@ fr:
     list: "Liste les portefeuilles"
     list_keys: "Liste les clés GPG dans le portefeuille"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
+    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)"
     random_password: "Génére un mot de passe aléatoire"
@@ -120,6 +122,8 @@ fr:
       not_valid: "Aucune donnée à importer!"
     set_config:
       valid: "Le fichier de configuration a bien été modifié!"
+    set_wallet_path:
+      valid: "Le portefeuille a bien été déplacé!"
     setup_config:
       title: "Création d'un nouveau fichier de configuration"
       lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 56e77a1..23114d1 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -45,6 +45,17 @@ module MPW
       exit 2
     end
 
+    # Change the wallet path
+    # @args: path -> the new path
+    def set_wallet_path(path)
+      @config.set_wallet_path(path, @wallet)
+
+      puts I18n.t('form.set_wallet_path.valid').to_s.green
+    rescue => e
+      puts "#{I18n.t('display.error')} #19: #{e}".red
+      exit 2
+    end
+
     # Create a new config file
     # @args: options -> set param
     def setup(options)
@@ -315,19 +326,25 @@ module MPW
     # Display the wallet
     # @args: wallet -> the wallet name
     def get_wallet(wallet = nil)
-      @wallet_file =
+      @wallet =
         if wallet.to_s.empty?
           wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
-
           if wallets.length == 1
-            wallets[0]
+            File.basename(wallets[0], '.mpw')
           elsif !@config.default_wallet.to_s.empty?
-            "#{@config.wallet_dir}/#{@config.default_wallet}.mpw"
+            @config.default_wallet
           else
-            "#{@config.wallet_dir}/default.mpw"
+            'default'
           end
         else
-          "#{@config.wallet_dir}/#{wallet}.mpw"
+          wallet
+        end
+
+      @wallet_file =
+        if @config.wallet_paths.key?(@wallet)
+          "#{@config.wallet_paths[@wallet]}/#{@wallet}.mpw"
+        else
+          "#{@config.wallet_dir}/#{@wallet}.mpw"
         end
     end
 
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index 591b991..fd78cf8 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -30,6 +30,7 @@ module MPW
     attr_accessor :config_dir
     attr_accessor :default_wallet
     attr_accessor :wallet_dir
+    attr_accessor :wallet_paths
     attr_accessor :gpg_exe
     attr_accessor :password
     attr_accessor :pinmode
@@ -38,8 +39,7 @@ module MPW
     # @args: config_file -> the specify config file
     def initialize(config_file = nil)
       @config_file = config_file
-
-      @config_dir =
+      @config_dir  =
         if /darwin/ =~ RUBY_PLATFORM
           "#{Dir.home}/Library/Preferences/mpw"
         elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
@@ -48,7 +48,7 @@ module MPW
           "#{Dir.home}/.config/mpw"
         end
 
-      @config_file = "#{@config_dir}/mpw.cfg" if @config_file.nil? || @config_file.empty?
+      @config_file = "#{@config_dir}/mpw.cfg" if @config_file.to_s.empty?
     end
 
     # Create a new config file
@@ -85,7 +85,8 @@ module MPW
                      'default_wallet' => default_wallet,
                      'gpg_exe'        => gpg_exe,
                      'password'       => password,
-                     'pinmode'        => pinmode }
+                     'pinmode'        => pinmode,
+                     'wallet_paths'   => @wallet_paths }
 
       FileUtils.mkdir_p(@config_dir, mode: 0700)
       FileUtils.mkdir_p(wallet_dir,  mode: 0700)
@@ -132,6 +133,7 @@ module MPW
       @gpg_key        = config['gpg_key']
       @lang           = config['lang']
       @wallet_dir     = config['wallet_dir']
+      @wallet_paths   = config['wallet_paths'] || {}
       @default_wallet = config['default_wallet']
       @gpg_exe        = config['gpg_exe']
       @password       = config['password'] || {}
@@ -154,5 +156,33 @@ module MPW
 
       false
     end
+
+    # Change the path of one wallet
+    # @args: path -> the new directory path
+    #        wallet -> the wallet name
+    def set_wallet_path(path, wallet)
+      path = @wallet_dir if path == 'default'
+
+      return if path == @wallet_dir && File.exist?("#{@wallet_dir}/#{wallet}.mpw")
+      return if path == @wallet_paths[wallet]
+
+      old_wallet_file =
+        if @wallet_paths.key?(wallet)
+          "#{@wallet_paths[wallet]}/#{wallet}.mpw"
+        else
+          "#{@wallet_dir}/#{wallet}.mpw"
+        end
+
+      FileUtils.mkdir_p(path) unless Dir.exist?(path)
+      FileUtils.mv(old_wallet_file, "#{path}/#{wallet}.mpw") if File.exist?(old_wallet_file)
+
+      if path == @wallet_dir
+        @wallet_paths.delete(wallet)
+      else
+        @wallet_paths[wallet] = path
+      end
+
+      setup
+    end
   end
 end

From 18ba8967d9b634eeb8cb5f65df6e9f3f92949df0 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 8 Apr 2017 17:27:56 +0200
Subject: [PATCH 465/531] add test for wallet_paths

---
 test/test_config.rb | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/test/test_config.rb b/test/test_config.rb
index 2d1a51b..49af82c 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -61,4 +61,19 @@ class TestConfig < Test::Unit::TestCase
     assert(!@config.password[:numeric])
     assert(@config.password[:special])
   end
+
+  def test_02_wallet_paths
+    new_path = '/tmp/mpw-test'
+
+    @config = MPW::Config.new
+    @config.load_config
+
+    assert(!@config.wallet_paths['default'])
+
+    @config.set_wallet_path(new_path, 'default')
+    assert_equal(@config.wallet_paths['default'], new_path)
+
+    @config.set_wallet_path('default', 'default')
+    assert(!@config.wallet_paths['default'])
+  end
 end

From 388dc439b6baf9bfff10645a5dfad0191e5c36b5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 8 Apr 2017 17:39:51 +0200
Subject: [PATCH 466/531] fix argument in config setup

---
 lib/mpw/config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index fd78cf8..fa1a825 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -54,7 +54,7 @@ module MPW
     # Create a new config file
     # @args: options -> hash with values
     # @rtrn: true if le config file is create
-    def setup(options)
+    def setup(**options)
       gpg_key        = options[:gpg_key]        || @gpg_key
       lang           = options[:lang]           || @lang
       wallet_dir     = options[:wallet_dir]     || @wallet_dir

From b65595d0b7eea175bf20a5bce731fb9732e35d9a Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 9 Apr 2017 08:47:07 +0200
Subject: [PATCH 467/531] minor refacto

---
 lib/mpw/mpw.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index c6a5cf0..896339c 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -93,7 +93,7 @@ module MPW
         end
       end
 
-      add_key(@key) if @keys[@key].nil?
+      add_key(@key) unless @keys.key?(@key)
     rescue => e
       raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
     end

From 3f9d7eff3368b2042311fe54b38f8e07d18ec2d8 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 9 Apr 2017 10:08:25 +0200
Subject: [PATCH 468/531] feat: add print configuration

---
 bin/mpw-config |  6 +++++-
 i18n/en.yml    |  1 +
 i18n/fr.yml    |  1 +
 lib/mpw/cli.rb | 46 +++++++++++++++++++++++++++++++++++-----------
 4 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/bin/mpw-config b/bin/mpw-config
index 67ffc50..04f960b 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -112,5 +112,9 @@ if options.key?(:init)
   cli.setup_wallet_config
 else
   cli.load_config
-  cli.set_config(values)
+  if values.empty?
+    cli.list_config
+  else
+    cli.set_config(values)
+  end
 end
diff --git a/i18n/en.yml b/i18n/en.yml
index 1fa2618..5ed50c7 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -159,6 +159,7 @@ en:
 
   display:
     comment: "Comment"
+    config: "Configuration"
     error: "ERROR"
     keys: "GPG keys"
     gpg_password: "GPG passphrase: "
diff --git a/i18n/fr.yml b/i18n/fr.yml
index 76b07ef..13829a2 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -159,6 +159,7 @@ fr:
 
   display:
     comment: "Commentaire"
+    config: "Configuration"
     error: "ERREUR"
     keys: "Clés GPG"
     gpg_password: "Mot de passe GPG: "
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 23114d1..22b24cb 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -100,6 +100,21 @@ module MPW
       table_list('keys', @mpw.list_keys)
     end
 
+    # List config
+    def list_config
+      config = {
+        'lang'       => @config.lang,
+        'gpg_key'    => @config.gpg_key,
+        'config_dir' => @config.config_dir,
+        'pinmode'    => @config.pinmode,
+        'gpg_exe'    => @config.gpg_exe
+      }
+
+      @config.password.each { |k, v| config["password_#{k}"] = v }
+
+      table_list('config', config)
+    end
+
     # Load config
     def load_config
       @config.load_config
@@ -122,31 +137,40 @@ module MPW
     end
 
     # Format list on a table
+    # @args: title -> the name of table
+    #        list -> array or hash
     def table_list(title, list)
-      i      = 1
-      length = 0
+      length = { k: 0, v: 0 }
 
-      list.each do |item|
-        length = item.length if length < item.length
+      if list.is_a?(Array)
+        i    = 0
+        list = list.map do |item|
+          i += 1
+          [i, item]
+        end.to_h
+      end
+
+      list.each do |k, v|
+        length[:k] = k.to_s.length if length[:k] < k.to_s.length
+        length[:v] = v.to_s.length if length[:v] < v.to_s.length
       end
-      length += 7
 
       puts "\n#{I18n.t("display.#{title}")}".red
       print ' '
-      length.times { print '=' }
+      (length[:k] + length[:v] + 5).times { print '=' }
       print "\n"
 
-      list.each do |item|
-        print "  #{i}".cyan
-        (3 - i.to_s.length).times { print ' ' }
-        puts "| #{item}"
-        i += 1
+      list.each do |k, v|
+        print "  #{k}".cyan
+        (length[:k] - k.to_s.length + 1).times { print ' ' }
+        puts "| #{v}"
       end
 
       print "\n"
     end
 
     # Format items on a table
+    # @args: items -> an aray items
     def table_items(items = [])
       group        = '.'
       i            = 1

From 77bca426bca26c81dff1ccdaec9c51f247017a52 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sun, 9 Apr 2017 11:02:07 +0200
Subject: [PATCH 469/531] add information in print config

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

diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 22b24cb..6fd257f 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -103,14 +103,16 @@ module MPW
     # List config
     def list_config
       config = {
-        'lang'       => @config.lang,
-        'gpg_key'    => @config.gpg_key,
-        'config_dir' => @config.config_dir,
-        'pinmode'    => @config.pinmode,
-        'gpg_exe'    => @config.gpg_exe
+        'lang'           => @config.lang,
+        'gpg_key'        => @config.gpg_key,
+        'default_wallet' => @config.default_wallet,
+        'config_dir'     => @config.config_dir,
+        'pinmode'        => @config.pinmode,
+        'gpg_exe'        => @config.gpg_exe
       }
 
-      @config.password.each { |k, v| config["password_#{k}"] = v }
+      @config.wallet_paths.each { |k, v| config["path_wallet_#{k}"] = "#{v}/#{k}.mpw" }
+      @config.password.each     { |k, v| config["password_#{k}"] = v }
 
       table_list('config', config)
     end

From 0fdefdf4332d33e88bcfc1027addd159e70e4c45 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 13 Apr 2017 22:35:57 +0200
Subject: [PATCH 470/531] feat: add options to edit or update an item

---
 bin/mpw-add               | 38 +++++++++++++++++++++++++++++++++--
 bin/mpw-update            | 42 +++++++++++++++++++++++++++++++++++----
 i18n/en.yml               |  8 ++++++++
 i18n/fr.yml               |  8 ++++++++
 lib/mpw/cli.rb            | 26 ++++++++++++++----------
 templates/add_form.erb    | 16 +++++++--------
 templates/update_form.erb | 14 ++++++-------
 7 files changed, 120 insertions(+), 32 deletions(-)

diff --git a/bin/mpw-add b/bin/mpw-add
index 6376326..20eaf7b 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -24,7 +24,9 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
+values                = {}
+options               = {}
+options[:text_editor] = true
 
 OptionParser.new do |opts|
   opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
@@ -33,15 +35,47 @@ OptionParser.new do |opts|
     options[:config] = config
   end
 
+  opts.on('-C', '--comment COMMENT', I18n.t('option.comment')) do |comment|
+    values[:comment] = comment
+  end
+
+  opts.on('-G', '--group NAME', I18n.t('option.new_group')) do |group|
+    values[:group] = group
+  end
+
   opts.on('-h', '--help', I18n.t('option.help')) do
     puts 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
 
+  opts.on('-t', '--text-editor', I18n.t('option.text_editor')) do
+    options[:text_editor] = true
+  end
+
+  opts.on('-u', '--user USER', I18n.t('option.user')) do |user|
+    values[:user] = user
+  end
+
   opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
     options[:wallet] = wallet
   end
@@ -53,4 +87,4 @@ cli    = MPW::Cli.new(config)
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.add(options[:password])
+cli.add(options[:password], options[:text_editor], values)
diff --git a/bin/mpw-update b/bin/mpw-update
index c258330..d887ce3 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -24,8 +24,10 @@ require 'mpw/cli'
 # Options
 # --------------------------------------------------------- #
 
-options = {}
-values  = {}
+values                = {}
+search                = {}
+options               = {}
+options[:text_editor] = false
 
 OptionParser.new do |opts|
   opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
@@ -34,7 +36,15 @@ OptionParser.new do |opts|
     options[:config] = config
   end
 
+  opts.on('-C', '--comment COMMENT', I18n.t('option.comment')) do |comment|
+    values[:comment] = comment
+  end
+
   opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
+    search[:group] = group
+  end
+
+  opts.on('-G', '--new-group NAME', I18n.t('option.new_group')) do |group|
     values[:group] = group
   end
 
@@ -43,14 +53,38 @@ 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|
-    values[:pattern] = 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
 
+  opts.on('-t', '--text-editor', I18n.t('option.text_editor')) do
+    options[:text_editor] = true
+  end
+
+  opts.on('-u', '--user USER', I18n.t('option.user')) do |user|
+    values[:user] = user
+  end
+
   opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
     options[:wallet] = wallet
   end
@@ -62,4 +96,4 @@ cli    = MPW::Cli.new(config)
 cli.load_config
 cli.get_wallet(options[:wallet])
 cli.decrypt
-cli.update(options[:password], values)
+cli.update(options[:password], options[:text_editor], search, values)
diff --git a/i18n/en.yml b/i18n/en.yml
index 5ed50c7..402facd 100644
--- a/i18n/en.yml
+++ b/i18n/en.yml
@@ -43,6 +43,7 @@ en:
     add: "Add an item or key"
     add_gpg_key: "Share the wallet with an other GPG key"
     alpha: "Use letter to generate 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"
@@ -61,6 +62,7 @@ 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 since a yaml file"
     key: "Specify the key name"
@@ -68,17 +70,23 @@ en:
     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"
+    otp_code: "Set an otp key"
     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"
     special_chars: "Use special char to generate a password"
     show: "Search and show the items"
     show_all: "List all items"
+    text_editor: "Use text editor to edit the item"
     usage: "Usage"
+    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 13829a2..b1b4ecc 100644
--- a/i18n/fr.yml
+++ b/i18n/fr.yml
@@ -44,6 +44,7 @@ fr:
     add_gpg_key: "Partage le portefeuille avec une autre clé GPG"
     alpha: "Utilise des lettres dans la génération d'un mot de passe"
     config: "Spécifie le fichier de configuration à utiliser"
+    comment: "Spécifie un commentaire"
     clipboard: "Désactive la fonction presse papier"
     default_path: "Déplace le portefeuille dans le dossier par défaut"
     default_wallet: "Spécifie le porte-feuille à utiliser par défaut"
@@ -61,6 +62,7 @@ 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é"
@@ -68,17 +70,23 @@ fr:
     length: "Taille du mot de passe"
     list: "Liste les portefeuilles"
     list_keys: "Liste les clés GPG dans le portefeuille"
+    new_group: "Spécifie le groupe de l'item"
     numeric: "Utilise des chiffre dans la génération d'un mot de passe"
+    otp_code: "Spécifie un code OTP"
     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"
     special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
     show: "Recherche et affiche les éléments"
     show_all: "Liste tous les éléments"
+    text_editor: "Active l'édition avec un éditeur de texte"
     usage: "Utilisation"
+    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 6fd257f..7aad7f0 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -400,9 +400,9 @@ module MPW
     # @args: template -> template name
     #        item -> the item to edit
     #        password -> disable field password
-    def text_editor(template_name, item = nil, password = false)
+    # @rtrn: a hash with the value for an item
+    def text_editor(template_name, password = false, item = nil, **options)
       editor        = ENV['EDITOR'] || 'nano'
-      options       = {}
       opts          = {}
       template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
       template      = ERB.new(IO.read(template_file))
@@ -430,8 +430,10 @@ module MPW
 
     # Form to add a new item
     # @args: password -> generate a random password
-    def add(password = false)
-      options            = text_editor('add_form', nil, password)
+    #        text_editor -> enable text editor mode
+    #        values -> hash with 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)
       options[:password] = MPW.password(@config.password) if password
 
@@ -447,8 +449,10 @@ module MPW
 
     # Update an item
     # @args: password -> generate a random password
+    #        text_editor -> enable text editor mode
     #        options -> the option to search
-    def update(password = false, **options)
+    #        values -> hash with multiples value to set the item
+    def update(password = false, text_editor = false, options = {}, **values)
       items = @mpw.list(options)
 
       if items.empty?
@@ -456,13 +460,13 @@ module MPW
       else
         table_items(items) if items.length > 1
 
-        item               = get_item(items)
-        options            = text_editor('update_form', item, password)
-        options[:password] = MPW.password(@config.password) if password
+        item              = get_item(items)
+        values            = text_editor('update_form', password, item, values) if text_editor
+        values[:password] = MPW.password(@config.password) if password
 
-        item.update(options)
-        @mpw.set_password(item.id, options[:password]) if options.key?(:password)
-        @mpw.set_otp_key(item.id, options[:otp_key])   if options.key?(:otp_key)
+        item.update(values)
+        @mpw.set_password(item.id, values[:password]) if values.key?(:password)
+        @mpw.set_otp_key(item.id, values[:otp_key])   if values.key?(:otp_key)
         @mpw.write_data
 
         puts I18n.t('form.update_item.valid').to_s.green
diff --git a/templates/add_form.erb b/templates/add_form.erb
index bb81787..d88874e 100644
--- a/templates/add_form.erb
+++ b/templates/add_form.erb
@@ -1,9 +1,9 @@
 ---
-host:           # <%= I18n.t('form.add_item.host') %>
-user:           # <%= I18n.t('form.add_item.login') %>
-group:          # <%= I18n.t('form.add_item.group') %> 
-protocol:       # <%= I18n.t('form.add_item.protocol') %><% unless password %>
-password:       # <%= I18n.t('form.add_item.password') %><% end %>
-port:           # <%= I18n.t('form.add_item.port') %>
-comment:        # <%= I18n.t('form.add_item.comment') %>
-otp_key:        # <%= I18n.t('form.add_item.otp_key') %>
+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') %>
diff --git a/templates/update_form.erb b/templates/update_form.erb
index f27da0b..5037568 100644
--- a/templates/update_form.erb
+++ b/templates/update_form.erb
@@ -1,17 +1,17 @@
 ---
 # <%= I18n.t('form.update_item.host') %>
-host: <%= item.host %>
+host: <%  if options[:host] %><%= options[:host] %><% else %><%= item.host %><% end %>
 # <%= I18n.t('form.update_item.login') %>
-user: <%= item.user %><% unless password %>
+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: <%= item.group %>
+group: <%  if options[:group] %><%= options[:group] %><% else %><%= item.group %><% end %>
 # <%= I18n.t('form.update_item.protocol') %>
-protocol: <%= item.protocol %>
+protocol: <%  if options[:protocol] %><%= options[:protocol] %><% else %><%= item.protocol %><% end %>
 # <%= I18n.t('form.update_item.port') %>
-port: <%= item.port %>
+port: <%  if options[:port] %><%= options[:port] %><% else %><%= item.port %><% end %>
 # <%= I18n.t('form.update_item.otp_key') %>
-otp_key: 
+otp_key: <%  if options[:otp_key] %><%= options[:otp_key] %><% end %>
 # <%= I18n.t('form.update_item.comment') %>
-comment: <%= item.comment %>
+comment: <%  if options[:comment] %><%= options[:comment] %><% else %><%= item.comment %><% end %>

From b2b94374312f570a79c2971a5361803171b579d9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Apr 2017 23:22:56 +0200
Subject: [PATCH 471/531] change copyright year

---
 bin/mpw           | 2 +-
 bin/mpw-add       | 2 +-
 bin/mpw-config    | 2 +-
 bin/mpw-copy      | 2 +-
 bin/mpw-delete    | 2 +-
 bin/mpw-export    | 2 +-
 bin/mpw-genpwd    | 2 +-
 bin/mpw-import    | 2 +-
 bin/mpw-list      | 2 +-
 bin/mpw-update    | 2 +-
 bin/mpw-wallet    | 2 +-
 lib/mpw/cli.rb    | 2 +-
 lib/mpw/config.rb | 2 +-
 lib/mpw/item.rb   | 2 +-
 lib/mpw/mpw.rb    | 2 +-
 15 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/bin/mpw b/bin/mpw
index 1bdf835..811322e 100755
--- a/bin/mpw
+++ b/bin/mpw
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-add b/bin/mpw-add
index 20eaf7b..2c2e996 100644
--- a/bin/mpw-add
+++ b/bin/mpw-add
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-config b/bin/mpw-config
index 04f960b..3f049fa 100644
--- a/bin/mpw-config
+++ b/bin/mpw-config
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-copy b/bin/mpw-copy
index 9436a10..95c01d6 100644
--- a/bin/mpw-copy
+++ b/bin/mpw-copy
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-delete b/bin/mpw-delete
index f276785..ea352c4 100644
--- a/bin/mpw-delete
+++ b/bin/mpw-delete
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-export b/bin/mpw-export
index 982d1ad..83d35ae 100644
--- a/bin/mpw-export
+++ b/bin/mpw-export
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd
index 1c09489..051c5c4 100644
--- a/bin/mpw-genpwd
+++ b/bin/mpw-genpwd
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-import b/bin/mpw-import
index d9c868d..0502b2b 100644
--- a/bin/mpw-import
+++ b/bin/mpw-import
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-list b/bin/mpw-list
index 3571abc..434ea11 100644
--- a/bin/mpw-list
+++ b/bin/mpw-list
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-update b/bin/mpw-update
index d887ce3..5e2c62d 100644
--- a/bin/mpw-update
+++ b/bin/mpw-update
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/bin/mpw-wallet b/bin/mpw-wallet
index 5b3b7f9..c23e1e2 100644
--- a/bin/mpw-wallet
+++ b/bin/mpw-wallet
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb
index 7aad7f0..d622bff 100644
--- a/lib/mpw/cli.rb
+++ b/lib/mpw/cli.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb
index fa1a825..72b732d 100644
--- a/lib/mpw/config.rb
+++ b/lib/mpw/config.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb
index 8b4b867..b338998 100644
--- a/lib/mpw/item.rb
+++ b/lib/mpw/item.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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
diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb
index 896339c..71676c7 100644
--- a/lib/mpw/mpw.rb
+++ b/lib/mpw/mpw.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby
 # MPW is a software to crypt and manage your passwords
-# Copyright (C) 2016  Adrien Waksberg <mpw@yae.im>
+# 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

From 02996b590419aa81e84fc5ff3ea0a3b24e46bfbd Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 20 Apr 2017 23:26:40 +0200
Subject: [PATCH 472/531] gemspec: add minimal ruby version

---
 mpw.gemspec | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mpw.gemspec b/mpw.gemspec
index 9f27023..4afbe3b 100644
--- a/mpw.gemspec
+++ b/mpw.gemspec
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ['lib']
 
+  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 'highline',  '~> 1.7', '>= 1.7.8'

From 83fe41921c4c4ab55af4e9ef88d21f2b05f820de Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Sat, 22 Apr 2017 10:25:19 +0200
Subject: [PATCH 473/531] update version 4.1

---
 CHANGELOG.md |  9 +++++++++
 README.md    | 33 +++++++++++++++++++++++++++------
 VERSION      |  2 +-
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 452ac98..7f52b5f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,13 @@
 # CHANGELOG
+## v4.1.0
+
+ * feat: add options to update or add an item in command line
+ * feat: print config
+ * feat: add a specific path for a wallet
+ * feat: add rubocop to fix syntax
+ * fix: pinentry mode with gpg >= 2.1
+ * remove SSH and FTP synchronization
+
 ## v4.0.0
 
  * feature: set default wallet
diff --git a/README.md b/README.md
index fd2534a..60a69d8 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # MPW: Manage your passwords!
-[![Version](https://img.shields.io/badge/latest_version-4.0.0-green.svg)](https://github.com/nishiki/manage-password/releases)
+[![Version](https://img.shields.io/badge/latest_version-4.1.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)
 
@@ -82,11 +82,6 @@ List all available wallets:
 mpw wallet --list
 ```
 
-Create an other wallet:
-```
-mpw config --wallet work --init user@host.com
-```
-
 List all GPG keys in wallet:
 ```
 mpw wallet --list-keys [--wallet NAME]
@@ -139,3 +134,29 @@ Example yaml file for mpw:
   otp_key: 
   comment: Da Linux French Site
 ```
+
+### Config
+
+Print the current config
+```
+mpw config
+```
+
+Output:
+
+```
+Configuration
+ ==============================================
+  lang             | fr
+  gpg_key          | mpw@yae.im
+  default_wallet   |
+  config_dir       | /home/mpw/.config/mpw
+  pinmode          | true
+  gpg_exe          |
+  path_wallet_test | /tmp/test.mpw
+  password_numeric | true
+  password_alpha   | true
+  password_special | false
+  password_length  | 16
+
+```
diff --git a/VERSION b/VERSION
index 4aa925d..ee74734 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.1.0-dev
+4.1.0

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 474/531] 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 475/531] 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 476/531] 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 477/531] 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 478/531] 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 479/531] 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 480/531] 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 481/531] 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 482/531] 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 483/531] 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 484/531] 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 485/531] 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 486/531] 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 487/531] 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 488/531] 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 489/531] 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 490/531] 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 491/531] 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 492/531] 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 493/531] 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 494/531] 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 495/531] 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 496/531] 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 497/531] 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 498/531] 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 499/531] 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 500/531] 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 501/531] 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 502/531] 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 503/531] 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 504/531] 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 505/531] 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 506/531] 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 507/531] 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 508/531] 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 509/531] 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 510/531] 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 511/531] 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 512/531] 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 513/531] 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 514/531] 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 515/531] 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 516/531] 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 517/531] 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 518/531] 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 519/531] 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 520/531] 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 521/531] 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 522/531] 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 523/531] 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 524/531] 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 525/531] 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 526/531] 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 527/531] 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 528/531] 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 529/531] 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 530/531] 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 531/531] 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.