sub-mailhandler.py
| 1 |
#!/usr/bin/env python
|
|---|---|
| 2 |
#
|
| 3 |
# sub-mailhandler.py - frontend for rdm-mailhandler.rb
|
| 4 |
# v.1.0.1
|
| 5 |
#
|
| 6 |
# This script parses the header of a message until it finds a known email;
|
| 7 |
# then use that email to look for a subaddress pattern that provides a
|
| 8 |
# project to rdm-mailhandler.rb
|
| 9 |
#
|
| 10 |
# It works by using an email of the form:
|
| 11 |
# <user+project@example.com>
|
| 12 |
# Most mail servers will deliver this to <user@example.com>, so the + part
|
| 13 |
# is used to determine the project. A default ptoject can be specified.
|
| 14 |
#
|
| 15 |
#
|
| 16 |
# Copyright 2010 Thomas Guyot-Sionnest <tguyot@gmail.com>
|
| 17 |
#
|
| 18 |
# This program is free software: you can redistribute it and/or modify
|
| 19 |
# it under the terms of the GNU General Public License as published by
|
| 20 |
# the Free Software Foundation, either version 3 of the License, or
|
| 21 |
# (at your option) any later version.
|
| 22 |
#
|
| 23 |
# This program is distributed in the hope that it will be useful,
|
| 24 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 25 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 26 |
# GNU General Public License for more details.
|
| 27 |
#
|
| 28 |
# You should have received a copy of the GNU General Public License
|
| 29 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
| 30 |
#
|
| 31 |
|
| 32 |
import sys |
| 33 |
import email |
| 34 |
from optparse import OptionParser |
| 35 |
from subprocess import Popen, PIPE |
| 36 |
|
| 37 |
oparser = OptionParser(usage='%prog -h | -e <email> [ -p <project> ] -- <command-line>',
|
| 38 |
description='''The <command-line> portion is the full
|
| 39 |
rdm-mailhandler.rb command that would normally be executed as the mail |
| 40 |
handler. The full path to the executable is required. This command should not |
| 41 |
include a project; use the build-in --project argument instead.''') |
| 42 |
|
| 43 |
oparser.add_option('-e', '--email', type='string', dest='email', |
| 44 |
help='Known email to look for (i.e. redmine recipient)')
|
| 45 |
oparser.add_option('-p', '--project', type='string', dest='project', |
| 46 |
help='Default project to pass to rdm-mailhandler.rb if there is no subaddress')
|
| 47 |
|
| 48 |
(options, args) = oparser.parse_args() |
| 49 |
|
| 50 |
# Get email split and make sute it's not having weird formatting already
|
| 51 |
if options.email is None: oparser.error('You must provide an email address') |
| 52 |
|
| 53 |
try:
|
| 54 |
esplit = options.email.index('@')
|
| 55 |
except ValueError: |
| 56 |
oparser.error('Not an email address')
|
| 57 |
|
| 58 |
# Split out email to use the individual parts here and later:
|
| 59 |
ename = options.email[:esplit] |
| 60 |
edomain = options.email[esplit+1:]
|
| 61 |
try:
|
| 62 |
ename.index('+')
|
| 63 |
oparser.error('Email provided contains a subaddress already')
|
| 64 |
except ValueError: |
| 65 |
pass
|
| 66 |
try:
|
| 67 |
edomain.index('@')
|
| 68 |
oparser.error('Duplicate @ in email address')
|
| 69 |
except ValueError: |
| 70 |
pass
|
| 71 |
|
| 72 |
# Read-in the headers...
|
| 73 |
buf = ''
|
| 74 |
while True: |
| 75 |
line = sys.stdin.readline() |
| 76 |
buf += line |
| 77 |
if line.strip() == '': break |
| 78 |
|
| 79 |
# parse them...
|
| 80 |
eo = email.parser.HeaderParser() |
| 81 |
msg = eo.parsestr(buf, headersonly=True)
|
| 82 |
|
| 83 |
# Fetch all email addresses out of them...
|
| 84 |
tos = msg.get_all('to', [])
|
| 85 |
ccs = msg.get_all('cc', [])
|
| 86 |
resent_tos = msg.get_all('resent-to', [])
|
| 87 |
resent_ccs = msg.get_all('resent-cc', [])
|
| 88 |
all_recipients = email.utils.getaddresses(tos + ccs + resent_tos + resent_ccs) |
| 89 |
|
| 90 |
# And look for a matching one
|
| 91 |
project = None
|
| 92 |
for n, e in all_recipients: |
| 93 |
split = e.index('@')
|
| 94 |
email = e[:split] |
| 95 |
domain = e[split+1:]
|
| 96 |
subaddr = None
|
| 97 |
|
| 98 |
# If we have a subaddress, get it.
|
| 99 |
try:
|
| 100 |
ssplit = email.rindex('+')
|
| 101 |
subaddr = email[ssplit+1:]
|
| 102 |
email = email[:ssplit] |
| 103 |
except ValueError: |
| 104 |
pass
|
| 105 |
|
| 106 |
# Now check email and subaddress, then break if found
|
| 107 |
if email == ename and domain == edomain: |
| 108 |
if subaddr:
|
| 109 |
project = subaddr |
| 110 |
break
|
| 111 |
|
| 112 |
# Finally, execute the handler and pass it the whole thing
|
| 113 |
if project:
|
| 114 |
projectarg = ['-p', project]
|
| 115 |
elif options.project:
|
| 116 |
projectarg = ['-p', options.project]
|
| 117 |
else:
|
| 118 |
projectarg = [] |
| 119 |
|
| 120 |
handler = Popen(args + projectarg, stdin=PIPE, stdout=sys.stdout, stderr=sys.stderr, shell=False)
|
| 121 |
handler.stdin.write(buf) |
| 122 |
for l in sys.stdin: |
| 123 |
handler.stdin.write(l) |
| 124 |
|
| 125 |
handler.stdin.close() |
| 126 |
handler.wait() |
| 127 |
sys.exit(handler.returncode) |
| 128 |
|