X-GM-EXT-1 Gmail Label Extension for Proton Bridge#450
Conversation
|
@bolausson any plan to add QUOTA COMMAND |
I don't see why paperless-ngx would need that. The sole purpose of this patch is to make the bride compatible with the way paperless-ngx tags (applies labels) to eMails. Cheers, |
Implement the X-GM-EXT-1 IMAP extension to support Gmail-style label operations via STORE X-GM-LABELS commands. This allows IMAP clients like Paperless-NGX to apply/remove labels without moving messages between folders. Changes: - Advertise X-GM-EXT-1 capability in IMAP sessions - Extend STORE command parser to handle X-GM-LABELS data item - Add StoreGmailLabels method to state Mailbox - Add SetGmailLabels to connector interface - Route Gmail label STORE through connector to backend
Extend the FETCH command to recognize X-GM-LABELS as a fetch attribute,
allowing IMAP clients to read Gmail-style labels on messages. This
complements the existing STORE X-GM-LABELS support.
Changes:
- Parse X-GM-LABELS in FETCH attribute handler
- Add GetGmailLabels to connector interface for label retrieval
- Format response as X-GM-LABELS ("Label1" "Label2")
Python's imaplib sends CAPABILITY before LOGIN and never re-reads capabilities after authentication. Moving X-GM-EXT-1 to the pre-auth capability list so clients like paperless-ngx can detect Gmail label support.
Parse X-GM-LABELS as a search key so clients like paperless-ngx can filter messages by Gmail-style label (e.g. NOT X-GM-LABELS "Paperless"). The search operation calls GetGmailLabels on the connector for each candidate message.
Replace per-message Proton API calls with local DB queries. The search now resolves the label to a mailbox ID via the connector, queries the local DB for all messages in that mailbox, and builds an in-memory set for O(1) per-message lookups. This turns N API calls into 2 local queries.
Python's imaplib sends labels without wrapping in parentheses: UID STORE 123 +X-GM-LABELS Paperless The parser previously required parenthesized form only: UID STORE 123 +X-GM-LABELS (Paperless) Now both forms are accepted.
Covers the previously-untested X-GM-LABELS extension surface: - imap/command: STORE/FETCH/SEARCH parser tests, incl. the bare-label (Python imaplib) form and NOT X-GM-LABELS — the exact wire forms Paperless-NGX emits. - internal/response: itemGmailLabels formatting (empty/quoting/spaces). - connector dummy: store labels as non-exclusive label mailboxes so STORE/FETCH/SEARCH round-trip end-to-end without touching folder membership; previous methods were no-op stubs. - tests: STORE -> FETCH -> STORE remove round trip, multi-message, labels with spaces, and SEARCH / NOT X-GM-LABELS (the Paperless dedupe query). - Fix TestCapability/TestCapabilityAuthenticateDisabled/login/ authenticate which now must advertise X-GM-EXT-1 (sorted last); these were broken when the feature commit started advertising it.
1974e75 to
fb4bd2d
Compare
Point the gluon dependency at the gluon module that carries the X-GM-EXT-1 server support (depends on ProtonMail/gluon#450) and pin golang.org/x/exp to the revision internal/usertypes expects so the module graph resolves and builds.
|
This PR adds the X-GM-EXT-1 Gmail label extension ( Motivation: Paperless-NGX tags processed e-mails over IMAP using Gmail-style labels ( Rebased on current |
|
@bolausson can I use it explicitly |
X-GM-EXT-1 is a server-side IMAP capability, so it depends on what your IMAP client connects to, not the client itself! Server: Client: |
Bring the Gmail label (X-GM-EXT-1) work up to date with current ProtonMail/proton-bridge master (through Oberbaum 3.25.0). Conflict resolution: - go.mod/go.sum: took upstream's dependency versions (urfave/cli, msgpack, google.golang.org/api, grpc, go toolchain 1.26.2, gluon bump). - Dropped the golang.org/x/exp pin + replace directive. Upstream BRIDGE-534 moved exp/maps to stdlib maps, so no first-party code imports x/exp anymore; go mod tidy now resolves it as a normal indirect dependency at a current version. The gluon dependency stays pointed at the local ../gluon fork that carries X-GM-EXT-1 server support (depends on ProtonMail/gluon#450); this must be repointed at a published revision before the PR can build upstream. Verified: go build ./... , go vet and go test ./internal/services/imapservice/... all pass.
Add a WithGmailExtension() server option controlling the non-standard Gmail X-GM-EXT-1 extension. When not set (the default): - the X-GM-EXT-1 capability is not advertised, and - STORE/FETCH/SEARCH X-GM-LABELS commands are rejected with BAD. This prevents standard IMAP clients (Thunderbird, Apple Mail, etc.) from being exposed to Gmail label semantics they do not expect. The option is threaded from the server builder into the session, mirroring the existing disableIMAPAuthenticate gating. Tests opt in via withGmailExtension(); added coverage for the disabled default (capability absent + X-GM-LABELS commands rejected).
Just to be clear - This was entirly coded with a LLM!
X-GM-EXT-1 Gmail Label Extension for Proton Bridge
Paperless-NGX uses Gmail's
X-GM-LABELSIMAP extension to apply and query labels on emails after processing. Proton Bridge didn't support this, so we implemented it across Gluon and Bridge.Gluon (IMAP server library)
STORE +X-GM-LABELS ("Paperless")/-X-GM-LABELScommands, routes them through a newSetGmailLabelsconnector interface to the Bridge backend. AddedX-GM-EXT-1capability advertisement.FETCH (X-GM-LABELS)as a fetch attribute, callsGetGmailLabelson the connector, returns labels in Gmail format:X-GM-LABELS ("Paperless" "Notifications").X-GM-LABELSas a search key so clients can filter messages by label (e.g.NOT X-GM-LABELS "Paperless"). The search operation callsGetGmailLabelson the connector for each candidate message with case-insensitive matching.X-GM-EXT-1to the pre-auth capability list. Python'simaplibchecks capabilities beforeLOGINand never re-reads them, so clients like Paperless-NGX weren't detecting support.Proton Bridge
MarkMessagesWithGmailLabelsmaps label names to Proton label IDs (auto-creating if needed), then callsLabelMessages/UnlabelMessagesAPI. Messages stay in INBOX — labels are applied without moving.GetGmailLabelscallsGetMessageAPI to getLabelIDs, maps them back to names via the shared label cache, filtering forLabelTypeLabelonly. Used by both FETCH and SEARCH operations.Tests
Paperless-ngx logs:
paperless.log
mail.log
celery.log