diff --git a/melib/src/backends/jmap.rs b/melib/src/backends/jmap.rs index 0e4603a5f..c3b1832d4 100644 --- a/melib/src/backends/jmap.rs +++ b/melib/src/backends/jmap.rs @@ -54,6 +54,9 @@ macro_rules! _impl_get_mut { } } +pub mod operations; +use operations::*; + pub mod connection; use connection::*; @@ -168,6 +171,13 @@ macro_rules! get_conf_val { }; } +#[derive(Debug, Default)] +pub struct Store { + byte_cache: FnvHashMap, + id_store: FnvHashMap, + blob_id_store: FnvHashMap, +} + #[derive(Debug)] pub struct JmapType { account_name: String, @@ -175,6 +185,7 @@ pub struct JmapType { is_subscribed: Arc, server_conf: JmapServerConf, connection: Arc, + store: Arc>, folders: Arc>>, } @@ -185,6 +196,7 @@ impl MailBackend for JmapType { fn get(&mut self, folder: &Folder) -> Async>> { let mut w = AsyncBuilder::new(); let folders = self.folders.clone(); + let store = self.store.clone(); let connection = self.connection.clone(); let folder_hash = folder.hash(); let handle = { @@ -192,6 +204,7 @@ impl MailBackend for JmapType { let closure = move |_work_context| { tx.send(AsyncStatus::Payload(protocol::get( &connection, + &store, &folders.read().unwrap()[&folder_hash], ))) .unwrap(); @@ -231,7 +244,11 @@ impl MailBackend for JmapType { } fn operation(&self, hash: EnvelopeHash) -> Box { - unimplemented!() + Box::new(JmapOp::new( + hash, + self.connection.clone(), + self.store.clone(), + )) } fn save(&self, bytes: &[u8], folder: &str, flags: Option) -> Result<()> { @@ -257,6 +274,7 @@ impl JmapType { Ok(Box::new(JmapType { connection: Arc::new(JmapConnection::new(&server_conf, online.clone())?), + store: Arc::new(RwLock::new(Store::default())), folders: Arc::new(RwLock::new(FnvHashMap::default())), account_name: s.name.clone(), online, diff --git a/melib/src/backends/jmap/objects/email.rs b/melib/src/backends/jmap/objects/email.rs index 37cb8f6dc..8340ca5ba 100644 --- a/melib/src/backends/jmap/objects/email.rs +++ b/melib/src/backends/jmap/objects/email.rs @@ -129,7 +129,7 @@ use std::hash::Hasher; #[serde(rename_all = "camelCase")] pub struct EmailObject { #[serde(default)] - id: Id, + pub id: Id, #[serde(default)] mailbox_ids: HashMap, #[serde(default)] @@ -155,7 +155,7 @@ pub struct EmailObject { #[serde(default)] attachments: Vec, #[serde(default)] - blob_id: String, + pub blob_id: String, #[serde(default)] has_attachment: bool, #[serde(default)] diff --git a/melib/src/backends/jmap/operations.rs b/melib/src/backends/jmap/operations.rs new file mode 100644 index 000000000..db001b4a6 --- /dev/null +++ b/melib/src/backends/jmap/operations.rs @@ -0,0 +1,102 @@ +/* + * meli - jmap module. + * + * Copyright 2017 - 2019 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +use super::*; + +use crate::backends::BackendOp; +use crate::email::*; +use crate::error::{MeliError, Result}; +use std::cell::Cell; +use std::sync::{Arc, RwLock}; + +/// `BackendOp` implementor for Imap +#[derive(Debug, Clone)] +pub struct JmapOp { + hash: EnvelopeHash, + connection: Arc, + store: Arc>, + bytes: Option, + flags: Cell>, + headers: Option, + body: Option, +} + +impl JmapOp { + pub fn new( + hash: EnvelopeHash, + connection: Arc, + store: Arc>, + ) -> Self { + JmapOp { + hash, + connection, + store, + bytes: None, + headers: None, + body: None, + flags: Cell::new(None), + } + } +} + +impl BackendOp for JmapOp { + fn description(&self) -> String { + self.store + .try_read() + .and_then(|store_lck| Ok(store_lck.id_store[&self.hash].clone())) + .unwrap_or(String::new()) + } + + fn as_bytes(&mut self) -> Result<&[u8]> { + if self.bytes.is_none() { + let mut store_lck = self.store.write().unwrap(); + if !(store_lck.byte_cache.contains_key(&self.hash) + && store_lck.byte_cache[&self.hash].bytes.is_some()) + { + let blob_id = &store_lck.blob_id_store[&self.hash]; + let res = self.connection + .client + .lock() + .unwrap() + .get(&format!("https://jmap-proxy.local/raw/fc32dffe-14e7-11ea-a277-2477037a1804/{blob_id}/{name}", blob_id = blob_id, name = "")) + .send(); + + let res_text = res?.text()?; + + store_lck.byte_cache.entry(self.hash).or_default().bytes = Some(res_text); + } + self.bytes = store_lck.byte_cache[&self.hash].bytes.clone(); + } + Ok(&self.bytes.as_ref().unwrap().as_bytes()) + } + + fn fetch_flags(&self) -> Flag { + Flag::default() + } + + fn set_flag(&mut self, _envelope: &mut Envelope, f: Flag, value: bool) -> Result<()> { + Ok(()) + } + + fn set_tag(&mut self, _envelope: &mut Envelope, _tag: String, value: bool) -> Result<()> { + Ok(()) + } +} diff --git a/melib/src/backends/jmap/protocol.rs b/melib/src/backends/jmap/protocol.rs index 3f393ab60..99f1b8200 100644 --- a/melib/src/backends/jmap/protocol.rs +++ b/melib/src/backends/jmap/protocol.rs @@ -314,24 +314,11 @@ pub fn get_message(conn: &JmapConnection, ids: &[String]) -> Result>()) } -/* - * - *json!({ - "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"], - "methodCalls": [["Email/get", { - "ids": ids, - "properties": [ "threadId", "mailboxIds", "from", "subject", - "receivedAt", - "htmlBody", "bodyValues" ], - "bodyProperties": [ "partId", "blobId", "size", "type" ], - "fetchHTMLBodyValues": true, - "maxBodyValueBytes": 256 - }, format!("m{}", seq).as_str()]], - })) - -*/ - -pub fn get(conn: &JmapConnection, folder: &JmapFolder) -> Result> { +pub fn get( + conn: &JmapConnection, + store: &Arc>, + folder: &JmapFolder, +) -> Result> { let email_query_call: EmailQueryCall = EmailQueryCall { filter: EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())), collapse_threads: false, @@ -349,7 +336,6 @@ pub fn get(conn: &JmapConnection, folder: &JmapFolder) -> Result> MessageProperty::From, MessageProperty::To, MessageProperty::Subject, - MessageProperty::Date, MessageProperty::Preview, ], }; @@ -382,8 +368,18 @@ pub fn get(conn: &JmapConnection, folder: &JmapFolder) -> Result> let mut v: MethodResponse = serde_json::from_str(&res_text).unwrap(); let e = GetResponse::::try_from(v.method_responses.pop().unwrap())?; let GetResponse:: { list, .. } = e; - Ok(list + let ids = list + .iter() + .map(|obj| (obj.id.clone(), obj.blob_id.clone())) + .collect::>(); + let ret = list .into_iter() .map(std::convert::Into::into) - .collect::>()) + .collect::>(); + let mut store_lck = store.write().unwrap(); + for (env, (id, blob_id)) in ret.iter().zip(ids.into_iter()) { + store_lck.id_store.insert(env.hash(), id); + store_lck.blob_id_store.insert(env.hash(), blob_id); + } + Ok(ret) }