从 python 通过 sendmail 发送邮件

Sending mail via sendmail from python

提问人:Nate 提问时间:9/16/2008 最后编辑:Nate 更新时间:11/2/2020 访问量:95867

问:

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

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

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

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

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

python 电子邮件 sendmail

评论


答:

3赞 tovare 9/16/2008 #1

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

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

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

评论

1赞 Gert van den Berg 12/11/2017
SMTP 不能使用 DMA 之类的东西在 ~nix 盒子上工作,它提供 sendmail,但不侦听端口 25...
-7赞 Frank Wiles 9/16/2008 #2

最简单的答案是 smtplib,您可以在此处找到有关它的文档。

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

评论

7赞 S19N 5/31/2012
问题说“如果我想不通过SMTP发送邮件,而是通过sendmail发送邮件...”
2赞 Denis Barmenkov 10/12/2012
如果与SMTP服务器没有连接或滞后,您的程序将延迟。使用 sendmail,您可以将邮件传递给 MTA,然后您的程序继续。
38赞 Pieter 9/16/2008 #3

这是一个简单的 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" % "[email protected]")
    p.write("To: %s\n" % "[email protected]")
    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

评论

2赞 Jim 8/3/2012
他们明确表示他们不想要 - 风格的解决方案。更糟糕的是,给出的原因是为了避免诸如标头注入漏洞之类的事情。如果用户提供 from 或 to 地址,则此代码容易受到标头注入攻击。这正是他们不想要的。popen
8赞 Pieter 8/4/2012
@Jim是的,他们专门在我的答案给出后编辑了那部分(检查编辑日期),我想是为了回应我的答案。
0赞 Hariom Singh 8/15/2017
@Pieter,如果我必须发送附件?
133赞 Jim 9/17/2008 #4

标头注入不是您如何发送邮件的一个因素,而是您如何构建邮件的一个因素。检查电子邮件包,用它构建邮件,序列化它,然后使用子进程模块将其发送到:/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"] = "[email protected]"
msg["To"] = "[email protected]"
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())

评论

0赞 Nate 9/17/2008
完善。我的概念问题是将电子邮件的构造和发送分开。谢谢!
3赞 Peter 4/8/2013
很好,它今天帮助了我,经过 5 年的回答:)
15赞 Robie Basak 7/1/2013
您还应该使用该参数来发送邮件。这样可以防止邮件中的单个邮件过早终止电子邮件。-oi.
26赞 ov7a 9/17/2017
python3 用户应该使用代替.as_bytes()as_string()
1赞 IngoMeyer 10/15/2022
@DylanYoung 这是一面旗帜。来自手册页:-oisendmail-oi (default) Do not take dots on a line by themselves as a message terminator.
5赞 amcgregor 4/5/2011 #5

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

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

评论

0赞 TankorSmash 5/28/2012
一年后,turboMail 链接不行了
1赞 tjklemz 12/15/2012
+1 用于 Marrow Mailer。我试过了,它使通过 smpt、sendmail 等发送邮件变得非常容易,并且还提供了非常好的验证。因为 Marrow Mailer 是“一个很好的库,它抽象了整个'sendmail -vsus-smtp'选择”,我觉得这最好地回答了最初的问题。
-3赞 elec3647 6/27/2013 #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 服务器,但您可能需要仔细检查是否遇到任何问题。

评论

0赞 mbirth 1/26/2017
这仍然通过SMTP协议使用。有些用户不想打开端口 21,甚至不想只在 localhost 上打开。localhost
12赞 MEI 9/20/2015 #7

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

from email.mime.text import MIMEText
from subprocess import Popen, PIPE

msg = MIMEText("Here is the body of my message")
msg["From"] = "[email protected]"
msg["To"] = "[email protected]"
msg["Subject"] = "This is the subject."
p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE, universal_newlines=True)
p.communicate(msg.as_string())

没有 I getuniversal_newlines=True

TypeError: 'str' does not support the buffer interface
8赞 Robin Stewart 5/21/2020 #8

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())