File Coverage

blib/lib/Mail/GPG.pm
Criterion Covered Total %
statement 546 596 91.6
branch 161 248 64.9
condition 66 99 66.6
subroutine 42 55 76.3
pod 16 43 37.2
total 831 1041 79.8


line stmt bran cond sub pod time code
1             package Mail::GPG;
2              
3             $VERSION = "1.0.12";
4              
5 6     6   806 use strict;
  6         25  
  6         166  
6 6     6   29 use Carp;
  6         12  
  6         528  
7 6     6   458 use IO::Handle;
  6         4990  
  6         223  
8 6     6   3125 use Data::Dumper ();
  6         32226  
  6         142  
9 6     6   3147 use GnuPG::Interface;
  6         1621657  
  6         246  
10 6     6   570 use MIME::Parser;
  6         84437  
  6         169  
11 6     6   2320 use Mail::GPG::Result;
  6         16  
  6         174  
12 6     6   31 use Mail::Address;
  6         59  
  6         132  
13 6     6   26 use File::Temp;
  6         6  
  6         569  
14 6     6   32 use List::MoreUtils ();
  6         43  
  6         34167  
15              
16 51     51 0 259 sub get_default_key_id { shift->{default_key_id} }
17 81     81 0 250 sub get_default_passphrase { shift->{default_passphrase} }
18 107     107 0 1139 sub get_debug { shift->{debug} }
19 0     0 0 0 sub get_debug_dir { shift->{debug_dir} }
20 184     184 0 535 sub get_gnupg_hash_init { shift->{gnupg_hash_init} }
21 201     201 0 7339 sub get_digest { shift->{digest} }
22 30     30 0 116 sub get_default_key_encrypt { shift->{default_key_encrypt} }
23 368     368 0 4056 sub get_gpg_call { shift->{gpg_call} }
24 81     81 0 220 sub get_no_strict_7bit_encoding { shift->{no_strict_7bit_encoding} }
25 103     103 0 537 sub get_use_long_key_ids { shift->{use_long_key_ids} }
26              
27 0     0 0 0 sub set_default_key_id { shift->{default_key_id} = $_[1] }
28 0     0 0 0 sub set_default_passphrase { shift->{default_passphrase} = $_[1] }
29 0     0 0 0 sub set_debug { shift->{debug} = $_[1] }
30 0     0 0 0 sub set_debug_dir { shift->{debug_dir} = $_[1] }
31 0     0 0 0 sub set_gnupg_hash_init { shift->{gnupg_hash_init} = $_[1] }
32 0     0 0 0 sub set_digest { shift->{digest} = $_[1] }
33 0     0 0 0 sub set_default_key_encrypt { shift->{default_key_encrypt} = $_[1] }
34 0     0 0 0 sub set_gpg_call { shift->{gpg_call} = $_[1] }
35 0     0 0 0 sub set_no_strict_7bit_encoding { shift->{no_strict_7bit_encoding}=$_[1]}
36 0     0 0 0 sub set_use_long_key_ids { shift->{use_long_key_ids} = $_[1] }
37              
38             sub new {
39 62     62 1 234 my $class = shift;
40 62         1222 my %par = @_;
41             my ($default_key_id, $default_passphrase, $debug, $debug_dir) =
42 62         361 @par{'default_key_id','default_passphrase','debug','debug_dir'};
43             my ($gnupg_hash_init, $digest, $gpg_call, $default_key_encrypt) =
44 62         265 @par{'gnupg_hash_init','digest','gpg_call','default_key_encrypt'};
45             my ($no_strict_7bit_encoding, $use_long_key_ids) =
46 62         155 @par{'no_strict_7bit_encoding','use_long_key_ids'};
47              
48 62   33     2745 $debug_dir ||= $ENV{DUMPDIR} || File::Spec->tmpdir . '/mail-gpg-test';
      33        
49 62   50     171 $gnupg_hash_init ||= {};
50 62   50     552 $digest ||= "RIPEMD160";
51 62   50     454 $gpg_call ||= "gpg";
52 62   50     499 $no_strict_7bit_encoding ||= 0;
53 62   100     313 $use_long_key_ids ||= 0;
54              
55 62         1113 my $self = bless {
56             default_key_id => $default_key_id,
57             default_passphrase => $default_passphrase,
58             debug => $debug,
59             debug_dir => $debug_dir,
60             gnupg_hash_init => $gnupg_hash_init,
61             digest => $digest,
62             default_key_encrypt => $default_key_encrypt,
63             gpg_call => $gpg_call,
64             no_strict_7bit_encoding => $no_strict_7bit_encoding,
65             use_long_key_ids => $use_long_key_ids,
66             }, $class;
67              
68 62         295 return $self;
69             }
70              
71             sub new_gpg_interface {
72 184     184 0 532 my $self = shift;
73 184         569 my %par = @_;
74 184         525 my ($options, $passphrase) = @par{'options','passphrase'};
75              
76 184         7397 my $gpg = GnuPG::Interface->new;
77              
78 184 100       48313 $gpg->passphrase($passphrase) if defined $passphrase;
79 184 50       3742 $gpg->call( $self->get_gpg_call ) if $self->get_gpg_call ne '';
80              
81 184         3964 my $gnupg_hash_init = $self->get_gnupg_hash_init;
82              
83 184 100       646 if ($options) {
84 55         1236 $gpg->options->hash_init( %{$options}, %{$gnupg_hash_init} );
  55         23256  
  55         279  
85             }
86             else {
87 129         3518 $gpg->options->hash_init( %{$gnupg_hash_init} );
  129         63491  
88             }
89              
90 184         32815 $gpg->options->push_extra_args( '--digest', $self->get_digest );
91 184         22109 $gpg->options->meta_interactive(0);
92              
93 184         6461 return $gpg;
94             }
95              
96             sub save_debug_file {
97 0     0 0 0 my $self = shift;
98 0         0 my %par = @_;
99 0         0 my ($name, $data, $data_fh ) = @par{'name','data','data_fh' };
100              
101 0         0 $name = $self->get_debug_dir . "/mgpg-" . $name;
102              
103 0 0       0 open( DBG, ">$name" ) or die "can't write $name";
104 0 0       0 if ($data_fh) {
    0          
105 0         0 seek $data_fh, 0, 0;
106 0         0 print DBG $_ while <$data_fh>;
107             }
108             elsif ( ref $data ) {
109 0         0 print DBG $$data;
110             }
111             else {
112 0         0 print DBG $data;
113             }
114 0         0 close DBG;
115              
116 0         0 1;
117             }
118              
119             sub check_7bit_encoding_of_all_parts {
120 73     73 0 334 my $self = shift;
121 73         183 my %par = @_;
122 73         162 my ($entity) = $par{'entity'};
123              
124             #-- skip if no strict encoding check should be applied
125 73 50       217 return 1 if $self->get_no_strict_7bit_encoding;
126              
127             #-- first the primary entity
128 73         185 my $encoding = $entity->head->get("content-transfer-encoding");
129 73 50 66     3597 die "Content transfer encoding '$encoding' is not 7bit safe"
130             unless not defined $encoding
131             or $encoding =~ /^(quoted-printable|base64|7bit)\s*$/i;
132              
133             #-- now all parts
134 73 100       384 return 1 if not $entity->parts;
135              
136             #-- recursively
137 17         186 my $parts = $entity->parts;
138 17         152 for ( my $i = 0; $i < $parts; ++$i ) {
139 34         247 $self->check_7bit_encoding_of_all_parts( entity => $entity->parts($i),
140             );
141             }
142              
143 17         150 return 1;
144             }
145              
146             sub check_encryption {
147 60     60 0 149 my $self = shift;
148 60         386 my %par = @_;
149             my ($entity, $encrypted_text_sref) =
150 60         218 @par{'entity','encrypted_text_sref'};
151              
152 60         172 my $is_armor;
153 60 100       496 if ( $entity->effective_type =~ m!multipart/encrypted!i ) {
    50          
154              
155             #-- is this a valid RFC 3156 multipart/encrypted entity?
156 44 50       12755 die "Entity must have two parts"
157             if $entity->parts != 2;
158 44 50       512 die "Entity is not OpenPGP encrypted"
159             unless $entity->parts(0)->effective_type
160             =~ m!application/pgp-encrypted!i;
161 44         5095 $$encrypted_text_sref = $entity->parts(1)->body_as_string;
162              
163             }
164             elsif ( $entity->bodyhandle ) {
165              
166             #-- probably an ASCII armor encrypted entity
167             #-- (we need the *decoded* data here - hopefully the
168             #-- MIME::Parser had decode_body(1) set).
169 16         3615 $$encrypted_text_sref = $entity->bodyhandle->as_string;
170 16 50       311 die "Entity is not OpenPGP encrypted"
171             unless $$encrypted_text_sref =~ /^-----BEGIN PGP MESSAGE-----/m;
172 16         57 $is_armor = 1;
173             }
174             else {
175 0         0 die "Entity is not multipart/encrypted and has no body";
176             }
177              
178 60         20698 return $is_armor;
179             }
180              
181             sub perform_multiplexed_gpg_io {
182 137     137 0 1066 my $self = shift;
183 137         3447 my %par = @_;
184             my ($data_fh, $data_canonify, $stdin_fh, $stderr_fh) =
185 137         598 @par{'data_fh','data_canonify','stdin_fh','stderr_fh'};
186             my ($stdout_fh, $status_fh, $stderr_sref, $stdout_sref) =
187 137         533 @par{'stdout_fh','status_fh','stderr_sref','stdout_sref'};
188             my ($status_sref) =
189 137         319 $par{'status_sref'};
190              
191 137         1749 require IO::Select;
192              
193             #-- perl < 5.6 compatibility: seek() and read() work
194             #-- on native GLOB filehandle only, so dertmine type
195             #-- of filehandle here
196 137         1655 my $data_fh_glob = ref $data_fh eq 'GLOB';
197              
198             #-- rewind the data filehandle
199 137 50       688 if ($data_fh_glob) {
200 137         3782 seek $data_fh, 0, 0;
201             }
202             else {
203 0         0 $data_fh->seek( 0, 0 );
204             }
205              
206             #-- create IO::Select objects for all
207             #-- filehandles in question
208 137         5275 my $stdin = IO::Select->new($stdin_fh);
209 137         13855 my $stderr = IO::Select->new($stderr_fh);
210 137         5120 my $stdout = IO::Select->new($stdout_fh);
211 137 100       4699 my $status = $status_fh ? IO::Select->new($status_fh) : undef;
212              
213 137         1790 my $buffer;
214 137         222 while (1) {
215              
216             #-- as long we has data try to write
217             #-- it into gpg
218 339   100     3819 while ( $data_fh && $stdin->can_write(0.001) ) {
219 94706 50       3164390 if ( $data_fh_glob
    100          
220             ? read $data_fh,
221             $buffer, 1024
222             : $data_fh->read( $buffer, 1024 ) ) {
223              
224             #-- ok, got a block of data
225 94569 100       146384 if ($data_canonify) {
226              
227             #-- canonify it if requested
228 94561         693933 $buffer =~ s/\x0A/\x0D\x0A/g;
229 94561         176675 $buffer =~ s/\x0D\x0D\x0A/\x0D\x0A/g;
230             }
231              
232             #-- feed it into gpg
233 94569         1595870 print $stdin_fh $buffer;
234             }
235             else {
236              
237             #-- no data read, close gpg's stdin
238             #-- and set the data filehandle to false
239 137         979 close $stdin_fh;
240 137         437 $data_fh = 0;
241             }
242             }
243              
244             #-- probably we can read from gpg's stdout
245 339         72129 while ( $stdout->can_read(0.001) ) {
246 1571984 100       40060404 last if eof($stdout_fh);
247 1571861         4156648 $$stdout_sref .= <$stdout_fh>;
248             }
249              
250             #-- probably we can read from gpg's stderr
251 339         267311 while ( $stderr->can_read(0.001) ) {
252 342 100       37098 last if eof($stderr_fh);
253 194         1198 $$stderr_sref .= <$stderr_fh>;
254             }
255              
256             #-- probably we can read from gpg's status
257 339 100       217713 if ($status) {
258 135         541 while ( $status->can_read(0.001) ) {
259 479 100       19296 last if eof($status_fh);
260 416         1605 $$status_sref .= <$status_fh>;
261             }
262             }
263              
264             #-- we're finished if no more data left
265             #-- and both gpg's stdout and stderr
266             #-- are at eof.
267             return
268 339 50 100     1440479 if !$data_fh
      100        
      66        
      100        
269             && eof($stderr_fh)
270             && eof($stdout_fh)
271             && ( !$status_fh || eof($status_fh) );
272             }
273              
274 0         0 1;
275             }
276              
277             #-- Addresses bug: https://rt.cpan.org/Public/Bug/Display.html?id=103828 (here)
278             #-- Possibly also: https://rt.cpan.org/Public/Bug/Display.html?id=81082 (here)
279             # and https://rt.cpan.org/Public/Bug/Display.html?id=80500 (in Makefile.PL)
280              
281             sub _parse_key_list {
282 59     59   11700 my $self = shift;
283 59         500 my ($output_stdout, %par) = @_;
284             my ($coerce, $debug, $verbose) =
285 59         265 @par{'coerce','debug','verbose'};
286              
287             #-- grab key ID's and emails from output (backward compatible to gpg 1.x)
288             #
289             # Example:
290             # search request: "--list-keys --with-colons 6C187D0F196ED9E3"
291             # format : see /usr/share/doc/packages/gpg2/DETAILS
292             #
293             #--OLD sample: gpg (GnuPG) 1.4.11
294             # tru:t:1:1431088683:0:3:1:5
295             # pub:-:1024:17:062F00DAE20F5035:2004-02-10:::-:Jörn Reder Mail\x3a\x3aGPG Test Key ::scaESCA:
296             # sub:-:1024:16:6C187D0F196ED9E3:2004-02-10::::::e:
297             #
298             # expected output: (062F00DAE20F5035, 'Jörn Reder Mail::GPG Test Key ')
299             #
300             #--NEW sample: gpg (GnuPG) 2.0.22
301             # tru:t:1:1429473192:0:3:1:5
302             # pub:-:1024:17:062F00DAE20F5035:1076425915:::-:::scaESCA:
303             # uid:-::::1076425915::588869ADE077B8FB05788A99565AEED15AED8231::Jörn Reder Mail\x3a\x3aGPG Test Key :
304             # sub:-:1024:16:6C187D0F196ED9E3:1076425917::::::e:
305             #
306             # expected output: (062F00DAE20F5035, 'Jörn Reder Mail::GPG Test Key ',
307             # 6C187D0F196ED9E3, 'Jörn Reder Mail::GPG Test Key ')
308             #
309             # PERLBOTIX<ätt>cpan.org / May, 2015
310              
311 59         107 my @result;
312              
313             #-- needed for utf8 handling
314 59 50       596 require Encode if $] >= 5.008;
315              
316 59         390 $output_stdout .= "\nFLUSH\n"; #-- we add this token to trigger flushing of the last record
317              
318             #-- these values are valid per "paragraph" (from pub: to pub:)
319 59         286 my @ids; #-- list of potential IDs (pub, sub)
320             my @emails; #-- list of potential email adresses (uid)
321 59         139 my $gpg2_mode = 0; #-- auto-detect OLD/NEW format: true if NEW (gpg2) format
322              
323             #-- parse output line by line
324             #-- OLD-format (gpg 1.x): simulate old behaviour
325             #-- NEW-format (gpg 2.x): create all combinations of valid key-IDs and emails; return (pub) key-ID first,
326             # so the result stays backward compatible
327              
328 59         954 while ( $output_stdout =~ m!^((\w+):?.*?)[\r\n]+!mg ) {
329              
330 274         1079 my ($line, $tag) = ($1, $2);
331 274         1109 my @fields = split /:/, $line, -1;
332              
333 274 50       445 warn("\nFields: ", join(", ", map { "($_)" } @fields)) if $debug;
  0         0  
334              
335             #-- skip entries that are expired or otherwise invalid
336 274 100 100     1702 if ( ( $fields[1] and $fields[1] =~ /^([eird])$/ )
      66        
      66        
337             or ( $fields[11] and $fields[11] =~ /([D])/ )
338             ) { #-- cannot use key (invalid/expired/revoked/[dD]isabled)
339 48 50 33     100 carp "Skipping key invalidated by '$1'-flag: ($line)" if $verbose or $debug;
340 48         289 next;
341             }
342              
343             #-- a further 'pub' marks a new paragraph or the end of the current paragraph
344             #-- a 'FLUSH' marks the end of the the whole text
345 226 100 100     1964 if ( $tag eq 'pub' or $tag eq 'FLUSH' ) {
    100 100        
    100 66        
      66        
      66        
346              
347             #-- flush intermediate or final results
348 110 100 66     778 if ( @ids and @emails ) {
    50 33        
349              
350 51         513 @emails = List::MoreUtils::uniq @emails; #-- uniq preserves order
351              
352 51         183 for ( @emails ) {
353             #-- $key_mail is quoted C-style (e.g. \x3a is a colon)
354 55         539 s/\\x(..)/chr(hex($1))/eg;
  86         457  
355              
356             #-- tell Perl that this variable is utf8 encoded
357             #-- (if Perl version is 5.8.0 or greater)
358 55 50       263 eval { $_ = Encode::decode("utf-8", $_, Encode::FB_CROAK) }
  55         752  
359             if $] >= 5.008;
360             }
361              
362 51         6010 @ids = List::MoreUtils::uniq @ids;
363 51 100       465 if ( !$self->get_use_long_key_ids ) {
364 29         115 $_ = substr($_,-8) for ( @ids );
365             }
366              
367             #-- finally, fill result array with all combinations of key-ids and emails (legacy), found in the previous paragraph
368 51         219 for my $id ( @ids ) {
369              
370 63 100       150 if ( $coerce ) {
371 14         24 push @result, $id, [ @emails ]; #-- "modern" interface
372             }
373             else {
374 49         82 for my $m ( @emails ) {
375 53         226 push @result, ($id, $m); #-- legacy
376             }
377             }
378             }
379              
380             } elsif ( @ids or @emails ) {
381 0         0 carp "Incomplete key data - key is probably invalid? (@ids) x (@emails)!";
382             }
383              
384 110         275 @ids = ();
385 110         168 @emails = ();
386 110 50 66     433 last if defined $fields[1] and $fields[1] eq 'FLUSH';
387              
388              
389             #-- parse 'new' format (pub:..; next: uid:..., uid:..., sub:... )
390 110 100 100     885 if ( not defined $fields[9] or $fields[9] =~ /^\s*$/ ) {
    50          
391 71         114 $gpg2_mode = 1;
392 71         306 push @ids, $fields[4];
393             }
394             #-- parse 'old' format (pub:)
395             elsif ( $fields[9] =~ /<[^>]+>/ ) {
396 39         82 $gpg2_mode = 0;
397 39         346 push @ids, $fields[4];
398 39         337 push @emails, $fields[9];
399             }
400             else {
401 0         0 die "Cannot parse: ($line)";
402             }
403              
404             } #-- 'pub' & 'FLUSH' handled obove
405              
406             #-- handle 'sub' entries / extract key-id
407             elsif ( $tag eq 'sub' and $gpg2_mode and $fields[4]) {
408 12         52 push @ids, $fields[4];
409             }
410              
411             #-- handle 'uid' entries / extract email
412             elsif ( $tag eq 'uid' and $gpg2_mode and $fields[9] =~ /<[^>]+>/ ) {
413 16         101 push @emails, $fields[9];
414             }
415              
416             #-- ignore anything else
417             else {
418 88 50       697 warn "Ignoring line [gpg2_mode=$gpg2_mode] -- '$line'" if $debug;
419             }
420              
421             } #-- loop over output_stdout
422              
423 59 50       227 warn Data::Dumper->Dump( [ \@result], [qw(RESULT_AS_REF)] ) if $debug;
424              
425 59         248 return @result;
426             }
427              
428              
429             sub query_keyring {
430 35     35 1 142 my $self = shift;
431 35         340 my %par = @_;
432 35         151 my ($search, $debug) = @par{'search','debug'};
433              
434             #-- ignore any PIPE signals, in case of gpg exited
435             #-- early before we fed our data into it.
436 35         963 local $SIG{PIPE} = 'IGNORE';
437              
438             #-- we parse gpg's output and rely on english
439 35         648 local $ENV{LC_ALL} = "C";
440              
441             #-- get a GnuPG::Interface
442 35         379 my $gpg = $self->new_gpg_interface;
443              
444             #-- initialize Handles
445 35         637 my $stdout = IO::Handle->new;
446 35         1809 my $stderr = IO::Handle->new;
447 35         1823 my $handles = GnuPG::Handles->new(
448             stdout => $stdout,
449             stderr => $stderr,
450             );
451              
452             #-- execute gpg --list-public-keys
453 35         26078 my $pid = $gpg->wrap_call(
454             handles => $handles,
455             commands => [ "--list-keys", "--with-colons" ],
456             command_args => [$search],
457             );
458              
459             #-- fetch gpg's STDERR
460 35         114474 my $output_stderr;
461 35         461364 $output_stderr .= $_ while <$stderr>;
462 35         870 close $stderr;
463              
464             #-- fetch gpg's STDOUT
465 35         217 my $output_stdout;
466 35         1004 $output_stdout .= $_ while <$stdout>;
467 35         343 close $stdout;
468              
469             #-- wait on gpg exit
470 35         592 waitpid $pid, 0;
471              
472 35 50       339 if ( $debug ) {
473 0         0 warn "LIST_KEYS(CMD) -- --list-keys --with-colons $search\n";
474 0         0 warn "LIST_KEYS(STDERR) -- search for ($search):\n$output_stderr\n";
475 0         0 warn "LIST_KEYS(STDOUT) -- search for ($search):\n$output_stdout\n";
476             }
477              
478 35         641 my @result = $self->_parse_key_list( $output_stdout , %par);
479              
480             #-- return result: undef if nothing found, first key-id if
481             #-- a scalar is requested, all entries suitable for a hash
482             #-- slurp if an array is requested
483              
484             # Compatibility note: The first id is always the id of the 'pub' entry, even for a subkey-hit.
485             # gpgv2: The result should not be used to initialise a hash, since some emails will be clobbered.
486             # We need another interface here. Curently Mail::GPG uses only the first entry...
487 35 50       122 return if not @result;
488 35 100       971 return $result[0] if not wantarray;
489 30         3215 return @result;
490             }
491              
492             sub build_rfc3156_multipart_entity {
493 39     39 0 102 my $self = shift;
494 39         645 my %par = @_;
495 39         159 my ($entity, $method) = @par{'entity','method'};
496              
497             #-- check, if content-transfer-encoding follows the
498             #-- RFC 3156 requirement of being 7bit safe
499 39         369 $self->check_7bit_encoding_of_all_parts( entity => $entity );
500              
501             #-- build entity for signed/encrypted version; first make
502             #-- a copy of the given entity (deep copy of body
503             #-- files isn't necessary, body data isn't modified
504             #-- here).
505 39         394 my $rfc_entity = $entity->dup;
506              
507             #-- determine the part, which is to be signed/encrypted
508 39         18304 my ( $work_part, $multipart );
509 39 100       183 if ( $rfc_entity->parts > 1 ) {
510              
511             #-- the entity is multipart, so we need to build
512             #-- a new version of it with all parts, but without
513             #-- the rfc822 mail headers of the original entity
514             #-- (according RFC 3156 the signed/encrypted parts
515             #-- need MIME content headers only)
516 17         164 $work_part = MIME::Entity->build( Type => "multipart/mixed" );
517 17         14860 $work_part->add_part($_) for $rfc_entity->parts;
518 17         469 $rfc_entity->parts( [] );
519 17         138 $multipart = 1;
520             }
521             else {
522              
523             #-- the entity is single part, so just make it
524             #-- multipart and take the first (and only) part
525 22         354 $rfc_entity->make_multipart;
526 22         32292 $work_part = $rfc_entity->parts(0);
527 22         140 $multipart = 0;
528             }
529              
530             #-- configure headers and add first part to the entity
531 39 100       124 if ( $method eq 'sign' ) {
532              
533             #-- set correct MIME OpenPGP header für multipart/signed
534 17         45 $rfc_entity->head->mime_attr( "Content-Type", "multipart/signed" );
535 17         4106 $rfc_entity->head->mime_attr( "Content-Type.protocol",
536             "application/pgp-signature" );
537 17         5197 $rfc_entity->head->mime_attr( "Content-Type.micalg",
538             "pgp-" . lc( $self->get_digest ) );
539              
540             #-- add content part as first part
541 17 100       5232 $rfc_entity->add_part($work_part) if $multipart;
542             }
543             else {
544              
545             #-- set correct MIME OpenPGP header für multipart/encrypted
546 22         56 $rfc_entity->head->mime_attr( "Content-Type", "multipart/encrypted" );
547 22         5116 $rfc_entity->head->mime_attr( "Content-Type.protocol",
548             "application/pgp-encrypted" );
549              
550             #-- remove all parts
551 22         6389 $rfc_entity->parts( [] );
552              
553             #-- and add OpenPGP version part as first part
554 22         310 $rfc_entity->attach(
555             Type => "application/pgp-encrypted",
556             Disposition => "inline",
557             Data => ["Version: 1\n"],
558             Encoding => "7bit",
559             );
560             }
561              
562             #-- return the newly created entitiy and the part to work on
563 39         19045 return ( $rfc_entity, $work_part );
564             }
565              
566             sub mime_sign {
567 17     17 1 4711 my $self = shift;
568 17         84 my %par = @_;
569             my ($key_id, $passphrase, $entity) =
570 17         62 @par{'key_id','passphrase','entity'};
571              
572             #-- ignore any PIPE signals, in case of gpg exited
573             #-- early before we fed our data into it.
574 17         342 local $SIG{PIPE} = 'IGNORE';
575              
576             #-- we parse gpg's output and rely on english
577 17         186 local $ENV{LC_ALL} = "C";
578              
579             #-- get default key ID and passphrase, if not given
580 17 50       126 $key_id = $self->get_default_key_id if not defined $key_id;
581 17 50       113 $passphrase = $self->get_default_passphrase if not defined $passphrase;
582              
583             #-- check parameters
584 17 50       55 die "No key_id set" if $key_id eq '';
585 17 50       53 die "No passphrase set" if not defined $passphrase;
586              
587             #-- build entity for signed version
588             #-- (only the 2nd part with the signature data
589             #-- needs to be added later)
590 17         151 my ( $signed_entity, $sign_part ) = $self->build_rfc3156_multipart_entity(
591             entity => $entity,
592             method => "sign",
593             );
594              
595             #-- get a GnuPG::Interface
596 17         259 my $gpg = $self->new_gpg_interface(
597             options => {
598             armor => 1,
599             default_key => $key_id,
600             },
601             passphrase => $passphrase,
602             );
603              
604             #-- initialize handles
605 17         303 my $stdin = IO::Handle->new;
606 17         763 my $stdout = IO::Handle->new;
607 17         349 my $stderr = IO::Handle->new;
608 17         647 my $handles = GnuPG::Handles->new(
609             stdin => $stdin,
610             stdout => $stdout,
611             stderr => $stderr,
612             );
613              
614             #-- execute gpg for signing
615 17         11286 my $pid = $gpg->detach_sign( handles => $handles );
616              
617             #-- put encoded entity data into temporary file
618             #-- (faster than in-memory operation)
619 17         69549 my ( $data_fh, $data_file ) = File::Temp::tempfile();
620 17         11995 unlink $data_file;
621 17         564 $sign_part->print($data_fh);
622              
623             #-- perform I/O (multiplexed to prevent blocking)
624 17         46857 my ( $output_stdout, $output_stderr ) = ("", "");
625 17         362 $self->perform_multiplexed_gpg_io(
626             data_fh => $data_fh,
627             data_canonify => 1,
628             stdin_fh => $stdin,
629             stderr_fh => $stderr,
630             stdout_fh => $stdout,
631             stderr_sref => \$output_stderr,
632             stdout_sref => \$output_stdout,
633             );
634              
635             #-- close reader filehandles (stdin was closed
636             #-- by perform_multiplexed_gpg_io())
637 17         277 close $stdout;
638 17         229 close $stderr;
639              
640             #-- fetch zombie
641 17         277 waitpid $pid, 0;
642 17 50       305 die $output_stderr if $?;
643              
644             #-- attach OpenPGP signature as second part
645 17         194 $signed_entity->attach(
646             Type => "application/pgp-signature",
647             Disposition => "inline",
648             Data => [$output_stdout],
649             Encoding => "7bit",
650             );
651              
652             #-- debugging: create file with signed data
653 17 50       21756 if ( $self->get_debug ) {
654 0         0 $self->save_debug_file(
655             name => "mime-sign-data.txt",
656             data_fh => $data_fh,
657             );
658 0         0 $self->save_debug_file(
659             name => "mime-sign-entity.txt",
660             data => \$signed_entity->as_string,
661             );
662             }
663              
664             #-- close temporary data filehandle
665 17         579 close $data_fh;
666              
667             #-- return signed entity
668 17         2065 return $signed_entity;
669             }
670              
671             sub mime_encrypt {
672 8     8 1 31 my $self = shift;
673 8         58 my %par = @_;
674 8         34 my ($entity, $recipients) = @par{'entity','recipients'};
675              
676             #-- call mime_sign_encrypt() with no_sign option
677 8         59 return $self->mime_sign_encrypt(
678             entity => $entity,
679             recipients => $recipients,
680             _no_sign => 1,
681             );
682             }
683              
684             sub mime_sign_encrypt {
685 22     22 1 72 my $self = shift;
686 22         127 my %par = @_;
687             my ($key_id, $passphrase, $entity, $recipients, $_no_sign) =
688 22         100 @par{'key_id','passphrase','entity','recipients','_no_sign'};
689              
690             #-- ignore any PIPE signals, in case of gpg exited
691             #-- early before we fed our data into it.
692 22         373 local $SIG{PIPE} = 'IGNORE';
693              
694             #-- we parse gpg's output and rely on english
695 22         225 local $ENV{LC_ALL} = "C";
696              
697             #-- get default key ID and passphrase, if not given
698 22 50       139 $key_id = $self->get_default_key_id if not defined $key_id;
699 22 50       124 $passphrase = $self->get_default_passphrase if not defined $passphrase;
700              
701             #-- check parameters
702 22 50 66     183 die "No key_id set" if not $_no_sign and $key_id eq '';
703 22 50 66     110 die "No passphrase set" if not $_no_sign and not defined $passphrase;
704              
705             #-- build entity for encrypted version
706             #-- (only the 2nd part with the encrypted data
707             #-- needs to be added later)
708 22         221 my ( $encrypted_entity, $encrypt_part )
709             = $self->build_rfc3156_multipart_entity(
710             entity => $entity,
711             method => "encrypt",
712             );
713              
714             #-- get a GnuPG::Interface
715 22         366 my $gpg = $self->new_gpg_interface(
716             options => {
717             armor => 1,
718             default_key => $key_id,
719             },
720             passphrase => $passphrase,
721             );
722              
723             #-- add recipients, but first extract the mail-adress
724             #-- part, otherwise gpg couldn't find keys for adresses
725             #-- with quoted printable encodings in the name part-
726 22         191 $recipients = $self->extract_mail_address( recipients => $recipients, );
727 22         41 $gpg->options->push_recipients($_) for @{$recipients};
  22         417  
728              
729             #-- add default key to recipients if requested
730 22 50 33     2368 $gpg->options->push_recipients( $self->get_default_key_id )
731             if $self->get_default_key_encrypt
732             and $self->get_default_key_id;
733              
734             #-- initialize handles
735 22         145 my $stdin = IO::Handle->new;
736 22         519 my $stdout = IO::Handle->new;
737 22         368 my $stderr = IO::Handle->new;
738 22         973 my $handles = GnuPG::Handles->new(
739             stdin => $stdin,
740             stdout => $stdout,
741             stderr => $stderr,
742             );
743              
744             #-- execute gpg for encryption
745 22         10116 my $pid;
746 22 100       74 if ($_no_sign) {
747 8         30 $pid = $gpg->encrypt( handles => $handles );
748             }
749             else {
750 14         60 $pid = $gpg->sign_and_encrypt( handles => $handles );
751             }
752              
753             #-- put encoded entity data into temporary file
754             #-- (faster than in-memory operation)
755 22         105912 my ( $data_fh, $data_file ) = File::Temp::tempfile();
756 22         15688 unlink $data_file;
757 22         472 $encrypt_part->print($data_fh);
758              
759             #-- perform I/O (multiplexed to prevent blocking)
760 22         16590769 my ( $output_stdout, $output_stderr ) = ("", "");
761 22         580 $self->perform_multiplexed_gpg_io(
762             data_fh => $data_fh,
763             data_canonify => 1,
764             stdin_fh => $stdin,
765             stderr_fh => $stderr,
766             stdout_fh => $stdout,
767             stderr_sref => \$output_stderr,
768             stdout_sref => \$output_stdout,
769             );
770              
771             #-- close reader filehandles (stdin was closed
772             #-- by perform_multiplexed_gpg_io())
773 22         290 close $stdout;
774 22         224 close $stderr;
775              
776             #-- fetch zombie
777 22         417 waitpid $pid, 0;
778 22 50       194 die $output_stderr if $?;
779              
780             #-- attach second part with the encrytped text
781 22         812 $encrypted_entity->attach(
782             Type => "application/octet-stream",
783             Disposition => "inline",
784             Data => [$output_stdout],
785             Encoding => "7bit",
786             );
787              
788             #-- debugging: create file with encrypted data
789 22 50       29931 if ( $self->get_debug ) {
790 0         0 $self->save_debug_file(
791             name => "mime-enc-data.txt",
792             data_fh => $data_fh,
793             );
794 0         0 $self->save_debug_file(
795             name => "mime-enc-entity.txt",
796             data => \$encrypted_entity->as_string,
797             );
798             }
799              
800             #-- close temporary data filehandle
801 22         19123 close $data_fh;
802              
803             #-- return encrytped entity
804 22         3043 return $encrypted_entity;
805             }
806              
807             sub armor_sign {
808 8     8 1 24 my $self = shift;
809 8         45 my %par = @_;
810             my ($key_id, $passphrase, $entity) =
811 8         34 @par{'key_id','passphrase','entity'};
812              
813             #-- ignore any PIPE signals, in case of gpg exited
814             #-- early before we fed our data into it.
815 8         166 local $SIG{PIPE} = 'IGNORE';
816              
817             #-- we parse gpg's output and rely on english
818 8         108 local $ENV{LC_ALL} = "C";
819              
820             #-- get default key ID and passphrase, if not given
821 8 50       70 $key_id = $self->get_default_key_id if not defined $key_id;
822 8 50       54 $passphrase = $self->get_default_passphrase if not defined $passphrase;
823              
824             #-- check parameters
825 8 50       23 die "No key_id set" if $key_id eq '';
826 8 50       27 die "No passphrase set" if not defined $passphrase;
827 8 50       23 die "Entity has no body" if not $entity->bodyhandle;
828              
829             #-- check, if body content-transfer-encoding is 7bit safe
830 8 50       76 if ( not $self->get_no_strict_7bit_encoding ) {
831 8         16 my $encoding = $entity->head->get("content-transfer-encoding");
832 8 50       542 die "Content transfer encoding '$encoding' is not 7bit safe"
833             unless $encoding =~ /^(quoted-printable|base64|7bit)\s*$/i;
834             }
835              
836             #-- get a GnuPG::Interface, with ASCII armor enabled
837 8         132 my $gpg = $self->new_gpg_interface(
838             options => {
839             armor => 1,
840             default_key => $key_id,
841             },
842             passphrase => $passphrase,
843             );
844              
845             #-- initialize handles
846 8         156 my $stdin = IO::Handle->new;
847 8         352 my $stdout = IO::Handle->new;
848 8         117 my $stderr = IO::Handle->new;
849 8         321 my $handles = GnuPG::Handles->new(
850             stdin => $stdin,
851             stdout => $stdout,
852             stderr => $stderr,
853             );
854              
855             #-- execute gpg for signing
856 8         3932 my $pid = $gpg->clearsign( handles => $handles );
857              
858             #-- put encoded entity data into temporary file
859             #-- (faster than in-memory operation)
860 8         33055 my ( $data_fh, $data_file ) = File::Temp::tempfile();
861 8         5682 unlink $data_file;
862 8         259 $entity->print($data_fh);
863              
864             #-- perform I/O (multiplexed to prevent blocking)
865 8         20824 my ( $output_stdout, $output_stderr ) = ("", "");
866 8         158 $self->perform_multiplexed_gpg_io(
867             data_fh => $data_fh,
868             data_canonify => 1,
869             stdin_fh => $stdin,
870             stderr_fh => $stderr,
871             stdout_fh => $stdout,
872             stderr_sref => \$output_stderr,
873             stdout_sref => \$output_stdout,
874             );
875              
876             #-- close reader filehandles (stdin was closed
877             #-- by perform_multiplexed_gpg_io())
878 8         111 close $stdout;
879 8         68 close $stderr;
880              
881             #-- fetch zombie
882 8         119 waitpid $pid, 0;
883 8 50       81 die $output_stderr if $?;
884              
885             #-- build entity for encrypted version
886 8         159 my $signed_entity = MIME::Entity->build( Data => [$output_stdout], );
887              
888             #-- copy all header fields from original entity
889 8         10796 foreach my $tag ( $entity->head->tags ) {
890 56         5255 my @values = $entity->head->get($tag);
891 56         1847 for ( my $i = 0; $i < @values; ++$i ) {
892 56         103 $signed_entity->head->replace( $tag, $values[$i], $i );
893             }
894             }
895              
896             #-- debugging: create file with signed data
897 8 50       810 if ( $self->get_debug ) {
898 0         0 $self->save_debug_file(
899             name => "armor-sign-data.txt",
900             data => \$entity->bodyhandle->as_string,
901             );
902 0         0 $self->save_debug_file(
903             name => "armor-sign-entity.txt",
904             data => \$signed_entity->as_string,
905             );
906             }
907              
908             #-- return the signed entity
909 8         1104 return $signed_entity;
910             }
911              
912             sub armor_encrypt {
913 4     4 1 10 my $self = shift;
914 4         35 my %par = @_;
915 4         17 my ($entity, $recipients) = @par{'entity','recipients'};
916              
917             #-- call armor_sign_encrypt() with no_sign option
918 4         29 return $self->armor_sign_encrypt(
919             entity => $entity,
920             recipients => $recipients,
921             _no_sign => 1,
922             );
923             }
924              
925             sub armor_sign_encrypt {
926 8     8 1 15 my $self = shift;
927 8         51 my %par = @_;
928             my ($key_id, $passphrase, $entity, $recipients, $_no_sign) =
929 8         38 @par{'key_id','passphrase','entity','recipients','_no_sign'};
930              
931             #-- ignore any PIPE signals, in case of gpg exited
932             #-- early before we fed our data into it.
933 8         116 local $SIG{PIPE} = 'IGNORE';
934              
935             #-- we parse gpg's output and rely on english
936 8         92 local $ENV{LC_ALL} = "C";
937              
938             #-- get default key ID and passphrase, if not given
939 8 100       29 if ( not $_no_sign ) {
940 4 50       20 $key_id = $self->get_default_key_id if not defined $key_id;
941 4 50       28 $passphrase = $self->get_default_passphrase
942             if not defined $passphrase;
943              
944             #-- check parameters
945 4 50       19 die "No key_id set" if $key_id eq '';
946 4 50       11 die "No passphrase set" if not defined $passphrase;
947             }
948              
949             #-- check parameters
950 8 50       50 die "Entity has no body" if not $entity->bodyhandle;
951              
952             #-- get a GnuPG::Interface, with ASCII armor enabled
953 8         186 my $gpg = $self->new_gpg_interface(
954             options => {
955             armor => 1,
956             default_key => $key_id,
957             },
958             passphrase => $passphrase,
959             );
960              
961             #-- add recipients, but first extract the mail-adress
962             #-- part, otherwise gpg couldn't find keys for adresses
963             #-- with quoted printable encodings in the name part-
964 8         76 $recipients = $self->extract_mail_address( recipients => $recipients, );
965 8         15 $gpg->options->push_recipients($_) for @{$recipients};
  8         155  
966              
967             #-- add default key to recipients if requested
968 8 50 33     870 $gpg->options->push_recipients( $self->get_default_key_id )
969             if $self->get_default_key_encrypt
970             and $self->get_default_key_id;
971              
972             #-- initialize handles
973 8         80 my $stdin = IO::Handle->new;
974 8         254 my $stdout = IO::Handle->new;
975 8         143 my $stderr = IO::Handle->new;
976 8         441 my $handles = GnuPG::Handles->new(
977             stdin => $stdin,
978             stdout => $stdout,
979             stderr => $stderr,
980             );
981              
982             #-- execute gpg for encryption
983 8         12520 my $pid;
984 8 100       32 if ($_no_sign) {
985 4         24 $pid = $gpg->encrypt( handles => $handles );
986             }
987             else {
988 4         27 $pid = $gpg->sign_and_encrypt( handles => $handles );
989             }
990              
991             #-- put encoded entity data into temporary file
992             #-- (faster than in-memory operation)
993 8         35611 my ( $data_fh, $data_file ) = File::Temp::tempfile();
994 8         5949 unlink $data_file;
995 8         198 $entity->print($data_fh);
996              
997             #-- perform I/O (multiplexed to prevent blocking)
998 8         30483 my ( $output_stdout, $output_stderr ) = ("", "");
999 8         223 $self->perform_multiplexed_gpg_io(
1000             data_fh => $data_fh,
1001             data_canonify => 0,
1002             stdin_fh => $stdin,
1003             stderr_fh => $stderr,
1004             stdout_fh => $stdout,
1005             stderr_sref => \$output_stderr,
1006             stdout_sref => \$output_stdout,
1007             );
1008              
1009             #-- close reader filehandles (stdin was closed
1010             #-- by perform_multiplexed_gpg_io())
1011 8         104 close $stdout;
1012 8         68 close $stderr;
1013              
1014             #-- fetch zombie
1015 8         113 waitpid $pid, 0;
1016 8 50       67 die $output_stderr if $?;
1017              
1018             #-- build entity for encrypted version
1019 8         173 my $encrypted_entity = MIME::Entity->build(
1020             Type => "text/plain",
1021             Encoding => "7bit",
1022             Data => [$output_stdout],
1023             );
1024              
1025             #-- copy header fields from original entity
1026 8         10442 foreach my $tag ( $entity->head->tags ) {
1027 56 100       3395 next if $tag =~ /^content/i;
1028 32         87 my @values = $entity->head->get($tag);
1029 32         1188 for ( my $i = 0; $i < @values; ++$i ) {
1030 32         67 $encrypted_entity->head->replace( $tag, $values[$i], $i );
1031             }
1032             }
1033              
1034             #-- debugging: create file with signed data
1035 8 50       622 if ( $self->get_debug ) {
1036 0         0 $self->save_debug_file(
1037             name => "armor-enc-data.txt",
1038             data => \$entity->bodyhandle->as_string,
1039             );
1040 0         0 $self->save_debug_file(
1041             name => "armor-enc-entity.txt",
1042             data => \$encrypted_entity->as_string,
1043             );
1044             }
1045              
1046             #-- return the signed entity
1047 8         1192 return $encrypted_entity;
1048             }
1049              
1050             sub decrypt {
1051 30     30 1 130 my $self = shift;
1052 30         109 my %par = @_;
1053 30         91 my ($entity, $passphrase) = @par{'entity','passphrase'};
1054              
1055             #-- ignore any PIPE signals, in case of gpg exited
1056             #-- early before we fed our data into it.
1057 30         462 local $SIG{PIPE} = 'IGNORE';
1058              
1059             #-- we parse gpg's output and rely on english
1060 30         290 local $ENV{LC_ALL} = "C";
1061              
1062             #-- get default passphrase, if not given
1063 30 50       287 $passphrase = $self->get_default_passphrase if not defined $passphrase;
1064              
1065             #-- check if the entity is encrypted at all
1066             #-- (dies if not)
1067 30         48 my $encrypted_text;
1068 30         233 my $is_armor = $self->check_encryption(
1069             entity => $entity,
1070             encrypted_text_sref => \$encrypted_text,
1071             );
1072              
1073             #-- get a GnuPG::Interface
1074 30         231 my $gpg = $self->new_gpg_interface( passphrase => $passphrase, );
1075              
1076             #-- initialize handles
1077 30         255 my $stdin = IO::Handle->new;
1078 30         884 my $stdout = IO::Handle->new;
1079 30         508 my $stderr = IO::Handle->new;
1080 30         484 my $status = IO::Handle->new;
1081              
1082 30         1362 my $handles = GnuPG::Handles->new(
1083             stdin => $stdin,
1084             stdout => $stdout,
1085             stderr => $stderr,
1086             status => $status,
1087             );
1088              
1089             #-- start gpg for decryption
1090 30         15753 my $pid = $gpg->decrypt( handles => $handles );
1091              
1092             #-- put encoded entity data into temporary file
1093             #-- (faster than in-memory operation)
1094 30         131969 my ( $data_fh, $data_file ) = File::Temp::tempfile();
1095 30         21501 unlink $data_file;
1096 30         1285 print $data_fh $encrypted_text;
1097              
1098             #-- perform I/O (multiplexed to prevent blocking)
1099 30         439 my ( $output_stdout, $output_stderr, $output_status ) = ( "", "", "" );
1100              
1101 30         668 $self->perform_multiplexed_gpg_io(
1102             data_fh => $data_fh,
1103             data_canonify => 1,
1104             stdin_fh => $stdin,
1105             stderr_fh => $stderr,
1106             stdout_fh => $stdout,
1107             status_fh => $status,
1108             stderr_sref => \$output_stderr,
1109             stdout_sref => \$output_stdout,
1110             status_sref => \$output_status,
1111             );
1112              
1113             #-- close reader filehandles (stdin was closed
1114             #-- by perform_multiplexed_gpg_io())
1115 30         417 close $stdout;
1116 30         297 close $stderr;
1117              
1118             #-- fetch zombie
1119 30         510 waitpid $pid, 0;
1120 30         246 my $rc = $? >> 8;
1121              
1122             #-- don't die here for return values != 0, because
1123             #-- this also happens for encrypted+signed mails,
1124             #-- where the public key is missing for verification
1125             #-- and that's not intended here.
1126              
1127             #-- parse decrypted text
1128 30         930 my $parser = new MIME::Parser;
1129 30         10151 $parser->output_to_core(1);
1130              
1131             # for armor message (which usually contain no MIME entity)
1132             # and if the first line seems to be no header, add an empty
1133             # line at the top, otherwise the first line of a text message
1134             # will be removed by the parser.
1135 30 50 66     723 if ( $is_armor and $output_stdout !~ /^[\w-]+:/ ) {
1136 0         0 $output_stdout = "\n" . $output_stdout;
1137             }
1138              
1139 30         247 my $dec_entity = $parser->parse_data( $output_stdout );
1140              
1141             #-- Add headers from original entity
1142 30 50       704843 if ( $dec_entity->head->as_string eq '' ) {
1143 0         0 $dec_entity->head( $entity->head->dup );
1144             }
1145             else {
1146              
1147             #-- copy header fields from original entity
1148 30         1938 foreach my $tag ( $entity->head->tags ) {
1149 166 100       11793 next if $tag =~ /^content/i;
1150 120         346 my @values = $entity->head->get($tag);
1151 120         4090 for ( my $i = 0; $i < @values; ++$i ) {
1152 120         275 $dec_entity->head->replace( $tag, $values[$i], $i );
1153             }
1154             }
1155             }
1156              
1157             #-- debugging: create file with encrypted data
1158 30 50       2783 if ( $self->get_debug ) {
1159 0         0 $self->save_debug_file(
1160             name => "dec-data.txt",
1161             data => $dec_entity->as_string,
1162             );
1163             }
1164              
1165             #-- fetch information from gpg's stderr output
1166             #-- and construct a Mail::GPG::Result object from it
1167 30         621 my $result = Mail::GPG::Result->new(
1168             mail_gpg => $self,
1169             gpg_stdout => \$output_stdout,
1170             gpg_stderr => \$output_stderr,
1171             gpg_status => \$output_status,
1172             gpg_rc => $rc,
1173             );
1174              
1175             #-- return decrypted entity and result object
1176 30 50       103 return $dec_entity if not wantarray;
1177 30         4875 return ( $dec_entity, $result );
1178             }
1179              
1180             sub verify {
1181 24     24 1 59 my $self = shift;
1182 24         85 my %par = @_;
1183 24         62 my ($entity) = $par{'entity'};
1184              
1185             #-- ignore any PIPE signals, in case of gpg exited
1186             #-- early before we fed our data into it.
1187 24         407 local $SIG{PIPE} = 'IGNORE';
1188              
1189             #-- we parse gpg's output and rely on english
1190 24         249 local $ENV{LC_ALL} = "C";
1191              
1192             #-- check if the entity is signed
1193 24         70 my ( $signed_text, $signature_text );
1194              
1195 24 100       85 if ( $entity->effective_type =~ m!multipart/signed!i ) {
    50          
1196              
1197             #-- is this a valid RFC 3156 multipart/signed entity?
1198 16 50       2767 die "Entity must have two parts"
1199             if $entity->parts != 2;
1200 16 50       159 die "Entity is not OpenPGP signed"
1201             unless $entity->parts(1)->effective_type
1202             =~ m!application/pgp-signature!i;
1203              
1204             #-- hopefully the $entity was parsed with
1205             #-- decode_bodies(0), otherwise this would
1206             #-- return decoded data, but the signature
1207             #-- is calculated on the *encoded* version.
1208 16         1793 $signed_text = $entity->parts(0)->as_string;
1209 16         10387 $signature_text = $entity->parts(1)->body_as_string;
1210              
1211             }
1212             elsif ( $entity->bodyhandle ) {
1213              
1214             #-- probably an ASCII armor signed entity
1215             #-- in that case we need the *decoded* data
1216 8         1192 $signed_text = $entity->bodyhandle->as_string;
1217 8 100       198 die "Entity is not OpenPGP signed"
1218             unless $signed_text =~ /^-----BEGIN PGP SIGNED MESSAGE-----/m;
1219             }
1220             else {
1221 0         0 die "Entity is not multipart/signed and has no body";
1222             }
1223              
1224             #-- get a GnuPG::Interface
1225 22         4402 my $gpg = $self->new_gpg_interface;
1226              
1227             #-- initialize handles
1228 22         105 my $stdin = IO::Handle->new;
1229 22         391 my $stdout = IO::Handle->new;
1230 22         315 my $stderr = IO::Handle->new;
1231 22         297 my $status = IO::Handle->new;
1232              
1233 22         764 my $handles = GnuPG::Handles->new(
1234             stdin => $stdin,
1235             stdout => $stdout,
1236             stderr => $stderr,
1237             status => $status,
1238             );
1239              
1240             #-- distinguish between ascii amor embedded signature
1241             #-- and detached signature (RFC 3156)
1242 22         11770 my ( $pid, $sign_file, $sign_fh );
1243 22 100       58 if ($signature_text) {
1244              
1245             #-- signature is detached, save it to a temp file
1246 16         56 ( $sign_fh, $sign_file ) = File::Temp::tempfile();
1247 16         4113 print $sign_fh $signature_text;
1248 16         353 close $sign_fh;
1249              
1250             #-- pass signature filename to gpg
1251 16         121 $pid = $gpg->verify(
1252             handles => $handles,
1253             command_args => [ $sign_file, "-" ],
1254             );
1255              
1256             }
1257             else {
1258              
1259             #-- ASCII armor message with embedded signature
1260 6         22 $pid = $gpg->verify( handles => $handles, );
1261             }
1262              
1263             #-- put encoded entity data into temporary file
1264             #-- (faster than in-memory operation)
1265 22         83503 my ( $data_fh, $data_file ) = File::Temp::tempfile();
1266 22         14110 unlink $data_file;
1267 22         610 print $data_fh $signed_text;
1268              
1269             #-- perform I/O (multiplexed to prevent blocking)
1270 22         240 my ( $output_stdout, $output_stderr, $output_status ) = ( "", "", "" );
1271 22         447 $self->perform_multiplexed_gpg_io(
1272             data_fh => $data_fh,
1273             data_canonify => 1,
1274             stdin_fh => $stdin,
1275             stderr_fh => $stderr,
1276             stdout_fh => $stdout,
1277             status_fh => $status,
1278             stderr_sref => \$output_stderr,
1279             stdout_sref => \$output_stdout,
1280             status_sref => \$output_status,
1281             );
1282              
1283             #-- close reader filehandles (stdin was closed
1284             #-- by perform_multiplexed_gpg_io())
1285 22         297 close $stdout;
1286 22         200 close $stderr;
1287              
1288             #-- fetch zombie
1289 22         383 waitpid $pid, 0;
1290 22         201 my $rc = $? >> 8;
1291              
1292             #-- remove detached signature file
1293 22 100       857 unlink $sign_file if defined $sign_file;
1294              
1295             #-- debugging: create file with verified data
1296 22 50       251 if ( $self->get_debug ) {
1297 0         0 $self->save_debug_file(
1298             name => "verify-data.txt",
1299             data => \$signed_text,
1300             );
1301             }
1302              
1303             #-- construct a Mail::GPG::Result object from
1304             #-- gpg's stderr output
1305 22         510 my $result = Mail::GPG::Result->new(
1306             mail_gpg => $self,
1307             gpg_stdout => \$output_stdout,
1308             gpg_stderr => \$output_stderr,
1309             gpg_status => \$output_status,
1310             gpg_rc => $rc,
1311             );
1312              
1313             #-- return result object
1314 22         3329 return $result;
1315             }
1316              
1317             sub is_encrypted {
1318 30     30 1 71 my $self = shift;
1319 30         111 my %par = @_;
1320 30         84 my ($entity) = $par{'entity'};
1321              
1322 30 100       177 if ( $entity->effective_type =~ m!multipart/encrypted!i ) {
    50          
1323              
1324             #-- is this a valid RFC 3156 multipart/encrypted entity?
1325 22 50       3695 return 0 if $entity->parts != 2;
1326 22 50       246 return 0
1327             unless $entity->parts(0)->effective_type
1328             =~ m!application/pgp-encrypted!i;
1329              
1330             }
1331             elsif ( $entity->bodyhandle ) {
1332              
1333             #-- probably an ASCII armor encrypted entity
1334             #-- check the decoded body for a PGP message
1335 8 50       1144 return 0
1336             unless $entity->bodyhandle->as_string
1337             =~ /^-----BEGIN PGP MESSAGE-----/m;
1338             }
1339             else {
1340 0         0 return 0;
1341             }
1342              
1343 30         2809 return 1;
1344             }
1345              
1346             sub is_signed {
1347 34     34 1 84 my $self = shift;
1348 34         105 my %par = @_;
1349 34         75 my ($entity) = $par{'entity'};
1350              
1351 34 100       103 if ( $entity->effective_type =~ m!multipart/signed!i ) {
    50          
1352              
1353             #-- is this a valid RFC 3156 multipart/signed entity?
1354 24 50       4339 return 0 if $entity->parts != 2;
1355 24 50       349 return 0
1356             unless $entity->parts(1)->effective_type
1357             =~ m!application/pgp-signature!i;
1358              
1359             }
1360             elsif ( $entity->bodyhandle ) {
1361              
1362             #-- probably an ASCII armor signed entity,
1363             #-- check the decoded body for a PGP message
1364 10 50       1508 return 0
1365             unless $entity->bodyhandle->as_string
1366             =~ /^-----BEGIN PGP SIGNED MESSAGE-----/m;
1367             }
1368             else {
1369 0         0 return 0;
1370             }
1371              
1372 34         3201 return 1;
1373             }
1374              
1375             sub is_signed_quick {
1376 20     20 1 50 my $self = shift;
1377 20         112 my %par = @_;
1378 20         59 my ($mail_fh, $mail_sref) = @par{'mail_fh', 'mail_sref'};
1379              
1380 20 50 50     153 croak "Specify mail_fh xor mail_sref"
1381             unless $mail_fh xor $mail_sref;
1382              
1383 20 100       57 if ( defined $mail_fh ) {
    50          
1384              
1385             #-- rewind filehandle
1386 10         246 seek( $mail_fh, 0, 0 );
1387              
1388             #-- read filehandle and do rough checks
1389 10         27 local ($_);
1390 10         16 my $is_signed = 0;
1391 10         125 while (<$mail_fh>) {
1392 82 100       193 if (m!application/pgp-signature!i) {
1393 8         13 $is_signed = 1;
1394 8         31 last;
1395             }
1396 74 100       168 if (/^-----BEGIN PGP SIGNED MESSAGE-----/) {
1397 2         5 $is_signed = 1;
1398 2         7 last;
1399             }
1400             }
1401              
1402             #-- rewind filehandle again
1403 10         94 seek( $mail_fh, 0, 0 );
1404              
1405             #-- return sign status
1406 10         71 return $is_signed;
1407              
1408             }
1409             elsif ( defined $mail_sref ) {
1410              
1411             #-- looks like a RFC 3156 multipart/signed entity?
1412 10 100       140 return 1 if $$mail_sref =~ m!application/pgp-signature!i;
1413              
1414             #-- or ASCII armor signed?
1415 2 50       35 return 1 if $$mail_sref =~ m!^-----BEGIN PGP SIGNED MESSAGE-----!m;
1416              
1417             #-- not signed at all
1418 0         0 return 0,;
1419             }
1420              
1421 0         0 return 1;
1422             }
1423              
1424             sub get_decrypt_key {
1425 30     30 1 74 my $self = shift;
1426 30         87 my %par = @_;
1427 30         73 my ($entity) = $par{'entity'};
1428              
1429             #-- ignore any PIPE signals, in case of gpg exited
1430             #-- early before we fed our data into it.
1431 30         498 local $SIG{PIPE} = 'IGNORE';
1432              
1433             #-- we parse gpg's output and rely on english
1434 30         290 local $ENV{LC_ALL} = "C";
1435              
1436             #-- check if the entity is encrypted at all
1437             #-- (dies if not)
1438 30         79 my $encrypted_text;
1439 30         148 my $is_armor = $self->check_encryption(
1440             entity => $entity,
1441             encrypted_text_sref => \$encrypted_text,
1442             );
1443              
1444             #-- get a GnuPG::Interface
1445 30         99 my $gpg = $self->new_gpg_interface;
1446              
1447             #-- initialize handles
1448 30         150 my $stdin = IO::Handle->new;
1449 30         583 my $stdout = IO::Handle->new;
1450 30         521 my $stderr = IO::Handle->new;
1451 30         1431 my $handles = GnuPG::Handles->new(
1452             stdin => $stdin,
1453             stdout => $stdout,
1454             stderr => $stderr,
1455             );
1456              
1457             #-- start gpg for decryption
1458 30         14744 my $pid = $gpg->wrap_call(
1459             handles => $handles,
1460             commands =>
1461             [ "--decrypt", "--batch", "--list-only", "--status-fd", "1" ],
1462             );
1463              
1464             #-- put encoded entity data into temporary file
1465             #-- (faster than in-memory operation)
1466 30         105511 my ( $data_fh, $data_file ) = File::Temp::tempfile();
1467 30         21741 unlink $data_file;
1468 30         1667 print $data_fh $encrypted_text;
1469              
1470             #-- perform I/O (multiplexed to prevent blocking)
1471 30         591 my ( $output_stdout, $output_stderr ) = ( "", "" );
1472 30         854 $self->perform_multiplexed_gpg_io(
1473             data_fh => $data_fh,
1474             data_canonify => 1,
1475             stdin_fh => $stdin,
1476             stderr_fh => $stderr,
1477             stdout_fh => $stdout,
1478             stderr_sref => \$output_stderr,
1479             stdout_sref => \$output_stdout,
1480             );
1481              
1482             #-- close reader filehandles (stdin was closed
1483             #-- by perform_multiplexed_gpg_io())
1484 30         413 close $stdout;
1485 30         279 close $stderr;
1486              
1487             #-- fetch zombie
1488 30         448 waitpid $pid, 0;
1489 30         251 my $rc = $? >> 8;
1490              
1491             #-- grep ENC_TO and NO_SECKEY items
1492 30         92 my ( @enc_to_keys, %no_sec_keys, $line );
1493 30         395 while ( $output_stdout =~ /^(.*)$/mg ) {
1494 90         231 $line = $1;
1495 90 100       565 push @enc_to_keys, $1 if $line =~ /ENC_TO\s+([^\s]+)/;
1496 90 50       349 $no_sec_keys{$1} = 1 if $line =~ /NO_SECKEY\s+([^\s]+)/;
1497             }
1498              
1499             #-- find first key we have the secret portion of
1500 30         61 my $key_id;
1501 30         180 foreach my $k (@enc_to_keys) {
1502 30 50       113 if ( not exists $no_sec_keys{$k} ) {
1503 30         73 $key_id = $k;
1504 30         55 last;
1505             }
1506             }
1507              
1508             #-- get mail address of this key
1509 30         56 my $key_mail;
1510 30         256 ( $key_id, $key_mail ) = $self->query_keyring( search => $key_id );
1511              
1512 30 50       163 return $key_id if not wantarray;
1513 30         3690 return ( $key_id, $key_mail );
1514             }
1515              
1516             sub extract_mail_address {
1517 30     30 0 78 my $self = shift;
1518 30         105 my %par = @_;
1519 30         75 my ($recipients) = $par{'recipients'};
1520              
1521 30         53 my @recipients;
1522              
1523             my $address;
1524 30         47 foreach my $r ( @{$recipients} ) {
  30         174  
1525 30         703 ($address) = Mail::Address->parse($r);
1526 30 50       8881 push @recipients, $address
1527             ? $address->address
1528             : $r;
1529             }
1530 30         499 return \@recipients;
1531             }
1532              
1533             sub parse {
1534 54     54 1 150 my $thing = shift;
1535 54         391 my %par = @_;
1536 54         200 my ($mail_fh, $mail_sref) = @par{'mail_fh','mail_sref'};
1537              
1538 54 50 25     468 croak "Specify mail_fh xor mail_sref"
1539             unless $mail_fh xor $mail_sref;
1540              
1541 54         355 require MIME::Parser;
1542 54         108 my ( $parser, $entity );
1543              
1544             #-- First parse without body decoding, which is correct
1545             #-- for MIME messages
1546 54         1261 $parser = MIME::Parser->new;
1547 54         18774 $parser->output_to_core(1);
1548 54         1060 $parser->decode_bodies(0);
1549 54 50       688 $entity = $mail_fh
1550             ? $parser->parse($mail_fh)
1551             : $parser->parse_data($$mail_sref);
1552              
1553             #-- Ok, if this is a MIME message
1554 54 100 100     379156 return $entity
1555             if $entity->effective_type eq 'multipart/signed'
1556             or $entity->effective_type eq 'multipart/encrypted';
1557              
1558             #-- Now with body decoding, which is MIME::Parser's default
1559             #-- and correct for OpenPGP armor message. Probably this
1560             #-- isn't an OpenPGP message at all. But also in that case
1561             #-- it's the best to return a decoded entity, as MIME::Parser
1562             #-- usually does.
1563 16 50       3906 seek( $mail_fh, 0, 0 ) if $mail_fh;
1564 16         76 $parser->decode_bodies(1);
1565 16 50       138 $entity = $mail_fh
1566             ? $parser->parse($mail_fh)
1567             : $parser->parse_data($$mail_sref);
1568              
1569 16         33281 return $entity;
1570             }
1571              
1572             sub get_key_trust {
1573 12     12 1 53 my $self = shift;
1574 12         75 my %par = @_;
1575 12         30 my ($key_id) = $par{'key_id'};
1576              
1577             # Suppress warnings about unknown record type 'tru'
1578             # in GnuPG::Interface
1579 12     0   279 local $SIG{__WARN__} = sub {1};
  0         0  
1580              
1581 12         140 my $gpg = $self->new_gpg_interface;
1582 12         93 my @keys = $gpg->get_public_keys($key_id);
1583              
1584 12 50       710891 croak "Request for key ID '$key_id' got multiple result"
1585             if @keys > 1;
1586              
1587 12 50       50 return "" unless $keys[0];
1588 12         257 return $keys[0]->owner_trust;
1589             }
1590              
1591             1;
1592              
1593             __END__