melib/error: add NetworkErrorKind enum

jmap-status-and-connect-retry-wip
Manos Pitsidianakis 2022-10-04 15:49:34 +03:00
parent 7935e49a00
commit 347be54305
7 changed files with 411 additions and 194 deletions

View File

@ -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());
}

View File

@ -337,6 +337,9 @@ impl MailBackend for JmapType {
&store,
mailbox_hash,
).await?;
if res.is_empty() {
return;
}
yield res;
}))
}

View File

@ -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(())
}

View File

@ -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,
)),
)
}

View File

@ -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))
}
}

View File

@ -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));
}
}
}

View File

@ -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)) => {