Add pubkey importing support
[app-edwardng.git] / lib / App / EdwardNG.pm
index f6b54a4254cffe720246bd503447c89763f024ac..94a5a200c2ed29c51e7c9f4b74ed2a3a24faa0b6 100644 (file)
@@ -5,12 +5,13 @@ use strict;
 use warnings;
 use parent qw/Exporter/;
 our $VERSION = '0.001';
-our @EXPORT = qw/process_message/;
+our @EXPORT_OK = qw/import_pubkeys process_message/;
 
 use Email::Sender::Simple qw/sendmail/;
 use File::Share qw/dist_file/;
 use File::Slurp qw/read_file/;
 use File::Spec::Functions qw/rel2abs/;
+use IO::Handle;
 use Getopt::Long;
 use MIME::Entity;
 use MIME::Parser;
@@ -31,18 +32,41 @@ sub mg {
                @_);
 }
 
+sub mp {
+       my $parser = MIME::Parser->new;
+       $parser->decode_bodies($_[0] // 0);
+       $parser->output_to_core(1);
+       $parser
+}
+
 sub first_part{
        my ($ent) = @_;
        return first_part ($ent->parts(0)) if $ent->parts;
        stringify [$ent->bodyhandle->as_lines]
 }
 
+sub import_pubkeys {
+       my ($ent, $mg) = @_;
+       my @keys;
+       if ($ent->mime_type eq 'application/pgp-keys') {
+               $ent = mp(1)->parse_data($ent->stringify);
+               my $gpg = GnuPG::Interface->new;
+               $mg->_set_options($gpg);
+               $gpg->options->quiet(1);
+               my ($input, $status) = (IO::Handle->new, IO::Handle->new);
+               my $pid = $gpg->import_keys(handles => GnuPG::Handles->new(stdin => $input, status => $status));
+               my $read = Mail::GnuPG::_communicate([$status], [$input], {$input => $ent->bodyhandle->as_string});
+               push @keys, map { /IMPORT_OK \d+ (\w+)/ } $read->{$status};
+               waitpid $pid, 0
+       }
+       push @keys, import_pubkeys ($_, $mg) for $ent->parts;
+       @keys
+}
+
 sub process_message {
        my ($in) = @_;
        my $msg;
-       my $parser = MIME::Parser->new;
-       $parser->decode_bodies(0);
-       $parser->output_to_core(1);
+       my $parser = mp;
 
        $msg =                      $in   if     ref $in eq 'MIME::Entity';
        $msg = $parser->parse      ($in)  if     ref $in eq 'IO';
@@ -104,11 +128,10 @@ sub run {
        my $tmpl_path = $ENV{EDWARDNG_TMPL_PATH} // 'en';
        open STDERR, '>>', $ENV{EDWARDNG_LOGFILE} if $ENV{EDWARDNG_LOGFILE};
 
-       my $parser = MIME::Parser->new;
-       $parser->decode_bodies(0);
-       $parser->output_to_core(1);
-       my $in = $parser->parse(\*STDIN);
+       my $in = mp->parse(\*STDIN);
        debug 'Received mail from ', $in->get('From');
+       my @keys = import_pubkeys $in, mg;
+       say 'Found keys: ', join ' ', @keys if @keys;
 
        my ($tmpl, %params);
        try {
@@ -121,13 +144,14 @@ sub run {
        $params{plaintext} = first_part $params{decrypted} if $params{decrypted};
 
        my $tt = Template->new(INCLUDE_PATH => rel2abs $tmpl_path, dist_file 'App-EdwardNG', 'tmpl');
-       my $data;
-       $tt->process($tmpl, \%params, \$data);
+       my ($keys, $result) = ('', '');
+       $tt->process('keys', {keys => \@keys}, \$keys) if @keys;
+       $tt->process($tmpl, \%params, \$result);
        my $email = MIME::Entity->build(
                From    => $ENV{EDWARDNG_FROM},
                To      => $in->get('From'),
                Subject => 'Re: ' . $in->get('Subject'),
-               Data    => $data);
+               Data    => $keys.$result);
 
        my $email_unencrypted = $email->dup;
        my $mg = mg always_trust => 1;
@@ -171,7 +195,19 @@ App::EdwardNG - GnuPG email sign/encrypt testing bot
 
 EdwardNG is a reimplementation of the Edward reply bot referenced in L<https://emailselfdefense.fsf.org/>.
 
-This module exports a single function, B<process_message>, which takes a single parameter representing the message. This parameter can be:
+=head1 EXPORTS
+
+None by default.
+
+=head2 B<import_keys>(I<$entity>, I<$gpg>)
+
+Scan a message for PGP public keys, and import them. I<$entity> is a L<MIME::Entity> to scan, I<$gpg> is a L<Mail::GnuPG> instance.
+
+Returns a list of fingerprints of keys found.
+
+=head2 B<process_message>(I<$message>)
+
+Analyze a message, looking for PGP signatures and encryption. I<$message> can be:
 
 =over
 
@@ -181,7 +217,7 @@ This module exports a single function, B<process_message>, which takes a single
 
 =item A scalar which represents a path to a message.
 
-=item A L<MIME::Entity> object
+=item A L<MIME::Entity> object created with decode_bodies(0)
 
 =back
 
This page took 0.011403 seconds and 4 git commands to generate.