Skip to content

Commit dadb465

Browse files
CI and test fixes (#25)
CI and test fixes
2 parents 6797ca0 + dac1ee3 commit dadb465

File tree

4 files changed

+57
-167
lines changed

4 files changed

+57
-167
lines changed

Cargo.toml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,12 @@ stop-token = { version = "0.1.1", features = ["unstable"] }
3636
byte-pool = "0.2.1"
3737
lazy_static = "1.4.0"
3838
log = "0.4.8"
39+
thiserror = "1.0.9"
3940

4041
[dev-dependencies]
41-
lettre = "0.9"
4242
lettre_email = "0.9"
43-
rustls-connector = "0.8.0"
44-
rustls = { version = "0.16.0", features = ["dangerous_configuration"] }
45-
webpki = "0.21.0"
4643
pretty_assertions = "0.6.1"
47-
native-tls = "0.2.3"
44+
async-smtp = "0.2.0"
4845

4946
[[example]]
5047
name = "basic"

src/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ impl<T: Read + Write + Unpin + fmt::Debug> Client<T> {
319319
let challenge = if let Some(text) = information {
320320
ok_or_unauth_client_err!(
321321
base64::decode(text).map_err(|e| Error::Parse(
322-
ParseError::Authentication(text.to_string(), Some(e))
322+
ParseError::Authentication((*text).to_string(), Some(e))
323323
)),
324324
self
325325
)

src/error.rs

Lines changed: 23 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,66 @@
11
//! IMAP error types.
22
3-
use std::error::Error as StdError;
4-
use std::fmt;
53
use std::io::Error as IoError;
64
use std::result;
75
use std::str::Utf8Error;
86

97
use base64::DecodeError;
10-
use imap_proto::Response;
118

129
/// A convenience wrapper around `Result` for `imap::Error`.
1310
pub type Result<T> = result::Result<T, Error>;
1411

1512
/// A set of errors that can occur in the IMAP client
16-
#[derive(Debug)]
13+
#[derive(thiserror::Error, Debug)]
1714
pub enum Error {
1815
/// An `io::Error` that occurred while trying to read or write to a network stream.
19-
Io(IoError),
16+
#[error("io: {0}")]
17+
Io(#[from] IoError),
2018
/// A BAD response from the IMAP server.
19+
#[error("bad response: {0}")]
2120
Bad(String),
2221
/// A NO response from the IMAP server.
22+
#[error("no response: {0}")]
2323
No(String),
2424
/// The connection was terminated unexpectedly.
25+
#[error("connection lost")]
2526
ConnectionLost,
2627
/// Error parsing a server response.
27-
Parse(ParseError),
28+
#[error("parse: {0}")]
29+
Parse(#[from] ParseError),
2830
/// Command inputs were not valid [IMAP
2931
/// strings](https://tools.ietf.org/html/rfc3501#section-4.3).
30-
Validate(ValidateError),
31-
/// `native_tls` error
32-
NativeTlsError(async_native_tls::Error),
32+
#[error("validate: {0}")]
33+
Validate(#[from] ValidateError),
34+
/// `async_native_tls` error
35+
#[error("async_native_tls: {0}")]
36+
NativeTlsError(#[from] async_native_tls::Error),
3337
/// Error appending an e-mail.
38+
#[error("could not append mail to mailbox")]
3439
Append,
3540
#[doc(hidden)]
41+
#[error("unknown")]
3642
__Nonexhaustive,
3743
}
3844

39-
impl From<IoError> for Error {
40-
fn from(err: IoError) -> Error {
41-
Error::Io(err)
42-
}
43-
}
44-
45-
impl From<ParseError> for Error {
46-
fn from(err: ParseError) -> Error {
47-
Error::Parse(err)
48-
}
49-
}
50-
51-
impl<'a> From<&'a Response<'a>> for Error {
52-
fn from(err: &'a Response<'a>) -> Error {
53-
Error::Parse(ParseError::Unexpected(format!("{:?}", err)))
54-
}
55-
}
56-
57-
impl From<async_native_tls::Error> for Error {
58-
fn from(err: async_native_tls::Error) -> Error {
59-
Error::NativeTlsError(err)
60-
}
61-
}
62-
63-
impl fmt::Display for Error {
64-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65-
match *self {
66-
Error::Io(ref e) => fmt::Display::fmt(e, f),
67-
Error::Validate(ref e) => fmt::Display::fmt(e, f),
68-
Error::No(ref data) | Error::Bad(ref data) => {
69-
write!(f, "{}: {}", &String::from(self.description()), data)
70-
}
71-
ref e => f.write_str(e.description()),
72-
}
73-
}
74-
}
75-
76-
impl StdError for Error {
77-
fn description(&self) -> &str {
78-
match *self {
79-
Error::Io(ref e) => e.description(),
80-
Error::Parse(ref e) => e.description(),
81-
Error::Validate(ref e) => e.description(),
82-
Error::NativeTlsError(ref e) => e.description(),
83-
Error::Bad(_) => "Bad Response",
84-
Error::No(_) => "No Response",
85-
Error::ConnectionLost => "Connection lost",
86-
Error::Append => "Could not append mail to mailbox",
87-
Error::__Nonexhaustive => "Unknown",
88-
}
89-
}
90-
91-
fn cause(&self) -> Option<&dyn StdError> {
92-
match *self {
93-
Error::Io(ref e) => Some(e),
94-
Error::Parse(ParseError::DataNotUtf8(_, ref e)) => Some(e),
95-
_ => None,
96-
}
97-
}
98-
}
99-
10045
/// An error occured while trying to parse a server response.
101-
#[derive(Debug)]
46+
#[derive(thiserror::Error, Debug)]
10247
pub enum ParseError {
10348
/// Indicates an error parsing the status response. Such as OK, NO, and BAD.
49+
#[error("unable to parse status response")]
10450
Invalid(Vec<u8>),
10551
/// An unexpected response was encountered.
52+
#[error("encountered unexpected parsed response: {0}")]
10653
Unexpected(String),
10754
/// The client could not find or decode the server's authentication challenge.
55+
#[error("unable to parse authentication response: {0} - {1:?}")]
10856
Authentication(String, Option<DecodeError>),
10957
/// The client received data that was not UTF-8 encoded.
110-
DataNotUtf8(Vec<u8>, Utf8Error),
111-
}
112-
113-
impl fmt::Display for ParseError {
114-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115-
match *self {
116-
ref e => f.write_str(e.description()),
117-
}
118-
}
119-
}
120-
121-
impl StdError for ParseError {
122-
fn description(&self) -> &str {
123-
match *self {
124-
ParseError::Invalid(_) => "Unable to parse status response",
125-
ParseError::Unexpected(_) => "Encountered unexpected parsed response",
126-
ParseError::Authentication(_, _) => "Unable to parse authentication response",
127-
ParseError::DataNotUtf8(_, _) => "Unable to parse data as UTF-8 text",
128-
}
129-
}
130-
131-
fn cause(&self) -> Option<&dyn StdError> {
132-
match *self {
133-
ParseError::Authentication(_, Some(ref e)) => Some(e),
134-
_ => None,
135-
}
136-
}
58+
#[error("unable to parse data ({0:?}) as UTF-8 text: {1:?}")]
59+
DataNotUtf8(Vec<u8>, #[source] Utf8Error),
13760
}
13861

13962
/// An [invalid character](https://tools.ietf.org/html/rfc3501#section-4.3) was found in an input
14063
/// string.
141-
#[derive(Debug)]
64+
#[derive(thiserror::Error, Debug)]
65+
#[error("invalid character in input: '{0}'")]
14266
pub struct ValidateError(pub char);
143-
144-
impl fmt::Display for ValidateError {
145-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146-
// print character in debug form because invalid ones are often whitespaces
147-
write!(f, "{}: {:?}", self.description(), self.0)
148-
}
149-
}
150-
151-
impl StdError for ValidateError {
152-
fn description(&self) -> &str {
153-
"Invalid character in input"
154-
}
155-
156-
fn cause(&self) -> Option<&dyn StdError> {
157-
None
158-
}
159-
}

tests/imap_integration.rs

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,11 @@ use async_native_tls::TlsConnector;
55
use async_std::net::TcpStream;
66
use async_std::prelude::*;
77
use async_std::task;
8-
use lettre::Transport;
98

10-
fn native_tls() -> native_tls::TlsConnector {
11-
native_tls::TlsConnector::builder()
9+
fn native_tls() -> async_native_tls::TlsConnector {
10+
async_native_tls::TlsConnector::new()
1211
.danger_accept_invalid_certs(true)
1312
.danger_accept_invalid_hostnames(true)
14-
.build()
15-
.unwrap()
16-
}
17-
18-
pub struct NoCertificateVerification {}
19-
20-
impl rustls::ServerCertVerifier for NoCertificateVerification {
21-
fn verify_server_cert(
22-
&self,
23-
_roots: &rustls::RootCertStore,
24-
_presented_certs: &[rustls::Certificate],
25-
_dns_name: webpki::DNSNameRef<'_>,
26-
_ocsp: &[u8],
27-
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
28-
Ok(rustls::ServerCertVerified::assertion())
29-
}
3013
}
3114

3215
fn tls() -> TlsConnector {
@@ -54,21 +37,23 @@ async fn session(user: &str) -> Session<async_native_tls::TlsStream<TcpStream>>
5437
s
5538
}
5639

57-
fn smtp(user: &str) -> lettre::SmtpTransport {
58-
let creds = lettre::smtp::authentication::Credentials::new(user.to_string(), user.to_string());
59-
lettre::SmtpClient::new(
40+
async fn smtp(user: &str) -> async_smtp::SmtpTransport {
41+
let creds =
42+
async_smtp::smtp::authentication::Credentials::new(user.to_string(), user.to_string());
43+
async_smtp::SmtpClient::with_security(
6044
&format!(
6145
"{}:3465",
6246
std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string())
6347
),
64-
lettre::ClientSecurity::Wrapper(lettre::ClientTlsParameters {
48+
async_smtp::ClientSecurity::Wrapper(async_smtp::ClientTlsParameters {
6549
connector: native_tls(),
66-
domain: "smpt.example.com".to_string(),
50+
domain: "localhost".to_string(),
6751
}),
6852
)
69-
.unwrap()
53+
.await
54+
.expect("Failed to connect to smtp server")
7055
.credentials(creds)
71-
.transport()
56+
.into_transport()
7257
}
7358

7459
// #[test]
@@ -130,6 +115,18 @@ fn inbox_zero() {
130115
assert_eq!(inbox.len(), 0);
131116
});
132117
}
118+
fn make_email(to: &str) -> async_smtp::SendableEmail {
119+
let message_id = "abc";
120+
async_smtp::SendableEmail::new(
121+
async_smtp::Envelope::new(
122+
Some("sender@localhost".parse().unwrap()),
123+
vec![to.parse().unwrap()],
124+
)
125+
.unwrap(),
126+
message_id.to_string(),
127+
format!("To: <{}>\r\nFrom: <sender@localhost>\r\nMessage-ID: <{}.msg@localhost>\r\nSubject: My first e-mail\r\n\r\nHello world from SMTP", to, message_id),
128+
)
129+
}
133130

134131
#[test]
135132
#[ignore]
@@ -142,16 +139,11 @@ fn inbox() {
142139
c.select("INBOX").await.unwrap();
143140

144141
println!("sending");
142+
let mut s = smtp(to).await;
143+
145144
// then send the e-mail
146-
let mut s = smtp(to);
147-
let e = lettre_email::Email::builder()
148-
.from("sender@localhost")
149-
.to(to)
150-
.subject("My first e-mail")
151-
.text("Hello world from SMTP")
152-
.build()
153-
.unwrap();
154-
s.send(e.into()).unwrap();
145+
let mail = make_email(to);
146+
s.connect_and_send(mail).await.unwrap();
155147

156148
println!("searching");
157149

@@ -191,7 +183,7 @@ fn inbox() {
191183
let fetch = &fetch[0];
192184
assert_eq!(fetch.message, 1);
193185
assert_ne!(fetch.uid, None);
194-
assert_eq!(fetch.size, Some(138));
186+
assert_eq!(fetch.size, Some(21));
195187
let e = fetch.envelope().unwrap();
196188
assert_eq!(e.subject, Some(&b"My first e-mail"[..]));
197189
assert_ne!(e.from, None);
@@ -232,15 +224,9 @@ fn inbox_uid() {
232224
c.select("INBOX").await.unwrap();
233225

234226
// then send the e-mail
235-
let mut s = smtp(to);
236-
let e = lettre_email::Email::builder()
237-
.from("sender@localhost")
238-
.to(to)
239-
.subject("My first e-mail")
240-
.text("Hello world from SMTP")
241-
.build()
242-
.unwrap();
243-
s.send(e.into()).unwrap();
227+
let mut s = smtp(to).await;
228+
let e = make_email(to);
229+
s.connect_and_send(e).await.unwrap();
244230

245231
// now we should see the e-mail!
246232
let inbox = c.uid_search("ALL").await.unwrap();

0 commit comments

Comments
 (0)