-- Leo's gemini proxy

-- Connecting to typed-hole.org:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

commit a7872fd21369c10068a09306e65b8930e39ab420

Author: Julien Blanchard <julien@sideburns.eu>

Date: Mon Dec 23 13:03:34 2019 +0100


Handles statuses, open external links


diff --git a/Cargo.lock b/Cargo.lock

index 9dac585..eec952b 100644

--- a/Cargo.lock

+++ b/Cargo.lock

@@ -140,6 +140,7 @@ dependencies = [

"json 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",

"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",

"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",

+ "open 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",

"pango 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",

"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",

"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

@@ -547,6 +548,14 @@ dependencies = [

]


[[package]]

+name = "open"

+version = "1.3.2"

+source = "registry+https://github.com/rust-lang/crates.io-index"

+dependencies = [

+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",

+]

+

+[[package]]

name = "openssl"

version = "0.10.26"

source = "registry+https://github.com/rust-lang/crates.io-index"

@@ -975,6 +984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"

"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"

"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"

"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"

+"checksum open 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "94b424e1086328b0df10235c6ff47be63708071881bead9e76997d9291c0134b"

"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585"

"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"

"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f"

diff --git a/Cargo.toml b/Cargo.toml

index 3bc3ee8..d07e2e4 100644

--- a/Cargo.toml

+++ b/Cargo.toml

@@ -14,6 +14,7 @@ glib = "*"

glib-sys = "*"

pango = "*"

json = "*"

+open = "*"

regex = "*"

native-tls = "*"

url = "*"

diff --git a/src/content.rs b/src/content.rs

index 6e8be18..184d9e4 100644

--- a/src/content.rs

+++ b/src/content.rs

@@ -47,10 +47,10 @@ pub fn get_data(url: &url::Url) -> Result<(Vec<u8>, Vec<u8>), String> {

}

}


-// pub fn download(content: Vec<u8>) {

-// let path = write_tmp_file(content);

-// open::that(path).unwrap();

-// }

+pub fn download(content: Vec<u8>) {

+ let path = write_tmp_file(content);

+ open::that(path).unwrap();

+}


