|
@@ -6,6 +6,7 @@ use models::*;
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::{fs, thread};
|
|
use std::{fs, thread};
|
|
use std::fs::{create_dir_all, File, OpenOptions};
|
|
use std::fs::{create_dir_all, File, OpenOptions};
|
|
|
|
+use std::future::Future;
|
|
use std::io::{BufReader, Write};
|
|
use std::io::{BufReader, Write};
|
|
use std::net::SocketAddr;
|
|
use std::net::SocketAddr;
|
|
use std::time::Duration;
|
|
use std::time::Duration;
|
|
@@ -18,6 +19,8 @@ use mailparse::parse_mail;
|
|
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, read_and_decompress_file};
|
|
use crate::util::{compress_and_save_file, read_and_decompress_file};
|
|
|
|
+use crate::imap::{delete_folder, rename_folder, create_folder};
|
|
|
|
+use crate::js::email_scripts;
|
|
|
|
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
use axum::extract::Query;
|
|
use axum::extract::Query;
|
|
@@ -29,11 +32,9 @@ use axum::http::{Method, StatusCode};
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::response::{IntoResponse, Response};
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
use axum::routing::{get, post};
|
|
use axum::routing::{get, post};
|
|
|
|
+use axum::routing::any;
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
use tower_http::cors::{Any, CorsLayer};
|
|
use tower_http::cors::{Any, CorsLayer};
|
|
-#[cfg(not(target_os = "wasi"))]
|
|
|
|
-use imap::create_folder;
|
|
|
|
-
|
|
|
|
|
|
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
use bytecodec::DecodeExt;
|
|
use bytecodec::DecodeExt;
|
|
@@ -44,7 +45,6 @@ use lettre::message::{Mailbox, Message as LettreMessage, MultiPart, SinglePart};
|
|
use tokio::runtime::Runtime;
|
|
use tokio::runtime::Runtime;
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
use wasmedge_wasi_socket::{Shutdown, TcpListener, TcpStream};
|
|
use wasmedge_wasi_socket::{Shutdown, TcpListener, TcpStream};
|
|
-use crate::imap::{delete_folder, rename_folder};
|
|
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
use crate::server_wasi::{handle_client};
|
|
use crate::server_wasi::{handle_client};
|
|
#[cfg(target_os = "wasi")]
|
|
#[cfg(target_os = "wasi")]
|
|
@@ -61,6 +61,7 @@ mod indexes;
|
|
mod imap;
|
|
mod imap;
|
|
mod server;
|
|
mod server;
|
|
mod server_wasi;
|
|
mod server_wasi;
|
|
|
|
+mod js;
|
|
|
|
|
|
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();
|
|
@@ -136,7 +137,8 @@ pub fn message_to_html(msg: StrMessage) -> String{
|
|
<title>Email</title>
|
|
<title>Email</title>
|
|
<meta http-equiv='Permissions-Policy' content='interest-cohort=()'/>
|
|
<meta http-equiv='Permissions-Policy' content='interest-cohort=()'/>
|
|
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0' />
|
|
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0' />
|
|
- </head>
|
|
|
|
|
|
+ </head>
|
|
|
|
+ {js}
|
|
<body>
|
|
<body>
|
|
<div id="email-body" class="email-body" style="position: relative;">
|
|
<div id="email-body" class="email-body" style="position: relative;">
|
|
<style>
|
|
<style>
|
|
@@ -149,6 +151,7 @@ pub fn message_to_html(msg: StrMessage) -> String{
|
|
</body>
|
|
</body>
|
|
</html>
|
|
</html>
|
|
"#,
|
|
"#,
|
|
|
|
+ js = email_scripts(),
|
|
styles = styles,
|
|
styles = styles,
|
|
body_content = body_content,
|
|
body_content = body_content,
|
|
)
|
|
)
|
|
@@ -225,6 +228,89 @@ pub fn add_email(path: PathBuf, uid: u32) -> anyhow::Result<()>{
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+async fn delete_email(message_id: String) -> anyhow::Result<()>{
|
|
|
|
+ let message = Indexes::delete_from_messages(message_id);
|
|
|
|
+ match message {
|
|
|
|
+ Ok(message) => {
|
|
|
|
+ let _ = Indexes::delete_from_threads(message.clone());
|
|
|
|
+ let lists: Vec<String> = fs::read_dir(Config::global().out_dir.clone())?
|
|
|
|
+ .filter_map(Result::ok)
|
|
|
|
+ .filter(|entry| entry.metadata().map(|m| m.is_dir()).unwrap_or(false))
|
|
|
|
+ .filter_map(|entry| entry.file_name().into_string().ok())
|
|
|
|
+ .collect();
|
|
|
|
+ let _ = Indexes::persist_indexes(lists);
|
|
|
|
+
|
|
|
|
+ let message_html_name = Config::global().out_dir.clone()
|
|
|
|
+ .join(message.list.clone())
|
|
|
|
+ .join("messages")
|
|
|
|
+ .join(message.hash.clone() + ".html");
|
|
|
|
+ let message_xml_name = Config::global().out_dir.clone()
|
|
|
|
+ .join(message.list.clone())
|
|
|
|
+ .join("messages_xml")
|
|
|
|
+ .join(message.hash.clone() + ".xml");
|
|
|
|
+ let _ = fs::remove_file(message_html_name);
|
|
|
|
+ let _ = fs::remove_file(message_xml_name);
|
|
|
|
+
|
|
|
|
+ let _ = imap::delete_email_by_uid(message.list.clone(), message.uid.clone()).await;
|
|
|
|
+ }
|
|
|
|
+ Err(_) => {}
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Ok(())
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// create folder both locally and remotely
|
|
|
|
+pub async fn create_folder_lar(name: String) -> anyhow::Result<()>{
|
|
|
|
+ match create_folder(name.clone()).await {
|
|
|
|
+ Ok(_) => {
|
|
|
|
+ let folder_path = Config::global().out_dir.clone().join(name.clone());
|
|
|
|
+ if !folder_path.exists() {
|
|
|
|
+ return match fs::create_dir_all(folder_path) {
|
|
|
|
+ Ok(_) => Indexes::persist_indexes(vec![name.clone()]),
|
|
|
|
+ Err(_) => Err(anyhow!("Cannot create folder locally")),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Ok(())
|
|
|
|
+ }
|
|
|
|
+ Err(_) => Err(anyhow!("Cannot create folder remotely"))
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// delete folder both locally and remotely
|
|
|
|
+pub async fn delete_folder_lar(name: String) -> anyhow::Result<()>{
|
|
|
|
+ match delete_folder(name.clone()).await {
|
|
|
|
+ Ok(_) => {
|
|
|
|
+ let folder_path = Config::global().out_dir.clone().join(name);
|
|
|
|
+ if folder_path.exists() {
|
|
|
|
+ return match fs::remove_dir_all(folder_path) {
|
|
|
|
+ Ok(_) => Ok(()),
|
|
|
|
+ Err(_) => Err(anyhow!("Cannot delete folder locally")),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Ok(())
|
|
|
|
+ }
|
|
|
|
+ Err(_) => Err(anyhow!("Cannot delete folder remotely"))
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// rename folder both locally and remotely
|
|
|
|
+pub async fn rename_folder_lar(old_name: String, new_name: String) -> anyhow::Result<()>{
|
|
|
|
+ match rename_folder(old_name.clone(), new_name.clone()).await {
|
|
|
|
+ Ok(_) => {
|
|
|
|
+ let old_path = Config::global().out_dir.clone().join(old_name);
|
|
|
|
+ let new_path = Config::global().out_dir.clone().join(new_name);
|
|
|
|
+ if old_path.exists() {
|
|
|
|
+ return match fs::rename(old_path, new_path) {
|
|
|
|
+ Ok(_) => Ok(()),
|
|
|
|
+ Err(_) => Err(anyhow!("Cannot rename folder locally"))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Ok(())
|
|
|
|
+ }
|
|
|
|
+ Err(_) => Err(anyhow!("Cannot rename folder remotely"))
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
fn parse_args(){
|
|
fn parse_args(){
|
|
let args = arg::Args::from_env();
|
|
let args = arg::Args::from_env();
|
|
|
|
|
|
@@ -286,37 +372,6 @@ async fn run(){
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-async fn delete_email(message_id: String) -> anyhow::Result<()>{
|
|
|
|
- let message = Indexes::delete_from_messages(message_id);
|
|
|
|
- match message {
|
|
|
|
- Ok(message) => {
|
|
|
|
- let _ = Indexes::delete_from_threads(message.clone());
|
|
|
|
- let lists: Vec<String> = fs::read_dir(Config::global().out_dir.clone())?
|
|
|
|
- .filter_map(Result::ok)
|
|
|
|
- .filter(|entry| entry.metadata().map(|m| m.is_dir()).unwrap_or(false))
|
|
|
|
- .filter_map(|entry| entry.file_name().into_string().ok())
|
|
|
|
- .collect();
|
|
|
|
- let _ = Indexes::persist_indexes(lists);
|
|
|
|
-
|
|
|
|
- let message_html_name = Config::global().out_dir.clone()
|
|
|
|
- .join(message.list.clone())
|
|
|
|
- .join("messages")
|
|
|
|
- .join(message.hash.clone() + ".html");
|
|
|
|
- let message_xml_name = Config::global().out_dir.clone()
|
|
|
|
- .join(message.list.clone())
|
|
|
|
- .join("messages_xml")
|
|
|
|
- .join(message.hash.clone() + ".xml");
|
|
|
|
- let _ = fs::remove_file(message_html_name);
|
|
|
|
- let _ = fs::remove_file(message_xml_name);
|
|
|
|
-
|
|
|
|
- let _ = imap::delete_email_by_uid(message.list.clone(), message.uid.clone()).await;
|
|
|
|
- }
|
|
|
|
- Err(_) => {}
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Ok(())
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
#[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<()> {
|
|
@@ -489,7 +544,7 @@ struct CreateFolderRequest {
|
|
}
|
|
}
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
async fn create_folder_handle(Json(data): Json<CreateFolderRequest>) -> impl IntoResponse {
|
|
async fn create_folder_handle(Json(data): Json<CreateFolderRequest>) -> impl IntoResponse {
|
|
- match create_folder(data.name.clone()).await {
|
|
|
|
|
|
+ match create_folder_lar(data.name.clone()).await {
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
(StatusCode::OK, "OK").into_response()
|
|
(StatusCode::OK, "OK").into_response()
|
|
}
|
|
}
|
|
@@ -509,7 +564,7 @@ struct DeleteFolderRequest {
|
|
}
|
|
}
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
async fn delete_folder_handle(Json(data): Json<DeleteFolderRequest>) -> impl IntoResponse {
|
|
async fn delete_folder_handle(Json(data): Json<DeleteFolderRequest>) -> impl IntoResponse {
|
|
- match delete_folder(data.name.clone()).await {
|
|
|
|
|
|
+ match delete_folder_lar(data.name.clone()).await {
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
(StatusCode::OK, "OK").into_response()
|
|
(StatusCode::OK, "OK").into_response()
|
|
}
|
|
}
|
|
@@ -530,7 +585,7 @@ struct RenameFolderRequest {
|
|
}
|
|
}
|
|
#[cfg(not(target_os = "wasi"))]
|
|
#[cfg(not(target_os = "wasi"))]
|
|
async fn rename_folder_handle(Json(data): Json<RenameFolderRequest>) -> impl IntoResponse {
|
|
async fn rename_folder_handle(Json(data): Json<RenameFolderRequest>) -> impl IntoResponse {
|
|
- match rename_folder(data.old_name.clone(), data.new_name.clone()).await {
|
|
|
|
|
|
+ match rename_folder_lar(data.old_name.clone(), data.new_name.clone()).await {
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
(StatusCode::OK, "OK").into_response()
|
|
(StatusCode::OK, "OK").into_response()
|
|
}
|
|
}
|