Error smtplib in python - python

i've a python script, run on a raspeberry pi that monitoring my server room and periodically send me an e-mail and make other stuff.
First time script is running it send a notification and works fine, then wait for a time (5 or 30 minutues) depending if an alarm is triggered, so i've write a for loop to wait. when the loop is finish, script make a call of the function for sending e-mail and python return an error.
Could someone help me ?
i test it also on debian 7.7.0 box and i've the same error.
Below a sample script and the error.
Thanks in advance.
Dom.
File "/usr/lib/python2.7/smtplib.py", line 361, in getreply
raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed
# Import
from time import sleep
import time
import smtplib
import sys
import string
smtp_server = "10.0.0.4" # mail server address
mail_from = "server#domain.com"
mail_to = "user#gmail.com"
mail_subj = "Server room notify"
server = smtplib.SMTP(smtp_server)
valore = 24 # value for the funtion
def SendAlertEmail(t_value):
log_t = time.strftime("%d/%m/%A %H:%M:%S ")
Text = string.join("Server room notify \n " + str(log_t) + str(t_value))
Body_email = string.join((
"From: %s" % mail_from,
"To: %s" % mail_to,
"Subject: %s" % mail_subj,
"",
Text
), "\r\n")
server.sendmail(mail_from,mail_to,Body_email)
return
SendAlertEmail(valore) # calling from here works fine
for i in range(0,500):
sleep(1)
print(str(i))
SendAlertEmail(valore) # after 500 secs goes in error. Test on debian 7.7.0 python 2.7.3 gcc 4.7.2

smtplib opens a TCP connection at the creation of the SMTP object. You try to reuse it after 500 seconds of inactivity when it is likely that a timeout has already closed the connection.
You should keep only the server name, an create a new SMTP object for each connection in your use case, because the delay between sending messages is too high.

As Serge Ballesta mentions, you should open and close the connection to the server for each one the sent e-mails.
Some months ago I programmed a class to send e-mails in Python, which you can find here: https://github.com/kmundnic/Habitus/blob/master/data_sender.py You create a DataSender object and it handles the connections to the server. Also, it calls a JSON file where the accounts and password is stored, so you don't save it in your code.
Feel free to use it. It was adapted from http://kutuma.blogspot.com/2007/08/sending-emails-via-gmail-with-python.html, as it's mentioned in the comments at the beginning of the source code.

Related

Socket Errors while Setting up Non-Login SMTP Server for error email with Python

I have a python script that runs automatically weekly. I want the script to send an e-mail if it encounters an error (such as from a locked or missing file). For obvious security concerns, I don't want to use an external SMTP server that requires a login.
Using: Python 3 (script will run with Python 2.7.5 though, using 3 because my Spyder is configured to 3 and I prefer to write in it), Windows Server 2012
I believe my issue is a blocked port, but I'm inexperienced and can't really understand the responses I get from various nelnet commands. I want to verify that the python code I'm using, and the error I'm getting, is in fact due to a blocked port and not something else.
I run the code in python's base IDE using Run As Administrator. It returns: ('Unexpected error:', ).
I've tried with ports 25 & 80, but I'm basically shooting randomly at ports as I don't know if (1) that's the issue or (2) how to interpret the 20+ lines in the CMD I get when I put in netstat -an |find /i "25" for example.
# Import modules
import sys, arcpy, datetime, traceback
import os
import shutil
# Import the email libraries
import smtplib, smtpd, asyncore, threading, ctypes
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
try:
if is_admin():
print("You an admin yay!")
# set datetime variable
d = datetime.datetime.now()
sdeTempPath = r"C:\tempUSIC"
if not os.path.exists(sdeTempPath):
os.mkdir(sdeTempPath)
print("Temporary directory not found. A new directory has been " + \
"created at {0}.".format(sdeTempPath))
else:
print("The temp USIC directory already exists at {0} and will be used.".format(sdeTempPath))
# open log file for holding errors
# log code skipped
##--------Set up SMTP Server Local
smtpServer = smtpd.SMTPServer(('localhost',80), None)
loop_thread = threading.Thread(target=asyncore.loop, name="Asyncore Loop")
loop_thread.start()
##Set the Email Server Information-------------------
# Set the server object that has the Outlook SMTP and port
fromAddr = 'robert.domiano#spireenergy.com'
toAddr = 'robert.domiano#spireenergy.com'
text = 'This is a test of sending email from within python.'
server = smtplib.SMTP('localhost',80)
server.ehlo()
server.starttls()
server.sendmail(fromAddr, toAddr, text)
server.quit()
else:
# This is the python 3 version
print("You not an admin boo.")
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
####main code stuff skipped here####
except:
#Rest of except code skipped
print("Unexpected error:", sys.exc_info()[0])

