seitime-frappe/frappe/email/smtp.py
Gavin D'souza 3446026555 chore: Update header: license.txt => LICENSE
The license.txt file has been replaced with LICENSE for quite a while
now. INAL but it didn't seem accurate to say "hey, checkout license.txt
although there's no such file". Apart from this, there were
inconsistencies in the headers altogether...this change brings
consistency.
2021-09-03 12:02:59 +05:30

126 lines
3.6 KiB
Python

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import frappe
import smtplib
import email.utils
import _socket, sys
from frappe import _
from frappe.utils import cint, cstr, parse_addr
CONNECTION_FAILED = _('Could not connect to outgoing email server')
AUTH_ERROR_TITLE = _("Invalid Credentials")
AUTH_ERROR = _("Incorrect email or password. Please check your login credentials.")
SOCKET_ERROR_TITLE = _("Incorrect Configuration")
SOCKET_ERROR = _("Invalid Outgoing Mail Server or Port")
SEND_MAIL_FAILED = _("Unable to send emails at this time")
EMAIL_ACCOUNT_MISSING = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account')
class InvalidEmailCredentials(frappe.ValidationError):
pass
def send(email, append_to=None, retry=1):
"""Deprecated: Send the message or add it to Outbox Email"""
def _send(retry):
from frappe.email.doctype.email_account.email_account import EmailAccount
try:
email_account = EmailAccount.find_outgoing(match_by_doctype=append_to)
smtpserver = email_account.get_smtp_server()
# validate is called in as_string
email_body = email.as_string()
smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body)
except smtplib.SMTPSenderRefused:
frappe.throw(_("Invalid login or password"), title='Email Failed')
raise
except smtplib.SMTPRecipientsRefused:
frappe.msgprint(_("Invalid recipient address"), title='Email Failed')
raise
except (smtplib.SMTPServerDisconnected, smtplib.SMTPAuthenticationError):
if not retry:
raise
else:
retry = retry - 1
_send(retry)
_send(retry)
class SMTPServer:
def __init__(self, server, login=None, password=None, port=None, use_tls=None, use_ssl=None):
self.login = login
self.password = password
self._server = server
self._port = port
self.use_tls = use_tls
self.use_ssl = use_ssl
self._session = None
if not self.server:
frappe.msgprint(EMAIL_ACCOUNT_MISSING, raise_exception=frappe.OutgoingEmailError)
@property
def port(self):
port = self._port or (self.use_ssl and 465) or (self.use_tls and 587)
return cint(port)
@property
def server(self):
return cstr(self._server or "")
def secure_session(self, conn):
"""Secure the connection incase of TLS.
"""
if self.use_tls:
conn.ehlo()
conn.starttls()
conn.ehlo()
@property
def session(self):
if self.is_session_active():
return self._session
SMTP = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP
try:
_session = SMTP(self.server, self.port)
if not _session:
frappe.msgprint(CONNECTION_FAILED, raise_exception=frappe.OutgoingEmailError)
self.secure_session(_session)
if self.login and self.password:
res = _session.login(str(self.login or ""), str(self.password or ""))
# check if logged correctly
if res[0]!=235:
frappe.msgprint(res[1], raise_exception=frappe.OutgoingEmailError)
self._session = _session
return self._session
except smtplib.SMTPAuthenticationError as e:
self.throw_invalid_credentials_exception()
except _socket.error as e:
# Invalid mail server -- due to refusing connection
frappe.throw(SOCKET_ERROR, title=SOCKET_ERROR_TITLE)
except smtplib.SMTPException:
frappe.msgprint(SEND_MAIL_FAILED)
raise
def is_session_active(self):
if self._session:
try:
return self._session.noop()[0] == 250
except Exception:
return False
def quit(self):
if self.is_session_active():
self._session.quit()
@classmethod
def throw_invalid_credentials_exception(cls):
frappe.throw(AUTH_ERROR, title=AUTH_ERROR_TITLE, exc=InvalidEmailCredentials)