'A simple SMTP server (in Python)

Could you please suggest a simple SMTP server with the very basic APIs (by very basic I mean, to read, write, delete email), that could be run on a linux box? I just need to convert the crux of the email into XML format and FTP it to another machine.

Solution 1:[1]

Take a look at this SMTP sink server:

from __future__ import print_function
from datetime import datetime
import asyncore
from smtpd import SMTPServer

class EmlServer(SMTPServer):
    no = 0
    def process_message(self, peer, mailfrom, rcpttos, data):
        filename = '%s-%d.eml' % (datetime.now().strftime('%Y%m%d%H%M%S'),
        f = open(filename, 'w')
        print('%s saved.' % filename)
        self.no += 1

def run():
    # start the smtp server on localhost:1025
    foo = EmlServer(('localhost', 1025), None)
    except KeyboardInterrupt:

if __name__ == '__main__':

It uses smtpd.SMTPServer to dump emails to files.

Solution 2:[2]

There are really 2 things required to send an email:

  • An SMTP Server - This can either be the Python SMTP Server or you can use GMail or your ISP's server. Chances are you don't need to run your own.
  • An SMTP Library - Something that will send an email request to the SMTP server. Python ships with a library called smtplib that can do that for you. There is tons of information on how to use it here: http://docs.python.org/library/smtplib.html

For reading, there are two options depending on what server you are reading the email from.

Solution 3:[3]

To get Hasen's script working in Python 3 I had to tweak it slightly:

from datetime import datetime
import asyncore
from smtpd import SMTPServer

class EmlServer(SMTPServer):
    no = 0
    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        filename = '%s-%d.eml' % (datetime.now().strftime('%Y%m%d%H%M%S'),
        f = open(filename, 'wb')
        print('%s saved.' % filename)
        self.no += 1

def run():
    EmlServer(('localhost', 25), None)
    except KeyboardInterrupt:

if __name__ == '__main__':

Solution 4:[4]

Two python smtp servers I've used with success are:

  1. Twisted's Mail - A very flexible mail library for SMTP, IMAP, ...
  2. python-slimta - A complete MTA (smtp relay/forwarding server)

Twisted's example is shown below

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

# You can run this module directly with:
#    twistd -ny emailserver.tac

A toy email server.
from __future__ import print_function

from zope.interface import implementer

from twisted.internet import defer
from twisted.mail import smtp
from twisted.mail.imap4 import LOGINCredentials, PLAINCredentials

from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
from twisted.cred.portal import IRealm
from twisted.cred.portal import Portal

class ConsoleMessageDelivery:
    def receivedHeader(self, helo, origin, recipients):
        return "Received: ConsoleMessageDelivery"

    def validateFrom(self, helo, origin):
        # All addresses are accepted
        return origin

    def validateTo(self, user):
        # Only messages directed to the "console" user are accepted.
        if user.dest.local == "console":
            return lambda: ConsoleMessage()
        raise smtp.SMTPBadRcpt(user)

class ConsoleMessage:
    def __init__(self):
        self.lines = []

    def lineReceived(self, line):

    def eomReceived(self):
        print("New message received:")
        self.lines = None
        return defer.succeed(None)

    def connectionLost(self):
        # There was an error, throw away the stored lines
        self.lines = None

class ConsoleSMTPFactory(smtp.SMTPFactory):
    protocol = smtp.ESMTP

    def __init__(self, *a, **kw):
        smtp.SMTPFactory.__init__(self, *a, **kw)
        self.delivery = ConsoleMessageDelivery()

    def buildProtocol(self, addr):
        p = smtp.SMTPFactory.buildProtocol(self, addr)
        p.delivery = self.delivery
        p.challengers = {"LOGIN": LOGINCredentials, "PLAIN": PLAINCredentials}
        return p

class SimpleRealm:
    def requestAvatar(self, avatarId, mind, *interfaces):
        if smtp.IMessageDelivery in interfaces:
            return smtp.IMessageDelivery, ConsoleMessageDelivery(), lambda: None
        raise NotImplementedError()

def main():
    from twisted.application import internet
    from twisted.application import service    

    portal = Portal(SimpleRealm())
    checker = InMemoryUsernamePasswordDatabaseDontUse()
    checker.addUser("guest", "password")

    a = service.Application("Console SMTP Server")
    internet.TCPServer(2500, ConsoleSMTPFactory(portal)).setServiceParent(a)

    return a

application = main()

Solution 5:[5]

A more modern approach is to use the aiosmtpd library (documentation available here).

You can find a good example here: https://aiosmtpd.readthedocs.io/en/latest/controller.html.

Solution 6:[6]

These are nice examples for a start.

smtpd – Sample SMTP Servers


smtplib – Simple Mail Transfer Protocol client


Solution 7:[7]

I also wanted to start a smtp server in Python and send emails with Python. I wanted to run all this in a Flask web application in a single process, which means the smtp server must be non-blocking. Here's the solution I eventually came to [gist]:


from flask import Flask, render_template
from smtp_client import send_email
from smtp_server import SMTPServer

app = Flask(__name__)

def email():
  server = SMTPServer()
  return 'OK'

def index():
  return 'Woohoo'

if __name__ == '__main__':
  app.run(debug=True, host='')


# smtp_server.py
import smtpd
import asyncore
import threading

class CustomSMTPServer(smtpd.SMTPServer):
  def process_message(self, peer, mailfrom, rcpttos, data):
    print('Receiving message from:', peer)
    print('Message addressed from:', mailfrom)
    print('Message addressed to:', rcpttos)
    print('Message length:', len(data))

class SMTPServer():
  def __init__(self):
    self.port = 1025

  def start(self):
    '''Start listening on self.port'''
    # create an instance of the SMTP server, derived from  asyncore.dispatcher
    self.smtp = CustomSMTPServer(('', self.port), None)
    # start the asyncore loop, listening for SMTP connection, within a thread
    # timeout parameter is important, otherwise code will block 30 seconds
    # after the smtp channel has been closed
    kwargs = {'timeout':1, 'use_poll': True}
    self.thread = threading.Thread(target=asyncore.loop, kwargs=kwargs)

  def stop(self):
    '''Stop listening to self.port'''
    # close the SMTPserver to ensure no channels connect to asyncore
    # now it is safe to wait for asyncore.loop() to exit

  # check for emails in a non-blocking way
  def get(self):
    '''Return all emails received so far'''
    return self.smtp.emails

if __name__ == '__main__':
  server = CustomSMTPServer(('', 1025), None)


import smtplib
import email.utils
from email.mime.text import MIMEText

def send_email():
  sender='[email protected]'
  recipient='[email protected]'

  msg = MIMEText('This is the body of the message.')
  msg['To'] = email.utils.formataddr(('Recipient', recipient))
  msg['From'] = email.utils.formataddr(('Author', '[email protected]'))
  msg['Subject'] = 'Simple test message'

  client = smtplib.SMTP('', 1025)
  client.set_debuglevel(True) # show communication with the server
    client.sendmail('[email protected]', [recipient], msg.as_string())

Then start the server with python app.py and in another request simulate a request to /send_email with curl localhost:5000/send_email. Note that to actually send the email (or sms) you'll need to jump through other hoops detailed here: https://blog.codinghorror.com/so-youd-like-to-send-some-email-through-code/.

Solution 8:[8]

There is Python SMTP server.

This module offers several classes to implement SMTP servers. One is a generic do-nothing implementation, which can be overridden, while the other two offer specific mail-sending strategies.

Solution 9:[9]

If you want to quickly test Django's send_mail with hasen's answer above:

# Skip lines 3 and 4 if not using virtualenv.
# At command prompt

mkdir django1
cd django1
virtualenv venv
source venv/bin/activate
pip install django==1.11
django-admin startproject django1 .

# run the Django shell

python manage.py shell

# paste into shell following:

from django.core.mail import send_mail

    'Subject here',
    'Here is the message.',
    '[email protected]',
    ['[email protected]'],
# This should write an email like the following:

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: Subject here
From: [email protected]
To: [email protected]
Date: Wed, 02 May 2018 02:12:09 -0000
Message-ID: <20180502021209.32641.51865@i1022>

Here is the message.

Not necessary to have valid values in send_mail function. Above values will work just fine with hasen's example.


This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 duhaime
Solution 2 Chris Dail
Solution 3 Protector one
Solution 4
Solution 5 hoefling
Solution 6 The Demz
Solution 7 duhaime
Solution 8 Jacob Schoen
Solution 9 small mammal