Jelajahi Sumber

changed a way how attachments are stored,
added two endpoints for retrieving attachments

Yurii Sokolovskyi 1 Minggu lalu
induk
melakukan
ce5fa33c1d
2 mengubah file dengan 105 tambahan dan 7 penghapusan
  1. 3 2
      src/main.rs
  2. 102 5
      src/server.rs

+ 3 - 2
src/main.rs

@@ -92,7 +92,7 @@ pub fn message_to_html(msg: StrMessage) -> String{
     // body_content = purple_numbers(&*body_content, "#");
 
     // retrieving attachments
-    retrieve_and_save_attachments(parsed_mail, msg.list.clone(), msg.clone().hash)
+    retrieve_and_save_attachments(parsed_mail, msg.list.clone(), msg.clone().id)
         .expect("Unable to retrieve attachment");
     
     format!(
@@ -184,6 +184,7 @@ fn persist_email(msg: &Message, uid: u32, list: String, original_path: PathBuf)
     }
     
     // ingest text from the message to the sonic server // TODO create as queue if server is not accessible
+    // TODO add attachments
     match IngestSonic::new(){
         Ok(mut ingest_channel) => {
             let data = message.to_ingest();
@@ -350,7 +351,7 @@ async fn run(){
     }
     
     // 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 entry in std::fs::read_dir(Config::global().maildir.clone().join("INBOX").join("new")).expect("could not read maildir").filter_map(|m| m.ok()){
     for (uid, path) in new_paths.clone() {
         // let path = entry.path();
 

+ 102 - 5
src/server.rs

@@ -1,12 +1,14 @@
+use std::fs;
 use crate::config::Config;
 use crate::indexes::{Indexes, SerializableMessage, SerializableThread};
 use serde::{Deserialize, Serialize};
-use std::fs::File;
-use std::io::{BufReader};
+use std::fs::{File, metadata};
+use std::io::{BufReader, Read};
 use std::net::{IpAddr, SocketAddr};
 use std::str::FromStr;
 use chrono::{DateTime, Utc};
 use warp::Filter;
+use warp::fs::file;
 use warp::http::StatusCode;
 use crate::{create_folder_lar, delete_folder_lar, rename_folder_lar};
 use crate::models::MailAddress;
@@ -47,6 +49,14 @@ pub async fn run_api() {
             .and(warp::get())
             .and(warp::query::<GetEmailQuery>())
             .and_then(get_email_handle))
+        .or(warp::path("get_attachments_info")
+            .and(warp::get())
+            .and(warp::query::<GetAttachmentsInfoQuery>())
+            .and_then(get_attachments_info_handle))
+        .or(warp::path("get_attachment")
+            .and(warp::get())
+            .and(warp::query::<GetAttachmentQuery>())
+            .and_then(get_attachment_handle))
         .or(warp::path("search")
             .and(warp::get())
             .and(warp::query::<SearchQuery>())
@@ -70,6 +80,64 @@ pub async fn run_api() {
     warp::serve(routes).run(addr).await;
 }
 
+// functions
+pub async fn get_attachment(folder: String, id: String, name: String) -> GetAttachmentResponse{    
+    let attachment_path = Config::global().out_dir.clone()
+        .join(folder.clone())
+        .join(".attachments")
+        .join(id)
+        .join(name.clone());
+    let file_result = File::open(attachment_path);
+    let mut file = match file_result {
+        Ok(f) => f,
+        Err(_) => {return GetAttachmentResponse{ name, data: vec![] }}
+    };
+
+    let mut buffer = Vec::new();
+    let read_result = file.read_to_end(&mut buffer);
+    match read_result {
+        Ok(_) => GetAttachmentResponse{ name, data: buffer },
+        Err(e) => GetAttachmentResponse{ name, data: vec![] },
+    }
+}
+
+pub async fn get_attachments_info(folder: String, id: String) -> String{
+    let mut attachments_info: Vec<AttachmentInfo> = Vec::new();
+    let attachments_path = Config::global().out_dir.clone()
+        .join(folder.clone())
+        .join(".attachments")
+        .join(id);
+    
+    let entries = fs::read_dir(attachments_path);
+    match entries {
+        Ok(entries) => {
+            for entry in entries {
+                match entry {
+                    Ok(entry) => {
+                        let path = entry.path();
+                        if path.is_file() {
+                            match metadata(&path) {
+                                Ok(meta) => {
+                                    let file_size = meta.len();
+                                    attachments_info.push(AttachmentInfo{
+                                        name: entry.file_name().to_str().unwrap().to_string(),
+                                        size: file_size,
+                                    })
+                                }
+                                Err(_) => {}
+                            }
+                        }
+                    }
+                    Err(_) => {}
+                }
+            }
+        }
+        Err(_) => {}
+    }
+    
+    serde_json::to_string(&attachments_info).unwrap()
+}
+
 pub async fn get_folders() -> String {
     let mut folders: Vec<String> = Vec::new();
     for entry in std::fs::read_dir(Config::global().out_dir.clone())
@@ -89,7 +157,7 @@ pub async fn get_folders() -> String {
     serde_json::to_string(&folders).unwrap()
 }
 
-pub async fn get_email(id: String) -> String {
+pub async fn get_email(id: String) -> String { // TODO replace with Indexes::get_messages()
     let messages_path = Config::global()
         .out_dir
         .clone()
@@ -196,6 +264,37 @@ pub async fn get_sorted_threads_by_date(folder: String) -> String {
 }
 
 // Handlers
+#[derive(Deserialize)]
+struct GetAttachmentQuery {
+    folder: String,
+    id: String,
+    name: String
+}
+#[derive(Deserialize, Serialize)]
+struct GetAttachmentResponse{
+    pub name: String,
+    pub data: Vec<u8>
+}
+async fn get_attachment_handle(query: GetAttachmentQuery) -> Result<impl warp::Reply, warp::Rejection>{
+    let result: GetAttachmentResponse = get_attachment(query.folder, query.id, query.name).await;
+    Ok(warp::reply::json(&result))
+}
+
+#[derive(Deserialize)]
+struct GetAttachmentsInfoQuery {
+    folder: String,
+    id: String
+}
+#[derive(Deserialize, Serialize)]
+struct AttachmentInfo{
+    pub name: String,
+    pub size: u64
+}
+async fn get_attachments_info_handle(query: GetAttachmentsInfoQuery) -> Result<impl warp::Reply, warp::Rejection>{
+    let result: Vec<AttachmentInfo> = serde_json::from_str(&*get_attachments_info(query.folder, query.id).await).unwrap();
+    Ok(warp::reply::json(&result))
+}
+
 #[derive(Deserialize, Serialize)]
 struct ErrorResponse {
     error: String,
@@ -230,7 +329,6 @@ async fn get_email_handle(query: GetEmailQuery) -> Result<impl warp::Reply, warp
 struct GetThreadQuery {
     id: u32,
 }
-
 #[derive(Serialize, Deserialize)]
 struct GetThreadResponse{
     pub id: u32,
@@ -254,7 +352,6 @@ impl GetThreadResponse{
         }
     }
 }
-
 async fn get_thread_handle(query: GetThreadQuery) -> Result<impl warp::Reply, warp::Rejection> {
     let result: GetThreadResponse = serde_json::from_str(&*get_thread(query.id).await).unwrap();
     Ok(warp::reply::json(&result))