fn write_tmp_file(content: Vec<u8>) -> std::path::PathBuf {

let mut tmp_file = NamedTempFile::new().unwrap();

diff --git a/src/main.rs b/src/main.rs

index 5cfaef3..7a012ee 100644

--- a/src/main.rs

+++ b/src/main.rs

@@ -11,6 +11,8 @@ use glib::clone;

use gtk::prelude::*;

use gtk::TextBuffer;


+use url::Url;

+

mod gui;

use gui::Gui;

mod absolute;

@@ -19,8 +21,10 @@ mod history;

mod link;

use link::Link;

mod parser;

-mod tags;

use parser::{ParseError, TextElement, TextElement::*};

+mod status;

+use status::Status;

+mod tags;


fn main() {

// Start up the GTK3 subsystem.

@@ -80,16 +84,61 @@ fn visit_url(gui: &Arc<Gui>, url: String) {


match absolute::make(url.as_str()) {

Ok(url) => match content::get_data(&url) {

- Ok((_meta, new_content)) => {

- history::append(url.as_str());

- update_url_field(&gui, url.as_str());

- let content_str = String::from_utf8_lossy(&new_content).to_string();

-

- let parsed_content = parser::parse(content_str);

- clear_buffer(&content_view);

- draw_content(&gui, parsed_content);

-

- content_view.show_all();

+ Ok((meta, new_content)) => {

+ let meta_str = String::from_utf8_lossy(&meta).to_string();

+ if let Ok(status) = Status::from_str(&meta_str) {

+ match status {

+ Status::Success(meta) => {

+ if meta.starts_with("text/") {

+ // display text files.

+ history::append(url.as_str());

+ update_url_field(&gui, url.as_str());

+ let content_str =

+ String::from_utf8_lossy(&new_content).to_string();

+

+ let parsed_content = parser::parse(content_str);

+ clear_buffer(&content_view);

+ draw_content(&gui, parsed_content);

+

+ content_view.show_all();

+ } else {

+ // download and try to open the rest.

+ content::download(new_content);

+ }

+ }

+ Status::Gone(_meta) => {

+ clear_buffer(&content_view);

+

+ let buffer = content_view.get_buffer().unwrap();

+ let mut end_iter = buffer.get_end_iter();

+

+ buffer.insert_markup(

+ &mut end_iter,

+ "<span foreground=\"red\" size=\"x-large\">Sorry page is gone.</span>\n",

+ );

+ }

+ Status::RedirectTemporary(new_url)

+ | Status::RedirectPermanent(new_url) => {

+ visit_url(&gui, new_url);

+ }

+ Status::TransientCertificateRequired(_meta)

+ | Status::AuthorisedCertificatedRequired(_meta) => {

+ clear_buffer(&content_view);

+

+ let buffer = content_view.get_buffer().unwrap();

+ let mut end_iter = buffer.get_end_iter();

+

+ buffer.insert_markup(

+ &mut end_iter,

+ "<span foreground=\"red\" size=\"x-large\">You need a valid certificate to access this page.</span>\n",

+ );

+ }

+ // Status::Input(message) => {

+ // prompt_for_answer(s, url_copy, message);

+ // }

+ _ => (),

+ }

+ }

}

Err(_) => {

let buffer = content_view.get_buffer().unwrap();

@@ -167,9 +216,6 @@ fn draw_content(gui: &Arc<Gui>, content: Vec<Result<TextElement, ParseError>>) -

}


fn draw_link(gui: &Arc<Gui>, link_item: String) {

- let content_view = gui.content_view();

- let buffer = content_view.get_buffer().unwrap();

-

match Link::from_str(&link_item) {

Ok(Link::Http(url, label)) => {

let button_label = if label.is_empty() {

@@ -179,19 +225,7 @@ fn draw_link(gui: &Arc<Gui>, link_item: String) {

};

let www_label = format!("{} [WWW]", button_label);


- let button = gtk::Button::new_with_label(&www_label);

- button.set_tooltip_text(Some(&url.to_string()));

-

- button.connect_clicked(clone!(@weak gui => move |_| {

- let new_url = absolute::make(&url.clone().to_string()).unwrap().to_string();

- visit_url(&gui, new_url);

- }));

-

- let mut start_iter = buffer.get_end_iter();

- let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();

- content_view.add_child_at_anchor(&button, &anchor);

- let mut end_iter = buffer.get_end_iter();

- buffer.insert(&mut end_iter, "\n");

+ insert_external_button(&gui, url, &www_label);

}

Ok(Link::Gopher(url, label)) => {

let button_label = if label.is_empty() {

@@ -200,66 +234,60 @@ fn draw_link(gui: &Arc<Gui>, link_item: String) {

label

};

let gopher_label = format!("{} [Gopher]", button_label);

+ insert_external_button(&gui, url, &gopher_label);

+ }

+ Ok(Link::Gemini(url, label)) => {

+ insert_gemini_button(&gui, url, label);

+ }

+ Ok(Link::Relative(url, label)) => {

+ let new_url = absolute::make(&url.clone().to_string()).unwrap();

+ insert_gemini_button(&gui, new_url, label);

+ }

+ Ok(Link::Unknown(_, _)) => (),

+ Err(_) => (),

+ }

+}


- let button = gtk::Button::new_with_label(&gopher_label);

- button.set_tooltip_text(Some(&url.to_string()));

+fn insert_gemini_button(gui: &Arc<Gui>, url: Url, label: String) {

+ let content_view = gui.content_view();

+ let buffer = content_view.get_buffer().unwrap();


- button.connect_clicked(clone!(@weak gui => move |_| {

- let new_url = absolute::make(&url.clone().to_string()).unwrap().to_string();

- visit_url(&gui, new_url);

- }));

+ let button_label = if label.is_empty() {

+ url.clone().to_string()

+ } else {

+ label

+ };


- let mut start_iter = buffer.get_end_iter();

- let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();

- content_view.add_child_at_anchor(&button, &anchor);

- let mut end_iter = buffer.get_end_iter();

- buffer.insert(&mut end_iter, "\n");

- }

- Ok(Link::Gemini(url, label)) => {

- let button_label = if label.is_empty() {

- url.clone().to_string()

- } else {

- label

- };

+ let button = gtk::Button::new_with_label(&button_label);

+ button.set_tooltip_text(Some(&url.to_string()));


- let button = gtk::Button::new_with_label(&button_label);

- button.set_tooltip_text(Some(&url.to_string()));

+ button.connect_clicked(clone!(@weak gui => move |_| {

+ visit_url(&gui, url.to_string());

+ }));


- button.connect_clicked(clone!(@weak gui => move |_| {

- let new_url = absolute::make(&url.clone().to_string()).unwrap().to_string();

- visit_url(&gui, new_url);

- }));

+ let mut start_iter = buffer.get_end_iter();

+ let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();

+ content_view.add_child_at_anchor(&button, &anchor);

+ let mut end_iter = buffer.get_end_iter();

+ buffer.insert(&mut end_iter, "\n");

+}


- let mut start_iter = buffer.get_end_iter();

- let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();

- content_view.add_child_at_anchor(&button, &anchor);

- let mut end_iter = buffer.get_end_iter();

- buffer.insert(&mut end_iter, "\n");

- }

- Ok(Link::Relative(url, label)) => {

- let button_label = if label.is_empty() {

- url.clone().to_string()

- } else {

- label

- };

+fn insert_external_button(gui: &Arc<Gui>, url: Url, label: &str) {

+ let content_view = gui.content_view();

+ let buffer = content_view.get_buffer().unwrap();


- let button = gtk::Button::new_with_label(&button_label);

- button.set_tooltip_text(Some(&url.to_string()));

+ let button = gtk::Button::new_with_label(&label);

+ button.set_tooltip_text(Some(&url.to_string()));


- button.connect_clicked(clone!(@weak gui => move |_| {

- let new_url = absolute::make(&url.clone().to_string()).unwrap().to_string();

- visit_url(&gui, new_url);

- }));

+ button.connect_clicked(move |_| {

+ open::that(url.to_string()).unwrap();

+ });


- let mut start_iter = buffer.get_end_iter();

- let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();

- content_view.add_child_at_anchor(&button, &anchor);

- let mut end_iter = buffer.get_end_iter();

- buffer.insert(&mut end_iter, "\n");

- }

- Ok(Link::Unknown(_, _)) => (),

- Err(_) => (),

- }

+ let mut start_iter = buffer.get_end_iter();

+ let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();

+ content_view.add_child_at_anchor(&button, &anchor);

+ let mut end_iter = buffer.get_end_iter();

+ buffer.insert(&mut end_iter, "\n");

}


fn clear_buffer(view: &gtk::TextView) {

diff --git a/src/status.rs b/src/status.rs

new file mode 100644

index 0000000..2b70940

--- /dev/null

+++ b/src/status.rs

@@ -0,0 +1,84 @@

+extern crate regex;

+use regex::Regex;

+

+use std::str::FromStr;

+

+#[derive(Debug)]

+pub enum Status {

+ Input(String),

+ Success(String),

+ SuccessEndOfClientCertificateSession(String),

+ RedirectTemporary(String),

+ RedirectPermanent(String),

+ TemporaryFailure(String),

+ ServerUnavailable(String),

+ CGIError(String),

+ ProxyError(String),

+ SlowDown(String),

+ PermanentFailure(String),

+ NotFound(String),

+ Gone(String),

+ ProxyRequestRefused(String),

+ BadRequest(String),

+ ClientCertificateRequired(String),

+ TransientCertificateRequired(String),

+ AuthorisedCertificatedRequired(String),

+ CertificateNotAccepted(String),

+ FutureCertificateRejected(String),

+ ExpiredCertificateRejected(String),

+ Unknown(String),

+}

+

+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

+pub struct ParseError;

+

+const STATUS_REGEX: &str = r"^(\d{1,3})[ \t](.*)\r\n$";

+

+impl FromStr for Status {

+ type Err = ParseError;

+

+ // Parses a &str into an instance of 'Header'

+ fn from_str(line: &str) -> Result<Self, Self::Err> {

+ let status_regexp = Regex::new(STATUS_REGEX).unwrap();

+

+ match status_regexp.captures(&line) {

+ Some(caps) => {

+ let code_str = caps.get(1).map_or("", |m| m.as_str());

+ let meta_str = caps.get(2).map_or("", |m| m.as_str());

+

+ let code: i16 = code_str.parse().expect("2");

+ let meta = meta_str.to_string();

+

+ Ok(make_status(code, meta))

+ }

+ None => Err(ParseError),

+ }

+ }

+}

+

+fn make_status(code: i16, meta: String) -> Status {

+ match code {

+ 10 => Status::Input(meta),

+ 20 => Status::Success(meta),

+ 21 => Status::SuccessEndOfClientCertificateSession(meta),

+ 30 => Status::RedirectTemporary(meta),

+ 31 => Status::RedirectPermanent(meta),

+ 40 => Status::TemporaryFailure(meta),

+ 41 => Status::ServerUnavailable(meta),

+ 42 => Status::CGIError(meta),

+ 43 => Status::ProxyError(meta),

+ 44 => Status::SlowDown(meta),

+ 50 => Status::PermanentFailure(meta),

+ 51 => Status::NotFound(meta),

+ 52 => Status::Gone(meta),

+ 53 => Status::ProxyRequestRefused(meta),

+ 59 => Status::BadRequest(meta),

+ 60 => Status::ClientCertificateRequired(meta),

+ 61 => Status::TransientCertificateRequired(meta),

+ 62 => Status::AuthorisedCertificatedRequired(meta),

+ 63 => Status::CertificateNotAccepted(meta),

+ 64 => Status::FutureCertificateRejected(meta),

+ 65 => Status::ExpiredCertificateRejected(meta),

+ _ => Status::Unknown(meta),

+ }

+}



---

Served by Pollux Gemini Server.

-- Response ended

-- Page fetched on Sun May 19 07:56:29 2024