diff --git a/melib/src/email.rs b/melib/src/email.rs index ca69f928..39a4439e 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -376,16 +376,18 @@ impl Envelope { let _strings: Vec = self.to.iter().map(|a| format!("{}", a)).collect(); _strings.join(", ") } - pub fn bytes(&self, mut operation: Box) -> Vec { - operation - .as_bytes() - .map(|v| v.into()) - .unwrap_or_else(|_| Vec::new()) + + /// Requests bytes from backend and thus can fail + pub fn bytes(&self, mut operation: Box) -> Result> { + operation.as_bytes().map(|v| v.to_vec()) } + pub fn body_bytes(&self, bytes: &[u8]) -> Attachment { let builder = AttachmentBuilder::new(bytes); builder.build() } + + /// Requests bytes from backend and thus can fail pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result> { let ret = parser::headers(bytes).to_full_result()?; let len = ret.len(); @@ -397,34 +399,14 @@ impl Envelope { }) }) } - pub fn body(&self, mut operation: Box) -> Attachment { + + /// Requests bytes from backend and thus can fail + pub fn body(&self, mut operation: Box) -> Result { debug!("searching body for {:?}", self.message_id_display()); - let file = operation.as_bytes(); - self.body_bytes(file.unwrap()) - /* - let (headers, body) = match parser::mail(file.unwrap()).to_full_result() { - Ok(v) => v, - Err(_) => { - debug!("2error in parsing mail\n"); - let error_msg = b"Mail cannot be shown because of errors."; - let mut builder = AttachmentBuilder::new(error_msg); - return builder.build(); - } - }; - let mut builder = AttachmentBuilder::new(body); - for (name, value) in headers { - if value.len() == 1 && value.is_empty() { - continue; - } - if name.eq_ignore_ascii_case(b"content-transfer-encoding") { - builder.content_transfer_encoding(value); - } else if name.eq_ignore_ascii_case(b"content-type") { - builder.content_type(value); - } - } - builder.build() - */ + let file = operation.as_bytes()?; + Ok(self.body_bytes(file)) } + pub fn subject(&self) -> Cow { match self.subject { Some(ref s) => String::from_utf8_lossy(s), diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index 29a62e45..17b6c16a 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -113,7 +113,7 @@ impl str::FromStr for Draft { } impl Draft { - pub fn edit(envelope: &Envelope, mut op: Box) -> Self { + pub fn edit(envelope: &Envelope, mut op: Box) -> Result { let mut ret = Draft::default(); //TODO: Inform user if error { @@ -128,10 +128,11 @@ impl Draft { } } - ret.body = envelope.body(op).text(); + ret.body = envelope.body(op)?.text(); - ret + Ok(ret) } + pub fn set_header(&mut self, header: &str, value: String) { if self.headers.insert(header.to_string(), value).is_none() { self.header_order.push(header.to_string()); diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 3f6da8eb..6c268772 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -128,15 +128,15 @@ impl Composer { * msg: index of message we reply to in thread_nodes * context: current context */ - pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Self { + pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Result { let mut ret = Composer::default(); let op = context.accounts[account_pos].operation(h); let envelope: &Envelope = context.accounts[account_pos].get_env(&h); - ret.draft = Draft::edit(envelope, op); + ret.draft = Draft::edit(envelope, op)?; ret.account_cursor = account_pos; - ret + Ok(ret) } pub fn with_context( coordinates: (usize, usize, usize), diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index ecbec4ad..3aef9145 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -352,6 +352,7 @@ impl ListingTrait for CompactListing { } self.filter_term.clear(); + let mut error: Option<(EnvelopeHash, MeliError)> = None; for (i, h) in self.order.keys().enumerate() { let account = &context.accounts[self.cursor_pos.0]; let envelope = &account.collection[h]; @@ -367,8 +368,14 @@ impl ListingTrait for CompactListing { self.selection.insert(*h, false); continue; } - let op = account.operation(*h); - let body = envelope.body(op); + let op = account.operation(env_hash); + let body = match envelope.body(op) { + Ok(b) => b, + Err(e) => { + error = Some((env_hash, e)); + break; + } + }; let decoded = decode_rec(&body, None); let body_text = String::from_utf8_lossy(&decoded); if body_text.contains(&filter_term) { @@ -377,22 +384,45 @@ impl ListingTrait for CompactListing { self.selection.insert(*h, false); } } - if !self.filtered_selection.is_empty() { - self.filter_term = filter_term.to_string(); - self.cursor_pos.2 = std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2); - self.length = self.filtered_selection.len(); - } else { + + if let Some((env_hash, error)) = error { self.length = 0; - let message = format!("No results for `{}`.", filter_term); - self.data_columns.columns[0] = - CellBuffer::new(message.len(), self.length + 1, Cell::with_char(' ')); + let message = format!("Error: {}", error.to_string()); + log( + format!( + "Failed to open envelope {}: {}", + context.accounts[self.new_cursor_pos.0] + .get_env(&env_hash) + .message_id_display(), + error.to_string() + ), + ERROR, + ); + self.data_columns.columns[0] = CellBuffer::new(message.len(), 1, Cell::with_char(' ')); write_string_to_grid( &message, &mut self.data_columns.columns[0], Color::Default, Color::Default, Attr::Default, - ((0, 0), (MAX_COLS - 1, 0)), + ((0, 0), (message.len() - 1, 0)), + false, + ); + } else if !self.filtered_selection.is_empty() { + self.filter_term = filter_term.to_string(); + self.cursor_pos.2 = std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2); + self.length = self.filtered_selection.len(); + } else { + self.length = 0; + let message = format!("No results for `{}`.", filter_term); + self.data_columns.columns[0] = CellBuffer::new(message.len(), 1, Cell::with_char(' ')); + write_string_to_grid( + &message, + &mut self.data_columns.columns[0], + Color::Default, + Color::Default, + Attr::Default, + ((0, 0), (message.len() - 1, 0)), false, ); } diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs index 716f0882..f40c3583 100644 --- a/ui/src/components/mail/listing/conversations.rs +++ b/ui/src/components/mail/listing/conversations.rs @@ -417,7 +417,13 @@ impl ListingTrait for ConversationsListing { continue; } let op = account.operation(env_hash); - let body = envelope.body(op); + let body = match envelope.body(op) { + Ok(b) => b, + Err(e) => { + error = Some(e); + break; + } + }; let decoded = decode_rec(&body, None); let body_text = String::from_utf8_lossy(&decoded); if body_text.contains(&filter_term) { diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index cd256f6c..529d8799 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -597,7 +597,25 @@ impl Component for MailView { let account = &mut context.accounts[self.coordinates.0]; let envelope: &Envelope = &account.get_env(&self.coordinates.2); let op = account.operation(envelope.hash()); - envelope.body(op) + match envelope.body(op) { + Ok(body) => body, + Err(e) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + e.to_string(), + Some(NotificationType::ERROR), + )); + log( + format!( + "Failed to open envelope {}: {}", + envelope.message_id_display(), + e.to_string() + ), + ERROR, + ); + return; + } + } }; match self.mode { ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => { @@ -799,7 +817,27 @@ impl Component for MailView { let account = &mut context.accounts[self.coordinates.0]; let envelope: &Envelope = &account.get_env(&self.coordinates.2); let op = account.operation(envelope.hash()); - if let Some(u) = envelope.body(op).attachments().get(lidx) { + + let attachments = match envelope.body(op) { + Ok(body) => body.attachments(), + Err(e) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + e.to_string(), + Some(NotificationType::ERROR), + )); + log( + format!( + "Failed to open envelope {}: {}", + envelope.message_id_display(), + e.to_string() + ), + ERROR, + ); + return true; + } + }; + if let Some(u) = attachments.get(lidx) { match u.content_type() { ContentType::MessageRfc822 => { match EnvelopeWrapper::new(u.body().to_vec()) { @@ -910,7 +948,25 @@ impl Component for MailView { let envelope: &Envelope = &account.get_env(&self.coordinates.2); let finder = LinkFinder::new(); let op = account.operation(envelope.hash()); - let t = envelope.body(op).text().to_string(); + let t = match envelope.body(op) { + Ok(body) => body.text().to_string(), + Err(e) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + e.to_string(), + Some(NotificationType::ERROR), + )); + log( + format!( + "Failed to open envelope {}: {}", + envelope.message_id_display(), + e.to_string() + ), + ERROR, + ); + return true; + } + }; let links: Vec = finder.links(&t).collect(); if let Some(u) = links.get(lidx) { u.as_str().to_string() diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index 8fa94552..02711fbe 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -1455,7 +1455,28 @@ impl Component for Tabbed { return true; } UIEvent::Action(Tab(Edit(account_pos, msg))) => { - self.add_component(Box::new(Composer::edit(account_pos, msg, context))); + let composer = match Composer::edit(account_pos, msg, context) { + Ok(c) => c, + Err(e) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + e.to_string(), + Some(NotificationType::ERROR), + )); + log( + format!( + "Failed to open envelope {}: {}", + context.accounts[account_pos] + .get_env(&msg) + .message_id_display(), + e.to_string() + ), + ERROR, + ); + return true; + } + }; + self.add_component(Box::new(composer)); self.cursor_pos = self.children.len() - 1; self.children[self.cursor_pos].set_dirty(); return true;