Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass IDLE responses to caller. #186

Merged
merged 23 commits into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
529401a
Implement a method to pass unilateral responses while IDLE.
mordak Mar 14, 2021
2874bfd
Add IDLE example.
mordak Mar 28, 2021
c9b7c0a
Update src/extensions/idle.rs
mordak Apr 5, 2021
5e3f087
Merge remote-tracking branch 'origin/master' into idle-responses
mordak Apr 5, 2021
bbff7d4
Remove deprecated wait_timeout()
mordak Apr 5, 2021
bb38142
Change callback_stop to stop_on_any.
mordak Apr 5, 2021
e8a7c91
Comment example where we turn on debugging.
mordak Apr 5, 2021
b8bd1e4
Reorder UnsolicitedResponse alphabetically so it is easier to follow.
mordak Apr 5, 2021
e1db863
Add helper function to transform a vec of flag strings into a vec of …
mordak Apr 5, 2021
064c2e0
Use drain() instead of reallocating.
mordak Apr 5, 2021
ff39ebf
Merge branch 'idle-responses' of github.com:mordak/rust-imap into idl…
mordak Apr 5, 2021
9126d3c
Improve documentation around unhandled responses.
mordak Apr 7, 2021
11adcfc
Tweak to how we handle incomplete parse.
mordak Apr 7, 2021
5942553
Use iterators for Flag::from_strs()
mordak Apr 7, 2021
7eb2cfd
Use bool instead of CallbackAction.
mordak Apr 7, 2021
efa02f0
Remove wrapper around ResponseCode.
mordak Apr 8, 2021
f2d7919
Do not wrap AttributeValue.
mordak Apr 8, 2021
584c954
Reorder variants alphabetically in try_from.
mordak Apr 8, 2021
692dcdd
Move buffer management into parse match arms.
mordak Apr 8, 2021
4232c77
wait to wait_while
mordak Apr 8, 2021
08de336
Move debug assertion.
mordak Apr 9, 2021
1cabb3b
Promote Unexpected error from ParseError to Error.
mordak Apr 20, 2021
48db461
Merge remote-tracking branch 'origin/master' into idle-responses
mordak Apr 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ default = ["tls"]
native-tls = { version = "0.2.2", optional = true }
regex = "1.0"
bufstream = "0.1"
imap-proto = "0.14.0"
imap-proto = "0.14.1"
nom = { version = "6.0", default-features = false }
base64 = "0.13"
chrono = { version = "0.4", default-features = false }
Expand All @@ -31,6 +31,7 @@ lazy_static = "1.4"
lettre = "0.9"
lettre_email = "0.9"
rustls-connector = "0.13.0"
structopt = "0.3"

[[example]]
name = "basic"
Expand All @@ -40,6 +41,10 @@ required-features = ["default"]
name = "gmail_oauth2"
required-features = ["default"]

[[example]]
name = "idle"
required-features = ["default"]

[[test]]
name = "imap_integration"
required-features = ["default"]
84 changes: 84 additions & 0 deletions examples/idle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use native_tls::TlsConnector;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "idle")]
struct Opt {
// The server name to connect to
#[structopt(short, long)]
server: String,

// The port to use
#[structopt(short, long, default_value = "993")]
port: u16,

// The account username
#[structopt(short, long)]
username: String,

// The account password. In a production system passwords
// would normally be in a config or fetched at runtime from
// a password manager or user prompt and not passed on the
// command line.
#[structopt(short = "w", long)]
password: String,

// The mailbox to IDLE on
#[structopt(short, long, default_value = "INBOX")]
mailbox: String,

#[structopt(
short = "x",
long,
help = "The number of responses to receive before exiting",
default_value = "5"
)]
max_responses: usize,
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
}

fn main() {
let opt = Opt::from_args();

let ssl_conn = TlsConnector::builder().build().unwrap();
let client = imap::connect((opt.server.clone(), opt.port), opt.server, &ssl_conn)
.expect("Could not connect to imap server");
let mut imap = client
.login(opt.username, opt.password)
.expect("Could not authenticate");

// Turn on debug output so we can see the actual traffic coming
// from the server and how it is handled in our callback.
// This wouldn't be turned on in a production build, but is helpful
// in examples and for debugging.
imap.debug = true;
jonhoo marked this conversation as resolved.
Show resolved Hide resolved

imap.select(opt.mailbox).expect("Could not select mailbox");

let idle = imap.idle().expect("Could not IDLE");
jonhoo marked this conversation as resolved.
Show resolved Hide resolved

// Implement a trivial counter that causes the IDLE callback to end the IDLE
// after a fixed number of responses.
//
// A threaded client could use channels or shared data to interact with the
// rest of the program and update mailbox state, decide to exit the IDLE, etc.
let mut num_responses = 0;
let max_responses = opt.max_responses;
let idle_result = idle.wait_keepalive_while(|response| {
num_responses += 1;
println!("IDLE response #{}: {:?}", num_responses, response);
if num_responses >= max_responses {
// Stop IDLE
false
} else {
// Continue IDLE
true
}
});

match idle_result {
Ok(()) => println!("IDLE finished normally"),
Err(e) => println!("IDLE finished with error {:?}", e),
}

imap.logout().expect("Could not log out");
}
12 changes: 7 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ pub enum Error {
Validate(ValidateError),
/// Error appending an e-mail.
Append,
/// An unexpected response was received. This could be a response from a command,
/// or an unsolicited response that could not be converted into a local type in
/// [`UnsolicitedResponse`].
Unexpected(Response<'static>),
}

impl From<IoError> for Error {
Expand Down Expand Up @@ -112,7 +116,7 @@ impl From<TlsError> for Error {

impl<'a> From<Response<'a>> for Error {
fn from(err: Response<'a>) -> Error {
Error::Parse(ParseError::Unexpected(format!("{:?}", err)))
Error::Unexpected(err.into_owned())
}
}

Expand All @@ -130,6 +134,7 @@ impl fmt::Display for Error {
Error::Bad(ref data) => write!(f, "Bad 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),
}
}
}
Expand All @@ -149,6 +154,7 @@ impl StdError for Error {
Error::No(_) => "No Response",
Error::ConnectionLost => "Connection lost",
Error::Append => "Could not append mail to mailbox",
Error::Unexpected(_) => "Unexpected Response",
}
}

Expand All @@ -170,8 +176,6 @@ impl StdError for Error {
pub enum ParseError {
/// Indicates an error parsing the status response. Such as OK, NO, and BAD.
Invalid(Vec<u8>),
/// An unexpected response was encountered.
Unexpected(String),
/// The client could not find or decode the server's authentication challenge.
Authentication(String, Option<DecodeError>),
/// The client received data that was not UTF-8 encoded.
Expand All @@ -182,7 +186,6 @@ impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ParseError::Invalid(_) => f.write_str("Unable to parse status response"),
ParseError::Unexpected(_) => f.write_str("Encountered unexpected parse response"),
ParseError::Authentication(_, _) => {
f.write_str("Unable to parse authentication response")
}
Expand All @@ -195,7 +198,6 @@ impl StdError for ParseError {
fn description(&self) -> &str {
match *self {
ParseError::Invalid(_) => "Unable to parse status response",
ParseError::Unexpected(_) => "Encountered unexpected parsed response",
ParseError::Authentication(_, _) => "Unable to parse authentication response",
ParseError::DataNotUtf8(_, _) => "Unable to parse data as UTF-8 text",
}
Expand Down
Loading