From c79f386bf1c1c3e5eb02920adf143122358a7e01 Mon Sep 17 00:00:00 2001 From: yggverse Date: Fri, 28 Mar 2025 00:33:33 +0200 Subject: [PATCH 01/13] update version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 442d0ee..27d8051 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ggemini" -version = "0.18.0" +version = "0.18.1" edition = "2024" license = "MIT" readme = "README.md" From bb5b1dfb533ded64b0eb02d1cdecb19bbd7d40a9 Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 22 Jul 2025 08:44:50 +0300 Subject: [PATCH 02/13] apply clippy optimizations --- src/gio/file_output_stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gio/file_output_stream.rs b/src/gio/file_output_stream.rs index a8e5d70..777600f 100644 --- a/src/gio/file_output_stream.rs +++ b/src/gio/file_output_stream.rs @@ -39,7 +39,7 @@ pub fn from_stream_async( } } - if bytes.len() == 0 { + if bytes.is_empty() { return on_complete(Ok((file_output_stream, size.total))); } From 44196608cebe51213c1feb4cc53bc8557bc16f72 Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 22 Jul 2025 08:48:56 +0300 Subject: [PATCH 03/13] implement optional TOFU validation --- Cargo.toml | 2 +- README.md | 3 ++- src/client.rs | 6 ++++-- src/client/connection.rs | 27 +++++++++++++++++++-------- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27d8051..1c6ea42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ggemini" -version = "0.18.1" +version = "0.19.0" edition = "2024" license = "MIT" readme = "README.md" diff --git a/README.md b/README.md index d81850e..03c633e 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ fn main() -> ExitCode { }, Priority::DEFAULT, Cancellable::new(), - None, // optional `GTlsCertificate` + None, // optional auth `GTlsCertificate` + None, // optional TOFU `GTlsCertificate` array |result| match result { Ok((response, _connection)) => match response { Response::Success(success) => match success.mime().unwrap().as_str() { diff --git a/src/client.rs b/src/client.rs index cff557e..f11e9ec 100644 --- a/src/client.rs +++ b/src/client.rs @@ -59,7 +59,8 @@ impl Client { request: Request, priority: Priority, cancellable: Cancellable, - certificate: Option, + client_certificate: Option, + server_certificates: Option>, callback: impl FnOnce(Result<(Response, Connection), Error>) + 'static, ) { // Begin new connection @@ -75,7 +76,8 @@ impl Client { match Connection::build( socket_connection, network_address, - certificate, + client_certificate, + server_certificates, is_session_resumption, ) { Ok(connection) => connection.request_async( diff --git a/src/client/connection.rs b/src/client/connection.rs index d1cd849..b7833a7 100644 --- a/src/client/connection.rs +++ b/src/client/connection.rs @@ -6,11 +6,9 @@ pub use error::Error; pub use request::{Mode, Request}; pub use response::Response; -// Local dependencies - use gio::{ Cancellable, IOStream, NetworkAddress, SocketConnection, TlsCertificate, TlsClientConnection, - prelude::{IOStreamExt, OutputStreamExtManual, TlsConnectionExt}, + prelude::{IOStreamExt, OutputStreamExtManual, TlsCertificateExt, TlsConnectionExt}, }; use glib::{ Bytes, Priority, @@ -30,17 +28,19 @@ impl Connection { pub fn build( socket_connection: SocketConnection, network_address: NetworkAddress, - certificate: Option, + client_certificate: Option, + server_certificates: Option>, is_session_resumption: bool, ) -> Result { Ok(Self { tls_client_connection: match new_tls_client_connection( &socket_connection, Some(&network_address), + server_certificates, is_session_resumption, ) { Ok(tls_client_connection) => { - if let Some(ref c) = certificate { + if let Some(ref c) = client_certificate { tls_client_connection.set_certificate(c); } tls_client_connection @@ -136,6 +136,7 @@ impl Connection { fn new_tls_client_connection( socket_connection: &SocketConnection, server_identity: Option<&NetworkAddress>, + server_certificates: Option>, is_session_resumption: bool, ) -> Result { match TlsClientConnection::new(socket_connection, server_identity) { @@ -149,9 +150,19 @@ fn new_tls_client_connection( // https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections tls_client_connection.set_require_close_notify(true); - // @TODO validate - // https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation - tls_client_connection.connect_accept_certificate(|_, _, _| true); + // [TOFU](https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation) + tls_client_connection.connect_accept_certificate(move |_, c, _| { + server_certificates + .as_ref() + .is_none_or(|server_certificates| { + for server_certificate in server_certificates { + if server_certificate.is_same(c) { + return true; + } + } + false + }) + }); Ok(tls_client_connection) } From cc1018224a42b3d63e9cc77db86102710e79435a Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 22 Jul 2025 09:46:32 +0300 Subject: [PATCH 04/13] reorganize error types, return `socket_connection` on init error --- src/client.rs | 10 ++++++---- src/client/error.rs | 16 ++++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/client.rs b/src/client.rs index f11e9ec..0f33a94 100644 --- a/src/client.rs +++ b/src/client.rs @@ -74,7 +74,7 @@ impl Client { move |result| match result { Ok(socket_connection) => { match Connection::build( - socket_connection, + socket_connection.clone(), network_address, client_certificate, server_certificates, @@ -87,18 +87,20 @@ impl Client { move |result| { callback(match result { Ok(response) => Ok(response), - Err(e) => Err(Error::Connection(e)), + Err(e) => Err(Error::Request(e)), }) }, ), - Err(e) => callback(Err(Error::Connection(e))), + Err(e) => { + callback(Err(Error::Connection(socket_connection, e))) + } } } Err(e) => callback(Err(Error::Connect(e))), } }) } - Err(e) => callback(Err(Error::Request(e))), + Err(e) => callback(Err(Error::NetworkAddress(e))), } } diff --git a/src/client/error.rs b/src/client/error.rs index 6083e77..b49d65a 100644 --- a/src/client/error.rs +++ b/src/client/error.rs @@ -3,21 +3,25 @@ use std::fmt::{Display, Formatter, Result}; #[derive(Debug)] pub enum Error { Connect(glib::Error), - Connection(crate::client::connection::Error), - Request(crate::client::connection::request::Error), + Connection(gio::SocketConnection, crate::client::connection::Error), + NetworkAddress(crate::client::connection::request::Error), + Request(crate::client::connection::Error), } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> Result { match self { - Self::Connection(e) => { - write!(f, "Connection error: {e}") - } Self::Connect(e) => { write!(f, "Connect error: {e}") } + Self::Connection(_, e) => { + write!(f, "Connection init error: {e}") + } + Self::NetworkAddress(e) => { + write!(f, "Network address error: {e}") + } Self::Request(e) => { - write!(f, "Request error: {e}") + write!(f, "Connection error: {e}") } } } From e878fe4ba2d7b73816dd3e4be90411aa401ab272 Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 22 Jul 2025 09:49:39 +0300 Subject: [PATCH 05/13] return `NetworkAddress` on `Error::Connect` --- src/client.rs | 2 +- src/client/error.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.rs b/src/client.rs index 0f33a94..5198f81 100644 --- a/src/client.rs +++ b/src/client.rs @@ -96,7 +96,7 @@ impl Client { } } } - Err(e) => callback(Err(Error::Connect(e))), + Err(e) => callback(Err(Error::Connect(network_address, e))), } }) } diff --git a/src/client/error.rs b/src/client/error.rs index b49d65a..eb951b6 100644 --- a/src/client/error.rs +++ b/src/client/error.rs @@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter, Result}; #[derive(Debug)] pub enum Error { - Connect(glib::Error), + Connect(gio::NetworkAddress, glib::Error), Connection(gio::SocketConnection, crate::client::connection::Error), NetworkAddress(crate::client::connection::request::Error), Request(crate::client::connection::Error), @@ -11,7 +11,7 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut Formatter) -> Result { match self { - Self::Connect(e) => { + Self::Connect(_, e) => { write!(f, "Connect error: {e}") } Self::Connection(_, e) => { From c5d10e020a1d246aa1bd5ae521cde03f06155e6d Mon Sep 17 00:00:00 2001 From: yggverse Date: Wed, 23 Jul 2025 01:27:20 +0300 Subject: [PATCH 06/13] return `Connection` on `Request` error --- src/client.rs | 4 ++-- src/client/connection.rs | 1 + src/client/error.rs | 7 +++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/client.rs b/src/client.rs index 5198f81..2152781 100644 --- a/src/client.rs +++ b/src/client.rs @@ -80,14 +80,14 @@ impl Client { server_certificates, is_session_resumption, ) { - Ok(connection) => connection.request_async( + Ok(connection) => connection.clone().request_async( request, priority, cancellable, move |result| { callback(match result { Ok(response) => Ok(response), - Err(e) => Err(Error::Request(e)), + Err(e) => Err(Error::Request(connection, e)), }) }, ), diff --git a/src/client/connection.rs b/src/client/connection.rs index b7833a7..6be90f1 100644 --- a/src/client/connection.rs +++ b/src/client/connection.rs @@ -15,6 +15,7 @@ use glib::{ object::{Cast, ObjectExt}, }; +#[derive(Debug, Clone)] pub struct Connection { pub network_address: NetworkAddress, pub socket_connection: SocketConnection, diff --git a/src/client/error.rs b/src/client/error.rs index eb951b6..73031da 100644 --- a/src/client/error.rs +++ b/src/client/error.rs @@ -5,7 +5,10 @@ pub enum Error { Connect(gio::NetworkAddress, glib::Error), Connection(gio::SocketConnection, crate::client::connection::Error), NetworkAddress(crate::client::connection::request::Error), - Request(crate::client::connection::Error), + Request( + crate::client::connection::Connection, + crate::client::connection::Error, + ), } impl Display for Error { @@ -20,7 +23,7 @@ impl Display for Error { Self::NetworkAddress(e) => { write!(f, "Network address error: {e}") } - Self::Request(e) => { + Self::Request(_, e) => { write!(f, "Connection error: {e}") } } From d8e0a8e35a136ee4b32e3bb1c38979539c0d8044 Mon Sep 17 00:00:00 2001 From: yggverse Date: Wed, 23 Jul 2025 03:07:24 +0300 Subject: [PATCH 07/13] update dependencies version --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c6ea42..7c12b4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ repository = "https://github.com/YGGverse/ggemini" [dependencies.gio] package = "gio" -version = "0.20.9" +version = "0.21" features = ["v2_70"] [dependencies.glib] package = "glib" -version = "0.20.9" +version = "0.21" features = ["v2_66"] From 5019e6666772b8d06823339176094db9062167f9 Mon Sep 17 00:00:00 2001 From: yggverse Date: Wed, 23 Jul 2025 04:29:01 +0300 Subject: [PATCH 08/13] use latest 0.20 api --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c12b4a..0e27134 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ repository = "https://github.com/YGGverse/ggemini" [dependencies.gio] package = "gio" -version = "0.21" +version = "0.20.12" features = ["v2_70"] [dependencies.glib] package = "glib" -version = "0.21" +version = "0.20.12" features = ["v2_66"] From f8537e4ab63cf07992f12c909ea9480d541df35d Mon Sep 17 00:00:00 2001 From: yggverse Date: Wed, 23 Jul 2025 05:08:13 +0300 Subject: [PATCH 09/13] use latest dependencies version --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e27134..901e1b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ repository = "https://github.com/YGGverse/ggemini" [dependencies.gio] package = "gio" -version = "0.20.12" +version = "0.21.0" features = ["v2_70"] [dependencies.glib] package = "glib" -version = "0.20.12" +version = "0.21.0" features = ["v2_66"] From 7e9ecf64b3c5e456f739413892d14b0c5c83dda2 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 19 Oct 2025 22:37:48 +0300 Subject: [PATCH 10/13] implement default trait --- src/gio/file_output_stream/size.rs | 10 ++++++++++ src/gio/memory_input_stream/size.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/gio/file_output_stream/size.rs b/src/gio/file_output_stream/size.rs index 285d9f1..5d0c911 100644 --- a/src/gio/file_output_stream/size.rs +++ b/src/gio/file_output_stream/size.rs @@ -5,3 +5,13 @@ pub struct Size { pub limit: Option, pub total: usize, } + +impl Default for Size { + fn default() -> Self { + Self { + chunk: 0x10000, // 64KB + limit: None, + total: 0, + } + } +} diff --git a/src/gio/memory_input_stream/size.rs b/src/gio/memory_input_stream/size.rs index b95ef39..9a10bd3 100644 --- a/src/gio/memory_input_stream/size.rs +++ b/src/gio/memory_input_stream/size.rs @@ -4,3 +4,13 @@ pub struct Size { pub limit: usize, pub total: usize, } + +impl Default for Size { + fn default() -> Self { + Self { + chunk: 0x10000, // 64KB + limit: 0xfffff, // 1 MB + total: 0, + } + } +} From 0f6eaa563c428f85ecd49c9f562b2987b6fed317 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 19 Oct 2025 22:38:51 +0300 Subject: [PATCH 11/13] update version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 901e1b7..12c3c6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ggemini" -version = "0.19.0" +version = "0.20.0" edition = "2024" license = "MIT" readme = "README.md" From bba51e38e831cc84060c901e1dbe2e6efa0823ad Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 19 Oct 2025 22:46:49 +0300 Subject: [PATCH 12/13] apply fmt updates --- src/gio/file_output_stream.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gio/file_output_stream.rs b/src/gio/file_output_stream.rs index 777600f..2dffb5e 100644 --- a/src/gio/file_output_stream.rs +++ b/src/gio/file_output_stream.rs @@ -33,10 +33,10 @@ pub fn from_stream_async( size.total += bytes.len(); on_chunk(bytes.clone(), size.total); - if let Some(limit) = size.limit { - if size.total > limit { - return on_complete(Err(Error::BytesTotal(size.total, limit))); - } + if let Some(limit) = size.limit + && size.total > limit + { + return on_complete(Err(Error::BytesTotal(size.total, limit))); } if bytes.is_empty() { From 11d17e004e473354bc0d9ca9ae701af6cd281ee7 Mon Sep 17 00:00:00 2001 From: yggverse Date: Fri, 7 Nov 2025 21:15:20 +0200 Subject: [PATCH 13/13] update version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 12c3c6c..801fb2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ggemini" -version = "0.20.0" +version = "0.20.1" edition = "2024" license = "MIT" readme = "README.md"