Parent: [febbd0] (diff)

Child: [6e4657] (diff)

Download this file

command.py    110 lines (98 with data), 4.2 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
import smtpd
import asyncore
import email.feedparser
from pprint import pformat
import tg
import pylons
from paste.script import command
import pyforge.command
from pyforge.lib.helpers import find_project
from pyforge.command import base
M = None
class SMTPServerCommand(pyforge.command.Command):
min_args=1
max_args=1
usage = 'NAME <ini file>'
summary = 'Handle incoming emails, routing them to RabbitMQ'
parser = command.Command.standard_parser(verbose=True)
parser.add_option('-c', '--context', dest='context',
help=('The context of the message (path to the project'
' and/or plugin'))
def command(self):
global M
self.basic_setup()
from pyforge import model
M = model
server = MailServer((tg.config.get('forgemail.host', '0.0.0.0'),
tg.config.get('forgemail.port', 8825)),
None)
asyncore.loop()
class MailServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data):
'''Route messages according to their destination:
<topic>@<mount_point>.<subproj2>.<subproj1>.<project>.projects.sourceforge.net
goes to the audit with routing ID
<plugin name>.<topic>
'''
base.log.info('Msg Received from %s for %s', mailfrom, rcpttos)
base.log.info('%s', data)
common_suffix = tg.config.get('forgemail.domain', '.sourceforge.net')
parsed_message = parse_message(data)
sending_user = identify_sender(peer, mailfrom, parsed_message)
base.log.info('Sender: %s', sending_user)
base.log.info('Message headers:\n%s', pformat(parsed_message['headers']))
if parsed_message['multipart']:
for part in parsed_message['parts']:
base.log.info('Message part:\n%s', part['payload'])
else:
base.log.info('Message payload:\n%s', parsed_message['payload'])
for addr in rcpttos:
try:
user, domain = addr.split('@')
# remove common domain suffix
if not domain.endswith(common_suffix):
base.log.warning(
'Unknown domain, dropping message: %s', domain)
continue
domain = domain[:-len(common_suffix)]
path = list(reversed(domain.split('.')))
project, mount_point = find_project(path)
if project is None:
base.log.warning('Unknown project at %s', domain)
continue
if len(mount_point) != 1:
base.log.warning('Unknown plugin at %s', domain)
continue
pylons.c.project = project
pylons.c.app = app = project.app_instance(mount_point[0])
topic = '%s.%s' % (app.config.plugin_name, user)
pylons.g.publish('audit', topic, dict(parsed_message,
user_id=str(sending_user._id)),
serializer='yaml')
except:
base.log.exception('Error handling mail to %s', addr)
def parse_message(data):
# Parse the email to its constituent parts
parser = email.feedparser.FeedParser()
parser.feed(data)
msg = parser.close()
# Extract relevant data
result = {}
result['multipart'] = multipart = msg.is_multipart()
result['headers'] = dict(msg)
if multipart:
result['parts'] = [
dict(headers=dict(subpart),
payload=subpart.get_payload())
for subpart in msg.walk() ]
else:
result['payload'] = msg.get_payload()
return result
def identify_sender(peer, email_address, msg):
base.log.info('Trying ID sender for addr %s', email_address)
# Dumb ID -- just look for email address claimed by a particular user
addr = M.EmailAddress.query.get(_id=M.EmailAddress.canonical(email_address))
if addr and addr.claimed_by_user_id:
return addr.claimed_by_user()
# TODO: look at the From: header, maybe?
return None