File Coverage

blib/lib/Mail/GnuPG.pm
Criterion Covered Total %
statement 277 395 70.1
branch 71 172 41.2
condition 13 33 39.3
subroutine 26 31 83.8
pod 13 14 92.8
total 400 645 62.0


line stmt bran cond sub pod time code
1             package Mail::GnuPG;
2              
3             =head1 NAME
4              
5             Mail::GnuPG - Process email with GPG.
6              
7             =head1 SYNOPSIS
8              
9             use Mail::GnuPG;
10             my $mg = new Mail::GnuPG( key => 'ABCDEFGH' );
11             $ret = $mg->mime_sign( $MIMEObj, 'you@my.dom' );
12              
13             =head1 DESCRIPTION
14              
15             Use GnuPG::Interface to process or create PGP signed or encrypted
16             email.
17              
18             =cut
19              
20 8     8   329620 use 5.006;
  8         32  
  8         318  
21 8     8   74 use strict;
  8         27  
  8         271  
22 8     8   45 use warnings;
  8         15  
  8         482  
23              
24             our $VERSION = '0.21';
25             my $DEBUG = 0;
26              
27 8     8   9344 use GnuPG::Interface;
  8         3328748  
  8         343  
28 8     8   78 use File::Spec;
  8         16  
  8         214  
29 8     8   7867 use File::Temp;
  8         79729  
  8         769  
30 8     8   70 use IO::Handle;
  8         17  
  8         314  
31 8     8   19748 use MIME::Entity;
  8         960793  
  8         333  
32 8     8   22759 use MIME::Parser;
  8         139103  
  8         353  
33 8     8   167 use Mail::Address;
  8         15  
  8         178  
34 8     8   40 use IO::Select;
  8         13  
  8         443  
35 8     8   46 use Errno qw(EPIPE);
  8         14  
  8         60293  
