diff --git a/server/src/bin/server.rs b/server/src/bin/server.rs index 2dc9cc3..cb2360b 100644 --- a/server/src/bin/server.rs +++ b/server/src/bin/server.rs @@ -91,23 +91,24 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for InlineAttachmentResponder { } } -struct DownloadPartResponder { - bytes: Vec, - filename: Option, -} +struct DownloadAttachmentResponder(Attachment); -impl<'r, 'o: 'r> Responder<'r, 'o> for DownloadPartResponder { +impl<'r, 'o: 'r> Responder<'r, 'o> for DownloadAttachmentResponder { fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> { let mut resp = Response::build(); - if let Some(filename) = self.filename { + if let Some(filename) = self.0.filename { info!("filename {:?}", filename); resp.header(Header::new( "Content-Disposition", format!(r#"attachment; filename="{}""#, filename), - )) - .header(ContentType::Binary); + )); } - resp.sized_body(self.bytes.len(), Cursor::new(self.bytes)) + if let Some(content_type) = self.0.content_type { + if let Some(ct) = ContentType::parse_flexible(&content_type) { + resp.header(ct); + } + } + resp.sized_body(self.0.bytes.len(), Cursor::new(self.0.bytes)) .ok() } } @@ -129,7 +130,6 @@ async fn view_attachment( .map(|s| s.parse().expect("not a usize")) .collect(); let attachment = attachment_bytes(nm, &mid, &idx)?; - // TODO: plumb Content-Type, or just create wrappers for serving the Attachment type Ok(InlineAttachmentResponder(attachment)) } @@ -138,7 +138,7 @@ async fn download_attachment( nm: &State, id: &str, idx: &str, -) -> Result> { +) -> Result> { let mid = if id.starts_with("id:") { id.to_string() } else { @@ -150,30 +150,7 @@ async fn download_attachment( .map(|s| s.parse().expect("not a usize")) .collect(); let attachment = attachment_bytes(nm, &mid, &idx)?; - // TODO(wathiede): use walk_attachments from graphql to fill this out - Ok(DownloadPartResponder { - bytes: attachment.bytes, - filename: attachment.filename, - }) -} - -#[get("/original//part/")] -async fn original_part( - nm: &State, - id: &str, - part: usize, -) -> Result> { - let mid = if id.starts_with("id:") { - id.to_string() - } else { - format!("id:{}", id) - }; - let meta = nm.show_part(&mid, part)?; - let res = nm.show_original_part(&mid, part)?; - Ok(DownloadPartResponder { - bytes: res, - filename: meta.filename, - }) + Ok(DownloadAttachmentResponder(attachment)) } #[get("/original/")] @@ -240,7 +217,6 @@ async fn main() -> Result<(), Box> { .mount( "/", routes![ - original_part, original, refresh, search_all, diff --git a/server/src/graphql.rs b/server/src/graphql.rs index c0c79f9..e3d855c 100644 --- a/server/src/graphql.rs +++ b/server/src/graphql.rs @@ -86,8 +86,10 @@ pub struct Message { // X-Attachment-Id: f_lponoluo1 #[derive(Default, Debug, SimpleObject)] pub struct Attachment { + pub id: String, + pub idx: String, pub filename: Option, - pub size: Option, + pub size: usize, pub content_type: Option, pub content_id: Option, pub disposition: DispositionType, @@ -375,7 +377,7 @@ impl QueryRoot { }) .collect(); // TODO(wathiede): parse message and fill out attachments - let attachments = extract_attachments(&m)?; + let attachments = extract_attachments(&m, &id)?; messages.push(Message { id, from, @@ -656,17 +658,17 @@ fn walk_attachments Option>( // TODO(wathiede): make this walk_attachments that takes a closure. // Then implement one closure for building `Attachment` and imlement another that can be used to // get the bytes for serving attachments of HTTP -fn extract_attachments(m: &ParsedMail) -> Result, Error> { +fn extract_attachments(m: &ParsedMail, id: &str) -> Result, Error> { let mut attachments = Vec::new(); - for sp in &m.subparts { - if let Some(attachment) = extract_attachment(sp) { + for (idx, sp) in m.subparts.iter().enumerate() { + if let Some(attachment) = extract_attachment(sp, id, &[idx]) { attachments.push(attachment); } } Ok(attachments) } -fn extract_attachment(m: &ParsedMail) -> Option { +fn extract_attachment(m: &ParsedMail, id: &str, idx: &[usize]) -> Option { let pcd = m.get_content_disposition(); // TODO: do we need to handle empty filename attachments, or should we change the definition of // Attachment::filename? @@ -684,12 +686,15 @@ fn extract_attachment(m: &ParsedMail) -> Option { } }; return Some(Attachment { + id: id.to_string(), + idx: idx + .iter() + .map(|i| i.to_string()) + .collect::>() + .join("."), disposition: pcd.disposition.into(), filename: Some(filename), - size: pcd - .params - .get("size") - .map(|s| s.parse().unwrap_or_default()), + size: bytes.len(), // TODO: what is the default for ctype? // TODO: do we want to use m.ctype.params for anything? content_type: Some(m.ctype.mimetype.clone()), @@ -841,7 +846,7 @@ pub fn attachment_bytes(nm: &Notmuch, id: &str, idx: &[usize]) -> Result