import logging
from pylons import c, g
from bson import ObjectId
from allura.lib import helpers as h
from allura.lib.decorators import task
from allura.lib import mail_util
from allura.lib import exceptions as exc
log = logging.getLogger(__name__)
smtp_client = mail_util.SMTPClient()
@task
def route_email(
peer, mailfrom, rcpttos, data):
'''Route messages according to their destination:
<topic>@<mount_point>.<subproj2>.<subproj1>.<project>.projects.sourceforge.net
gets sent to c.app.handle_message(topic, message)
'''
try:
msg = mail_util.parse_message(data)
except: # pragma no cover
log.exception('Parse Error: (%r,%r,%r)', peer, mailfrom, rcpttos)
return
c.user = mail_util.identify_sender(peer, mailfrom, msg['headers'], msg)
log.info('Received email from %s', c.user.username)
# For each of the addrs, determine the project/app and route appropriately
for addr in rcpttos:
try:
userpart, project, app = mail_util.parse_address(addr)
with h.push_config(c, project=project, app=app):
if not app.has_access(c.user, userpart):
log.info('Access denied for %s to mailbox %s', c.user, userpart)
else:
if msg['multipart']:
msg_hdrs = msg['headers']
for part in msg['parts']:
if part.get('content_type', '').startswith('multipart/'): continue
msg = dict(
headers=dict(msg_hdrs, **part['headers']),
message_id=part['message_id'],
in_reply_to=part['in_reply_to'],
references=part['references'],
filename=part['filename'],
content_type=part['content_type'],
payload=part['payload'])
c.app.handle_message(userpart, msg)
else:
c.app.handle_message(userpart, msg)
except exc.MailError, e:
log.error('Error routing email to %s: %s', addr, e)
except:
log.exception('Error routing mail to %s', addr)
@task
def sendmail(
fromaddr,
destinations,
text,
reply_to,
subject,
message_id,
in_reply_to=None):
from allura import model as M
addrs_plain = []
addrs_html = []
addrs_multi = []
if '@' not in fromaddr:
user = M.User.query.get(_id=ObjectId(fromaddr))
if not user:
log.warning('Cannot find user with ID %s', fromaddr)
fromaddr = 'noreply@in.sf.net'
else:
fromaddr = user.email_address_header()
# Divide addresses based on preferred email formats
for addr in destinations:
if mail_util.isvalid(addr):
addrs_plain.append(addr)
else:
try:
user = M.User.query.get(_id=ObjectId(addr))
if not user:
log.warning('Cannot find user with ID %s', addr)
continue
except:
log.exception('Error looking up user with ID %r')
continue
addr = user.email_address_header()
if not addr and user.email_addresses:
addr = user.email_addresses[0]
log.warning('User %s has not set primary email address, using %s',
user._id, addr)
if not addr:
log.error("User %s (%s) has not set any email address, can't deliver",
user._id, user.username)
continue
if user.get_pref('email_format') == 'plain':
addrs_plain.append(addr)
elif user.get_pref('email_format') == 'html':
addrs_html.append(addr)
else:
addrs_multi.append(addr)
plain_msg = mail_util.encode_email_part(text, 'plain')
html_text = g.forge_markdown(email=True).convert(text)
html_msg = mail_util.encode_email_part(html_text, 'html')
multi_msg = mail_util.make_multipart_message(plain_msg, html_msg)
smtp_client.sendmail(
addrs_multi, fromaddr, reply_to, subject, message_id,
in_reply_to, multi_msg)
smtp_client.sendmail(
addrs_plain, fromaddr, reply_to, subject, message_id,
in_reply_to, plain_msg)
smtp_client.sendmail(
addrs_html, fromaddr, reply_to, subject, message_id,
in_reply_to, html_msg)