36              
37             =head2 new
38              
39             Create a new Mail::GnuPG instance.
40              
41             Arguments:
42             Paramhash...
43              
44             key => gpg key id
45             keydir => gpg configuration/key directory
46             passphrase => primary key password
47             use_agent => use gpg-agent if non-zero
48             always_trust => always trust a public key
49             # FIXME: we need more things here, maybe primary key id.
50              
51              
52             =cut
53              
54             sub new {
55 5     5 1 3609213 my $proto = shift;
56 5   33     181 my $class = ref($proto) || $proto;
57 5         165 my $self = {
58             key => undef,
59             keydir => undef,
60             passphrase => "",
61             gpg_path => "gpg",
62             use_agent => 0,
63             @_
64             };
65 5         46 $self->{last_message} = [];
66 5         39 $self->{plaintext} = [];
67 5         39 bless ($self, $class);
68 5         36 return $self;
69             }
70              
71             sub _set_options {
72 11     11   40 my ($self,$gnupg) = @_;
73 11         313 $gnupg->options->meta_interactive( 0 );
74 11 50       166404 $gnupg->options->hash_init( armor => 1,
    100          
75             ( defined $self->{keydir} ?
76             (homedir => $self->{keydir}) : () ),
77             ( defined $self->{key} ?
78             ( default_key => $self->{key} ) : () ),
79             # ( defined $self->{passphrase} ?
80             # ( passphrase => $self->{passphrase} ) : () ),
81             );
82 11 50       13653 if ($self->{use_agent}) {
83 0         0 push @{$gnupg->options->extra_args}, '--use-agent';
  0         0  
84             }
85              
86 11 50       86 if (defined $self->{always_trust}) {
87 0         0 $gnupg->options->always_trust($self->{always_trust})
88             }
89 11 50       480 $gnupg->call( $self->{gpg_path} ) if defined $self->{gpg_path};
90             }
91              
92              
93             =head2 decrypt
94              
95             Decrypt an encrypted message
96              
97             Input:
98             MIME::Entity containing email message to decrypt.
99              
100             The message can either be in RFC compliant-ish multipart/encrypted
101             format, or just a single part ascii armored message.
102              
103             Output:
104             On Failure:
105             Exit code of gpg. (0 on success)
106              
107             On Success: (just encrypted)
108             (0, undef, undef)
109              
110             On success: (signed and encrypted)
111             ( 0,
112             keyid, # ABCDDCBA
113             emailaddress # Foo Bar
114             )
115              
116             where the keyid is the key that signed it, and emailaddress is full
117             name and email address of the primary uid
118              
119              
120             $self->{last_message} => any errors from gpg
121             $self->{plaintext} => plaintext output from gpg
122             $self->{decrypted} => parsed output as MIME::Entity
123              
124             =cut
125              
126             sub decrypt {
127 2     2 1 1076 my ($self, $message) = @_;
128 2         14 my $ciphertext = "";
129              
130 2         18 $self->{last_message} = [];
131              
132 2 50 33     71 unless (ref $message && $message->isa("MIME::Entity")) {
133 0         0 die "decrypt only knows about MIME::Entitys right now";
134 0         0 return 255;
135             }
136              
137 2         12 my $armor_message = 0;
138 2 50       22 if ($message->effective_type =~ m!multipart/encrypted!) {
    50          
139 0 0       0 die "multipart/encrypted with more than two parts"
140             if ($message->parts != 2);
141 0 0       0 die "Content-Type not pgp-encrypted"
142             unless $message->parts(0)->effective_type =~
143             m!application/pgp-encrypted!;
144 0         0 $ciphertext = $message->parts(1)->stringify_body;
145             }
146             elsif ($message->bodyhandle->as_string
147             =~ m!^-----BEGIN PGP MESSAGE-----!m ) {
148 2         723 $ciphertext = $message->bodyhandle->as_string;
149 2         21 $armor_message = 1;
150             }
151             else {
152 0         0 die "Unknown Content-Type or no PGP message in body"
153             }
154              
155 2         91 my $gnupg = GnuPG::Interface->new();
156 2         6378 $self->_set_options($gnupg);
157             # how we create some handles to interact with GnuPG
158             # This time we'll catch the standard error for our perusing
159             # as well as passing in the passphrase manually
160             # as well as the status information given by GnuPG
161 2         106 my ( $input, $output, $error, $passphrase_fh, $status_fh )
162             = ( new IO::Handle, new IO::Handle,new IO::Handle,
163             new IO::Handle,new IO::Handle,);
164              
165 2 50       262 my $handles = GnuPG::Handles->new( stdin => $input,
166             stdout => $output,
167             stderr => $error,
168             $self->{use_agent} ? () : (passphrase => $passphrase_fh),
169             status => $status_fh,
170             );
171              
172             # this sets up the communication
173 2         8935 my $pid = $gnupg->decrypt( handles => $handles );
174              
175 2 50       14431 die "NO PASSPHRASE" unless defined $passphrase_fh;
176 2 50       254 my $read = _communicate([$output, $error, $status_fh],
    50          
177             [$input, $self->{use_agent} ? () : $passphrase_fh],
178             { $input => $ciphertext,
179             $self->{use_agent} ? () : ($passphrase_fh => $self->{passphrase})}
180             );
181              
182 2         51305 my @plaintext = split(/^/m, $read->{$output});
183 2         4660 my @error_output = split(/^/m, $read->{$error});
184 2         33 my @status_info = split(/^/m, $read->{$status_fh});
185              
186 2         80 waitpid $pid, 0;
187 2         26 my $return = $?;
188 2 50       19 $return = 0 if $return == -1;
189              
190 2         9 my $exit_value = $return >> 8;
191            
192              
193              
194 2         21 $self->{last_message} = \@error_output;
195 2         12 $self->{plaintext} = \@plaintext;
196              
197 2         73 my $parser = new MIME::Parser;
198 2         746 $parser->output_to_core(1);
199              
200             # for armor message (which usually contain no MIME entity)
201             # and if the first line seems to be no header, add an empty
202             # line at the top, otherwise the first line of a text message
203             # will be removed by the parser.
204 2 50 33     205 if ( $armor_message and $plaintext[0] and $plaintext[0] !~ /^[\w-]+:/ ) {
      33        
205 2         2524 unshift @plaintext, "\n";
206             }
207              
208 2         34 my $entity = $parser->parse_data(\@plaintext);
209 2         54305 $self->{decrypted} = $entity;
210              
211 2 50       42 return $exit_value if $exit_value; # failure
212              
213             # if the message was signed and encrypted, extract the signature
214             # information and return it. In some theory or another, you can't
215             # trust an unsigned encrypted message is from who it says signed it.
216             # (Although I think it would have to go hand in hand at some point.)
217              
218 2         10 my $result = join "", @error_output;
219 2         30 my ($keyid, $pemail) = key_and_uid_from_status(@status_info);
220              
221 2         1488 return ($exit_value,$keyid,$pemail);
222              
223             }
224              
225             =head2 get_decrypt_key
226              
227             determines the decryption key (and corresponding mail) of a message
228              
229             Input:
230             MIME::Entity containing email message to analyze.
231              
232             The message can either be in RFC compliant-ish multipart/signed
233             format, or just a single part ascii armored message.
234              
235             Output:
236             $key -- decryption key
237             $mail -- corresponding mail address
238              
239             =cut
240              
241             sub get_decrypt_key {
242 0     0 1 0 my ($self, $message) = @_;
243              
244 0 0 0     0 unless (ref $message && $message->isa("MIME::Entity")) {
245 0         0 die "decrypt only knows about MIME::Entitys right now";
246             }
247              
248 0         0 my $ciphertext;
249              
250 0 0       0 if ($message->effective_type =~ m!multipart/encrypted!) {
    0          
251 0 0       0 die "multipart/encrypted with more than two parts"
252             if ($message->parts != 2);
253 0 0       0 die "Content-Type not pgp-encrypted"
254             unless $message->parts(0)->effective_type =~
255             m!application/pgp-encrypted!;
256 0         0 $ciphertext = $message->parts(1)->stringify_body;
257             }
258             elsif ($message->bodyhandle->as_string
259             =~ m!^-----BEGIN PGP MESSAGE-----!m ) {
260 0         0 $ciphertext = $message->bodyhandle->as_string;
261             }
262             else {
263 0         0 die "Unknown Content-Type or no PGP message in body"
264             }
265              
266 0         0 my $gnupg = GnuPG::Interface->new();
267 0         0 $gnupg->options->batch(1);
268 0         0 $gnupg->options->status_fd(1);
269 0         0 push @{$gnupg->options->extra_args}, '--list-only';
  0         0  
270              
271             # how we create some handles to interact with GnuPG
272             # This time we'll catch the standard error for our perusing
273             # as well as passing in the passphrase manually
274             # as well as the status information given by GnuPG
275 0         0 my ( $input, $output, $stderr )
276             = ( new IO::Handle, new IO::Handle, new IO::Handle );
277              
278 0         0 my $handles = GnuPG::Handles->new( stdin => $input,
279             stdout => $output,
280             stderr => $stderr,
281             );
282              
283             # this sets up the communication
284 0         0 my $pid = $gnupg->wrap_call(
285             handles => $handles,
286             commands => [ "--decrypt" ],
287             command_args => [ ],
288             );
289              
290 0         0 my $read = _communicate([$output], [$input], { $input => $ciphertext });
291              
292             # reading the output
293 0         0 my @result = split(/^/m, $read->{$output});
294              
295             # clean up the finished GnuPG process
296 0         0 waitpid $pid, 0;
297 0         0 my $return = $?;
298 0 0       0 $return = 0 if $return == -1;
299              
300 0         0 my $exit_value = $return >> 8;
301            
302              
303              
304             # set last_message
305 0         0 $self->{last_message} = \@result;
306              
307             # grep ENC_TO and NO_SECKEY items
308 0         0 my (@enc_to_keys, %no_sec_keys);
309 0         0 for ( @result ) {
310 0 0       0 push @enc_to_keys, $1 if /ENC_TO\s+([^\s]+)/;
311 0 0       0 $no_sec_keys{$1} = 1 if /NO_SECKEY\s+([^\s]+)/;
312             }
313              
314             # find first key we have the secret portion of
315 0         0 my $key;
316 0         0 foreach my $k ( @enc_to_keys ) {
317 0 0       0 if ( not exists $no_sec_keys{$k} ) {
318 0         0 $key = $k;
319 0         0 last;
320             }
321             }
322              
323 0 0       0 return if not $key;
324              
325             # get mail address of this key
326 0 0       0 die "Invalid Key Format: $key" unless $key =~ /^[0-9A-F]+$/i;
327 0         0 my $cmd = $self->{gpg_path} . " --with-colons --list-keys $key 2>&1";
328 0         0 my $gpg_out = qx[ $cmd ];
329             ## FIXME: this should probably use open| instead.
330 0 0 0     0 die "Couldn't find key $key in keyring" if $gpg_out !~ /\S/ or $?;
331 0         0 my $mail = (split(":", $gpg_out))[9];
332              
333 0         0 return ($mail, $key);
334             }
335              
336             =head2 verify
337              
338             verify a signed message
339              
340             Input:
341             MIME::Entity containing email message to verify.
342              
343             The message can either be in RFC compliant-ish multipart/signed
344             format, or just a single part ascii armored message.
345              
346             Note that MIME-encoded data should be supplied unmodified inside
347             the MIME::Entity input message, otherwise the signature will be
348             broken. Since MIME-tools version 5.419, this can be achieved with
349             the C method of MIME::Parser. See the MIME::Parser
350             documentation for more information.
351              
352             Output:
353             On error:
354             Exit code of gpg. (0 on success)
355             On success
356             ( 0,
357             keyid, # ABCDDCBA
358             emailaddress # Foo Bar
359             )
360              
361             where the keyid is the key that signed it, and emailaddress is full
362             name and email address of the primary uid. The email/uid is UTF8
363             encoded, as output by GPG.
364              
365             $self->{last_message} => any errors from gpg
366              
367             =cut
368              
369             # Verify RFC2015/RFC3156 email
370             sub verify {
371 4     4 1 823 my ($self, $message) = @_;
372              
373 4         21 my $ciphertext = "";
374 4         18 my $sigtext = "";
375              
376 4         24 $self->{last_message} = [];
377              
378 4 50 33     113 unless (ref $message && $message->isa("MIME::Entity")) {
379 0         0 die "VerifyMessage only knows about MIME::Entitys right now";
380 0         0 return 255;
381             }
382              
383 4 100 33     37 if ($message->effective_type =~ m!multipart/signed!) {
    50          
384 2 50       432 die "multipart/signed with more than two parts"
385             if ($message->parts != 2);
386 2 50       28 die "Content-Type not pgp-signed"
387             unless $message->parts(1)->effective_type =~
388             m!application/pgp-signature!;
389 2         272 $ciphertext = $message->parts(0)->as_string;
390 2         1914 $sigtext = $message->parts(1)->stringify_body;
391             }
392             elsif ( $message->bodyhandle and $message->bodyhandle->as_string
393             =~ m!^-----BEGIN PGP SIGNED MESSAGE-----!m ) {
394             # don't use not $message->body_as_string here, because
395             # the body isn't decoded in this case!!!
396             # (which is evil for quoted-printable transfer encoding)
397             # also the headers and stuff are not needed here
398 2         609 $ciphertext = undef;
399 2         14 $sigtext = $message->bodyhandle->as_string; # well, actually both
400             }
401             else {
402 0         0 die "Unknown Content-Type or no PGP message in body"
403             }
404              
405 4         3245 my $gnupg = GnuPG::Interface->new();
406 4         12454 $self->_set_options($gnupg);
407             # how we create some handles to interact with GnuPG
408 4         113 my $input = IO::Handle->new();
409 4         123 my $error = IO::Handle->new();
410 4         79 my $status_fh = IO::Handle->new();
411              
412 4         177 my $handles = GnuPG::Handles->new( stderr => $error,
413             stdin => $input,
414             status => $status_fh );
415              
416 4         23809 my ($sigfh, $sigfile)
417             = File::Temp::tempfile('mgsXXXXXXXX',
418             DIR => File::Spec->tmpdir,
419             UNLINK => 1,
420             );
421 4         4042 print $sigfh $sigtext;
422 4         238 close($sigfh);
423              
424 4         80 my ($datafh, $datafile) =
425             File::Temp::tempfile('mgdXXXXXX',
426             DIR => File::Spec->tmpdir,
427             UNLINK => 1,
428             );
429              
430             # according to RFC3156 all line endings MUST be CR/LF
431 4 100       1601 if ( defined $ciphertext ) {
432 2         16355 $ciphertext =~ s/\x0A/\x0D\x0A/g;
433 2         55252 $ciphertext =~ s/\x0D+/\x0D/g;
434             }
435              
436             # Read the (unencoded) body data:
437             # as_string includes the header portion
438 4 100       1328 print $datafh $ciphertext if $ciphertext;
439 4         108 close($datafh);
440              
441 4 100       67 my $pid = $gnupg->verify( handles => $handles,
442             command_args => ( $ciphertext ?
443             ["$sigfile", "$datafile"] :
444             "$sigfile" ),
445             );
446              
447 4         29677 my $read = _communicate([$error,$status_fh], [$input], {$input => ''});
448              
449 4         77 my @result = split(/^/m, $read->{$error});
450 4         71 my @status_info = split(/^/m, $read->{$status_fh});
451              
452 4         1469 unlink $sigfile, $datafile;
453              
454 4         97 waitpid $pid, 0;
455 4         51 my $return = $?;
456 4 50       54 $return = 0 if $return == -1;
457              
458 4         17 my $exit_value = $return >> 8;
459              
460 4         47 $self->{last_message} = [@result];
461              
462 4 50       42 return $exit_value if $exit_value; # failure
463              
464 4         21 my $result = join "", @result;
465              
466 4         56 my ($keyid, $pemail) = key_and_uid_from_status(@status_info);
467              
468 4         740 return ($exit_value,$keyid,$pemail);
469              
470             }
471              
472             sub key_and_uid_from_status {
473              
474 6     6 0 38 my @status_info = @_;
475              
476 6         55 chomp(@status_info);
477              
478 6         45 my ($keyid) = grep { s/^\[GNUPG:\] VALIDSIG \S+(\S{8}) .*$/$1/; } @status_info;
  62         202  
479              
480             # FIXME: we should really distinguish between GOOD and the others
481             # but this will change the existing behaviour
482              
483 6         17 my ($pemail) = grep { s/^\[GNUPG:\] (GOODSIG|EXPKEYSIG|REVKEYSIG) \S+ (.*)$/$2/; } @status_info;
  62         229  
484              
485 6         43 return ($keyid,$pemail);
486             }
487              
488             # Should this go elsewhere? The Key handling stuff doesn't seem to
489             # make sense in a Mail:: module.
490             my %key_cache;
491             my $key_cache_age = 0;
492             my $key_cache_expire = 60*60*30; # 30 minutes
493              
494             sub _rebuild_key_cache {
495 2     2   7 my $self = shift;
496 2         8 local $_;
497 2         12 %key_cache = ();
498 2         58 my $gnupg = GnuPG::Interface->new();
499 2         11365 $self->_set_options($gnupg);
500 2         67 my @keys = $gnupg->get_public_keys();
501 2         381790 foreach my $key (@keys) {
502 2         29 foreach my $uid ($key->user_ids) {
503             # M::A may not parse the gpg stuff properly. Cross fingers
504 2         72 my ($a) = Mail::Address->parse($uid->as_string); # list context, please
505 2 50       1367 $key_cache{$a->address}=1 if ref $a;
506             }
507             }
508             }
509              
510             =head2 has_public_key
511              
512             Does the keyring have a public key for the specified email address?
513              
514             FIXME: document better. talk about caching. maybe put a better
515             interface in.
516              
517             =cut
518              
519              
520             sub has_public_key {
521 2     2 1 891 my ($self,$address) = @_;
522              
523             # cache aging is disabled until someone has enough time to test this
524 2         10 if (0) {
525             $self->_rebuild_key_cache() unless ($key_cache_age);
526              
527             if ( $key_cache_age && ( time() - $key_cache_expire > $key_cache_age )) {
528             $self->_rebuild_key_cache();
529             }
530             }
531              
532 2         21 $self->_rebuild_key_cache();
533              
534 2 100       278 return 1 if exists $key_cache{$address};
535 1         35 return 0;
536              
537             }
538              
539             =head2 mime_sign
540              
541             sign an email message
542              
543             Input:
544             MIME::Entity containing email message to sign
545              
546             Output:
547             Exit code of gpg. (0 on success)
548              
549             $self->{last_message} => any errors from gpg
550              
551             The provided $entity will be signed. (i.e. it _will_ be modified.)
552              
553             =cut
554              
555              
556             sub mime_sign {
557 1     1 1 17937 my ($self,$entity) = @_;
558              
559 1 50       21 die "Not a mime entity"
560             unless $entity->isa("MIME::Entity");
561              
562 1         14 $entity->make_multipart;
563 1         2406 my $workingentity = $entity;
564 1 50       12 if ($entity->parts > 1) {
565 0         0 $workingentity = MIME::Entity->build(Type => $entity->head->mime_attr("Content-Type"));
566 0         0 $workingentity->add_part($_) for ($entity->parts);
567 0         0 $entity->parts([]);
568 0         0 $entity->add_part($workingentity);
569             }
570              
571 1         50 my $gnupg = GnuPG::Interface->new();
572 1         6928 $self->_set_options( $gnupg );
573 1         46 my ( $input, $output, $error, $passphrase_fh, $status_fh )
574             = ( new IO::Handle, new IO::Handle,new IO::Handle,
575             new IO::Handle,new IO::Handle,);
576 1 50       143 my $handles = GnuPG::Handles->new( stdin => $input,
577             stdout => $output,
578             stderr => $error,
579             $self->{use_agent} ? () : (passphrase => $passphrase_fh),
580             status => $status_fh,
581             );
582 1         10528 my $pid = $gnupg->detach_sign( handles => $handles );
583 1 50       11661 die "NO PASSPHRASE" unless defined $passphrase_fh;
584              
585             # this passes in the plaintext
586 1         40 my $plaintext;
587 1 50       33 if ($workingentity eq $entity) {
588 1         57 $plaintext = $entity->parts(0)->as_string;
589             } else {
590 0         0 $plaintext = $workingentity->as_string;
591             }
592              
593             # according to RFC3156 all line endings MUST be CR/LF
594 1         24324 $plaintext =~ s/\x0A/\x0D\x0A/g;
595 1         52021 $plaintext =~ s/\x0D+/\x0D/g;
596              
597             # DEBUG:
598             # print "SIGNING THIS STRING ----->\n";
599             # $plaintext =~ s/\n/-\n/gs;
600             # warn("SIGNING:\n$plaintext<<<");
601             # warn($entity->as_string);
602             # print STDERR $plaintext;
603             # print "<----\n";
604 1 50       617 my $read = _communicate([$output, $error, $status_fh],
    50          
605             [$input, $self->{use_agent} ? () : ($passphrase_fh)],
606             { $input => $plaintext,
607             $self->{use_agent} ? () : ($passphrase_fh => $self->{passphrase})}
608             );
609              
610 1         91 my @signature = split(/^/m, $read->{$output});
611 1         8 my @error_output = split(/^/m, $read->{$error});
612 1         6 my @status_info = split(/^/m, $read->{$status_fh});
613              
614 1         33 waitpid $pid, 0;
615 1         7 my $return = $?;
616 1 50       5 $return = 0 if $return == -1;
617              
618 1         3 my $exit_value = $return >> 8;
619              
620              
621 1         4 $self->{last_message} = \@error_output;
622              
623 1         18 $entity->attach( Type => "application/pgp-signature",
624             Disposition => "inline",
625             Data => [@signature],
626             Encoding => "7bit");
627              
628 1         1410 $entity->head->mime_attr("Content-Type","multipart/signed");
629 1         256 $entity->head->mime_attr("Content-Type.protocol","application/pgp-signature");
630             # $entity->head->mime_attr("Content-Type.micalg","pgp-md5");
631             # Richard Hirner notes that Thunderbird/Enigmail really wants a micalg
632             # of pgp-sha1 (which will be GPG version dependent.. older versions
633             # used md5. For now, until we can detect which type was used, the end
634             # user should read the source code, notice this comment, and insert
635             # the appropriate value themselves.
636              
637 1         532 return $exit_value;
638             }
639              
640             =head2 clear_sign
641              
642             clearsign the body of an email message
643              
644             Input:
645             MIME::Entity containing email message to sign.
646             This entity MUST have a body.
647              
648             Output:
649             Exit code of gpg. (0 on success)
650              
651             $self->{last_message} => any errors from gpg
652              
653             The provided $entity will be signed. (i.e. it _will_ be modified.)
654              
655             =cut
656              
657             sub clear_sign {
658 1     1 1 660 my ($self, $entity) = @_;
659            
660 1 50       15 die "Not a mime entity"
661             unless $entity->isa("MIME::Entity");
662              
663 1         8 my $body = $entity->bodyhandle;
664            
665 1 50       11 die "Message has no body"
666             unless defined $body;
667              
668 1         239 my $gnupg = GnuPG::Interface->new();
669 1         288 $self->_set_options( $gnupg );
670 1         57 $gnupg->passphrase ( $self->{passphrase} );
671              
672 1         38 my ( $input, $output, $error )
673             = ( new IO::Handle, new IO::Handle, new IO::Handle);
674              
675 1         101 my $handles = GnuPG::Handles->new(
676             stdin => $input,
677             stdout => $output,
678             stderr => $error,
679             );
680              
681 1         768 my $pid = $gnupg->clearsign ( handles => $handles );
682              
683 1         9368 my $plaintext = $body->as_string;
684              
685 1         18735 $plaintext =~ s/\x0A/\x0D\x0A/g;
686 1         56202 $plaintext =~ s/\x0D+/\x0D/g;
687              
688 1         350 my $read = _communicate([$output, $error], [$input], { $input => $plaintext });
689            
690 1         38865 my @ciphertext = split(/^/m, $read->{$output});
691 1         3251 my @error_output = split(/^/m, $read->{$error});
692            
693 1         64 waitpid $pid, 0;
694 1         16 my $return = $?;
695 1 50       20 $return = 0 if $return == -1;
696              
697 1         8 my $exit_value = $return >> 8;
698            
699 1         13 $self->{last_message} = [@error_output];
700              
701 1 50       32 my $io = $body->open ("w") or die "can't open entity body";
702 1         7770 $io->print (join('',@ciphertext));
703 1         500 $io->close;
704              
705 1         8153 return $exit_value;
706             }
707              
708              
709             =head2 ascii_encrypt
710              
711             encrypt an email message body using ascii armor
712              
713             Input:
714             MIME::Entity containing email message to encrypt.
715             This entity MUST have a body.
716              
717             list of recipients
718              
719             Output:
720             Exit code of gpg. (0 on success)
721              
722             $self->{last_message} => any errors from gpg
723              
724             The provided $entity will be encrypted. (i.e. it _will_ be modified.)
725              
726             =head2 ascii_signencrypt
727              
728             encrypt and sign an email message body using ascii armor
729              
730             Input:
731             MIME::Entity containing email message to encrypt.
732             This entity MUST have a body.
733              
734             list of recipients
735              
736             Output:
737             Exit code of gpg. (0 on success)
738              
739             $self->{last_message} => any errors from gpg
740              
741             The provided $entity will be encrypted. (i.e. it _will_ be modified.)
742              
743             =cut
744              
745             sub ascii_encrypt {
746 1     1 1 939 my ($self, $entity, @recipients) = @_;
747 1         7 $self->_ascii_encrypt($entity, 0, @recipients);
748             }
749              
750             sub ascii_signencrypt {
751 0     0 1 0 my ($self, $entity, @recipients) = @_;
752 0         0 $self->_ascii_encrypt($entity, 1, @recipients);
753             }
754              
755             sub _ascii_encrypt {
756 1     1   3 my ($self, $entity, $sign, @recipients) = @_;
757            
758 1 50       13 die "Not a mime entity"
759             unless $entity->isa("MIME::Entity");
760              
761 1         4 my $body = $entity->bodyhandle;
762            
763 1 50       10 die "Message has no body"
764             unless defined $body;
765              
766 1         11 my $plaintext = $body->as_string;
767              
768 1         41 my $gnupg = GnuPG::Interface->new();
769 1         165 $self->_set_options( $gnupg );
770 1         69 $gnupg->passphrase ( $self->{passphrase} );
771 1         47 $gnupg->options->push_recipients( $_ ) for @recipients;
772              
773 1         1400 my ( $input, $output, $error )
774             = ( new IO::Handle, new IO::Handle, new IO::Handle);
775              
776 1         85 my $handles = GnuPG::Handles->new(
777             stdin => $input,
778             stdout => $output,
779             stderr => $error,
780             );
781              
782 1         593 my $pid = do {
783 1 50       10 if ( $sign ) {
784 0         0 $gnupg->sign_and_encrypt ( handles => $handles );
785             } else {
786 1         9 $gnupg->encrypt ( handles => $handles );
787             }
788             };
789              
790 1         6702 my $read = _communicate([$output, $error], [$input], { $input => $plaintext });
791            
792 1         24 my @ciphertext = split(/^/m, $read->{$output});
793 1         10 my @error_output = split(/^/m, $read->{$error});
794            
795 1         21 waitpid $pid, 0;
796 1         20 my $return = $?;
797 1 50       11 $return = 0 if $return == -1;
798              
799 1         3 my $exit_value = $return >> 8;
800            
801              
802 1         9 $self->{last_message} = [@error_output];
803              
804 1 50       29 my $io = $body->open ("w") or die "can't open entity body";
805 1         228 $io->print (join('',@ciphertext));
806 1         26 $io->close;
807              
808 1         126 return $exit_value;
809             }
810              
811             =head2 mime_encrypt
812              
813             encrypt an email message
814              
815             Input:
816             MIME::Entity containing email message to encrypt
817             list of email addresses to sign to
818              
819             Output:
820             Exit code of gpg. (0 on success)
821              
822             $self->{last_message} => any errors from gpg
823              
824             The provided $entity will be encrypted. (i.e. it _will_ be modified.)
825              
826             =head2 mime_signencrypt
827              
828             sign and encrypt an email message
829              
830             Input:
831             MIME::Entity containing email message to sign encrypt
832             list of email addresses to sign to
833              
834             Output:
835             Exit code of gpg. (0 on success)
836              
837             $self->{last_message} => any errors from gpg
838              
839             The provided $entity will be encrypted. (i.e. it _will_ be modified.)
840              
841             =cut
842              
843             sub mime_encrypt {
844 0     0 1 0 my $self = shift;
845 0         0 $self->_mime_encrypt(0,@_);
846             }
847              
848             sub mime_signencrypt {
849 0     0 1 0 my $self = shift;
850 0         0 $self->_mime_encrypt(1,@_);
851             }
852              
853             sub _mime_encrypt {
854 0     0   0 my ($self,$sign,$entity,@recipients) = @_;
855              
856 0 0       0 die "Not a mime entity"
857             unless $entity->isa("MIME::Entity");
858              
859 0         0 my $workingentity = $entity;
860 0         0 $entity->make_multipart;
861 0 0       0 if ($entity->parts > 1) {
862 0         0 $workingentity = MIME::Entity->build(Type => $entity->head->mime_attr("Content-Type"));
863 0         0 $workingentity->add_part($_) for ($entity->parts);
864 0         0 $entity->parts([]);
865 0         0 $entity->add_part($workingentity);
866             }
867              
868 0         0 my $gnupg = GnuPG::Interface->new();
869              
870 0         0 $gnupg->options->push_recipients( $_ ) for @recipients;
871 0         0 $self->_set_options($gnupg);
872 0         0 my ( $input, $output, $error, $passphrase_fh, $status_fh )
873             = ( new IO::Handle, new IO::Handle,new IO::Handle,
874             new IO::Handle,new IO::Handle,);
875 0 0       0 my $handles = GnuPG::Handles->new( stdin => $input,
876             stdout => $output,
877             stderr => $error,
878             $self->{use_agent} ? () : (passphrase => $passphrase_fh),
879             status => $status_fh,
880             );
881              
882 0         0 my $pid = do {
883 0 0       0 if ($sign) {
884 0         0 $gnupg->sign_and_encrypt( handles => $handles );
885             } else {
886 0         0 $gnupg->encrypt( handles => $handles );
887             }
888             };
889              
890             # this passes in the plaintext
891 0         0 my $plaintext;
892 0 0       0 if ($workingentity eq $entity) {
893 0         0 $plaintext= $entity->parts(0)->as_string;
894             } else {
895 0         0 $plaintext=$workingentity->as_string;
896             }
897              
898             # no need to mangle line endings for encryption (RFC3156)
899             # $plaintext =~ s/\n/\x0D\x0A/sg;
900             # should we store this back into the body?
901              
902             # DEBUG:
903             #print "ENCRYPTING THIS STRING ----->\n";
904             # print $plaintext;
905             # print "<----\n";
906              
907 0 0       0 die "NO PASSPHRASE" unless defined $passphrase_fh;
908 0 0       0 my $read = _communicate([$output, $error, $status_fh],
    0          
909             [$input, $self->{use_agent} ? () : ($passphrase_fh)],
910             { $input => $plaintext,
911             $self->{use_agent} ? () : ($passphrase_fh => $self->{passphrase})}
912             );
913              
914 0         0 my @plaintext = split(/^/m, $read->{$output});
915 0         0 my @ciphertext = split(/^/m, $read->{$output});
916 0         0 my @error_output = split(/^/m, $read->{$error});
917 0         0 my @status_info = split(/^/m, $read->{$status_fh});
918              
919 0         0 waitpid $pid, 0;
920 0         0 my $return = $?;
921 0 0       0 $return = 0 if $return == -1;
922              
923 0         0 my $exit_value = $return >> 8;
924            
925              
926            
927            
928 0         0 $self->{last_message} = [@error_output];
929              
930              
931 0         0 $entity->parts([]); # eliminate all parts
932              
933 0         0 $entity->attach(Type => "application/pgp-encrypted",
934             Disposition => "inline",
935             Filename => "msg.asc",
936             Data => ["Version: 1",""],
937             Encoding => "7bit");
938 0         0 $entity->attach(Type => "application/octet-stream",
939             Disposition => "inline",
940             Data => [@ciphertext],
941             Encoding => "7bit");
942              
943 0         0 $entity->head->mime_attr("Content-Type","multipart/encrypted");
944 0         0 $entity->head->mime_attr("Content-Type.protocol","application/pgp-encrypted");
945              
946 0         0 $exit_value;
947             }
948              
949             =head2 is_signed
950              
951             best guess as to whether a message is signed or not (by looking at
952             the mime type and message content)
953              
954             Input:
955             MIME::Entity containing email message to test
956              
957             Output:
958             True or False value
959              
960             =head2 is_encrypted
961              
962             best guess as to whether a message is signed or not (by looking at
963             the mime type and message content)
964              
965             Input:
966             MIME::Entity containing email message to test
967              
968             Output:
969             True or False value
970              
971             =cut
972              
973             sub is_signed {
974 3     3 1 4920 my ($self,$entity) = @_;
975 3 100 100     50 return 1
976             if (($entity->effective_type =~ m!multipart/signed!)
977             ||
978             ($entity->as_string =~ m!^-----BEGIN PGP SIGNED MESSAGE-----!m));
979 1         2799 return 0;
980             }
981              
982             sub is_encrypted {
983 3     3 1 14 my ($self,$entity) = @_;
984 3 100 66     24 return 1
985             if (($entity->effective_type =~ m!multipart/encrypted!)
986             ||
987             ($entity->as_string =~ m!^-----BEGIN PGP MESSAGE-----!m));
988 2         7942 return 0;
989             }
990              
991             # interleave reads and writes
992             # input parameters:
993             # $rhandles - array ref with a list of file handles for reading
994             # $whandles - array ref with a list of file handles for writing
995             # $wbuf_of - hash ref indexed by the stringified handles
996             # containing the data to write
997             # return value:
998             # $rbuf_of - hash ref indexed by the stringified handles
999             # containing the data that has been read
1000             #
1001             # read and write errors due to EPIPE (gpg exit) are skipped silently on the
1002             # assumption that gpg will explain the problem on the error handle
1003             #
1004             # other errors cause a non-fatal warning, processing continues on the rest
1005             # of the file handles
1006             #
1007             # NOTE: all the handles get closed inside this function
1008              
1009             sub _communicate {
1010 9     9   118 my $blocksize = 2048;
1011 9         141 my ($rhandles, $whandles, $wbuf_of) = @_;
1012 9         192 my $rbuf_of = {};
1013              
1014             # the current write offsets, again indexed by the stringified handle
1015 9         30 my $woffset_of;
1016              
1017 9         492 my $reader = IO::Select->new;
1018 9         374 for (@$rhandles) {
1019 21         274 $reader->add($_);
1020 21         1410 $rbuf_of->{$_} = '';
1021             }
1022              
1023 9         77 my $writer = IO::Select->new;
1024 9         178 for (@$whandles) {
1025 12 50       201 die("no data supplied for handle " . fileno($_)) if !exists $wbuf_of->{$_};
1026 12 100       137 if ($wbuf_of->{$_}) {
1027 8         31 $writer->add($_);
1028             } else { # nothing to write
1029 4         83 close $_;
1030             }
1031             }
1032              
1033             # we'll handle EPIPE explicitly below
1034 9         520 local $SIG{PIPE} = 'IGNORE';
1035              
1036 9   66     236 while ($reader->handles || $writer->handles) {
1037 561         18488 my @ready = IO::Select->select($reader, $writer, undef, undef);
1038 561 50       647838 if (!@ready) {
1039 0         0 die("error doing select: $!");
1040             }
1041 561         6130 my ($rready, $wready, $eready) = @ready;
1042 561 50       1863 if (@$eready) {
1043 0         0 die("select returned an unexpected exception handle, this shouldn't happen");
1044             }
1045 561         1016 for my $rhandle (@$rready) {
1046 305         633 my $n = fileno($rhandle);
1047 305         7540 my $count = sysread($rhandle, $rbuf_of->{$rhandle},
1048             $blocksize, length($rbuf_of->{$rhandle}));
1049 305 50       750 warn("read $count bytes from handle $n") if $DEBUG;
1050 305 50       618 if (!defined $count) { # read error
1051 0 0       0 if ($!{EPIPE}) {
1052 0 0       0 warn("read failure (gpg exited?) from handle $n: $!")
1053             if $DEBUG;
1054             } else {
1055 0         0 warn("read failure from handle $n: $!");
1056             }
1057 0         0 $reader->remove($rhandle);
1058 0         0 close $rhandle;
1059 0         0 next;
1060             }
1061 305 100       1151 if ($count == 0) { # EOF
1062 21 50       192 warn("read done from handle $n") if $DEBUG;
1063 21         195 $reader->remove($rhandle);
1064 21         1394 close $rhandle;
1065 21         61 next;
1066             }
1067             }
1068 561         1561 for my $whandle (@$wready) {
1069 397         964 my $n = fileno($whandle);
1070 397 100       1798 $woffset_of->{$whandle} = 0 if !exists $woffset_of->{$whandle};
1071 397         6528 my $count = syswrite($whandle, $wbuf_of->{$whandle},
1072             $blocksize, $woffset_of->{$whandle});
1073 397 50       957 if (!defined $count) {
1074 0 0       0 if ($!{EPIPE}) { # write error
1075 0 0       0 warn("write failure (gpg exited?) from handle $n: $!")
1076             if $DEBUG;
1077             } else {
1078 0         0 warn("write failure from handle $n: $!");
1079             }
1080 0         0 $writer->remove($whandle);
1081 0         0 close $whandle;
1082 0         0 next;
1083             }
1084 397 50       706 warn("wrote $count bytes to handle $n") if $DEBUG;
1085 397         2586 $woffset_of->{$whandle} += $count;
1086 397 100       4007 if ($woffset_of->{$whandle} >= length($wbuf_of->{$whandle})) {
1087 8 50       37 warn("write done to handle $n") if $DEBUG;
1088 8         134 $writer->remove($whandle);
1089 8         5279 close $whandle;
1090 8         446 next;
1091             }
1092             }
1093             }
1094 9         741 return $rbuf_of;
1095             }
1096              
1097             # FIXME: there's no reason why is_signed and is_encrypted couldn't be
1098             # static (class) methods, so maybe we should support that.
1099              
1100             # FIXME: will we properly deal with signed+encrypted stuff? probably not.
1101              
1102             # Autoload methods go after =cut, and are processed by the autosplit program.
1103              
1104             1;
1105             __END__