|
|
|
|
@ -3,10 +3,10 @@ import smtplib
|
|
|
|
|
from email.mime.multipart import MIMEMultipart
|
|
|
|
|
from email.mime.text import MIMEText
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SMTPClient:
|
|
|
|
|
def __init__(
|
|
|
|
|
self, server: str, port: int, username: str, password: str, _from: str, use_tls=False, opportunistic_tls=False
|
|
|
|
|
self, server: str, port: int, username: str, password: str, _from: str,
|
|
|
|
|
use_tls=False, opportunistic_tls=False
|
|
|
|
|
):
|
|
|
|
|
self.server = server
|
|
|
|
|
self.port = port
|
|
|
|
|
@ -19,18 +19,26 @@ class SMTPClient:
|
|
|
|
|
def send(self, mail: dict):
|
|
|
|
|
smtp = None
|
|
|
|
|
try:
|
|
|
|
|
# Always use STARTTLS for ports like 587 (Office365/modern SMTP)
|
|
|
|
|
# Port 465 is implicit SSL, always use SMTP_SSL.
|
|
|
|
|
if self.port == 465:
|
|
|
|
|
smtp = smtplib.SMTP_SSL(self.server, self.port, timeout=10)
|
|
|
|
|
elif self.use_tls or self.opportunistic_tls:
|
|
|
|
|
else:
|
|
|
|
|
smtp = smtplib.SMTP(self.server, self.port, timeout=10)
|
|
|
|
|
smtp.ehlo(self.server)
|
|
|
|
|
if self.use_tls:
|
|
|
|
|
# Strict: fail if STARTTLS fails
|
|
|
|
|
smtp.starttls()
|
|
|
|
|
smtp.ehlo(self.server)
|
|
|
|
|
else:
|
|
|
|
|
smtp = smtplib.SMTP(self.server, self.port, timeout=10)
|
|
|
|
|
elif self.opportunistic_tls:
|
|
|
|
|
# Try STARTTLS, but continue if it fails
|
|
|
|
|
try:
|
|
|
|
|
smtp.starttls()
|
|
|
|
|
smtp.ehlo(self.server)
|
|
|
|
|
logging.info("Opportunistic STARTTLS succeeded")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.warning(f"Opportunistic STARTTLS failed, continuing unencrypted: {e}")
|
|
|
|
|
|
|
|
|
|
# Only authenticate if both username and password are non-empty
|
|
|
|
|
# Authenticate if needed
|
|
|
|
|
if self.username and self.password and self.username.strip() and self.password.strip():
|
|
|
|
|
smtp.login(self.username, self.password)
|
|
|
|
|
|
|
|
|
|
@ -48,8 +56,11 @@ class SMTPClient:
|
|
|
|
|
logging.exception("Timeout occurred while sending email")
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.exception(f"Unexpected error occurred while sending email to {mail['to']}")
|
|
|
|
|
logging.exception(f"Unexpected error occurred while sending email to {mail['to']}: {e}")
|
|
|
|
|
raise
|
|
|
|
|
finally:
|
|
|
|
|
if smtp:
|
|
|
|
|
try:
|
|
|
|
|
smtp.quit()
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|