一个简单的SMTP服务器(用Python)

时间:2010-04-22 13:02:53

标签: python smtp

你能否建议一个简单的SMTP服务器,其中包含可以在linux机器上运行的基本API(非常基本的意思是读,写,删除电子邮件)? 我只需要将电子邮件的关键字转换为XML格式并将其FTP到另一台机器。

9 个答案:

答案 0 :(得分:39)

看看这个SMTP sink server

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'),
                self.no)
        f = open(filename, 'w')
        f.write(data)
        f.close
        print '%s saved.' % filename
        self.no += 1


def run():
    foo = EmlServer(('localhost', 25), None)
    try:
        asyncore.loop()
    except KeyboardInterrupt:
        pass


if __name__ == '__main__':
    run()

它使用smtpd.SMTPServer将电子邮件转储到文件中。

答案 1 :(得分:21)

发送电子邮件确实需要2件事:

  • SMTP服务器 - 可以是Python SMTP Server,也可以使用GMail或ISP的服务器。您可能不需要自己运行。
  • SMTP库 - 将向SMTP服务器发送电子邮件请求的内容。 Python附带了一个名为smtplib的库,可以为您完成。这里有大量有关如何使用它的信息:http://docs.python.org/library/smtplib.html

对于阅读,有两种选择,具体取决于您从哪个服务器阅读电子邮件。

答案 2 :(得分:4)

我成功使用的两个python smtp服务器是:

  1. Twisted的Mail - 一个非常灵活的SMTP,IMAP邮件库,......
  2. python-slimta - 完整的MTA(smtp中继/转发服务器)
  3. Twisted的例子如下所示

    # 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
    
    
    
    @implementer(smtp.IMessageDelivery)
    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)
    
    
    
    @implementer(smtp.IMessage)
    class ConsoleMessage:
        def __init__(self):
            self.lines = []
    
    
        def lineReceived(self, line):
            self.lines.append(line)
    
    
        def eomReceived(self):
            print("New message received:")
            print("\n".join(self.lines))
            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
    
    
    
    @implementer(IRealm)
    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")
        portal.registerChecker(checker)
    
        a = service.Application("Console SMTP Server")
        internet.TCPServer(2500, ConsoleSMTPFactory(portal)).setServiceParent(a)
    
        return a
    
    application = main()
    

答案 3 :(得分:2)

这些是开始的好例子。

smtpd - 示例SMTP服务器

http://pymotw.com/2/smtpd/index.html

smtplib - 简单邮件传输协议客户端

http://pymotw.com/2/smtplib/index.html

答案 4 :(得分:2)

更现代的方法是使用aiosmtpd库(可用文档here)。

你可以在这里找到一个很好的例子:https://aiosmtpd.readthedocs.io/en/latest/aiosmtpd/docs/controller.html

答案 5 :(得分:2)

我还想用Python启动一个smtp服务器,并使用Python发送电子邮件。我想在一个单一的Flask Web应用程序中运行所有这些,这意味着smtp服务器必须是非阻塞的。这是我最终来到[gist]的解决方案:

app.py

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

app = Flask(__name__)

@app.route('/send_email')
def email():
  server = SMTPServer()
  server.start()
  try:
    send_email()
  finally:
    server.stop()
  return 'OK'

@app.route('/')
def index():
  return 'Woohoo'

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

smtp_server.py

# 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))
    return

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(('0.0.0.0', 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)
    self.thread.start()

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

  # 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(('0.0.0.0', 1025), None)
  asyncore.loop()

smtp_client.py

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

def send_email():
  sender='author@example.com'
  recipient='6142546977@tmomail.net'

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

  client = smtplib.SMTP('127.0.0.1', 1025)
  client.set_debuglevel(True) # show communication with the server
  try:
    client.sendmail('author@example.com', [recipient], msg.as_string())
  finally:
    client.quit()

然后使用python app.py启动服务器,并在另一个请求中使用/send_email模拟对curl localhost:5000/send_email的请求。请注意,要实际发送电子邮件(或短信),您需要跳过以下详细介绍的其他操作:https://blog.codinghorror.com/so-youd-like-to-send-some-email-through-code/

答案 6 :(得分:1)

Python SMTP server

  

此模块提供了几个实现SMTP服务器的类。一个是   通用的do-nothing实现,可以被覆盖,而   另外两个提供特定的邮件发送策略。

答案 7 :(得分:0)

要让Hasen的脚本在Python 3中运行,我不得不稍微调整一下:

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'),
            self.no)
        print(filename)
        f = open(filename, 'wb')
        f.write(data)
        f.close
        print('%s saved.' % filename)
        self.no += 1

def run():
    EmlServer(('localhost', 25), None)
    try:
        asyncore.loop()
    except KeyboardInterrupt:
        pass

if __name__ == '__main__':
    run()

答案 8 :(得分:0)

如果你想快速测试Django send_mail上面有hasen's的答案:

    <FrameLayout
    android:id="@+id/container_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.cameraview.CameraView
        android:id="@+id/camera_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:keepScreenOn="true"
        android:adjustViewBounds="true"
        app:autoFocus="true"
        app:aspectRatio="4:3"
        app:facing="back"
        app:flash="off"/>

    <SurfaceView
        style="@style/ThemeOverlay.AppCompat.Light"
        android:clickable="false"
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

在send_mail函数中没有必要具有有效值。哈森的例子可以很好地使用上面的值。

相关问题