class Dotgpg::Cli
Public Instance Methods
add(file=nil)
click to toggle source
# File lib/dotgpg/cli.rb, line 44 def add(file=nil) return if helped? dir = Dotgpg::Dir.closest fail "not in a dotgpg directory" unless dir key = read_key_file_for_add(file) fail "#{file || "<stdin>"}: not a valid GPG key" unless key if dir.has_key?(key) && !options[:force] fail "#{dir.key_path(key)}: already exists" end info "Adding #{key.name} to #{dir.path}" info " #{dir.key_path(key).relative_path_from(dir.path)}" dir.add_key(key) rescue GPGME::Error::BadPassphrase => e fail e.message end
cat(*files)
click to toggle source
# File lib/dotgpg/cli.rb, line 90 def cat(*files) return if helped? dir = Dotgpg::Dir.closest(*files) fail "not in a dotgpg directory" unless dir files.each do |f| dir.decrypt f, $stdout end rescue GPGME::Error::BadPassphrase => e fail e.message end
create(file)
click to toggle source
# File lib/dotgpg/cli.rb, line 112 def create(file) return if helped? files = [file] dir = Dotgpg::Dir.closest(*files) fail "not in a dotgpg directory" unless dir fail "No input received" if STDIN.tty? dir.encrypt file, STDIN.read rescue GPGME::Error::BadPassphrase => e fail e.message end
edit(*files)
click to toggle source
# File lib/dotgpg/cli.rb, line 126 def edit(*files) return if helped? dir = Dotgpg::Dir.closest(*files) fail "not in a dotgpg directory" unless dir dir.reencrypt files do |tempfiles| if tempfiles.any? to_edit = tempfiles.values.map do |temp| Shellwords.escape(temp.path) end system "#{Dotgpg.editor} #{to_edit.join(" ")}" fail "Problem with editor. Not saving changes" unless $?.success? end end rescue GPGME::Error::BadPassphrase => e fail e.message end
init(directory=".")
click to toggle source
# File lib/dotgpg/cli.rb, line 10 def init(directory=".") return if helped? dir = Dotgpg::Dir.new directory if dir.dotgpg.exist? fail "#{directory}/.gpg already exists" end key = Dotgpg::Key.secret_key(options[:email], options[:"new-key"]) info "Initializing new dotgpg directory" info " #{directory}/README.md" unless File.exist? 'README.md' info " #{directory}/.gpg/#{key.email}" FileUtils.mkdir_p(dir.dotgpg) unless File.exist? 'README.md' FileUtils.cp Pathname.new(__FILE__).dirname.join("template/README.md"), dir.path.join("README.md") end dir.add_key(key) end
key()
click to toggle source
# File lib/dotgpg/cli.rb, line 35 def key return if helped? key = Dotgpg::Key.secret_key(options[:email], options[:"new-key"]) $stdout.print key.export(armor: true).to_s end
merge(*files)
click to toggle source
# File lib/dotgpg/cli.rb, line 148 def merge(*files) require 'find' require 'digest' require 'fileutils' return if helped? fail "usage: MYFILE OLDFILE YOURFILE" unless files.length == 3 # ok, we won't know which gpg directory was used for our files because # the .merge_files are dumped in the root git dir. so we resort to ulginess # - we hash OLDFILE, search for directories which contain a .gpg subdir # and check the md5sums of all the files in said directory. if there's a # match we have our dir old_hash = Digest::SHA256.file(files[1]).hexdigest # if file is nil DotGpg:Dir.closest throws an error. if it's just a blank # string we get the "not in a dotgpg directory" message file = '' Find.find(::Dir.pwd) do |path| if FileTest.directory?(path) && File.basename(path) == ".gpg" # found a .gpg dir, check in the parent for our target file dotgpg_dir = File.dirname path gpg_files = ::Dir.glob(File.join dotgpg_dir, "*") if matched = gpg_files.find { |f| File.file?(f) && old_hash == Digest::SHA256.file(f).hexdigest } file = matched break end end end dir = Dotgpg::Dir.closest(file) fail "not in a dotgpg directory" unless dir mine = Tempfile.open('mine') old = Tempfile.open('old') yours = Tempfile.open('yours') begin # decrypt all three of our files dir.decrypt files[0], mine dir.decrypt files[1], old dir.decrypt files[2], yours # flush our io mine.flush old.flush yours.flush # TODO - could also use diff3(1) here: # # "diff3 -L mine -L old -L yours -m #{mine.path} #{old.path} #{yours.path}" # # but git merge-file's output is more diff3-ish than diff3's, weird. cmd = "git merge-file -L mine -L old -L yours -p %s %s %s 2>/dev/null" % [ Shellwords.escape(mine.path), Shellwords.escape(old.path), Shellwords.escape(yours.path) ] # run our merge diff = `#{cmd}` conflict = !$?.success? ensure # close and unlink our tempfiles mine.close! old.close! yours.close! end # make a new stringio object out of our diff output io = StringIO.new diff # create a new tempfile to write our merged file to t = Tempfile.open('merged') begin # encrypt our diff3 output dir.encrypt t, io t.flush # and copy that file back to OLDFILE (aka files[1]) since that's where # git expects to find it FileUtils.copy t.path, files[1] ensure t.close! end # this is important - this exit value is what git uses to decide if there # is a conflict or not exit (conflict ? 1 : 0) rescue GPGME::Error::BadPassphrase => e fail e.message end
rm(file=nil)
click to toggle source
# File lib/dotgpg/cli.rb, line 67 def rm(file=nil) return if helped?(file.nil?) dir = Dotgpg::Dir.closest fail "not in a dotgpg directory" unless dir key = read_key_file_for_rm(file) fail "#{file}: not a valid GPG key" if !key && !options[:force] if key if GPGME::Key.find(:secret).include?(key) && !options[:force] fail "#{file}: refusing to remove your own secret key" end info "Removing #{key.name} from #{dir.path}" info "D #{dir.key_path(key).relative_path_from(dir.path)}" dir.remove_key(key) end rescue GPGME::Error::BadPassphrase => e fail e.message end
unsafe_cat(*files)
click to toggle source
# File lib/dotgpg/cli.rb, line 104 def unsafe_cat(*files) return if helped? files.each do |f| $stdout.puts Dotgpg.decrypt_without_validating_signatures File.open f end end
Private Instance Methods
fail(msg)
click to toggle source
Fail with a message.
In interactive mode, exits the program with status 1. Otherwise raises a Dotgpg::Failure
.
@param [String] msg
# File lib/dotgpg/cli.rb, line 271 def fail(msg) if Dotgpg.interactive? $stderr.puts msg exit 1 else raise Dotgpg::Failure, msg, caller[1] end end
helped?(force=false)
click to toggle source
If the global –help or -h flag is passed, show help.
Should be invoked at the start of every command.
@param [Boolean] force force showing help @return [Boolean] help was shown
# File lib/dotgpg/cli.rb, line 249 def helped?(force=false) if options[:help] || force invoke :help, @_invocations[self.class] true end end
info(msg)
click to toggle source
Print an informational message in interactive mode.
@param [String] msg The message to show
# File lib/dotgpg/cli.rb, line 259 def info(msg) if Dotgpg.interactive? $stdout.puts msg end end
read_key_file_for_add(file)
click to toggle source
Read a key from a given file or stdin.
@param [nil, String] the file the user specified. @return [nil, GPGME::Key]
# File lib/dotgpg/cli.rb, line 284 def read_key_file_for_add(file) if file.nil? if $stdin.tty? info "Paste a public key, then hit <ctrl+d> twice." key = Dotgpg::Key.read($stdin) else key = Dotgpg::Key.read($stdin) $stdin.reopen "/dev/tty" end elsif File.readable?(file) key = Dotgpg::Key.read(File.read(file)) end end
read_key_file_for_rm(file)
click to toggle source
Read a key from a given file or from the .gpg directory
@param [String] the file the user specified @return [nil, GPGME::Key]
# File lib/dotgpg/cli.rb, line 302 def read_key_file_for_rm(file) if !File.exist?(file) && File.exist?(".gpg/" + file) file = ".gpg/" + file end if File.readable?(file) Dotgpg::Key.read(File.read(file)) end end