diff options
Diffstat (limited to 'contrib/importers/lastpass2pass.rb')
-rwxr-xr-x | contrib/importers/lastpass2pass.rb | 131 |
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 |