App-Eduard
view release on metacpan or search on metacpan
t/App-Eduard.t
t/data/contains-pubkey
t/data/inline-encrypted
t/data/inline-encrypted-attachment
t/data/inline-signed
t/data/inline-signed-attachment
t/data/inline-signed-encrypted
t/data/mime-encrypted
t/data/mime-signed
t/data/mime-signed-encrypted
t/keydir/pubring.gpg
t/keydir/secring.gpg
share/tmpl/en/encrypt
share/tmpl/en/encrypt_error
share/tmpl/en/error
share/tmpl/en/keys
share/tmpl/en/plain
share/tmpl/en/sign
share/tmpl/en/sign_error
share/tmpl/en/signencrypt
META.yml Module YAML meta-data (added by MakeMaker)
META.json Module JSON meta-data (added by MakeMaker)
make
make test
make install
USAGE
Typical usage of Eduard is as follows:
1. Create a new GPG homedir
$ mkdir /srv/eduard
2. Create a suitable gpg.conf
$ cat > /srv/eduard/gpg.conf <<END
no-greeting
keyserver hkp://keys.gnupg.net
auto-key-locate keyserver
keyserver-options auto-key-retrieve
END
3. Generate a new key
$ gpg --homedir /srv/eduard --gen-key
4. Add an entry to /etc/aliases
$ echo 'eduard: |/path/to/eduard --keydir=/srv/eduard --key=KEYID --from=eduard@hostname.tld' >> /etc/aliases
For multiple language support, add multiple entries to /etc/aliases of the form:
$ echo 'eduard-lang: |/path/to/eduard --keydir=/srv/eduard --key=KEYID --from=eduard@hostname.tld --tmpl=lang' >>/etc/aliases
DEPENDENCIES
This module requires these other modules and libraries:
* Email::Sender
SHA1 016e4f0429e73f85dbfec2d98672d5db3b1a98bb t/App-Eduard.t
SHA1 d80a6379a9fad261587caa3aa631d26f5c552ff3 t/data/contains-pubkey
SHA1 052a9473becfebcf8ae4dedde64c9c839600c0d6 t/data/inline-encrypted
SHA1 2ebeff7efdf1dcd6c48af9078639f492f733a282 t/data/inline-encrypted-attachment
SHA1 7a3473b4be8d31ea46eaf34b28788201472a3846 t/data/inline-signed
SHA1 1c7904ccac2a4fbf6ea9565e91cccc5e5967396d t/data/inline-signed-attachment
SHA1 2bac2e28ed9b53c5496b754d1c3525326f2450c8 t/data/inline-signed-encrypted
SHA1 57bd261514d397946d2dc997fbe3d278dd1a8a0c t/data/mime-encrypted
SHA1 3cfff105472b47414f272be06e0649e60f6f1b0a t/data/mime-signed
SHA1 9c67bed693e85e821047d2dfa5282a1ba28e61c2 t/data/mime-signed-encrypted
SHA1 59c25e0d4a8a1b400d75e8892723b84d88db7954 t/keydir/pubring.gpg
SHA1 d071b8efe7c2d59b6260bb07c194708054e8630b t/keydir/secring.gpg
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAEBCgAGBQJUQs+2AAoJEMoENb5ewbNi6xkQAJaJOxPfbNfQamzcvaHyZ2KF
0oVLJr9X+CKvQx65otlFbGRR2jKWq03+Y8YYDyHFYDvGqekfFptx83ZUwpQkP+3P
O1fjN6ehjts1cANY1/KCYNlLJ9NzK/k8x7fRw7Htm+8G7BbNnaV2SedwlDCuos7I
VvZRRnlDznGE0J6GG4lD31cv5z10gHQyJ08S5qeko8pj1kDd8MK6NMk6gyk82Z5V
HkrKqmroLtF2XtQ28Y8yNF/35JQyhKMWhrLhUaEsgrZMgwF1y0zOjYYVdWFQEBr9
WHtO4PU2su/7p9iC9zcS62SPC+tefhj7qMnYVM0NIooiVrt1KrdSVm9O894KWRuL
qF60v6ltE5djESzt1mNCt+2iOuXp6MddC/6LjFj/erh8Hoa9qGNMgwx9fXvH+xre
Eduard is Ceata's reimplementation of the Edward reply bot referenced in L<https://emailselfdefense.fsf.org/>.
It reads an email from STDIN, checks it for PGP signatures and encryption, then replies appropriately.
=head1 OPTIONS
=over
=item B<--always-trust>, B<--no-always-trust>
If B<--always-trust>, skip key validation and assume that used keys are always fully trusted. See the gpg(1) manpage, option C<--trust-model always> for more information. Defaults to B<--no-always-trust>.
=item B<--debug>, B<--no-debug>
If B<--debug>, output some debugging information on STDERR. Defaults to B<--no-debug>
=item B<--from>=I<address>
Mail address to send messages from.
=item B<--key>=I<keyid>
=item B<--passphrase>=I<passphrase>
Private key passphrase.
=item B<--tmpl-path>=I<path>
Path to the template directory. Users of the default templates can select the language with this argument. Available languages: en. Defaults to B<--tmpl-path=en>.
=item B<--use-agent>, B<--no-use-agent>
If B<--use-agent>, use L<gpg-agent(1)>. Defaults to B<--no-gpg-agent>.
=back
=head1 ENVIRONMENT
Configuration can also be done via the environment. Use 1 for true and 0 for false. Command-line options override environment variables.
=over
=item EDUARD_ALWAYS_TRUST
lib/App/Eduard.pm view on Meta::CPAN
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 find_pgp_part {
my ($ent, $mg) = @_;
lib/App/Eduard.pm view on Meta::CPAN
$msg = $parser->parse_data ($in) if ref $in eq 'SCALAR';
$msg = $parser->parse_open ($in) unless ref $in;
die "Don't know how to parse $in" unless $msg;
if ($msg->mime_type ne 'multipart/signed' && $msg->mime_type ne 'multipart/encrypted') {
# PGP/Inline requires decoding
$parser->decode_bodies(1);
$msg = $parser->parse_data($msg->stringify)
}
my $gpg = mg;
if ($msg->effective_type ne 'multipart/signed' && $msg->effective_type ne 'multipart/encrypted' && !$msg->bodyhandle) {
debug 'This is (probably) a PGP/Inline mail with attachments. Working around...';
$msg = find_pgp_part $msg, $gpg
}
if ($gpg->is_signed($msg)) {
debug 'This mail looks signed';
my ($code, $keyid, $email) = $gpg->verify($msg);
return sign_error => (
message => stringify $gpg->{last_message}) if $code;
return sign => (
keyid => $keyid,
email => $email,
message => stringify $gpg->{last_message});
}
if ($gpg->is_encrypted($msg)) {
debug 'This mail looks encrypted';
my ($code, $keyid, $email) = $gpg->decrypt($msg);
return encrypt_error => (
message => stringify $gpg->{last_message}) if $code;
return encrypt => (
plaintext => stringify $gpg->{plaintext},
decrypted => $gpg->{decrypted},
message => stringify $gpg->{last_message}) unless defined $keyid;
return signencrypt => (
keyid => $keyid,
email => $email,
plaintext => stringify $gpg->{plaintext},
decrypted => $gpg->{decrypted},
message => stringify $gpg->{last_message});
}
debug 'This mail doesn\'t seem to be signed or encrypted';
return 'plain', message => ''
}
sub run {
GetOptions(
'always-trust!' => \$ENV{EDUARD_ALWAYS_TRUST},
'debug!' => \$ENV{EDUARD_DEBUG},
lib/App/Eduard.pm view on Meta::CPAN
}
=head1 DESCRIPTION
Eduard is Ceata's reimplementation of the Edward reply bot referenced in L<https://emailselfdefense.fsf.org/>.
=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
=item A filehandle reference, e.g. C<\*STDIN>.
t/App-Eduard.t view on Meta::CPAN
use constant EMAIL => 'Eduard (Key for testing Eduard) <eduard@ceata.org>';
use File::Copy qw/cp/;
use File::Temp qw/tempdir/;
use Test::More tests => 25;
BEGIN { use_ok('App::Eduard', qw/import_pubkeys process_message/) };
umask 0077; # GPG doesn't like group-/world-readable homedirs
$ENV{EDUARD_DEBUG} = $ENV{TEST_VERBOSE};
$ENV{EDUARD_KEYDIR} = tempdir 'App-Eduard-test.XXXX', TMPDIR => 1, CLEANUP => 1;
cp "t/keydir/$_", $ENV{EDUARD_KEYDIR} for qw/pubring.gpg secring.gpg/;
my $contains_pubkey = App::Eduard::mp->parse_open('t/data/contains-pubkey');
my @keys = import_pubkeys ($contains_pubkey, App::Eduard::mg);
is $keys[0], 'DE12658069C2F09BF996CC855AAF79E969137654', 'import_pubkeys';
my ($tmpl, %params);
sub process {
my ($name, $expected) = @_;
($tmpl, %params) = process_message("t/data/$name");
( run in 0.616 second using v1.01-cache-2.11-cpan-df04353d9ac )