ssl.SSLEOFError while sending emails through Django - django

Settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER = 'no_reply#domain.com'
SERVER_EMAIL = 'no_reply#domain.com'
DEFAULT_DO_NOT_REPLY = 'User <no_reply#domain.com>'
EMAIL_HOST_PASSWORD = 'xxxxxxxxx'
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
Class
class EmailThread(threading.Thread):
def __init__(self, subject, context, recipient, template):
self.subject = subject
self.recipient = recipient
self.message = get_template(template).render(context)
threading.Thread.__init__(self)
def run(self):
msg = EmailMessage(
subject=self.subject,
from_email=settings.DEFAULT_DO_NOT_REPLY,
to=self.recipient,
body=self.message
)
msg.content_subtype = "html"
try:
msg.send(fail_silently=False)
log.info("E-mail triggered. Subject: '%s', to: %s" % (self.subject, self.recipient))
except Exception as e:
log.exception(e)
Usage
def notify_admin_blocked_account(user):
"""
Sends sends an email when the account is blocked
:return:
"""
email = EmailThread(
subject='{}, your account has been blocked'.format(user),
context={
'user': user,
'login_attempts': settings.MAX_STAFF_PWD_ATTEMPTS,
},
recipient=[user.email,],
template='mail/notify_admin_blocked_account.html'
)
email.start()
Error
[ERROR][2017-08-01 10:40:26,835][mail] EOF occurred in violation of protocol (_ssl.c:645)
Traceback (most recent call last):
File "./bin/mail.py", line 27, in run
msg.send(fail_silently=False)
File "/home/web/sites/envs/project/lib/python3.5/site-packages/django/core/mail/message.py", line 348, in send
return self.get_connection(fail_silently).send_messages([self])
File "/home/web/sites/envs/project/lib/python3.5/site-packages/django/core/mail/backends/smtp.py", line 104, in send_messages
new_conn_created = self.open()
File "/home/web/sites/envs/project/lib/python3.5/site-packages/django/core/mail/backends/smtp.py", line 69, in open
self.connection.starttls(keyfile=self.ssl_keyfile, certfile=self.ssl_certfile)
File "/usr/lib/python3.5/smtplib.py", line 766, in starttls
server_hostname=self._host)
File "/usr/lib/python3.5/ssl.py", line 377, in wrap_socket
_context=self)
File "/usr/lib/python3.5/ssl.py", line 752, in __init__
self.do_handshake()
File "/usr/lib/python3.5/ssl.py", line 988, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.5/ssl.py", line 633, in do_handshake
self._sslobj.do_handshake()
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:645)
I have some weird behavior when I try to send an e-mail (async) with Django. I really wanted to avoid third-party apps being installed to reduce code fragmentation - specially with some of them that seem to use crontab.
It is worth noticing that the error doesn't happen all the time. However, even though it doesn't happen, sometimes e-mails are not sent either. Only some of them manage to go through.
On my development environment it runs fine, and I can send the e-mails. However, on my production environment (EC2 instance) it simply doesn't work.
If I trigger an e-mail through the normal process, it is not sent. If I login to the server, open the manage.py shell, import a project and call the function that sends e-mail, sometimes I receive both triggered and manually-triggered e-mails or get an error (the one bellow).

My solution was to use Django-Mailer. It fragments the project, something I wanted to avoid, but its usage is pretty minimalist.

Related

Sending Confirmation Emails with Flask, RQ (Working outside of application context.)

