+
+ def EncryptedFile.targets(text)
+ metaline = text.split("\n").first
+ m = /^access: (.*)/.match metaline
+ return [] unless m
+ return m[1].strip.split(/[\t ,]+/)
+ end
+
+
+ def initialize(filename, new=false)
+ @groupconfig = GroupConfig.new
+ @new = new
+ if @new
+ @readers = []
+ end
+
+ @filename = filename
+ unless FileTest.readable?(filename)
+ @accessible = false
+ return
+ end
+ @accessible = true
+ @encrypted_content = File.read(filename)
+ (outtxt, stderrtxt, statustxt) = GnuPG.gpgcall(@encrypted_content, %w{--with-colons --no-options --no-default-keyring --secret-keyring=/dev/null --keyring=/dev/null})
+ @encrypted = !(statustxt =~ /\[GNUPG:\] NODATA/)
+ if @encrypted
+ @readers = EncryptedFile.list_readers(statustxt)
+ @readable = EncryptedFile.determine_readable(@readers)
+ end
+ end
+
+ def decrypt
+ (outtxt, stderrtxt, statustxt, exitstatus) = GnuPG.gpgcall(@encrypted_content, %w{--decrypt})
+ if !@new and exitstatus != 0
+ proceed = read_input("Warning: gpg returned non-zero exit status #{exitstatus} when decrypting #{@filename}. Proceed?", false)
+ exit(0) unless proceed
+ elsif !@new and outtxt.length == 0
+ proceed = read_input("Warning: #{@filename} decrypted to an empty file. Proceed?")
+ exit(0) unless proceed
+ end
+
+ return outtxt
+ end
+
+ def encrypt(content, recipients)
+ args = recipients.collect{ |r| "--recipient=#{r}"}
+ args.push "--trust-model=always"
+ args.push "--keyring=./.keyring" if FileTest.exists?(".keyring")
+ args.push "--armor"
+ args.push "--encrypt"
+ (outtxt, stderrtxt, statustxt, exitstatus) = GnuPG.gpgcall(content, args)
+
+ invalid = []
+ statustxt.split("\n").each do |line|
+ m = /^\[GNUPG:\] INV_RECP \S+ ([0-9A-F]+)/.match line
+ next unless m
+ invalid.push m[1]
+ end
+ if invalid.size > 0
+ again = read_input("Warning: the following recipients are invalid: #{invalid.join(", ")}. Try again (or proceed)?")
+ return false if again
+ end
+ if outtxt.length == 0
+ tryagain = read_input("Error: #{@filename} encrypted to an empty file. Edit again (or exit)?")
+ return false if tryagain
+ exit(0)
+ end
+ if exitstatus != 0
+ proceed = read_input("Warning: gpg returned non-zero exit status #{exitstatus} when encrypting #{@filename}. Said:\n#{stderrtxt}\n#{statustxt}\n\nProceed (or try again)?")
+ return false unless proceed
+ end
+
+ return true, outtxt
+ end
+
+
+ def determine_encryption_targets(content)
+ targets = EncryptedFile.targets(content)
+ if targets.size == 0
+ tryagain = read_input("Warning: Did not find targets to encrypt to in header. Try again (or exit)?", true)
+ return false if tryagain
+ exit(0)
+ end
+
+ ok, expanded = @groupconfig.expand_targets(targets)
+ if (expanded.size == 0)
+ tryagain = read_input("Errors in access header. Edit again (or exit)?", true)
+ return false if tryagain
+ exit(0)
+ elsif (not ok)
+ tryagain = read_input("Warnings in access header. Edit again (or continue)?", true)
+ return false if tryagain
+ end
+
+ to_me = false
+ GnuPG.get_my_fprs.each do |fpr|
+ if expanded.include?(fpr)
+ to_me = true
+ break
+ end
+ end
+ unless to_me
+ tryagain = read_input("File is not being encrypted to you. Edit again (or continue)?", true)
+ return false if tryagain
+ end
+
+ return true, expanded
+ end
+
+ def write_back(content, targets)
+ ok, encrypted = encrypt(content, targets)
+ return false unless ok
+
+ File.open(@filename,"w").write(encrypted)
+ return true
+ end