JMAP WIP
parent
d69be5bb0b
commit
a43f6919cc
File diff suppressed because it is too large
Load Diff
|
@ -26,9 +26,11 @@ bincode = "1.2.0"
|
|||
uuid = { version = "0.7.4", features = ["serde", "v4"] }
|
||||
text_processing = { path = "../text_processing", version = "*", optional= true }
|
||||
libc = {version = "0.2.59", features = ["extra_traits",]}
|
||||
reqwest = { version ="0.10.0-alpha.2", optional=true, features = ["json", "blocking" ]}
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend", "vcard"]
|
||||
default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend", "jmap_backend", "vcard"]
|
||||
|
||||
debug-tracing = []
|
||||
unicode_algorithms = ["text_processing"]
|
||||
|
@ -36,4 +38,5 @@ imap_backend = ["native-tls"]
|
|||
maildir_backend = ["notify", "notify-rust", "memmap"]
|
||||
mbox_backend = ["notify", "notify-rust", "memmap"]
|
||||
notmuch_backend = []
|
||||
jmap_backend = ["reqwest", "serde_json" ]
|
||||
vcard = []
|
||||
|
|
|
@ -38,6 +38,10 @@ pub mod mbox;
|
|||
pub mod notmuch;
|
||||
#[cfg(feature = "notmuch_backend")]
|
||||
pub use self::notmuch::NotmuchDb;
|
||||
#[cfg(feature = "jmap_backend")]
|
||||
pub mod jmap;
|
||||
#[cfg(feature = "jmap_backend")]
|
||||
pub use self::jmap::JmapType;
|
||||
|
||||
#[cfg(feature = "imap_backend")]
|
||||
pub use self::imap::ImapType;
|
||||
|
@ -129,6 +133,16 @@ impl Backends {
|
|||
},
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "jmap_backend")]
|
||||
{
|
||||
b.register(
|
||||
"jmap".to_string(),
|
||||
Backend {
|
||||
create_fn: Box::new(|| Box::new(|f, i| JmapType::new(f, i))),
|
||||
validate_conf_fn: Box::new(JmapType::validate_config),
|
||||
},
|
||||
);
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* meli - jmap module.
|
||||
*
|
||||
* Copyright 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 crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use crate::backends::BackendOp;
|
||||
use crate::backends::FolderHash;
|
||||
use crate::backends::RefreshEvent;
|
||||
use crate::backends::RefreshEventKind::{self, *};
|
||||
use crate::backends::{BackendFolder, Folder, FolderOperation, MailBackend, RefreshEventConsumer};
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::*;
|
||||
use crate::error::{MeliError, Result};
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
pub mod protocol;
|
||||
|
||||
use protocol::*;
|
||||
|
||||
pub mod folder;
|
||||
|
||||
use folder::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EnvelopeCache {
|
||||
bytes: Option<String>,
|
||||
headers: Option<String>,
|
||||
body: Option<String>,
|
||||
flags: Option<Flag>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JmapServerConf {
|
||||
pub server_hostname: String,
|
||||
pub server_username: String,
|
||||
pub server_password: String,
|
||||
pub server_port: u16,
|
||||
pub danger_accept_invalid_certs: bool,
|
||||
}
|
||||
|
||||
macro_rules! get_conf_val {
|
||||
($s:ident[$var:literal]) => {
|
||||
$s.extra.get($var).ok_or_else(|| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): JMAP connection requires the field `{}` set",
|
||||
$s.name.as_str(),
|
||||
$var
|
||||
))
|
||||
})
|
||||
};
|
||||
($s:ident[$var:literal], $default:expr) => {
|
||||
$s.extra
|
||||
.get($var)
|
||||
.map(|v| {
|
||||
<_>::from_str(v).map_err(|e| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): Invalid value for field `{}`: {}\n{}",
|
||||
$s.name.as_str(),
|
||||
$var,
|
||||
v,
|
||||
e
|
||||
))
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| Ok($default))
|
||||
};
|
||||
}
|
||||
|
||||
impl JmapServerConf {
|
||||
pub fn new(s: &AccountSettings) -> Result<Self> {
|
||||
Ok(JmapServerConf {
|
||||
server_hostname: get_conf_val!(s["server_hostname"])?.to_string(),
|
||||
server_username: get_conf_val!(s["server_username"])?.to_string(),
|
||||
server_password: get_conf_val!(s["server_password"])?.to_string(),
|
||||
server_port: get_conf_val!(s["server_port"], 443)?,
|
||||
danger_accept_invalid_certs: get_conf_val!(s["danger_accept_invalid_certs"], false)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct IsSubscribedFn(Box<dyn Fn(&str) -> bool + Send + Sync>);
|
||||
|
||||
impl std::fmt::Debug for IsSubscribedFn {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "IsSubscribedFn Box")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for IsSubscribedFn {
|
||||
type Target = Box<dyn Fn(&str) -> bool + Send + Sync>;
|
||||
fn deref(&self) -> &Box<dyn Fn(&str) -> bool + Send + Sync> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
macro_rules! get_conf_val {
|
||||
($s:ident[$var:literal]) => {
|
||||
$s.extra.get($var).ok_or_else(|| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): IMAP connection requires the field `{}` set",
|
||||
$s.name.as_str(),
|
||||
$var
|
||||
))
|
||||
})
|
||||
};
|
||||
($s:ident[$var:literal], $default:expr) => {
|
||||
$s.extra
|
||||
.get($var)
|
||||
.map(|v| {
|
||||
<_>::from_str(v).map_err(|e| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): Invalid value for field `{}`: {}\n{}",
|
||||
$s.name.as_str(),
|
||||
$var,
|
||||
v,
|
||||
e
|
||||
))
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| Ok($default))
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JmapType {
|
||||
account_name: String,
|
||||
online: Arc<Mutex<bool>>,
|
||||
is_subscribed: Arc<IsSubscribedFn>,
|
||||
server_conf: JmapServerConf,
|
||||
connection: Arc<Mutex<JmapConnection>>,
|
||||
folders: Arc<RwLock<FnvHashMap<FolderHash, JmapFolder>>>,
|
||||
}
|
||||
|
||||
impl MailBackend for JmapType {
|
||||
fn is_online(&self) -> bool {
|
||||
*self.online.lock().unwrap()
|
||||
}
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let folders = self.folders.clone();
|
||||
let connection = self.connection.clone();
|
||||
let folder_hash = folder.hash();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
let closure = move |_work_context| {
|
||||
let mut conn_lck = connection.lock().unwrap();
|
||||
tx.send(AsyncStatus::Payload(
|
||||
protocol::get_message_list(
|
||||
&mut conn_lck,
|
||||
&folders.read().unwrap()[&folder_hash],
|
||||
)
|
||||
.and_then(|ids| {
|
||||
protocol::get_message(&mut conn_lck, std::dbg!(&ids).as_slice())
|
||||
}),
|
||||
))
|
||||
.unwrap();
|
||||
tx.send(AsyncStatus::Finished).unwrap();
|
||||
};
|
||||
Box::new(closure)
|
||||
};
|
||||
w.build(handle)
|
||||
}
|
||||
|
||||
fn watch(
|
||||
&self,
|
||||
sender: RefreshEventConsumer,
|
||||
work_context: WorkContext,
|
||||
) -> Result<std::thread::ThreadId> {
|
||||
Err(MeliError::from("sadfsa"))
|
||||
}
|
||||
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
if self.folders.read().unwrap().is_empty() {
|
||||
let folders = std::dbg!(protocol::get_mailboxes(
|
||||
&mut self.connection.lock().unwrap()
|
||||
))?;
|
||||
let ret = Ok(folders
|
||||
.iter()
|
||||
.map(|(&h, f)| (h, BackendFolder::clone(f) as Folder))
|
||||
.collect());
|
||||
*self.folders.write().unwrap() = folders;
|
||||
ret
|
||||
} else {
|
||||
Ok(self
|
||||
.folders
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(&h, f)| (h, BackendFolder::clone(f) as Folder))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn folder_operation(&mut self, path: &str, op: FolderOperation) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn::std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl JmapType {
|
||||
pub fn new(
|
||||
s: &AccountSettings,
|
||||
is_subscribed: Box<dyn Fn(&str) -> bool + Send + Sync>,
|
||||
) -> Result<Box<dyn MailBackend>> {
|
||||
let online = Arc::new(Mutex::new(false));
|
||||
let server_conf = JmapServerConf::new(s)?;
|
||||
|
||||
Ok(Box::new(JmapType {
|
||||
connection: Arc::new(Mutex::new(JmapConnection::new(
|
||||
&server_conf,
|
||||
online.clone(),
|
||||
)?)),
|
||||
folders: Arc::new(RwLock::new(FnvHashMap::default())),
|
||||
account_name: s.name.clone(),
|
||||
online,
|
||||
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
|
||||
server_conf,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
get_conf_val!(s["server_hostname"])?;
|
||||
get_conf_val!(s["server_username"])?;
|
||||
get_conf_val!(s["server_password"])?;
|
||||
get_conf_val!(s["server_port"], 443)?;
|
||||
get_conf_val!(s["danger_accept_invalid_certs"], false)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JmapConnection {
|
||||
request_no: usize,
|
||||
client: Client,
|
||||
online_status: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl JmapConnection {
|
||||
pub fn new(server_conf: &JmapServerConf, online_status: Arc<Mutex<bool>>) -> Result<Self> {
|
||||
use reqwest::header;
|
||||
let mut headers = header::HeaderMap::new();
|
||||
headers.insert(
|
||||
header::AUTHORIZATION,
|
||||
header::HeaderValue::from_static("fc32dffe-14e7-11ea-a277-2477037a1804"),
|
||||
);
|
||||
headers.insert(
|
||||
header::ACCEPT,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
Ok(JmapConnection {
|
||||
request_no: 0,
|
||||
client: reqwest::blocking::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(server_conf.danger_accept_invalid_certs)
|
||||
.default_headers(headers)
|
||||
.build()?,
|
||||
online_status,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* meli - jmap module.
|
||||
*
|
||||
* Copyright 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::{FolderPermissions, SpecialUsageMailbox};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JmapFolder {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub hash: FolderHash,
|
||||
pub v: Vec<FolderHash>,
|
||||
pub id: String,
|
||||
pub is_subscribed: bool,
|
||||
pub my_rights: JmapRights,
|
||||
pub parent_id: Option<String>,
|
||||
pub role: Option<String>,
|
||||
pub sort_order: u64,
|
||||
pub total_emails: u64,
|
||||
pub total_threads: u64,
|
||||
pub unread_emails: u64,
|
||||
pub unread_threads: u64,
|
||||
pub usage: SpecialUsageMailbox,
|
||||
}
|
||||
|
||||
impl BackendFolder for JmapFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
|
||||
fn change_name(&mut self, _s: &str) {}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
Box::new(std::clone::Clone::clone(self))
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
&self.v
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
None
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
FolderPermissions::default()
|
||||
}
|
||||
|
||||
fn special_usage(&self) -> SpecialUsageMailbox {
|
||||
self.usage
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* meli - jmap module.
|
||||
*
|
||||
* Copyright 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::folder::JmapFolder;
|
||||
use super::*;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
macro_rules! get_path_hash {
|
||||
($path:expr) => {{
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut hasher = DefaultHasher::new();
|
||||
$path.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}};
|
||||
}
|
||||
|
||||
static USING: &'static [&'static str] = &["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"];
|
||||
|
||||
pub fn get_mailboxes(conn: &mut JmapConnection) -> Result<FnvHashMap<FolderHash, JmapFolder>> {
|
||||
let res = conn
|
||||
.client
|
||||
.post("https://jmap-proxy.local/jmap/fc32dffe-14e7-11ea-a277-2477037a1804/")
|
||||
.json(&json!({
|
||||
"using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
|
||||
"methodCalls": [["Mailbox/get", {},
|
||||
format!("#m{}", conn.request_no + 1).as_str()]],
|
||||
}))
|
||||
.send();
|
||||
conn.request_no += 1;
|
||||
|
||||
let mut v: JsonResponse =
|
||||
serde_json::from_str(&std::dbg!(res.unwrap().text().unwrap())).unwrap();
|
||||
*conn.online_status.lock().unwrap() = true;
|
||||
std::dbg!(&v);
|
||||
assert_eq!("Mailbox/get", v.method_responses[0].0);
|
||||
Ok(
|
||||
if let Response::MailboxGet { list, .. } = v.method_responses.remove(0).1 {
|
||||
list.into_iter().map(|r| {
|
||||
if let MailboxResponse {
|
||||
id,
|
||||
is_subscribed,
|
||||
my_rights,
|
||||
name,
|
||||
parent_id,
|
||||
role,
|
||||
sort_order,
|
||||
total_emails,
|
||||
total_threads,
|
||||
unread_emails,
|
||||
unread_threads,
|
||||
} = r
|
||||
{
|
||||
let hash = get_path_hash!(&name);
|
||||
(
|
||||
hash,
|
||||
JmapFolder {
|
||||
name: name.clone(),
|
||||
hash,
|
||||
path: name,
|
||||
v: Vec::new(),
|
||||
id,
|
||||
is_subscribed,
|
||||
my_rights,
|
||||
parent_id,
|
||||
role,
|
||||
usage: Default::default(),
|
||||
sort_order,
|
||||
total_emails,
|
||||
total_threads,
|
||||
unread_emails,
|
||||
unread_threads,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct JsonResponse {
|
||||
method_responses: Vec<MethodResponse>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MethodResponse(String, Response, String);
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(untagged)]
|
||||
pub enum Response {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MailboxGet {
|
||||
account_id: String,
|
||||
list: Vec<MailboxResponse>,
|
||||
not_found: Vec<String>,
|
||||
state: String,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
EmailQuery {
|
||||
account_id: String,
|
||||
can_calculate_changes: bool,
|
||||
collapse_threads: bool,
|
||||
filter: Value,
|
||||
ids: Vec<String>,
|
||||
position: u64,
|
||||
query_state: String,
|
||||
sort: Option<String>,
|
||||
total: usize,
|
||||
},
|
||||
Empty {},
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MailboxResponse {
|
||||
id: String,
|
||||
is_subscribed: bool,
|
||||
my_rights: JmapRights,
|
||||
name: String,
|
||||
parent_id: Option<String>,
|
||||
role: Option<String>,
|
||||
sort_order: u64,
|
||||
total_emails: u64,
|
||||
total_threads: u64,
|
||||
unread_emails: u64,
|
||||
unread_threads: u64,
|
||||
}
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct JmapRights {
|
||||
may_add_items: bool,
|
||||
may_create_child: bool,
|
||||
may_delete: bool,
|
||||
may_read_items: bool,
|
||||
may_remove_items: bool,
|
||||
may_rename: bool,
|
||||
may_set_keywords: bool,
|
||||
may_set_seen: bool,
|
||||
may_submit: bool,
|
||||
}
|
||||
|
||||
// [
|
||||
// [ "getMessageList", {
|
||||
// filter: {
|
||||
// inMailboxes: [ "mailbox1" ]
|
||||
// },
|
||||
// sort: [ "date desc", "id desc" ]
|
||||
// collapseThreads: true,
|
||||
// position: 0,
|
||||
// limit: 10,
|
||||
// fetchThreads: true,
|
||||
// fetchMessages: true,
|
||||
// fetchMessageProperties: [
|
||||
// "threadId",
|
||||
// "mailboxId",
|
||||
// "isUnread",
|
||||
// "isFlagged",
|
||||
// "isAnswered",
|
||||
// "isDraft",
|
||||
// "hasAttachment",
|
||||
// "from",
|
||||
// "to",
|
||||
// "subject",
|
||||
// "date",
|
||||
// "preview"
|
||||
// ],
|
||||
// fetchSearchSnippets: false
|
||||
// }, "call1"]
|
||||
// ]
|
||||
pub fn get_message_list(conn: &mut JmapConnection, folder: &JmapFolder) -> Result<Vec<String>> {
|
||||
let res = conn
|
||||
.client
|
||||
.post("https://jmap-proxy.local/jmap/fc32dffe-14e7-11ea-a277-2477037a1804/")
|
||||
.json(&json!({
|
||||
"using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
|
||||
"methodCalls": [["Email/query", { "filter": {
|
||||
"inMailboxes": [ folder.id ]
|
||||
},
|
||||
"collapseThreads": false,
|
||||
"position": 0,
|
||||
"fetchThreads": true,
|
||||
"fetchMessages": true,
|
||||
"fetchMessageProperties": [
|
||||
"threadId",
|
||||
"mailboxId",
|
||||
"isUnread",
|
||||
"isFlagged",
|
||||
"isAnswered",
|
||||
"isDraft",
|
||||
"hasAttachment",
|
||||
"from",
|
||||
"to",
|
||||
"subject",
|
||||
"date",
|
||||
"preview"
|
||||
],
|
||||
}, format!("#m{}", conn.request_no + 1).as_str()]],
|
||||
}))
|
||||
.send();
|
||||
|
||||
conn.request_no += 1;
|
||||
let mut v: JsonResponse = serde_json::from_str(&std::dbg!(res.unwrap().text().unwrap()))?;
|
||||
|
||||
let result: Response = v.method_responses.remove(0).1;
|
||||
if let Response::EmailQuery { ids, .. } = result {
|
||||
Ok(ids)
|
||||
} else {
|
||||
Err(MeliError::new(format!("response was {:#?}", &result)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_message(conn: &mut JmapConnection, ids: &[String]) -> Result<Vec<Envelope>> {
|
||||
let res = conn
|
||||
.client
|
||||
.post("https://jmap-proxy.local/jmap/fc32dffe-14e7-11ea-a277-2477037a1804/")
|
||||
.json(&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{}", conn.request_no + 1).as_str()]],
|
||||
}))
|
||||
.send();
|
||||
conn.request_no += 1;
|
||||
|
||||
let v: JsonResponse = serde_json::from_str(&std::dbg!(res.unwrap().text().unwrap()))?;
|
||||
std::dbg!(&v);
|
||||
Ok(vec![])
|
||||
}
|
|
@ -151,6 +151,22 @@ impl From<std::num::ParseIntError> for MeliError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "jmap_backend")]
|
||||
impl From<reqwest::Error> for MeliError {
|
||||
#[inline]
|
||||
fn from(kind: reqwest::Error) -> MeliError {
|
||||
MeliError::new(format!("{}", kind))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "jmap_backend")]
|
||||
impl From<serde_json::error::Error> for MeliError {
|
||||
#[inline]
|
||||
fn from(kind: serde_json::error::Error) -> MeliError {
|
||||
MeliError::new(format!("{}", kind))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MeliError {
|
||||
#[inline]
|
||||
fn from(kind: &str) -> MeliError {
|
||||
|
|
Loading…
Reference in New Issue