Python: check mail with smtplib and handle errors

I need to check emails programmatically. I'm using this code:
resolver = dns.resolver.Resolver()
resolver.timeout = 3
resolver.lifetime = 3
mx_records = resolver.query(domain, 'MX')
for mxRecord in mx_records:
mxRecord = str(mxRecord.exchange)
host = socket.gethostname()
server = smtplib.SMTP(timeout=30)
server.set_debuglevel(0)
addressToVerify = var0
server.connect(mxRecord)
server.helo(host)
server.mail('me#domain.com')
code, message = server.rcpt(str(addressToVerify))
server.quit()
res_email = 'Not Found'
if code == 250:
result = 'Success'
else:
print "doesn't exist"
I'm receiving an error when I run this code:
Connection unexpectedly closed
Connection unexpectedly closed: [Errno 10054]
[Errno 10061]
I have tried to change values of timeout, but it didn't help. I then tried to specify the port in smtplib.SMTP(), but specified 587 and that didn't resolve the issue either.
How can I solve this problem?
You can copy the code to interrogate a POP3 server from the Python help file. Here it is in ever-so-slightly modified form.
import poplib, getpass
pop = poplib.POP3('***********', port=110)
pop.user('**********')
pop.pass_('*********')
print (pop.stat())
print (pop.list()[1])
You have only to copy various constants such as the port number from your mail client.
What this script produces as output might surprise you, depending on the settings in your mail client. Here's what I just got.
(43, 1104784)
[b'1 15848', b'2 7889', b'3 7938', b'4 3705', b'5 28933', b'6 35479', b'7 12793', b'8 7094', b'9 10045', b'10 12793', b'11 17194', b'12 312802', b'13 12688', b'14 18431', b'15 24454', b'16 17769', b'17 16223', b'18 14975', b'19 11951', b'20 13373', b'21 34949', b'22 23647', b'23 14958', b'24 22449', b'25 5068', b'26 14920', b'27 8611', b'28 10982', b'29 14311', b'30 7477', b'31 3852', b'32 8497', b'33 4086', b'34 7240', b'35 69458', b'36 19430', b'37 110263', b'38 2434', b'39 12043', b'40 5306', b'41 10288', b'42 17164', b'43 74974']
But I know that no messages remain on the server that I haven't received in my mail client. The answer to this minor puzzle is that my email client leaves messages on the server for a fortnight, or until I delete them, by default, presumably as a safeguard against loss in case of some kind of computer failure.
This implies that it would be insufficient to simply use the above output as a check for new messages. It would be necessary to record dates from headers from received messages for up to a fortnight for comparison with those on the server.

Accessing Gmail SMTP via Python - AUTH issues

