From 86d191cc46fcc158d04bf567ff5cbd3d3ff6ea93 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sat, 18 Jan 2025 07:21:06 +0200 Subject: [PATCH] implement shared redirection `Response` builder --- .../tab/item/page/client/driver/gemini.rs | 100 ++++++++++++------ src/tool.rs | 13 +-- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/src/app/browser/window/tab/item/page/client/driver/gemini.rs b/src/app/browser/window/tab/item/page/client/driver/gemini.rs index 404983cd..92b1df7e 100644 --- a/src/app/browser/window/tab/item/page/client/driver/gemini.rs +++ b/src/app/browser/window/tab/item/page/client/driver/gemini.rs @@ -2,6 +2,7 @@ use super::{ response::{Certificate, Failure, Input, Redirect}, Profile, Request, Response, }; + use gtk::{ gio::Cancellable, glib::{Priority, Uri, UriFlags}, @@ -99,39 +100,23 @@ pub fn handle( })), }, // https://geminiprotocol.net/docs/protocol-specification.gmi#status-30-temporary-redirection - Status::Redirect => callback(match response.meta.data { - Some(data) => match Uri::parse_relative(&base, data.as_str(), UriFlags::NONE) { - Ok(target) => Response::Redirect(Redirect::Foreground(Request::build( - &target.to_string(), - Some(referrer), - cancellable, - priority, - ))), - Err(e) => Response::Failure(Failure::Error { - message: format!("Could not parse target address: {e}"), - }), - }, - None => Response::Failure(Failure::Error { - message: "Target address not found".to_string(), - }), - }), // @TODO validate redirect count + Status::Redirect => callback(redirect( + response.meta.data, + base, + referrer, + cancellable, + priority, + false, + )), // https://geminiprotocol.net/docs/protocol-specification.gmi#status-31-permanent-redirection - Status::PermanentRedirect => callback(match response.meta.data { - Some(data) => match Uri::parse_relative(&base, data.as_str(), UriFlags::NONE) { - Ok(target) => Response::Redirect(Redirect::Background(Request::build( - &target.to_string(), - Some(referrer), - cancellable, - priority, - ))), - Err(e) => Response::Failure(Failure::Error { - message: format!("Could not parse target address: {e}"), - }), - }, - None => Response::Failure(Failure::Error { - message: "Target address not found".to_string(), - }), - }), // @TODO validate redirect count + Status::PermanentRedirect => callback(redirect( + response.meta.data, + base, + referrer, + cancellable, + priority, + true, + )), // https://geminiprotocol.net/docs/protocol-specification.gmi#status-60 Status::CertificateRequest => callback(Response::Certificate(Certificate::Request { title: match response.meta.data { @@ -164,3 +149,54 @@ pub fn handle( })), } } + +/// Shared redirection `Response` builder +fn redirect( + data: Option, + base: Uri, + referrer: Vec, + cancellable: Cancellable, + priority: Priority, + is_foreground: bool, +) -> Response { + // Validate redirect according to + // [Gemini protocol specifications](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection) + if referrer.len() > 5 { + return Response::Failure(Failure::Error { + message: format!("Max redirection count reached"), + }); + } + match data { + Some(target) => match Uri::parse_relative(&base, target.as_str(), UriFlags::NONE) { + Ok(target) => { + // Disallow external redirection + if base.scheme() != target.scheme() + || base.port() != target.port() + || base.host() != target.host() + { + return Response::Failure(Failure::Error { + message: format!( + "External redirects not allowed by protocol specification" + ), + }); // @TODO placeholder page with optional link open button + } + + // Build request + let request = + Request::build(&target.to_string(), Some(referrer), cancellable, priority); + + Response::Redirect(if is_foreground { + Redirect::Foreground(request) + } else { + Redirect::Background(request) + }) + } + Err(e) => Response::Failure(Failure::Error { + message: format!("Could not parse target address: {e}"), + }), + }, + None => Response::Failure(Failure::Error { + message: "Target address not found".to_string(), + }), + } +} diff --git a/src/tool.rs b/src/tool.rs index 13c50508..e111d38f 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -1,7 +1,7 @@ //! Some shared helpers collection // Global dependencies -use gtk::glib::{DateTime, GString, Uri}; +use gtk::glib::{DateTime, GString}; /// Format bytes to KB/MB/GB presentation pub fn format_bytes(value: usize) -> String { @@ -34,14 +34,3 @@ pub fn format_time(t: &DateTime) -> GString { pub fn now() -> DateTime { DateTime::now_local().unwrap() // @TODO handle? } - -/// Compare `subject` with `base` -pub fn _is_external(subject: &Uri, base: &Uri) -> bool { - if subject.scheme() != base.scheme() { - return true; - } - if subject.port() != base.port() { - return true; - } - subject.host() != base.host() -} // @TODO not in use