File Coverage

blib/lib/Mail/GPG.pm
Criterion Covered Total %
statement 592 644 91.9
branch 167 254 65.7
condition 49 81 60.4
subroutine 48 61 78.6
pod 16 43 37.2
total 872 1083 80.5


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