I'm writing this currently for an assignment but years back I had the same problem and just gave up. Further searches had me try a variety of things and I cannot get past the login portion.
from socket import *
from ssl import *
msg = "\r\n I love computer networks!"
endmsg = "\r\n.\r\n"
clientSocket = socket(AF_INET, SOCK_STREAM)
mailserver = getaddrinfo('smtp.gmail.com',465, AF_INET, SOCK_STREAM, IPPROTO_TCP)[0][4]
clientSocket = wrap_socket(clientSocket)
clientSocket.connect(mailserver)
recv = clientSocket.recv(1024)
print recv
if recv[:3] != '220':
print '220 reply not received from server.'
clientSocket.send('EHLO Nolan\r\n')
recv = clientSocket.recv(1024)
print recv
if recv1[:3] != '250':
print '250 reply not received from server.'
clientSocket.send('AUTH LOGIN ' + 'my Google email'.encode('base64','strict'))
recv = clientSocket.recv(1024)
print recv
print recv.split()[1].decode('base64')
clientSocket.send("my password".encode('base64','strict'))
recv = clientSocket.recv(1024)
print recv
# And onto other tasks
I'm under the impression I don't need STARTTLS as I start the connection with SSL. If I change AUTH LOGIN to AUTH PLAIN it doesn't know how to decode it. If I use PLAIN but don't encode it my program just hangs. If I terminate it with "\n\r" (not encoded) I get a broken pipe. Using the code above I get the password request.
Then sending the password I get 535-5.7.8 Username and Password not excepted.It's the same password I sign in with?!? I already setup my account to allow less secure devices. 2-step verification... started the process seems to be orientated to mobile phones, not my python app.
I've changed EHLO to use my IP, as I believe RFC 5321 says that's how it should be. Didn't matter.
I've looked through RFC 4954...
I dug into smtplib to uncover "AUTH PLAIN " + encode_base64("\0%s\0%s" % (user,pass),eol='') Just hangs... waiting...
I have no clue.
Update
I changed the smtplib to output every string submitted. This confuses me even more:
ehlo [127.0.1.1]
AUTH PLAIN [almost forgot one can decode this base64]==
mail FROM:<[gmail account]>
rcpt TO:<[hotmail account]>
data
quit
My AUTH string is exactly the same but it hangs. And I am receiving the email at my hotmail account (using smtplib, not my code).
If you want a basic, non-SSL connection to Gmail, you can use this:
import smtplib
gmail = smtplib.SMTP("smtp.gmail.com",587)
gmail.ehlo() #open the connection
gmail.starttls()
gmail.login("gmaillogin","gmailpassword") #login*
gmail.quit() #close the connection
*Note: You will need to generate a gmail application password from your google account.
Edit:
If you want to use SSL encryption, use this (thanks to furas):
import smtplib
gmail = smtp.SMTP_SSL("smtp.gmail.com",465)
gmail.ehlo()
gmail.login("gmaillogin","gmailpassword")
gmail.quit()
As is typically my fashion, it's the simple things. I just spent hours looking over one program and Googling only to have one error pop out at me: the parameter order in my definition was not how I used it in my code.
Here... I changed my code to use AUTH PLAIN and the issue... I had been putting in '\n\r' not '\r\n' (if I had ended the command with anything, only omitted because '\n\r' produced errors on some commands and went through okay on others). Thank you for classical music to study by on your favorite streaming video site. It raised my intelligence briefly. :)
clientSocket.send('AUTH PLAIN ' + encode_base64("\0%s\0%s" % ([email],[password]), eol="") + '\r\n')

Sending a string to a program

I am trying to send a string from a program 1 to another program program 2, both in python 3
e.g.
#_____________________________________1.py
a = input('Type in a string: ')
# send somehow a string a from this program
# to the second program
I want to somehow send a string a to my second program so it will print out a:
#_____________________________________2.py
# receive somehow a string from the first
# program and store it in a
print(a)
How do I do this?
I am still a beginner programmer and would love it if you could help me.
I need to be able to enter the string in 1.py
I need then be able to access the string that I entered from 2.py.
I have to have them as two separate files.
ANSWER:
I found a way to solve this.
import subprocess
username = input()
subprocess.Popen(['python.exe', 'file.py', username])
You have many way communicate between two or N python program, Ex:
Socket
Database - MySQL, Mongodb, SQL Server... etc
or maybe you can try ZeroMQ
There are multiple ways to do that, you could use socket, file, pipe, shared-memory, message, ... to transfer a string from one process to another.
As an example of using messages, ZeroMQ provides an easy messaging library to do that smarter, than with system (raw, low level) sockets:
for more details look into http://zguide.zeromq.org/
A HelloWorld server example:
import time
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# Wait for next request from client
message = socket.recv()
print("Received request: %s" % message)
# Do some 'work'
time.sleep(1)
# Send reply back to client
socket.send(b"World")
A HelloWorld client example:
import zmq
context = zmq.Context()
# Socket to talk to server
print("Connecting to hello world server…")
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
# Do 10 requests, waiting each time for a response
for request in range(10):
print("Sending request %s …" % request)
socket.send(b"Hello")
# Get the reply.
message = socket.recv()
print("Received reply %s [ %s ]" % (request, message))
With files, you write a file with program A then poll on it with program B.
# file_1.py
def get_input():
return input('Type in a string: ')
# file_2.py
from file_1 import get_input
print(get_input())
Most common way of two programs communicating together is through http, tcp or other protocol. The same way as your browser (one program) communicates with the web server (another program).
You can send http request from one program and the second has to listen for that.
If you want more info, look for SOA. It is a bit more complicated than that, so if you have any questions, ask.
I found the answer.
import subprocess
username = input()
subprocess.Popen(['python.exe', 'file.py', username], subprocess.creationflags=CREATE_NEW_CONSOLE)

