diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d03717..6889fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixed +- `MaildirConnection.fetch_message()` now marks messages as read after reading them (sets the `S` flag and moves the file from `new/` to `cur/`), unless `--test` is in effect. Previously, a message was processed but its on-disk maildir state was unchanged, so an MUA scanning the same maildir kept showing it as unread. Mirrors the existing `mark_read=not test` pattern used for `MSGraphConnection`. - `get_ip_address_info()` no longer caches weak-fallback attributions (no PTR + no ASN-domain map match → raw `as_name` used as `source_name`, `source_type` left null). `get_reverse_dns()` swallows every `DNSException` as `None`, so a transient PTR lookup failure (timeout, SERVFAIL, socket error) is indistinguishable from a genuine no-PTR case at that layer — caching the weak result would poison the 4-hour cache with a misattribution that persisted even after the PTR became resolvable again. PTR-backed matches and ASN-domain matches (both stable attributions) are still cached as before; only the specific `reverse_dns=None AND type=None AND name=as_name` state skips the cache write so the next lookup retries. ## 9.10.1 diff --git a/parsedmarc/__init__.py b/parsedmarc/__init__.py index ce48c19..8b87b44 100644 --- a/parsedmarc/__init__.py +++ b/parsedmarc/__init__.py @@ -48,6 +48,7 @@ from parsedmarc.mail import ( GmailConnection, IMAPConnection, MailboxConnection, + MaildirConnection, MSGraphConnection, ) from parsedmarc.types import ( @@ -2042,6 +2043,9 @@ def get_dmarc_reports_from_mailbox( elif isinstance(connection, MSGraphConnection): message_id = str(msg_uid) msg_content = connection.fetch_message(message_id, mark_read=not test) + elif isinstance(connection, MaildirConnection): + message_id = str(msg_uid) if not isinstance(msg_uid, str) else msg_uid + msg_content = connection.fetch_message(message_id, mark_read=not test) else: message_id = str(msg_uid) if not isinstance(msg_uid, str) else msg_uid msg_content = connection.fetch_message(message_id) diff --git a/parsedmarc/mail/maildir.py b/parsedmarc/mail/maildir.py index 9f075e0..3e7a4f0 100644 --- a/parsedmarc/mail/maildir.py +++ b/parsedmarc/mail/maildir.py @@ -65,13 +65,18 @@ class MaildirConnection(MailboxConnection): self._active_folder = self._client return self._active_folder.keys() - def fetch_message(self, message_id: str) -> str: + def fetch_message(self, message_id: str, **kwargs) -> str: msg = self._active_folder.get(message_id) - if msg is not None: - msg = msg.as_string() - if msg is not None: - return msg - return "" + if msg is None: + return "" + msg_str = msg.as_string() + if kwargs.get("mark_read"): + # Maildir spec: a message is "read" once it has been moved out of + # new/ into cur/ with the "S" (Seen) flag set in its info field. + msg.set_subdir("cur") + msg.add_flag("S") + self._active_folder[message_id] = msg + return msg_str or "" def delete_message(self, message_id: str): self._active_folder.remove(message_id)