class Tome::Command
Public Class Methods
run(tome_filename, args, stdout = $stdout, stderr = $stderr, stdin = $stdin)
click to toggle source
# File lib/tome/command.rb, line 12 def self.run(tome_filename, args, stdout = $stdout, stderr = $stderr, stdin = $stdin) command = new() return command.send(:run, tome_filename, args, stdout, stderr, stdin) end
Private Instance Methods
command_from_arg(arg)
click to toggle source
# File lib/tome/command.rb, line 57 def command_from_arg(arg) commands = { /\A(help|-h|--help)\z/i => :help, /\A(version|ver|-v|--version)\z/i => :version, /\A(set|s|add)\z/i => :set, /\A(get|g|show)\z/i => :get, /\A(delete|del|rm|remove)\z/i => :delete, /\A(generate|gen)\z/i => :generate, /\A(copy|cp)\z/i => :copy, /\A(rename|ren|rn)\z/i => :rename, /\A(master)\z/i => :master, /\A(list|ls)\z/i => :list } commands.each { |pattern, command| return command if arg =~ pattern } return nil end
copy(args)
click to toggle source
# File lib/tome/command.rb, line 243 def copy(args) if args.length != 1 raise CommandError, "Invalid arguments.\n\n#{$copy_usage}" end # tome cp bar.com # tome cp foo@bar.com pattern = args[0] tome = tome_connect() matches = tome.find(pattern) if matches.empty? raise CommandError, "No password found for #{pattern}." elsif matches.count > 1 match = pick_match(pattern, matches) else match = matches.first end name = match.first password = match.last Clipboard.copy(password) if Clipboard.paste == password @out.puts "Password for #{name} copied to clipboard." else @err.puts "Failed to copy password for #{name} to clipboard." end end
delete(args)
click to toggle source
# File lib/tome/command.rb, line 185 def delete(args) if args.length != 1 raise CommandError, "Invalid arguments.\n\n#{$delete_usage}" end tome = tome_connect() # tome del bar.com # tome del foo@bar.com id = args[0] exists = !tome.get(id).nil? if exists confirmed = prompt_confirm("Are you sure you want to delete the password for #{id} (y/n)? ") if !confirmed raise CommandError, 'Aborted.' end end deleted = tome.delete(id) if deleted @out.puts "Deleted password for #{id}." else @out.puts "No password found for #{id}." end end
generate(args)
click to toggle source
# File lib/tome/command.rb, line 213 def generate(args) if args.length != 1 raise CommandError, "Invalid arguments.\n\n#{$generate_usage}" end created, tome = tome_create_connect() # tome gen bar.com # tome gen foo@bar.com id = args[0] password = generate_password() exists = !tome.get(id).nil? if exists confirm = prompt_confirm("A password already exists for #{id}. Overwrite (y/n)? ") if !confirm raise CommandError, 'Aborted.' end end created = tome.set(id, password) Clipboard.copy(password) if created @out.puts "Generated and copied password for #{id}." else @out.puts "Updated and copied password for #{id}." end end
generate_password()
click to toggle source
# File lib/tome/command.rb, line 356 def generate_password Passgen.generate(:length => 30, :symbols => true) end
get(args)
click to toggle source
# File lib/tome/command.rb, line 159 def get(args) if args.length != 1 raise CommandError, "Invalid arguments.\n\n#{$get_usage}" end # tome get bar.com # tome get foo@bar.com pattern = args[0] tome = tome_connect() matches = tome.find(pattern) if matches.empty? raise CommandError, "No password found for #{pattern}." elsif matches.count == 1 match = matches.first @out.puts "Password for #{match.first}:" @out.puts match.last else @out.puts "Multiple matches for #{pattern}:" matches.each { |key, password| @out.puts "#{key}: #{password}" } end end
handle_command(args)
click to toggle source
# File lib/tome/command.rb, line 44 def handle_command(args) # TODO: Handle 'command --help', e.g. 'tome set --help'. command = command_from_arg(args[0]) if command.nil? raise CommandError, "Unrecognized command: #{args[0]}.\n\n#{$usage}" end args.shift send(command, args) end
help(args)
click to toggle source
# File lib/tome/command.rb, line 78 def help(args) if args.length > 1 raise CommandError, "Invalid arguments.\n\n#{$usage}" end if args.empty? usage() return end command = command_from_arg(args[0]) if command.nil? raise CommandError, "No help for unrecognized command: #{args[0]}.\n\n#{$usage}" end help = { :help => $help_usage, :set => $set_usage, :get => $get_usage, :delete => $delete_usage, :generate => $generate_usage, :copy => $copy_usage, :rename => $rename_usage, :master => $master_usage, :list => $list_usage } usage = help[command] if usage.nil? raise CommandError, "No help available for command: #{args[0]}." end @out.puts usage end
input_password()
click to toggle source
# File lib/tome/command.rb, line 384 def input_password input = proc { |stdin| raw = stdin.gets return nil if raw.nil? password = raw.strip @out.puts return password } begin @in.noecho { |stdin| input.call stdin } rescue Errno::EBADF # This can happen when stdin refers to a file or pipe. # In this case, we ignore 'no echo' and do normal input. input.call @in end end
list(args)
click to toggle source
# File lib/tome/command.rb, line 297 def list(args) if !args.empty? raise CommandError, "Invalid arguments.\n\n#{$list_usage}" end tome = tome_connect() count = 0 tome.each_password { |id, password| @out.puts "#{id}: #{password}" count += 1 } if count == 0 @out.puts 'No passwords stored.' end end
master(args)
click to toggle source
# File lib/tome/command.rb, line 342 def master(args) if args.count > 0 raise CommandError, "Invalid arguments.\n\n#{$master_usage}" end created, tome = tome_create_connect() if !created master_password = prompt_password('New master password') tome.master_password = master_password @out.puts 'Master password updated.' end end
pick_match(pattern, matches)
click to toggle source
# File lib/tome/command.rb, line 274 def pick_match(pattern, matches) matches = matches.to_a @out.puts "Found multiple matches for \"#{pattern}\":\n\n" matches.each_with_index { |match, i| @out.puts "\t#{i + 1}: #{match[0]}" } begin @out.print "\n> " index = @in.gets.to_i if index <= 0 || index > matches.length @out.puts 'Invalid option.' raise end return matches[index - 1] rescue retry end end
prompt_confirm(prompt)
click to toggle source
# File lib/tome/command.rb, line 406 def prompt_confirm(prompt) begin @out.print prompt confirm = @in.gets.strip if confirm =~ /\Ay/i return true elsif confirm =~ /\An/i return false end rescue retry end end
prompt_password(prompt = 'Password')
click to toggle source
# File lib/tome/command.rb, line 360 def prompt_password(prompt = 'Password') begin @err.print "#{prompt}: " password = input_password() if password.empty? @err.puts 'Password cannot be blank.' raise end @err.print "#{prompt} (verify): " verify = input_password() if verify != password @err.puts 'Passwords do not match.' raise end rescue retry end return password end
rename(args)
click to toggle source
# File lib/tome/command.rb, line 315 def rename(args) if args.count != 2 raise CommandError, "Invalid arguments.\n\n#{$rename_usage}" end tome = tome_connect() old_id = args[0] new_id = args[1] overwriting = !tome.get(new_id).nil? if overwriting confirm = prompt_confirm("A password already exists for #{new_id}. Overwrite (y/n)? ") if !confirm raise CommandError, 'Aborted.' end end renamed = tome.rename(old_id, new_id) if !renamed raise CommandError, "#{old_id} does not exist." else @out.puts "#{old_id} renamed to #{new_id}." end end
run(tome_filename, args, stdout, stderr, stdin)
click to toggle source
# File lib/tome/command.rb, line 18 def run(tome_filename, args, stdout, stderr, stdin) @out = stdout @err = stderr @in = stdin @tome_filename = tome_filename if args.length < 1 usage() return 1 end begin handle_command(args) rescue CommandError => error @err.puts "Error: #{error.message}" return 1 rescue FileFormatError => error # Fix file separators for Windows. filename = @tome_filename.gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR) @err.puts "Error: Cannot read #{filename}: #{error.message}" return 1 end return 0 end
set(args)
click to toggle source
# File lib/tome/command.rb, line 118 def set(args) if args.length < 1 || args.length > 2 raise CommandError, "Invalid arguments.\n\n#{$set_usage}" end created, tome = tome_create_connect() case args.length # TODO: Validate that first argument is in [username@]domain form. # tome set bar.com # tome set foo@bar.com when 1 id = args[0] password = prompt_password() # tome set bar.com p4ssw0rd # tome set foo@bar.com p4ssw0rd when 2 id = args[0] password = args[1] end exists = !tome.get(id).nil? if exists confirm = prompt_confirm("A password already exists for #{id}. Overwrite (y/n)? ") if !confirm raise CommandError, 'Aborted.' end end created = tome.set(id, password) if created @out.print 'Created ' else @out.print 'Updated ' end @out.puts "password for #{id}." end
tome_connect()
click to toggle source
# File lib/tome/command.rb, line 428 def tome_connect if !Tome.exists?(@tome_filename) raise CommandError, "Tome database does not exist. Use 'tome set' or 'tome generate' to create a password first." end begin @err.print 'Master password: ' master_password = input_password() tome = Tome.new(@tome_filename, master_password) rescue MasterPasswordError @err.puts 'Incorrect master password.' if master_password.nil? raise CommandError, 'Authentication failed.' else retry end end return tome end
tome_create_connect()
click to toggle source
# File lib/tome/command.rb, line 450 def tome_create_connect if !Tome.exists?(@tome_filename) @out.puts 'Creating tome database.' master_password = prompt_password('Master password') return true, Tome.create!(@tome_filename, master_password) else return false, tome_connect() end end
usage()
click to toggle source
# File lib/tome/command.rb, line 422 def usage @err.puts "tome version #{VERSION}" @err.puts @err.puts $usage end
version(args)
click to toggle source
# File lib/tome/command.rb, line 114 def version(args) @out.puts "tome version #{VERSION}" end