|
|
@ -21,7 +21,7 @@ use gtk::{ |
|
|
|
}, |
|
|
|
}, |
|
|
|
glib::{ |
|
|
|
glib::{ |
|
|
|
gformat, uuid_string_random, Bytes, GString, Priority, Regex, RegexCompileFlags, |
|
|
|
gformat, uuid_string_random, Bytes, GString, Priority, Regex, RegexCompileFlags, |
|
|
|
RegexMatchFlags, Uri, UriFlags, |
|
|
|
RegexMatchFlags, Uri, UriFlags, UriHideFlags, |
|
|
|
}, |
|
|
|
}, |
|
|
|
prelude::{ |
|
|
|
prelude::{ |
|
|
|
ActionExt, IOStreamExt, OutputStreamExt, SocketClientExt, StaticVariantType, ToVariant, |
|
|
|
ActionExt, IOStreamExt, OutputStreamExt, SocketClientExt, StaticVariantType, ToVariant, |
|
|
@ -29,7 +29,7 @@ use gtk::{ |
|
|
|
Box, |
|
|
|
Box, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use sqlite::Transaction; |
|
|
|
use sqlite::Transaction; |
|
|
|
use std::{cell::RefCell, sync::Arc, time::Duration}; |
|
|
|
use std::{sync::Arc, time::Duration}; |
|
|
|
|
|
|
|
|
|
|
|
pub struct Page { |
|
|
|
pub struct Page { |
|
|
|
id: GString, |
|
|
|
id: GString, |
|
|
@ -42,7 +42,7 @@ pub struct Page { |
|
|
|
content: Arc<Content>, |
|
|
|
content: Arc<Content>, |
|
|
|
input: Arc<Input>, |
|
|
|
input: Arc<Input>, |
|
|
|
// Extras
|
|
|
|
// Extras
|
|
|
|
meta: Arc<RefCell<Meta>>, |
|
|
|
meta: Arc<Meta>, |
|
|
|
// GTK
|
|
|
|
// GTK
|
|
|
|
widget: Arc<Widget>, |
|
|
|
widget: Arc<Widget>, |
|
|
|
} |
|
|
|
} |
|
|
@ -84,7 +84,7 @@ impl Page { |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Init async mutable Meta object
|
|
|
|
// Init async mutable Meta object
|
|
|
|
let meta = Arc::new(RefCell::new(Meta::new())); |
|
|
|
let meta = Meta::new_arc(Status::New, gformat!("New page")); |
|
|
|
|
|
|
|
|
|
|
|
// Init events
|
|
|
|
// Init events
|
|
|
|
action_page_open.connect_activate({ |
|
|
|
action_page_open.connect_activate({ |
|
|
@ -105,7 +105,7 @@ impl Page { |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Return activated structure
|
|
|
|
// Return activated `Self`
|
|
|
|
Arc::new(Self { |
|
|
|
Arc::new(Self { |
|
|
|
id, |
|
|
|
id, |
|
|
|
// Actions
|
|
|
|
// Actions
|
|
|
@ -164,11 +164,7 @@ impl Page { |
|
|
|
let id = self.id.to_variant(); |
|
|
|
let id = self.id.to_variant(); |
|
|
|
|
|
|
|
|
|
|
|
// Update
|
|
|
|
// Update
|
|
|
|
self.meta.replace(Meta { |
|
|
|
self.meta.set_status(Status::Reload).set_title(&"Loading.."); |
|
|
|
status: Some(Status::Reload), |
|
|
|
|
|
|
|
title: Some(gformat!("Loading..")), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.action_update.activate(Some(&id)); |
|
|
|
self.action_update.activate(Some(&id)); |
|
|
|
|
|
|
|
|
|
|
|
// Route by request
|
|
|
|
// Route by request
|
|
|
@ -181,20 +177,18 @@ impl Page { |
|
|
|
scheme => { |
|
|
|
scheme => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
let description = gformat!("Protocol `{scheme}` not supported"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
self.content |
|
|
|
self.content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
.set_description(Some( |
|
|
|
|
|
|
|
gformat!("Protocol `{scheme}` not supported").as_str(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
self.meta.replace(Meta { |
|
|
|
self.meta.set_status(status).set_title(title); |
|
|
|
status: Some(status), |
|
|
|
|
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
self.action_update.activate(Some(&id)); |
|
|
|
self.action_update.activate(Some(&id)); |
|
|
@ -314,19 +308,19 @@ impl Page { |
|
|
|
// Getters
|
|
|
|
// Getters
|
|
|
|
pub fn progress_fraction(&self) -> Option<f64> { |
|
|
|
pub fn progress_fraction(&self) -> Option<f64> { |
|
|
|
// Interpret status to progress fraction
|
|
|
|
// Interpret status to progress fraction
|
|
|
|
match self.meta.borrow().status { |
|
|
|
match self.meta.status() { |
|
|
|
Some(Status::Reload) => Some(0.0), |
|
|
|
Status::Reload => Some(0.0), |
|
|
|
Some(Status::Resolving) => Some(0.1), |
|
|
|
Status::Resolving => Some(0.1), |
|
|
|
Some(Status::Resolved) => Some(0.2), |
|
|
|
Status::Resolved => Some(0.2), |
|
|
|
Some(Status::Connecting) => Some(0.3), |
|
|
|
Status::Connecting => Some(0.3), |
|
|
|
Some(Status::Connected) => Some(0.4), |
|
|
|
Status::Connected => Some(0.4), |
|
|
|
Some(Status::ProxyNegotiating) => Some(0.5), |
|
|
|
Status::ProxyNegotiating => Some(0.5), |
|
|
|
Some(Status::ProxyNegotiated) => Some(0.6), |
|
|
|
Status::ProxyNegotiated => Some(0.6), |
|
|
|
Some(Status::TlsHandshaking) => Some(0.7), |
|
|
|
Status::TlsHandshaking => Some(0.7), |
|
|
|
Some(Status::TlsHandshaked) => Some(0.8), |
|
|
|
Status::TlsHandshaked => Some(0.8), |
|
|
|
Some(Status::Complete) => Some(0.9), |
|
|
|
Status::Complete => Some(0.9), |
|
|
|
Some(Status::Failure | Status::Redirect | Status::Success | Status::Input) => Some(1.0), |
|
|
|
Status::Failure | Status::Redirect | Status::Success | Status::Input => Some(1.0), |
|
|
|
_ => None, |
|
|
|
Status::New => None, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -337,8 +331,8 @@ impl Page { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn meta_title(&self) -> Option<GString> { |
|
|
|
pub fn meta_title(&self) -> GString { |
|
|
|
self.meta.borrow().title.clone() |
|
|
|
self.meta.title() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn gobject(&self) -> &Box { |
|
|
|
pub fn gobject(&self) -> &Box { |
|
|
@ -366,6 +360,7 @@ impl Page { |
|
|
|
|
|
|
|
|
|
|
|
// Init shared objects (async)
|
|
|
|
// Init shared objects (async)
|
|
|
|
let action_page_open = self.action_page_open.clone(); |
|
|
|
let action_page_open = self.action_page_open.clone(); |
|
|
|
|
|
|
|
let action_page_reload = self.action_page_reload.clone(); |
|
|
|
let action_update = self.action_update.clone(); |
|
|
|
let action_update = self.action_update.clone(); |
|
|
|
let content = self.content.clone(); |
|
|
|
let content = self.content.clone(); |
|
|
|
let id = self.id.to_variant(); |
|
|
|
let id = self.id.to_variant(); |
|
|
@ -374,6 +369,31 @@ impl Page { |
|
|
|
let navigation = self.navigation.clone(); |
|
|
|
let navigation = self.navigation.clone(); |
|
|
|
let url = uri.clone().to_str(); |
|
|
|
let url = uri.clone().to_str(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check for page redirect pending
|
|
|
|
|
|
|
|
if meta.is_redirect() { |
|
|
|
|
|
|
|
// Check for protocol limits
|
|
|
|
|
|
|
|
if meta.redirect_count().unwrap() > 5 { |
|
|
|
|
|
|
|
// Update meta
|
|
|
|
|
|
|
|
meta.set_status(Status::Failure).set_title(&"Oops"); |
|
|
|
|
|
|
|
// Show placeholder with confirmation request to continue
|
|
|
|
|
|
|
|
content.to_text_gemini( |
|
|
|
|
|
|
|
&uri, |
|
|
|
|
|
|
|
&gformat!( |
|
|
|
|
|
|
|
// @TODO status page?
|
|
|
|
|
|
|
|
"# Redirect issue\n\nRedirection limit reached\n\nContinue:\n\n=> {}", |
|
|
|
|
|
|
|
meta.redirect_target().unwrap().to_string() |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return; // @TODO
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
action_page_open.activate(Some( |
|
|
|
|
|
|
|
&meta.redirect_target().unwrap().to_string().to_variant(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
// @TODO is_follow
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add history record
|
|
|
|
// Add history record
|
|
|
|
match navigation.history_current() { |
|
|
|
match navigation.history_current() { |
|
|
|
Some(current) => { |
|
|
|
Some(current) => { |
|
|
@ -397,7 +417,7 @@ impl Page { |
|
|
|
let id = id.clone(); |
|
|
|
let id = id.clone(); |
|
|
|
let meta = meta.clone(); |
|
|
|
let meta = meta.clone(); |
|
|
|
move |_, event, _, _| { |
|
|
|
move |_, event, _, _| { |
|
|
|
meta.borrow_mut().status = Some(match event { |
|
|
|
meta.set_status(match event { |
|
|
|
SocketClientEvent::Resolving => Status::Resolving, |
|
|
|
SocketClientEvent::Resolving => Status::Resolving, |
|
|
|
SocketClientEvent::Resolved => Status::Resolved, |
|
|
|
SocketClientEvent::Resolved => Status::Resolved, |
|
|
|
SocketClientEvent::Connecting => Status::Connecting, |
|
|
|
SocketClientEvent::Connecting => Status::Connecting, |
|
|
@ -442,10 +462,10 @@ impl Page { |
|
|
|
gemini::client::response::meta::Status::SensitiveInput => { |
|
|
|
gemini::client::response::meta::Status::SensitiveInput => { |
|
|
|
// Format response
|
|
|
|
// Format response
|
|
|
|
let status = Status::Input; |
|
|
|
let status = Status::Input; |
|
|
|
let title = gformat!("Input expected"); |
|
|
|
let title = &"Input expected"; |
|
|
|
let description = match response.data() { |
|
|
|
let description = match response.data() { |
|
|
|
Some(data) => data.value(), |
|
|
|
Some(data) => data.value().as_str(), |
|
|
|
None => &title, |
|
|
|
None => title, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Toggle input form variant
|
|
|
|
// Toggle input form variant
|
|
|
@ -467,10 +487,8 @@ impl Page { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update page
|
|
|
|
// Update page
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -493,12 +511,14 @@ impl Page { |
|
|
|
&buffer.data() |
|
|
|
&buffer.data() |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let title = match text_gemini.meta_title() { |
|
|
|
|
|
|
|
Some(title) => title, |
|
|
|
|
|
|
|
None => &uri_to_title(&uri) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Update page meta
|
|
|
|
// Update page meta
|
|
|
|
meta.borrow_mut().status = Some(Status::Success); |
|
|
|
meta.set_status(Status::Success) |
|
|
|
meta.borrow_mut().title = Some(match text_gemini.meta_title() { |
|
|
|
.set_title(title); |
|
|
|
Some(title) => title.clone(), |
|
|
|
|
|
|
|
None => uri_to_title(&uri) |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window components
|
|
|
|
// Update window components
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -506,7 +526,7 @@ impl Page { |
|
|
|
Err((reason, message)) => { |
|
|
|
Err((reason, message)) => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
let description = match reason { |
|
|
|
let description = match reason { |
|
|
|
gemini::client::response::data::text::Error::InputStream => match message { |
|
|
|
gemini::client::response::data::text::Error::InputStream => match message { |
|
|
|
Some(error) => gformat!("{error}"), |
|
|
|
Some(error) => gformat!("{error}"), |
|
|
@ -519,14 +539,12 @@ impl Page { |
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -569,8 +587,8 @@ impl Page { |
|
|
|
match result { |
|
|
|
match result { |
|
|
|
Ok(buffer) => { |
|
|
|
Ok(buffer) => { |
|
|
|
// Update page meta
|
|
|
|
// Update page meta
|
|
|
|
meta.borrow_mut().status = Some(Status::Success); |
|
|
|
meta.set_status(Status::Success) |
|
|
|
meta.borrow_mut().title = Some(uri_to_title(&uri)); |
|
|
|
.set_title(uri_to_title(&uri).as_str()); |
|
|
|
|
|
|
|
|
|
|
|
// Update page content
|
|
|
|
// Update page content
|
|
|
|
content.to_image(&buffer); |
|
|
|
content.to_image(&buffer); |
|
|
@ -581,19 +599,17 @@ impl Page { |
|
|
|
Err(reason) => { |
|
|
|
Err(reason) => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
|
|
|
|
|
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(reason.message())); |
|
|
|
.set_description(Some(reason.message())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -602,7 +618,7 @@ impl Page { |
|
|
|
Err((error, reason)) => { |
|
|
|
Err((error, reason)) => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
let description = match reason { |
|
|
|
let description = match reason { |
|
|
|
Some(message) => gformat!("{message}"), |
|
|
|
Some(message) => gformat!("{message}"), |
|
|
|
None => match error { |
|
|
|
None => match error { |
|
|
@ -614,14 +630,12 @@ impl Page { |
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
); |
|
|
@ -643,20 +657,18 @@ impl Page { |
|
|
|
_ => { |
|
|
|
_ => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
let description = gformat!("Content type not supported"); |
|
|
|
let description = gformat!("Content type not supported"); |
|
|
|
|
|
|
|
|
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -666,29 +678,87 @@ impl Page { |
|
|
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#redirection
|
|
|
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#redirection
|
|
|
|
gemini::client::response::meta::Status::Redirect | |
|
|
|
gemini::client::response::meta::Status::Redirect | |
|
|
|
gemini::client::response::meta::Status::PermanentRedirect => { |
|
|
|
gemini::client::response::meta::Status::PermanentRedirect => { |
|
|
|
|
|
|
|
// Extract redirection URL from response data
|
|
|
|
// @TODO ClientStatus::TemporaryRedirect
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
|
|
|
|
meta.borrow_mut().status = Some(Status::Redirect); |
|
|
|
|
|
|
|
meta.borrow_mut().title = Some(gformat!("Redirect")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Build gemtext message for manual redirection @TODO use template?
|
|
|
|
|
|
|
|
match response.data() { |
|
|
|
match response.data() { |
|
|
|
Some(url) => { |
|
|
|
Some(unresolved_url) => { |
|
|
|
// @TODO URI can by relative, resolve to base
|
|
|
|
// New URL from server MAY to be relative (according to the protocol),
|
|
|
|
content.to_text_gemini( |
|
|
|
// resolve to absolute URI using current request value as the base for parser
|
|
|
|
&uri, |
|
|
|
// https://docs.gtk.org/glib/type_func.Uri.resolve_relative.html
|
|
|
|
&gformat!( |
|
|
|
match Uri::resolve_relative( |
|
|
|
"# Redirect\n\nAuto-follow not implemented, click on link below to continue\n\n=> {}", |
|
|
|
Some(&uri.to_string()), |
|
|
|
url.value() |
|
|
|
&unresolved_url.value(), |
|
|
|
) |
|
|
|
UriFlags::NONE, |
|
|
|
); |
|
|
|
) { |
|
|
|
|
|
|
|
Ok(resolved_url) => { |
|
|
|
|
|
|
|
// Build valid URI (this conversion wanted to process query and fragment later)
|
|
|
|
|
|
|
|
match Uri::parse(resolved_url.as_str(), UriFlags::NONE) { |
|
|
|
|
|
|
|
Ok(resolved_uri) => { |
|
|
|
|
|
|
|
// Client MUST prevent external redirects
|
|
|
|
|
|
|
|
if is_external_uri(&resolved_uri, &uri) { |
|
|
|
|
|
|
|
// Update meta
|
|
|
|
|
|
|
|
meta.set_status(Status::Failure) |
|
|
|
|
|
|
|
.set_title(&"Oops"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show placeholder with confirmation request to continue
|
|
|
|
|
|
|
|
content.to_text_gemini( |
|
|
|
|
|
|
|
&uri, |
|
|
|
|
|
|
|
&gformat!( // @TODO status page?
|
|
|
|
|
|
|
|
"# Redirect issue\n\nExternal redirects not allowed by protocol\n\nContinue:\n\n=> {}", |
|
|
|
|
|
|
|
resolved_uri.to_string() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Update meta
|
|
|
|
|
|
|
|
meta.set_redirect( |
|
|
|
|
|
|
|
match meta.redirect_count() { |
|
|
|
|
|
|
|
Some(count) => count + 1, |
|
|
|
|
|
|
|
None => 0 |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
match response.status() { |
|
|
|
|
|
|
|
gemini::client::response::meta::Status::PermanentRedirect => true, |
|
|
|
|
|
|
|
_ => false |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Uri::parse( |
|
|
|
|
|
|
|
resolved_uri.to_string_partial( |
|
|
|
|
|
|
|
UriHideFlags::FRAGMENT | UriHideFlags::QUERY // @TODO review fragment specification
|
|
|
|
|
|
|
|
).as_str(), |
|
|
|
|
|
|
|
UriFlags::NONE |
|
|
|
|
|
|
|
).unwrap() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
.set_status(Status::Redirect) |
|
|
|
|
|
|
|
.set_title(&"Redirect"); // @TODO is really wanted here?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reload page to apply redirect
|
|
|
|
|
|
|
|
action_page_reload.activate(None); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Err(reason) => { |
|
|
|
|
|
|
|
meta.set_status(Status::Failure); |
|
|
|
|
|
|
|
content |
|
|
|
|
|
|
|
.to_status_failure() |
|
|
|
|
|
|
|
.set_description(Some(reason.message())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Err(reason) => { |
|
|
|
|
|
|
|
meta.set_status(Status::Failure); |
|
|
|
|
|
|
|
content |
|
|
|
|
|
|
|
.to_status_failure() |
|
|
|
|
|
|
|
.set_description(Some(reason.message())); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
None => { |
|
|
|
None => { |
|
|
|
|
|
|
|
let status = Status::Failure; |
|
|
|
|
|
|
|
let title = &"Oops"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
meta.set_status(status) |
|
|
|
|
|
|
|
.set_title(title); |
|
|
|
|
|
|
|
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_description(Some("Could not parse redirect meta")); |
|
|
|
.set_title(title) |
|
|
|
|
|
|
|
.set_description(Some("Redirection target not defined")); |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -697,19 +767,17 @@ impl Page { |
|
|
|
_ => { |
|
|
|
_ => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
|
|
|
|
|
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some("Status code yet not supported")); |
|
|
|
.set_description(Some("Status code yet not supported")); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -719,7 +787,7 @@ impl Page { |
|
|
|
Err((reason, message)) => { |
|
|
|
Err((reason, message)) => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
let description = match reason { |
|
|
|
let description = match reason { |
|
|
|
// Common
|
|
|
|
// Common
|
|
|
|
gemini::client::response::meta::Error::InputStream => match message { |
|
|
|
gemini::client::response::meta::Error::InputStream => match message { |
|
|
@ -770,15 +838,12 @@ impl Page { |
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
.set_description(Some(description.as_str())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
//description: Some(description),
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -789,19 +854,17 @@ impl Page { |
|
|
|
Err(reason) => { |
|
|
|
Err(reason) => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
|
|
|
|
|
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(reason.message())); |
|
|
|
.set_description(Some(reason.message())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -812,19 +875,17 @@ impl Page { |
|
|
|
Err(reason) => { |
|
|
|
Err(reason) => { |
|
|
|
// Define common data
|
|
|
|
// Define common data
|
|
|
|
let status = Status::Failure; |
|
|
|
let status = Status::Failure; |
|
|
|
let title = gformat!("Oops"); |
|
|
|
let title = &"Oops"; |
|
|
|
|
|
|
|
|
|
|
|
// Update widget
|
|
|
|
// Update widget
|
|
|
|
content |
|
|
|
content |
|
|
|
.to_status_failure() |
|
|
|
.to_status_failure() |
|
|
|
.set_title(title.as_str()) |
|
|
|
.set_title(title) |
|
|
|
.set_description(Some(reason.message())); |
|
|
|
.set_description(Some(reason.message())); |
|
|
|
|
|
|
|
|
|
|
|
// Update meta
|
|
|
|
// Update meta
|
|
|
|
meta.replace(Meta { |
|
|
|
meta.set_status(status) |
|
|
|
status: Some(status), |
|
|
|
.set_title(title); |
|
|
|
title: Some(title), |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update window
|
|
|
|
// Update window
|
|
|
|
action_update.activate(Some(&id)); |
|
|
|
action_update.activate(Some(&id)); |
|
|
@ -850,3 +911,16 @@ fn uri_to_title(uri: &Uri) -> GString { |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Compare `subject` with `base`
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Return `false` on scheme, port or host mismatch
|
|
|
|
|
|
|
|
fn is_external_uri(subject: &Uri, base: &Uri) -> bool { |
|
|
|
|
|
|
|
if subject.scheme() != base.scheme() { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if subject.port() != base.port() { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
subject.host() != base.host() |
|
|
|
|
|
|
|
} |
|
|
|