Releases: seanthegeek/mailsuite
Releases · seanthegeek/mailsuite
Release list
2.2.2
2.2.1
- Require
mail-parser>=4.2.1(was>=4.1.2). 4.2.1 stops returning a phantom('', '')entry for absent address headers and adds the CVE-2023-27043 strict address-parsing hardening.parse_email()already re-parsed these headers itself, so mailsuite's own output is unchanged — this guarantees the fixed dependency for code that reads the underlying mail-parser object.
2.2.0
Enhancements
- Add a
mailsuite.arcmodule for Authenticated Received Chain (RFC 8617) (#32):seal_email()— add an ARC set to a message, extending any existing chain.verify_arc_chain()— verify the chain and report thecvresult.- Errors raise the new
ARCError. Addsauthresas a dependency (required by dkimpy's ARC sealing).
- Add OAuth2 authentication to
mailsuite.smtp.send_email, matching the IMAP client (#33):- Pass
oauth2_token=(oroauth2_token_provider=for a fresh token at send time) withusername=to authenticate without a password. oauth2_mechanism=selectsXOAUTH2(default) orOAUTHBEARER;oauth2_vendor=supplies Yahoo's vendor string.
- Pass
- Add
ClientAssertionauth toMSGraphConnectionfor federated / workload-identity scenarios that avoid a long-lived client secret. Passclient_assertion=with a signed-JWT assertion, orclient_assertion_provider=(a zero-arg callable) to supply a fresh assertion each timeazure-identityacquires a token. The assertion is exchanged for an access token via the JWT-bearer client-credentials grant — it is not itself a Graph access token (#31).
Fixes
- Fix two
GmailConnectionbugs (#38):fetch_messages(..., since=…)dropped the date filter on every page after the first, so paginated fetches returned messages older thansince. The filter is now carried through pagination.create_folder()did not clear the label-id cache, so a label looked up before creation stayed cached as missing —merge_folders(create=True)could then move messages to an empty destination label and orphan them. It now clears the cache likerename_folder/delete_folder/move_folder.
- Fix several
mailsuite.utilsparsing bugs (#37):parse_email()always returnedreply-toas[], discarding the parsed Reply-To addresses; they are now preserved.parse_email()raisedre.error(or corruptedheaders_string) when theSubjectorThread-Topiccontained a backslash sequence such as\1; the value is now inserted literally.parse_email_address()raisedUnboundLocalErroron input that was neither astrnor a tuple; it now raises a clearTypeError.
- Fix
mailsuite.smtp.send_email()not delivering toCcandBccrecipients. The SMTP envelope was built frommessage_toonly — Cc/Bcc were appended after the envelope was captured — so the MTA delivered to theToaddresses alone. The envelope now includes every recipient, and the caller's recipient lists are no longer mutated in place (#36). - Fix
from_trusted_domain()raisingTypeErrorwhenallow_multiple_authentication_results=Trueand the message carries multipleAuthentication-Resultsheaders (whichparse_emailrepresents as raw strings). Each header is now parsed before inspection, and the matched DMARC domain is lower-cased/stripped to match the single-header path (#35). - Fix
IMAPClientfolder normalization removing the personal-namespace prefix wherever it appeared inside a path rather than only at the start, so e.g.Projects/INBOX/old(prefixINBOX/) becameINBOX/Projects/old. Only a leading prefix is now stripped before it is re-applied (#39). - Fix
IMAPClient.move_messages()duplicating messages on servers without theMOVEcapability. The copy/delete fallback operated on the full UID list inside the per-100 chunk loop, so moving more than 100 messages copied every message once per chunk. Each chunk is now copied and deleted on its own (#40). - Fix
IMAPClient.delete_messages()raising on servers without theUIDPLUScapability. It always issued aUID EXPUNGE(expunge of specific UIDs), which requires UIDPLUS; on servers lacking it the error was uncaught. It now falls back to a plainEXPUNGEwhen UIDPLUS is unavailable (#42). - Fix
parse_email()auto-reply detection (automatic_reply). It matched the literalauto_generated(underscore) and required both anAuto-Submittedand anX-Auto-Response-Suppressheader, so it never flagged real auto-replies (Gmail sendsAuto-Submitted: auto-replied, Microsoft 365 / Exchange sendsauto-generated). It now follows RFC 3834 — anyAuto-Submittedvalue other thanno, or the presence of Exchange'sX-Auto-Response-Suppressheader (#47). - Fix the IMAP IDLE watch loop ignoring new mail on servers that signal it with an untagged
EXISTSbut noRECENT. The loop reacted only toRECENT; it now also reacts toEXISTS(the standard new-message signal) (#41). - Fix
MSGraphConnection.fetch_messages()ignoringbatch_sizewheneversincewas set, so a date-filtered fetch pulled every matching page regardless of the requested cap.batch_sizeandsinceare independent now — the caller controls both (#46). - Fix the IMAP IDLE watch loop stacking a nested IDLE loop on every reconnect. A connection drop during
watch()calledreset_connection(), which re-ran__init__and restarted IDLE recursively (duplicated callbacks, ever-deepening recursion on a long-running watch). Reconnects now re-arm the existing loop in place via a re-entrancy guard (#45). - Fix
create_email()emitting an emptyCc:header whenmessage_cc=[]. It now adds the header only when there are Cc addresses, matching theTohandling (#44).
2.0.2
- Fix
RuntimeError: Event loop is closedinMSGraphConnectionafter the first Graph call. The internal_runhelper usedasyncio.run, which closed the event loop after each invocation; the Graph SDK's underlyinghttpx.AsyncClientkeeps connection-pool resources bound to that loop, so the next call would fail._runnow retains a single persistent event loop across calls (domainaware/parsedmarc#742). - Detect "folder already exists" in
MSGraphConnection.create_folderby HTTP status code (409 Conflicton the SDK'sAPIError.response_status_code) rather than by string-matching the exception message. The previous string match was brittle to error-message localization and Graph SDK changes. - Escape single quotes in folder-name OData filters in
MSGraphConnection. Folder names containing an apostrophe (e.g.John's mail) previously produced a malformeddisplayName eq '…'filter expression that Graph rejected.
2.0.1
- Add OAuth2 (XOAUTH2 / OAUTHBEARER) login support to
mailsuite.imap.IMAPClientandmailsuite.mailbox.IMAPConnection. Passoauth2_token=for a static access token, oroauth2_token_provider=(a zero-arg callable) to have a fresh token fetched on every connect/reconnect — recommended for long-running watch / IDLE loops where tokens expire mid-session. Yahoo's vendor string is supported viaoauth2_vendor=. Workspace and Microsoft 365 users should still prefer the higher-levelGmailConnection/MSGraphConnectionbackends, which handle token refresh end-to-end (#6). - Fix
IMAPClient.select_folder(and other folder operations) on shared / other-users namespaces._normalise_folderpreviously prepended the personal namespace prefix to every path, so e.g.user/colleague/InboxbecameINBOX/user/colleague/Inbox. Paths already inside an RFC 2342 non-personal namespace are now passed through untouched (#13). - Fix the
IMAPClientdefault port (was933, now the standard993).
2.0.0
- Add a new
mailsuite.dkimmodule for DKIM key generation, public key extraction, TXT record generation, email signing, and signature verification smtp.send_email()now acceptsdkim_private_key,dkim_selector,dkim_domain, anddkim_additional_headersparameters for sending DKIM-signed mail- Add
mailsuite.mailbox, a provider-agnostic mailbox abstraction lifted fromparsedmarcMailboxConnectionABC withcreate_folder,fetch_messages,fetch_message,delete_message,move_message,keepalive,watch, andsend_messageIMAPConnection(built onmailsuite.imap.IMAPClient, IDLE-based watch loop)MaildirConnection(built on the stdlibmailbox.Maildir)MSGraphConnection(built onmsgraph-sdk; sends through/users/{mailbox}/sendMail). Requires themailsuite[msgraph]extra. Constructor acceptsgraph_url(any Microsoft Graph endpoint — sovereign clouds, dev/test endpoints, etc.) andtoken_cache_name(defaults to"mailsuite"; pass the previous installation's cache name — e.g."parsedmarc"— for seamless migration so cachedAuthenticationRecords carry over without re-prompting users).GmailConnection(built ongoogle-api-python-client; sends throughusers.messages.send). Requires themailsuite[gmail]extra.- Optional cloud backends are loaded lazily via PEP 562 — importing
mailsuite.mailboxnever requires the extras to be installed; referencing the class surfaces a clearImportErrorif they aren't. send_message()raisesNotImplementedErroron the receive-only IMAP and Maildir backends; usemailsuite.smtp.send_emailfor standalone sending.
- Add a comprehensive
pytesttest suite covering all modules, plus a GitHub Actions CI workflow that runsruff,pyright, andpytest(with coverage) across supported Python versions