feat: allow setting sent date on APPEND (#174)

Fixes #60
This commit is contained in:
Michael Bøcker-Larsen 2020-12-16 00:25:38 +08:00 committed by GitHub
parent 8be583a9f7
commit ee56c8e42b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 1 deletions

View file

@ -1,5 +1,6 @@
use base64; use base64;
use bufstream::BufStream; use bufstream::BufStream;
use chrono::{DateTime, FixedOffset};
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
use native_tls::{TlsConnector, TlsStream}; use native_tls::{TlsConnector, TlsStream};
use nom; use nom;
@ -1096,6 +1097,32 @@ impl<T: Read + Write> Session<T> {
mailbox: S, mailbox: S,
content: B, content: B,
flags: &[Flag<'_>], flags: &[Flag<'_>],
) -> Result<()> {
self.append_with_flags_and_date(mailbox, content, flags, None)
}
/// The [`APPEND` command](https://tools.ietf.org/html/rfc3501#section-6.3.11) can take
/// an optional FLAGS parameter to set the flags on the new message.
///
/// > If a flag parenthesized list is specified, the flags SHOULD be set
/// > in the resulting message; otherwise, the flag list of the
/// > resulting message is set to empty by default. In either case, the
/// > Recent flag is also set.
///
/// The [`\Recent` flag](https://tools.ietf.org/html/rfc3501#section-2.3.2) is not
/// allowed as an argument to `APPEND` and will be filtered out if present in `flags`.
///
/// Pass a date in order to set the date that the message was originally sent.
///
/// > If a date-time is specified, the internal date SHOULD be set in
/// > the resulting message; otherwise, the internal date of the
/// > resulting message is set to the current date and time by default.
pub fn append_with_flags_and_date<S: AsRef<str>, B: AsRef<[u8]>>(
&mut self,
mailbox: S,
content: B,
flags: &[Flag<'_>],
date: impl Into<Option<DateTime<FixedOffset>>>,
) -> Result<()> { ) -> Result<()> {
let content = content.as_ref(); let content = content.as_ref();
let flagstr = flags let flagstr = flags
@ -1104,11 +1131,16 @@ impl<T: Read + Write> Session<T> {
.map(|f| f.to_string()) .map(|f| f.to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" "); .join(" ");
let datestr = match date.into() {
Some(date) => format!(" \"{}\"", date.format("%d-%h-%Y %T %z")),
None => "".to_string(),
};
self.run_command(&format!( self.run_command(&format!(
"APPEND \"{}\" ({}) {{{}}}", "APPEND \"{}\" ({}){} {{{}}}",
mailbox.as_ref(), mailbox.as_ref(),
flagstr, flagstr,
datestr,
content.len() content.len()
))?; ))?;
let mut v = Vec::new(); let mut v = Vec::new();

View file

@ -1,8 +1,10 @@
extern crate chrono;
extern crate imap; extern crate imap;
extern crate lettre; extern crate lettre;
extern crate lettre_email; extern crate lettre_email;
extern crate native_tls; extern crate native_tls;
use chrono::{FixedOffset, TimeZone};
use lettre::Transport; use lettre::Transport;
use std::net::TcpStream; use std::net::TcpStream;
@ -330,3 +332,54 @@ fn append_with_flags() {
let inbox = c.search("ALL").unwrap(); let inbox = c.search("ALL").unwrap();
assert_eq!(inbox.len(), 0); assert_eq!(inbox.len(), 0);
} }
#[test]
fn append_with_flags_and_date() {
use imap::types::Flag;
let to = "inbox-append3@localhost";
// make a message to append
let e: lettre::SendableEmail = lettre_email::Email::builder()
.from("sender@localhost")
.to(to)
.subject("My third e-mail")
.text("Hello world")
.build()
.unwrap()
.into();
// connect
let mut c = session(to);
let mbox = "INBOX";
c.select(mbox).unwrap();
// append
let flags: &[Flag] = &[Flag::Seen, Flag::Flagged];
let date = FixedOffset::east(8 * 3600)
.ymd(2020, 12, 13)
.and_hms(13, 36, 36);
c.append_with_flags_and_date(mbox, e.message_to_string().unwrap(), flags, Some(date))
.unwrap();
// now we should see the e-mail!
let inbox = c.uid_search("ALL").unwrap();
// and the one message should have the first message sequence number
assert_eq!(inbox.len(), 1);
let uid = inbox.into_iter().next().unwrap();
// fetch the e-mail
let fetch = c.uid_fetch(format!("{}", uid), "(ALL UID)").unwrap();
assert_eq!(fetch.len(), 1);
let fetch = &fetch[0];
assert_eq!(fetch.uid, Some(uid));
assert_eq!(fetch.internal_date(), Some(date));
// and let's delete it to clean up
c.uid_store(format!("{}", uid), "+FLAGS (\\Deleted)")
.unwrap();
c.expunge().unwrap();
// the e-mail should be gone now
let inbox = c.search("ALL").unwrap();
assert_eq!(inbox.len(), 0);
}