forked from meli/meli
1
Fork 0

melib/jmap: fix blob download URL formatting

imap-codec-server-tests
Manos Pitsidianakis 2023-07-18 16:12:09 +03:00
parent 48a10f7241
commit 6280bc75e5
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
12 changed files with 105 additions and 145 deletions

11
Cargo.lock generated
View File

@ -1284,6 +1284,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"serde_path_to_error",
"smallvec",
"smol",
"stderrlog",
@ -1934,6 +1935,16 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335"
dependencies = [
"itoa",
"serde",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"

View File

@ -71,7 +71,7 @@ regex = "1"
tempfile = "3.3"
[features]
default = ["sqlite3", "notmuch", "regexp", "smtp", "dbus-notifications", "gpgme", "cli-docs"]
default = ["sqlite3", "notmuch", "regexp", "smtp", "dbus-notifications", "gpgme", "cli-docs", "jmap"]
notmuch = ["melib/notmuch_backend", ]
jmap = ["melib/jmap_backend",]
sqlite3 = ["melib/sqlite3"]

View File

@ -207,7 +207,9 @@ impl MailBackend for PluginBackend {
&mut self,
_name: String,
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
Err(Error::new("Creating a mailbox is currently unimplemented for plugins"))
Err(Error::new(
"Creating a mailbox is currently unimplemented for plugins",
))
}
fn collection(&self) -> melib::Collection {
self.collection.clone()

View File

@ -40,9 +40,10 @@ nom = { version = "7" }
notify = { version = "4.0.15", optional = true }
regex = { version = "1" }
rusqlite = { version = "^0.28", default-features = false, optional = true }
serde = { version = "1.0.71", features = ["rc", ] }
serde_derive = "1.0.71"
serde = { version = "1.0", features = ["rc", ] }
serde_derive = "1.0"
serde_json = { version = "1.0", features = ["raw_value",] }
serde_path_to_error = { version = "0.1" }
smallvec = { version = "^1.5.0", features = ["serde", ] }
smol = "1.0.0"

View File

@ -84,6 +84,22 @@ use objects::*;
pub mod mailbox;
use mailbox::*;
pub fn deserialize_from_str<'de, T: serde::de::Deserialize<'de>>(s: &'de str) -> Result<T> {
let jd = &mut serde_json::Deserializer::from_str(s);
match serde_path_to_error::deserialize(jd) {
Ok(v) => Ok(v),
Err(err) => Err(Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report this!\nError \
{} at {}. Reply from server: {}",
err,
err.path(),
&s
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug)),
}
}
#[derive(Debug, Default)]
pub struct EnvelopeCache {
bytes: Option<String>,
@ -430,15 +446,8 @@ impl MailBackend for JmapType {
};
let res_text = res.text().await?;
let upload_response: UploadResponse = match serde_json::from_str(&res_text) {
let upload_response: UploadResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please \
report this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
@ -467,23 +476,15 @@ impl MailBackend for JmapType {
.await?;
let res_text = res.text().await?;
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please \
report this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
Ok(s) => s,
};
let m = ImportResponse::try_from(v.method_responses.remove(0)).map_err(|err| {
let ierr: Result<ImportError> =
serde_json::from_str(&res_text).map_err(|err| err.into());
let ierr: Result<ImportError> = deserialize_from_str(&res_text);
if let Ok(err) = ierr {
Error::new(format!("Could not save message: {:?}", err))
} else {
@ -554,15 +555,8 @@ impl MailBackend for JmapType {
.await?;
let res_text = res.text().await?;
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please \
report this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
@ -694,15 +688,8 @@ impl MailBackend for JmapType {
let res_text = res.text().await?;
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please \
report this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
@ -816,15 +803,8 @@ impl MailBackend for JmapType {
* p-5;vfs-0"}
*/
//debug!("res_text = {}", &res_text);
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please \
report this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}

View File

@ -100,8 +100,8 @@ impl JmapConnection {
jmap_session_resource_url = to_well_known(&self.server_conf.server_url);
if let Ok(s) = self.client.get_async(&jmap_session_resource_url).await {
log::error!(
"Account {} server URL should start with `https`. Please correct \
your configuration value. Its current value is `{}`.",
"Account {} server URL should start with `https`. Please correct your \
configuration value. Its current value is `{}`.",
self.store.account_name,
self.server_conf.server_url
);
@ -151,7 +151,7 @@ impl JmapConnection {
Ok(s) => s,
};
let session: JmapSession = match serde_json::from_str(&res_text) {
let session: JmapSession = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"Could not connect to JMAP server endpoint for {}. Is your server url setting \
@ -167,14 +167,36 @@ impl JmapConnection {
Ok(s) => s,
};
if !session.capabilities.contains_key(JMAP_CORE_CAPABILITY) {
let err = Error::new(format!("Server {} did not return JMAP Core capability ({core_capability}). Returned capabilities were: {}", &self.server_conf.server_url, session.capabilities.keys().map(String::as_str).collect::<Vec<&str>>().join(", "), core_capability=JMAP_CORE_CAPABILITY));
let err = Error::new(format!(
"Server {} did not return JMAP Core capability ({core_capability}). Returned \
capabilities were: {}",
&self.server_conf.server_url,
session
.capabilities
.keys()
.map(String::as_str)
.collect::<Vec<&str>>()
.join(", "),
core_capability = JMAP_CORE_CAPABILITY
));
*self.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
*self.store.core_capabilities.lock().unwrap() =
session.capabilities[JMAP_CORE_CAPABILITY].clone();
if !session.capabilities.contains_key(JMAP_MAIL_CAPABILITY) {
let err = Error::new(format!("Server {} does not support JMAP Mail capability ({mail_capability}). Returned capabilities were: {}", &self.server_conf.server_url, session.capabilities.keys().map(String::as_str).collect::<Vec<&str>>().join(", "), mail_capability=JMAP_MAIL_CAPABILITY));
let err = Error::new(format!(
"Server {} does not support JMAP Mail capability ({mail_capability}). Returned \
capabilities were: {}",
&self.server_conf.server_url,
session
.capabilities
.keys()
.map(String::as_str)
.collect::<Vec<&str>>()
.join(", "),
mail_capability = JMAP_MAIL_CAPABILITY
));
*self.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
@ -266,15 +288,8 @@ impl JmapConnection {
let res_text = res.text().await?;
debug!(&res_text);
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please \
report this!\nReply from server: {}",
&self.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*self.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
@ -446,11 +461,10 @@ impl JmapConnection {
let res_text = res.text().await?;
debug!(&res_text);
let _: MethodResponse = match serde_json::from_str(&res_text) {
let _: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!("BUG: Could not deserialize {} server JSON response properly, please report this!\nReply from server: {}", &self.server_conf.server_url, &res_text)).set_source(Some(Arc::new(err))).set_kind(ErrorKind::Bug);
*self.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
log::error!("{}", &err);
*self.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
Ok(s) => s,

View File

@ -337,7 +337,7 @@ impl std::convert::From<EmailObject> for crate::Envelope {
pub struct HtmlBody {
pub blob_id: Id<BlobObject>,
#[serde(default)]
pub charset: String,
pub charset: Option<String>,
#[serde(default)]
pub cid: Option<String>,
#[serde(default)]
@ -364,7 +364,7 @@ pub struct HtmlBody {
pub struct TextBody {
pub blob_id: Id<BlobObject>,
#[serde(default)]
pub charset: String,
pub charset: Option<String>,
#[serde(default)]
pub cid: Option<String>,
#[serde(default)]
@ -854,16 +854,9 @@ pub struct EmailQueryChangesResponse {
impl std::convert::TryFrom<&RawValue> for EmailQueryChangesResponse {
type Error = crate::error::Error;
fn try_from(t: &RawValue) -> Result<Self> {
let res: (String, Self, String) = serde_json::from_str(t.get()).map_err(|err| {
crate::error::Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report \
this!\nReply from server: {}",
&t
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug)
})?;
fn try_from(t: &RawValue) -> std::result::Result<Self, Self::Error> {
let res: (String, Self, String) = deserialize_from_str(t.get())?;
assert_eq!(&res.0, "Email/queryChanges");
Ok(res.1)
}

View File

@ -196,16 +196,9 @@ pub struct ImportResponse {
impl std::convert::TryFrom<&RawValue> for ImportResponse {
type Error = crate::error::Error;
fn try_from(t: &RawValue) -> Result<Self> {
let res: (String, Self, String) = serde_json::from_str(t.get()).map_err(|err| {
crate::error::Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report \
this!\nReply from server: {}",
&t
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug)
})?;
let res: (String, Self, String) = deserialize_from_str(t.get())?;
assert_eq!(&res.0, &ImportCall::NAME);
Ok(res.1)
}

View File

@ -19,9 +19,10 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use super::*;
use core::marker::PhantomData;
use super::*;
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ThreadObject {

View File

@ -49,8 +49,7 @@ impl BackendOp for JmapOp {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
{
let byte_lck = self.store.byte_cache.lock().unwrap();
if byte_lck.contains_key(&self.hash) && byte_lck[&self.hash].bytes.is_some() {
let ret = byte_lck[&self.hash].bytes.clone().unwrap();
if let Some(Some(ret)) = byte_lck.get(&self.hash).map(|c| c.bytes.clone()) {
return Ok(Box::pin(async move { Ok(ret.into_bytes()) }));
}
}

View File

@ -90,7 +90,7 @@ pub async fn get_mailboxes(conn: &JmapConnection) -> Result<HashMap<MailboxHash,
}))?)
.await?;
let mut v: MethodResponse = serde_json::from_str(&res_text).unwrap();
let mut v: MethodResponse = deserialize_from_str(&res_text)?;
*conn.store.online_status.lock().await = (std::time::Instant::now(), Ok(()));
let m = GetResponse::<MailboxObject>::try_from(v.method_responses.remove(0))?;
let GetResponse::<MailboxObject> {
@ -191,15 +191,8 @@ pub async fn get_message_list(
.await?;
let res_text = res.text().await?;
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please report \
this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await = (Instant::now(), Err(err.clone()));
return Err(err);
}
@ -259,7 +252,7 @@ impl EmailFetchState {
let current_state_lck = mbox.email_state.lock().unwrap();
(
current_state_lck.is_none(),
current_state_lck.as_ref() != Some(&state),
current_state_lck.as_ref() == Some(&state),
)
})
.unwrap_or((true, true))
@ -324,15 +317,8 @@ impl EmailFetchState {
let _prev_seq = req.add_call(&email_call);
let res_text = conn.send_request(serde_json::to_string(&req)?).await?;
let mut v: MethodResponse = match serde_json::from_str(&res_text) {
let mut v: MethodResponse = match deserialize_from_str(&res_text) {
Err(err) => {
let err = Error::new(format!(
"BUG: Could not deserialize {} server JSON response properly, please report \
this!\nReply from server: {}",
&conn.server_conf.server_url, &res_text
))
.set_source(Some(Arc::new(err)))
.set_kind(ErrorKind::Bug);
*conn.store.online_status.lock().await =
(Instant::now(), Err(err.clone()));
return Err(err);

View File

@ -42,7 +42,7 @@ use std::collections::HashMap;
pub use argument::*;
use super::protocol::Method;
use super::{deserialize_from_str, protocol::Method};
pub trait Object {
const NAME: &'static str;
}
@ -282,7 +282,6 @@ impl Object for BlobObject {
/// - `account_id`: `Id`
///
/// The id of the account to use.
///
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Get<OBJ>
@ -427,16 +426,9 @@ pub struct GetResponse<OBJ: Object> {
impl<OBJ: Object + DeserializeOwned> std::convert::TryFrom<&RawValue> for GetResponse<OBJ> {
type Error = crate::error::Error;
fn try_from(t: &RawValue) -> Result<Self, crate::error::Error> {
let res: (String, Self, String) = serde_json::from_str(t.get()).map_err(|err| {
crate::error::Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report \
this!\nReply from server: {}",
&t
))
.set_source(Some(Arc::new(err)))
.set_kind(crate::error::ErrorKind::Bug)
})?;
let res: (String, Self, String) = deserialize_from_str(t.get())?;
assert_eq!(&res.0, &format!("{}/get", OBJ::NAME));
Ok(res.1)
}
@ -548,16 +540,9 @@ pub struct QueryResponse<OBJ: Object> {
impl<OBJ: Object + DeserializeOwned> std::convert::TryFrom<&RawValue> for QueryResponse<OBJ> {
type Error = crate::error::Error;
fn try_from(t: &RawValue) -> Result<Self, crate::error::Error> {
let res: (String, Self, String) = serde_json::from_str(t.get()).map_err(|err| {
crate::error::Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report \
this!\nReply from server: {}",
&t
))
.set_source(Some(Arc::new(err)))
.set_kind(crate::error::ErrorKind::Bug)
})?;
fn try_from(t: &RawValue) -> std::result::Result<Self, Self::Error> {
let res: (String, Self, String) = deserialize_from_str(t.get())?;
assert_eq!(&res.0, &format!("{}/query", OBJ::NAME));
Ok(res.1)
}
@ -618,7 +603,6 @@ impl<M: Method<OBJ>, OBJ: Object> ResultField<M, OBJ> {
/// to return. If supplied by the client, the value MUST be a
/// positive integer greater than 0. If a value outside of this range
/// is given, the server MUST re
///
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
/* ch-ch-ch-ch-ch-Changes */
@ -697,16 +681,9 @@ pub struct ChangesResponse<OBJ: Object> {
impl<OBJ: Object + DeserializeOwned> std::convert::TryFrom<&RawValue> for ChangesResponse<OBJ> {
type Error = crate::error::Error;
fn try_from(t: &RawValue) -> Result<Self, crate::error::Error> {
let res: (String, Self, String) = serde_json::from_str(t.get()).map_err(|err| {
crate::error::Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report \
this!\nReply from server: {}",
&t
))
.set_source(Some(Arc::new(err)))
.set_kind(crate::error::ErrorKind::Bug)
})?;
fn try_from(t: &RawValue) -> std::result::Result<Self, Self::Error> {
let res: (String, Self, String) = deserialize_from_str(t.get())?;
assert_eq!(&res.0, &format!("{}/changes", OBJ::NAME));
Ok(res.1)
}
@ -911,16 +888,9 @@ pub struct SetResponse<OBJ: Object> {
impl<OBJ: Object + DeserializeOwned> std::convert::TryFrom<&RawValue> for SetResponse<OBJ> {
type Error = crate::error::Error;
fn try_from(t: &RawValue) -> Result<Self, crate::error::Error> {
let res: (String, Self, String) = serde_json::from_str(t.get()).map_err(|err| {
crate::error::Error::new(format!(
"BUG: Could not deserialize server JSON response properly, please report \
this!\nReply from server: {}",
&t
))
.set_source(Some(Arc::new(err)))
.set_kind(crate::error::ErrorKind::Bug)
})?;
let res: (String, Self, String) = deserialize_from_str(t.get())?;
assert_eq!(&res.0, &format!("{}/set", OBJ::NAME));
Ok(res.1)
}
@ -1038,6 +1008,16 @@ pub fn download_request_format(
} else if download_url[prev_pos..].starts_with("{name}") {
ret.push_str(name.as_deref().unwrap_or(""));
prev_pos += "{name}".len();
} else if download_url[prev_pos..].starts_with("{type}") {
ret.push_str("application/octet-stream");
prev_pos += "{name}".len();
} else {
// [ref:FIXME]: return protocol error here
log::error!(
"BUG: unknown parameter in download url: {}",
&download_url[prev_pos..]
);
break;
}
}
if prev_pos != download_url.len() {