Re: mu-mh questions
Sergey Poznyakoff <[email protected]>
| Newsgroups | gmane.comp.gnu.mailutils.bugs |
|---|---|
| Organization | GNU.org.ua |
| Message-ID | <[email protected]> |
Hi Pierre-Jean, On 08 Jun 2017 I wrote: > I'll return to the problem of using encrypted/signed messages in > my next email. The problem of using PGP messages with MH can be subdivided in three parts: (1) message decryption, (2) message verification, and (3) message signing and encryption. To handle the first part, observe that in MIME messages, encrypted parts have application/octet-stream content type. Find attached the script "mhshow", copy it somewhere to your path and make sure it has executable bit set. Now add the following to your .mh_profile: mhn-show-application/octet-stream: mhshow %F The script uses file(1) to determine whether it has to do with a PGP encrypted message, and calls gpg to decrypt it if so. Whenever you need to read a signed part, run mhn -show [-part N] MSG or "edit mhn -show [-part N] MSG", from the whatnow prompt. Parts (2) and (3) are handled by the script mhgpg, attached herewith. To verify a PGP-signed message, run edit mhgpg To sign the composed message, run edit mhgpg -s If you have several keys, you can select the right one by placing its ID in the gpg-user profile entry. E.g. gpg-user: DE45AF11 or gpg-user: [email protected] Finally, to encrypt the message being composed, do edit mhgpg -e Hope that helps. Regards, Sergey _______________________________________________ Bug-mailutils mailing list [email protected] https://lists.gnu.org/mailman/listinfo/bug-mailutils
mhshow
(text/x-shellscript, 371 B)
#! /bin/sh
# Simple script to show PGP encrypted MIME parts.
# Usage (from .mh_profile):
# mhn-show-application/octet-stream: mhshow %F
type=`file -b ${1:?}`
case $type in
"PGP message")
temp=/tmp/mhshow.$$
trap "rm -f $temp" TERM QUIT
if gpg --decrypt $1 > $temp; then
mhn -show -file $temp || less -c $temp
fi
rm $temp
;;
*)
od -c $1 | less
;;
esac
mhgpg
(text/x-perl, 6.7 KB)
#! /usr/bin/perl use strict; use warnings; use Getopt::Long qw(:config gnu_getopt no_ignore_case require_order); use File::Temp qw(tempfile); use File::Copy; use File::Basename; use Cwd qw(abs_path); use Pod::Man; use Pod::Usage; =head1 NAME mhgpg - wrapper for verifying, signing, and encrypting messages using GPG =head1 SYNOPSIS B<mhgpg> [B<-ves>] [B<--verify>] [B<--encrypt>] [B<--sign>] [B<--> I<GPGOPT>...] I<FILE> B<edit mhgpg> [B<-ves>] [B<--verify>] [B<--encrypt>] [B<--sign>] [B<--> I<GPGOPT>...] =head1 DESCRIPTION Verifies, signs, or encrypting messages from B<MH> using B<GPG>. The script is normally invoked from the B<whatnow> prompt as B<edit mhgpg I<OPTIONS>>. I<OPTIONS> select operation mode. If the B<--> marker is present, all arguments that follow it will be passed to the B<gpg> command line. Another way to pass additional options to B<gpg> is by specifying them in the B<gpg-options> entry in F<~/.mh_profile>. If both methods are used, options from B<gpg-options> precede those from the command line. =head1 OPTIONS =over 4 =item B<-v>, B<--verify> Verify the GPG signature. This is the default. =item B<-s>, B<--sign> Sign the message. The key to sign with can be specified in the B<gpg-user> profile entry. =item B<-e>, B<--encrypt> Encrypt the message. Can be used together with B<--sign>. =back =head1 AUTHOR Sergey Poznyakoff <[email protected]> =cut my @gpg = ( 'gpg' ); my $verify; my $sign; my $encrypt; my $input; my $inputdir; my %profile; my @tempfiles; END { unlink @tempfiles if @tempfiles; } sub readprofile { my $name = $ENV{MH} || "$ENV{HOME}/.mh_profile"; return undef unless -f $name; open(my $fd, '<', $name) or die "can't open profile $name: $!"; my $line; while (<$fd>) { chomp; if (s/^\s+/ /) { $line .= $_; } else { if (defined($line) && $line !~ /^#/ && $line !~ /^$/) { my ($kw, $val) = split /:/, $line, 2; if (defined($kw) && defined($val)) { $profile{$kw} = $val; } } $line = $_; } } if (defined($line) && $line !~ /^#/ && $line !~ /^$/) { my ($kw, $val) = split /:/, $line, 2; if (defined($kw) && defined($val)) { $profile{$kw} = $val; } } close($fd); } sub split_message_parts { my $file = shift; open(my $fd, '<', $file) or die "can't open $file: $!"; # Split message on header and body parts my @headers; my $body; while (<$fd>) { chomp; if (/^(-+)?$/) { local $/ = undef; $body = <$fd>; last; } push @headers, $_; } close $fd; return \@headers unless wantarray; die "malformed input" unless $body; return ( \@headers, $body ); } sub getkeys { my ($opt, $input) = @_; my $fd; open($fd, '-|', "whom $input") or die "whom failed"; my @recp; while (<$fd>) { chomp; s/^\s+//; if (/^-- Network Recipients --$/) { push @recp, 1; } elsif (/^-- .* --$/) { last; } elsif (@recp) { if (s/^(.+?)\s+at\s+(.+)$/$1\@$2/) { push @recp, $_; } } } close $fd; shift @recp; die "no recipients in message" unless @recp; open($fd, '-|', 'gpg', '--list-keys', '--with-colons', @recp) or die "can't list keys"; while (<$fd>) { chomp; if (/^pub:/) { my @a = split /:/; print "signing for $a[9]\n"; push @gpg, $opt, $a[4] } } close $fd; } sub gpg_verify { open(STDIN, '<', $input); exec(@gpg, '--verify'); } sub replace_input { my $msgfile = shift; my $backup = $inputdir . '/' . ',' . basename($input); unlink $backup if -e $backup; rename $input, $backup or die "can't rename $input to $backup: $!"; rename $msgfile, $input or die "can't rename $msgfile to $input: $!"; } sub gpg_sign { my $fd; push @gpg, '--clearsign'; my ($href, $body) = split_message_parts($input); ($fd, my $bodyfile) = tempfile('mhgpgXXXXXX', SUFFIX => '.bod', DIR => $inputdir, UNLINK => 1); print $fd $body; close $fd; my $outfile = $bodyfile . '.out'; push @tempfiles, $outfile; system(@gpg, '--output', $outfile, $bodyfile); if ($? == -1) { die "failed to run gpg: $!"; } elsif ($? & 127) { die "gpg died with signal ".($? & 127); } else { my $code = $? >> 8; if ($code) { die "fatal error"; } } ($fd, my $msgfile) = tempfile('mhgpgXXXXXX', SUFFIX => '.msg', UNLINK => 1, DIR => $inputdir); select((select($fd), $|=1)[0]); print $fd join("\n", @$href); print $fd "\n\n"; copy($outfile, $fd); close($fd); replace_input($msgfile); } sub gpg_encrypt { push @gpg, '--armor'; getkeys('-r', $input); my ($href, $body) = split_message_parts($input); my $ascfd = tempfile(); $^F = 255; my $pid = fork; if ($pid == 0) { open(STDOUT, '<&', $ascfd) or die "can't redirect STDOUT"; open(my $fd, '|-', @gpg) or die "gpg failed"; print $fd $body; close $fd; exit !!$? } my $ret = wait; exit(1) if $ret != $pid or $?; my $bdry = sprintf("%08u-%08u=:%u", int(rand(0xffffffff)), time, $$); my ($fd, $msgfile) = tempfile('mhgpgXXXXXX', SUFFIX => '.msg', DIR => $inputdir, UNLINK => 1); select((select($fd), $|=1)[0]); foreach my $h (@$href) { print $fd "$h\n"; } print $fd "Mime-Version: 1.0\n"; print $fd "Content-Type: multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"$bdry\"\n"; print $fd "\n"; print $fd <<EOT --$bdry Content-Type: application/pgp-encrypted Content-Transfer-Encoding: 7bit Content-Disposition: attachment Version: 1 EOT ; print $fd <<EOT --$bdry Content-Type: application/octet-stream Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="msg.asc" EOT ; seek $ascfd, 0, 0; copy($ascfd, $fd); print $fd "\n"; print $fd "--$bdry--\n"; close($fd); replace_input($msgfile); } GetOptions("sign|s" => \$sign, "encrypt|e" => \$encrypt, "verify|v" => \$verify, "help|?" => sub { pod2usage(-exitstatus => 0, -verbose => 2); } ) or exit(1); pod2usage(-exitstatus => 1, -verbose => 0, -output => \*STDERR) if (($sign||$encrypt) && $verify); $verify = 1 unless ($sign||$encrypt); pod2usage(-exitstatus => 1, -verbose => 0, -output => \*STDERR) unless @ARGV == 1; $input = abs_path(pop(@ARGV)); $inputdir = dirname($input); readprofile; push @gpg, $profile{'gpg-options'} if exists($profile{'gpg-options'}); push @gpg, @ARGV; if ($verify) { gpg_verify; } else { if ($encrypt) { push @gpg, '--encrypt'; gpg_encrypt } elsif ($sign) { push @gpg, '-u', $profile{'gpg-user'} if exists($profile{'gpg-user'}); if ($encrypt) { push @gpg, '--sign', '--encrypt'; gpg_encrypt } else { gpg_sign; } } }