Python sending email too slow

I have the script below which I'm using to send say 10 messages myself<->myself. However, I've noticed that Python really takes a while to do that. Last year I needed a system to send about 200 emails with attachments and text and I implemented it with msmtp + bash. As far as I remember it was much faster.
Moving the while loop inside (around the smtp_serv.sendmail(sender, recepient, msg) function yields similar results).
Am I doing something wrong? Surely it can't be slower than bash + msmtp (and I'm only sending a 'hi' message, no attachments).
#! /usr/bin/python3.1
def sendmail(recepient, msg):
import smtplib
# Parameters
sender = 'login#gmail.com'
password = 'password'
smtpStr = 'smtp.gmail.com'
smtpPort = 587
# /Parameters
smtp_serv = smtplib.SMTP(smtpStr, smtpPort)
smtp_serv.ehlo_or_helo_if_needed()
smtp_serv.starttls()
smtp_serv.ehlo()
recepientExists = smtp_serv.verify(recepient)
if recepientExists[0] == 250:
smtp_serv.login(sender, password)
try:
smtp_serv.sendmail(sender, recepient, msg)
except smtplib.SMTPException:
print(recepientExists[1])
else:
print('Error', recepientExists[0], ':', recepientExists[1])
smtp_serv.quit()
for in in range(10):
sendmail('receiver#gmail.com', 'hi')
In this script it takes five times more time to setup SMTP connection (5 seconds) than to send a e-mail (1 second) so it could make sense to setup a single connection and send several e-mails instead of creating the connection each time:
#!/usr/bin/env python3
import smtplib
from contextlib import contextmanager
from datetime import datetime
from email.mime.text import MIMEText
from netrc import netrc
from timeit import default_timer as timer
#contextmanager
def logined(sender, password, smtp_host='smtp.gmail.com', smtp_port=587):
start = timer(); smtp_serv = smtplib.SMTP(smtp_host, smtp_port, timeout=10)
try: # make smtp server and login
smtp_serv.ehlo_or_helo_if_needed()
smtp_serv.starttls()
smtp_serv.ehlo()
print('smtp setup took (%.2f seconds passed)' % (timer()-start,))
start = timer(); smtp_serv.login(sender, password)
print('login took %.2f seconds' % (timer()-start,))
start = timer(); yield smtp_serv
finally:
print('Operations with smtp_serv took %.2f seconds' % (timer()-start,))
start = timer(); smtp_serv.quit()
print('Quiting took %.2f seconds' % (timer()-start,))
smtp_host = 'smtp.gmail.com'
login, _, password = netrc().authenticators(smtp_host)
with logined(login, password, smtp_host) as smtp_serv:
for i in range(10):
msg = MIMEText('#%d timestamp %s' % (i, datetime.utcnow()))
msg['Subject'] = 'test #%d' % i
msg['From'] = login
msg['To'] = login
smtp_serv.send_message(msg)
Output
smtp setup took (5.43 seconds passed)
login took 0.40 seconds
Operations with smtp_serv took 9.84 seconds
Quiting took 0.05 seconds
If your Python version doesn't have .send_message() then you could use:
smtp_serv.sendmail(from, to, msg.as_string())
You are opening the connection to the SMTP server and then closing it for each email. It would be more efficient to keep the connection open while sending all of the emails.
Maybe this comes very late, but I think it is relevant for the matter.
I had the same issue recently and realized, by searching around, that the call to connect the SMTP server may be very time consuming due to issues with domain name resolution, since the SMTP server performs a reverse lookup to verify the connecting client.
In my case this call was taking around 1 minute!:
s = smtplib.SMTP(smtp_server)
Solution was to fix the domain name resolution on the Linux box. After that, connection became very quick.
Hope this may be of help.
The real answer here is "profile that code!". Time how long different parts of the code take so you know where most of the time is spent. That way you'll have a real answer without guesswork.
Still, my guess would be that it is the calls to smtp_serv.verify(recipient) may be the slow ones. Reasons might be that the server sometimes needs to ask other SMTP servers for info, or that it does throttling on these operations to avoid having spammers use them massively to gather email addresses.
Also, try pinging the SMTP server. If the ping-pong takes significant time, I would expect sending each email would take at least that long.

Categories