Senden von E-Mails über sendmail aus Python

77

Wenn ich E-Mails nicht über SMTP, sondern über sendmail senden möchte, gibt es eine Bibliothek für Python, die diesen Prozess kapselt?

Besser noch, gibt es eine gute Bibliothek, die die gesamte Auswahl von "sendmail -versus- smtp" abstrahiert?

Ich werde dieses Skript auf einer Reihe von Unix-Hosts ausführen, von denen nur einige auf localhost lauschen: 25; Einige davon sind Teil eingebetteter Systeme und können nicht für die Annahme von SMTP eingerichtet werden.

Als Teil von Good Practice möchte ich wirklich, dass die Bibliothek sich selbst um die Schwachstellen bei der Header-Injektion kümmert. Wenn Sie also nur einen String an ablegen, popen('/usr/bin/sendmail', 'w')ist dies etwas näher am Metall als ich möchte.

Wenn die Antwort lautet "Schreib eine Bibliothek", dann sei es so ;-)

Nate
quelle

Antworten:

126

Die Header-Injektion ist kein Faktor für das Senden der E-Mail, sondern ein Faktor für die Erstellung der E-Mail. Überprüfen Sie das E-Mail- Paket, erstellen Sie die E- Mail damit, serialisieren Sie sie und senden Sie sie /usr/sbin/sendmailmithilfe des Unterprozessmoduls an :

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())
Jim
quelle
Perfekt. Mein konzeptionelles Problem bestand darin, den Aufbau und das Versenden von E-Mails zu trennen. Vielen Dank!
Nate
3
schön, es hat mir heute geholfen, nach 5 Jahren der Antwort :)
Peter
14
Sie sollten den -oiParameter auch zum Senden von E-Mails verwenden. Dadurch wird verhindert, dass eine einzelne .Nachricht in Ihrer Nachricht die E-Mail vorzeitig beendet.
Robie Basak
26
Python3-Benutzer sollten .as_bytes()anstelle vonas_string()
ov7a
Ich bin verwirrt von der -oi. -oEs sieht so aus, als würde es Verarbeitungsoptionen -i
festlegen
36

Dies ist eine einfache Python-Funktion, die das Unix sendmail verwendet, um eine E-Mail zuzustellen.

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
Pieter
quelle
2
Sie sagten ausdrücklich, sie wollten keine popenLösungen im Stil. Schlimmer noch, der angegebene Grund war, Dinge wie Schwachstellen bei der Header-Injektion zu vermeiden. Wenn der Benutzer entweder die Absender- oder die Absenderadresse angibt, ist dieser Code anfällig für Header-Injection-Angriffe. Genau das wollten sie nicht.
Jim
6
@ Jim ja, sie haben diesen Teil speziell bearbeitet, nachdem meine Antwort gegeben wurde (siehe Bearbeitungsdaten), als Antwort auf meine Antwort, nehme ich an.
Pieter
@Pieter wenn ich Anhang senden muss?
Hariom Singh
12

Jims Antwort hat in Python 3.4 bei mir nicht funktioniert. Ich musste ein zusätzliches universal_newlines=TrueArgument hinzufügensubrocess.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())

Ohne das universal_newlines=Truebekomme ich

TypeError: 'str' does not support the buffer interface
MEI
quelle
5

Diese Frage ist sehr alt, aber es ist erwähnenswert, dass es ein System zur Erstellung von Nachrichten und zur E-Mail-Zustellung namens Marrow Mailer (früher TurboMail) gibt, das seit dem Zeitpunkt verfügbar ist, bevor diese Nachricht gestellt wurde.

Es wird jetzt portiert, um Python 3 zu unterstützen, und als Teil der Marrow- Suite aktualisiert .

amcgregor
quelle
Die turboMail-Verbindung ist ein Jahr später nicht mehr
verfügbar
1
+1 für Marrow Mailer. Ich habe es ausprobiert und es macht es sehr einfach, E-Mails über smpt, sendmail usw. zu senden, und bietet auch eine sehr gute Validierung. Da Marrow Mailer "eine gute Bibliothek ist, die die gesamte Auswahl von" sendmail -versus- smtp "abstrahiert", ist dies meiner Meinung nach die beste Antwort auf die ursprüngliche Frage.
Tjklemz
3

Es ist durchaus üblich, nur den Befehl sendmail von Python mit os.popen zu verwenden

Für Skripte, die ich nicht selbst geschrieben habe, halte ich es für besser, nur das SMTP-Protokoll zu verwenden, da für die Ausführung unter Windows beispielsweise kein sendmail-Klon installiert werden muss.

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

tovare
quelle
1
SMTP funktioniert nicht auf ~ nix-Boxen mit Dingen wie DMA , das sendmail bereitstellt, aber lauscht nicht auf Port 25 ...
Gert van den Berg
3

Python 3.5+ Version:

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())
Robin Stewart
quelle
-2

Ich habe mich nur nach dem gleichen Thema umgesehen und auf der Python-Website ein gutes Beispiel gefunden: http://docs.python.org/2/library/email-examples.html

Von der genannten Seite:

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

Beachten Sie, dass dafür sendmail / mailx korrekt eingerichtet sein muss, um Verbindungen auf "localhost" zu akzeptieren. Dies funktioniert standardmäßig auf meinen Mac-, Ubuntu- und Redhat-Servern. Möglicherweise möchten Sie jedoch überprüfen, ob Probleme auftreten.

elec3647
quelle
Dies wird immer noch localhostüber das SMTP-Protokoll verwendet. Und einige Benutzer möchten Port 21 nicht öffnen, auch nicht nur auf localhost.
Geburt
-7

Die einfachste Antwort ist die smtplib, können Sie Dokumente auf ihm finden hier .

Alles, was Sie tun müssen, ist, Ihr lokales sendmail so zu konfigurieren, dass es eine Verbindung von localhost akzeptiert, was wahrscheinlich bereits standardmäßig der Fall ist. Sicher, Sie verwenden immer noch SMTP für die Übertragung, aber es ist das lokale Sendmail, das im Grunde das gleiche ist wie das Kommandozeilen-Tool.

Frank Wiles
quelle
7
Frage sagt "Wenn ich Mail nicht über SMTP, sondern über sendmail senden möchte ..."
S19N
1
Ihr Programm verzögert sich, falls keine Verbindung oder Verzögerung zum SMTP-Server besteht. Mit sendmail übergeben Sie eine Nachricht an MTA und Ihr Programm wird fortgesetzt.
Denis Barmenkov