melib/error: add NetworkErrorKind enum
parent
7935e49a00
commit
347be54305
|
@ -134,26 +134,19 @@ impl ImapStream {
|
|||
}
|
||||
let connector = connector
|
||||
.build()
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
.chain_err_kind(crate::error::ErrorKind::Network(
|
||||
crate::error::NetworkErrorKind::InvalidTLSConnection,
|
||||
))?;
|
||||
|
||||
let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) {
|
||||
a
|
||||
} else {
|
||||
return Err(MeliError::new(format!(
|
||||
"Could not lookup address {}",
|
||||
&path
|
||||
)));
|
||||
};
|
||||
let addr = lookup_ipv4(path, server_conf.server_port)?;
|
||||
|
||||
let mut socket = AsyncWrapper::new(Connection::Tcp(
|
||||
if let Some(timeout) = server_conf.timeout {
|
||||
TcpStream::connect_timeout(&addr, timeout)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
TcpStream::connect_timeout(&addr, timeout)?
|
||||
} else {
|
||||
TcpStream::connect(&addr).chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
TcpStream::connect(&addr)?
|
||||
},
|
||||
))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
))?;
|
||||
if server_conf.use_starttls {
|
||||
let err_fn = || {
|
||||
if server_conf.server_port == 993 {
|
||||
|
@ -167,36 +160,22 @@ impl ImapStream {
|
|||
ImapProtocol::IMAP { .. } => socket
|
||||
.write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes())
|
||||
.await
|
||||
.chain_err_summary(err_fn)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?,
|
||||
.chain_err_summary(err_fn)?,
|
||||
ImapProtocol::ManageSieve => {
|
||||
socket
|
||||
.read(&mut buf)
|
||||
.await
|
||||
.chain_err_summary(err_fn)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
socket.read(&mut buf).await.chain_err_summary(err_fn)?;
|
||||
socket
|
||||
.write_all(b"STARTTLS\r\n")
|
||||
.await
|
||||
.chain_err_summary(err_fn)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
.chain_err_summary(err_fn)?;
|
||||
}
|
||||
}
|
||||
socket
|
||||
.flush()
|
||||
.await
|
||||
.chain_err_summary(err_fn)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
socket.flush().await.chain_err_summary(err_fn)?;
|
||||
let mut response = Vec::with_capacity(1024);
|
||||
let mut broken = false;
|
||||
let now = Instant::now();
|
||||
|
||||
while now.elapsed().as_secs() < 3 {
|
||||
let len = socket
|
||||
.read(&mut buf)
|
||||
.await
|
||||
.chain_err_summary(err_fn)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let len = socket.read(&mut buf).await.chain_err_summary(err_fn)?;
|
||||
response.extend_from_slice(&buf[0..len]);
|
||||
match server_conf.protocol {
|
||||
ImapProtocol::IMAP { .. } => {
|
||||
|
@ -229,9 +208,7 @@ impl ImapStream {
|
|||
|
||||
{
|
||||
// FIXME: This is blocking
|
||||
let socket = socket
|
||||
.into_inner()
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let socket = socket.into_inner()?;
|
||||
let mut conn_result = connector.connect(path, socket);
|
||||
if let Err(native_tls::HandshakeError::WouldBlock(midhandshake_stream)) =
|
||||
conn_result
|
||||
|
@ -247,20 +224,17 @@ impl ImapStream {
|
|||
midhandshake_stream = Some(stream);
|
||||
}
|
||||
p => {
|
||||
p.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
p.chain_err_kind(crate::error::ErrorKind::Network(
|
||||
crate::error::NetworkErrorKind::InvalidTLSConnection,
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AsyncWrapper::new(Connection::Tls(
|
||||
conn_result
|
||||
.chain_err_summary(|| {
|
||||
format!("Could not initiate TLS negotiation to {}.", path)
|
||||
})
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?,
|
||||
))
|
||||
.chain_err_summary(|| format!("Could not initiate TLS negotiation to {}.", path))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
AsyncWrapper::new(Connection::Tls(conn_result.chain_err_summary(|| {
|
||||
format!("Could not initiate TLS negotiation to {}.", path)
|
||||
})?))
|
||||
.chain_err_summary(|| format!("Could not initiate TLS negotiation to {}.", path))?
|
||||
}
|
||||
} else {
|
||||
let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) {
|
||||
|
@ -273,13 +247,11 @@ impl ImapStream {
|
|||
};
|
||||
AsyncWrapper::new(Connection::Tcp(
|
||||
if let Some(timeout) = server_conf.timeout {
|
||||
TcpStream::connect_timeout(&addr, timeout)
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
TcpStream::connect_timeout(&addr, timeout)?
|
||||
} else {
|
||||
TcpStream::connect(&addr).chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
TcpStream::connect(&addr)?
|
||||
},
|
||||
))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
))?
|
||||
};
|
||||
if let Err(err) = stream
|
||||
.get_ref()
|
||||
|
@ -507,8 +479,8 @@ impl ImapStream {
|
|||
last_line_idx += pos + b"\r\n".len();
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(MeliError::from(e).set_err_kind(crate::error::ErrorKind::Network));
|
||||
Err(err) => {
|
||||
return Err(MeliError::from(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -524,7 +496,7 @@ impl ImapStream {
|
|||
}
|
||||
|
||||
pub async fn send_command(&mut self, command: &[u8]) -> Result<()> {
|
||||
if let Err(err) = timeout(
|
||||
_ = timeout(
|
||||
self.timeout,
|
||||
try_await(async move {
|
||||
let command = command.trim();
|
||||
|
@ -558,42 +530,22 @@ impl ImapStream {
|
|||
Ok(())
|
||||
}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Err(err.set_err_kind(crate::error::ErrorKind::Network))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> {
|
||||
if let Err(err) = try_await(async move {
|
||||
self.stream.write_all(data).await?;
|
||||
self.stream.write_all(b"\r\n").await?;
|
||||
self.stream.flush().await?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
{
|
||||
Err(err.set_err_kind(crate::error::ErrorKind::Network))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
self.stream.write_all(data).await?;
|
||||
self.stream.write_all(b"\r\n").await?;
|
||||
self.stream.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_raw(&mut self, raw: &[u8]) -> Result<()> {
|
||||
if let Err(err) = try_await(async move {
|
||||
self.stream.write_all(raw).await?;
|
||||
self.stream.write_all(b"\r\n").await?;
|
||||
self.stream.flush().await?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
{
|
||||
Err(err.set_err_kind(crate::error::ErrorKind::Network))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
self.stream.write_all(raw).await?;
|
||||
self.stream.write_all(b"\r\n").await?;
|
||||
self.stream.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1152,7 +1104,7 @@ async fn read(
|
|||
*prev_failure = None;
|
||||
}
|
||||
Err(_err) => {
|
||||
*err = Some(Into::<MeliError>::into(_err).set_kind(crate::error::ErrorKind::Network));
|
||||
*err = Some(Into::<MeliError>::into(_err));
|
||||
*break_flag = true;
|
||||
*prev_failure = Some(SystemTime::now());
|
||||
}
|
||||
|
|
|
@ -337,6 +337,9 @@ impl MailBackend for JmapType {
|
|||
&store,
|
||||
mailbox_hash,
|
||||
).await?;
|
||||
if res.is_empty() {
|
||||
return;
|
||||
}
|
||||
yield res;
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -90,11 +90,10 @@ impl NntpStream {
|
|||
|
||||
let stream = {
|
||||
let addr = lookup_ipv4(path, server_conf.server_port)?;
|
||||
AsyncWrapper::new(Connection::Tcp(
|
||||
TcpStream::connect_timeout(&addr, std::time::Duration::new(16, 0))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?,
|
||||
))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout(
|
||||
&addr,
|
||||
std::time::Duration::new(16, 0),
|
||||
)?))?
|
||||
};
|
||||
let mut res = String::with_capacity(8 * 1024);
|
||||
let mut ret = NntpStream {
|
||||
|
@ -109,9 +108,7 @@ impl NntpStream {
|
|||
if server_conf.danger_accept_invalid_certs {
|
||||
connector.danger_accept_invalid_certs(true);
|
||||
}
|
||||
let connector = connector
|
||||
.build()
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let connector = connector.build()?;
|
||||
|
||||
if server_conf.use_starttls {
|
||||
ret.read_response(&mut res, false, &["200 ", "201 "])
|
||||
|
@ -147,14 +144,8 @@ impl NntpStream {
|
|||
&server_conf.server_hostname
|
||||
)));
|
||||
}
|
||||
ret.stream
|
||||
.write_all(b"STARTTLS\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
ret.stream
|
||||
.flush()
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
ret.stream.write_all(b"STARTTLS\r\n").await?;
|
||||
ret.stream.flush().await?;
|
||||
ret.read_response(&mut res, false, command_to_replycodes("STARTTLS"))
|
||||
.await?;
|
||||
if !res.starts_with("382 ") {
|
||||
|
@ -167,10 +158,7 @@ impl NntpStream {
|
|||
|
||||
{
|
||||
// FIXME: This is blocking
|
||||
let socket = ret
|
||||
.stream
|
||||
.into_inner()
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let socket = ret.stream.into_inner()?;
|
||||
let mut conn_result = connector.connect(path, socket);
|
||||
if let Err(native_tls::HandshakeError::WouldBlock(midhandshake_stream)) =
|
||||
conn_result
|
||||
|
@ -186,16 +174,17 @@ impl NntpStream {
|
|||
midhandshake_stream = Some(stream);
|
||||
}
|
||||
p => {
|
||||
p.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
p.chain_err_kind(crate::error::ErrorKind::Network(
|
||||
crate::error::NetworkErrorKind::InvalidTLSConnection,
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.stream = AsyncWrapper::new(Connection::Tls(
|
||||
conn_result.chain_err_kind(crate::error::ErrorKind::Network)?,
|
||||
))
|
||||
.chain_err_summary(|| format!("Could not initiate TLS negotiation to {}.", path))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
ret.stream =
|
||||
AsyncWrapper::new(Connection::Tls(conn_result?)).chain_err_summary(|| {
|
||||
format!("Could not initiate TLS negotiation to {}.", path)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
//ret.send_command(
|
||||
|
@ -367,8 +356,8 @@ impl NntpStream {
|
|||
last_line_idx += pos + "\r\n".len();
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(MeliError::from(e).set_err_kind(crate::error::ErrorKind::Network));
|
||||
Err(err) => {
|
||||
return Err(MeliError::from(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +382,7 @@ impl NntpStream {
|
|||
.await
|
||||
{
|
||||
debug!("stream send_command err {:?}", err);
|
||||
Err(err.set_err_kind(crate::error::ErrorKind::Network))
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -427,7 +416,7 @@ impl NntpStream {
|
|||
.await
|
||||
{
|
||||
debug!("stream send_multiline_data_block err {:?}", err);
|
||||
Err(err.set_err_kind(crate::error::ErrorKind::Network))
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -271,7 +271,9 @@ pub fn lookup_ipv4(host: &str, port: u16) -> crate::Result<std::net::SocketAddr>
|
|||
|
||||
Err(
|
||||
crate::error::MeliError::new(format!("Could not lookup address {}:{}", host, port))
|
||||
.set_kind(crate::error::ErrorKind::Network),
|
||||
.set_kind(crate::error::ErrorKind::Network(
|
||||
crate::error::NetworkErrorKind::HostLookupFailed,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,284 @@ use std::sync::Arc;
|
|||
|
||||
pub type Result<T> = result::Result<T, MeliError>;
|
||||
|
||||
#[derive(Debug, Copy, PartialEq, Clone)]
|
||||
pub enum NetworkErrorKind {
|
||||
/// Unspecified
|
||||
None,
|
||||
/// Name lookup of host failed.
|
||||
HostLookupFailed,
|
||||
/// Bad client Certificate
|
||||
BadClientCertificate,
|
||||
/// Bad server certificate
|
||||
BadServerCertificate,
|
||||
/// Client initialization
|
||||
ClientInitialization,
|
||||
/// Connection failed
|
||||
ConnectionFailed,
|
||||
/// Invalid content encoding
|
||||
InvalidContentEncoding,
|
||||
/// Invalid credentials
|
||||
InvalidCredentials,
|
||||
/// Invalid request
|
||||
InvalidRequest,
|
||||
/// IO Error
|
||||
Io,
|
||||
/// Name resolution
|
||||
NameResolution,
|
||||
/// Protocol violation
|
||||
ProtocolViolation,
|
||||
/// Request body not rewindable
|
||||
RequestBodyNotRewindable,
|
||||
/// Connection (not request) timeout.
|
||||
Timeout,
|
||||
/// TooManyRedirects
|
||||
TooManyRedirects,
|
||||
/// Invalid TLS connection
|
||||
InvalidTLSConnection,
|
||||
/// Equivalent to HTTP status code 400 Bad Request
|
||||
/// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
|
||||
BadRequest,
|
||||
/// Equivalent to HTTP status code 401 Unauthorized
|
||||
/// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
|
||||
Unauthorized,
|
||||
/// Equivalent to HTTP status code 402 Payment Required
|
||||
/// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
|
||||
PaymentRequired,
|
||||
/// Equivalent to HTTP status code 403 Forbidden
|
||||
/// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
|
||||
Forbidden,
|
||||
/// Equivalent to HTTP status code 404 Not Found
|
||||
/// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
|
||||
NotFound,
|
||||
/// Equivalent to HTTP status code 405 Method Not Allowed
|
||||
/// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
|
||||
MethodNotAllowed,
|
||||
/// Equivalent to HTTP status code 406 Not Acceptable
|
||||
/// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
|
||||
NotAcceptable,
|
||||
/// Equivalent to HTTP status code 407 Proxy Authentication Required
|
||||
/// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
|
||||
ProxyAuthenticationRequired,
|
||||
/// Equivalent to HTTP status code 408 Request Timeout
|
||||
/// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
|
||||
RequestTimeout,
|
||||
/// Equivalent to HTTP status code 409 Conflict
|
||||
/// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
|
||||
Conflict,
|
||||
/// Equivalent to HTTP status code 410 Gone
|
||||
/// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
|
||||
Gone,
|
||||
/// Equivalent to HTTP status code 411 Length Required
|
||||
/// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
|
||||
LengthRequired,
|
||||
/// Equivalent to HTTP status code 412 Precondition Failed
|
||||
/// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
|
||||
PreconditionFailed,
|
||||
/// Equivalent to HTTP status code 413 Payload Too Large
|
||||
/// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
|
||||
PayloadTooLarge,
|
||||
/// Equivalent to HTTP status code 414 URI Too Long
|
||||
/// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
|
||||
URITooLong,
|
||||
/// Equivalent to HTTP status code 415 Unsupported Media Type
|
||||
/// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
|
||||
UnsupportedMediaType,
|
||||
/// Equivalent to HTTP status code 416 Range Not Satisfiable
|
||||
/// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
|
||||
RangeNotSatisfiable,
|
||||
/// Equivalent to HTTP status code 417 Expectation Failed
|
||||
/// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
|
||||
ExpectationFailed,
|
||||
/// Equivalent to HTTP status code 421 Misdirected Request
|
||||
/// [RFC7540, Section 9.1.2](http://tools.ietf.org/html/rfc7540#section-9.1.2)
|
||||
MisdirectedRequest,
|
||||
/// Equivalent to HTTP status code 422 Unprocessable Entity
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
UnprocessableEntity,
|
||||
/// Equivalent to HTTP status code 423 Locked
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
Locked,
|
||||
/// Equivalent to HTTP status code 424 Failed Dependency
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
FailedDependency,
|
||||
|
||||
/// Equivalent to HTTP status code 426 Upgrade Required
|
||||
/// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
|
||||
UpgradeRequired,
|
||||
|
||||
/// Equivalent to HTTP status code 428 Precondition Required
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
PreconditionRequired,
|
||||
/// Equivalent to HTTP status code 429 Too Many Requests
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
TooManyRequests,
|
||||
|
||||
/// Equivalent to HTTP status code 431 Request Header Fields Too Large
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
RequestHeaderFieldsTooLarge,
|
||||
|
||||
/// Equivalent to HTTP status code 451 Unavailable For Legal Reasons
|
||||
/// [[RFC7725](http://tools.ietf.org/html/rfc7725)]
|
||||
UnavailableForLegalReasons,
|
||||
|
||||
/// Equivalent to HTTP status code 500 Internal Server Error
|
||||
/// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
|
||||
InternalServerError,
|
||||
/// Equivalent to HTTP status code 501 Not Implemented
|
||||
/// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
|
||||
NotImplemented,
|
||||
/// Equivalent to HTTP status code 502 Bad Gateway
|
||||
/// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
|
||||
BadGateway,
|
||||
/// Equivalent to HTTP status code 503 Service Unavailable
|
||||
/// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
|
||||
ServiceUnavailable,
|
||||
/// Equivalent to HTTP status code 504 Gateway Timeout
|
||||
/// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
|
||||
GatewayTimeout,
|
||||
/// Equivalent to HTTP status code 505 HTTP Version Not Supported
|
||||
/// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
|
||||
HTTPVersionNotSupported,
|
||||
/// Equivalent to HTTP status code 506 Variant Also Negotiates
|
||||
/// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
|
||||
VariantAlsoNegotiates,
|
||||
/// Equivalent to HTTP status code 507 Insufficient Storage
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
InsufficientStorage,
|
||||
/// Equivalent to HTTP status code 508 Loop Detected
|
||||
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
|
||||
LoopDetected,
|
||||
/// Equivalent to HTTP status code 510 Not Extended
|
||||
/// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
|
||||
NotExtended,
|
||||
/// Equivalent to HTTP status code 511 Network Authentication Required
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
NetworkAuthenticationRequired,
|
||||
}
|
||||
|
||||
impl NetworkErrorKind {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
use NetworkErrorKind::*;
|
||||
match self {
|
||||
None => "Network",
|
||||
HostLookupFailed => "Name lookup of host failed.",
|
||||
BadClientCertificate => "Bad client Certificate",
|
||||
BadServerCertificate => "Bad server certificate",
|
||||
ClientInitialization => "Client initialization",
|
||||
ConnectionFailed => "Connection failed",
|
||||
InvalidContentEncoding => "Invalid content encoding",
|
||||
InvalidCredentials => "Invalid credentials",
|
||||
InvalidRequest => "Invalid request",
|
||||
Io => "IO Error",
|
||||
NameResolution => "Name resolution",
|
||||
ProtocolViolation => "Protocol violation",
|
||||
RequestBodyNotRewindable => "Request body not rewindable",
|
||||
Timeout => "Connection (not request) timeout.",
|
||||
TooManyRedirects => "TooManyRedirects",
|
||||
InvalidTLSConnection => "Invalid TLS connection",
|
||||
BadRequest => "Bad Request",
|
||||
Unauthorized => "Unauthorized",
|
||||
PaymentRequired => "Payment Required",
|
||||
Forbidden => "Forbidden",
|
||||
NotFound => "Not Found",
|
||||
MethodNotAllowed => "Method Not Allowed",
|
||||
NotAcceptable => "Not Acceptable",
|
||||
ProxyAuthenticationRequired => "Proxy Authentication Required",
|
||||
RequestTimeout => "Request Timeout",
|
||||
Conflict => "Conflict",
|
||||
Gone => "Gone",
|
||||
LengthRequired => "Length Required",
|
||||
PreconditionFailed => "Precondition Failed",
|
||||
PayloadTooLarge => "Payload Too Large",
|
||||
URITooLong => "URI Too Long",
|
||||
UnsupportedMediaType => "Unsupported Media Type",
|
||||
RangeNotSatisfiable => "Range Not Satisfiable",
|
||||
ExpectationFailed => "Expectation Failed",
|
||||
MisdirectedRequest => "Misdirected Request",
|
||||
UnprocessableEntity => "Unprocessable Entity",
|
||||
Locked => "Locked",
|
||||
FailedDependency => "Failed Dependency",
|
||||
UpgradeRequired => "Upgrade Required",
|
||||
PreconditionRequired => "Precondition Required",
|
||||
TooManyRequests => "Too Many Requests",
|
||||
RequestHeaderFieldsTooLarge => "Request Header Fields Too Large",
|
||||
UnavailableForLegalReasons => "Unavailable For Legal Reasons",
|
||||
InternalServerError => "Internal Server Error",
|
||||
NotImplemented => "Not Implemented",
|
||||
BadGateway => "Bad Gateway",
|
||||
ServiceUnavailable => "Service Unavailable",
|
||||
GatewayTimeout => "Gateway Timeout",
|
||||
HTTPVersionNotSupported => "HTTP Version Not Supported",
|
||||
VariantAlsoNegotiates => "Variant Also Negotiates",
|
||||
InsufficientStorage => "Insufficient Storage",
|
||||
LoopDetected => "Loop Detected",
|
||||
NotExtended => "Not Extended",
|
||||
NetworkAuthenticationRequired => "Network Authentication Required",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NetworkErrorKind {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
impl From<isahc::http::StatusCode> for NetworkErrorKind {
|
||||
fn from(val: isahc::http::StatusCode) -> Self {
|
||||
match val {
|
||||
isahc::http::StatusCode::BAD_REQUEST => Self::BadRequest,
|
||||
isahc::http::StatusCode::UNAUTHORIZED => Self::Unauthorized,
|
||||
isahc::http::StatusCode::PAYMENT_REQUIRED => Self::PaymentRequired,
|
||||
isahc::http::StatusCode::FORBIDDEN => Self::Forbidden,
|
||||
isahc::http::StatusCode::NOT_FOUND => Self::NotFound,
|
||||
isahc::http::StatusCode::METHOD_NOT_ALLOWED => Self::MethodNotAllowed,
|
||||
isahc::http::StatusCode::NOT_ACCEPTABLE => Self::NotAcceptable,
|
||||
isahc::http::StatusCode::PROXY_AUTHENTICATION_REQUIRED => {
|
||||
Self::ProxyAuthenticationRequired
|
||||
}
|
||||
isahc::http::StatusCode::REQUEST_TIMEOUT => Self::RequestTimeout,
|
||||
isahc::http::StatusCode::CONFLICT => Self::Conflict,
|
||||
isahc::http::StatusCode::GONE => Self::Gone,
|
||||
isahc::http::StatusCode::LENGTH_REQUIRED => Self::LengthRequired,
|
||||
isahc::http::StatusCode::PRECONDITION_FAILED => Self::PreconditionFailed,
|
||||
isahc::http::StatusCode::PAYLOAD_TOO_LARGE => Self::PayloadTooLarge,
|
||||
isahc::http::StatusCode::URI_TOO_LONG => Self::URITooLong,
|
||||
isahc::http::StatusCode::UNSUPPORTED_MEDIA_TYPE => Self::UnsupportedMediaType,
|
||||
isahc::http::StatusCode::RANGE_NOT_SATISFIABLE => Self::RangeNotSatisfiable,
|
||||
isahc::http::StatusCode::EXPECTATION_FAILED => Self::ExpectationFailed,
|
||||
isahc::http::StatusCode::MISDIRECTED_REQUEST => Self::MisdirectedRequest,
|
||||
isahc::http::StatusCode::UNPROCESSABLE_ENTITY => Self::UnprocessableEntity,
|
||||
isahc::http::StatusCode::LOCKED => Self::Locked,
|
||||
isahc::http::StatusCode::FAILED_DEPENDENCY => Self::FailedDependency,
|
||||
isahc::http::StatusCode::UPGRADE_REQUIRED => Self::UpgradeRequired,
|
||||
isahc::http::StatusCode::PRECONDITION_REQUIRED => Self::PreconditionRequired,
|
||||
isahc::http::StatusCode::TOO_MANY_REQUESTS => Self::TooManyRequests,
|
||||
isahc::http::StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => {
|
||||
Self::RequestHeaderFieldsTooLarge
|
||||
}
|
||||
isahc::http::StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => {
|
||||
Self::UnavailableForLegalReasons
|
||||
}
|
||||
isahc::http::StatusCode::INTERNAL_SERVER_ERROR => Self::InternalServerError,
|
||||
isahc::http::StatusCode::NOT_IMPLEMENTED => Self::NotImplemented,
|
||||
isahc::http::StatusCode::BAD_GATEWAY => Self::BadGateway,
|
||||
isahc::http::StatusCode::SERVICE_UNAVAILABLE => Self::ServiceUnavailable,
|
||||
isahc::http::StatusCode::GATEWAY_TIMEOUT => Self::GatewayTimeout,
|
||||
isahc::http::StatusCode::HTTP_VERSION_NOT_SUPPORTED => Self::HTTPVersionNotSupported,
|
||||
isahc::http::StatusCode::VARIANT_ALSO_NEGOTIATES => Self::VariantAlsoNegotiates,
|
||||
isahc::http::StatusCode::INSUFFICIENT_STORAGE => Self::InsufficientStorage,
|
||||
isahc::http::StatusCode::LOOP_DETECTED => Self::LoopDetected,
|
||||
isahc::http::StatusCode::NOT_EXTENDED => Self::NotExtended,
|
||||
isahc::http::StatusCode::NETWORK_AUTHENTICATION_REQUIRED => {
|
||||
Self::NetworkAuthenticationRequired
|
||||
}
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, PartialEq, Clone)]
|
||||
pub enum ErrorKind {
|
||||
None,
|
||||
|
@ -41,7 +319,7 @@ pub enum ErrorKind {
|
|||
Authentication,
|
||||
Configuration,
|
||||
Bug,
|
||||
Network,
|
||||
Network(NetworkErrorKind),
|
||||
Timeout,
|
||||
OSError,
|
||||
NotImplemented,
|
||||
|
@ -58,7 +336,7 @@ impl fmt::Display for ErrorKind {
|
|||
ErrorKind::External => "External",
|
||||
ErrorKind::Authentication => "Authentication",
|
||||
ErrorKind::Bug => "Bug, please report this!",
|
||||
ErrorKind::Network => "Network",
|
||||
ErrorKind::Network(ref inner) => inner.as_str(),
|
||||
ErrorKind::Timeout => "Timeout",
|
||||
ErrorKind::OSError => "OS Error",
|
||||
ErrorKind::Configuration => "Configuration",
|
||||
|
@ -71,7 +349,7 @@ impl fmt::Display for ErrorKind {
|
|||
|
||||
impl ErrorKind {
|
||||
pub fn is_network(&self) -> bool {
|
||||
matches!(self, ErrorKind::Network)
|
||||
matches!(self, ErrorKind::Network(_))
|
||||
}
|
||||
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
|
@ -279,7 +557,7 @@ impl<T: Sync + Send + 'static + core::fmt::Debug> From<native_tls::HandshakeErro
|
|||
fn from(kind: native_tls::HandshakeError<T>) -> MeliError {
|
||||
MeliError::new(kind.to_string())
|
||||
.set_source(Some(Arc::new(kind)))
|
||||
.set_kind(ErrorKind::Network)
|
||||
.set_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,7 +567,7 @@ impl From<native_tls::Error> for MeliError {
|
|||
fn from(kind: native_tls::Error) -> MeliError {
|
||||
MeliError::new(kind.to_string())
|
||||
.set_source(Some(Arc::new(kind)))
|
||||
.set_kind(ErrorKind::Network)
|
||||
.set_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,11 +578,46 @@ impl From<std::num::ParseIntError> for MeliError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "jmap_backend")]
|
||||
#[cfg(feature = "http")]
|
||||
impl From<&isahc::error::ErrorKind> for NetworkErrorKind {
|
||||
#[inline]
|
||||
fn from(val: &isahc::error::ErrorKind) -> NetworkErrorKind {
|
||||
use isahc::error::ErrorKind::*;
|
||||
match val {
|
||||
BadClientCertificate => NetworkErrorKind::BadClientCertificate,
|
||||
BadServerCertificate => NetworkErrorKind::BadServerCertificate,
|
||||
ClientInitialization => NetworkErrorKind::ClientInitialization,
|
||||
ConnectionFailed => NetworkErrorKind::ConnectionFailed,
|
||||
InvalidContentEncoding => NetworkErrorKind::InvalidContentEncoding,
|
||||
InvalidCredentials => NetworkErrorKind::InvalidCredentials,
|
||||
InvalidRequest => NetworkErrorKind::BadRequest,
|
||||
Io => NetworkErrorKind::Io,
|
||||
NameResolution => NetworkErrorKind::HostLookupFailed,
|
||||
ProtocolViolation => NetworkErrorKind::ProtocolViolation,
|
||||
RequestBodyNotRewindable => NetworkErrorKind::RequestBodyNotRewindable,
|
||||
Timeout => NetworkErrorKind::Timeout,
|
||||
TlsEngine => NetworkErrorKind::InvalidTLSConnection,
|
||||
TooManyRedirects => NetworkErrorKind::TooManyRedirects,
|
||||
_ => NetworkErrorKind::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NetworkErrorKind> for ErrorKind {
|
||||
#[inline]
|
||||
fn from(kind: NetworkErrorKind) -> ErrorKind {
|
||||
ErrorKind::Network(kind)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
impl From<isahc::Error> for MeliError {
|
||||
#[inline]
|
||||
fn from(kind: isahc::Error) -> MeliError {
|
||||
MeliError::new(kind.to_string()).set_source(Some(Arc::new(kind)))
|
||||
fn from(val: isahc::Error) -> MeliError {
|
||||
let kind: NetworkErrorKind = val.kind().into();
|
||||
MeliError::new(val.to_string())
|
||||
.set_source(Some(Arc::new(val)))
|
||||
.set_kind(ErrorKind::Network(kind))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -268,16 +268,13 @@ impl SmtpConnection {
|
|||
if danger_accept_invalid_certs {
|
||||
connector.danger_accept_invalid_certs(true);
|
||||
}
|
||||
let connector = connector
|
||||
.build()
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let connector = connector.build()?;
|
||||
|
||||
let addr = lookup_ipv4(path, server_conf.port)?;
|
||||
let mut socket = AsyncWrapper::new(Connection::Tcp(
|
||||
TcpStream::connect_timeout(&addr, std::time::Duration::new(4, 0))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?,
|
||||
))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let mut socket = AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout(
|
||||
&addr,
|
||||
std::time::Duration::new(4, 0),
|
||||
)?))?;
|
||||
let pre_ehlo_extensions_reply = read_lines(
|
||||
&mut socket,
|
||||
&mut res,
|
||||
|
@ -300,10 +297,7 @@ impl SmtpConnection {
|
|||
return Err(MeliError::new("Please specify what SMTP security transport to use explicitly instead of `auto`."));
|
||||
}
|
||||
}
|
||||
socket
|
||||
.write_all(b"EHLO meli.delivery\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
socket.write_all(b"EHLO meli.delivery\r\n").await?;
|
||||
if let SmtpSecurity::StartTLS { .. } = server_conf.security {
|
||||
let pre_tls_extensions_reply = read_lines(
|
||||
&mut socket,
|
||||
|
@ -314,10 +308,7 @@ impl SmtpConnection {
|
|||
.await?;
|
||||
drop(pre_tls_extensions_reply);
|
||||
//debug!(pre_tls_extensions_reply);
|
||||
socket
|
||||
.write_all(b"STARTTLS\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
socket.write_all(b"STARTTLS\r\n").await?;
|
||||
let _post_starttls_extensions_reply = read_lines(
|
||||
&mut socket,
|
||||
&mut res,
|
||||
|
@ -329,15 +320,11 @@ impl SmtpConnection {
|
|||
}
|
||||
|
||||
let mut ret = {
|
||||
let socket = socket
|
||||
.into_inner()
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let socket = socket.into_inner()?;
|
||||
let _path = path.clone();
|
||||
|
||||
socket.set_nonblocking(false)?;
|
||||
let conn = unblock(move || connector.connect(&_path, socket))
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let conn = unblock(move || connector.connect(&_path, socket)).await?;
|
||||
/*
|
||||
if let Err(native_tls::HandshakeError::WouldBlock(midhandshake_stream)) =
|
||||
conn_result
|
||||
|
@ -359,21 +346,17 @@ impl SmtpConnection {
|
|||
}
|
||||
}
|
||||
*/
|
||||
AsyncWrapper::new(Connection::Tls(conn))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?
|
||||
AsyncWrapper::new(Connection::Tls(conn))?
|
||||
};
|
||||
ret.write_all(b"EHLO meli.delivery\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
ret.write_all(b"EHLO meli.delivery\r\n").await?;
|
||||
ret
|
||||
}
|
||||
SmtpSecurity::None => {
|
||||
let addr = lookup_ipv4(path, server_conf.port)?;
|
||||
let mut ret = AsyncWrapper::new(Connection::Tcp(
|
||||
TcpStream::connect_timeout(&addr, std::time::Duration::new(4, 0))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?,
|
||||
))
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
let mut ret = AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout(
|
||||
&addr,
|
||||
std::time::Duration::new(4, 0),
|
||||
)?))?;
|
||||
res.clear();
|
||||
let reply = read_lines(
|
||||
&mut ret,
|
||||
|
@ -391,9 +374,7 @@ impl SmtpConnection {
|
|||
Reply::new(&res, code)
|
||||
)));
|
||||
}
|
||||
ret.write_all(b"EHLO meli.delivery\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
ret.write_all(b"EHLO meli.delivery\r\n").await?;
|
||||
ret
|
||||
}
|
||||
};
|
||||
|
@ -601,15 +582,10 @@ impl SmtpConnection {
|
|||
// .trim()
|
||||
//);
|
||||
for c in command {
|
||||
self.stream
|
||||
.write_all(c)
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream.write_all(c).await?;
|
||||
}
|
||||
self.stream
|
||||
.write_all(b"\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)
|
||||
self.stream.write_all(b"\r\n").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends mail
|
||||
|
@ -697,10 +673,7 @@ impl SmtpConnection {
|
|||
let mail_length = format!("{}", mail.as_bytes().len());
|
||||
self.send_command(&[b"BDAT", mail_length.as_bytes(), b"LAST"])
|
||||
.await?;
|
||||
self.stream
|
||||
.write_all(mail.as_bytes())
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream.write_all(mail.as_bytes()).await?;
|
||||
} else {
|
||||
//The third step in the procedure is the DATA command
|
||||
//(or some alternative specified in a service extension).
|
||||
|
@ -737,35 +710,20 @@ impl SmtpConnection {
|
|||
//line.If it is a period, one additional period is inserted at the beginning of the line.
|
||||
for line in mail.lines() {
|
||||
if line.starts_with('.') {
|
||||
self.stream
|
||||
.write_all(b".")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream.write_all(b".").await?;
|
||||
}
|
||||
self.stream
|
||||
.write_all(line.as_bytes())
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream
|
||||
.write_all(b"\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream.write_all(line.as_bytes()).await?;
|
||||
self.stream.write_all(b"\r\n").await?;
|
||||
}
|
||||
|
||||
if !mail.ends_with('\n') {
|
||||
self.stream
|
||||
.write_all(b".\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream.write_all(b".\r\n").await?;
|
||||
}
|
||||
|
||||
//The mail data are terminated by a line containing only a period, that is, the character
|
||||
//sequence "<CRLF>.<CRLF>", where the first <CRLF> is actually the terminator of the
|
||||
//previous line (see Section 4.5.2). This is the end of mail data indication.
|
||||
self.stream
|
||||
.write_all(b".\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream.write_all(b".\r\n").await?;
|
||||
}
|
||||
|
||||
//The end of mail data indicator also confirms the mail transaction and tells the SMTP
|
||||
|
@ -1041,8 +999,8 @@ async fn read_lines<'r>(
|
|||
Ok(b) => {
|
||||
ret.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..b]) });
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(MeliError::from(e).set_kind(crate::error::ErrorKind::Network));
|
||||
Err(err) => {
|
||||
return Err(MeliError::from(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ mod dbus {
|
|||
| Some(NotificationType::Error(melib::ErrorKind::External)) => {
|
||||
notification.icon("dialog-error");
|
||||
}
|
||||
Some(NotificationType::Error(melib::ErrorKind::Network)) => {
|
||||
Some(NotificationType::Error(melib::ErrorKind::Network(_))) => {
|
||||
notification.icon("network-error");
|
||||
}
|
||||
Some(NotificationType::Error(melib::ErrorKind::Timeout)) => {
|
||||
|
|
Loading…
Reference in New Issue