sync: mailcore 0.2.1-beta

This commit is contained in:
Eugen Kaparulin
2026-07-24 20:14:32 +03:00
parent 467928d541
commit e6df0d451e
6 changed files with 3 additions and 519 deletions

View File

@@ -34,7 +34,7 @@ pub fn init_logger() {
}
/// Step-by-step IMAP connection diagnostic. Returns a multi-line string describing
/// each phase (TCP → TLS → greeting → XOAUTH2 → NOOP) with timing and outcome.
/// each phase (TCP → TLS → greeting) with timing and outcome.
/// Use this from Flutter to identify exactly where the hang is on Android.
pub async fn diagnose_imap_connection(host: String, port: u32, email: String) -> String {
use std::sync::Arc;
@@ -44,6 +44,7 @@ pub async fn diagnose_imap_connection(host: String, port: u32, email: String) ->
use tokio_rustls::TlsConnector;
let mut out = String::new();
out.push_str(&format!("Diagnosing connection for {}\n", email));
let mut root_cert_store = rustls::RootCertStore::empty();
root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
@@ -137,63 +138,6 @@ pub async fn diagnose_imap_connection(host: String, port: u32, email: String) ->
}
};
// Phase 4: Token fetch
let t3 = Instant::now();
let token = match tokio::time::timeout(
Duration::from_secs(10),
crate::oauth::get_valid_token(&email),
)
.await
{
Err(_) => {
out.push_str(&format!("OAuth2 token: TIMEOUT after 10s\n"));
return out;
}
Ok(Err(e)) => {
out.push_str(&format!("OAuth2 token: FAILED ({}) in {:.1}s\n", e, t3.elapsed().as_secs_f32()));
return out;
}
Ok(Ok(t)) => {
out.push_str(&format!(
"OAuth2 token: OK in {:.1}s (len={})\n",
t3.elapsed().as_secs_f32(),
t.len()
));
t
}
};
// Phase 5: XOAUTH2 authenticate (raw IMAP)
use tokio::io::AsyncWriteExt;
let auth_str = format!("user={}\x01auth=Bearer {}\x01\x01", email, token);
use base64::Engine as _;
let encoded = base64::engine::general_purpose::STANDARD.encode(auth_str.as_bytes());
let cmd = format!("A001 AUTHENTICATE XOAUTH2 {}\r\n", encoded);
let t4 = Instant::now();
if let Err(e) = tokio::time::timeout(Duration::from_secs(5), tls_stream.write_all(cmd.as_bytes())).await {
out.push_str(&format!("AUTHENTICATE write: {:?}\n", e));
return out;
}
let mut resp = vec![0u8; 2048];
match tokio::time::timeout(Duration::from_secs(10), tls_stream.read(&mut resp)).await {
Err(_) => {
out.push_str(&format!("AUTHENTICATE response: TIMEOUT after 10s in {:.1}s\n", t4.elapsed().as_secs_f32()));
}
Ok(Err(e)) => {
out.push_str(&format!("AUTHENTICATE response: FAILED ({}) in {:.1}s\n", e, t4.elapsed().as_secs_f32()));
}
Ok(Ok(n)) => {
let line = String::from_utf8_lossy(&resp[..n.min(200)]).to_string();
out.push_str(&format!(
"AUTHENTICATE response in {:.1}s: {:?}\n",
t4.elapsed().as_secs_f32(),
line.trim()
));
}
}
out
}
@@ -1521,52 +1465,3 @@ pub fn get_folder_stats(
local_total,
})
}
/// Populate the in-memory OAuth2 token store from Flutter secure storage.
/// Call at startup for every OAuth2 account with the persisted refresh token.
/// Rust will perform the first token refresh automatically on the next IMAP connect.
pub fn init_oauth2_session(email: String, refresh_token: String) {
crate::oauth::init_session(&email, &refresh_token);
}
/// Remove all OAuth2 tokens for an account from the in-memory store.
/// Call when removing an OAuth2 account.
pub fn delete_oauth2_tokens(email: String) -> Result<(), MailError> {
crate::oauth::delete_tokens(&email);
Ok(())
}
/// Provide OAuth2 client credentials. Must be called once at startup before
/// any Google sign-in or token refresh. Lives in the private mailmore layer.
pub fn init_google_credentials(client_id: String, client_secret: String) {
crate::oauth::init_credentials(&client_id, &client_secret);
}
/// Provide Desktop/mobile PKCE client credentials for browser-based OAuth flow.
pub fn init_google_desktop_credentials(client_id: String, client_secret: String) {
crate::oauth::init_desktop_credentials(&client_id, &client_secret);
}
/// Exchange a Google server-side authorization code for access + refresh tokens.
/// Returns (access_token, refresh_token) — the caller must persist the refresh
/// token via Flutter secure storage for use across app restarts.
pub async fn exchange_google_auth_code(
email: String,
server_auth_code: String,
) -> Result<Vec<String>, MailError> {
let (access, refresh) =
crate::oauth::exchange_auth_code(&email, &server_auth_code).await?;
Ok(vec![access, refresh])
}
/// Exchange an authorization code from the desktop PKCE flow.
/// Returns (email, access_token, refresh_token).
pub async fn exchange_google_auth_code_desktop(
code: String,
redirect_uri: String,
code_verifier: String,
) -> Result<Vec<String>, MailError> {
let (email, access, refresh) =
crate::oauth::exchange_auth_code_desktop(&code, &redirect_uri, &code_verifier).await?;
Ok(vec![email, access, refresh])
}