diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..d7e3bd8 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,45 @@ +# backend + +The `backend` package defines a unified `Provider` interface for multi-protocol email support and provides three protocol implementations: IMAP, JMAP, and POP3. + +## Architecture + +This package acts as an abstraction layer, allowing the rest of the application to interact with mail servers through a consistent interface regardless of the underlying protocol. Implementations self-register at init time via `RegisterBackend`, and the factory function `New()` creates the right provider based on the account's `Protocol` field (defaults to `"imap"`). + +### Provider interface + +The `Provider` interface composes five sub-interfaces: + +| Interface | Methods | Purpose | +|-----------|---------|---------| +| `EmailReader` | `FetchEmails`, `FetchEmailBody`, `FetchAttachment` | Retrieve email lists, bodies, and raw attachments | +| `EmailWriter` | `MarkAsRead`, `DeleteEmail`, `ArchiveEmail`, `MoveEmail` | Modify email state and location | +| `EmailSender` | `SendEmail` | Send outgoing mail | +| `FolderManager` | `FetchFolders` | List available mailboxes | +| `Notifier` | `Watch` | Real-time push notifications for mailbox changes | + +Backends that don't support an operation return `ErrNotSupported`. + +## Protocols + +### IMAP (`backend/imap`) + +Wraps the existing `fetcher` and `sender` packages behind the `Provider` interface. IMAP IDLE is handled externally in `main.go`, so `Watch()` returns `ErrNotSupported`. + +### JMAP (`backend/jmap`) + +Native JMAP implementation (RFC 8620 / RFC 8621) using `go-jmap`. Supports OAuth2 and Basic Auth, real-time push via JMAP EventSource, and full mailbox operations including send (via `EmailSubmission`). JMAP string IDs are hashed to `uint32` UIDs for interface compatibility. + +### POP3 (`backend/pop3`) + +POP3 + SMTP implementation. Inherently limited to a single INBOX folder, no read flags, no move/archive, and no push notifications. Uses the `sender` package for outgoing mail. + +## Files + +| File | Description | +|------|-------------| +| `backend.go` | Core interfaces and data types (`Provider`, `Email`, `Attachment`, `Folder`, `OutgoingEmail`, `NotifyEvent`, `Capabilities`) | +| `factory.go` | Protocol registry and `New()` factory function | +| `imap/imap.go` | IMAP provider — adapter over `fetcher` and `sender` packages | +| `jmap/jmap.go` | JMAP provider — native implementation with session management and mailbox caching | +| `pop3/pop3.go` | POP3 provider — per-connection model with UIDL-based UID hashing | diff --git a/clib/README.md b/clib/README.md index 2324d48..d641663 100644 --- a/clib/README.md +++ b/clib/README.md @@ -23,6 +23,32 @@ Image decoding and PNG re-encoding using [stb_image](https://github.com/nothings Files: `imgconv.c`, `imgconv.h`, `imgconv.go`, `stb_image.h`, `stb_image_write.h` +### htmlconv + +Single-pass HTML-to-structured-elements parser. Takes raw HTML and returns a slice of `HTMLElement` values representing headings, links, images, blockquotes, tables, and text. Used by the email view to render HTML emails in the terminal without a full DOM. + +- `HTMLToElements()` — parses HTML into structured elements with type, text, and up to two attributes (e.g., `href`/`src`, `alt`/`cite`). + +Files: `htmlconv.c`, `htmlconv.h`, `htmlconv.go` + +### markdown + +Markdown-to-HTML conversion using [md4c](https://github.com/mity/md4c) (vendored). Supports GitHub-flavored features: tables, strikethrough, task lists, and permissive autolinks. + +- `MarkdownToHTML()` — converts Markdown bytes to HTML bytes. + +Files: `md4c.c`, `md4c.h`, `md4c-html.c`, `md4c-html.h`, `markdown.go` + +## Pure Go fallbacks + +Every function has a `_nocgo.go` counterpart (build tag `!cgo`) that provides the same API using pure Go libraries: + +| C implementation | Go fallback | +|-----------------|-------------| +| `base64wrap.go` | Manual string builder | +| `imgconv.go` (stb_image) | `image/png`, `image/jpeg`, `image/gif` | +| `htmlconv.go` | `goquery` DOM parsing | +| `markdown.go` (md4c) | `goldmark` | ## Adding new C code diff --git a/config/README.md b/config/README.md index 7189dd3..061a53d 100644 --- a/config/README.md +++ b/config/README.md @@ -21,4 +21,20 @@ All cache files use JSON serialization with restrictive file permissions (`0600` | `cache.go` | Email, contacts, and drafts caching. Provides CRUD operations for `EmailCache`, `ContactsCache` (with search and frequency-based ranking), and `DraftsCache` (with save/delete/get operations). | | `folder_cache.go` | Caches IMAP folder listings per account and per-folder email metadata. Stores folder names to avoid repeated IMAP `LIST` commands, and caches email headers per folder for fast navigation. | | `signature.go` | Loads and saves the user's email signature from `~/.config/matcha/signature.txt`. | +| `oauth.go` | OAuth2 integration — token retrieval, authorization flow launcher, and embedded Python helper extraction. | +| `oauth_script.py` | Embedded Gmail OAuth2 helper script (browser-based auth, token refresh, secure storage). | | `config_test.go` | Unit tests for configuration logic. | + +## OAuth2 / XOAUTH2 + +Accounts with `auth_method: "oauth2"` use Gmail's XOAUTH2 mechanism instead of passwords. The flow works across three layers: + +1. **`config/oauth.go`** — Go-side orchestration. Extracts the embedded Python helper to `~/.config/matcha/oauth/`, invokes it to run the browser-based authorization flow (`RunOAuth2Flow`) or to retrieve a fresh access token (`GetOAuth2Token`). The `IsOAuth2()` method on `Account` checks the auth method. + +2. **`config/oauth_script.py`** — Embedded Python script that handles the full OAuth2 lifecycle: + - `auth` — Opens a browser for Google authorization, captures the callback on `localhost:8189`, exchanges the code for tokens, and saves them to `~/.config/matcha/oauth_tokens/`. + - `token` — Returns a fresh access token, automatically refreshing if expired (with a 5-minute buffer). + - `revoke` — Revokes tokens with Google and deletes local storage. + - Client credentials are stored in `~/.config/matcha/oauth_client.json`. + +3. **`fetcher/xoauth2.go`** — Implements the XOAUTH2 SASL mechanism (`sasl.Client` interface) for IMAP/SMTP authentication. Formats the initial response as `user=\x01auth=Bearer \x01\x01` per Google's XOAUTH2 protocol spec. diff --git a/fetcher/README.md b/fetcher/README.md index a2a584c..d21d2fc 100644 --- a/fetcher/README.md +++ b/fetcher/README.md @@ -13,3 +13,8 @@ This package is the IMAP client layer for Matcha. It: - Supports S/MIME decryption (opaque and enveloped) and detached signature verification - Provides mailbox operations: delete (expunge), archive (move), and folder-to-folder moves - Exposes both mailbox-specific and convenience functions (e.g., `FetchEmails` defaults to INBOX) +- Supports XOAUTH2 SASL authentication for Gmail OAuth2 accounts (see `xoauth2.go`) + +## XOAUTH2 + +The `xoauth2.go` file implements the XOAUTH2 SASL mechanism as a `sasl.Client`. When an account uses `auth_method: "oauth2"`, the fetcher calls `config.GetOAuth2Token()` to get a fresh access token, then authenticates the IMAP connection using this SASL client instead of a password. The initial response follows Google's XOAUTH2 protocol: `user=\x01auth=Bearer \x01\x01`. diff --git a/plugin/README.md b/plugin/README.md new file mode 100644 index 0000000..c784493 --- /dev/null +++ b/plugin/README.md @@ -0,0 +1,55 @@ +# plugin + +Lua-based plugin system for extending Matcha. Plugins are loaded from `~/.config/matcha/plugins/` and run inside a sandboxed Lua VM (no `os`, `io`, or `debug` libraries). + +## How it works + +The `Manager` creates a Lua VM at startup, registers the `matcha` module, and loads all plugins from the user's plugins directory. Plugins can be either a single `.lua` file or a directory with an `init.lua` entry point. + +Plugins interact with Matcha by registering callbacks on hooks: + +```lua +local matcha = require("matcha") + +matcha.on("email_received", function(email) + matcha.log("New email from: " .. email.from) + matcha.notify("New mail!", 3) +end) +``` + +## Lua API (`matcha` module) + +| Function | Description | +|----------|-------------| +| `matcha.on(event, callback)` | Register a callback for a hook event | +| `matcha.log(msg)` | Log a message to stderr | +| `matcha.notify(msg [, seconds])` | Show a temporary notification in the TUI (default 2s) | +| `matcha.set_status(area, text)` | Set a persistent status string for a view area (`"inbox"`, `"composer"`, `"email_view"`) | + +## Hook events + +| Event | Callback argument | Description | +|-------|-------------------|-------------| +| `startup` | — | Matcha has started | +| `shutdown` | — | Matcha is exiting | +| `email_received` | Lua table with `uid`, `from`, `to`, `subject`, `date`, `is_read`, `account_id`, `folder` | New email arrived | +| `email_viewed` | Same as `email_received` | User opened an email | +| `email_send_before` | Table with `to`, `cc`, `subject`, `account_id` | About to send an email | +| `email_send_after` | Same as `email_send_before` | Email sent successfully | +| `folder_changed` | Folder name (string) | User switched folders | +| `composer_updated` | Table with `body`, `body_len`, `subject`, `to` | Composer content changed | + +## Available plugins + +The following example plugins ship in `~/.config/matcha/plugins/`: + +- `email_age.lua` +- `recipient_counter.lua` + +## Files + +| File | Description | +|------|-------------| +| `plugin.go` | Plugin manager — Lua VM setup, plugin discovery and loading, notification/status state | +| `hooks.go` | Hook definitions, callback registration, and hook invocation helpers | +| `api.go` | `matcha` Lua module registration (`on`, `log`, `notify`, `set_status`) |