Implement MOVE / UID MOVE (RFC 6851): resolve the target by IMAP path
(UTF-7 decoded), call the SDK move_mails for the selected messages, then
expunge them from the source view. Advertise the MOVE capability.
COPY is rejected with NO — Tuta folders are exclusive, so duplication
isn't supported; clients use MOVE instead.
FolderInfo gains the folder's list id so the target MailSet IdTuple can
be reconstructed. Pulls in the SDK move_mails (tuta-repo submodule bump).
Live-tested: a mail moved from one custom folder to a nested UTF-7 folder
lands in the target and leaves the source server-side.
The syncer ran one loop: phase 1 (folder list + mail-id lists) then
phase 2 (body prefetch). On a large mailbox the cold prefetch pass takes
many minutes, so the next folder refresh was stuck behind it and new
folders/mail only showed up after a restart.
Split into two independent loops sharing the store: a fast list_sync_loop
(folder list + mail ids, ~every SYNC_INTERVAL) and a slow prefetch_loop
(bodies, background). Folder and new-mail refresh no longer wait on
prefetch.
Live-tested: a folder created while the bridge runs appears over IMAP in
~18s, no restart.
Add an imap::utf7 module (RFC 3501 §5.1.3) and apply it at the protocol
boundary: encode mailbox names in LIST, decode them in SELECT/STATUS.
Folder names internally stay UTF-8; only the IMAP wire form is UTF-7.
Also fix STATUS argument parsing to handle quoted mailbox names with
spaces (it split on the first space, truncating names like
"Not Important" or nested paths).
Live-tested: "Café" lists as "Caf&AOk-", a nested child lists as
"Caf&AOk-/Test dossier avec espace", and SELECT/STATUS resolve both.
Key the syncer, local store and IMAP server by Tuta MailSet folder id
instead of the system folder kind, so custom (and nested) folders are
first-class.
- tuta.rs: add FolderInfo; MailBackend.list_folders enumerates system +
custom folders via the SDK FolderSystem tree, building IMAP paths and
RFC 6154 special-use flags; load mails by the folder's entries list.
- store.rs: folder_kind INTEGER -> folder_id TEXT, schema v2 with a
migration that drops the cache tables and re-syncs (encrypted .eml
files survive).
- sync.rs: MailStore keyed by folder id; the syncer enumerates the live
folder list each cycle.
- imap/session.rs: dynamic LIST/SELECT/STATUS driven by the folder list;
drop the hardcoded six-folder mapping.
Pulls in the SDK FolderSystem tree (tuta-repo submodule bump).
Live-tested: 9 custom folders listed and selectable over IMAP, with
headers and decrypted bodies. Not yet covered: nested-folder paths,
modified UTF-7 for non-ASCII names, labels, and IMAP MOVE/COPY.
Split the bridge into a tutabridge-core crate, a Tauri v2 desktop app
(src-tauri) and a React/TS UI (ui), keeping the CLI entrypoint at the
workspace root.
Add encrypted local storage (SQLCipher metadata index + encrypted .eml
files) so mail persists across launches and only the delta is fetched.
Wire the bridge to the Tuta Rust SDK via the tuta-repo submodule
(batch loading, MailDetailsBlob reading, interactive 2FA login).
Implement SMTP sending: build the draft and send it through Tuta's
DraftService/SendDraftService, mirroring the web client (body in
compressedBodyText, non-empty sender/recipient names, populated
SendDraftParameters). Add unit tests for the draft/send payload building.