Commit Graph

7 Commits

Author SHA1 Message Date
e349638700 fix: fetch() selects message body by structure, not length (v0.1.3)
select the literal payload by isinstance bytearray instead of len>20. aioimaplib
stores the message body as the only bytearray in the response; every other line
(including the '<id> FETCH (...' header) is plain bytes. the length heuristic
matched the header line first for any 2+ digit message id or BODY[]/UID fetch,
returning a blank Message and silently breaking OTP retrieval on real mailboxes.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-29 17:09:08 -04:00
a4abe354eb fix: add match_field=from|to to restore recipient-primary OTP matching
the clean lib matched senders by From only; the original imap_tool.py matched primarily by TO (the per-user alias the code was sent to) with a HEADER FROM forwarded fallback. added match_field="from"|"to" to retrieve_otp: "from" (default) is byte-identical to current behavior, "to" searches TO primary and accepts a forwarded From match, restoring the alias flow. server query + client-side predicate both honor it. bump to v0.1.2.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-29 03:25:18 -04:00
6ac8957583 fix: pass XOAUTH2 token as str, not bytes (was corrupting the Bearer value)
aioimaplib's mail.xoauth2(user, token) builds the SASL string by f-string interpolating the token, so a bytes token injects the b'...' repr into auth=Bearer and breaks every XOAUTH2 login. dropped the .encode() (token is already str via _resolve_token/_as_str). corrected the inline comment and the CLAUDE.md note that both wrongly claimed bytes was required — that false note propagated the bug through prior review passes.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-28 18:45:25 -04:00
7934688595 fix: clean up the IMAP object on a failed connect; module-level asyncio import
on a failed connect attempt the IMAP4 object was dropped without logout(), leaking the socket aioimaplib held; now it is logged out (best-effort) before nulling. also moved the asyncio import in oauth.py from inside the retry loop to module top.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-28 17:18:28 -04:00
ba7ae48a87 fix: bytes-token XOAUTH2 + tolerant SEARCH parse (v0.1.1)
- a token provider (or static token) returning bytes crashed token.encode() in the
  XOAUTH2 path. coerce to str at the source (_resolve_token via _as_str) so both
  the .encode() and SASL-fallback entrypoints get a str.
- client.search() did int(x) on every SEARCH token unguarded; a malformed/non-
  numeric token aborted the whole search. skip non-numeric tokens (log at debug)
  instead of crashing.

verified by execution: bytes static + async-provider tokens authenticate without
crashing (both xoauth2 and SASL-fallback paths); guarded search skips garbage.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-27 21:51:27 -04:00
5f7ed74306 add package: pyproject + src
async IMAP one-time-code retrieval consolidated from 5 forks. three
injected layers: auth (PasswordAuth/OAuth2Auth XOAUTH2), client
(IMAPClient connect/retry/folders/search/fetch/mark_seen), retrieve
(retrieve_otp newest-first with sender/subject/code matching via
substring/regex/callable). config-free, emit-only logging. optional
[oauth] extra adds aiohttp refresh-token providers (Microsoft/Google).

fixed vs forks: dropped nonexistent uid_fetch/uid_store for aioimaplib's
uid() dispatcher, xoauth2 token now bytes, guarded XOAUTH2 fallback,
use_uid decoupled from use_ssl. src/ multi-module, hatchling.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-24 21:36:43 -04:00
c08d13c02a init: async IMAP OTP retrieval
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-24 20:10:03 -04:00