【问题标题】:Sending mail via sendmail from python从 python 通过 sendmail 发送邮件
【发布时间】:2010-09-09 13:58:11
【问题描述】:

如果我不想通过 SMTP 发送邮件,而是通过 sendmail 发送邮件,是否有封装此过程的 python 库?

更好的是,有没有一个好的库可以抽象出整个“sendmail - 与 smtp”的选择?

我将在一堆 unix 主机上运行这个脚本,其中只有一些在 localhost:25 上监听;其中一些是嵌入式系统的一部分,无法设置为接受 SMTP。

作为良好实践的一部分,我真的很想让库自己处理标头注入漏洞 - 所以将字符串转储到 popen('/usr/bin/sendmail', 'w') 比我想要的更接近金属。

如果答案是“去写一个库”,那就这样吧;-)

【问题讨论】:

    标签: python email sendmail


    【解决方案1】:

    标头注入不是影响您如何发送邮件的因素,而是影响您如何构建邮件的因素。检查email 包,用它构建邮件,将其序列化,然后使用subprocess 模块将其发送到/usr/sbin/sendmail

    import sys
    from email.mime.text import MIMEText
    from subprocess import Popen, PIPE
    
    
    msg = MIMEText("Here is the body of my message")
    msg["From"] = "me@example.com"
    msg["To"] = "you@example.com"
    msg["Subject"] = "This is the subject."
    p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE)
    # Both Python 2.X and 3.X
    p.communicate(msg.as_bytes() if sys.version_info >= (3,0) else msg.as_string()) 
    
    # Python 2.X
    p.communicate(msg.as_string())
    
    # Python 3.X
    p.communicate(msg.as_bytes())
    

    【讨论】:

    • 完美。我的概念问题是将电子邮件的构建和发送分开。谢谢!
    • 很好,今天它对我有帮助,经过 5 年的回答 :)
    • 您还应该使用-oi 参数来发送邮件。这会阻止邮件中的单个 . 过早终止电子邮件。
    • python3 用户应该使用.as_bytes() 而不是as_string()
    • 我对@9​​87654329@ 感到困惑。 -o 好像是用来设置处理选项的,-i 好像只适用于接收邮件...
    【解决方案2】:

    Python 3.5+ 版本:

    import subprocess
    from email.message import EmailMessage
    
    def sendEmail(from_addr, to_addrs, msg_subject, msg_body):
        msg = EmailMessage()
        msg.set_content(msg_body)
        msg['From'] = from_addr
        msg['To'] = to_addrs
        msg['Subject'] = msg_subject
    
        sendmail_location = "/usr/sbin/sendmail"
        subprocess.run([sendmail_location, "-t", "-oi"], input=msg.as_bytes())
    

    【讨论】:

      【解决方案3】:

      这个问题很老了,但值得注意的是,有一个名为 Marrow Mailer(以前称为 TurboMail)的消息构建和电子邮件传递系统,在询问此消息之前就已经可用。

      现在正在移植以支持 Python 3,并作为 Marrow 套件的一部分进行了更新。

      【讨论】:

      • turboMail 链接不行,一年后
      • +1 用于 Marrow Mailer。我试了一下,通过smpt、sendmail等发送邮件非常方便,并且提供了非常好的验证。因为 Marrow Mailer 是“一个很好的库,它抽象了整个 'sendmail -versus- smtp' 选择”,我觉得这最好地回答了最初的问题。
      【解决方案4】:

      Jim 的回答在 Python 3.4 中对我不起作用。我不得不在subrocess.Popen() 中添加一个额外的universal_newlines=True 参数

      from email.mime.text import MIMEText
      from subprocess import Popen, PIPE
      
      msg = MIMEText("Here is the body of my message")
      msg["From"] = "me@example.com"
      msg["To"] = "you@example.com"
      msg["Subject"] = "This is the subject."
      p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE, universal_newlines=True)
      p.communicate(msg.as_string())
      

      没有universal_newlines=True 我得到了

      TypeError: 'str' does not support the buffer interface
      

      【讨论】:

        【解决方案5】:

        使用 os.popen 从 Python 中使用 sendmail 命令是很常见的

        就我个人而言,对于不是我自己编写的脚本,我认为只使用 SMTP 协议会更好,因为它不需要安装 sendmail 克隆就可以在 Windows 上运行。

        https://docs.python.org/library/smtplib.html

        【讨论】:

        • SMTP 不能在 ~nix 盒子上使用 DMA 之类的东西,它提供 sendmail,但不监听端口 25...
        【解决方案6】:

        我只是在寻找同样的东西,并在 Python 网站上找到了一个很好的例子:http://docs.python.org/2/library/email-examples.html

        来自提到的网站:

        # Import smtplib for the actual sending function
        import smtplib
        
        # Import the email modules we'll need
        from email.mime.text import MIMEText
        
        # Open a plain text file for reading.  For this example, assume that
        # the text file contains only ASCII characters.
        fp = open(textfile, 'rb')
        # Create a text/plain message
        msg = MIMEText(fp.read())
        fp.close()
        
        # me == the sender's email address
        # you == the recipient's email address
        msg['Subject'] = 'The contents of %s' % textfile
        msg['From'] = me
        msg['To'] = you
        
        # Send the message via our own SMTP server, but don't include the
        # envelope header.
        s = smtplib.SMTP('localhost')
        s.sendmail(me, [you], msg.as_string())
        s.quit()
        

        请注意,这需要您正确设置 sendmail/mailx 以接受“localhost”上的连接。默认情况下,这适用于我的 Mac、Ubuntu 和 Redhat 服务器,但您可能需要仔细检查是否遇到任何问题。

        【讨论】:

        • 这仍然通过 SMTP 协议使用localhost。有些用户不想打开 21 端口,甚至只在 localhost 上也不想。
        【解决方案7】:

        最简单的答案是 smtplib,您可以在上面找到文档here

        您需要做的就是将本地 sendmail 配置为接受来自 localhost 的连接,默认情况下它可能已经这样做了。当然,您仍然使用 SMTP 进行传输,但它是本地发送邮件,这与使用命令行工具基本相同。

        【讨论】:

        • 问题是“如果我不想通过 SMTP 发送邮件,而是通过 sendmail...”
        • 如果 SMTP 服务器没有连接或延迟,您的程序将延迟。使用 sendmail 将消息传递给 MTA,然后您的程序继续运行。
        【解决方案8】:

        这是一个简单的 python 函数,它使用 unix sendmail 来传递邮件。

        def sendMail():
            sendmail_location = "/usr/sbin/sendmail" # sendmail location
            p = os.popen("%s -t" % sendmail_location, "w")
            p.write("From: %s\n" % "from@somewhere.com")
            p.write("To: %s\n" % "to@somewhereelse.com")
            p.write("Subject: thesubject\n")
            p.write("\n") # blank line separating headers from body
            p.write("body of the mail")
            status = p.close()
            if status != 0:
                   print "Sendmail exit status", status
        

        【讨论】:

        • 他们明确表示他们不想要popen 风格的解决方案。更糟糕的是,给出的原因是为了避免诸如标头注入漏洞之类的事情。如果用户提供 from 或 to 地址,则此代码容易受到标头注入攻击。这正是他们不想要的。
        • @Jim 是的,他们在我给出答案后专门编辑了该部分(检查编辑日期),以回应我的回答。
        • @Pieter 如果我必须发送附件?
        猜你喜欢
        • 2016-09-24
        • 2014-09-03
        • 2015-04-23
        • 1970-01-01
        • 2011-05-03
        • 2016-02-21
        • 1970-01-01
        • 2014-08-31
        相关资源
        最近更新 更多