Merge pull request #212 from mordak/logout_bye
Handle BYE responses explicitly.
This commit is contained in:
commit
b656618987
2 changed files with 66 additions and 3 deletions
|
|
@ -8,7 +8,7 @@ use std::str;
|
|||
use std::sync::mpsc;
|
||||
|
||||
use super::authenticator::Authenticator;
|
||||
use super::error::{Bad, Error, No, ParseError, Result, ValidateError};
|
||||
use super::error::{Bad, Bye, Error, No, ParseError, Result, ValidateError};
|
||||
use super::extensions;
|
||||
use super::parse::*;
|
||||
use super::types::*;
|
||||
|
|
@ -609,7 +609,18 @@ impl<T: Read + Write> Session<T> {
|
|||
|
||||
/// Logout informs the server that the client is done with the connection.
|
||||
pub fn logout(&mut self) -> Result<()> {
|
||||
self.run_command_and_check_ok("LOGOUT")
|
||||
// Check for OK or BYE.
|
||||
// According to the RFC:
|
||||
// https://datatracker.ietf.org/doc/html/rfc3501#section-6.1.3
|
||||
// We should get an untagged BYE and a tagged OK.
|
||||
// Apparently some servers send a tagged BYE (imap.wp.pl #210)
|
||||
// instead, so we just treat it like OK since we are logging out
|
||||
// anyway and this avoids returning an error on logout.
|
||||
match self.run_command_and_check_ok("LOGOUT") {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::Bye(_)) => Ok(()),
|
||||
resp => resp,
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CREATE` command](https://tools.ietf.org/html/rfc3501#section-6.3.3) creates a mailbox
|
||||
|
|
@ -1337,7 +1348,7 @@ impl<T: Read + Write> Connection<T> {
|
|||
)) => {
|
||||
assert_eq!(tag.as_bytes(), match_tag.as_bytes());
|
||||
Some(match status {
|
||||
Status::Bad | Status::No => Err((
|
||||
Status::Bad | Status::No | Status::Bye => Err((
|
||||
status,
|
||||
information.map(|v| v.into_owned()),
|
||||
code.map(|v| v.into_owned()),
|
||||
|
|
@ -1376,6 +1387,13 @@ impl<T: Read + Write> Connection<T> {
|
|||
.unwrap_or_else(|| "no explanation given".to_string()),
|
||||
}));
|
||||
}
|
||||
Status::Bye => {
|
||||
break Err(Error::Bye(Bye {
|
||||
code,
|
||||
information: expl
|
||||
.unwrap_or_else(|| "no explanation given".to_string()),
|
||||
}));
|
||||
}
|
||||
_ => break Err(Error::Parse(ParseError::Invalid(data.split_off(0)))),
|
||||
}
|
||||
}
|
||||
|
|
@ -1570,6 +1588,32 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn logout_with_untagged_bye() {
|
||||
let response = b"* BYE Logging out\r\na1 OK Logout completed.\r\n".to_vec();
|
||||
let command = format!("a1 LOGOUT\r\n");
|
||||
let mock_stream = MockStream::new(response);
|
||||
let mut session = mock_session!(mock_stream);
|
||||
session.logout().unwrap();
|
||||
assert!(
|
||||
session.stream.get_ref().written_buf == command.as_bytes().to_vec(),
|
||||
"Invalid logout command"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn logout_with_tagged_bye() {
|
||||
let response = b"a1 BYE IMAP4rev1 Server logging out\r\n".to_vec();
|
||||
let command = format!("a1 LOGOUT\r\n");
|
||||
let mock_stream = MockStream::new(response);
|
||||
let mut session = mock_session!(mock_stream);
|
||||
session.logout().unwrap();
|
||||
assert!(
|
||||
session.stream.get_ref().written_buf == command.as_bytes().to_vec(),
|
||||
"Invalid logout command"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename() {
|
||||
let response = b"a1 OK RENAME completed\r\n".to_vec();
|
||||
|
|
|
|||
19
src/error.rs
19
src/error.rs
|
|
@ -52,6 +52,21 @@ impl fmt::Display for No {
|
|||
}
|
||||
}
|
||||
|
||||
/// A BYE response from the server, which indicates it is going to hang up on us.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct Bye {
|
||||
/// Human-redable message included with the response.
|
||||
pub information: String,
|
||||
/// A more specific error status code included with the response.
|
||||
pub code: Option<ResponseCode<'static>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Bye {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.information)
|
||||
}
|
||||
}
|
||||
/// A set of errors that can occur in the IMAP client
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
|
|
@ -71,6 +86,8 @@ pub enum Error {
|
|||
Bad(Bad),
|
||||
/// A NO response from the IMAP server.
|
||||
No(No),
|
||||
/// A BYE response from the IMAP server.
|
||||
Bye(Bye),
|
||||
/// The connection was terminated unexpectedly.
|
||||
ConnectionLost,
|
||||
/// Error parsing a server response.
|
||||
|
|
@ -148,6 +165,7 @@ impl fmt::Display for Error {
|
|||
Error::Parse(ref e) => fmt::Display::fmt(e, f),
|
||||
Error::No(ref data) => write!(f, "No Response: {}", data),
|
||||
Error::Bad(ref data) => write!(f, "Bad Response: {}", data),
|
||||
Error::Bye(ref data) => write!(f, "Bye Response: {}", data),
|
||||
Error::ConnectionLost => f.write_str("Connection Lost"),
|
||||
Error::Append => f.write_str("Could not append mail to mailbox"),
|
||||
Error::Unexpected(ref r) => write!(f, "Unexpected Response: {:?}", r),
|
||||
|
|
@ -171,6 +189,7 @@ impl StdError for Error {
|
|||
Error::Validate(ref e) => e.description(),
|
||||
Error::Bad(_) => "Bad Response",
|
||||
Error::No(_) => "No Response",
|
||||
Error::Bye(_) => "Bye Response",
|
||||
Error::ConnectionLost => "Connection lost",
|
||||
Error::Append => "Could not append mail to mailbox",
|
||||
Error::Unexpected(_) => "Unexpected Response",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue