mirror of
https://github.com/YGGverse/ggemini.git
synced 2026-03-31 17:15:31 +00:00
add client certificate api
This commit is contained in:
parent
9d240c4c37
commit
4e712260ff
6 changed files with 129 additions and 5 deletions
|
|
@ -1,10 +1,12 @@
|
|||
//! 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;
|
||||
|
|
|
|||
|
|
@ -8,15 +8,20 @@ use gio::{prelude::TlsCertificateExt, TlsCertificate};
|
|||
use glib::DateTime;
|
||||
|
||||
pub struct Certificate {
|
||||
tls_certificate: TlsCertificate,
|
||||
pub scope: Scope,
|
||||
pub tls_certificate: TlsCertificate,
|
||||
}
|
||||
|
||||
impl Certificate {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self`
|
||||
pub fn from_pem(pem: &str) -> Result<Self, Error> {
|
||||
pub fn from_pem(pem: &str, scope_url: &str) -> Result<Self, Error> {
|
||||
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
|
||||
55
src/client/certificate/error.rs
Normal file
55
src/client/certificate/error.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/client/certificate/scope.rs
Normal file
41
src/client/certificate/scope.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
pub mod error;
|
||||
pub use error::Error;
|
||||
|
||||
use glib::{GString, Uri, UriFlags, UriHideFlags};
|
||||
|
||||
/// Scope implement path prefix to apply TLS authorization for
|
||||
/// * https://geminiprotocol.net/docs/protocol-specification.gmi#status-60
|
||||
pub struct Scope {
|
||||
uri: Uri,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self` for given `url` string
|
||||
/// * check URI parts required for valid `Scope` build
|
||||
pub fn from_url(url: &str) -> Result<Self, Error> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
24
src/client/certificate/scope/error.rs
Normal file
24
src/client/certificate/scope/error.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Host,
|
||||
Scheme,
|
||||
Uri(glib::Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match self {
|
||||
Self::Host => {
|
||||
write!(f, "Host required")
|
||||
}
|
||||
Self::Scheme => {
|
||||
write!(f, "Scope does not match `gemini`")
|
||||
}
|
||||
Self::Uri(reason) => {
|
||||
write!(f, "Could not parse URI: {reason}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
pub mod certificate;
|
||||
pub mod error;
|
||||
|
||||
pub use certificate::Certificate;
|
||||
pub use error::Error;
|
||||
|
||||
use gio::{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue