|
@@ -1,76 +1,69 @@
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
-use std::error::Error;
|
|
|
use std::{io};
|
|
|
+use std::convert::TryFrom;
|
|
|
use std::fs::File;
|
|
|
use std::future::Future;
|
|
|
-use std::net::{TcpStream};
|
|
|
use std::path::{PathBuf};
|
|
|
use std::io::{BufReader, BufWriter, ErrorKind, Read, Write};
|
|
|
+use std::sync::Arc;
|
|
|
use std::time::Duration;
|
|
|
+use anyhow::anyhow;
|
|
|
+use futures::{StreamExt, TryStreamExt};
|
|
|
use kuchiki::iter::NodeIterator;
|
|
|
use serde_json::to_writer;
|
|
|
-use tokio::task::JoinHandle;
|
|
|
+use tokio::net::TcpStream;
|
|
|
+use tokio_rustls::client::TlsStream;
|
|
|
+use tokio_rustls::rustls::{ClientConfig, RootCertStore};
|
|
|
+use tokio_rustls::rustls::pki_types::ServerName;
|
|
|
use crate::config::Config;
|
|
|
-
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
-use native_tls::{TlsStream};
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
-use imap::{Session};
|
|
|
use crate::{add_email};
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
-use crate::imap::imap_client::{Client, connect_to_imap_server};
|
|
|
-
|
|
|
-pub(crate) mod imap_client;
|
|
|
+use tokio::task;
|
|
|
+use tokio::time::sleep;
|
|
|
|
|
|
#[cfg(not(target_os = "wasi"))]
|
|
|
-pub async fn download_email_from_imap(maildir_path: PathBuf, imap_domain: String, username: String, password: String) -> Result<Vec<(u32, PathBuf)>, Box<dyn Error>>{
|
|
|
- let tls = native_tls::TlsConnector::builder().build().unwrap();
|
|
|
- let client = imap::connect((imap_domain.clone(), Config::global().imap_port.clone() as u16), imap_domain.clone(), &tls).unwrap();
|
|
|
- let mut imap_session = client
|
|
|
- .login(username, password)
|
|
|
- .map_err(|e| e.0)?;
|
|
|
- let mut stored_paths: Vec<(u32, PathBuf)> = vec![];
|
|
|
-
|
|
|
- match list_imap_folders(&mut imap_session).await {
|
|
|
- Ok(folders) => {
|
|
|
- for folder in folders {
|
|
|
- println!("Start downloading {}", folder.clone());
|
|
|
- let new_paths = match fetch_and_store_emails(&mut imap_session, maildir_path.clone(), folder.clone()).await {
|
|
|
- Ok(paths) => {
|
|
|
- println!("Emails fetched and stored successfully for {}.", folder);
|
|
|
- paths
|
|
|
- },
|
|
|
- Err(err) => {
|
|
|
- eprintln!("Error during email fetching and storing: {}", err);
|
|
|
- vec![]
|
|
|
- }
|
|
|
- };
|
|
|
- stored_paths.extend(new_paths);
|
|
|
- }
|
|
|
- },
|
|
|
- Err(e) => eprintln!("Error listing folders: {}", e),
|
|
|
- }
|
|
|
-
|
|
|
- // Logout from the IMAP session
|
|
|
- match imap_session.logout() {
|
|
|
- Ok(_) => println!("Logged out successfully."),
|
|
|
- Err(err) => eprintln!("Error logging out: {}", err),
|
|
|
- }
|
|
|
-
|
|
|
- Ok(stored_paths)
|
|
|
-}
|
|
|
+use async_imap::{Client, Session};
|
|
|
+#[cfg(not(target_os = "wasi"))]
|
|
|
+use async_imap::extensions::idle::IdleResponse::NewData;
|
|
|
|
|
|
#[cfg(target_os = "wasi")]
|
|
|
-pub async fn download_email_from_imap(maildir_path: PathBuf, imap_domain: String, username: String, password: String) -> Result<Vec<(u32, PathBuf)>, Box<dyn Error>>{
|
|
|
- let mut client = connect_to_imap_server(imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
- client.login(username.clone(), password.clone()).await?;
|
|
|
- let mut stored_paths: Vec<(u32, PathBuf)> = vec![];
|
|
|
+use async_imap_wasi::{Client, Session};
|
|
|
+#[cfg(target_os = "wasi")]
|
|
|
+use async_imap_wasi::extensions::idle::IdleResponse::NewData;
|
|
|
|
|
|
- match client.list().await {
|
|
|
+pub async fn connect_to_imap() -> anyhow::Result<Client<TlsStream<TcpStream>>>{
|
|
|
+ let mut root_cert_store = RootCertStore::empty();
|
|
|
+ root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
|
|
+ let config = ClientConfig::builder()
|
|
|
+ .with_root_certificates(root_cert_store)
|
|
|
+ .with_no_client_auth();
|
|
|
+ let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
|
|
|
+ let dnsname = ServerName::try_from(Config::global().imap_domain.clone()).unwrap();
|
|
|
+
|
|
|
+ let stream = TcpStream::connect(format!("{}:{}", Config::global().imap_domain.clone(), Config::global().imap_port.clone())).await?;
|
|
|
+ let stream = connector.connect(dnsname, stream).await?;
|
|
|
+
|
|
|
+ #[cfg(not(target_os = "wasi"))]
|
|
|
+ let client = async_imap::Client::new(stream);
|
|
|
+ #[cfg(target_os = "wasi")]
|
|
|
+ let client = async_imap_wasi::Client::new(stream);
|
|
|
+
|
|
|
+ Ok(client)
|
|
|
+}
|
|
|
+
|
|
|
+pub async fn download_email_from_imap() -> anyhow::Result<Vec<(u32, PathBuf)>>{
|
|
|
+ let mut client = connect_to_imap().await?;
|
|
|
+ let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
|
|
|
+ let mut session = match session_result {
|
|
|
+ Ok(session) => {session}
|
|
|
+ Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut stored_paths: Vec<(u32, PathBuf)> = vec![];
|
|
|
+ match list_imap_folders(&mut session).await {
|
|
|
Ok(folders) => {
|
|
|
for folder in folders {
|
|
|
println!("Start downloading {}", folder.clone());
|
|
|
- let new_paths = match fetch_and_store_emails(&mut client, maildir_path.clone(), folder.clone()).await {
|
|
|
+ let new_paths = match fetch_and_store_emails(&mut session, folder.clone()).await {
|
|
|
Ok(paths) => {
|
|
|
println!("Emails fetched and stored successfully for {}.", folder);
|
|
|
paths
|
|
@@ -82,43 +75,47 @@ pub async fn download_email_from_imap(maildir_path: PathBuf, imap_domain: String
|
|
|
};
|
|
|
stored_paths.extend(new_paths);
|
|
|
}
|
|
|
- },
|
|
|
- Err(e) => eprintln!("Error listing folders: {}", e),
|
|
|
+ }
|
|
|
+ Err(_) => {return Err(anyhow!("Unable to retrieve folders from IMAP server"))}
|
|
|
}
|
|
|
|
|
|
// Logout from the IMAP session
|
|
|
- client.logout().await.expect("Unable to logout");
|
|
|
+ session.logout().await.expect("Unable to logout");
|
|
|
|
|
|
Ok(stored_paths)
|
|
|
}
|
|
|
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
-pub async fn list_imap_folders(imap_session: &mut Session<TlsStream<TcpStream>>) -> imap::error::Result<Vec<String>> {
|
|
|
- // The function return all available folders(lists) on the IMAP server
|
|
|
- // Fetch the list of mailboxes (folders)
|
|
|
- let mailboxes = imap_session.list(None, Some("*"))?;
|
|
|
- let folder_names = mailboxes.iter().map(|mailbox| mailbox.name().to_string()).collect();
|
|
|
-
|
|
|
- Ok(folder_names)
|
|
|
+pub async fn list_imap_folders(session: &mut Session<TlsStream<TcpStream>>) -> anyhow::Result<Vec<String>> {
|
|
|
+ let mut folders: Vec<String> = vec![];
|
|
|
+ let mut folders_stream = session.list(None, Some("*")).await?;
|
|
|
+ while let Some(folder_result) = folders_stream.next().await {
|
|
|
+ match folder_result {
|
|
|
+ Ok(folder) => {
|
|
|
+ folders.push(folder.name().to_string())
|
|
|
+ }
|
|
|
+ Err(_) => {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(folders)
|
|
|
}
|
|
|
|
|
|
+pub async fn fetch_and_store_emails(session: &mut Session<TlsStream<TcpStream>>, list: String) -> anyhow::Result<Vec<(u32, PathBuf)>> {
|
|
|
+ session.select(list.clone()).await?;
|
|
|
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
-pub async fn fetch_and_store_emails(imap_session: &mut Session<TlsStream<TcpStream>>, maildir_path: PathBuf, list: String) -> imap::error::Result<Vec<(u32, PathBuf)>> {
|
|
|
- // the function download all emails from the IMAP in the folder(list)
|
|
|
- imap_session.select(list.clone())?;
|
|
|
+ // Create directories for maildir
|
|
|
+ std::fs::create_dir_all(Config::global().maildir.clone().join(list.clone()).join("new"))
|
|
|
+ .expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
+ std::fs::create_dir_all(Config::global().maildir.clone().join(list.clone()).join("cur"))
|
|
|
+ .expect(&*("Unable to create 'cur' directory in ".to_owned() + &*list.clone()));
|
|
|
+ std::fs::create_dir_all(Config::global().maildir.clone().join(list.clone()).join("tmp"))
|
|
|
+ .expect(&*("Unable to create 'tmp' directory in ".to_owned() + &*list.clone()));
|
|
|
|
|
|
- // creating files for maildir
|
|
|
- std::fs::create_dir_all(maildir_path.clone().join(list.clone()).join("new")).expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
- std::fs::create_dir_all(maildir_path.clone().join(list.clone()).join("cur")).expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
- std::fs::create_dir_all(maildir_path.clone().join(list.clone()).join("tmp")).expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
+ let uids_path = Config::global().maildir.clone().join(".uids.json");
|
|
|
|
|
|
- let uids_path = maildir_path.clone().join(".uids.json");
|
|
|
-
|
|
|
- let uids_server = imap_session.uid_search("ALL").unwrap();
|
|
|
+ let uids_server = session.uid_search("ALL").await.unwrap();
|
|
|
let uids_local = read_local_uids(uids_path.clone()).unwrap();
|
|
|
let mut stored_paths: Vec<(u32, PathBuf)> = vec![];
|
|
|
-
|
|
|
+
|
|
|
for uid in uids_server {
|
|
|
if let Some(uids) = uids_local.get(&list) {
|
|
|
if uids.contains(&uid.to_string()) {
|
|
@@ -126,77 +123,45 @@ pub async fn fetch_and_store_emails(imap_session: &mut Session<TlsStream<TcpStre
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- let messages = imap_session.uid_fetch(uid.to_string(), "(UID BODY[])")?;
|
|
|
- let message = messages.iter().next().ok_or(imap::error::Error::Bad("No messages found".to_string()))?;
|
|
|
-
|
|
|
- if let Some(body) = message.body() {
|
|
|
- let mail_file = store(maildir_path.clone().join(list.clone()), uid.clone().to_string(), "new".to_string(), body, "");
|
|
|
- match mail_file {
|
|
|
- Ok(file) => {stored_paths.push((uid.to_string().parse().unwrap(), file))},
|
|
|
- Err(e) => eprintln!("Failed to store email: {}", e),
|
|
|
+ let mut messages_stream = session.uid_fetch(uid.to_string(), "(UID BODY[])").await?;
|
|
|
+ let mut message = None;
|
|
|
+ while let Some(message_result) = messages_stream.next().await {
|
|
|
+ match message_result {
|
|
|
+ Ok(message_ok) => {
|
|
|
+ message = Some(message_ok);
|
|
|
+ }
|
|
|
+ Err(_) => {
|
|
|
+ return Err(anyhow!("Unable to retrieve message"));
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Some(msg) = &message {
|
|
|
+ if let Some(body) = msg.body() {
|
|
|
+ let mail_file = store(Config::global().maildir.clone().join(list.clone()), uid.clone().to_string(), "new".to_string(), body, "");
|
|
|
+ match mail_file {
|
|
|
+ Ok(file) => stored_paths.push((uid.to_string().parse().unwrap(), file)),
|
|
|
+ Err(e) => eprintln!("Failed to store email: {}", e),
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ eprintln!("Failed to retrieve email body");
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Some(uid) = msg.uid {
|
|
|
+ update_local_uids(uid.to_string(), list.clone(), uids_path.clone())
|
|
|
+ .expect("Cannot write uid to .uids.json");
|
|
|
+ }
|
|
|
+
|
|
|
+ println!("Downloaded message with UID {}", uid.to_string());
|
|
|
} else {
|
|
|
- eprintln!("Failed to retrieve email body");
|
|
|
+ return Err(anyhow!("No message found with the given UID"));
|
|
|
}
|
|
|
-
|
|
|
- if let Some(uid) = message.uid {
|
|
|
- update_local_uids(uid.to_string(), list.clone(), uids_path.clone()).expect("Cannot write uid to .uids.json");
|
|
|
- }
|
|
|
-
|
|
|
- println!("Downloaded message with UID {}", uid.to_string());
|
|
|
}
|
|
|
|
|
|
Ok(stored_paths)
|
|
|
}
|
|
|
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
-pub async fn fetch_and_store_emails(client: &mut Client, maildir_path: PathBuf, list: String) -> Result<Vec<(u32, PathBuf)>, Box<dyn std::error::Error>> {
|
|
|
- // the function download all emails from the IMAP in the folder(list)
|
|
|
- client.select(list.clone()).await?;
|
|
|
-
|
|
|
- // creating files for maildir
|
|
|
- std::fs::create_dir_all(maildir_path.clone().join(list.clone()).join("new")).expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
- std::fs::create_dir_all(maildir_path.clone().join(list.clone()).join("cur")).expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
- std::fs::create_dir_all(maildir_path.clone().join(list.clone()).join("tmp")).expect(&*("Unable to create 'new' directory in ".to_owned() + &*list.clone()));
|
|
|
-
|
|
|
- let uids_path = maildir_path.clone().join(".uids.json");
|
|
|
-
|
|
|
- let uids_server = client.uid_search("ALL".to_string()).await.unwrap();
|
|
|
- let uids_local = read_local_uids(uids_path.clone()).unwrap();
|
|
|
- let mut stored_paths: Vec<(u32, PathBuf)> = vec![];
|
|
|
-
|
|
|
- for uid in uids_server {
|
|
|
- if let Some(uids) = uids_local.get(&list) {
|
|
|
- if uids.contains(&uid.to_string()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let message = client.uid_fetch(uid.clone().to_string(), "(UID BODY[])".to_string()).await?;
|
|
|
- let mut body = message.clone();
|
|
|
-
|
|
|
- if let Some(index) = message.find('\n') {
|
|
|
- body = (&message[index+1..]).to_string();
|
|
|
- } else {}
|
|
|
-
|
|
|
-
|
|
|
- let mail_file = store(maildir_path.clone().join(list.clone()), uid.clone().to_string(), "new".to_string(), body.as_bytes(), "");
|
|
|
- match mail_file {
|
|
|
- Ok(file) => {stored_paths.push((uid.to_string().parse().unwrap(), file))},
|
|
|
- Err(e) => eprintln!("Failed to store email: {}", e),
|
|
|
- }
|
|
|
-
|
|
|
- update_local_uids(uid.to_string(), list.clone(), uids_path.clone()).expect("Cannot write uid to .uids.txt");
|
|
|
-
|
|
|
-
|
|
|
- println!("Downloaded message with UID {}", uid.to_string());
|
|
|
- }
|
|
|
-
|
|
|
- Ok(stored_paths)
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-fn read_local_uids(uids_path: PathBuf) -> io::Result<HashMap<String, HashSet<String>>> {
|
|
|
+fn read_local_uids(uids_path: PathBuf) -> anyhow::Result<HashMap<String, HashSet<String>>> {
|
|
|
let file = match File::open(&uids_path) {
|
|
|
Ok(file) => file,
|
|
|
Err(_) => return Ok(HashMap::new()), // Propagate other errors
|
|
@@ -214,7 +179,7 @@ fn read_local_uids(uids_path: PathBuf) -> io::Result<HashMap<String, HashSet<Str
|
|
|
// Deserialize the JSON into a temporary structure
|
|
|
let data: HashMap<String, Vec<String>> = match serde_json::from_str(&content) {
|
|
|
Ok(data) => data,
|
|
|
- Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)), // Handle JSON parsing errors
|
|
|
+ Err(e) => return Err(anyhow!("Error deserializing local uids")), // Handle JSON parsing errors
|
|
|
};
|
|
|
|
|
|
// Convert Vec<String> to HashSet<String>
|
|
@@ -226,7 +191,7 @@ fn read_local_uids(uids_path: PathBuf) -> io::Result<HashMap<String, HashSet<Str
|
|
|
Ok(result)
|
|
|
}
|
|
|
|
|
|
-fn update_local_uids(uid: String, list: String, uids_path: PathBuf) -> io::Result<()> {
|
|
|
+fn update_local_uids(uid: String, list: String, uids_path: PathBuf) -> anyhow::Result<()> {
|
|
|
let file = std::fs::OpenOptions::new()
|
|
|
.write(true)
|
|
|
.create(true)
|
|
@@ -252,178 +217,144 @@ fn update_local_uids(uid: String, list: String, uids_path: PathBuf) -> io::Resul
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
pub async fn delete_email_by_uid(list: String, uid: u32) -> anyhow::Result<()>{
|
|
|
- let mut client = connect_to_imap_server(Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
- client.login(Config::global().username.clone(), Config::global().password.clone()).await?;
|
|
|
- client.select(list.clone()).await?;
|
|
|
- client.mark_deleted(uid).await?;
|
|
|
- client.expunge().await?;
|
|
|
- client.logout().await?;
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
-pub async fn delete_email_by_uid(list: String, uid: u32) -> anyhow::Result<()>{
|
|
|
- let tls = native_tls::TlsConnector::builder().build().unwrap();
|
|
|
- let client = imap::connect((Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16), Config::global().imap_domain.clone(), &tls).unwrap();
|
|
|
- let mut imap_session = client
|
|
|
- .login(Config::global().username.clone(), Config::global().password.clone())
|
|
|
- .map_err(|e| e.0)?;
|
|
|
+ let mut client = connect_to_imap().await?;
|
|
|
+ let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
|
|
|
+ let mut session = match session_result {
|
|
|
+ Ok(session) => {session}
|
|
|
+ Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
|
|
|
+ };
|
|
|
|
|
|
- imap_session.select(list)?;
|
|
|
- imap_session.uid_store(uid.to_string(), "+FLAGS (\\Deleted)")?;
|
|
|
- imap_session.expunge()?;
|
|
|
- imap_session.logout()?;
|
|
|
-
|
|
|
+ session.select(list.clone()).await?;
|
|
|
+ // session.mark_deleted(uid).await?;
|
|
|
+ let updates_stream = session.store(format!("{}", uid), "+FLAGS (\\Deleted)").await?;
|
|
|
+ let _updates: Vec<_> = updates_stream.try_collect().await?;
|
|
|
+ let _stream = session.expunge().await?;
|
|
|
+ drop(_stream);
|
|
|
+ session.logout().await?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
pub async fn create_folder(name: String) -> anyhow::Result<()>{
|
|
|
- let tls = native_tls::TlsConnector::builder().build().unwrap();
|
|
|
- let client = imap::connect((Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16), Config::global().imap_domain.clone(), &tls).unwrap();
|
|
|
- let mut imap_session = client
|
|
|
- .login(Config::global().username.clone(), Config::global().password.clone())
|
|
|
- .map_err(|e| e.0)?;
|
|
|
-
|
|
|
- imap_session.create(name)?;
|
|
|
- imap_session.logout()?;
|
|
|
-
|
|
|
+ let mut client = connect_to_imap().await?;
|
|
|
+ let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
|
|
|
+ let mut session = match session_result {
|
|
|
+ Ok(session) => {session}
|
|
|
+ Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
|
|
|
+ };
|
|
|
+ session.create(name.clone()).await?;
|
|
|
+ session.logout().await?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
-pub async fn create_folder(name: String) -> anyhow::Result<()>{
|
|
|
- let mut client = connect_to_imap_server(Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
- client.login(Config::global().username.clone(), Config::global().password.clone()).await?;
|
|
|
- client.create_folder(name.clone()).await?;
|
|
|
- client.logout().await?;
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
pub async fn rename_folder(name: String, new_name: String) -> anyhow::Result<()>{
|
|
|
- let tls = native_tls::TlsConnector::builder().build().unwrap();
|
|
|
- let client = imap::connect((Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16), Config::global().imap_domain.clone(), &tls).unwrap();
|
|
|
- let mut imap_session = client
|
|
|
- .login(Config::global().username.clone(), Config::global().password.clone())
|
|
|
- .map_err(|e| e.0)?;
|
|
|
-
|
|
|
- imap_session.rename(name, new_name)?;
|
|
|
- imap_session.logout()?;
|
|
|
-
|
|
|
+ let mut client = connect_to_imap().await?;
|
|
|
+ let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
|
|
|
+ let mut session = match session_result {
|
|
|
+ Ok(session) => {session}
|
|
|
+ Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
|
|
|
+ };
|
|
|
+ session.rename(name.clone(), new_name.clone()).await?;
|
|
|
+ session.logout().await?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
-pub async fn rename_folder(name: String, new_name: String) -> anyhow::Result<()>{
|
|
|
- let mut client = connect_to_imap_server(Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
- client.login(Config::global().username.clone(), Config::global().password.clone()).await?;
|
|
|
- client.rename_folder(name.clone(), new_name.clone()).await?;
|
|
|
- client.logout().await?;
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
pub async fn delete_folder(name: String) -> anyhow::Result<()>{
|
|
|
- let tls = native_tls::TlsConnector::builder().build().unwrap();
|
|
|
- let client = imap::connect((Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16), Config::global().imap_domain.clone(), &tls).unwrap();
|
|
|
- let mut imap_session = client
|
|
|
- .login(Config::global().username.clone(), Config::global().password.clone())
|
|
|
- .map_err(|e| e.0)?;
|
|
|
-
|
|
|
- imap_session.delete(name)?;
|
|
|
- imap_session.logout()?;
|
|
|
-
|
|
|
+ let mut client = connect_to_imap().await?;
|
|
|
+ let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
|
|
|
+ let mut session = match session_result {
|
|
|
+ Ok(session) => {session}
|
|
|
+ Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
|
|
|
+ };
|
|
|
+ session.delete(name.clone()).await?;
|
|
|
+ session.logout().await?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
-pub async fn delete_folder(name: String) -> anyhow::Result<()>{
|
|
|
- let mut client = connect_to_imap_server(Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
- client.login(Config::global().username.clone(), Config::global().password.clone()).await?;
|
|
|
- client.delete_folder(name.clone()).await?;
|
|
|
- client.logout().await?;
|
|
|
- Ok(())
|
|
|
-}
|
|
|
+// #[cfg(target_os = "wasi")]
|
|
|
+// pub async fn check_for_updates(mailbox: String) -> anyhow::Result<()>{
|
|
|
+// let mut client = connect_to_imap_server(Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
+// client.login(Config::global().username.clone(), Config::global().password.clone()).await?;
|
|
|
+//
|
|
|
+// client.select(mailbox).await?;
|
|
|
+// let mut id = client.idle().await?;
|
|
|
+//
|
|
|
+// println!("Start looking for updates");
|
|
|
+// loop {
|
|
|
+// println!("UPDATE loop");
|
|
|
+// client.read_response_by_keyword("EXISTS".to_string()).await?;
|
|
|
+//
|
|
|
+// // TODO do not update all emails (IMAP returns * {number} RECENT) and do it only for one mailbox
|
|
|
+// let new_paths = download_email_from_imap().await.expect("Cannot download new emails");
|
|
|
+//
|
|
|
+// for (uid, path) in new_paths.clone() {
|
|
|
+// match add_email(path.clone(), uid.clone()){
|
|
|
+// Ok(_) => {}
|
|
|
+// Err(_) => {println!("Error adding email from {:?}", path.clone())}
|
|
|
+// };
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// client.idle_done().await;
|
|
|
+// client.logout().await;
|
|
|
+// Ok(())
|
|
|
+// }
|
|
|
+//
|
|
|
|
|
|
-#[cfg(target_os = "wasi")]
|
|
|
-pub async fn check_for_updates(mailbox: String) -> anyhow::Result<()>{
|
|
|
- let mut client = connect_to_imap_server(Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16).await?;
|
|
|
- client.login(Config::global().username.clone(), Config::global().password.clone()).await?;
|
|
|
+pub async fn check_for_updates(mailbox: String) -> anyhow::Result<()> {
|
|
|
+ task::spawn(async move {
|
|
|
+ let mut client = match connect_to_imap().await {
|
|
|
+ Ok(client) => client,
|
|
|
+ Err(e) => {
|
|
|
+ return Err::<(), anyhow::Error>(anyhow!("Failed to connect to IMAP"));
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- client.select(mailbox).await?;
|
|
|
- let mut id = client.idle().await?;
|
|
|
+ let session_result = client
|
|
|
+ .login(Config::global().username.clone(), Config::global().password.clone())
|
|
|
+ .await;
|
|
|
|
|
|
- println!("Start looking for updates");
|
|
|
- loop {
|
|
|
- println!("UPDATE loop");
|
|
|
- client.read_response_by_keyword("EXISTS".to_string()).await?;
|
|
|
+ let mut session = match session_result {
|
|
|
+ Ok(session) => session,
|
|
|
+ Err(_) => return Err(anyhow!("Unable to login to IMAP server")),
|
|
|
+ };
|
|
|
|
|
|
- // TODO do not update all emails (IMAP returns * {number} RECENT) and do it only for one mailbox
|
|
|
- let new_paths = download_email_from_imap(
|
|
|
- Config::global().maildir.clone(),
|
|
|
- Config::global().imap_domain.clone(),
|
|
|
- Config::global().username.clone(),
|
|
|
- Config::global().password.clone()
|
|
|
- ).await.expect("Cannot download new emails");
|
|
|
+ session.select(mailbox.clone()).await?;
|
|
|
|
|
|
- for (uid, path) in new_paths.clone() {
|
|
|
- match add_email(path.clone(), uid.clone()){
|
|
|
- Ok(_) => {}
|
|
|
- Err(_) => {println!("Error adding email from {:?}", path.clone())}
|
|
|
- };
|
|
|
+ loop {
|
|
|
+ let mut idle = session.idle();
|
|
|
+ idle.init().await.unwrap();
|
|
|
+ let (idle_wait, interrupt) = idle.wait();
|
|
|
+ let idle_handle = task::spawn(async move {
|
|
|
+ println!("IDLE: waiting for 30s");
|
|
|
+ sleep(Duration::from_secs(30)).await;
|
|
|
+ println!("IDLE: waited 30 secs, now interrupting idle");
|
|
|
+ drop(interrupt);
|
|
|
+ });
|
|
|
+
|
|
|
+ match idle_wait.await.unwrap() {
|
|
|
+ NewData(data) => {
|
|
|
+ let s = String::from_utf8(data.borrow_owner().to_vec()).unwrap();
|
|
|
+ println!("IDLE data:\n{}", s);
|
|
|
+ // Process the data as needed here
|
|
|
+ }
|
|
|
+ reason => {
|
|
|
+ println!("IDLE failed {:?}", reason);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ensure the idle handle is dropped before the next loop iteration
|
|
|
+ idle_handle.await.unwrap();
|
|
|
+
|
|
|
+ // Reassign session to prevent ownership issues in the next loop iteration
|
|
|
+ session = idle.done().await.unwrap();
|
|
|
}
|
|
|
- }
|
|
|
+ });
|
|
|
|
|
|
- client.idle_done().await;
|
|
|
- client.logout().await;
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
-pub async fn check_for_updates(mailbox: String) -> anyhow::Result<()>{
|
|
|
- let tls = native_tls::TlsConnector::builder().build().unwrap();
|
|
|
- let client = imap::connect((Config::global().imap_domain.clone(), Config::global().imap_port.clone() as u16), Config::global().imap_domain.clone(), &tls).unwrap();
|
|
|
- let mut imap_session = client
|
|
|
- .login(Config::global().username.clone(), Config::global().password.clone())
|
|
|
- .map_err(|e| e.0)?;
|
|
|
-
|
|
|
- imap_session.select(mailbox)?;
|
|
|
-
|
|
|
- println!("Start looking for updates");
|
|
|
- loop {
|
|
|
- let idle = imap_session.idle()?;
|
|
|
- idle.wait()?;
|
|
|
-
|
|
|
- // TODO do not update all emails (IMAP returns * {number} RECENT) and do it only for one mailbox
|
|
|
- let new_paths = download_email_from_imap(
|
|
|
- Config::global().maildir.clone(),
|
|
|
- Config::global().imap_domain.clone(),
|
|
|
- Config::global().username.clone(),
|
|
|
- Config::global().password.clone()
|
|
|
- ).await.expect("Cannot download new emails");
|
|
|
-
|
|
|
- for (uid, path) in new_paths.clone() {
|
|
|
- match add_email(path.clone(), uid.clone()){
|
|
|
- Ok(_) => {}
|
|
|
- Err(_) => {println!("Error adding email from {:?}", path.clone())}
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- imap_session.logout()?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-fn store(
|
|
|
- path: PathBuf,
|
|
|
- uid: String,
|
|
|
- subfolder: String,
|
|
|
- data: &[u8],
|
|
|
- info: &str,
|
|
|
-) -> io::Result<PathBuf> {
|
|
|
+fn store(path: PathBuf, uid: String, subfolder: String, data: &[u8], info: &str) -> io::Result<PathBuf> {
|
|
|
// loop when conflicting filenames occur, as described at
|
|
|
// http://www.courier-mta.org/maildir.html
|
|
|
// this assumes that pid and hostname don't change.
|