fix: email adapter IMAP UID tracking and SMTP TLS verification

- Use imap.uid() for search and fetch instead of imap.search/fetch.
  Sequence numbers shift when messages are deleted, causing the adapter
  to skip new messages or reprocess old ones. UIDs are stable.

- Pass ssl.create_default_context() to starttls() so the server
  certificate is actually verified. Without it smtplib uses
  ssl._create_stdlib_context() which skips verification.
This commit is contained in:
Himess 2026-03-12 03:02:51 +03:00 committed by teknium1
parent 914bb12035
commit fa72f4ff55

View file

@ -22,6 +22,7 @@ import logging
import os import os
import re import re
import smtplib import smtplib
import ssl
import uuid import uuid
from datetime import datetime from datetime import datetime
from email.header import decode_header from email.header import decode_header
@ -212,7 +213,7 @@ class EmailAdapter(BasePlatformAdapter):
imap.login(self._address, self._password) imap.login(self._address, self._password)
# Mark all existing messages as seen so we only process new ones # Mark all existing messages as seen so we only process new ones
imap.select("INBOX") imap.select("INBOX")
status, data = imap.search(None, "ALL") status, data = imap.uid("search", None, "ALL")
if status == "OK" and data[0]: if status == "OK" and data[0]:
for uid in data[0].split(): for uid in data[0].split():
self._seen_uids.add(uid) self._seen_uids.add(uid)
@ -225,7 +226,7 @@ class EmailAdapter(BasePlatformAdapter):
try: try:
# Test SMTP connection # Test SMTP connection
smtp = smtplib.SMTP(self._smtp_host, self._smtp_port) smtp = smtplib.SMTP(self._smtp_host, self._smtp_port)
smtp.starttls() smtp.starttls(context=ssl.create_default_context())
smtp.login(self._address, self._password) smtp.login(self._address, self._password)
smtp.quit() smtp.quit()
logger.info("[Email] SMTP connection test passed.") logger.info("[Email] SMTP connection test passed.")
@ -277,7 +278,7 @@ class EmailAdapter(BasePlatformAdapter):
imap.login(self._address, self._password) imap.login(self._address, self._password)
imap.select("INBOX") imap.select("INBOX")
status, data = imap.search(None, "UNSEEN") status, data = imap.uid("search", None, "UNSEEN")
if status != "OK" or not data[0]: if status != "OK" or not data[0]:
imap.logout() imap.logout()
return results return results
@ -287,7 +288,7 @@ class EmailAdapter(BasePlatformAdapter):
continue continue
self._seen_uids.add(uid) self._seen_uids.add(uid)
status, msg_data = imap.fetch(uid, "(RFC822)") status, msg_data = imap.uid("fetch", uid, "(RFC822)")
if status != "OK": if status != "OK":
continue continue
@ -427,7 +428,7 @@ class EmailAdapter(BasePlatformAdapter):
msg.attach(MIMEText(body, "plain", "utf-8")) msg.attach(MIMEText(body, "plain", "utf-8"))
smtp = smtplib.SMTP(self._smtp_host, self._smtp_port) smtp = smtplib.SMTP(self._smtp_host, self._smtp_port)
smtp.starttls() smtp.starttls(context=ssl.create_default_context())
smtp.login(self._address, self._password) smtp.login(self._address, self._password)
smtp.send_message(msg) smtp.send_message(msg)
smtp.quit() smtp.quit()
@ -515,7 +516,7 @@ class EmailAdapter(BasePlatformAdapter):
msg.attach(part) msg.attach(part)
smtp = smtplib.SMTP(self._smtp_host, self._smtp_port) smtp = smtplib.SMTP(self._smtp_host, self._smtp_port)
smtp.starttls() smtp.starttls(context=ssl.create_default_context())
smtp.login(self._address, self._password) smtp.login(self._address, self._password)
smtp.send_message(msg) smtp.send_message(msg)
smtp.quit() smtp.quit()