sync: mailcore 0.2.1-beta
This commit is contained in:
@@ -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])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user