100 lines
3.1 KiB
Python
Executable File
100 lines
3.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
from email import message, message_from_binary_file, policy
|
|
from email.generator import BytesGenerator
|
|
from inspect import getsourcefile
|
|
from os.path import abspath, dirname, exists, join, realpath
|
|
import re
|
|
from subprocess import Popen, PIPE
|
|
from sys import argv, stdin
|
|
|
|
# Hack to determine the directory this script resides in,
|
|
# regardless of symlinks or other weirdness.
|
|
basedir = dirname(realpath(abspath(getsourcefile(lambda:0))))
|
|
|
|
# Set a sane email policy.
|
|
mypolicy = policy.default
|
|
|
|
# Read the incoming message.
|
|
inmsg = message_from_binary_file(stdin.buffer, policy=mypolicy)
|
|
|
|
# Bail if the incoming message was automatically generated
|
|
auto = inmsg['Auto-Submitted']
|
|
if auto and auto.lower() != 'no':
|
|
exit()
|
|
|
|
# Build the outgoing reply.
|
|
msg = message.EmailMessage(policy=mypolicy)
|
|
|
|
## Basic headers.
|
|
msg['To'] = inmsg['From']
|
|
msg['From'] = inmsg['To']
|
|
|
|
## Only add "re" to the subject if it's not already there.
|
|
if inmsg['Subject'].startswith(('re: ', 'Re: ', 'Re ', 're ')):
|
|
msg['Subject'] = inmsg['Subject']
|
|
else:
|
|
msg['Subject'] = 'Re: ' + inmsg['Subject']
|
|
|
|
## Set up correct threading identifiers.
|
|
if inmsg['Message-ID']:
|
|
msg['In-Reply-To'] = inmsg['Message-ID']
|
|
if inmsg['References']:
|
|
msg['References'] = inmsg['References'] +' '+ inmsg.get('Message-ID', '')
|
|
elif inmsg['In-Reply-To'] or inmsg['Message-ID']:
|
|
msg['References'] = inmsg.get('In-Reply-To', '') +' '+ inmsg.get('Message-ID', '')
|
|
|
|
## Add loop avoidance header (RFC3834)
|
|
msg['Auto-Submitted'] = 'auto-generated'
|
|
|
|
## Also add the exchange-specific anti-loop header
|
|
## https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmail/ced68690-498a-4567-9d14-5c01f974d8b1
|
|
msg['X-Auto-Response-Suppress'] = 'All'
|
|
|
|
## Set the template directory.
|
|
templatedir = join(basedir, 'templates')
|
|
|
|
## Pick an autoreply template.
|
|
### Start with a sane default.
|
|
mytemplate = 'default'
|
|
|
|
### Try a template based on the recipient address.
|
|
#### Extract the address part from the To: header.
|
|
#### The regex handles: "name" <address@domain>
|
|
#### <address@domain>
|
|
#### address@domain
|
|
matches = re.match(r'("[^"]+" +)?<?([^>]+)>?', inmsg['To'])
|
|
address = matches.group(2)
|
|
|
|
#### If there was a match, use the name part to look for a template and use
|
|
#### it if it exists.
|
|
if address:
|
|
trytemplate = address.split('@')[0]
|
|
if exists(join(templatedir, trytemplate)):
|
|
mytemplate = trytemplate
|
|
|
|
### If a template was specified on the commandline, use that instead.
|
|
### Will cause an error if the template doesn't exist. This is intentional.
|
|
if len(argv) > 1:
|
|
mytemplate = argv[1]
|
|
|
|
## Set response body.
|
|
with open(join(templatedir, mytemplate)) as f:
|
|
msg.set_content(f.read())
|
|
|
|
## Include the original email
|
|
msg.add_related(inmsg)
|
|
|
|
## Include html version if one exists
|
|
htmlpath = join(templatedir, mytemplate + '.html')
|
|
if exists(htmlpath):
|
|
with open(htmlpath) as f:
|
|
msg.add_alternative(f.read(), subtype='html')
|
|
|
|
# Post the reply
|
|
p = Popen(['/usr/sbin/sendmail', '-t'], stdin=PIPE)
|
|
g = BytesGenerator(p.stdin, policy=msg.policy.clone(linesep='\r\n'))
|
|
g.flatten(msg)
|
|
p.stdin.close()
|
|
p.wait()
|