From 4050f6893f8fe736eed974a00894499c4b985cb4 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 9 Jan 2021 16:37:29 +0200 Subject: [PATCH] melib/mbox: add MboxFormat::append() method Add support for writing mbox files --- melib/src/backends/mbox.rs | 16 +++-- melib/src/backends/mbox/write.rs | 112 +++++++++++++++++++++++++++++++ melib/src/datetime.rs | 2 + 3 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 melib/src/backends/mbox/write.rs diff --git a/melib/src/backends/mbox.rs b/melib/src/backends/mbox.rs index 3955986a4..9311976f2 100644 --- a/melib/src/backends/mbox.rs +++ b/melib/src/backends/mbox.rs @@ -48,6 +48,8 @@ use std::str::FromStr; use std::sync::mpsc::channel; use std::sync::{Arc, Mutex, RwLock}; +pub mod write; + type Offset = usize; type Length = usize; @@ -323,7 +325,7 @@ macro_rules! find_From__line { } impl MboxFormat { - fn parse<'i>(&self, input: &'i [u8]) -> IResult<&'i [u8], Envelope> { + pub fn parse<'i>(&self, input: &'i [u8]) -> IResult<&'i [u8], Envelope> { let orig_input = input; let mut input = input; match self { @@ -649,12 +651,12 @@ pub fn mbox_parse( Ok((&[], envelopes)) } -struct MessageIterator<'a> { - index: Arc>>, - input: &'a [u8], - file_offset: usize, - offset: usize, - format: Option, +pub struct MessageIterator<'a> { + pub index: Arc>>, + pub input: &'a [u8], + pub file_offset: usize, + pub offset: usize, + pub format: Option, } impl<'a> Iterator for MessageIterator<'a> { diff --git a/melib/src/backends/mbox/write.rs b/melib/src/backends/mbox/write.rs new file mode 100644 index 000000000..772c70dfc --- /dev/null +++ b/melib/src/backends/mbox/write.rs @@ -0,0 +1,112 @@ +/* + * meli - mailbox module. + * + * Copyright 2021 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 . + */ + +use super::*; + +impl MboxFormat { + pub fn append( + &self, + writer: &mut dyn std::io::Write, + input: &[u8], + envelope_from: Option<&Address>, + delivery_date: Option, + is_empty: bool, + crlf: bool, + ) -> Result<()> { + let line_ending: &'static [u8] = if crlf { &b"\r\n"[..] } else { &b"\n"[..] }; + if !is_empty { + writer.write_all(line_ending)?; + writer.write_all(line_ending)?; + } + writer.write_all(&b"From "[..])?; + if let Some(from) = envelope_from { + writer.write_all(from.address_spec_raw())?; + } else { + writer.write_all(&b"MAILER-DAEMON"[..])?; + } + writer.write_all(&b" "[..])?; + writer.write_all( + crate::datetime::timestamp_to_string( + delivery_date.unwrap_or_else(|| crate::datetime::now()), + Some(crate::datetime::ASCTIME_FMT), + true, + ) + .trim() + .as_bytes(), + )?; + writer.write_all(line_ending)?; + let (mut headers, body) = parser::mail(input)?; + match self { + MboxFormat::MboxO | MboxFormat::MboxRd => Err(MeliError::new("Unimplemented.")), + MboxFormat::MboxCl => { + headers.retain(|(header_name, _)| { + !header_name.eq_ignore_ascii_case(b"Content-Length") + }); + let len = (body.len() + + body + .windows(b"\nFrom ".len()) + .filter(|w| w == b"\nFrom ") + .count() + + if body.starts_with(b"From ") { 1 } else { 0 }) + .to_string(); + for (h, v) in headers + .into_iter() + .chain(Some((&b"Content-Length"[..], len.as_bytes()))) + { + writer.write_all(h)?; + writer.write_all(&b": "[..])?; + writer.write_all(v)?; + writer.write_all(line_ending)?; + } + writer.write_all(line_ending)?; + + if body.starts_with(b"From ") { + writer.write_all(&[b'>'])?; + } + for i in 0..body.len() { + writer.write_all(&[body[i]])?; + if body[i..].starts_with(b"\nFrom ") { + writer.write_all(&[b'>'])?; + } + } + Ok(()) + } + MboxFormat::MboxCl2 => { + headers.retain(|(header_name, _)| { + !header_name.eq_ignore_ascii_case(b"Content-Length") + }); + let len = body.len().to_string(); + for (h, v) in headers + .into_iter() + .chain(Some((&b"Content-Length"[..], len.as_bytes()))) + { + writer.write_all(h)?; + writer.write_all(&b": "[..])?; + writer.write_all(v)?; + writer.write_all(line_ending)?; + } + writer.write_all(line_ending)?; + writer.write_all(body)?; + Ok(()) + } + } + } +} diff --git a/melib/src/datetime.rs b/melib/src/datetime.rs index 4e605376c..47799ee60 100644 --- a/melib/src/datetime.rs +++ b/melib/src/datetime.rs @@ -48,6 +48,8 @@ pub const RFC3339_FMT: &str = "%Y-%m-%d\0"; pub const RFC822_FMT_WITH_TIME: &str = "%a, %e %h %Y %H:%M:%S \0"; pub const RFC822_FMT: &str = "%e %h %Y %H:%M:%S \0"; pub const DEFAULT_FMT: &str = "%a, %d %b %Y %R\0"; +//"Tue May 21 13:46:22 1991\n" +pub const ASCTIME_FMT: &str = "%a %b %d %H:%M:%S %Y\n\0"; extern "C" { fn strptime(