|
@@ -13,17 +13,16 @@ use serde::{Deserialize, Serialize};
|
|
use crate::indexes::{Indexes};
|
|
use crate::indexes::{Indexes};
|
|
use config::{Config, INSTANCE};
|
|
use config::{Config, INSTANCE};
|
|
use crate::server::{run_api};
|
|
use crate::server::{run_api};
|
|
-use mailparse::parse_mail;
|
|
|
|
|
|
+use mailparse::{DispositionType, parse_mail, ParsedMail};
|
|
use regex::Regex;
|
|
use regex::Regex;
|
|
use crate::templates::util::parse_email;
|
|
use crate::templates::util::parse_email;
|
|
use crate::util::{compress_and_save_file};
|
|
use crate::util::{compress_and_save_file};
|
|
use crate::imap::{delete_folder, rename_folder, create_folder, check_for_updates};
|
|
use crate::imap::{delete_folder, rename_folder, create_folder, check_for_updates};
|
|
use crate::js::email_scripts;
|
|
use crate::js::email_scripts;
|
|
use warp::Filter;
|
|
use warp::Filter;
|
|
-use crate::sonic::{IngestSonic, SearchSonic};
|
|
|
|
|
|
+use crate::sonic::{IngestSonic};
|
|
|
|
|
|
mod smtp_client;
|
|
mod smtp_client;
|
|
-
|
|
|
|
mod arg;
|
|
mod arg;
|
|
mod config;
|
|
mod config;
|
|
mod models;
|
|
mod models;
|
|
@@ -35,6 +34,7 @@ mod server;
|
|
mod js;
|
|
mod js;
|
|
mod sonic;
|
|
mod sonic;
|
|
|
|
|
|
|
|
+/// appends an extension to a path
|
|
pub fn append_ext(ext: impl AsRef<OsStr>, path: &PathBuf) -> PathBuf {
|
|
pub fn append_ext(ext: impl AsRef<OsStr>, path: &PathBuf) -> PathBuf {
|
|
let mut os_string: OsString = path.into();
|
|
let mut os_string: OsString = path.into();
|
|
os_string.push(".");
|
|
os_string.push(".");
|
|
@@ -42,17 +42,7 @@ pub fn append_ext(ext: impl AsRef<OsStr>, path: &PathBuf) -> PathBuf {
|
|
os_string.into()
|
|
os_string.into()
|
|
}
|
|
}
|
|
|
|
|
|
-fn write_if_changed<T: AsRef<[u8]>>(path: &PathBuf, data: T) -> bool {
|
|
|
|
- if let Ok(d) = std::fs::read(path) {
|
|
|
|
- if d == data.as_ref() {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::fs::write(path, data).unwrap();
|
|
|
|
- true
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
|
|
+/// converts message to HTML string
|
|
pub fn message_to_html(msg: StrMessage) -> String{
|
|
pub fn message_to_html(msg: StrMessage) -> String{
|
|
fn remove_style_tags(html: &str) -> String {
|
|
fn remove_style_tags(html: &str) -> String {
|
|
Regex::new(r"(?s)<style.*?>.*?</style>").unwrap()
|
|
Regex::new(r"(?s)<style.*?>.*?</style>").unwrap()
|
|
@@ -101,6 +91,10 @@ pub fn message_to_html(msg: StrMessage) -> String{
|
|
let mut body_content = remove_style_tags(&extract_body(&html));
|
|
let mut body_content = remove_style_tags(&extract_body(&html));
|
|
// body_content = purple_numbers(&*body_content, "#");
|
|
// body_content = purple_numbers(&*body_content, "#");
|
|
|
|
|
|
|
|
+ // retrieving attachments
|
|
|
|
+ retrieve_and_save_attachments(parsed_mail, msg.list.clone(), msg.clone().hash)
|
|
|
|
+ .expect("Unable to retrieve attachment");
|
|
|
|
+
|
|
format!(
|
|
format!(
|
|
r#"
|
|
r#"
|
|
<!DOCTYPE html>
|
|
<!DOCTYPE html>
|
|
@@ -129,9 +123,37 @@ pub fn message_to_html(msg: StrMessage) -> String{
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+fn retrieve_and_save_attachments(parsed_mail: ParsedMail, list: String, file_name: String) -> anyhow::Result<()>{
|
|
|
|
+ // iterating through each part of email
|
|
|
|
+ for part in parsed_mail.subparts {
|
|
|
|
+ let disposition = part.get_content_disposition();
|
|
|
|
+ // if this is an attachment
|
|
|
|
+ if disposition.disposition == DispositionType::Attachment {
|
|
|
|
+ // creating directory for it
|
|
|
|
+ let full_dir = Config::global().out_dir.clone()
|
|
|
|
+ .join(list.clone())
|
|
|
|
+ .join(".attachments")
|
|
|
|
+ .join(file_name.clone());
|
|
|
|
+ create_dir_all(full_dir.clone()).expect("Unable to create directory for attachments");
|
|
|
|
+
|
|
|
|
+ let mut filename = disposition.params.get("filename")
|
|
|
|
+ .map(|s| s.to_string())
|
|
|
|
+ .unwrap_or_else(|| "unnamed_file".to_string());
|
|
|
|
+
|
|
|
|
+ // creating full absolute path to the file
|
|
|
|
+ let full_path = full_dir.join(filename.clone());
|
|
|
|
+ let mut output = File::create(full_path.clone())?;
|
|
|
|
+
|
|
|
|
+ // writing the data to the file
|
|
|
|
+ output.write_all(&part.get_body_raw().unwrap())?;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Ok(())
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/// saves email to local file system
|
|
fn persist_email(msg: &Message, uid: u32, list: String, original_path: PathBuf) -> anyhow::Result<()>{
|
|
fn persist_email(msg: &Message, uid: u32, list: String, original_path: PathBuf) -> anyhow::Result<()>{
|
|
- let mut indexes = Indexes::new();
|
|
|
|
-
|
|
|
|
// creating all folders
|
|
// creating all folders
|
|
if Config::global().out_dir.exists() {
|
|
if Config::global().out_dir.exists() {
|
|
std::fs::create_dir_all(&Config::global().out_dir).ok();
|
|
std::fs::create_dir_all(&Config::global().out_dir).ok();
|
|
@@ -162,18 +184,11 @@ fn persist_email(msg: &Message, uid: u32, list: String, original_path: PathBuf)
|
|
Err(_) => {println!("Error while compressing or saving {}", message.hash.clone())}
|
|
Err(_) => {println!("Error while compressing or saving {}", message.hash.clone())}
|
|
};
|
|
};
|
|
}
|
|
}
|
|
-
|
|
|
|
- // // xml
|
|
|
|
- // let xml = append_ext("xml", &message_xml_dir.join(message.hash.clone()));
|
|
|
|
- // let mut file = File::create(xml)?;
|
|
|
|
- // let data = message.to_xml();
|
|
|
|
- // file.write_all(data.as_bytes())?;
|
|
|
|
- // file.flush()?;
|
|
|
|
|
|
|
|
- // sonic
|
|
|
|
|
|
+ // ingest text from the message to the sonic server // TODO create as queue if server is not accessible
|
|
let mut ingest_channel = IngestSonic::new()?;
|
|
let mut ingest_channel = IngestSonic::new()?;
|
|
let data = message.to_ingest();
|
|
let data = message.to_ingest();
|
|
- match ingest_channel.ingest_document("emails", &*list, &*message.hash.clone(), &*data) { // message.hash to id
|
|
|
|
|
|
+ match ingest_channel.ingest_document("emails", &*list, &*message.hash.clone(), &*data) { // TODO change message.hash to id
|
|
Ok(_) => {}
|
|
Ok(_) => {}
|
|
Err(_) => {println!("Unable to ingest {} email to sonic search", message.hash.clone())}
|
|
Err(_) => {println!("Unable to ingest {} email to sonic search", message.hash.clone())}
|
|
};
|
|
};
|
|
@@ -186,6 +201,7 @@ fn persist_email(msg: &Message, uid: u32, list: String, original_path: PathBuf)
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// read raw email and saves HTML version
|
|
pub fn add_email(path: PathBuf, uid: u32) -> anyhow::Result<()>{
|
|
pub fn add_email(path: PathBuf, uid: u32) -> anyhow::Result<()>{
|
|
let maildir = match path.parent() {
|
|
let maildir = match path.parent() {
|
|
None => {return Err(anyhow!("Cannot retrieve parent folder from the path"))}
|
|
None => {return Err(anyhow!("Cannot retrieve parent folder from the path"))}
|
|
@@ -210,6 +226,7 @@ pub fn add_email(path: PathBuf, uid: u32) -> anyhow::Result<()>{
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// delete a single message both locally and remotely
|
|
async fn delete_email(message_id: String) -> anyhow::Result<()>{
|
|
async fn delete_email(message_id: String) -> anyhow::Result<()>{
|
|
let message = Indexes::delete_from_messages(message_id);
|
|
let message = Indexes::delete_from_messages(message_id);
|
|
match message {
|
|
match message {
|
|
@@ -241,7 +258,7 @@ async fn delete_email(message_id: String) -> anyhow::Result<()>{
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
-// create folder both locally and remotely
|
|
|
|
|
|
+/// create folder both locally and remotely
|
|
pub async fn create_folder_lar(name: String) -> anyhow::Result<()>{
|
|
pub async fn create_folder_lar(name: String) -> anyhow::Result<()>{
|
|
match create_folder(name.clone()).await {
|
|
match create_folder(name.clone()).await {
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
@@ -258,7 +275,7 @@ pub async fn create_folder_lar(name: String) -> anyhow::Result<()>{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-// delete folder both locally and remotely
|
|
|
|
|
|
+/// delete folder both locally and remotely
|
|
pub async fn delete_folder_lar(name: String) -> anyhow::Result<()>{
|
|
pub async fn delete_folder_lar(name: String) -> anyhow::Result<()>{
|
|
match delete_folder(name.clone()).await {
|
|
match delete_folder(name.clone()).await {
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
@@ -275,7 +292,7 @@ pub async fn delete_folder_lar(name: String) -> anyhow::Result<()>{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-// rename folder both locally and remotely
|
|
|
|
|
|
+/// rename folder both locally and remotely
|
|
pub async fn rename_folder_lar(old_name: String, new_name: String) -> anyhow::Result<()>{
|
|
pub async fn rename_folder_lar(old_name: String, new_name: String) -> anyhow::Result<()>{
|
|
match rename_folder(old_name.clone(), new_name.clone()).await {
|
|
match rename_folder(old_name.clone(), new_name.clone()).await {
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
@@ -293,6 +310,7 @@ pub async fn rename_folder_lar(old_name: String, new_name: String) -> anyhow::Re
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Parse args from CLI
|
|
fn parse_args(){
|
|
fn parse_args(){
|
|
let args = arg::Args::from_env();
|
|
let args = arg::Args::from_env();
|
|
|
|
|
|
@@ -306,11 +324,11 @@ fn parse_args(){
|
|
}
|
|
}
|
|
|
|
|
|
async fn run(){
|
|
async fn run(){
|
|
|
|
+ // parse args from CLI
|
|
parse_args();
|
|
parse_args();
|
|
|
|
|
|
// downloading new emails
|
|
// downloading new emails
|
|
let new_paths = imap::download_email_from_imap().await.expect("Cannot download new emails");
|
|
let new_paths = imap::download_email_from_imap().await.expect("Cannot download new emails");
|
|
-
|
|
|
|
|
|
|
|
let mut lists: Vec<String> = Vec::new();
|
|
let mut lists: Vec<String> = Vec::new();
|
|
|
|
|
|
@@ -347,8 +365,17 @@ async fn run(){
|
|
Indexes::persist_threads().expect("Unable to persist threads");
|
|
Indexes::persist_threads().expect("Unable to persist threads");
|
|
Indexes::persist_indexes(lists).expect("Unable to persist indexes");
|
|
Indexes::persist_indexes(lists).expect("Unable to persist indexes");
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // monitor updates in INBOX
|
|
|
|
+ if let Err(e) = check_for_updates("INBOX".to_string()).await {
|
|
|
|
+ eprintln!("Failed to monitor mailbox: {:?}", e);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // API
|
|
|
|
+ run_api().await;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Entry point for a wasi env
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
#[tokio::main(flavor = "current_thread")]
|
|
#[tokio::main(flavor = "current_thread")]
|
|
async fn main() -> anyhow::Result<()> {
|
|
async fn main() -> anyhow::Result<()> {
|
|
@@ -377,31 +404,13 @@ async fn main() -> anyhow::Result<()> {
|
|
|
|
|
|
// delete_email("Sent".to_string(), 4).await;
|
|
// delete_email("Sent".to_string(), 4).await;
|
|
|
|
|
|
- // imap::create_folder("testFolder".to_string()).await.unwrap();
|
|
|
|
- // imap::rename_folder("testFolder".to_string(), "newTestFolder".to_string()).await.unwrap();
|
|
|
|
- // imap::delete_folder("newTestFolder".to_string()).await.unwrap();
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // looking for updates
|
|
|
|
- if let Err(e) = check_for_updates("INBOX".to_string()).await {
|
|
|
|
- eprintln!("Failed to monitor mailbox: {:?}", e);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let duration = start.elapsed();
|
|
|
|
- println!("Duration {:?}", duration);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // API
|
|
|
|
- run_api().await;
|
|
|
|
-
|
|
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Entry point for a not wasi env
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[tokio::main]
|
|
#[tokio::main]
|
|
async fn main() {
|
|
async fn main() {
|
|
- let start = Instant::now();
|
|
|
|
-
|
|
|
|
run().await;
|
|
run().await;
|
|
|
|
|
|
// let email = LettreMessage::builder()
|
|
// let email = LettreMessage::builder()
|
|
@@ -424,15 +433,4 @@ 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");
|
|
-
|
|
|
|
-
|
|
|
|
- if let Err(e) = check_for_updates("INBOX".to_string()).await {
|
|
|
|
- eprintln!("Failed to monitor mailbox: {:?}", e);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let duration = start.elapsed();
|
|
|
|
- println!("Duration {:?}", duration);
|
|
|
|
-
|
|
|
|
- // API
|
|
|
|
- run_api().await;
|
|
|
|
}
|
|
}
|