I’m trying to send myself email from my own Linux machine, via SMPT. I’ve written the following Python program:
#!/usr/bin/env python3
# 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.
with open(“test.txt”) as fp:
# Create a text/plain message
msg = MIMEText(fp.read())# me == the sender’s email address
# you == the recipient’s email address
msg[‘Subject’] = “Test”
msg[‘From’] = “simberg@interglobal.org”
msg[‘To’] = “simberg@interglobal.org”# Send the message via our own SMTP server.
s = smtplib.SMTP(‘localhost’)
print(“s has been opened”)
s.send_message(msg)
print(“after send_message”)
s.quit()
print(“End script”)
It runs with no errors, prints the statements to the terminal, but no email appears. How do I diagnose this?
[Update a while later]
Thanks for the help in comments. Still haven’t solved it, but the need for the application that I needed it for has gone away. Nonetheless, it would be good in general to know how to email from a Python script.
I’d start with making sure your SMTP server/daemon is working. To see how see the following:
https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_transport_example
If this doesn’t respond as described make sure the daemon that runs SMTP is running on your server. This should be an rc.init script entry. Depending upon the version of Linux you are using it could also be a menu config option under root.
$ telnet smtp.mydomain.com 25
To get started with the above…
Say HELLO for me.
Sorry for the type. Supposed to be HELO.
Or HELP for that matter.
Well, I sort of assumed that the script would have an error if SMTP wasn’t running. I have started it.
Check your mail daemon’s logs?
Wild guess: Put in a 5 sec delay after the send(). Maybe there’s a queue that gets clobbered by the quit().
Also check which port your SMTP server is using. That is the optional 2nd argument smtplib.SMTP() it defaults to 25.
smtpObj = smtplib.SMTP( [host [, port [, local_hostname]]]
Googling the phrase smtp ports yielded this nugget:
SMTP by default uses TCP port 25. The protocol for mail submission is the same, but uses port 587. SMTP connections secured by SSL, known as SMTPS, default to port 465 (nonstandard, but sometimes used for legacy reasons).
Well, I haven’t done anything to configure SMTP, other than install it.
its entirely possible you have successfully sent mail to your mailserver local to your device, but now, it needs to send it to the MX of interglobal.org, and if that’s not your laptop, its probably queued up inside your SMTP mailer until it learns hot to send mail out. I have no idea what sort of local mailer you have, so look inside it’s logs for delivery status. The mail could be sitting there in the queue, unable to deliver. Or, it is working just fine, and the upstream SMTP server at interglobal.org is looking at your to and from and thinking “hah, spammer”.
I’m talking to the admin for interglobal.org, and he’s not seeing anything in his logs. I’ve no idea where the logs are for smtp.
I often send email to myself as a means of file backup or move, so I don’t think that’s the problem.
Turn on smtplib lame debugprint support
s = smtplib.SMTP()
s.debuglevel = 10
s.connect(‘localhost’)
File “./mail3.py”, line 25
s.connect(‘localhost’)
^
SyntaxError: invalid character in identifier
That should be just copypaste error .. i.e. the .py file UTF8/ascii encoding or something. Zap any suspect marks and maybe change all quote marks to double quotes “. The code itself is fine.
Longer modified segment :
# Send the message via our own SMTP server.
s = smtplib.SMTP()
print(“s has been opened”)
s.debuglevel = 10
s.connect(“localhost”)
print(“s has been connected”)
s.send_message(msg)
Yeah, it pasted as a backtick. Here’s the output now:
connect: (‘localhost’, 25)
connect: to (‘localhost’, 25) None
reply: b’220 new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 13:37:28 -0700\r\n’
reply: retcode (220); Msg: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 13:37:28 -0700′
connect: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 13:37:28 -0700′
s has been opened
after send_message
End script
You have new mail in /var/spool/mail/simberg
Doesnt look like its actually writing anything to the server connection, its supposed to dump out SMTP protocol commands like EHLO etc. Not sure why – could be, could be a TLS connection is required maybe. call:
s.starttls()
or call
s.ehlo()
right after s.connect()
You can run a simpler test with :
python -m smtplib
That will just run smptplib default demo sendmail code , which is at the end here :
https://github.com/enthought/Python-2.7.3/blob/master/Lib/smtplib.py
Generally, python’s default smtplib in python is not too straightforward, i can never figure out whats wrong with it unless i step through the code in a debugger – or i use a bit more robust library/service
You can try grabbing a bit more fully developed script like this and modify accordingly too :
https://gist.github.com/dbieber/5146518
From: simberg@interglobal.org
To: simberg@interglobal.org
Enter message, end with ^D:
testing.
Message length is 10 size=10\r\n’ … Sender ok\r\n’ … Sender ok \r\n’ … Recipient ok\r\n’ … Recipient ok
send: ‘ehlo new-host-5.home\r\n’
reply: ‘250-new-host-5.home Hello localhost.localdomain [127.0.0.1], pleased to meet you\r\n’
reply: ‘250-ENHANCEDSTATUSCODES\r\n’
reply: ‘250-PIPELINING\r\n’
reply: ‘250-8BITMIME\r\n’
reply: ‘250-SIZE\r\n’
reply: ‘250-DSN\r\n’
reply: ‘250-ETRN\r\n’
reply: ‘250-AUTH GSSAPI DIGEST-MD5 CRAM-MD5\r\n’
reply: ‘250-DELIVERBY\r\n’
reply: ‘250 HELP\r\n’
reply: retcode (250); Msg: new-host-5.home Hello localhost.localdomain [127.0.0.1], pleased to meet you
ENHANCEDSTATUSCODES
PIPELINING
8BITMIME
SIZE
DSN
ETRN
AUTH GSSAPI DIGEST-MD5 CRAM-MD5
DELIVERBY
HELP
send: ‘mail FROM:
reply: ‘250 2.1.0
reply: retcode (250); Msg: 2.1.0
send: ‘rcpt TO:
reply: ‘250 2.1.5
reply: retcode (250); Msg: 2.1.5
send: ‘data\r\n’
reply: ‘354 Enter mail, end with “.” on a line by itself\r\n’
reply: retcode (354); Msg: Enter mail, end with “.” on a line by itself
data: (354, ‘Enter mail, end with “.” on a line by itself’)
send: ‘testing.\r\n\r\n.\r\n’
reply: ‘250 2.0.0 u3FL6YFa011025 Message accepted for delivery\r\n’
reply: retcode (250); Msg: 2.0.0 u3FL6YFa011025 Message accepted for delivery
data: (250, ‘2.0.0 u3FL6YFa011025 Message accepted for delivery’)
send: ‘quit\r\n’
reply: ‘221 2.0.0 new-host-5.home closing connection\r\n’
reply: retcode (221); Msg: 2.0.0 new-host-5.home closing connection
Ah, another tiny detail is to print out what the send_message() method spits back. like this
result = s.send_message(msg)
print(“after send_message”)
print(result)
after send_message
{}
Doh, i think you have a zero-length ‘TO’ address list. SMTPlist expects the input parameters on to field to be a list ( or a tuple ), not a single value
So if you change:
msg[‘To’] = “simberg@interglobal.org”
to
msg[‘To’] = [“simberg@interglobal.org”] # makes a 1-element list
or
msg[‘To’] = (“simberg@interglobal.org”,) # makes a tuple
It just might work
With square brackets:
connect: (‘localhost’, 25) size=192\r\n’ … Sender ok\r\n’ … Sender ok’ \r\n’ … Recipient ok\r\n’ … Recipient ok’
connect: to (‘localhost’, 25) None
reply: b’220 new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 14:25:33 -0700\r\n’
reply: retcode (220); Msg: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 14:25:33 -0700′
connect: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 14:25:33 -0700′
send: ‘ehlo new-host-5.home\r\n’
reply: b’250-new-host-5.home Hello localhost.localdomain [127.0.0.1], pleased to meet you\r\n’
reply: b’250-ENHANCEDSTATUSCODES\r\n’
reply: b’250-PIPELINING\r\n’
reply: b’250-8BITMIME\r\n’
reply: b’250-SIZE\r\n’
reply: b’250-DSN\r\n’
reply: b’250-ETRN\r\n’
reply: b’250-AUTH GSSAPI DIGEST-MD5 CRAM-MD5\r\n’
reply: b’250-DELIVERBY\r\n’
reply: b’250 HELP\r\n’
reply: retcode (250); Msg: b’new-host-5.home Hello localhost.localdomain [127.0.0.1], pleased to meet you\nENHANCEDSTATUSCODES\nPIPELINING\n8BITMIME\nSIZE\nDSN\nETRN\nAUTH GSSAPI DIGEST-MD5 CRAM-MD5\nDELIVERBY\nHELP’
send: ‘mail FROM:
reply: b’250 2.1.0
reply: retcode (250); Msg: b’2.1.0
send: ‘rcpt TO:
reply: b’250 2.1.5
reply: retcode (250); Msg: b’2.1.5
send: ‘data\r\n’
reply: b’354 Enter mail, end with “.” on a line by itself\r\n’
reply: retcode (354); Msg: b’Enter mail, end with “.” on a line by itself’
data: (354, b’Enter mail, end with “.” on a line by itself’)
send: b’Content-Type: text/plain; charset=”us-ascii”\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nSubject: Test\r\nFrom: simberg@interglobal.org\r\nTo: simberg@interglobal.org\r\n\r\nThis is a test.\r\n.\r\n’
reply: b’250 2.0.0 u3FLPXbD011626 Message accepted for delivery\r\n’
reply: retcode (250); Msg: b’2.0.0 u3FLPXbD011626 Message accepted for delivery’
data: (250, b’2.0.0 u3FLPXbD011626 Message accepted for delivery’)
after send_message
{}
s has been opened
after send_message
End script
[simberg@new-host-5 Python]$ ./mail3.py
connect: (‘localhost’, 25)
connect: to (‘localhost’, 25) None
reply: b’220 new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:30:47 -0700\r\n’
reply: retcode (220); Msg: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:30:47 -0700′
connect: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:30:47 -0700′
Traceback (most recent call last):
File “./mail3.py”, line 27, in
result = s.send_message(msg)
File “/usr/lib64/python3.4/smtplib.py”, line 844, in send_message
to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
File “/usr/lib64/python3.4/email/utils.py”, line 112, in getaddresses
all = COMMASPACE.join(fieldvalues)
TypeError: sequence item 0: expected str instance, list found
You have new mail in /var/spool/mail/simberg
******************************************************
With parentheses and comma:
connect: (‘localhost’, 25)
connect: to (‘localhost’, 25) None
reply: b’220 new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:30:47 -0700\r\n’
reply: retcode (220); Msg: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:30:47 -0700′
connect: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:30:47 -0700′
Traceback (most recent call last):
File “./mail3.py”, line 27, in
result = s.send_message(msg)
File “/usr/lib64/python3.4/smtplib.py”, line 844, in send_message
to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
File “/usr/lib64/python3.4/email/utils.py”, line 112, in getaddresses
all = COMMASPACE.join(fieldvalues)
TypeError: sequence item 0: expected str instance, list found
You have new mail in /var/spool/mail/simberg
[simberg@new-host-5 Python]$ ./mail3.py
connect: (‘localhost’, 25)
connect: to (‘localhost’, 25) None
reply: b’220 new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:32:14 -0700\r\n’
reply: retcode (220); Msg: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:32:14 -0700′
connect: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:32:14 -0700′
Traceback (most recent call last):
File “./mail3.py”, line 27, in
result = s.send_message(msg)
File “/usr/lib64/python3.4/smtplib.py”, line 844, in send_message
to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
File “/usr/lib64/python3.4/email/utils.py”, line 112, in getaddresses
all = COMMASPACE.join(fieldvalues)
TypeError: sequence item 0: expected str instance, tuple found
*********************************************
With parentheses, no comma:
connect: (‘localhost’, 25) size=192\r\n’ … Sender ok\r\n’ … Sender ok’ \r\n’ … Recipient ok\r\n’ … Recipient ok’
connect: to (‘localhost’, 25) None
reply: b’220 new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:32:29 -0700\r\n’
reply: retcode (220); Msg: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:32:29 -0700′
connect: b’new-host-5.home ESMTP Sendmail 8.15.2/8.15.2; Fri, 15 Apr 2016 15:32:29 -0700′
send: ‘ehlo new-host-5.home\r\n’
reply: b’250-new-host-5.home Hello localhost.localdomain [127.0.0.1], pleased to meet you\r\n’
reply: b’250-ENHANCEDSTATUSCODES\r\n’
reply: b’250-PIPELINING\r\n’
reply: b’250-8BITMIME\r\n’
reply: b’250-SIZE\r\n’
reply: b’250-DSN\r\n’
reply: b’250-ETRN\r\n’
reply: b’250-AUTH GSSAPI DIGEST-MD5 CRAM-MD5\r\n’
reply: b’250-DELIVERBY\r\n’
reply: b’250 HELP\r\n’
reply: retcode (250); Msg: b’new-host-5.home Hello localhost.localdomain [127.0.0.1], pleased to meet you\nENHANCEDSTATUSCODES\nPIPELINING\n8BITMIME\nSIZE\nDSN\nETRN\nAUTH GSSAPI DIGEST-MD5 CRAM-MD5\nDELIVERBY\nHELP’
send: ‘mail FROM:
reply: b’250 2.1.0
reply: retcode (250); Msg: b’2.1.0
send: ‘rcpt TO:
reply: b’250 2.1.5
reply: retcode (250); Msg: b’2.1.5
send: ‘data\r\n’
reply: b’354 Enter mail, end with “.” on a line by itself\r\n’
reply: retcode (354); Msg: b’Enter mail, end with “.” on a line by itself’
data: (354, b’Enter mail, end with “.” on a line by itself’)
send: b’Content-Type: text/plain; charset=”us-ascii”\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nSubject: Test\r\nFrom: simberg@interglobal.org\r\nTo: simberg@interglobal.org\r\n\r\nThis is a test.\r\n.\r\n’
reply: b’250 2.0.0 u3FMWTEk013027 Message accepted for delivery\r\n’
reply: retcode (250); Msg: b’2.0.0 u3FMWTEk013027 Message accepted for delivery’
data: (250, b’2.0.0 u3FMWTEk013027 Message accepted for delivery’)
after send_message
{}
s has been opened
after send_message
End script
****************************
In none of the cases is mail sent.
Well, the python script part looks okay, as you are actually getting 250 Message accepted for delivery back from your local Sendmail server instance.
Why is your local sendmail not sending anything out is a different story.
If you point the python script at another properly routing STMP server it’l send it out – but a remote service will require account authentication as shown in the gist.github link with the gmail example.
send: ‘mail FROM: size=192\r\n’
reply: b’250 2.1.0 … Sender ok\r\n’
reply: retcode (250); Msg: b’2.1.0 … Sender ok’
send: ‘rcpt TO:\r\n’
reply: b’250 2.1.5 … Recipient ok\r\n’
Unless you or the logger are stripping out the email address, you’re not actually specifying a sender or a recipient. The log should have read
send: ‘rcpt TO: <simberg@interglobal.org>\r\n’
Same for the sender.
I’ve done something similar before with os.system(‘mail [stuff]’); Basically, calling the command line mail application.