I am trying to send confirmation emails when a new user registers. When I send a confirmation email, everything works fine, however, when I try to run that task in the background I get the following error:
File "/Users/martifont/Dev/bz/./FlaskApp/utils.py", line 22, in send_confirmation_email
msg = Message()
I have tried running a different task in the background with no issues at all.
users.py
#imports
from .utils import send_reset_email, send_confirmation_email, example
users = Blueprint('users', __name__)
#SIGNUP
#users.route('/signup', methods=\['GET', 'POST'\])
def signup():
form = SignupForm()
if form.validate_on_submit():
user = User(email=form.email.data, username=form.username.data, confirmed=False, is_admin=False, password = generate_password_hash(form.password.data, method='sha256'))
db.session.add(user)
db.session.commit()
user_id=user.id
with app.app_context():
job = q.enqueue(send_confirmation_email, user)
login_user(user)
flash('A confirmation email has been sent to your inbox.', 'is-success')
return redirect(url_for('users.account', id=user.id))
return render_template('signup.html', form=form)
utils.py
def send_confirmation_email(user):
token = user.get_confirmation_token()
msg = Message()
msg.subject = ('Email Confirmation')
msg.sender = 'MAIL_DEFAULT_SENDER'
msg.recipients = [user.email]
msg.body = f'''
Welcome { user.username }!,
Thanks for signing up. Please follow this link to activate your account:
{url_for('users.confirm_email', token=token, _external=True)}
Thanks!
'''
mail.send(msg)
EDIT
After following Sebastian's advice, I think the issue is solved, however, I am still getting the following error message.
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 2548, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 2528, in wsgi_app
response = self.handle_exception(e)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 2525, in wsgi_app
response = self.full_dispatch_request()
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 1822, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 1820, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 1796, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/Users/martifont/Dev/bz/FlaskApp/users.py", line 30, in signup
job = q.enqueue(send_confirmation_email, args=(app, user))
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/queue.py", line 514, in enqueue
return self.enqueue_call(
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/queue.py", line 410, in enqueue_call
return self.enqueue_job(job, pipeline=pipeline, at_front=at_front)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/queue.py", line 572, in enqueue_job
job.save(pipeline=pipe)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/job.py", line 694, in save
mapping = self.to_dict(include_meta=include_meta)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/job.py", line 629, in to_dict
'data': zlib.compress(self.data),
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/job.py", line 305, in data
self._data = self.serializer.dumps(job_tuple)
When you are in a view or inside the creat_app factory function there is no need to use the app_context() context manager.
The traceback defines where the error is been emited and that is when you instantiated the msg=Message() variable so instead of calling the app context inside the view i suggest you to refactor your view and send functions like so:
users.py
from flask import current_app
#users.route('/signup', methods=\['GET', 'POST'\])
def signup():
form = SignupForm()
if form.validate_on_submit():
user = User(email=form.email.data, username=form.username.data, confirmed=False, is_admin=False, password = generate_password_hash(form.password.data, method='sha256'))
db.session.add(user)
db.session.commit()
user_id=user.id
# pass the current app object to the send_confirmation_email function.
# Its important to pass the object and no the proxy
app = current_app._get_current_object()
job = q.enqueue(send_confirmation_email, args=(app, user))
login_user(user)
flash('A confirmation email has been sent to your inbox.', 'is-success')
return redirect(url_for('users.account', id=user.id))
return render_template('signup.html', form=form)
utils.py
def send_confirmation_email(app, user):
with app.app_context():
token = user.get_confirmation_token()
msg = Message()
msg.subject = ('Email Confirmation')
msg.sender = 'MAIL_DEFAULT_SENDER'
msg.recipients = [user.email]
msg.body = f'''
Welcome { user.username }!,
Thanks for signing up. Please follow this link to activate your account:
{url_for('users.confirm_email', token=token, _external=True)}
Thanks!'''
mail.send(msg)

Django Email Timeout on Production Server

I recently deployed a site that I developed in Django onto a production server running Ubuntu 19.10 and Apache/2.4.41. I've been able to get my site functioning in every respect with the exception of sending emails using SMTP. When running my site on the development server on my local machine Django is able to send emails without issue, but on the production server any attempt by the site to send an email hangs and eventually results in a server error. In Apache's error.log I'm finding this at the bottom of the traceback: TimeoutError: [Errno 110] Connection timed out. Curiously, this problem persists if I turn on port 8000 and run this site with Django's development server from the remote machine.
I've made sure that my settings.py is configured properly (as I mentioned this works fine on my local computer).
settings.py
...
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = site_config.get('EMAIL_USER')
EMAIL_HOST_PASSWORD = site_config.get('EMAIL_PASS')
My suspicion has been that the issue must be related to my firewall blocking outgoing traffic on port 587, but I double checked my settings and couldn't see how this could be the case. I even manually set rules to allow outgoing traffic on port 587 and 25 to be sure, but did not have any success.
ufw status
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
587 ALLOW OUT Anywhere
25/tcp ALLOW OUT Anywhere
587 (v6) ALLOW OUT Anywhere (v6)
25/tcp (v6) ALLOW OUT Anywhere (v6)
I've tried testing that I'm able to establish SMTP connections at all using the advice offered in this post and got a timeout error there as well.
All of this seems to point to some sort of error in the way I've configured my firewall and/or Apache, but, after reading a number of posts on SO and elsewhere, I feel I've run out of ideas.
Lastly, here's what I'm seeing in the error log:
/var/log/apache2/error.log
log: ERROR - Internal Server>
Traceback (most recent call last):
File "/home/user/mysite/venv>
response = get_response(request)
File "/home/user/mysite/venv>
response = self.process_exception_by_middleware(e,>
File "/home/user/mysite/venv>
response = wrapped_callback(request, *callback_arg>
File "/home/user/mysite/venv>
return self.dispatch(request, *args, **kwargs)
File "/home/user/mysite/venv>
return bound_method(*args, **kwargs)
File "/home/user/mysite/venv>
response = view_func(request, *args, **kwargs)
File "/home/user/mysite/venv>
return super().dispatch(*args, **kwargs)
File "/home/user/mysite/venv>
return handler(request, *args, **kwargs)
File "/home/user/mysite/venv>
return self.form_valid(form)
File "/home/user/mysite/venv>
form.save(**opts)
File "/home/user/mysite/venv>
user_email, html_email_template_name=html_email_te>
File "/home/user/mysite/venv>
email_message.send()
File "/home/user/mysite/venv>
return self.get_connection(fail_silently).send_mes>
File "/home/user/mysite/venv>
new_conn_created = self.open()
File "/home/user/mysite/venv>
self.connection = self.connection_class(self.host,>
File "/usr/lib/python3.7/smtplib.py", line 251, in _>
(code, msg) = self.connect(host, port)
File "/usr/lib/python3.7/smtplib.py", line 336, in c>
self.sock = self._get_socket(host, port, self.time>
File "/usr/lib/python3.7/smtplib.py", line 307, in _>
self.source_address)
File "/usr/lib/python3.7/socket.py", line 727, in cr>
raise err
File "/usr/lib/python3.7/socket.py", line 716, in cr>
sock.connect(sa)
TimeoutError: [Errno 110] Connection timed out
I'd happily add more information to this if there is anything important that I've left out. This is my first attempt at deploying a site to a remote server so keep in mind this is all a bit new to me.
If you use Linode to deploy your website, note that the port 587 is blocked by default.
It took me 2 days to figure it out.

Not sure why my email script is not sending an email

It was working but after updating my Raspberry Pi, my python script to send an email is not sending and I'm not sure why. It seems to just hang without throwing any error messages and I have to Ctrl+C to stop it every time, otherwise it'll just sit there indefinitely.
Here is my code...
import smtplib, datetime
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
email_send = [sender email]
email_receive = [my email]
password = [password]
subject = 'Test Subject'
#sYMD = datetime.date.today().strftime('%y%m%d')
#lpath = 'C:/Path/to/files/'
files = ['log1.txt', 'log2.txt']
msg = MIMEMultipart()
msg['From'] = email_send
msg['To'] = email_receive
msg['Subject'] = subject
body = """\
Test Message."""
msg.attach(MIMEText(body, 'plain'))
#filename = x.strftime("%y%m%d")+'_log.txt'
for file in files:
part = MIMEBase('application', "octet-stream")
part.set_payload(open(file, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',
'attachment; filename="%s"' % file)
msg.attach(part)
text = msg.as_string()
server = smtplib.SMTP('smtp.office365.com', 587)
server.starttls()
server.login(email_send, password)
server.sendmail(email_send, email_receive, text)
server.quit()
Here is what its doing when I run the script...
pi#raspberrypi: python send_email.py
^CTraceback (most recent call last):
File "send_email.py", line 36, in <module>
server = smtplib.SMTP('smtp.office365.com', 587)
File "/usr/lib/python2.7/smtplib.py", line 265, in __init__
(code, msg) = self.connect(host, port)
File "/usr/lib/python2.7/smtplib.py", line 317, in connect
(code, msg) = self.getreply()
File "/usr/lib/python2.7/smtplib.py", line 361, in getreply
line = self.file.readline(_MAXLINE + 1)
File "/usr/lib/python2.7/socket.py", line 480, in readline
data = self._sock.recv(self._rbufsize)
KeyboardInterrupt
Thanks for any help you can give.
Hmm sounds like the connection is timing out. Is it possible the client is unable to connect to the specified server and port? Try connecting directly with nc or telnet to confirm.
You can also pass a timeout value in seconds to the SMTP call:
server = smtplib.SMTP('smtp.office365.com', 587, timeout=5)

can't send mails using flask, any ideas?

L.T:
Ok now it works. I had to instantiate Mail() class after configuration(ie. app.config) :)
i have this error:
host = smtplib.SMTP(self.mail.server, self.mail.port)
File "C:\Python27\lib\smtplib.py", line 256, in __init__
(code, msg) = self.connect(host, port)
File "C:\Python27\lib\smtplib.py", line 316, in connect
self.sock = self._get_socket(host, port, self.timeout)
File "C:\Python27\lib\smtplib.py", line 291, in _get_socket
return socket.create_connection((host, port), timeout)
File "C:\Python27\lib\socket.py", line 571, in create_connection
raise err
error: [Errno 10061] No connection could be made because the target machine actively refused it
I'm trying to build a site for myself, and i'm stuck at the part of sending e-mails because of the above error. Thing is i opened my port (that's because i'm behind a router). One thing to note is that using 'standalone' smtplib works but the one from flask doesn't. I'll post what works and does not. I don't know what is the problem. I modified address from socket.py to my 'localhost' and port 465 because i thought that was the problem. Hence i tried almost everything i could but in avail. Could someone help me a little bit here? Thanks.
This doesn't work(hello.py):
from flask import Flask
from flask_mail import Mail
from flask_mail import Message
from flask.ext.script import Manager
import os
mail = Mail()
app = Flask(__name__)
mail.init_app(app)
mail = Mail(app)
manager = Manager(app)
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
#app.route('/')
def index():
msg = Message('Hello',
sender = 'my_mail#gmail.com',
recipients = ['my_mail#gmail.com'])
msg.body = 'testing'
msg.html = '<b>testing</b>'
mail.send(msg)
app.debug = True
if __name__ == '__main__':
manager.run()
And tried a more minimalist way to see if this works, and it works, i can send mail to myself(mail.py):
import smtplib
from hello import app
gmail_user = app.config['MAIL_USERNAME']
gmail_pwd = app.config['MAIL_PASSWORD']
FROM = 'my_mail#gmail.com'
TO = ['my_mail#gmail.com']
message = 'test'
server_ssl = smtplib.SMTP_SSL("smtp.gmail.com", 465)
server_ssl.ehlo() # optional, called by login()
server_ssl.login(gmail_user, gmail_pwd)
# ssl server doesn't support or need tls, so don't call server_ssl.starttls()
server_ssl.sendmail(FROM, TO, message)
#server_ssl.quit()
server_ssl.close()
print 'successfully sent the mail'
Bump...
It has nothing to do with objects. Functions are designed the way they return a value, and you can assign that value the same way you put the input into the list.
You just run the function, take the value it returned and put it into the variable.

Sending email using Outlook SMTP

I want to send email in Django application using Outlook's SMTP server. The problem is, I get SSL wrong version number error every time I'm trying to send a message.
Error traceback:
Traceback (most recent call last):
File "F:\Development\Python\lib\smtplib.py", line 366, in getreply
line = self.file.readline()
File "F:\Development\Python\lib\socket.py", line 297, in readinto
return self._sock.recv_into(b)
File "F:\Development\Python\lib\ssl.py", line 453, in recv_into
return self.read(nbytes, buffer)
File "F:\Development\Python\lib\ssl.py", line 327, in read
v = self._sslobj.read(len, buffer)
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1450)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "F:\Development\Python\lib\site-packages\django\core\handlers\base.py", line 115, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "E:\SkyDrive\Repositories\web\skyproject\views.py", line 13, in index
email.send()
File "F:\Development\Python\lib\site-packages\django\core\mail\message.py", line 255, in send
return self.get_connection(fail_silently).send_messages([self])
File "F:\Development\Python\lib\site-packages\django\core\mail\backends\smtp.py", line 88, in send_messages
new_conn_created = self.open()
File "F:\Development\Python\lib\site-packages\django\core\mail\backends\smtp.py", line 55, in open
self.connection.login(self.username, self.password)
File "F:\Development\Python\lib\smtplib.py", line 621, in login
AUTH_PLAIN + " " + encode_plain(user, password))
File "F:\Development\Python\lib\smtplib.py", line 398, in docmd
return self.getreply()
File "F:\Development\Python\lib\smtplib.py", line 370, in getreply
+ str(e))
smtplib.SMTPServerDisconnected: Connection unexpectedly closed: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1450)
This is my SMTP configuration in 'settings.py':
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.live.com'
EMAIL_HOST_USER = 'my_email#outlook.com'
EMAIL_HOST_PASSWORD = 'my_password'
EMAIL_PORT = 587
And this is how messages being sent:
from django.core.mail import EmailMessage
email = EmailMessage('Test', 'Test', to=['email_address#example.com'])
email.send()
I have no idea why I get this error. As far as I know, there are no SSL_VERSION parameter in Django settings.
If it is important, my interpreter's version is 3.3.2, and Django's version is 1.5.2.
Try these settings for OutLook:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp-mail.outlook.com'
EMAIL_HOST_USER = "youremail#outlook.com"
EMAIL_HOST_PASSWORD = "yourpassword"
I got it working with the following settings:
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp-mail.outlook.com'
EMAIL_HOST_USER = 'myemail#outlook.com'
EMAIL_HOST_PASSWORD = 'mypassword'
EMAIL_PORT = 25
I had a very similar issue. I was getting following error message:
SMTPServerDisconnected: Connection unexpectedly closed
The solution was to:
Login to the e-mail address via web interface available at www.outlook.com
Verify my account by providing MS with my phone number and typing back the received SMS.
After this I can nicely send e-mails from Django.
I am getting an error while sending a Django email using outlook, did you send it successfully recently?
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_PORT = 995
EMAIL_USE_TLS = True
EMAIL_HOST = 'outlook.office365.com'
EMAIL_HOST_USER = '********#*******'
EMAIL_HOST_PASSWORD = '************'
EMAIL_FROM_ADDRESS='*****#******'
EMAIL_ENCRYPTION='STARTTLS'
DEFAULT_FROM_EMAIL ='*****#********'
Due to the cancellation of Gmail's less secure apps, I tried outlook. Here is the code:
# setting.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp-mail.outlook.com'
EMAIL_HOST_USER = "your_account#outlook.com"
EMAIL_HOST_PASSWORD = "password"
You could test it in shell:
python manage.py shell
>>> from django.core.mail import send_mail
>>> send_mail("TEST","THis is a test","your_account#outlook.com",["another_account#gmail.com"],fail_silently=False)