mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-14 12:48:03 +00:00
fix(email): use UID commands instead of sequence numbers in IMAP fetches (#5149)
conn.search() / conn.fetch() operate on volatile positional sequence
numbers that shift whenever messages are deleted or expunged. Three call
sites in the sig-learner (_pull_headers, _fetch_bodies) and morning-brief
email section were storing these as "uid" and reusing them in subsequent
fetches — causing wrong-message returns or NO responses if another client
modified the mailbox concurrently.
Replaced with conn.uid("SEARCH", ...) / conn.uid("FETCH", ...), which use
persistent RFC 3501 UIDs. _scan_one (urgency action) already did this
correctly; these were the remaining callers.
The reproduction window is narrow (requires concurrent deletion between
search and fetch), so the fix is verified by regression tests rather than
manual end-to-end: _SpyImap raises AssertionError if conn.search() or
conn.fetch() are called instead of conn.uid().
This commit is contained in:
@@ -1125,14 +1125,14 @@ async def action_learn_sender_signatures(owner: str, **kwargs) -> Tuple[str, boo
|
||||
conn = _imap_connect(None, owner=owner)
|
||||
try:
|
||||
conn.select("INBOX", readonly=True)
|
||||
status, data = conn.search(None, "ALL")
|
||||
status, data = conn.uid("SEARCH", None, "ALL")
|
||||
if status != "OK" or not data or not data[0]:
|
||||
return results
|
||||
uids = data[0].split()[-300:][::-1] # newest 300
|
||||
for uid in uids:
|
||||
try:
|
||||
st, msg_data = conn.fetch(
|
||||
uid, "(BODY.PEEK[HEADER.FIELDS (FROM)])"
|
||||
st, msg_data = conn.uid(
|
||||
"FETCH", uid, "(BODY.PEEK[HEADER.FIELDS (FROM)])"
|
||||
)
|
||||
if st != "OK" or not msg_data or not msg_data[0]:
|
||||
continue
|
||||
@@ -1214,7 +1214,7 @@ async def action_learn_sender_signatures(owner: str, **kwargs) -> Tuple[str, boo
|
||||
conn2.select("INBOX", readonly=True)
|
||||
for mm in _msgs:
|
||||
try:
|
||||
st, data = conn2.fetch(mm["uid"], "(BODY.PEEK[TEXT])")
|
||||
st, data = conn2.uid("FETCH", mm["uid"], "(BODY.PEEK[TEXT])")
|
||||
if st != "OK" or not data or not data[0]:
|
||||
continue
|
||||
raw = data[0][1] if isinstance(data[0], tuple) else None
|
||||
@@ -1356,13 +1356,13 @@ async def action_daily_brief(owner: str, **kwargs) -> Tuple[str, bool]:
|
||||
conn = _imap_connect(None)
|
||||
try:
|
||||
conn.select("INBOX", readonly=True)
|
||||
status, data = conn.search(None, "UNSEEN")
|
||||
status, data = conn.uid("SEARCH", None, "UNSEEN")
|
||||
uids = (data[0].split() if status == "OK" and data and data[0] else [])
|
||||
unread_count = len(uids)
|
||||
# Grab headers for the most recent 5 unread (UIDs increase with arrival)
|
||||
for uid in uids[-5:][::-1]:
|
||||
try:
|
||||
_, msg_data = conn.fetch(uid, "(BODY.PEEK[HEADER.FIELDS (FROM SUBJECT)])")
|
||||
_, msg_data = conn.uid("FETCH", uid, "(BODY.PEEK[HEADER.FIELDS (FROM SUBJECT)])")
|
||||
if not msg_data or not msg_data[0]:
|
||||
continue
|
||||
hdr = msg_data[0][1] if isinstance(msg_data[0], tuple) else msg_data[0]
|
||||
|
||||
Reference in New Issue
Block a user