通过Postfix发送邮件至多个收件人仅被第一个地址接收

1 投票
1 回答
4673 浏览
提问于 2025-04-16 17:50

我正在使用下面的Python代码,把一个文件夹里的内容发送给一个在特定文件中列出的收件人。这个文件里的每个邮箱地址用分号(;)隔开,所有地址都在一个字符串里。当我执行这个命令时,所有内容都发送得很顺利,没有任何问题,但实际上邮件只发到了列表中的第一个邮箱地址。

经过一些调查,我发现Postfix的main.cf文件中有一个收件人限制,默认值是1,这可能限制了可以发送的收件人数量。我尝试把这一行注释掉,还把限制提高到200,但这两种方法都没有效果。

# dovecot 1.1.1
dovecot_destination_recipient_limit = 200

从单个收件人的角度来看邮件,一切似乎都正常,所以我觉得问题可能出在Postfix / Dovecot的sendmail部分?下面是一个示例,显示消息输出到文件,而不是发送到smtp。

Content-Type: multipart/mixed; boundary="===============7543504478351047681=="
MIME-Version: 1.0
Subject: Malware submission
To: xxxxx@gmail.com;xxxxx@hotmail.com
From: me@yu.com

You will not see this in a MIME-aware mail reader.

--===============7543504478351047681==
Content-Type: application/zip
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="sample.zip"

UEsDBAoAAAAAAHGfsT4AAAAAAAAAAAAAAAAGABwAdmlydXMvVVQJAAOVxdJNKJnSTXV4CwABBPUB
AAAEFAAAAFBLAQIeAwoAAAAAAHGfsT4AAAAAAAAAAAAAAAAGABgAAAAAAAAAEADtQQAAAAB2aXJ1
cy9VVAUAA5XF0k11eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBMAAAAQAAAAAAA
--===============7543504478351047681==--

Postfix的邮件日志包含以下内容:

May 17 21:10:41 MacBook-Pro-2 postfix/qmgr[3816]: 3FB902C186A: from=<chris.parker@email.co.uk>, size=1004, nrcpt=1 (queue active)
May 17 21:10:42 MacBook-Pro-2 postfix/smtp[3855]: 3FB902C186A: to=<xxxxo@gmail.com>, relay=gmail-smtp-in.l.google.com[209.85.143.27]:25, delay=1.3, delays=0.01/0.01/0.57/0.75, dsn=2.0.0, status=sent (250 2.0.0 OK 1305662986 k6si1621545wej.25)
May 17 21:10:42 MacBook-Pro-2 postfix/qmgr[3816]: 3FB902C186A: removed

请帮帮我...

    #!/usr/bin/env python

"""Send the contents of a directory as a MIME message."""

import os
import sys
import smtplib
# For guessing MIME type based on file name extension
import mimetypes

from optparse import OptionParser

from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

COMMASPACE = ', '

def main():
parser = OptionParser(usage="""\
Send the contents of a directory as a MIME message.

Usage: %prog [options]

Unless the -o option is given, the email is sent by forwarding to your local
SMTP server, which then does the normal delivery process. Your local machine
must be running an SMTP server.
""")
parser.add_option('-d','--directory',
type='string', action='store')
parser.add_option('-o', '--output',
type='string', action='store', metavar='FILE',
help="""Print the composed message to FILE instead of
sending the message to the SMTP server.""")
parser.add_option('-s', '--sender',
type='string', action='store', metavar='SENDER',
help='The value of the From: header (required)')
parser.add_option('-r', '--recipient',
type='string', action='append', metavar='RECIPIENT',
default=[], dest='recipients'),
parser.add_option('-f', '--recipientfile',
type='string', action='store', metavar='RECIPIENT_FILE',
dest='recipient_file', default="",
help='A To: header value (a file containing this)')

opts, args = parser.parse_args()
if not opts.sender or not (opts.recipient_file or opts.recipients):
    parser.print_help()
    sys.exit(1)
directory = opts.directory
if not directory:
    directory = '.'
# Create the enclosing (outer) message

try:
    rec_file = open(opts.recipient_file)
    recipients = rec_file.read()
    rec_file.close()
except IOError:
    print "/!\ Bad file. Falling back to recipent -r option"
    recipients = COMMASPACE.join(opts.recipients)

outer = MIMEMultipart()
outer['Subject'] = 'Malware submission'
outer['To'] = recipients
outer['From'] = opts.sender
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'

for filename in os.listdir(directory):
    path = os.path.join(directory, filename)
    if not os.path.isfile(path):
        continue
    # Guess the content type based on the file's extension. Encoding
    # will be ignored, although we should check for simple things like
    # gzip'd or compressed files.
    ctype, encoding = mimetypes.guess_type(path)
    if ctype is None or encoding is not None:
    # No guess could be made, or the file is encoded (compressed), so
    # use a generic bag-of-bits type.
        ctype = 'application/octet-stream'
    maintype, subtype = ctype.split('/', 1)
    if maintype == 'text':
        fp = open(path)
    # Note: we should handle calculating the charset
        msg = MIMEText(fp.read(), _subtype=subtype)
        fp.close()
    elif maintype == 'image':
        fp = open(path, 'rb')
        msg = MIMEImage(fp.read(), _subtype=subtype)
        fp.close()
    elif maintype == 'audio':
        fp = open(path, 'rb')
        msg = MIMEAudio(fp.read(), _subtype=subtype)
        fp.close()
    else:
        fp = open(path, 'rb')
        msg = MIMEBase(maintype, subtype)
        msg.set_payload(fp.read())
        fp.close()
    # Encode the payload using Base64
    encoders.encode_base64(msg)
    # Set the filename parameter
    msg.add_header('Content-Disposition', 'attachment', filename=filename)
    outer.attach(msg)
# Now send or store the message
composed = outer.as_string()
if opts.output:
    fp = open(opts.output, 'w')
    fp.write(composed)
    fp.close()
else:
    #print "Sender : " + opts.sender + ", Recipients : " + recipients #DEBUG :- Check send and recipients are correct
    s = smtplib.SMTP('localhost')
    s.sendmail(opts.sender, recipients, composed)
    s.quit()

if __name__ == '__main__':
main()

生成邮件的Python代码

1 个回答

7

当你定义你的消息时,你应该用逗号分隔的方式来列出收件人,也就是在To列表里写上所有的收件人:

recipients = 'foo, bar'
outer['To'] = recipients

不过,当你调用sendmail()这个函数时,你需要把收件人作为一个列表传递给它:

rcpts = [r.strip() for r in recipients.split(',') if r]
s.sendmail(sender, rcpts, composed)

撰写回答