From 641f8168bc9084b808620ec00add1355e2d9d1fb Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 26 Mar 2015 09:42:36 +0100 Subject: Validate form content after manual edition. --- cli/icevault | 67 +++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/cli/icevault b/cli/icevault index ef186d0..73136d1 100755 --- a/cli/icevault +++ b/cli/icevault @@ -343,7 +343,9 @@ sub getIdentityFile($) { return $1; } -# Decrypt the given identity file and return the YAML-parsed form. +# Decrypt the given identity file. In scalar context, return the +# YAML-parsed form; in void context, must be given a file handle (closed +# afterwards) where to dump the (unparsed) decrypted content. open my $NULL, '<', '/dev/null'; sub loadIdentityFile($;$) { my ($filename, $fh) = @_; @@ -366,7 +368,10 @@ sub loadIdentityFile($;$) { return YAML::Tiny::Load(decode_utf8 $str) if defined wantarray; } -# Dump and encrypt a form into the given filename. +# Encrypt a form into the given filename. If $form is a HASH +# reference, its YAML-formatted (and UTF8-encoded) content is encrypted; +# if $form is a GLOB reference, the file handle is duped, given as input +# to gpg(1), and closed afterwards. sub saveIdentityFile($$) { my ($form, $filename) = @_; myprintf \*STDERR, "Saving identity file C<%s>", $filename if $CONFIG{debug}; @@ -375,23 +380,23 @@ sub saveIdentityFile($$) { require 'File/Path.pm'; require 'File/Temp.pm'; require 'IPC/Open2.pm'; - require 'YAML/Tiny.pm' if ref $form; # XXX use Tiny::YAML instead? + + if (ref $form eq 'HASH') { + require 'YAML/Tiny.pm'; # XXX use Tiny::YAML instead? + $form->{fields} = [ grep defined, @{$form->{fields}} ]; # remove undefined fields + $form = encode_utf8(YAML::Tiny::Dump($form)); # dump the form as UTF8 + } # don't encrypt directly into the destination file so we don't # end up with a messed up file if something goes wrong + my $infh = "<&".fileno($form) if ref $form eq 'GLOB'; my $outfh = File::Temp->new(SUFFIX => '.gpg', UNLINK => 0, TMPDIR => 1) or die; - my $pid = IPC::Open2::open2( ">&".$outfh->fileno - , (ref $form ? my $infh : "<&".fileno($NULL)) + my $pid = IPC::Open2::open2( ">&".$outfh->fileno, $infh , $CONFIG{gpg}, qw/-o - --no-encrypt-to --recipient/, $CONFIG{keyid} - , '--encrypt', '--', (ref $form ? () : $form) - ) + , '--encrypt' ) or error "Can't fork: %s", $!; - - if (ref $form) { - $form->{fields} = [ grep defined, @{$form->{fields}} ]; # remove undefined fields - print $infh encode_utf8(YAML::Tiny::Dump($form)); # dump the form as UTF8 - close $infh; - } + print $infh $form unless ref $form; + close $infh; waitpid $pid, 0; error "C<%s> exited with value %d", $CONFIG{gpg}, ($? >> 8) if $? and $? != -1; $outfh->close; @@ -849,15 +854,35 @@ elsif ($command eq 'edit') { loadIdentityFile $filename, $fh; my $h = sha256_file $fh->filename; - system $EDITOR, $fh->filename; - error "C<%s> exited with value %d", $EDITOR, ($? >> 8) if $? and $? != -1; + while (1) { + system $EDITOR, $fh->filename; + error "C<%s> exited with value %d", $EDITOR, ($? >> 8) if $? and $? != -1; + my $h2 = sha256_file $fh->filename; + + my $fh2; + unless ($h eq $h2) { + require 'YAML/Tiny.pm'; # XXX use Tiny::YAML instead? + eval { my $str = YAML::Tiny::LoadFile($fh->filename) }; + if ($@ eq '') { + open $fh2, '<', $fh->filename or error "Can't open C<%s>: %s", $fh->filename, $!; + } else { + print STDERR $@; + my $r = promptYN "Not a valid YAML file! Reedit?", 1; + next if $r; + } + } - if ($h eq sha256_file $fh->filename) { - print "No modification made\n"; - } - else { - myprintf "Saving user changes for identity C<%s>", $id; - saveIdentityFile($fh->filename, $filename); + unlink $fh->filename or error "Can't unlink C<%s>: %s", $fh->filename, $!; + if ($h eq $h2) { + print "No modification made\n"; + } elsif (defined $fh2) { + myprintf "Saving user changes for identity C<%s>", $id; + saveIdentityFile($fh2, $filename); # use the FH we opened before unlinking + } else { + print "Aborting\n"; + exit 1; + } + last; } } -- cgit v1.2.3