diff --git a/src/client.rs b/src/client.rs index 3c8a6c1..78ac177 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,19 +1,18 @@ //! High-level client API to interact with Gemini Socket Server: //! * https://geminiprotocol.net/docs/protocol-specification.gmi -pub mod certificate; pub mod connection; pub mod error; pub mod response; -pub use certificate::Certificate; pub use connection::Connection; pub use error::Error; pub use response::Response; use gio::{ prelude::{IOStreamExt, OutputStreamExt, SocketClientExt, TlsConnectionExt}, - Cancellable, SocketClient, SocketClientEvent, SocketProtocol, TlsClientConnection, + Cancellable, SocketClient, SocketClientEvent, SocketProtocol, TlsCertificate, + TlsClientConnection, }; use glib::{object::Cast, Bytes, Priority, Uri}; @@ -64,7 +63,7 @@ impl Client { uri: Uri, priority: Option, cancellable: Option, - certificate: Option, + certificate: Option, callback: impl Fn(Result) + 'static, ) { // Toggle socket mode @@ -84,10 +83,7 @@ impl Client { Ok(connection) => { match Connection::new_wrap( &connection, - match certificate { - Some(ref certificate) => Some(&certificate.tls_certificate), - None => None, - }, + certificate.as_ref(), Some(&network_address), ) { Ok(result) => request_async( diff --git a/src/client/certificate.rs b/src/client/certificate.rs deleted file mode 100644 index 11e8ec1..0000000 --- a/src/client/certificate.rs +++ /dev/null @@ -1,57 +0,0 @@ -pub mod error; -pub mod scope; - -pub use error::Error; -pub use scope::Scope; - -use gio::{prelude::TlsCertificateExt, TlsCertificate}; -use glib::DateTime; - -pub struct Certificate { - pub scope: Scope, - pub tls_certificate: TlsCertificate, -} - -impl Certificate { - // Constructors - - /// Create new `Self` - pub fn from_pem(pem: &str, scope_url: &str) -> Result { - Ok(Self { - scope: match Scope::from_url(scope_url) { - Ok(scope) => scope, - Err(reason) => return Err(Error::Scope(reason)), - }, - tls_certificate: match TlsCertificate::from_pem(&pem) { - Ok(tls_certificate) => { - // Validate expiration time - match DateTime::now_local() { - Ok(now_local) => { - match tls_certificate.not_valid_after() { - Some(not_valid_after) => { - if now_local > not_valid_after { - return Err(Error::Expired(not_valid_after)); - } - } - None => return Err(Error::ValidAfter), - } - match tls_certificate.not_valid_before() { - Some(not_valid_before) => { - if now_local < not_valid_before { - return Err(Error::Inactive(not_valid_before)); - } - } - None => return Err(Error::ValidBefore), - } - } - Err(_) => return Err(Error::DateTime), - } - - // Success - tls_certificate - } - Err(reason) => return Err(Error::Decode(reason)), - }, - }) - } -} diff --git a/src/client/certificate/error.rs b/src/client/certificate/error.rs deleted file mode 100644 index 5dae782..0000000 --- a/src/client/certificate/error.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{Display, Formatter, Result}; - -use glib::gformat; - -#[derive(Debug)] -pub enum Error { - DateTime, - Decode(glib::Error), - Expired(glib::DateTime), - Inactive(glib::DateTime), - Scope(crate::client::certificate::scope::Error), - ValidAfter, - ValidBefore, -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> Result { - match self { - Self::DateTime => { - write!(f, "Could not parse local `DateTime`") - } - Self::Decode(reason) => { - write!( - f, - "Could not decode TLS certificate from PEM string: {reason}" - ) - } - Self::Expired(not_valid_after) => { - write!( - f, - "Certificate expired after: {}", - match not_valid_after.format_iso8601() { - Ok(value) => value, - Err(_) => gformat!("unknown"), - } - ) - } - Self::Inactive(not_valid_before) => { - write!( - f, - "Certificate inactive before: {}", - match not_valid_before.format_iso8601() { - Ok(value) => value, - Err(_) => gformat!("unknown"), - } - ) - } - Self::Scope(reason) => { - write!(f, "Certificate inactive before: {reason}") - } - Self::ValidAfter => write!(f, "Could not get `not_valid_after` value"), - Self::ValidBefore => write!(f, "Could not get `not_valid_before` value"), - } - } -} diff --git a/src/client/certificate/scope.rs b/src/client/certificate/scope.rs deleted file mode 100644 index 1b80784..0000000 --- a/src/client/certificate/scope.rs +++ /dev/null @@ -1,53 +0,0 @@ -pub mod error; -pub use error::Error; - -use crate::DEFAULT_PORT; -use gio::NetworkAddress; -use glib::{GString, Uri, UriFlags, UriHideFlags}; - -/// Scope implement path prefix to apply TLS authorization for -/// * external validator MAY decline `Certificate` if `Scope` defined out of protocol range -/// * [read more](https://geminiprotocol.net/docs/protocol-specification.gmi#status-60) -pub struct Scope { - uri: Uri, -} - -impl Scope { - // Constructors - - /// Create new `Self` for given `url` - pub fn from_url(url: &str) -> Result { - match Uri::parse(url, UriFlags::NONE) { - Ok(uri) => { - if !uri.scheme().to_lowercase().contains("gemini") { - return Err(Error::Scheme); - } - - if uri.host().is_none() { - return Err(Error::Host); - } - - Ok(Self { uri }) - } - Err(reason) => Err(Error::Uri(reason)), - } - } - - // Getters - - /// Get `Scope` string match [Specification](https://geminiprotocol.net/docs/protocol-specification.gmi#status-60) - pub fn to_string(&self) -> GString { - self.uri - .to_string_partial(UriHideFlags::QUERY | UriHideFlags::FRAGMENT) - } - - /// Get [NetworkAddress](https://docs.gtk.org/gio/class.NetworkAddress.html) - /// implement [SocketConnectable](https://docs.gtk.org/gio/iface.SocketConnectable.html) interface - /// * useful as [SNI](https://geminiprotocol.net/docs/protocol-specification.gmi#server-name-indication) in TLS context - pub fn to_network_address(&self) -> Result { - match crate::gio::network_address::from_uri(&self.uri, DEFAULT_PORT) { - Ok(network_address) => Ok(network_address), - Err(reason) => Err(Error::NetworkAddress(reason)), - } - } -} diff --git a/src/client/certificate/scope/error.rs b/src/client/certificate/scope/error.rs deleted file mode 100644 index f41c3b3..0000000 --- a/src/client/certificate/scope/error.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::fmt::{Display, Formatter, Result}; - -#[derive(Debug)] -pub enum Error { - Host, - NetworkAddress(crate::gio::network_address::Error), - Scheme, - Uri(glib::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> Result { - match self { - Self::Host => { - write!(f, "Host required") - } - Self::NetworkAddress(reason) => { - write!(f, "Could not parse network address: {reason}") - } - Self::Scheme => { - write!(f, "Scope does not match `gemini`") - } - Self::Uri(reason) => { - write!(f, "Could not parse URI: {reason}") - } - } - } -}