Fix for Python <2.5 not importing parser from email
[thomas_code.git] / redmine / sub-mailhandler.py
blobb82b625f89613a793d55a26800f937e6906fec7a
1 #!/usr/bin/env python
3 # sub-mailhandler.py - frontend for rdm-mailhandler.rb
4 # v.1.0.1
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
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.
16 # Copyright 2010 Thomas Guyot-Sionnest <tguyot@gmail.com>
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.
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.
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/>.
32 import sys
33 import email
34 from optparse import OptionParser
35 from subprocess import Popen, PIPE
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.''')
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')
48 (options, args) = oparser.parse_args()
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')
53 try:
54 esplit = options.email.index('@')
55 except ValueError:
56 oparser.error('Not an email address')
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
72 # Read-in the headers...
73 buf = ''
74 while True:
75 line = sys.stdin.readline()
76 buf += line
77 if line.strip() == '': break
79 # parse them...
80 eo = email.parser.HeaderParser()
81 msg = eo.parsestr(buf, headersonly=True)
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)
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
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
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
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 = []
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)
125 handler.stdin.close()
126 handler.wait()
127 sys.exit(handler.returncode)