|
@@ -1,13 +1,14 @@
|
|
use mail_parser::{Message};
|
|
use mail_parser::{Message};
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::error::Error;
|
|
use std::error::Error;
|
|
-use std::path::{PathBuf};
|
|
|
|
|
|
+use std::path::{Path, PathBuf};
|
|
use models::*;
|
|
use models::*;
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::ffi::{OsStr, OsString};
|
|
-use std::fs;
|
|
|
|
|
|
+use std::{fs, thread};
|
|
use std::fs::{create_dir_all, File, OpenOptions};
|
|
use std::fs::{create_dir_all, File, OpenOptions};
|
|
use std::io::{BufReader, Write};
|
|
use std::io::{BufReader, Write};
|
|
use std::net::SocketAddr;
|
|
use std::net::SocketAddr;
|
|
|
|
+use std::time::Duration;
|
|
use anyhow::anyhow;
|
|
use anyhow::anyhow;
|
|
use serde::Deserialize;
|
|
use serde::Deserialize;
|
|
use crate::indexes::{Indexes, SerializableMessage, SerializableThread};
|
|
use crate::indexes::{Indexes, SerializableMessage, SerializableThread};
|
|
@@ -36,6 +37,8 @@ use bytecodec::DecodeExt;
|
|
use httpcodec::{HttpVersion, ReasonPhrase, Request, RequestDecoder, Response, StatusCode, Header};
|
|
use httpcodec::{HttpVersion, ReasonPhrase, Request, RequestDecoder, Response, StatusCode, Header};
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
use lettre::message::{Mailbox, Message as LettreMessage, MultiPart, SinglePart};
|
|
use lettre::message::{Mailbox, Message as LettreMessage, MultiPart, SinglePart};
|
|
|
|
+use tokio::runtime::Runtime;
|
|
|
|
+use tokio::time::sleep;
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
use wasmedge_wasi_socket::{Shutdown, TcpListener, TcpStream};
|
|
use wasmedge_wasi_socket::{Shutdown, TcpListener, TcpStream};
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
@@ -194,6 +197,30 @@ fn persist_email(msg: &Message, uid: u32, list: String, original_path: PathBuf)
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+pub fn add_email(path: PathBuf, uid: u32) -> anyhow::Result<()>{
|
|
|
|
+ let maildir = match path.parent() {
|
|
|
|
+ None => {return Err(anyhow!("Cannot retrieve parent folder from the path"))}
|
|
|
|
+ Some(parent) => {
|
|
|
|
+ match parent.parent() {
|
|
|
|
+ None => {return Err(anyhow!("Cannot retrieve parent folder from the path"))}
|
|
|
|
+ Some(parent) => {parent.file_name().unwrap().to_owned().into_string().unwrap()}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ let maildir_out_path = Config::global().out_dir.clone().join(maildir.clone());
|
|
|
|
+ if !maildir_out_path.exists() { create_dir_all(maildir_out_path).ok(); }
|
|
|
|
+
|
|
|
|
+ let data = std::fs::read(&path).expect("could not read mail");
|
|
|
|
+
|
|
|
|
+ if let Some(mail) = Message::parse(&data) {
|
|
|
|
+ let _ = persist_email(&mail, uid.clone(), maildir.clone(), path.clone());
|
|
|
|
+ } else {
|
|
|
|
+ println!("Could not parse message {:?}", path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Ok(())
|
|
|
|
+}
|
|
|
|
+
|
|
fn parse_args(){
|
|
fn parse_args(){
|
|
let args = arg::Args::from_env();
|
|
let args = arg::Args::from_env();
|
|
|
|
|
|
@@ -211,7 +238,7 @@ async fn run(){
|
|
|
|
|
|
// downloading new emails
|
|
// downloading new emails
|
|
let new_paths = imap::download_email_from_imap(
|
|
let new_paths = imap::download_email_from_imap(
|
|
- Config::global().maildir.clone(),
|
|
|
|
|
|
+ Config::global().maildir.clone(), // tODO remove all args
|
|
Config::global().imap_domain.clone(),
|
|
Config::global().imap_domain.clone(),
|
|
Config::global().username.clone(),
|
|
Config::global().username.clone(),
|
|
Config::global().password.clone()
|
|
Config::global().password.clone()
|
|
@@ -219,8 +246,7 @@ async fn run(){
|
|
|
|
|
|
|
|
|
|
let mut lists: Vec<String> = Vec::new();
|
|
let mut lists: Vec<String> = Vec::new();
|
|
-
|
|
|
|
- // reading the folder with lists
|
|
|
|
|
|
+
|
|
for mail_list in std::fs::read_dir(Config::global().maildir.clone())
|
|
for mail_list in std::fs::read_dir(Config::global().maildir.clone())
|
|
.expect("could not read maildir")
|
|
.expect("could not read maildir")
|
|
.filter_map(|m| m.ok())
|
|
.filter_map(|m| m.ok())
|
|
@@ -228,28 +254,25 @@ async fn run(){
|
|
let dir_name = mail_list.file_name().into_string().unwrap();
|
|
let dir_name = mail_list.file_name().into_string().unwrap();
|
|
|
|
|
|
if dir_name.starts_with('.') || ["cur", "new", "tmp"].contains(&dir_name.as_str()) {
|
|
if dir_name.starts_with('.') || ["cur", "new", "tmp"].contains(&dir_name.as_str()) {
|
|
- continue;
|
|
|
|
|
|
+ continue;
|
|
}
|
|
}
|
|
|
|
+
|
|
lists.push(dir_name.clone());
|
|
lists.push(dir_name.clone());
|
|
|
|
+ let out_dir_name = Config::global().out_dir.clone().join(dir_name);
|
|
|
|
+ if !out_dir_name.exists() { create_dir_all(out_dir_name).ok(); }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // let mut uid = 1; // fake uid for tests
|
|
|
|
+ // for entry in std::fs::read_dir(Config::global().maildir.clone().join("Sent").join("new")).expect("could not read maildir").filter_map(|m| m.ok()){
|
|
|
|
+ for (uid, path) in new_paths.clone() {
|
|
|
|
+ // let path = entry.path();
|
|
|
|
+
|
|
|
|
+ match add_email(path.clone(), uid.clone()){
|
|
|
|
+ Ok(_) => {}
|
|
|
|
+ Err(_) => {println!("Error adding email from {:?}", path.clone())}
|
|
|
|
+ };
|
|
|
|
|
|
- if !Config::global().out_dir.clone().join(dir_name.clone()).exists(){
|
|
|
|
- std::fs::create_dir_all(Config::global().out_dir.clone().join(dir_name.clone())).ok();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // let mut uid = 1; // fake uid for tests
|
|
|
|
- // for entry in std::fs::read_dir(Config::global().maildir.clone().join(mail_list.path().clone()).join("new")).expect("could not read maildir").filter_map(|m| m.ok()){
|
|
|
|
- for (uid, path) in new_paths.clone().into_iter().filter(|(u16, path)| path.starts_with(mail_list.path())) {
|
|
|
|
- // let path = entry.path();
|
|
|
|
-
|
|
|
|
- let data = std::fs::read(&path).expect("could not read mail");
|
|
|
|
-
|
|
|
|
- if let Some(mail) = Message::parse(&data) {
|
|
|
|
- let _ = persist_email(&mail, uid.clone(), dir_name.clone(), path.clone());
|
|
|
|
- } else {
|
|
|
|
- println!("Could not parse message {:?}", path);
|
|
|
|
- }
|
|
|
|
- // uid += 1;
|
|
|
|
- }
|
|
|
|
|
|
+ // uid += 1;
|
|
}
|
|
}
|
|
|
|
|
|
let threads_indexes_path = Config::global().out_dir.clone().join("threads.json");
|
|
let threads_indexes_path = Config::global().out_dir.clone().join("threads.json");
|
|
@@ -322,14 +345,20 @@ async fn main() -> anyhow::Result<()> {
|
|
// imap::delete_folder("newTestFolder".to_string()).await.unwrap();
|
|
// imap::delete_folder("newTestFolder".to_string()).await.unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
+ // looking for updates
|
|
|
|
+ // let one = tokio::spawn(async {
|
|
|
|
+ // let imap_update_handler = imap::check_for_updates("INBOX".to_string()).await;
|
|
|
|
+ // });
|
|
|
|
+
|
|
// API
|
|
// API
|
|
|
|
+
|
|
println!("Server is running on {}", Config::global().api_port);
|
|
println!("Server is running on {}", Config::global().api_port);
|
|
let listener = TcpListener::bind(format!("{}:{}", Config::global().api_addr, Config::global().api_port), false)?;
|
|
let listener = TcpListener::bind(format!("{}:{}", Config::global().api_addr, Config::global().api_port), false)?;
|
|
loop {
|
|
loop {
|
|
let (stream, _) = listener.accept(false)?;
|
|
let (stream, _) = listener.accept(false)?;
|
|
let _ = handle_client(stream).await;
|
|
let _ = handle_client(stream).await;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
@@ -360,6 +389,12 @@ async fn main() {
|
|
// delete_email("lqo7m8r2.1js7w080jvr2o@express.medallia.com".to_string()).await.expect("Unable to delete an email");
|
|
// delete_email("lqo7m8r2.1js7w080jvr2o@express.medallia.com".to_string()).await.expect("Unable to delete an email");
|
|
|
|
|
|
|
|
|
|
|
|
+ // looking for updates
|
|
|
|
+ let imap_update_handle = tokio::spawn(async move {
|
|
|
|
+ imap::check_for_updates("INBOX".to_string()).await.expect("TODO: panic message");
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+
|
|
// imap::create_folder("testFolder".to_string()).await.unwrap();
|
|
// imap::create_folder("testFolder".to_string()).await.unwrap();
|
|
// imap::rename_folder("testFolder".to_string(), "newTestFolder".to_string()).await.unwrap();
|
|
// imap::rename_folder("testFolder".to_string(), "newTestFolder".to_string()).await.unwrap();
|
|
// imap::delete_folder("newTestFolder".to_string()).await.unwrap();
|
|
// imap::delete_folder("newTestFolder".to_string()).await.unwrap();
|
|
@@ -367,21 +402,31 @@ async fn main() {
|
|
|
|
|
|
// API
|
|
// API
|
|
// Define the CORS layer
|
|
// Define the CORS layer
|
|
- let cors = CorsLayer::new()
|
|
|
|
- .allow_origin(Any)
|
|
|
|
- .allow_methods(vec![Method::GET, Method::POST])
|
|
|
|
- .allow_headers(Any);
|
|
|
|
-
|
|
|
|
- let app = Router::new()
|
|
|
|
- .route("/folders", get(get_folders_handle))
|
|
|
|
- .route("/sorted_threads_by_date", get(sorted_threads_by_date_handle))
|
|
|
|
- .route("/get_thread_messages", get(get_thread_messages_handle))
|
|
|
|
- .route("/email", get(get_email_handle))
|
|
|
|
- .layer(cors);
|
|
|
|
-
|
|
|
|
- let listener = tokio::net::TcpListener::bind(format!("{}:{}", Config::global().api_addr, Config::global().api_port)).await.unwrap();
|
|
|
|
- println!("Server is running on {}", Config::global().api_port);
|
|
|
|
- axum::serve(listener, app).await.unwrap();
|
|
|
|
|
|
+ let handle = thread::spawn(|| {
|
|
|
|
+ let cors = CorsLayer::new()
|
|
|
|
+ .allow_origin(Any)
|
|
|
|
+ .allow_methods(vec![Method::GET, Method::POST])
|
|
|
|
+ .allow_headers(Any);
|
|
|
|
+
|
|
|
|
+ let app = Router::new()
|
|
|
|
+ .route("/folders", get(get_folders_handle))
|
|
|
|
+ .route("/sorted_threads_by_date", get(sorted_threads_by_date_handle))
|
|
|
|
+ .route("/get_thread_messages", get(get_thread_messages_handle))
|
|
|
|
+ .route("/email", get(get_email_handle))
|
|
|
|
+ .layer(cors);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ let rt = Runtime::new().unwrap();
|
|
|
|
+ // Use the runtime to block on the async task
|
|
|
|
+ rt.block_on(async {
|
|
|
|
+ let listener = tokio::net::TcpListener::bind(format!("{}:{}", Config::global().api_addr, Config::global().api_port)).await.unwrap();
|
|
|
|
+ println!("Server is running on {}", Config::global().api_port);
|
|
|
|
+ axum::serve(listener, app).await.unwrap();
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ handle.join().unwrap();
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|