summaryrefslogtreecommitdiff
path: root/contrib/importers/lastpass2pass.rb
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/importers/lastpass2pass.rb')
-rwxr-xr-xcontrib/importers/lastpass2pass.rb131
1 files changed, 131 insertions, 0 deletions
diff --git a/contrib/importers/lastpass2pass.rb b/contrib/importers/lastpass2pass.rb
new file mode 100755
index 0000000..41a2a29
--- /dev/null
+++ b/contrib/importers/lastpass2pass.rb
@@ -0,0 +1,131 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2012 Alex Sayers <alex.sayers@gmail.com>. All Rights Reserved.
+# This file is licensed under the GPLv2+. Please see COPYING for more information.
+
+# LastPass Importer
+#
+# Reads CSV files exported from LastPass and imports them into pass.
+
+# Usage:
+#
+# Go to lastpass.com and sign in. Next click on your username in the top-right
+# corner. In the drop-down meny that appears, click "Export". After filling in
+# your details again, copy the text and save it somewhere on your disk. Make sure
+# you copy the whole thing, and resist the temptation to "Save Page As" - the
+# script doesn't like HTML.
+#
+# Fire up a terminal and run the script, passing the file you saved as an argument.
+# It should look something like this:
+#
+#$ ./lastpass2pass.rb path/to/passwords_file.csv
+
+# Parse flags
+require 'optparse'
+optparse = OptionParser.new do |opts|
+ opts.banner = "Usage: #{$0} [options] filename"
+
+ FORCE = false
+ opts.on("-f", "--force", "Overwrite existing records") { FORCE = true }
+ DEFAULT_GROUP = ""
+ opts.on("-d", "--default GROUP", "Place uncategorised records into GROUP") { |group| DEFAULT_GROUP = group }
+ opts.on("-h", "--help", "Display this screen") { puts opts; exit }
+
+ opts.parse!
+end
+
+# Check for a filename
+if ARGV.empty?
+ puts optparse
+ exit 0
+end
+
+# Get filename of csv file
+filename = ARGV.join(" ")
+puts "Reading '#{filename}'..."
+
+
+class Record
+ def initialize name, url, username, password, extra, grouping, fav
+ @name, @url, @username, @password, @extra, @grouping, @fav = name, url, username, password, extra, grouping, fav
+ end
+
+ def name
+ s = ""
+ s << @grouping + "/" unless @grouping.empty?
+ s << @name
+ s.gsub(/ /, "_").gsub(/'/, "")
+ end
+
+ def to_s
+ s = ""
+ s << "#{@password}\n---\n"
+ s << "#{@grouping} / " unless @grouping.empty?
+ s << "#{@name}\n"
+ s << "username: #{@username}\n" unless @username.empty?
+ s << "password: #{@password}\n" unless @password.empty?
+ s << "url: #{@url}\n" unless @url == "http://sn"
+ s << "#{@extra}\n" unless @extra.nil?
+ return s
+ end
+end
+
+# Extract individual records
+entries = []
+entry = ""
+begin
+ file = File.open(filename)
+ file.each do |line|
+ if line =~ /^http/
+ entries.push(entry)
+ entry = ""
+ end
+ entry += line
+ end
+ entries.push(entry)
+ entries.shift
+ puts "#{entries.length} records found!"
+rescue
+ puts "Couldn't find #{filename}!"
+ exit 1
+end
+
+# Parse records and create Record objects
+records = []
+entries.each do |e|
+ args = e.split(",")
+ url = args.shift
+ username = args.shift
+ password = args.shift
+ fav = args.pop
+ grouping = args.pop
+ grouping = DEFAULT_GROUP if grouping.empty?
+ name = args.pop
+ extra = args.join(",")[1...-1]
+
+ records << Record.new(name, url, username, password, extra, grouping, fav)
+end
+puts "Records parsed: #{records.length}"
+
+successful = 0
+errors = []
+records.each do |r|
+ print "Creating record #{r.name}..."
+ IO.popen("pass insert -m#{"f" if FORCE} '#{r.name}' > /dev/null", 'w') do |io|
+ io.puts r
+ end
+ if $? == 0
+ puts " done!"
+ successful += 1
+ else
+ puts " error!"
+ errors << r
+ end
+end
+puts "#{successful} records successfully imported!"
+
+if errors
+ puts "There were #{errors.length} errors:"
+ errors.each { |e| print e.name + (e == errors.last ? ".\n" : ", ")}
+ puts "These probably occurred because an identically-named record already existed, or because there were multiple entries with the same name in the csv file."
+end