Merge pull request #232 from bitfehler/bitfehler/append-uid

Add support for APPENDUID response data
This commit is contained in:
Jon Gjengset 2022-08-10 22:21:56 -04:00 committed by GitHub
commit 8c2250297a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 2 deletions

View file

@ -215,7 +215,7 @@ impl<'a, T: Read + Write> AppendCmd<'a, T> {
/// ///
/// Note: be sure to set flags and optional date before you /// Note: be sure to set flags and optional date before you
/// finish the command. /// finish the command.
pub fn finish(&mut self) -> Result<()> { pub fn finish(&mut self) -> Result<Appended> {
let flagstr = self let flagstr = self
.flags .flags
.clone() .clone()
@ -246,7 +246,9 @@ impl<'a, T: Read + Write> AppendCmd<'a, T> {
self.session.stream.write_all(self.content)?; self.session.stream.write_all(self.content)?;
self.session.stream.write_all(b"\r\n")?; self.session.stream.write_all(b"\r\n")?;
self.session.stream.flush()?; self.session.stream.flush()?;
self.session.read_response().map(|_| ()) self.session
.read_response()
.and_then(|(lines, _)| parse_append(&lines, &mut self.session.unsolicited_responses_tx))
} }
} }

View file

@ -124,6 +124,40 @@ pub fn parse_expunge(
} }
} }
pub fn parse_append(
mut lines: &[u8],
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
) -> Result<Appended> {
let mut appended = Appended::default();
loop {
match imap_proto::parser::parse_response(lines) {
Ok((rest, Response::Done { status, code, .. })) => {
lines = rest;
assert_eq!(status, imap_proto::Status::Ok);
if let Some(ResponseCode::AppendUid(validity, uids)) = code {
appended.uid_validity = Some(validity);
appended.uids = Some(uids);
}
}
Ok((rest, data)) => {
lines = rest;
if let Some(resp) = try_handle_unilateral(data, unsolicited) {
break Err(resp.into());
}
}
_ => {
return Err(Error::Parse(ParseError::Invalid(lines.to_vec())));
}
}
if lines.is_empty() {
break Ok(appended);
}
}
}
pub fn parse_noop( pub fn parse_noop(
lines: Vec<u8>, lines: Vec<u8>,
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>, unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
@ -698,4 +732,48 @@ mod tests {
assert_eq!(del.next(), Some(12)); assert_eq!(del.next(), Some(12));
assert_eq!(del.next(), None); assert_eq!(del.next(), None);
} }
#[test]
fn parse_append_uid() {
// If the user has enabled UIDPLUS (RFC 4315), the response contains an APPENDUID
// response code followed by the UIDVALIDITY of the destination mailbox and the
// UID assigned to the appended message in the destination mailbox.
// If the MULTIAPPEND extension is also used, there can be multiple UIDs.
let lines = b"A003 OK [APPENDUID 38505 3955] APPEND completed\r\n";
let (mut send, recv) = mpsc::channel();
let resp = parse_append(lines, &mut send).unwrap();
assert!(recv.try_recv().is_err());
assert_eq!(resp.uid_validity, Some(38505));
match resp.uids {
Some(uid_list) => {
let mut it = uid_list.iter();
assert_eq!(it.next(), Some(&UidSetMember::Uid(3955)));
assert_eq!(it.next(), None);
}
None => panic!("Missing UIDs in APPEND response"),
};
}
#[test]
fn parse_multiappend_uid() {
// If the user has enabled UIDPLUS (RFC 4315), the response contains an APPENDUID
// response code followed by the UIDVALIDITY of the destination mailbox and the
// UID assigned to the appended message in the destination mailbox.
// If the MULTIAPPEND extension is also used, there can be multiple UIDs.
let lines = b"A003 OK [APPENDUID 38505 3955:3957] APPEND completed\r\n";
let (mut send, recv) = mpsc::channel();
let resp = parse_append(lines, &mut send).unwrap();
assert!(recv.try_recv().is_err());
assert_eq!(resp.uid_validity, Some(38505));
match resp.uids {
Some(uid_list) => {
let mut it = uid_list.iter();
assert_eq!(it.next(), Some(&UidSetMember::UidRange(3955..=3957)));
assert_eq!(it.next(), None);
}
None => panic!("Missing UIDs in APPEND response"),
};
}
} }

29
src/types/appended.rs Normal file
View file

@ -0,0 +1,29 @@
use imap_proto::UidSetMember;
/// Meta-information about a message, as returned by
/// [`APPEND`](https://tools.ietf.org/html/rfc3501#section-6.3.11).
/// Note that `APPEND` only returns any data if certain extensions are enabled,
/// for example [`UIDPLUS`](https://tools.ietf.org/html/rfc4315).
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct Appended {
/// The unique identifier validity value of the mailbox that the message was appended to.
/// See [`Uid`] for more details. Only present if server supports [`UIDPLUS`](https://tools.ietf.org/html/rfc4315).
pub uid_validity: Option<u32>,
/// The unique identifier value of the messages that were appended.
/// Only present if server supports [`UIDPLUS`](https://tools.ietf.org/html/rfc4315).
/// Contains only a single value unless the [`MULTIAPPEND`](https://tools.ietf.org/html/rfc3502) extension
/// was used to upload multiple messages.
pub uids: Option<Vec<UidSetMember>>,
}
#[allow(clippy::derivable_impls)]
impl Default for Appended {
fn default() -> Appended {
Appended {
uid_validity: None,
uids: None,
}
}
}

View file

@ -123,3 +123,6 @@ pub use self::deleted::Deleted;
mod unsolicited_response; mod unsolicited_response;
pub use self::unsolicited_response::{AttributeValue, UnsolicitedResponse}; pub use self::unsolicited_response::{AttributeValue, UnsolicitedResponse};
mod appended;
pub use self::appended::Appended;