Project

General

Profile

MailhandlerSubAddress » sub-mailhandler.py

Frontend for rdm-mailhandler.rb - Thomas Guyot-Sionnest, 2010-07-14 23:59

 
1
#!/usr/bin/env python
2
#
3
# sub-mailhandler.py - frontend for rdm-mailhandler.rb
4
# v.1.0
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
from optparse import OptionParser
34
from email import parser, message, utils
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 = 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 = 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

    
(1-1/3)