[Pgp-tools-commit] r782 - trunk/caff
Guilhem Moulin
guilhem-guest at moszumanska.debian.org
Fri Feb 20 19:36:30 UTC 2015
Author: guilhem-guest
Date: 2015-02-20 19:36:30 +0000 (Fri, 20 Feb 2015)
New Revision: 782
Modified:
trunk/caff/caff
Log:
caff: Refactor readwrite_gpg.
Modified: trunk/caff/caff
===================================================================
--- trunk/caff/caff 2015-02-20 19:36:24 UTC (rev 781)
+++ trunk/caff/caff 2015-02-20 19:36:30 UTC (rev 782)
@@ -484,22 +484,22 @@
$CONFIG{'gpg'} = $ENV{GNUPGBIN} // 'gpg';
my $gpg = mkGnuPG( extra_args => ['--with-colons'] );
- my $handles = mkGnuPG_fds ( stdout => undef, status => undef );
+ my $handles = mkGnuPG_fds ( stdout => undef );
my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $gecos ]);
- my ($stdout, $stderr, $status) = readwrite_gpg('', $handles);
+ my %output = readwrite_gpg($handles);
done_gpg($pid, $handles);
- if ($stdout eq '') {
+ if ($output{stdout} eq '') {
mywarn "No data from gpg for list-key"; # There should be at least 'tru:' everywhere.
};
- @keys = ($stdout =~ /^pub:[^r:]*:(?:[^:]*:){2}([0-9A-F]{16}):/mg);
+ @keys = ($output{stdout} =~ /^pub:[^r:]*:(?:[^:]*:){2}([0-9A-F]{16}):/mg);
unless (scalar @keys) {
info("Error: No keys were found using \"gpg --list-public-keys '$gecos'\".");
@keys = qw{0123456789abcdef 89abcdef76543210};
$Ckeys = '#';
}
- ($email) = ($stdout =~ /^uid:(?:[^:]*:){8}[^:]+ <([^:]+\@[^:]+)>(?::.*)?$/m);
+ ($email) = ($output{stdout} =~ /^uid:(?:[^:]*:){8}[^:]+ <([^:]+\@[^:]+)>(?::.*)?$/m);
unless (defined $email) {
info("Error: No email address was found using \"gpg --list-public-keys '$gecos'\".");
$email = $ENV{'LOGNAME'}.'@'.$hostname;
@@ -664,104 +664,91 @@
}
}
-sub readwrite_gpg($$%) {
- my ($in, $handles, %options) = @_;
- my ($inputfd, $stdoutfd, $stderrfd, $statusfd) = @$handles{qw/stdin stdout stderr status/};
- trace("Entering readwrite_gpg.");
+# Send some data on GnuPG handles, and retrieve output from all handles
+# at once using select(2) syscalls. Stop when some output matches a
+# given regex, or when there nothing more to read or write. A newline
+# '\n' character is automatically appended to the text to be send to the
+# 'command' handle; the prefix "[GNUPG:] " to the 'status' handle is
+# added as well.
+sub readwrite_gpg($%) {
+ my $handles = shift;
+ my %opts = @_;
- my ($first_line, undef) = split /\n/, $in;
- debug("readwrite_gpg sends ".(defined $first_line ? $first_line : "<nothing>"));
+ # ignore direct and dup handles
+ my @infhs = grep {defined $opts{$_} and !$handles->options($_)->{direct} and $handles->{$_} !~ /^[<>]&/} qw/stdin passphrase command/;
+ my @outfhs = grep {defined $handles->{$_} and !$handles->options($_)->{direct} and $handles->{$_} !~ /^[<>]&/} qw/stdout stderr status logger/;
+ my %fh = reverse %$handles{@infhs, @outfhs};
- local $/ = undef;
- my $sout = IO::Select->new();
- my $sin = IO::Select->new();
- my $offset = 0;
+ my %offset = map {$_ => 0} @infhs;
+ my %output = map {$_ => ''} @outfhs;
- trace("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".($statusfd // 'undef').".");
+ if (defined $opts{command}) {
+ # automatically send the command
+ chomp $opts{command};
+ $opts{command} .= "\n";
+ }
+ $opts{status} = qr/^\[GNUPG:\] $opts{status}$/m if defined $opts{status};
- $inputfd->blocking(0);
- $stdoutfd->blocking(0);
- $statusfd->blocking(0) if defined $statusfd;
- $stderrfd->blocking(0);
- $sout->add($stdoutfd);
- $sout->add($stderrfd);
- $sout->add($statusfd) if defined $statusfd;
- $sin->add($inputfd);
+ $handles->{$_}->blocking(0) foreach (@infhs, @outfhs);
+ my $sin = IO::Select::->new(map {$handles->{$_}} @infhs);
+ my $sout = IO::Select::->new(map {$handles->{$_}} @outfhs);
- my ($stdout, $stderr, $status) = ("", "", "");
- my $exitwhenstatusmatches = $options{'exitwhenstatusmatches'};
- trace("doing stuff until we find $exitwhenstatusmatches") if defined $exitwhenstatusmatches;
+ trace("entering readwrite_gpg.");
+ trace("doing stuff until one of: ". join(', ', map {"$_ =~ $opts{$_}"} grep {defined $opts{$_}} @outfhs))
+ if grep {defined $opts{$_}} @outfhs;
my $readwrote_stuff_this_time = 0;
my $do_not_wait_on_select = 0;
- my ($readyr, $readyw, $written);
- while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) {
- if (defined $exitwhenstatusmatches and $status =~ /^\[GNUPG:\] $exitwhenstatusmatches$/m) {
- trace("readwrite_gpg found match on $exitwhenstatusmatches");
+ while ($sin->count() + $sout->count() > 0) {
+ if (!$sin->count() and grep {defined $opts{$_} and $output{$_} =~ $opts{$_}} @outfhs) {
if ($readwrote_stuff_this_time) {
- trace("read/write some more\n");
+ trace("read/write some more.");
$do_not_wait_on_select = 1;
} else {
- trace("that's it in our while loop.\n");
+ trace("that's it in our while loop.");
last;
}
};
+ trace("select waiting for ".($sin->count()+$sout->count())." fds.");
+ my ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, $do_not_wait_on_select ? 0 : 1);
+ trace("ready: write: ". join (',', map {$fh{$_}} @{$readyw // []}).
+ "; read: ". join (',', map {$fh{$_}} @{$readyr // []}));
$readwrote_stuff_this_time = 0;
- trace("select waiting for ".($sout->count())." fds.");
- ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, $do_not_wait_on_select ? 0 : 1);
- trace("ready: write: ".(defined $readyw ? scalar @$readyw : 0 )."; read: ".(defined $readyr ? scalar @$readyr : 0));
- for my $wfd (@$readyw) {
+
+ for my $fd (@{$readyw // []}) {
$readwrote_stuff_this_time = 1;
- if (length($in) != $offset) {
- trace("writing to $wfd.");
- $written = $wfd->syswrite($in, length($in) - $offset, $offset);
- $offset += $written;
- };
- if ($offset == length($in)) {
- trace("writing to $wfd done.");
- unless ($options{'nocloseinput'}) {
- close $wfd;
- trace("$wfd closed.");
- };
- $sin->remove($wfd);
- $sin = undef;
+ my $fh = $fh{$fd};
+ if ($offset{$fh} != length $opts{$fh}) {
+ trace ("writing to '$fh'". ($offset{$fh} ? "" : ": ".(split /\n/, $opts{$fh}, 2)[0]));
+ my $written = $fd->syswrite($opts{$fh}, length($opts{$fh}) - $offset{$fh}, $offset{$fh});
+ $offset{$fh} += $written;
}
+ if ($offset{$fh} == length $opts{$fh}) {
+ trace "done writing to '$fh'.";
+ $sin->remove($fd);
+ $fd->close && trace "closed '$fh'." if $opts{autoclose};
+ }
}
-
- next unless ($readyr); # Wait some more.
-
- for my $rfd (@$readyr) {
+ for my $fd (@{$readyr // []}) {
$readwrote_stuff_this_time = 1;
- if ($rfd->eof) {
- trace("reading from $rfd done.");
- $sout->remove($rfd);
- close($rfd);
+ my $fh = $fh{$fd};
+ if ($fd->eof) {
+ trace "done reading from '$fh'.";
+ $sout->remove($fd);
next;
}
- trace("reading from $rfd.");
- if ($rfd == $stdoutfd) {
- $stdout .= <$rfd>;
- trace2("stdout is now $stdout\n================");
- next;
- }
- if (defined $statusfd && $rfd == $statusfd) {
- $status .= <$rfd>;
- trace2("status is now $status\n================");
- next;
- }
- if ($rfd == $stderrfd) {
- $stderr .= <$rfd>;
- trace2("stderr is now $stderr\n================");
- next;
- }
+ trace "reading from '$fh'.";
+ $output{$fh} .= do { local $/; <$fd> };
+ trace2 "$fh is now:\n$output{$fh}\n================";
}
}
trace("readwrite_gpg done.");
- return ($stdout, $stderr, $status);
-};
+ return %output;
+}
+
sub ask($$;$$) {
my ($question, $default, $forceyes, $forceno) = @_;
my $answer;
@@ -895,17 +882,17 @@
};
if ($can_encrypt) {
- my $message = $message_entity->stringify();
my $gpg = mkGnuPG( homedir => $GNUPGHOME, armor => 1, textmode => 1 );
$gpg->options->push_recipients($key_id);
$gpg->options->push_recipients(@{$CONFIG{'also-encrypt-to'}}) if defined $CONFIG{'also-encrypt-to'};
my $handles = mkGnuPG_fds( stdin => undef, stdout => undef, status => undef );
my $pid = $gpg->encrypt(handles => $handles);
- my ($stdout, $status) = readwrite_gpg($message, $handles);
+ my %output = readwrite_gpg($handles, stdin => $message_entity->stringify(), autoclose => 1);
done_gpg($pid, $handles);
- if ($stdout eq '') {
- if (($status =~ /^\[GNUPG:\] INV_RECP ([0-9]+) ([0-9A-F]+)$/m) and
- (defined $CONFIG{'also-encrypt-to'})) {
+ my ($message, $status) = @output{qw/stdout status/};
+
+ if ($message eq '') {
+ if ($status =~ /^\[GNUPG:\] INV_RECP ([0-9]+) ([0-9A-F]+)$/m and defined $CONFIG{'also-encrypt-to'}) {
my $reason = $1;
my $keyid = $2;
if (grep { $_ eq $keyid } @{$CONFIG{'also-encrypt-to'}}) {
@@ -916,10 +903,9 @@
return;
};
};
- mywarn "No data from gpg for encrypting mail. STDERR was:\n$stderr\nstatus output was:\n$status\n";
+ mywarn "No data from gpg for encrypting mail. status output was:\n$status";
return;
};
- $message = $stdout;
$message_entity = MIME::Entity->build(
Type => 'multipart/encrypted; protocol="application/pgp-encrypted"',
@@ -995,21 +981,20 @@
sub delete_signatures($$$$) {
my ($handles, $longkeyid, $uid, $keyids) = @_;
- readwrite_gpg("uid 0\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # unmark all uids from delsig
- readwrite_gpg("uid $uid\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # mark $uid for delsig
+ readwrite_gpg($handles, command => "uid 0", status => $KEYEDIT_PROMPT); # unmark all uids from delsig
+ readwrite_gpg($handles, command => "uid $uid", status => $KEYEDIT_PROMPT); # mark $uid for delsig
my $last_signed_on = 0;
my %signers;
- my ($stdout, $stderr, $status) =
- readwrite_gpg("delsig\n", $handles, exitwhenstatusmatches => $KEYEDIT_DELSIG_PROMPT, nocloseinput => 1);
+ my %output = readwrite_gpg($handles, command => "delsig", status => $KEYEDIT_DELSIG_PROMPT);
- while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) {
+ while($output{status} =~ /$KEYEDIT_DELSIG_PROMPT/m) {
# sig:?::17:EA2199412477CAF8:1058095214:::::13x
- my @sigline = grep /^sig:/, (split /\n/, $stdout);
+ my @sigline = grep /^sig:/, (split /\n/, $output{stdout});
my $answer = "no";
if (!@sigline) {
- debug("[sigremoval] no sig line here, only got:\n".$stdout);
+ debug("[sigremoval] no sig line here, only got:\n".$output{stdout});
}
else { # only if we found a sig here - we never remove revocation packets for instance
my $sig = pop @sigline;
@@ -1028,10 +1013,9 @@
debug("[sigremoval] not interested in that sig ($1).");
$answer = "yes";
};
- mywarn("I hit a bug, please report. Found the following ".($#sigline+2)." siglines in that part of the dialog:\n".$stdout) if @sigline;
+ mywarn("I hit a bug, please report. Found the following ".($#sigline+2)." siglines in that part of the dialog:\n".$output{stdout}) if @sigline;
}
- ($stdout, $stderr, $status) =
- readwrite_gpg($answer."\n", $handles, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1);
+ %output = readwrite_gpg($handles, command => $answer, status => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT);
};
return ($last_signed_on, \%signers);
@@ -1588,7 +1572,7 @@
handles => $handles );
debug("Starting edit session");
- my ($stdout, $stderr, $status) = readwrite_gpg('', $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ my %output = readwrite_gpg($handles, status => $KEYEDIT_PROMPT);
# delete other uids
###################
@@ -1599,14 +1583,14 @@
next if $uid->{type} ne 'uid' and $uids[$i-1]->{hash} eq $first_uid->{hash}; # keep the first UID
debug("Marking UID $i ($uids[$i-1]->{hash}) for deletion.");
- readwrite_gpg("uid $i\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ readwrite_gpg($handles, command => "uid $i", status => $KEYEDIT_PROMPT);
$delete_some++;
}
if ($delete_some) {
debug("Need to delete $delete_some uids.");
- readwrite_gpg("deluid\n", $handles, exitwhenstatusmatches => $KEYEDIT_DELUID_PROMPT, nocloseinput => 1);
- readwrite_gpg("yes\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ readwrite_gpg($handles, command => "deluid", status => $KEYEDIT_DELUID_PROMPT);
+ readwrite_gpg($handles, command => "yes", status => $KEYEDIT_PROMPT);
};
# delete all subkeys
@@ -1614,10 +1598,10 @@
if (@{$KEYS{$keyid}->{subkeys}}) {
for (my $i = 1; $i <= $#{$KEYS{$keyid}->{subkeys}} + 1; $i++) {
debug("Marking subkey $i ($KEYS{$keyid}->{subkeys}->[$i-1]) for deletion.");
- readwrite_gpg("key $i\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ readwrite_gpg($handles, command => "key $i", status => $KEYEDIT_PROMPT);
};
- readwrite_gpg("delkey\n", $handles, exitwhenstatusmatches => $KEYEDIT_DELSUBKEY_PROMPT, nocloseinput => 1);
- readwrite_gpg("yes\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ readwrite_gpg($handles, command => "delkey", status => $KEYEDIT_DELSUBKEY_PROMPT);
+ readwrite_gpg($handles, command => "yes", status => $KEYEDIT_PROMPT);
};
# delete signatures
@@ -1629,7 +1613,7 @@
if $uid->{type} ne 'uid'; # delete all sigs on the first UID if $uid is an attribute
- readwrite_gpg("save\n", $handles);
+ readwrite_gpg($handles, command => "save");
done_gpg($pid, $handles);
my $asciikey = export_keys($uiddir, [$keyid]);
@@ -1708,23 +1692,22 @@
handles => $handles );
debug("Starting edit session on $keyid, signer $u");
- readwrite_gpg('', $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ readwrite_gpg($handles, status => $KEYEDIT_PROMPT);
foreach my $level (0..3) {
my @signeduids_with_level = grep {$_->{signers}->{$u} eq $level} @signeduids;
next unless @signeduids_with_level;
info("lsign-ing (by $u) with cert level $level uid(s) #".(join ',', sort (map {$_->{serial}} @signeduids_with_level))." of $longkeyid.");
- readwrite_gpg("uid 0\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
- readwrite_gpg("uid $_->{hash}\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1)
- for @signeduids_with_level;
- my ($stdout, $stderr, $status) = readwrite_gpg("lsign\n", $handles, exitwhenstatusmatches => qr/$KEYEDIT_SIGNUID_CLASS_PROMPT|$KEYEDIT_PROMPT/, nocloseinput => 1);
- next if $status =~ /^\[GNUPG:\] $KEYEDIT_PROMPT/m; # already signed
- readwrite_gpg("$level\n", $handles, exitwhenstatusmatches => $KEYEDIT_SIGNUID_PROMPT, nocloseinput => 1);
- readwrite_gpg("yes\n", $handles, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1);
+ readwrite_gpg($handles, command => "uid 0", status => $KEYEDIT_PROMPT);
+ readwrite_gpg($handles, command => "uid $_->{hash}", status => $KEYEDIT_PROMPT) for @signeduids_with_level;
+ my %output = readwrite_gpg($handles, command => "lsign", statusmatches => qr/$KEYEDIT_SIGNUID_CLASS_PROMPT|$KEYEDIT_PROMPT/);
+ next if $output{status} =~ /^\[GNUPG:\] $KEYEDIT_PROMPT/m; # already signed
+ readwrite_gpg($handles, command => $level, status => $KEYEDIT_SIGNUID_PROMPT);
+ readwrite_gpg($handles, command => "yes", status => $KEYEDIT_PROMPT);
}
- readwrite_gpg("save\n", $handles);
+ readwrite_gpg($handles, command => "save");
done_gpg($pid, $handles);
myerror(1, "Couldn't auto lsign $keyid: $?.") if $?;
}
More information about the Pgp-tools-commit
mailing list