diff --git a/Cargo.lock b/Cargo.lock index ef4de45..ac544c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,9 +362,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bcrypt" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7" +checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e" dependencies = [ "base64 0.22.1", "blowfish 0.9.1", @@ -399,9 +399,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitfield" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" +checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" [[package]] name = "bitflags" @@ -888,7 +888,7 @@ dependencies = [ "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", - "fiat-crypto 0.2.9", + "fiat-crypto", "rustc_version", "subtle", "zeroize", @@ -1156,23 +1156,23 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1277,17 +1277,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ed448-goldilocks" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b5fa9e9e3dd5fe1369f380acd3dcdfa766dbd0a1cd5b048fb40e38a6a78e79" -dependencies = [ - "fiat-crypto 0.1.20", - "hex", - "subtle", -] - [[package]] name = "either" version = "1.15.0" @@ -1477,12 +1466,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" - [[package]] name = "fiat-crypto" version = "0.2.9" @@ -2452,9 +2435,9 @@ dependencies = [ [[package]] name = "mailparse" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da03d5980411a724e8aaf7b61a7b5e386ec55a7fb49ee3d0ff79efc7e5e7c7e" +checksum = "60819a97ddcb831a5614eb3b0174f3620e793e97e09195a395bfa948fd68ed2f" dependencies = [ "charset", "data-encoding", @@ -3016,9 +2999,9 @@ dependencies = [ [[package]] name = "pgp" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1877a97fd422433220ad272eb008ec55691944b1200e9eb204e3cb2cb69d34e9" +checksum = "30249ac8a98b356b473b04bc5358c75a260aa96a295d0743ce752fe7b173f235" dependencies = [ "aes", "aes-gcm", @@ -3074,10 +3057,9 @@ dependencies = [ "sha3", "signature", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", "twofish", "x25519-dalek", - "x448", "zeroize", ] @@ -3371,10 +3353,8 @@ dependencies = [ name = "proton-bridge" version = "0.1.0" dependencies = [ - "aes", "base64 0.22.1", "bcrypt", - "cfb-mode", "chrono", "env_logger", "keyring", @@ -3385,7 +3365,6 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sha1", "sha2 0.10.9", "tokio", "toml", @@ -3633,13 +3612,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] @@ -4258,9 +4237,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.6.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -5157,15 +5136,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5202,21 +5172,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5250,12 +5205,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5268,12 +5217,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5286,12 +5229,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5316,12 +5253,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5334,12 +5265,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5352,12 +5277,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5370,12 +5289,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5503,17 +5416,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "x448" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd07d4fae29e07089dbcacf7077cd52dce7760125ca9a4dd5a35ca603ffebb" -dependencies = [ - "ed448-goldilocks", - "hex", - "rand_core 0.5.1", -] - [[package]] name = "xdg-home" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 77a5133..c88a5b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,8 @@ name = "tuimail" version = "0.1.0" edition = "2024" -[features] -proton = ["dep:proton-bridge"] - [dependencies] -proton-bridge = { path = "proton-bridge", optional = true } +proton-bridge = { path = "proton-bridge", optional = false } ratatui = "0.30" crossterm = "0.29" imap = "2.4" @@ -18,13 +15,13 @@ native-tls = "0.2" serde = { version = "1.0", features = ["derive"] } toml = "1.0" chrono = "0.4" -mailparse = "0.15" +mailparse = "0.16" fast_html2md = "0.0" tui-markdown = "0.3" quoted_printable = "0.5" regex = "1" lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "native-tls", "builder"] } -dirs = "5" +dirs = "6.0" rand = { version = "0.8", features = ["getrandom"] } aes-gcm = "0.10" diff --git a/proton-bridge/Cargo.toml b/proton-bridge/Cargo.toml index c357c9e..aff55dd 100644 --- a/proton-bridge/Cargo.toml +++ b/proton-bridge/Cargo.toml @@ -12,15 +12,12 @@ toml = "1.0" sha2 = "0.10" num-bigint = "0.4" base64 = "0.22" -rand = "0.8" +rand = { version = "0.8.5", features = ["getrandom"] } pwhash = "0.3" # bcrypt with caller-supplied salt (used for SRP) -bcrypt = "0.15" # reference bcrypt impl for key passphrase derivation -pgp = { version = "0.14", default-features = false } # rpgp — OpenPGP decrypt +bcrypt = "0.16" # reference bcrypt impl for key passphrase derivation +pgp = { version = "0.15", default-features = false } # rpgp — OpenPGP decrypt chrono = "0.4" env_logger = "0.11" -aes = "0.8" -cfb-mode = "0.8" -sha1 = "0.10" tracing = "0.1.44" [target.'cfg(target_os = "macos")'.dependencies] diff --git a/proton-bridge/src/smtp_server.rs b/proton-bridge/src/smtp_server.rs index 263a0fd..4704708 100644 --- a/proton-bridge/src/smtp_server.rs +++ b/proton-bridge/src/smtp_server.rs @@ -5,13 +5,13 @@ /// the ProtonMail v4 API (create draft → send). use std::sync::Arc; -use base64::engine::general_purpose::STANDARD as B64; +use crate::api::ApiClient; +use crate::{SharedState, crypto}; use base64::Engine; +use base64::engine::general_purpose::STANDARD as B64; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::net::{TcpListener, TcpStream}; -use tracing::{error, info}; -use crate::api::ApiClient; -use crate::{crypto, SharedState}; +use tracing::error; // ── Public entry point ──────────────────────────────────────────────────────── @@ -49,7 +49,9 @@ async fn handle_connection( // auth_step: 0 = not in AUTH, 1 = sent "Username:", 2 = sent "Password:" let mut auth_step: u8 = 0; - writer.write_all(b"220 ProtonBridge ESMTP ready\r\n").await?; + writer + .write_all(b"220 ProtonBridge ESMTP ready\r\n") + .await?; while let Some(raw) = lines.next_line().await? { let line = raw.trim_end().to_string(); @@ -61,8 +63,7 @@ async fn handle_connection( if in_data { if line == "." { in_data = false; - let resp = - handle_send(&mail_from, &rcpt_to, &data_lines, &state).await; + let resp = handle_send(&mail_from, &rcpt_to, &data_lines, &state).await; writer.write_all(resp.as_bytes()).await?; mail_from.clear(); rcpt_to.clear(); @@ -198,9 +199,7 @@ async fn handle_send( let plaintext = data_lines[blank + 1..].join("\n"); for recipient in to { - if let Err(e) = - send_to_one(from, recipient, &subject, &plaintext, state).await - { + if let Err(e) = send_to_one(from, recipient, &subject, &plaintext, state).await { // Log the full error in the bridge console; return a short fixed // message to skim so the status line doesn't overflow. eprintln!("SMTP send to {recipient} FAILED:\n {e}"); @@ -264,7 +263,13 @@ async fn send_to_one( .map_err(|e| format!("encrypt draft body: {e}"))?, }; - (st.http_client.clone(), st.session.clone(), aid, db, signing_key) + ( + st.http_client.clone(), + st.session.clone(), + aid, + db, + signing_key, + ) }; let api = ApiClient::new(&http_client, &session); @@ -283,7 +288,8 @@ async fn send_to_one( // message body arrives clean (no PGP armor visible in Gmail / other clients). // ProtonMail decrypts the SEIPD using BodyKey and relays the multipart/signed // MIME entity to the external recipient over SMTP. - let signing_key = signing_key_opt.as_ref() + let signing_key = signing_key_opt + .as_ref() .ok_or("no signing key available for external send")?; let (seipd, session_key) = crypto::build_pgp_mime_for_external_send(plaintext, signing_key) .map_err(|e| format!("PGP/MIME external body: {e}"))?; @@ -298,8 +304,15 @@ async fn send_to_one( .create_draft(&address_id, subject, from, "", to, "", &draft_body) .await?; - api.send_draft(&draft_id, to, send_type, &body_b64, &key_packet_b64, body_signed) - .await?; + api.send_draft( + &draft_id, + to, + send_type, + &body_b64, + &key_packet_b64, + body_signed, + ) + .await?; Ok(()) } diff --git a/src/config.rs b/src/config.rs index cae6e21..a5f1af4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -187,7 +187,6 @@ impl Config { /// Convert to the `proton-bridge` crate's `Config` type. /// Only valid when `provider = "proton"`. - #[cfg(feature = "proton")] pub fn as_bridge_config(&self) -> Result { let p = self.proton.as_ref().ok_or("[proton] section missing")?; let b = self.bridge.as_ref().ok_or("[bridge] section missing")?; diff --git a/src/main.rs b/src/main.rs index 44f226d..adddb9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,10 @@ use std::process::exit; use crossterm::{ execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, }; +use ratatui::{Terminal, backend::CrosstermBackend}; use tuimail::config::{Config, Provider}; -use ratatui::{ - backend::CrosstermBackend, - Terminal, -}; fn main() -> io::Result<()> { // ── Parse --configure flag ── @@ -51,25 +48,16 @@ fn main() -> io::Result<()> { // ── Start bridge if needed (before entering raw mode so output is clean) ── if config.provider == Provider::Proton { - #[cfg(feature = "proton")] - { - let bridge_cfg = config.as_bridge_config().unwrap_or_else(|e| { - eprintln!("Bridge config error: {e}"); - exit(1); - }); - eprint!("Starting ProtonMail bridge..."); - proton_bridge::start(bridge_cfg).unwrap_or_else(|e| { - eprintln!("\nBridge failed to start: {e}"); - exit(1); - }); - eprintln!(" ready."); - } - #[cfg(not(feature = "proton"))] - { - eprintln!("tuimail was not compiled with ProtonMail support."); - eprintln!("Rebuild with: cargo build --features proton"); + let bridge_cfg = config.as_bridge_config().unwrap_or_else(|e| { + eprintln!("Bridge config error: {e}"); exit(1); - } + }); + eprint!("Starting ProtonMail bridge..."); + proton_bridge::start(bridge_cfg).unwrap_or_else(|e| { + eprintln!("\nBridge failed to start: {e}"); + exit(1); + }); + eprintln!(" ready."); } // ── Derive effective IMAP/SMTP configs for the chosen provider ──