melib/jmap: add byte operations

async
Manos Pitsidianakis 2019-12-06 10:06:15 +02:00
parent a41dc6c38a
commit 791033d2fc
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 140 additions and 24 deletions

View File

@ -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<EnvelopeHash, EnvelopeCache>,
id_store: FnvHashMap<EnvelopeHash, Id>,
blob_id_store: FnvHashMap<EnvelopeHash, Id>,
}
#[derive(Debug)]
pub struct JmapType {
account_name: String,
@ -175,6 +185,7 @@ pub struct JmapType {
is_subscribed: Arc<IsSubscribedFn>,
server_conf: JmapServerConf,
connection: Arc<JmapConnection>,
store: Arc<RwLock<Store>>,
folders: Arc<RwLock<FnvHashMap<FolderHash, JmapFolder>>>,
}
@ -185,6 +196,7 @@ impl MailBackend for JmapType {
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
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<dyn BackendOp> {
unimplemented!()
Box::new(JmapOp::new(
hash,
self.connection.clone(),
self.store.clone(),
))
}
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> 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,

View File

@ -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<Id, bool>,
#[serde(default)]
@ -155,7 +155,7 @@ pub struct EmailObject {
#[serde(default)]
attachments: Vec<Value>,
#[serde(default)]
blob_id: String,
pub blob_id: String,
#[serde(default)]
has_attachment: bool,
#[serde(default)]

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<JmapConnection>,
store: Arc<RwLock<Store>>,
bytes: Option<String>,
flags: Cell<Option<Flag>>,
headers: Option<String>,
body: Option<String>,
}
impl JmapOp {
pub fn new(
hash: EnvelopeHash,
connection: Arc<JmapConnection>,
store: Arc<RwLock<Store>>,
) -> 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(())
}
}

View File

@ -314,24 +314,11 @@ pub fn get_message(conn: &JmapConnection, ids: &[String]) -> Result<Vec<Envelope
.collect::<Vec<Envelope>>())
}
/*
*
*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<Vec<Envelope>> {
pub fn get(
conn: &JmapConnection,
store: &Arc<RwLock<Store>>,
folder: &JmapFolder,
) -> Result<Vec<Envelope>> {
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<Vec<Envelope>>
MessageProperty::From,
MessageProperty::To,
MessageProperty::Subject,
MessageProperty::Date,
MessageProperty::Preview,
],
};
@ -382,8 +368,18 @@ pub fn get(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<Envelope>>
let mut v: MethodResponse = serde_json::from_str(&res_text).unwrap();
let e = GetResponse::<EmailObject>::try_from(v.method_responses.pop().unwrap())?;
let GetResponse::<EmailObject> { list, .. } = e;
Ok(list
let ids = list
.iter()
.map(|obj| (obj.id.clone(), obj.blob_id.clone()))
.collect::<Vec<(Id, Id)>>();
let ret = list
.into_iter()
.map(std::convert::Into::into)
.collect::<Vec<Envelope>>())
.collect::<Vec<Envelope>>();
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)
}