commit 7ca90ee60f156f87b4cc8be76191ef830824eda8 Author: hiromipaw hiro@torproject.org Date: Mon Dec 12 21:48:15 2016 +0100
Add perl script to verify gpg and notify python script --- mailverify.pl | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ notify.py | 71 ++++++++++++++++++++++ 2 files changed, 263 insertions(+)
diff --git a/mailverify.pl b/mailverify.pl new file mode 100644 index 0000000..aa523b9 --- /dev/null +++ b/mailverify.pl @@ -0,0 +1,192 @@ +#!/usr/bin/env perl +# File Name: mailverify +# Maintainer: Moshe Kaminsky kaminsky@math.huji.ac.il +# Original Date: September 30, 2003 +# Last Update: September 30, 2003 +# http://mirror.hamakor.org.il/archives/linux-il/att-5615/mailverify +########################################################### + +use warnings; +use integer; + +BEGIN { + our $VERSION = 0.1; + + # analyze command line + use Getopt::Long qw(:config gnu_getopt); + use Pod::Usage; + + our $opt_help; + our $opt_man; + our $opt_version; + our $Gpg; + our $Tolerant; + GetOptions('gpg=s' => $Gpg, + 'tolerant!' => $Tolerant, + 'help', 'version', 'man'); + pod2usage(1) if $opt_help; + pod2usage(-verbose => 2) if $opt_man; + print "$0 version $VERSION\n" and exit if $opt_version; + $Gpg = '/usr/bin/gpg --batch --verify' unless $Gpg; +} + +use File::Temp qw( tempfile ); + +my $PrevField = ''; + +# process the header +while (<>) { + next if /^From /o; + last if /^$/o; + if (/^([\w-]+): (.*)$/o) { + $Header{$1} = $2; + $PrevField = $1; + } else { + $Header{$PrevField} .= $_; + } +} + +# check that the message is signed +$Str = $Header{'Content-Type'}; +@Parts = split /;\s+/, $Str if $Str; +if (not $Str or $Parts[0] ne 'multipart/signed' ) { + # the message is not multipart/signed, but might still be cleartext + # signed. Depending on --tolerant, we may pass the rest of the message to + # gpg directly + print "Message not signed\n" and exit -1 unless $Tolerant; + open GPG, "|$Gpg" or die "Can't open pipe to gpg ($Gpg): $!"; + print GPG <>; + close GPG; + exit $? >> 8; +} + +# the boundary string signals the boundary between two attachments +$Boundary = $1 if $Parts[3] =~ /^boundary="(.*)"$/o; +# go to the start of the message +while (<>) { + last if $_ eq "--$Boundary\n"; +} + +# read the message, excluding the last (empty) line +while (<>) { + last if $_ eq "--$Boundary\n"; + push @Message, $_; +} +pop @Message; +# read the sig +while (<>) {
+} while (<>); +}; + +# here comes the funny part: replace \n by \r\n +$_ = join '', @Message; +s/(?<!\r)\n/\r\n/g; + +# write everything to files +my ($MsgFH, $MsgFile) = tempfile; +print $MsgFH $_; +my $SigFile = "$MsgFile.asc"; +open SIGFH, ">$SigFile" or die "can't open $SigFile: $!"; +print SIGFH @Sig; +close $MsgFH; +close SIGFH; + +# run gpg +print `$Gpg $SigFile`; + +# clean up +unlink $MsgFile, $SigFile; + +# exit with the status of gpg +exit $? >> 8; + +__DATA__ + +# start of POD + +=head1 NAME + +mailverify - verify the pgp signature of a mime signed mail message + +=head1 SYNOPSIS + +B<mailverify> B<--help>|B<--man>|B<--version> + +B<mailverify> [B<--gpg=I<gpg command>>] [B<--(no)tolerant>] [I<mail file>] + +=head1 OPTIONS + +=over 4 + +=item B<--gpg=I<gpg command>> + +The command to run to do the actual checking. The default is +S<C</usr/local/bin/gpg --batch --verify>>. It is called with one argument, +which is the name of the file containing the signature. If B<--tolerant> is +used, it may also be called with the whole message on the standard input. + +=item B<--(no)tolerant> + +Normally (with B<--notolerant>), if the Content-Type is not +C<multipart/signed>, B<mailverify> decides that the message is not signed, +and exits with status -1. With this switch, the message is passed to I<gpg> +(or whatever was specified with the B<--gpg> option) as is. This way, +clearsigned messages can be verified with the same command. + +=item B<--help> + +Give a short usage message and exit with status 1 + +=item B<--man> + +Give the full description and exit with status 1 + +=item B<--version> + +Print a line with the program name and exit with status 0 + +=back + +=head1 ARGUMENTS + +If an argument is given, it is treated a file containing an e-mail message to +verify, but more common is to read the message from stdin. + +=head1 DESCRIPTION + +This script verifies the pgp signature of files whose signature appears as an +attachment (Content-Type C<multipart/signed>). If B<--tolerant> is used, it +will also verify clearsigned messages. The usage is basically to pipe the +mail to this program. + +=head1 EXIT STATUS + +If the message is not of type C<multipart/signed>, and B<--tolerant> is not +given, the exit status is -1. In any other case, it is the exit status of the +I<gpg> command. + +=head1 SEE ALSO + +I<gpg(1)> + +=head1 BUGS + +Probably plenty, since I don't read any RFCs. Works with all cases I checked, +though. + +=head1 AUTHOR + +Moshe Kaminsky kaminsky@math.huji.ac.il - Copyright (c) 2003 + +=head1 LICENSE + +This program is free software. You may copy or +redistribute it under the same terms as Perl itself. + +=cut diff --git a/notify.py b/notify.py new file mode 100755 index 0000000..f0f384e --- /dev/null +++ b/notify.py @@ -0,0 +1,71 @@ +import sys +import email +import re +from trac.env import Environment +from trac.ticket.model import Ticket +from trac.perm import IPermissionRequestor, PermissionCache, PermissionSystem + +def read_message: + full_msg = sys.stdin.read() + return email.message_from_string("".join(full_msg)) + +def get_message_body(message): + body = "" + if message.is_multipart(): + for payload in message.get_payload(): + # if payload.is_multipart(): ... + body.join(payload.get_payload()) + else: + body.join(message.get_payload()) + + return body + +def verify_user_permissions(env, user, permission): + ps = PermissionSystem(env) + permissions_list = ps.get_user_permissions(user) + return permission_list[permission] + +def find_or_create_ticket(message, env): + # Find the ticket ID if exists + ticket_id = re.search('#(.+?) ', summary) + + # Get the message body + body = get_message_body(message) + reporter = msg['From'] + + if ticket_id: + t_id = ticket_id.group(1) + tkt = Ticket(env, int(t_id)) + if verify_user_permissions(env, reporter, 'TICKET_MODIFY'): + tkt.save_changes(comment=body) + else: + if verify_user_permissions(env, reporter, 'TICKET_CREATE'): + summary = msg['Subject'] + cc_list = msg['Cc'] + component = re.findall(r'[(.*?)]', summary) + tkt = Ticket(env) + tkt['reporter'] = reporter + tkt['summary'] = summary + tkt['cc'] = cc_list + tkt['componet'] = component + tkt['description'] = body + tkt['status'] = 'new' + tkt.insert() + +# Open logs file +f = open('/home/tracadm/log/test.log', 'w') +f.write('PGP verified\n') + +# Define the current trac environment +env = Environment('/current') + +# Build email message +msg = read_message + +# Verify user can create/reply to tickets + +# Find or create ticket +find_or_create_ticket(msg, env) + +# Close log file +f.close
tor-commits@lists.torproject.org