Skip to content

Commit 870eb74

Browse files
committed
fix: correctly parse unsolicited APPEND responses
1 parent b2ab561 commit 870eb74

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

src/client.rs

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,24 +1122,27 @@ impl<T: Read + Write + Unpin + fmt::Debug + Send> Session<T> {
11221122
content: impl AsRef<[u8]>,
11231123
) -> Result<()> {
11241124
let content = content.as_ref();
1125-
self.run_command(&format!(
1126-
"APPEND \"{}\"{}{}{}{} {{{}}}",
1127-
mailbox.as_ref(),
1128-
if flags.is_some() { " " } else { "" },
1129-
flags.unwrap_or(""),
1130-
if internaldate.is_some() { " " } else { "" },
1131-
internaldate.unwrap_or(""),
1132-
content.len()
1133-
))
1134-
.await?;
1125+
let id = self
1126+
.run_command(&format!(
1127+
"APPEND \"{}\"{}{}{}{} {{{}}}",
1128+
mailbox.as_ref(),
1129+
if flags.is_some() { " " } else { "" },
1130+
flags.unwrap_or(""),
1131+
if internaldate.is_some() { " " } else { "" },
1132+
internaldate.unwrap_or(""),
1133+
content.len()
1134+
))
1135+
.await?;
11351136

11361137
match self.read_response().await {
11371138
Some(Ok(res)) => {
11381139
if let Response::Continue { .. } = res.parsed() {
11391140
self.stream.as_mut().write_all(content).await?;
11401141
self.stream.as_mut().write_all(b"\r\n").await?;
11411142
self.stream.flush().await?;
1142-
self.read_response().await.transpose()?;
1143+
self.conn
1144+
.check_done_ok(&id, Some(self.unsolicited_responses_tx.clone()))
1145+
.await?;
11431146
Ok(())
11441147
} else {
11451148
Err(Error::Append)
@@ -2352,6 +2355,67 @@ mod tests {
23522355
}
23532356
}
23542357

2358+
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
2359+
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
2360+
async fn append() {
2361+
{
2362+
// APPEND command when INBOX is *not* selected.
2363+
//
2364+
// Only APPENDUID response is returned.
2365+
let response = b"+ OK\r\nA0001 OK [APPENDUID 1725735035 2] Append completed (0.052 + 12.097 + 0.049 secs).\r\n".to_vec();
2366+
2367+
let mock_stream = MockStream::new(response);
2368+
let mut session = mock_session!(mock_stream);
2369+
session
2370+
.append("INBOX", Some(r"(\Seen)"), None, "foobarbaz")
2371+
.await
2372+
.unwrap();
2373+
assert_eq!(
2374+
session.stream.inner.written_buf,
2375+
b"A0001 APPEND \"INBOX\" (\\Seen) {9}\r\nfoobarbaz\r\n".to_vec()
2376+
);
2377+
}
2378+
2379+
{
2380+
// APPEND command when INBOX is selected.
2381+
//
2382+
// EXISTS response is returned before APPENDUID response is returned.
2383+
let response = b"+ OK\r\n* 3 EXISTS\r\n* 2 RECENT\r\nA0001 OK [APPENDUID 1725735035 2] Append completed (0.052 + 12.097 + 0.049 secs).\r\n".to_vec();
2384+
2385+
let mock_stream = MockStream::new(response);
2386+
let mut session = mock_session!(mock_stream);
2387+
session
2388+
.append("INBOX", Some(r"(\Seen)"), None, "foobarbaz")
2389+
.await
2390+
.unwrap();
2391+
assert_eq!(
2392+
session.stream.inner.written_buf,
2393+
b"A0001 APPEND \"INBOX\" (\\Seen) {9}\r\nfoobarbaz\r\n".to_vec()
2394+
);
2395+
let exists_response = session.unsolicited_responses.recv().await.unwrap();
2396+
assert_eq!(exists_response, UnsolicitedResponse::Exists(3));
2397+
let recent_response = session.unsolicited_responses.recv().await.unwrap();
2398+
assert_eq!(recent_response, UnsolicitedResponse::Recent(2));
2399+
}
2400+
2401+
{
2402+
// APPEND to nonexisting folder fails.
2403+
let response =
2404+
b"A0001 NO [TRYCREATE] Mailbox doesn't exist: foobar (0.001 + 0.000 secs)."
2405+
.to_vec();
2406+
let mock_stream = MockStream::new(response);
2407+
let mut session = mock_session!(mock_stream);
2408+
session
2409+
.append("foobar", None, None, "foobarbaz")
2410+
.await
2411+
.unwrap_err();
2412+
assert_eq!(
2413+
session.stream.inner.written_buf,
2414+
b"A0001 APPEND \"foobar\" {9}\r\n".to_vec()
2415+
);
2416+
}
2417+
}
2418+
23552419
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
23562420
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
23572421
async fn get_metadata() {

0 commit comments

Comments
 (0)