Pycon 2017 Portland, Oregon
May 21, 2017
Barry Warsaw
Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can.
- jwz (though maybe not)
Zawinski's Law of Software Envelopment
(and its little cousin LMTP
Local Mail Transport Protocol)
Some relevant RFCs
220 subdivisions Python SMTP 1.0
From: Geddy <geddy@example.com>
To: Alex <alex@example.com>
Bcc: Neil <neil@example.com>
Subject: New Music
Hey, we need to record a new album!
QUIT
221 Bye
HELO limelight
250 subdivisions
MAIL FROM:<geddy@example.com>
250 OK
RCPT TO:<alex@example.com>
250 OK
RCPT TO:<neil@example.com>
250 OK
DATA
354 End data with <CR><LF>.<CR><LF>
. 250 OK
RFC 5321
RFC 5322
Extended SMTP (ESMTP)
EHLO limelight
Local Mail Transport Protocol (LMTP)
LHLO limelight
Original SMTP
HELO limelight
Newly created on every client connection
HELO/RSET state
async def smtp_HELO(self, hostname):
if not hostname:
await self.push('501 Syntax: HELO hostname')
return
self._set_rset_state()
status = await self._call_handler_hook('HELO', hostname)
if status is MISSING:
self.session.host_name = hostname
status = '250' {}'.format(self.hostname)
await self.push(status)
from aiosmtpd.smtp import SMTP
from datetime import datetime
class MySMTPish(SMTP):
async def smtp_NOW(self, arg):
if arg == 'UTC':
now = datetime.utcnow()
elif not arg:
now = datetime.now()
else:
await self.push('501 Syntax: NOW [UTC]')
now = now.replace(microsecond=0)
await self.push('250 {}'.format(now))
% telnet localhost 8025 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 presto Python SMTP 1.0 NOW 250 2017-05-16 12:05:05 NOW 250 2017-05-16 12:05:08 NOW UTC 250 2017-05-16 19:05:10 NOW AND THEN 501 Syntax: NOW [UTC] QUIT 221 Bye
async def handle_VERB(self, server, session, envelope)
class Counter:
helo_counter = 0
async def handle_HELO(self, server, session, envelope, hostname):
self.session.hostname = hostname
self.helo_counter += 1
return '250 OK'
smtp = SMTP(Counter())
run_server_for_a_while(smtp)
print('We saw {} HELOs'.format(smtp.event_handler.helo_counter))
$ python3 -m aiosmtpd -n -c MyHandler arg1 arg2 $ telnet localhost 8025 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 subdivisions Python SMTP 1.0 QUIT 221 Bye Connection closed by foreign host.
class MyHandler: @classmethod def from_cli(cls, parser, *args): kws = convert(args) return cls(**kws)
https://github.com/aio-libs/aiosmtpd
http://aiosmtpd.readthedocs.io/
Requires at least Python 3.4 (but see GH#16)
barry@{python,list,debian}.org
barry@ubuntu.com
@pumpichank
github.com/warsaw
gitlab.com/warsaw