Project

General

Profile

MailhandlerSubAddress » sub-mailhandler.py

Frontend for rdm-mailhandler.rb (v.1.0.2) - Thomas Guyot-Sionnest, 2011-11-10 06:29

 
1
#!/usr/bin/env python
2
#
3
# sub-mailhandler.py - frontend for rdm-mailhandler.rb
4
# v.1.0.2
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-2011 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 email import parser, message, utils
35
from optparse import OptionParser
36
from subprocess import Popen, PIPE
37

    
38
oparser = OptionParser(usage='%prog -h | -e <email> [ -p <project> ] -- <command-line>',
39
                      description='''The <command-line> portion is the full
40
rdm-mailhandler.rb command that would normally be executed as the mail
41
handler. The full path to the executable is required. This command should not
42
include a project; use the build-in --project argument instead.''')
43

    
44
oparser.add_option('-e', '--email', type='string', dest='email',
45
                  help='Known email to look for (i.e. redmine recipient)')
46
oparser.add_option('-p', '--project', type='string', dest='project',
47
                  help='Default project to pass to rdm-mailhandler.rb if there is no subaddress')
48

    
49
(options, args) = oparser.parse_args()
50

    
51
# Get email and make sure it's not having weird formatting already
52
if options.email is None: oparser.error('You must provide an email address')
53

    
54
try:
55
    esplit = options.email.index('@')
56
except ValueError:
57
    oparser.error('Not an email address')
58

    
59
# Split out email to use the individual parts here and later:
60
ename = options.email[:esplit]
61
edomain = options.email[esplit+1:]
62
try:
63
    ename.index('+')
64
    oparser.error('Email provided contains a subaddress already')
65
except ValueError:
66
    pass
67
try:
68
    edomain.index('@')
69
    oparser.error('Duplicate @ in email address')
70
except ValueError:
71
    pass
72

    
73
# Read-in the headers...
74
buf = ''
75
while True:
76
    line = sys.stdin.readline()
77
    buf += line
78
    if line.strip() == '': break
79

    
80
# parse them...
81
eo = email.parser.HeaderParser()
82
msg = eo.parsestr(buf, headersonly=True)
83

    
84
# Fetch all email addresses out of them...
85
tos = msg.get_all('to', [])
86
ccs = msg.get_all('cc', [])
87
delivered_tos = msg.get_all('delivered-to',[])
88
resent_tos = msg.get_all('resent-to', [])
89
resent_ccs = msg.get_all('resent-cc', [])
90
all_recipients = email.utils.getaddresses(tos + ccs + delivered_tos + resent_tos + resent_ccs)
91

    
92
# And look for a matching one
93
project = None
94
for n, e in all_recipients:
95
    split = e.index('@')
96
    email = e[:split]
97
    domain = e[split+1:]
98
    subaddr = None
99

    
100
    # If we have a subaddress, get it.
101
    try:
102
        ssplit = email.rindex('+')
103
        subaddr = email[ssplit+1:]
104
        email = email[:ssplit]
105
    except ValueError:
106
        pass
107

    
108
    # Now check email and subaddress, then break if found
109
    if email == ename and domain == edomain:
110
        if subaddr:
111
            project = subaddr
112
            break
113

    
114
# Finally, execute the handler and pass it the whole thing
115
if project:
116
    projectarg = ['-p', project]
117
elif options.project:
118
    projectarg = ['-p', options.project]
119
else:
120
    projectarg = []
121

    
122
handler = Popen(args + projectarg, stdin=PIPE, stdout=sys.stdout, stderr=sys.stderr, shell=False)
123
handler.stdin.write(buf)
124
for l in sys.stdin:
125
    handler.stdin.write(l)
126

    
127
handler.stdin.close()
128
handler.wait()
129
sys.exit(handler.returncode)
130

    
(3-3/3)