[Rt-commit] rt branch, 4.2/gnupg-refactor, created. rt-4.0.4-245-gcd978dd
Kevin Falcone
falcone at bestpractical.com
Fri Sep 28 15:23:08 EDT 2012
The branch, 4.2/gnupg-refactor has been created
at cd978dd12587eddf319d0cc53f50650f687b4145 (commit)
- Log -----------------------------------------------------------------
commit 47a9cb5ba7de2ac50a0b94a89c85cf00c2864408
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Aug 31 14:31:52 2012 -0400
Refactor code which calls GPG::Interface to elimate duplicate code
Many of the functions in RT::Crypt::GnuPG contain nearly-duplicate
swaths of code. Refactor these into a common CallGnuPG method, which
deals with setting up IO handles, printing content, trapping errors,
reading output, and returning results in the form of a data structure.
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index d252504..1b08649 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -364,6 +364,107 @@ our $RE_FILE_EXTENSIONS = qr/pgp|asc/i;
# ...
# );
+=head2 CallGnuPG
+
+=cut
+
+sub CallGnuPG {
+ my %args = (
+ Options => undef,
+ Key => undef,
+ Recipients => [],
+ Passphrase => undef,
+
+ Method => undef,
+ CommandArgs => [],
+
+ Content => undef,
+ Handles => {},
+ Direct => undef,
+ Output => undef,
+ @_
+ );
+
+ my %handle = %{$args{Handles}};
+ my ($handles, $handle_list) = _make_gpg_handles( %handle );
+ $handles->options( $_ )->{'direct'} = 1
+ for @{$args{Direct} || [keys %handle] };
+ %handle = %$handle_list;
+
+ my $content = $args{Content};
+ my $method = $args{Method};
+
+ my %GnuPGOptions = RT->Config->Get('GnuPGOptions');
+ my %opt = (
+ 'digest-algo' => 'SHA1',
+ %GnuPGOptions,
+ %{ $args{Options} || {} },
+ );
+ my $gnupg = GnuPG::Interface->new;
+ $gnupg->options->hash_init(
+ _PrepareGnuPGOptions( %opt ),
+ );
+ $gnupg->options->armor( 1 );
+ $gnupg->options->meta_interactive( 0 );
+ $gnupg->options->default_key( $args{Key} );
+
+ my %seen;
+ $gnupg->options->push_recipients( $_ ) for
+ map { UseKeyForEncryption($_) || $_ }
+ grep { !$seen{ $_ }++ }
+ @{ $args{Recipients} || [] };
+
+ $args{Passphrase} = $GnuPGOptions{passphrase}
+ unless defined $args{'Passphrase'};
+ $args{Passphrase} = GetPassphrase( Address => $args{Key} )
+ unless defined $args{'Passphrase'};
+ $gnupg->passphrase( $args{'Passphrase'} );
+
+ eval {
+ local $SIG{'CHLD'} = 'DEFAULT';
+ my $pid = safe_run_child {
+ $gnupg->$method(
+ handles => $handles,
+ command_args => $args{CommandArgs},
+ )
+ };
+ {
+ local $SIG{'PIPE'} = 'IGNORE';
+ if (Scalar::Util::blessed($content) and $content->can("print")) {
+ $content->print( $handle{'stdin'} );
+ } elsif (ref($content) eq "SCALAR") {
+ $handle{'stdin'}->print( ${ $content } );
+ } elsif (defined $content) {
+ $handle{'stdin'}->print( $content );
+ }
+ close $handle{'stdin'} or die "Can't close gnupg handle: $!";
+ }
+ waitpid $pid, 0;
+ };
+ my $err = $@;
+ if ($args{Output}) {
+ push @{$args{Output}}, readline $handle{stdout};
+ close $handle{stdout};
+ }
+
+ my %res;
+ $res{'exit_code'} = $?;
+
+ foreach ( qw(stderr logger status) ) {
+ $res{$_} = do { local $/ = undef; readline $handle{$_} };
+ delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
+ close $handle{$_};
+ }
+ $RT::Logger->debug( $res{'status'} ) if $res{'status'};
+ $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
+ $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
+ if ( $err || $res{'exit_code'} ) {
+ $res{'message'} = $err? $err : "gpg exited with error code ". ($res{'exit_code'} >> 8);
+ }
+
+ return %res;
+}
+
=head2 SignEncrypt Entity => MIME::Entity, [ Encrypt => 1, Sign => 1, ... ]
Signs and/or encrypts an email message with GnuPG utility.
@@ -435,28 +536,7 @@ sub SignEncryptRFC3156 {
@_
);
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnuPGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined $args{'Passphrase'};
-
- $opt{'digest-algo'} ||= 'SHA1';
- $opt{'default_key'} = $args{'Signer'}
- if $args{'Sign'} && $args{'Signer'};
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- armor => 1,
- meta_interactive => 0,
- );
-
my $entity = $args{'Entity'};
-
- if ( $args{'Sign'} && !defined $args{'Passphrase'} ) {
- $args{'Passphrase'} = GetPassphrase( Address => $args{'Signer'} );
- }
-
my %res;
if ( $args{'Sign'} && !$args{'Encrypt'} ) {
# required by RFC3156(Ch. 5) and RFC1847(Ch. 2.1)
@@ -468,46 +548,26 @@ sub SignEncryptRFC3156 {
);
}
}
-
- my ($handles, $handle_list) = _make_gpg_handles(stdin =>IO::Handle::CRLF->new );
- my %handle = %$handle_list;
-
- $gnupg->passphrase( $args{'Passphrase'} );
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $gnupg->detach_sign( handles => $handles ) };
- $entity->make_multipart( 'mixed', Force => 1 );
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $entity->parts(0)->print( $handle{'stdin'} );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
- my $err = $@;
- my @signature = readline $handle{'stdout'};
- close $handle{'stdout'};
-
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- return %res;
- }
+ $entity->make_multipart( 'mixed', Force => 1 );
+
+ my @signature;
+ %res = CallGnuPG(
+ Key => $args{'Signer'},
+ Method => "detach_sign",
+ Handles => { stdin => IO::Handle::CRLF->new },
+ Direct => [],
+ Passphrase => $args{'Passphrase'},
+ Content => $entity->parts(0),
+ Output => \@signature,
+ );
+ return %res if $res{message};
# setup RFC1847(Ch.2.1) requirements
my $protocol = 'application/pgp-signature';
+ my $algo = RT->Config->Get('GnuPGOptions')->{'digest-algo'} || 'SHA1';
$entity->head->mime_attr( 'Content-Type' => 'multipart/signed' );
$entity->head->mime_attr( 'Content-Type.protocol' => $protocol );
- $entity->head->mime_attr( 'Content-Type.micalg' => 'pgp-'. lc $opt{'digest-algo'} );
+ $entity->head->mime_attr( 'Content-Type.micalg' => 'pgp-'. lc $algo );
$entity->attach(
Type => $protocol,
Disposition => 'inline',
@@ -516,48 +576,23 @@ sub SignEncryptRFC3156 {
);
}
if ( $args{'Encrypt'} ) {
- my %seen;
- $gnupg->options->push_recipients( $_ ) foreach
- map UseKeyForEncryption($_) || $_,
- grep !$seen{ $_ }++, map $_->address,
+ my @recipients = map $_->address,
map Email::Address->parse( $entity->head->get( $_ ) ),
qw(To Cc Bcc);
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(stdout => $tmp_fh);
- my %handle = %$handle_list;
- $handles->options( 'stdout' )->{'direct'} = 1;
- $gnupg->passphrase( $args{'Passphrase'} ) if $args{'Sign'};
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $args{'Sign'}
- ? $gnupg->sign_and_encrypt( handles => $handles )
- : $gnupg->encrypt( handles => $handles ) };
- $entity->make_multipart( 'mixed', Force => 1 );
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $entity->parts(0)->print( $handle{'stdin'} );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
-
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exited with error code ". ($? >> 8);
- return %res;
- }
+ $entity->make_multipart( 'mixed', Force => 1 );
+ %res = CallGnuPG(
+ Key => $args{'Signer'},
+ Recipients => \@recipients,
+ Method => ( $args{'Sign'} ? "sign_and_encrypt" : "encrypt" ),
+ Handles => { stdout => $tmp_fh },
+ Passphrase => $args{'Passphrase'},
+ Content => $entity->parts(0),
+ );
+ return %res if $res{message};
my $protocol = 'application/pgp-encrypted';
$entity->parts([]);
@@ -617,72 +652,23 @@ sub _SignEncryptTextInline {
);
return unless $args{'Sign'} || $args{'Encrypt'};
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnupGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $opt{'default_key'} = $args{'Signer'}
- if $args{'Sign'} && $args{'Signer'};
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- armor => 1,
- meta_interactive => 0,
- );
-
- if ( $args{'Sign'} && !defined $args{'Passphrase'} ) {
- $args{'Passphrase'} = GetPassphrase( Address => $args{'Signer'} );
- }
-
- if ( $args{'Encrypt'} ) {
- $gnupg->options->push_recipients( $_ ) foreach
- map UseKeyForEncryption($_) || $_,
- @{ $args{'Recipients'} || [] };
- }
-
- my %res;
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(stdout => $tmp_fh);
- my %handle = %$handle_list;
-
- $handles->options( 'stdout' )->{'direct'} = 1;
- $gnupg->passphrase( $args{'Passphrase'} ) if $args{'Sign'};
-
my $entity = $args{'Entity'};
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $method = $args{'Sign'} && $args{'Encrypt'}
- ? 'sign_and_encrypt'
- : ($args{'Sign'}? 'clearsign': 'encrypt');
- my $pid = safe_run_child { $gnupg->$method( handles => $handles ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $entity->bodyhandle->print( $handle{'stdin'} );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- my $err = $@;
-
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- return %res;
- }
+ my %res = CallGnuPG(
+ Key => $args{'Signer'},
+ Recipients => $args{'Recipients'},
+ Method => ( $args{'Sign'} && $args{'Encrypt'}
+ ? 'sign_and_encrypt'
+ : ( $args{'Sign'}
+ ? 'clearsign'
+ : 'encrypt' ) ),
+ Handles => { stdout => $tmp_fh },
+ Passphrase => $args{'Passphrase'},
+ Content => $entity->bodyhandle,
+ );
+ return %res if $res{message};
$entity->bodyhandle( MIME::Body::File->new( $tmp_fn) );
$entity->{'__store_tmp_handle_to_avoid_early_cleanup'} = $tmp_fh;
@@ -705,71 +691,25 @@ sub _SignEncryptAttachmentInline {
);
return unless $args{'Sign'} || $args{'Encrypt'};
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnupGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $opt{'default_key'} = $args{'Signer'}
- if $args{'Sign'} && $args{'Signer'};
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- armor => 1,
- meta_interactive => 0,
- );
-
- if ( $args{'Sign'} && !defined $args{'Passphrase'} ) {
- $args{'Passphrase'} = GetPassphrase( Address => $args{'Signer'} );
- }
my $entity = $args{'Entity'};
- if ( $args{'Encrypt'} ) {
- $gnupg->options->push_recipients( $_ ) foreach
- map UseKeyForEncryption($_) || $_,
- @{ $args{'Recipients'} || [] };
- }
-
- my %res;
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(stdout => $tmp_fh);
- my %handle = %$handle_list;
- $handles->options( 'stdout' )->{'direct'} = 1;
- $gnupg->passphrase( $args{'Passphrase'} ) if $args{'Sign'};
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $method = $args{'Sign'} && $args{'Encrypt'}
- ? 'sign_and_encrypt'
- : ($args{'Sign'}? 'detach_sign': 'encrypt');
- my $pid = safe_run_child { $gnupg->$method( handles => $handles ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $entity->bodyhandle->print( $handle{'stdin'} );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- my $err = $@;
-
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- return %res;
- }
+ my %res = CallGnuPG(
+ Key => $args{'Signer'},
+ Recipients => $args{'Recipients'},
+ Method => ( $args{'Sign'} && $args{'Encrypt'}
+ ? 'sign_and_encrypt'
+ : ( $args{'Sign'}
+ ? 'detach_sign'
+ : 'encrypt' ) ),
+ Handles => { stdout => $tmp_fh },
+ Passphrase => $args{'Passphrase'},
+ Content => $entity->bodyhandle,
+ );
+ return %res if $res{message};
my $filename = mime_recommended_filename( $entity ) || 'no_name';
if ( $args{'Sign'} && !$args{'Encrypt'} ) {
@@ -807,70 +747,22 @@ sub SignEncryptContent {
);
return unless $args{'Sign'} || $args{'Encrypt'};
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnupGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $opt{'default_key'} = $args{'Signer'}
- if $args{'Sign'} && $args{'Signer'};
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- armor => 1,
- meta_interactive => 0,
- );
-
- if ( $args{'Sign'} && !defined $args{'Passphrase'} ) {
- $args{'Passphrase'} = GetPassphrase( Address => $args{'Signer'} );
- }
-
- if ( $args{'Encrypt'} ) {
- $gnupg->options->push_recipients( $_ ) foreach
- map UseKeyForEncryption($_) || $_,
- @{ $args{'Recipients'} || [] };
- }
-
- my %res;
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(stdout => $tmp_fh);
- my %handle = %$handle_list;
- $handles->options( 'stdout' )->{'direct'} = 1;
- $gnupg->passphrase( $args{'Passphrase'} ) if $args{'Sign'};
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $method = $args{'Sign'} && $args{'Encrypt'}
- ? 'sign_and_encrypt'
- : ($args{'Sign'}? 'clearsign': 'encrypt');
- my $pid = safe_run_child { $gnupg->$method( handles => $handles ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $handle{'stdin'}->print( ${ $args{'Content'} } );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- my $err = $@;
-
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- return %res;
- }
+ my %res = CallGnuPG(
+ Key => $args{'Signer'},
+ Recipients => $args{'Recipients'},
+ Method => ( $args{'Sign'} && $args{'Encrypt'}
+ ? 'sign_and_encrypt'
+ : ( $args{'Sign'}
+ ? 'clearsign'
+ : 'encrypt' ) ),
+ Handles => { stdout => $tmp_fh },
+ Passphrase => $args{'Passphrase'},
+ Content => $args{'Content'},
+ );
+ return %res if $res{message};
${ $args{'Content'} } = '';
seek $tmp_fh, 0, 0;
@@ -1096,14 +988,6 @@ sub VerifyInline { return DecryptInline( @_ ) }
sub VerifyAttachment {
my %args = ( Data => undef, Signature => undef, Top => undef, @_ );
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $opt{'digest-algo'} ||= 'SHA1';
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
foreach ( $args{'Data'}, $args{'Signature'} ) {
next unless $_->bodyhandle->is_encoded;
@@ -1116,82 +1000,28 @@ sub VerifyAttachment {
$args{'Data'}->bodyhandle->print( $tmp_fh );
$tmp_fh->flush;
- my ($handles, $handle_list) = _make_gpg_handles();
- my %handle = %$handle_list;
-
- my %res;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $gnupg->verify(
- handles => $handles, command_args => [ '-', $tmp_fn ]
- ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $args{'Signature'}->bodyhandle->print( $handle{'stdin'} );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
- }
- return %res;
+ return CallGnuPG(
+ Method => "verify",
+ CommandArgs => [ '-', $tmp_fn ],
+ Passphrase => $args{'Passphrase'},
+ Content => $args{'Signature'}->bodyhandle,
+ );
}
sub VerifyRFC3156 {
my %args = ( Data => undef, Signature => undef, Top => undef, @_ );
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $opt{'digest-algo'} ||= 'SHA1';
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw:eol(CRLF?)';
$args{'Data'}->print( $tmp_fh );
$tmp_fh->flush;
- my ($handles, $handle_list) = _make_gpg_handles();
- my %handle = %$handle_list;
-
- my %res;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $gnupg->verify(
- handles => $handles, command_args => [ '-', $tmp_fn ]
- ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $args{'Signature'}->bodyhandle->print( $handle{'stdin'} );
- close $handle{'stdin'};
- }
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
- }
- return %res;
+ return CallGnuPG(
+ Method => "verify",
+ CommandArgs => [ '-', $tmp_fn ],
+ Passphrase => $args{'Passphrase'},
+ Content => $args{'Signature'}->bodyhandle,
+ );
}
sub DecryptRFC3156 {
@@ -1203,66 +1033,27 @@ sub DecryptRFC3156 {
@_
);
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnupGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
if ( $args{'Data'}->bodyhandle->is_encoded ) {
require RT::EmailParser;
RT::EmailParser->_DecodeBody($args{'Data'});
}
- $args{'Passphrase'} = GetPassphrase()
- unless defined $args{'Passphrase'};
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(stdout => $tmp_fh);
- my %handle = %$handle_list;
- $handles->options( 'stdout' )->{'direct'} = 1;
-
- my %res;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- $gnupg->passphrase( $args{'Passphrase'} );
- my $pid = safe_run_child { $gnupg->decrypt( handles => $handles ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- $args{'Data'}->bodyhandle->print( $handle{'stdin'} );
- close $handle{'stdin'}
- }
-
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
+ my %res = CallGnuPG(
+ Method => "decrypt",
+ Handles => { stdout => $tmp_fh },
+ Passphrase => $args{'Passphrase'},
+ Content => $args{'Data'}->bodyhandle,
+ );
# if the decryption is fine but the signature is bad, then without this
# status check we lose the decrypted text
# XXX: add argument to the function to control this check
- if ( $res{'status'} !~ /DECRYPTION_OKAY/ ) {
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
- return %res;
- }
- }
+ delete $res{'message'} if $res{'status'} =~ /DECRYPTION_OKAY/;
+
+ return %res if $res{message};
seek $tmp_fh, 0, 0;
my $parser = RT::EmailParser->new();
@@ -1281,27 +1072,11 @@ sub DecryptInline {
@_
);
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnuPGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
if ( $args{'Data'}->bodyhandle->is_encoded ) {
require RT::EmailParser;
RT::EmailParser->_DecodeBody($args{'Data'});
}
- $args{'Passphrase'} = GetPassphrase()
- unless defined $args{'Passphrase'};
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
@@ -1327,7 +1102,6 @@ sub DecryptInline {
my ($res_fh, $res_fn);
($res_fh, $res_fn, %res) = _DecryptInlineBlock(
%args,
- GnuPG => $gnupg,
BlockHandle => $block_fh,
);
return %res unless $res_fh;
@@ -1364,7 +1138,6 @@ sub DecryptInline {
my ($res_fh, $res_fn);
($res_fh, $res_fn, %res) = _DecryptInlineBlock(
%args,
- GnuPG => $gnupg,
BlockHandle => $block_fh,
);
return %res unless $res_fh;
@@ -1384,49 +1157,26 @@ sub DecryptInline {
sub _DecryptInlineBlock {
my %args = (
- GnuPG => undef,
BlockHandle => undef,
Passphrase => undef,
@_
);
- my $gnupg = $args{'GnuPG'};
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(
- stdin => $args{'BlockHandle'},
- stdout => $tmp_fh);
- my %handle = %$handle_list;
- $handles->options( 'stdout' )->{'direct'} = 1;
- $handles->options( 'stdin' )->{'direct'} = 1;
-
- my %res;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- $gnupg->passphrase( $args{'Passphrase'} );
- my $pid = safe_run_child { $gnupg->decrypt( handles => $handles ) };
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
+ my %res = CallGnuPG(
+ Method => "decrypt",
+ Handles => { stdout => $tmp_fh, stdin => $args{'BlockHandle'} },
+ Passphrase => $args{'Passphrase'},
+ );
# if the decryption is fine but the signature is bad, then without this
# status check we lose the decrypted text
# XXX: add argument to the function to control this check
- if ( $res{'status'} !~ /DECRYPTION_OKAY/ ) {
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
- return (undef, undef, %res);
- }
- }
+ delete $res{'message'} if $res{'status'} =~ /DECRYPTION_OKAY/;
+
+ return (undef, undef, %res) if $res{message};
seek $tmp_fh, 0, 0;
return ($tmp_fh, $tmp_fn, %res);
@@ -1440,27 +1190,11 @@ sub DecryptAttachment {
@_
);
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnuPGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
if ( $args{'Data'}->bodyhandle->is_encoded ) {
require RT::EmailParser;
RT::EmailParser->_DecodeBody($args{'Data'});
}
- $args{'Passphrase'} = GetPassphrase()
- unless defined $args{'Passphrase'};
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
$args{'Data'}->bodyhandle->print( $tmp_fh );
@@ -1468,7 +1202,6 @@ sub DecryptAttachment {
my ($res_fh, $res_fn, %res) = _DecryptInlineBlock(
%args,
- GnuPG => $gnupg,
BlockHandle => $tmp_fh,
);
return %res unless $res_fh;
@@ -1498,62 +1231,22 @@ sub DecryptContent {
@_
);
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
-
- # handling passphrase in GnupGOptions
- $args{'Passphrase'} = delete $opt{'passphrase'}
- if !defined($args{'Passphrase'});
-
- $opt{'digest-algo'} ||= 'SHA1';
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
- $args{'Passphrase'} = GetPassphrase()
- unless defined $args{'Passphrase'};
-
my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
binmode $tmp_fh, ':raw';
- my ($handles, $handle_list) = _make_gpg_handles(
- stdout => $tmp_fh);
- my %handle = %$handle_list;
- $handles->options( 'stdout' )->{'direct'} = 1;
-
- my %res;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- $gnupg->passphrase( $args{'Passphrase'} );
- my $pid = safe_run_child { $gnupg->decrypt( handles => $handles ) };
- {
- local $SIG{'PIPE'} = 'IGNORE';
- print { $handle{'stdin'} } ${ $args{'Content'} };
- close $handle{'stdin'};
- }
-
- waitpid $pid, 0;
- };
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
+ my %res = CallGnuPG(
+ Method => "decrypt",
+ Handles => { stdout => $tmp_fh },
+ Passphrase => $args{'Passphrase'},
+ Content => $args{'Content'},
+ );
# if the decryption is fine but the signature is bad, then without this
# status check we lose the decrypted text
# XXX: add argument to the function to control this check
- if ( $res{'status'} !~ /DECRYPTION_OKAY/ ) {
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
- return %res;
- }
- }
+ delete $res{'message'} if $res{'status'} =~ /DECRYPTION_OKAY/;
+
+ return %res if $res{'message'};
${ $args{'Content'} } = '';
seek $tmp_fh, 0, 0;
@@ -2086,47 +1779,19 @@ sub GetKeysInfo {
return (exit_code => 0) unless $force;
}
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $opt{'digest-algo'} ||= 'SHA1';
- $opt{'with-colons'} = undef; # parseable format
- $opt{'fingerprint'} = undef; # show fingerprint
- $opt{'fixed-list-mode'} = undef; # don't merge uid with keys
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- armor => 1,
- meta_interactive => 0,
+ my @info;
+ my $method = $type eq 'private'? 'list_secret_keys': 'list_public_keys';
+ my %res = CallGnuPG(
+ Options => {
+ 'with-colons' => undef, # parseable format
+ 'fingerprint' => undef, # show fingerprint
+ 'fixed-list-mode' => undef, # don't merge uid with keys
+ },
+ Method => $method,
+ ( $email ? (CommandArgs => [$email]) : () ),
+ Output => \@info,
);
-
- my %res;
-
- my ($handles, $handle_list) = _make_gpg_handles();
- my %handle = %$handle_list;
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $method = $type eq 'private'? 'list_secret_keys': 'list_public_keys';
- my $pid = safe_run_child { $gnupg->$method( handles => $handles, $email? (command_args => $email) : () ) };
- close $handle{'stdin'};
- waitpid $pid, 0;
- };
-
- my @info = readline $handle{'stdout'};
- close $handle{'stdout'};
-
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $@ || $? ) {
- $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
- return %res;
- }
+ return %res if $res{'message'};
@info = ParseKeysInfo( @info );
$res{'info'} = \@info;
@@ -2316,7 +1981,7 @@ sub DeleteKey {
my %res;
$res{'exit_code'} = $?;
foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
+ $res{$_} = do { local $/ = undef; readline $handle{$_} };
delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
close $handle{$_};
}
@@ -2332,43 +1997,10 @@ sub DeleteKey {
sub ImportKey {
my $key = shift;
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
+ return CallGnuPG(
+ Method => "import_keys",
+ Content => $key,
);
-
- my ($handles, $handle_list) = _make_gpg_handles();
- my %handle = %$handle_list;
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $gnupg->wrap_call(
- handles => $handles,
- commands => ['--import'],
- ) };
- print { $handle{'stdin'} } $key;
- close $handle{'stdin'};
- waitpid $pid, 0;
- };
- my $err = $@;
- close $handle{'stdout'};
-
- my %res;
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- }
- return %res;
}
=head2 KEY
@@ -2425,7 +2057,7 @@ sub Probe {
my ($handles, $handle_list) = _make_gpg_handles();
my %handle = %$handle_list;
- local $@;
+ local $@ = undef;
eval {
local $SIG{'CHLD'} = 'DEFAULT';
my $pid = safe_run_child { $gnupg->wrap_call( commands => ['--version' ], handles => $handles ) };
@@ -2449,7 +2081,7 @@ sub Probe {
. ($? & 127 ? (" as recieved signal ". ($? & 127)) : '')
. ".";
foreach ( qw(stderr logger status) ) {
- my $tmp = do { local $/; readline $handle{$_} };
+ my $tmp = do { local $/ = undef; readline $handle{$_} };
next unless $tmp && $tmp =~ /\S/s;
close $handle{$_};
$msg .= "\n$_:\n$tmp\n";
commit dc46058f56a2d309429b62f0d65c8320007d5f2b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jul 26 16:29:47 2011 -0400
Factor out code which finalizes GnuPG output into a data structure
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 1b08649..6698b57 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -447,6 +447,13 @@ sub CallGnuPG {
close $handle{stdout};
}
+ return Finalize( $err, \%handle );
+}
+
+sub Finalize {
+ my ($err, $handle) = @_;
+ my %handle = %{ $handle };
+
my %res;
$res{'exit_code'} = $?;
@@ -1978,20 +1985,7 @@ sub DeleteKey {
my $err = $@;
close $handle{'stdout'};
- my %res;
- $res{'exit_code'} = $?;
- foreach ( qw(stderr logger status) ) {
- $res{$_} = do { local $/ = undef; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- }
- return %res;
+ return Finalize( $err, \%handle );
}
sub ImportKey {
commit 09ccedaaf224ea71a4659f9ff9f1760336149332
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Aug 31 14:32:48 2012 -0400
Generalize CallGnuPG slightly more, allowing more code reuse
Three remaining locations had not been altered to use CallGnuPG, for two
reaspons: they required custom interactions with the streams beyond
simply printing content into gnupg's stdin, and because GnuPG::Interface
provided no direct method to call them. Extend CallGnuPG to allow both
of these features, which thus allows for more code removal.
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 6698b57..1f5e36c 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -423,10 +423,18 @@ sub CallGnuPG {
eval {
local $SIG{'CHLD'} = 'DEFAULT';
my $pid = safe_run_child {
- $gnupg->$method(
- handles => $handles,
- command_args => $args{CommandArgs},
- )
+ if ($method =~ /^--/) {
+ $gnupg->wrap_call(
+ handles => $handles,
+ commands => [$method],
+ command_args => $args{CommandArgs},
+ );
+ } else {
+ $gnupg->$method(
+ handles => $handles,
+ command_args => $args{CommandArgs},
+ );
+ }
};
{
local $SIG{'PIPE'} = 'IGNORE';
@@ -438,6 +446,7 @@ sub CallGnuPG {
$handle{'stdin'}->print( $content );
}
close $handle{'stdin'} or die "Can't close gnupg handle: $!";
+ $args{Callback}->(%handle) if $args{Callback};
}
waitpid $pid, 0;
};
@@ -1957,35 +1966,18 @@ sub _ParseDate {
sub DeleteKey {
my $key = shift;
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
- my ($handles, $handle_list) = _make_gpg_handles();
- my %handle = %$handle_list;
-
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $gnupg->wrap_call(
- handles => $handles,
- commands => ['--delete-secret-and-public-key'],
- command_args => [$key],
- ) };
- close $handle{'stdin'};
- while ( my $str = readline $handle{'status'} ) {
- if ( $str =~ /^\[GNUPG:\]\s*GET_BOOL delete_key\..*/ ) {
- print { $handle{'command'} } "y\n";
+ return CallGnuPG(
+ Method => "--delete-secret-and-public-key",
+ CommandArgs => [$key],
+ Callback => sub {
+ my %handle = @_;
+ while ( my $str = readline $handle{'status'} ) {
+ if ( $str =~ /^\[GNUPG:\]\s*GET_BOOL delete_key\..*/ ) {
+ print { $handle{'command'} } "y\n";
+ }
}
- }
- waitpid $pid, 0;
- };
- my $err = $@;
- close $handle{'stdout'};
-
- return Finalize( $err, \%handle );
+ },
+ );
}
sub ImportKey {
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 19dfb0d..05a3827 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -1151,125 +1151,50 @@ sub lsign_gnupg_key {
my $self = shift;
my $key = shift;
- require RT::Crypt::GnuPG; require GnuPG::Interface;
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $gnupg->options->hash_init(
- RT::Crypt::GnuPG::_PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
- my %handle;
- my $handles = GnuPG::Handles->new(
- stdin => ($handle{'input'} = IO::Handle->new()),
- stdout => ($handle{'output'} = IO::Handle->new()),
- stderr => ($handle{'error'} = IO::Handle->new()),
- logger => ($handle{'logger'} = IO::Handle->new()),
- status => ($handle{'status'} = IO::Handle->new()),
- command => ($handle{'command'} = IO::Handle->new()),
- );
+ require RT::Crypt::GnuPG;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- local @ENV{'LANG', 'LC_ALL'} = ('C', 'C');
- my $pid = $gnupg->wrap_call(
- handles => $handles,
- commands => ['--lsign-key'],
- command_args => [$key],
- );
- close $handle{'input'};
- while ( my $str = readline $handle{'status'} ) {
- if ( $str =~ /^\[GNUPG:\]\s*GET_BOOL sign_uid\..*/ ) {
- print { $handle{'command'} } "y\n";
+ return RT::Crypt::GnuPG::CallGnuPG(
+ Method => '--lsign-key',
+ CommandArgs => [$key],
+ Callback => sub {
+ my %handle = @_;
+ while ( my $str = readline $handle{'status'} ) {
+ if ( $str =~ /^\[GNUPG:\]\s*GET_BOOL sign_uid\..*/ ) {
+ print { $handle{'command'} } "y\n";
+ }
}
- }
- waitpid $pid, 0;
- };
- my $err = $@;
- close $handle{'output'};
-
- my %res;
- $res{'exit_code'} = $?;
- foreach ( qw(error logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'error'} ) if $res{'error'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- }
- return %res;
+ },
+ );
}
sub trust_gnupg_key {
my $self = shift;
my $key = shift;
- require RT::Crypt::GnuPG; require GnuPG::Interface;
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
- $gnupg->options->hash_init(
- RT::Crypt::GnuPG::_PrepareGnuPGOptions( %opt ),
- meta_interactive => 0,
- );
-
- my %handle;
- my $handles = GnuPG::Handles->new(
- stdin => ($handle{'input'} = IO::Handle->new()),
- stdout => ($handle{'output'} = IO::Handle->new()),
- stderr => ($handle{'error'} = IO::Handle->new()),
- logger => ($handle{'logger'} = IO::Handle->new()),
- status => ($handle{'status'} = IO::Handle->new()),
- command => ($handle{'command'} = IO::Handle->new()),
- );
+ require RT::Crypt::GnuPG;
- eval {
- local $SIG{'CHLD'} = 'DEFAULT';
- local @ENV{'LANG', 'LC_ALL'} = ('C', 'C');
- my $pid = $gnupg->wrap_call(
- handles => $handles,
- commands => ['--edit-key'],
- command_args => [$key],
- );
- close $handle{'input'};
-
- my $done = 0;
- while ( my $str = readline $handle{'status'} ) {
- if ( $str =~ /^\[GNUPG:\]\s*\QGET_LINE keyedit.prompt/ ) {
- if ( $done ) {
- print { $handle{'command'} } "quit\n";
- } else {
- print { $handle{'command'} } "trust\n";
+ return RT::Crypt::GnuPG::CallGnuPG(
+ Method => '--edit-key',
+ CommandArgs => [$key],
+ Callback => sub {
+ my %handle = @_;
+ my $done = 0;
+ while ( my $str = readline $handle{'status'} ) {
+ if ( $str =~ /^\[GNUPG:\]\s*\QGET_LINE keyedit.prompt/ ) {
+ if ( $done ) {
+ print { $handle{'command'} } "quit\n";
+ } else {
+ print { $handle{'command'} } "trust\n";
+ }
+ } elsif ( $str =~ /^\[GNUPG:\]\s*\QGET_LINE edit_ownertrust.value/ ) {
+ print { $handle{'command'} } "5\n";
+ } elsif ( $str =~ /^\[GNUPG:\]\s*\QGET_BOOL edit_ownertrust.set_ultimate.okay/ ) {
+ print { $handle{'command'} } "y\n";
+ $done = 1;
}
- } elsif ( $str =~ /^\[GNUPG:\]\s*\QGET_LINE edit_ownertrust.value/ ) {
- print { $handle{'command'} } "5\n";
- } elsif ( $str =~ /^\[GNUPG:\]\s*\QGET_BOOL edit_ownertrust.set_ultimate.okay/ ) {
- print { $handle{'command'} } "y\n";
- $done = 1;
}
- }
- waitpid $pid, 0;
- };
- my $err = $@;
- close $handle{'output'};
-
- my %res;
- $res{'exit_code'} = $?;
- foreach ( qw(error logger status) ) {
- $res{$_} = do { local $/; readline $handle{$_} };
- delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
- }
- $RT::Logger->debug( $res{'status'} ) if $res{'status'};
- $RT::Logger->warning( $res{'error'} ) if $res{'error'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
- if ( $err || $res{'exit_code'} ) {
- $res{'message'} = $err? $err : "gpg exitted with error code ". ($res{'exit_code'} >> 8);
- }
- return %res;
+ },
+ );
}
sub started_ok {
commit 019089fdc3dcad6b39681c760d8befbe420837fb
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jul 26 18:06:15 2011 -0400
Minor cleanups to Probe, the one remaining non-CallGnuPG gpg interaction
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 1f5e36c..289eac8 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -2032,13 +2032,11 @@ properly (and false otherwise).
sub Probe {
- my $gnupg = GnuPG::Interface->new();
- my %opt = RT->Config->Get('GnuPGOptions');
+ my $gnupg = GnuPG::Interface->new;
$gnupg->options->hash_init(
- _PrepareGnuPGOptions( %opt ),
- armor => 1,
- meta_interactive => 0,
+ _PrepareGnuPGOptions( RT->Config->Get('GnuPGOptions') )
);
+ $gnupg->options->meta_interactive( 0 );
my ($handles, $handle_list) = _make_gpg_handles();
my %handle = %$handle_list;
@@ -2046,7 +2044,12 @@ sub Probe {
local $@ = undef;
eval {
local $SIG{'CHLD'} = 'DEFAULT';
- my $pid = safe_run_child { $gnupg->wrap_call( commands => ['--version' ], handles => $handles ) };
+ my $pid = safe_run_child {
+ $gnupg->wrap_call(
+ commands => ['--version' ],
+ handles => $handles
+ )
+ };
close $handle{'stdin'};
waitpid $pid, 0;
};
@@ -2063,7 +2066,7 @@ sub Probe {
# but there is no way to get actuall error
if ( $? && ($? >> 8) != 2 ) {
my $msg = "Probe for GPG failed."
- ." Process exitted with code ". ($? >> 8)
+ ." Process exited with code ". ($? >> 8)
. ($? & 127 ? (" as recieved signal ". ($? & 127)) : '')
. ".";
foreach ( qw(stderr logger status) ) {
commit f5504208fa0784d74f830e86e5476ce4099cdcd7
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jul 26 16:30:25 2011 -0400
Catch errors on close()
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 289eac8..ca3d8f0 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -445,7 +445,7 @@ sub CallGnuPG {
} elsif (defined $content) {
$handle{'stdin'}->print( $content );
}
- close $handle{'stdin'} or die "Can't close gnupg handle: $!";
+ close $handle{'stdin'} or die "Can't close gnupg input handle: $!";
$args{Callback}->(%handle) if $args{Callback};
}
waitpid $pid, 0;
@@ -453,7 +453,9 @@ sub CallGnuPG {
my $err = $@;
if ($args{Output}) {
push @{$args{Output}}, readline $handle{stdout};
- close $handle{stdout};
+ if (not close $handle{stdout}) {
+ $err ||= "Can't close gnupg output handle: $!";
+ }
}
return Finalize( $err, \%handle );
@@ -469,7 +471,9 @@ sub Finalize {
foreach ( qw(stderr logger status) ) {
$res{$_} = do { local $/ = undef; readline $handle{$_} };
delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
- close $handle{$_};
+ if (not close $handle{$_}) {
+ $err ||= "Can't close gnupg $_ handle: $!";
+ }
}
$RT::Logger->debug( $res{'status'} ) if $res{'status'};
$RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
@@ -2050,7 +2054,7 @@ sub Probe {
handles => $handles
)
};
- close $handle{'stdin'};
+ close $handle{'stdin'} or die "Can't close gnupg input handle: $!";
waitpid $pid, 0;
};
if ( $@ ) {
@@ -2072,7 +2076,7 @@ sub Probe {
foreach ( qw(stderr logger status) ) {
my $tmp = do { local $/ = undef; readline $handle{$_} };
next unless $tmp && $tmp =~ /\S/s;
- close $handle{$_};
+ close $handle{$_} or $tmp .= "\nFailed to close: $!";
$msg .= "\n$_:\n$tmp\n";
}
$RT::Logger->debug( $msg );
commit 4598f0b68ae536e57551da0a5b2d13676b67769b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Wed Jul 27 13:36:06 2011 -0400
Rename Key to Signer for clarity and consistency
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index ca3d8f0..547f93d 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -371,7 +371,7 @@ our $RE_FILE_EXTENSIONS = qr/pgp|asc/i;
sub CallGnuPG {
my %args = (
Options => undef,
- Key => undef,
+ Signer => undef,
Recipients => [],
Passphrase => undef,
@@ -406,7 +406,7 @@ sub CallGnuPG {
);
$gnupg->options->armor( 1 );
$gnupg->options->meta_interactive( 0 );
- $gnupg->options->default_key( $args{Key} );
+ $gnupg->options->default_key( $args{Signer} );
my %seen;
$gnupg->options->push_recipients( $_ ) for
@@ -416,7 +416,7 @@ sub CallGnuPG {
$args{Passphrase} = $GnuPGOptions{passphrase}
unless defined $args{'Passphrase'};
- $args{Passphrase} = GetPassphrase( Address => $args{Key} )
+ $args{Passphrase} = GetPassphrase( Address => $args{Signer} )
unless defined $args{'Passphrase'};
$gnupg->passphrase( $args{'Passphrase'} );
@@ -572,7 +572,7 @@ sub SignEncryptRFC3156 {
my @signature;
%res = CallGnuPG(
- Key => $args{'Signer'},
+ Signer => $args{'Signer'},
Method => "detach_sign",
Handles => { stdin => IO::Handle::CRLF->new },
Direct => [],
@@ -605,7 +605,7 @@ sub SignEncryptRFC3156 {
$entity->make_multipart( 'mixed', Force => 1 );
%res = CallGnuPG(
- Key => $args{'Signer'},
+ Signer => $args{'Signer'},
Recipients => \@recipients,
Method => ( $args{'Sign'} ? "sign_and_encrypt" : "encrypt" ),
Handles => { stdout => $tmp_fh },
@@ -677,7 +677,7 @@ sub _SignEncryptTextInline {
my $entity = $args{'Entity'};
my %res = CallGnuPG(
- Key => $args{'Signer'},
+ Signer => $args{'Signer'},
Recipients => $args{'Recipients'},
Method => ( $args{'Sign'} && $args{'Encrypt'}
? 'sign_and_encrypt'
@@ -718,7 +718,7 @@ sub _SignEncryptAttachmentInline {
binmode $tmp_fh, ':raw';
my %res = CallGnuPG(
- Key => $args{'Signer'},
+ Signer => $args{'Signer'},
Recipients => $args{'Recipients'},
Method => ( $args{'Sign'} && $args{'Encrypt'}
? 'sign_and_encrypt'
@@ -771,7 +771,7 @@ sub SignEncryptContent {
binmode $tmp_fh, ':raw';
my %res = CallGnuPG(
- Key => $args{'Signer'},
+ Signer => $args{'Signer'},
Recipients => $args{'Recipients'},
Method => ( $args{'Sign'} && $args{'Encrypt'}
? 'sign_and_encrypt'
commit f37f58b444ee3191dcdce55f51771a1b7bcfad15
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Wed Jul 27 13:38:09 2011 -0400
Rename Method to Command for clarity; "--foo" is not a "method"
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 547f93d..b3f4894 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -375,7 +375,7 @@ sub CallGnuPG {
Recipients => [],
Passphrase => undef,
- Method => undef,
+ Command => undef,
CommandArgs => [],
Content => undef,
@@ -392,7 +392,7 @@ sub CallGnuPG {
%handle = %$handle_list;
my $content = $args{Content};
- my $method = $args{Method};
+ my $command = $args{Command};
my %GnuPGOptions = RT->Config->Get('GnuPGOptions');
my %opt = (
@@ -423,14 +423,14 @@ sub CallGnuPG {
eval {
local $SIG{'CHLD'} = 'DEFAULT';
my $pid = safe_run_child {
- if ($method =~ /^--/) {
+ if ($command =~ /^--/) {
$gnupg->wrap_call(
handles => $handles,
- commands => [$method],
+ commands => [$command],
command_args => $args{CommandArgs},
);
} else {
- $gnupg->$method(
+ $gnupg->$command(
handles => $handles,
command_args => $args{CommandArgs},
);
@@ -573,7 +573,7 @@ sub SignEncryptRFC3156 {
my @signature;
%res = CallGnuPG(
Signer => $args{'Signer'},
- Method => "detach_sign",
+ Command => "detach_sign",
Handles => { stdin => IO::Handle::CRLF->new },
Direct => [],
Passphrase => $args{'Passphrase'},
@@ -607,7 +607,7 @@ sub SignEncryptRFC3156 {
%res = CallGnuPG(
Signer => $args{'Signer'},
Recipients => \@recipients,
- Method => ( $args{'Sign'} ? "sign_and_encrypt" : "encrypt" ),
+ Command => ( $args{'Sign'} ? "sign_and_encrypt" : "encrypt" ),
Handles => { stdout => $tmp_fh },
Passphrase => $args{'Passphrase'},
Content => $entity->parts(0),
@@ -679,7 +679,7 @@ sub _SignEncryptTextInline {
my %res = CallGnuPG(
Signer => $args{'Signer'},
Recipients => $args{'Recipients'},
- Method => ( $args{'Sign'} && $args{'Encrypt'}
+ Command => ( $args{'Sign'} && $args{'Encrypt'}
? 'sign_and_encrypt'
: ( $args{'Sign'}
? 'clearsign'
@@ -720,7 +720,7 @@ sub _SignEncryptAttachmentInline {
my %res = CallGnuPG(
Signer => $args{'Signer'},
Recipients => $args{'Recipients'},
- Method => ( $args{'Sign'} && $args{'Encrypt'}
+ Command => ( $args{'Sign'} && $args{'Encrypt'}
? 'sign_and_encrypt'
: ( $args{'Sign'}
? 'detach_sign'
@@ -773,7 +773,7 @@ sub SignEncryptContent {
my %res = CallGnuPG(
Signer => $args{'Signer'},
Recipients => $args{'Recipients'},
- Method => ( $args{'Sign'} && $args{'Encrypt'}
+ Command => ( $args{'Sign'} && $args{'Encrypt'}
? 'sign_and_encrypt'
: ( $args{'Sign'}
? 'clearsign'
@@ -1021,7 +1021,7 @@ sub VerifyAttachment {
$tmp_fh->flush;
return CallGnuPG(
- Method => "verify",
+ Command => "verify",
CommandArgs => [ '-', $tmp_fn ],
Passphrase => $args{'Passphrase'},
Content => $args{'Signature'}->bodyhandle,
@@ -1037,7 +1037,7 @@ sub VerifyRFC3156 {
$tmp_fh->flush;
return CallGnuPG(
- Method => "verify",
+ Command => "verify",
CommandArgs => [ '-', $tmp_fn ],
Passphrase => $args{'Passphrase'},
Content => $args{'Signature'}->bodyhandle,
@@ -1062,7 +1062,7 @@ sub DecryptRFC3156 {
binmode $tmp_fh, ':raw';
my %res = CallGnuPG(
- Method => "decrypt",
+ Command => "decrypt",
Handles => { stdout => $tmp_fh },
Passphrase => $args{'Passphrase'},
Content => $args{'Data'}->bodyhandle,
@@ -1186,7 +1186,7 @@ sub _DecryptInlineBlock {
binmode $tmp_fh, ':raw';
my %res = CallGnuPG(
- Method => "decrypt",
+ Command => "decrypt",
Handles => { stdout => $tmp_fh, stdin => $args{'BlockHandle'} },
Passphrase => $args{'Passphrase'},
);
@@ -1255,7 +1255,7 @@ sub DecryptContent {
binmode $tmp_fh, ':raw';
my %res = CallGnuPG(
- Method => "decrypt",
+ Command => "decrypt",
Handles => { stdout => $tmp_fh },
Passphrase => $args{'Passphrase'},
Content => $args{'Content'},
@@ -1807,7 +1807,7 @@ sub GetKeysInfo {
'fingerprint' => undef, # show fingerprint
'fixed-list-mode' => undef, # don't merge uid with keys
},
- Method => $method,
+ Command => $method,
( $email ? (CommandArgs => [$email]) : () ),
Output => \@info,
);
@@ -1971,7 +1971,7 @@ sub DeleteKey {
my $key = shift;
return CallGnuPG(
- Method => "--delete-secret-and-public-key",
+ Command => "--delete-secret-and-public-key",
CommandArgs => [$key],
Callback => sub {
my %handle = @_;
@@ -1988,7 +1988,7 @@ sub ImportKey {
my $key = shift;
return CallGnuPG(
- Method => "import_keys",
+ Command => "import_keys",
Content => $key,
);
}
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 05a3827..3ccd017 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -1154,7 +1154,7 @@ sub lsign_gnupg_key {
require RT::Crypt::GnuPG;
return RT::Crypt::GnuPG::CallGnuPG(
- Method => '--lsign-key',
+ Command => '--lsign-key',
CommandArgs => [$key],
Callback => sub {
my %handle = @_;
@@ -1174,7 +1174,7 @@ sub trust_gnupg_key {
require RT::Crypt::GnuPG;
return RT::Crypt::GnuPG::CallGnuPG(
- Method => '--edit-key',
+ Command => '--edit-key',
CommandArgs => [$key],
Callback => sub {
my %handle = @_;
commit fde8a3f6520f736a6781c19fe7593c4a0b166aae
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Wed Jul 27 14:08:02 2011 -0400
Only set the defualt key if we actually have one
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index b3f4894..fce5bc9 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -406,7 +406,8 @@ sub CallGnuPG {
);
$gnupg->options->armor( 1 );
$gnupg->options->meta_interactive( 0 );
- $gnupg->options->default_key( $args{Signer} );
+ $gnupg->options->default_key( $args{Signer} )
+ if defined $args{Signer};
my %seen;
$gnupg->options->push_recipients( $_ ) for
commit ae6d549c42430b8a01625e63ea1f3aeb978871d5
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Wed Jul 27 14:11:29 2011 -0400
Only set the passphrase if we have one
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index fce5bc9..4567b2d 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -419,7 +419,8 @@ sub CallGnuPG {
unless defined $args{'Passphrase'};
$args{Passphrase} = GetPassphrase( Address => $args{Signer} )
unless defined $args{'Passphrase'};
- $gnupg->passphrase( $args{'Passphrase'} );
+ $gnupg->passphrase( $args{'Passphrase'} )
+ if defined $args{Passphrase};
eval {
local $SIG{'CHLD'} = 'DEFAULT';
commit cd978dd12587eddf319d0cc53f50650f687b4145
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Aug 30 19:49:43 2011 -0400
Split IO::Handle::CRLF into its own file in RT::Crypt::GnuPG::CRLFHandle
In doing so, also document the reasons it is necessary.
diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 4567b2d..1504688 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -52,6 +52,7 @@ use warnings;
package RT::Crypt::GnuPG;
use IO::Handle;
+use RT::Crypt::GnuPG::CRLFHandle;
use GnuPG::Interface;
use RT::EmailParser ();
use RT::Util 'safe_run_child', 'mime_recommended_filename';
@@ -573,10 +574,12 @@ sub SignEncryptRFC3156 {
$entity->make_multipart( 'mixed', Force => 1 );
my @signature;
+ # We use RT::Crypt::GnuPG::CRLFHandle to canonicalize the
+ # MIME::Entity output to use \r\n instead of \n for its newlines
%res = CallGnuPG(
Signer => $args{'Signer'},
Command => "detach_sign",
- Handles => { stdin => IO::Handle::CRLF->new },
+ Handles => { stdin => RT::Crypt::GnuPG::CRLFHandle->new },
Direct => [],
Passphrase => $args{'Passphrase'},
Content => $entity->parts(0),
@@ -2100,15 +2103,4 @@ sub _make_gpg_handles {
RT::Base->_ImportOverlays();
-# helper package to avoid using temp file
-package IO::Handle::CRLF;
-
-use base qw(IO::Handle);
-
-sub print {
- my ($self, @args) = (@_);
- s/\r*\n/\x0D\x0A/g foreach @args;
- return $self->SUPER::print( @args );
-}
-
1;
diff --git a/lib/RT/Crypt/GnuPG/CRLFHandle.pm b/lib/RT/Crypt/GnuPG/CRLFHandle.pm
new file mode 100644
index 0000000..5f74457
--- /dev/null
+++ b/lib/RT/Crypt/GnuPG/CRLFHandle.pm
@@ -0,0 +1,22 @@
+package RT::Crypt::GnuPG::CRLFHandle;
+use strict;
+use warnings;
+
+use base qw(IO::Handle);
+
+# https://metacpan.org/module/MIME::Tools#Fuzzing-of-CRLF-and-newline-when-encoding-composing
+# means that the output of $entity->print contains lines terminated by
+# "\n"; however, signatures are generated off of the "correct" form of
+# the MIME entity, which uses "\r\n" as the newline separator. This
+# class, used only when generating signatures, transparently munges "\n"
+# newlines into "\r\n" newlines such that the generated signature is
+# correct for the "\r\n"-newline version of the MIME entity which will
+# eventually be sent over the wire.
+
+sub print {
+ my ($self, @args) = (@_);
+ s/\r*\n/\x0D\x0A/g foreach @args;
+ return $self->SUPER::print( @args );
+}
+
+1;
-----------------------------------------------------------------------
More information about the Rt-commit
mailing list