roboform2pass.rb (3337B)
1 #!/usr/bin/env ruby 2 3 # Copyright 2015 Sergey Makridenkov <sergey@makridenkov.com>. 4 # Released under MIT License. 5 # 6 # Usage: 7 # 1. Save roboform print lists (like File > Print lists > Logins) to ~/roboform_print_lists. 8 # 2. Run import: ./roboform2pass.rb ~/roboform_print_lists 9 10 11 require 'nokogiri' 12 13 class Login 14 def initialize 15 self.fields = {} 16 self.raw = [] 17 end 18 19 attr_accessor :key 20 attr_accessor :password 21 attr_accessor :url 22 attr_accessor :fields 23 attr_accessor :raw 24 25 def ask_required_info 26 ask!(:key) if blank?(key) 27 find_password! 28 if blank?(password) 29 ask!(:password) 30 puts '' 31 end 32 end 33 34 def save 35 if valid? 36 IO.popen("pass insert -m -f '#{path}' > /dev/null", "w") do |pass_io| 37 pass_io.puts password 38 pass_io.puts "Url: #{url}" if present?(url) 39 pass_io.puts "Fields: #{fields}" if fields.any? 40 pass_io.puts "Notes: #{raw}" if raw.any? 41 end 42 43 $? == 0 44 end 45 end 46 47 private 48 49 def path 50 key = self.key.downcase.gsub(/[^\w\.\/]/, '_').gsub(/_{2,}/, '_') 51 "roboform/#{key}" 52 end 53 54 def valid? 55 present?(key) && present?(password) 56 end 57 58 def find_password! 59 fields.each do |key, val| 60 key = key.downcase 61 if key.include?('password') || key.include?('pwd') || key.include?('pass') 62 self.password = val 63 end 64 end 65 end 66 67 def ask!(field) 68 puts Colorize.red("#{field.capitalize} is empty for login:") 69 print_self 70 print Colorize.green "Please type #{field}: " 71 self.send("#{field}=", gets.chomp) 72 end 73 74 def print_self 75 puts Colorize.yellow "\tKey:\t#{key}" if present?(key) 76 puts Colorize.yellow "\tPassword:\t#{password}" if present?(password) 77 puts Colorize.yellow "\tUrl:\t#{url}" if present?(url) 78 puts Colorize.yellow "\tFields:\t#{fields}" if fields.any? 79 puts Colorize.yellow "\tRaw:\t#{raw}" if raw.any? 80 end 81 82 def blank?(str) 83 !str || str.strip.empty? 84 end 85 86 def present?(str) 87 !blank?(str) 88 end 89 end 90 91 class Colorize 92 class << self 93 def red(mes) 94 colorize(31, mes) 95 end 96 97 def green(mes) 98 colorize(32, mes) 99 end 100 101 def yellow(mes) 102 colorize(33, mes) 103 end 104 105 def pink(mes) 106 colorize(35, mes) 107 end 108 109 private 110 111 def colorize(color_code, mes) 112 "\e[#{color_code}m#{mes}\e[0m" 113 end 114 end 115 end 116 117 118 print_list_dir = ARGV.pop 119 unless print_list_dir 120 raise "No dir/to/roboform/print_lists" 121 end 122 print_list_dir = File.expand_path(print_list_dir) 123 124 # parse logins 125 logins_path = Dir.glob("#{print_list_dir}/RoboForm Logins*.html").first 126 unless logins_path 127 raise 'Login HTML (RoboForm Logins*.html) not found' 128 end 129 130 html_logins = Nokogiri::HTML(File.open(logins_path)) 131 132 saved_logins = 0 133 134 html_logins.css('table').each do |table| 135 login = Login.new 136 137 table.css('tr').each do |tr| 138 caption = tr.at_css('.caption') 139 subcaption = tr.at_css('.subcaption') 140 key = tr.at_css('.field') 141 142 if caption 143 login.key = caption.text() 144 elsif subcaption 145 login.url = subcaption.text() 146 elsif key 147 login.fields[key.text()] = tr.at_css('.wordbreakfield').text() 148 else 149 login.raw << tr.at_css('.wordbreakfield').text() 150 end 151 end 152 153 if login.fields.any? 154 login.ask_required_info 155 if login.save 156 saved_logins += 1 157 end 158 end 159 end 160 161 puts "Imported passwords: #{saved_logins}" 162