From 8005cdaf8c57a745eae4ca27c6ec07dafd951322 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 2 Jun 2024 14:37:54 +0300 Subject: [PATCH] melib/jmap: fastmail downloadUrl bug fix WIP Signed-off-by: Manos Pitsidianakis --- melib/src/jmap/methods.rs | 13 ++++++++--- melib/src/jmap/mod.rs | 44 ++++++++++++++++++++++++++++++------ melib/src/jmap/operations.rs | 1 + 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/melib/src/jmap/methods.rs b/melib/src/jmap/methods.rs index 6ec49049..ab9f497d 100644 --- a/melib/src/jmap/methods.rs +++ b/melib/src/jmap/methods.rs @@ -826,7 +826,7 @@ pub fn download_request_format( download_url: &RequestUrlTemplate, account_id: &Id, blob_id: &Id, - name: Option, + name: Option<&str>, ) -> Result { let mut ret = String::with_capacity( download_url.text.len() @@ -846,11 +846,18 @@ pub fn download_request_format( ret.push_str(blob_id.as_str()); prev_pos += "{blobId}".len(); } else if download_url.text[prev_pos..].starts_with("{name}") { - ret.push_str(name.as_deref().unwrap_or("")); + if let Some(name) = name.as_deref() { + ret.push_str(name); + //} else if ret.ends_with('/') { + // ret.pop(); + //} + } else { + ret.push_str("{name}"); + } prev_pos += "{name}".len(); } else if download_url.text[prev_pos..].starts_with("{type}") { ret.push_str("application/octet-stream"); - prev_pos += "{name}".len(); + prev_pos += "{type}".len(); } else { log::error!( "BUG: unknown parameter in download_url: {}", diff --git a/melib/src/jmap/mod.rs b/melib/src/jmap/mod.rs index 47e79f29..b4a359f6 100644 --- a/melib/src/jmap/mod.rs +++ b/melib/src/jmap/mod.rs @@ -279,6 +279,7 @@ pub struct Store { pub id_store: Arc>>>, pub reverse_id_store: Arc, EnvelopeHash>>>, pub blob_id_store: Arc>>>, + pub name_store: Arc>>>, pub collection: Collection, pub mailboxes: Arc>>, pub mailboxes_index: Arc>>>, @@ -293,9 +294,38 @@ impl Store { pub async fn add_envelope(&self, obj: email::EmailObject) -> Envelope { let mut flags = Flag::default(); let mut labels: IndexSet = IndexSet::new(); - let id; - let mailbox_ids; - let blob_id; + let id = obj.id.clone(); + let mailbox_ids = obj.mailbox_ids.clone(); + let blob_id = obj.blob_id.clone(); + // Source: https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.4 + // name: "String|null" + // This is the decoded "filename" parameter of the Content- + // Disposition header field per [RFC2231], or (for compatibility with + // existing systems) if not present, then it's the decoded "name" + // parameter of the Content-Type header field per [RFC2047]. + let name_fallback = |obj: &email::EmailObject| -> Box { + if let Some(val) = obj.headers.get(HeaderName::CONTENT_TYPE) { + val.to_string().into() + } else { + "text/plain".to_string().into() + } + }; + let name = if let Some(val) = obj.headers.get(HeaderName::CONTENT_DISPOSITION) { + use crate::email::attachment_types::ContentDisposition; + let mut cp = ContentDisposition::from(val); + if cp.kind.is_attachment() { + if let Some(fname) = cp.filename.take() { + fname.into() + } else { + name_fallback(&obj) + } + } else { + name_fallback(&obj) + } + } else { + name_fallback(&obj) + }; + { let mut tag_lck = self.collection.tag_index.write().unwrap(); for t in obj.keywords().keys() { @@ -320,11 +350,8 @@ impl Store { } } } - - id = obj.id.clone(); - mailbox_ids = obj.mailbox_ids.clone(); - blob_id = obj.blob_id.clone(); } + let mut ret: Envelope = obj.into(); ret.set_flags(flags); ret.tags_mut().extend(labels); @@ -332,6 +359,7 @@ impl Store { let mut id_store_lck = self.id_store.lock().await; let mut reverse_id_store_lck = self.reverse_id_store.lock().await; let mut blob_id_store_lck = self.blob_id_store.lock().await; + let mut name_store_lck = self.name_store.lock().await; let mailboxes_lck = self.mailboxes.read().unwrap(); let mut mailboxes_index_lck = self.mailboxes_index.write().unwrap(); for (mailbox_id, _) in mailbox_ids { @@ -346,6 +374,7 @@ impl Store { reverse_id_store_lck.insert(id.clone(), ret.hash()); id_store_lck.insert(ret.hash(), id); blob_id_store_lck.insert(ret.hash(), blob_id); + name_store_lck.insert(ret.hash(), name); ret } @@ -1217,6 +1246,7 @@ impl JmapType { id_store: Default::default(), reverse_id_store: Default::default(), blob_id_store: Default::default(), + name_store: Default::default(), mailboxes: Default::default(), mailboxes_index: Default::default(), mailbox_state: Default::default(), diff --git a/melib/src/jmap/operations.rs b/melib/src/jmap/operations.rs index 80b1491b..2077d802 100644 --- a/melib/src/jmap/operations.rs +++ b/melib/src/jmap/operations.rs @@ -64,6 +64,7 @@ impl BackendOp for JmapOp { } } let blob_id = store.blob_id_store.lock().await[&hash].clone(); + let name = store.name_store.lock().await[&hash].clone(); let mut conn = connection.lock().await; conn.connect().await?; let (download_url, mail_account_id) = {