From e1a9a8e33ebe820b2a5ae4eb716f89b541a6e891 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 20 Jul 2026 16:48:41 +0200 Subject: [PATCH] fix: retrieve UID after IMAP APPEND + handle uid=None mails in vanished check --- app/plugins/builtins/mail/services.py | 64 +++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/app/plugins/builtins/mail/services.py b/app/plugins/builtins/mail/services.py index 29fb3af..243b5ca 100644 --- a/app/plugins/builtins/mail/services.py +++ b/app/plugins/builtins/mail/services.py @@ -813,15 +813,33 @@ async def imap_sync_folder( and_( Mail.folder_id == folder_id, Mail.tenant_id == tenant_id, - Mail.imap_uid.is_not(None), ) ) ) ).scalars().all() for db_mail in db_mails: - if db_mail.imap_uid and db_mail.imap_uid not in imap_uids_set: - logger.info("imap_sync_folder: deleting vanished mail %s (UID %s no longer on server)", db_mail.id, db_mail.imap_uid) - await db.delete(db_mail) + if db_mail.imap_uid: + if db_mail.imap_uid not in imap_uids_set: + logger.info("imap_sync_folder: deleting vanished mail %s (UID %s no longer on server)", db_mail.id, db_mail.imap_uid) + await db.delete(db_mail) + elif db_mail.message_id: + # Mail has no UID — try to find it on IMAP by Message-ID + try: + mid_search = await client.uid_search(f'HEADER Message-ID "{db_mail.message_id}"') + mid_raw = mid_search[1][0] if mid_search[1] and mid_search[1][0] else b'' + if isinstance(mid_raw, (bytes, bytearray)) and mid_raw: + found_uids = mid_raw.decode().split() + if found_uids: + db_mail.imap_uid = found_uids[0] + logger.info("imap_sync_folder: found UID %s for mail %s via Message-ID", found_uids[0], db_mail.id) + else: + logger.info("imap_sync_folder: deleting mail %s (no UID, not found on server by Message-ID)", db_mail.id) + await db.delete(db_mail) + else: + logger.info("imap_sync_folder: deleting mail %s (no UID, not found on server by Message-ID)", db_mail.id) + await db.delete(db_mail) + except Exception as mid_exc: + logger.warning("imap_sync_folder: Message-ID search failed for mail %s: %s", db_mail.id, mid_exc) await db.flush() except Exception as vanished_exc: logger.warning("imap_sync_folder: vanished-UID check failed for folder %s: %s", folder_id, vanished_exc) @@ -1296,15 +1314,33 @@ async def imap_sync_account( and_( Mail.folder_id == folder.id, Mail.tenant_id == tenant_id, - Mail.imap_uid.is_not(None), ) ) ) ).scalars().all() for db_mail in db_mails: - if db_mail.imap_uid and db_mail.imap_uid not in imap_uids_set: - logger.info("imap_sync_account: deleting vanished mail %s (UID %s no longer on server)", db_mail.id, db_mail.imap_uid) - await db.delete(db_mail) + if db_mail.imap_uid: + if db_mail.imap_uid not in imap_uids_set: + logger.info("imap_sync_account: deleting vanished mail %s (UID %s no longer on server)", db_mail.id, db_mail.imap_uid) + await db.delete(db_mail) + elif db_mail.message_id: + # Mail has no UID — try to find it on IMAP by Message-ID + try: + mid_search = await client.uid_search(f'HEADER Message-ID "{db_mail.message_id}"') + mid_raw = mid_search[1][0] if mid_search[1] and mid_search[1][0] else b'' + if isinstance(mid_raw, (bytes, bytearray)) and mid_raw: + found_uids = mid_raw.decode().split() + if found_uids: + db_mail.imap_uid = found_uids[0] + logger.info("imap_sync_account: found UID %s for mail %s via Message-ID", found_uids[0], db_mail.id) + else: + logger.info("imap_sync_account: deleting mail %s (no UID, not found on server by Message-ID)", db_mail.id) + await db.delete(db_mail) + else: + logger.info("imap_sync_account: deleting mail %s (no UID, not found on server by Message-ID)", db_mail.id) + await db.delete(db_mail) + except Exception as mid_exc: + logger.warning("imap_sync_account: Message-ID search failed for mail %s: %s", db_mail.id, mid_exc) await db.flush() except Exception as vanished_exc: logger.warning("imap_sync_account: vanished-UID check failed for folder %s: %s", folder.imap_name, vanished_exc) @@ -1559,6 +1595,18 @@ async def send_mail_via_smtp( ) if append_resp.result == 'OK': logger.info("send_mail: uploaded sent mail to IMAP folder %s", sent_folder.imap_name) + # Retrieve the new UID for the appended mail by searching for its Message-ID + try: + await imap_client.select(sent_folder.imap_name) + search_resp = await imap_client.uid_search(f'HEADER Message-ID "{msg_id}"') + uid_raw = search_resp[1][0] if search_resp[1] and search_resp[1][0] else b'' + if isinstance(uid_raw, (bytes, bytearray)) and uid_raw: + new_uids = uid_raw.decode().split() + if new_uids: + sent_mail.imap_uid = new_uids[0] + logger.info("send_mail: set imap_uid=%s for sent mail %s", new_uids[0], sent_mail.id) + except Exception as uid_exc: + logger.warning("send_mail: could not retrieve UID after APPEND: %s", uid_exc) else: logger.warning("send_mail: IMAP APPEND failed for sent folder %s: %s", sent_folder.imap_name, append_resp) await imap_client.logout()