<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Guguweb - email</title><link href="https://www.guguweb.com/" rel="alternate"></link><link href="https://www.guguweb.com/feeds/email.atom.xml" rel="self"></link><id>https://www.guguweb.com/</id><updated>2022-01-07T15:50:01+01:00</updated><subtitle>Freelance developer and sysadmin</subtitle><entry><title>A simple Python email gateway</title><link href="https://www.guguweb.com/2017/05/10/a-simple-python-email-gateway/" rel="alternate"></link><published>2017-05-10T13:23:11+00:00</published><updated>2022-01-07T15:50:01+01:00</updated><author><name>Augusto Destrero</name></author><id>tag:www.guguweb.com,2017-05-10:/2017/05/10/a-simple-python-email-gateway/</id><summary type="html">&lt;p&gt;Say you have a Django web application that you want to integrate with emails to make it possibile to send data and files to your web application over SMTP.&lt;/p&gt;
&lt;p&gt;The good news is that Python has a simple SMTP daemon in the standard library, together with modules to parse emails …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Say you have a Django web application that you want to integrate with emails to make it possibile to send data and files to your web application over SMTP.&lt;/p&gt;
&lt;p&gt;The good news is that Python has a simple SMTP daemon in the standard library, together with modules to parse emails. Let&amp;#8217;s see how to create a simple email gateway on top of these tools.&lt;/p&gt;
&lt;p&gt;Using the following Python module you can setup a simple pure Python email server on port 25, parse the incoming emails and make a &lt;em&gt;multipart/form-data&lt;/em&gt; POST HTTP request to your web application. You&amp;#8217;ll need the awesome &lt;a href="http://docs.python-requests.org/en/master/"&gt;Requests&lt;/a&gt; library for the HTTP request to work.&lt;/p&gt;
&lt;div class="gist"&gt;
    &lt;script src='https://gist.github.com/d5d6f855774ba9f72e11909648a46cae.js'&gt;&lt;/script&gt;
    &lt;noscript&gt;
        &lt;pre&gt;&lt;code&gt;import smtpd
import asyncore
import logging
import email
from email.header import decode_header
import requests

logger = logging.getLogger(__name__)

class CustomSMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
        try:
            logger.debug('Receiving message from: %s:%d' % peer)
            logger.debug('Message addressed from: %s' % mailfrom)
            logger.debug('Message addressed to: %s' % str(rcpttos))

            msg = email.message_from_string(data)
            subject = ''
            for encoded_string, charset in decode_header(msg.get('Subject')):
                try:
                    if charset is not None:
                        subject += encoded_string.decode(charset)
                    else:
                        subject += encoded_string
                except:
                    logger.exception('Error reading part of subject: %s charset %s' %
                                     (encoded_string, charset))

            logger.debug('Subject: %s' % subject)

            text_parts = []
            attachments = {}

            logger.debug('Headers: %s' % msg.items())

            # YOU CAN DO SOME SECURITY CONTROLS HERE
            if (not mailfrom.endswith("@example.com") or
                not msg.get('Mail-Header') == 'expected value'):
                raise Exception("Email not trusted")

            # loop on the email parts
            for part in msg.walk():
                if part.get_content_maintype() == 'multipart':
                    continue

                c_type = part.get_content_type()
                c_disp = part.get('Content-Disposition')

                # text parts will be appended to text_parts
                if c_type == 'text/plain' and c_disp == None:
                    text_parts.append(part.get_payload(decode=True).strip())
                # ignore html part
                elif c_type == 'text/html':
                    continue
                # attachments will be sent as files in the POST request
                else:
                    filename = part.get_filename()
                    filecontent = part.get_payload(decode=True)
                    if filecontent is not None:
                        if filename is None:
                            filename = 'untitled'
                        attachments['file%d' % len(attachments)] = (filename,
                                                                    filecontent)

            body = '\n'.join(text_parts)
        except:
            logger.exception('Error reading incoming email')
        else:
            # this data will be sent as POST data
            data = {
                'subject': subject,
                'body': body,
                'from': mailfrom,
            }
            url = 'http://yourserver.example.com/email-receive/'
            logger.debug('Sending data and files to %s' % url)
            try:
                r = requests.post(url, data=data, files=attachments)
                r.raise_for_status()
            except:
                logger.exception('Error in server request')
                logger.debug(data)
                logger.debug(attachments)
            else:
                logger.debug('Response from server: %s %s' % (r.status_code, r.text))

        return

if __name__ == '__main__':
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    ch.setFormatter(formatter)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(ch)

    server = CustomSMTPServer(('111.111.111.111', 25), None) # use your public IP here
    asyncore.loop()&lt;/code&gt;&lt;/pre&gt;
    &lt;/noscript&gt;
&lt;/div&gt;
&lt;p&gt;In a production environment you can setup &lt;a href="http://supervisord.org/"&gt;supervisor&lt;/a&gt; to launch your simple SMTP server. With a simple configuration like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[program:smtp_server]&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/home/ubuntu/.virtualenvs/venv/bin/python /home/ubuntu/mail_gateway/smtp_server.py&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then try to send an email to your server, if the server public IP has been associated to a DNS record &lt;em&gt;yourserver.example.com&lt;/em&gt; you can use something like &lt;em&gt;test@yourserver.example.com&lt;/em&gt; as a recipient, and you should see the incoming email in the logs.&lt;/p&gt;
&lt;p&gt;Have fun with emails!&lt;/p&gt;</content><category term="sysadmin"></category><category term="django"></category><category term="email"></category><category term="python"></category></entry></feed>