-- Leo's gemini proxy

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

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

commit 15846feec2f3d66906ccd6bbd0ed2fdd129fe60e

Author: Julien Blanchard <julien@sideburns.eu>

Date: Fri Oct 4 11:56:58 2019 +0200


Don't try to display non-text content in the SelectView


Fix a bug where opening an image was making Asuka crash

after closing the external viewer.


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

index 1606dab..184d9e4 100644

--- a/src/content.rs

+++ b/src/content.rs

@@ -1,5 +1,5 @@

-use tempfile::NamedTempFile;

use std::io::{Read, Write};

+use tempfile::NamedTempFile;


use native_tls::TlsConnector;

use std::net::{TcpStream, ToSocketAddrs};

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

let mut res = vec![];

stream.read_to_end(&mut res).unwrap();


- let clrf_idx = find_subsequence(&res, b"\r\n");

+ let clrf_idx = find_clrf(&res);

let content = res.split_off(clrf_idx.unwrap() + 2);


Ok((res, content))

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

}

None => Err(format!("Could not connect to {}", urlf)),

},

- Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e))

+ Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)),

}

}


@@ -59,6 +59,7 @@ fn write_tmp_file(content: Vec<u8>) -> std::path::PathBuf {

path

}


-fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {

- haystack.windows(needle.len()).position(|window| window == needle)

+fn find_clrf(data: &[u8]) -> Option<usize> {

+ let clrf = b"\r\n";

+ data.windows(clrf.len()).position(|window| window == clrf)

}

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

index e64edb1..ca29775 100644

--- a/src/history.rs

+++ b/src/history.rs

@@ -1,5 +1,5 @@

-use url::Url;

use std::sync::Mutex;

+use url::Url;


lazy_static! {

static ref HISTORY: Mutex<Vec<Url>> = Mutex::new(vec![]);

@@ -23,13 +23,11 @@ pub fn append(url: &str) {

pub fn get_current_host() -> Option<String> {

let history = HISTORY.lock().unwrap();

match history.last() {

- Some(current_url) => {

- match current_url.host_str() {

- Some(host) => Some(String::from(host)),

- None => None

- }

- }

- None => None

+ Some(current_url) => match current_url.host_str() {

+ Some(host) => Some(String::from(host)),

+ None => None,

+ },

+ None => None,

}

}


@@ -40,9 +38,9 @@ pub fn get_current_url() -> Option<String> {

let current_path = current_url.join("./");

match current_path {

Ok(path) => Some(path.to_string()),

- Err(_) => None

+ Err(_) => None,

}

}

- None => None

+ None => None,

}

}

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

index a1aa1ff..c9ac638 100644

--- a/src/link.rs

+++ b/src/link.rs

@@ -1,8 +1,8 @@

extern crate regex;

+use json::JsonValue;

use regex::Regex;

-use url::Url;

use std::str::FromStr;

-use json::JsonValue;

+use url::Url;


pub enum Link {

@@ -50,19 +50,15 @@ impl FromStr for Link {

fn make_link(url: String, label: String) -> Option<Link> {

let urlp = Url::parse(&url);

match urlp {

- Ok(url) => {

- match url.scheme() {

- "gemini" => Some(Link::Gemini(url, label)),

- "gopher" => Some(Link::Gopher(url, label)),

- "http" => Some(Link::Http(url, label)),

- "https" => Some(Link::Http(url, label)),

- _ => Some(Link::Unknown(url, label))

- }

- },

- Err(url::ParseError::RelativeUrlWithoutBase) => {

- Some(Link::Relative(url, label))

+ Ok(url) => match url.scheme() {

+ "gemini" => Some(Link::Gemini(url, label)),

+ "gopher" => Some(Link::Gopher(url, label)),

+ "http" => Some(Link::Http(url, label)),

+ "https" => Some(Link::Http(url, label)),

+ _ => Some(Link::Unknown(url, label)),

},

- _ => None

+ Err(url::ParseError::RelativeUrlWithoutBase) => Some(Link::Relative(url, label)),

+ _ => None,

}

}


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

index 4ee3a51..13051c5 100644

--- a/src/main.rs

+++ b/src/main.rs

@@ -23,9 +23,9 @@ use status::Status;

mod link;

use link::Link;


+mod absolute;

mod content;

mod history;

-mod absolute;


const HELP: &str = "Welcome to Asuka Gemini browser!


@@ -151,15 +151,15 @@ fn visit_url(s: &mut Cursive, url: &Url) {

}


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

- Ok(url) => {

- match content::get_data(&url) {

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

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

- draw_content(s, &url, meta, new_content);

- }

- Err(msg) => {

- s.add_layer(Dialog::info(msg));

- }

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

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

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

+ // handle meta header

+ let response = handle_response_status(s, &url, meta, new_content);

+ draw_content(s, &url, response);

+ }

+ Err(msg) => {

+ s.add_layer(Dialog::info(msg));

}

},

Err(_) => {

@@ -168,87 +168,12 @@ fn visit_url(s: &mut Cursive, url: &Url) {

}

}


-fn draw_content(s: &mut Cursive, url: &Url, meta: Vec<u8>, content: Vec<u8>) {

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

-

- // handle meta header

- handle_response_status(s, url, meta, content);

-

- let mut main_view = match s.find_id::<SelectView>("main") {

- Some(view) => view,

- None => panic!("Can't find main view."),

- };

-

- // set title and clear old content

- set_title(s, url.as_str());

- main_view.clear();

-

- // draw new content lines

- for line in content_str.lines() {

- match Link::from_str(line) {

- Ok(link) => match link {

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

- let mut formatted = StyledString::new();

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

- formatted.append(StyledString::styled(

- www_label,

- Style::from(Color::Dark(BaseColor::Green)).combine(Effect::Bold),

- ));

-

- let data = object! {

- "type" => "www",

- "url" => url.to_string()

- };

- main_view.add_item(formatted, json::stringify(data))

- }

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

- let mut formatted = StyledString::new();

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

- formatted.append(StyledString::styled(

- gopher_label,

- Style::from(Color::Light(BaseColor::Magenta)).combine(Effect::Bold),

- ));

-

- let data = object! {

- "type" => "gopher",

- "url" => url.to_string()

- };

- main_view.add_item(formatted, json::stringify(data))

- }

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

- let mut formatted = StyledString::new();

- formatted.append(StyledString::styled(

- label,

- Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold),

- ));

-

- let data = object! {

- "type" => "gemini",

- "url" => url.to_string()

- };

- main_view.add_item(formatted, json::stringify(data))

- }

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

- let mut formatted = StyledString::new();

- formatted.append(StyledString::styled(

- label,

- Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold),

- ));

-

- let data = object! {

- "type" => "gemini",

- "url" => url.to_string()

- };

- main_view.add_item(formatted, json::stringify(data))

- }

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

- },

- Err(_) => main_view.add_item(str::replace(line, "\t", " "), String::from("0")),

- }

- }

-}

-

-fn handle_response_status(s: &mut Cursive, url: &Url, meta: Vec<u8>, content: Vec<u8>) {

+fn handle_response_status(

+ s: &mut Cursive,

+ url: &Url,

+ meta: Vec<u8>,

+ content: Vec<u8>,

+) -> Option<Vec<u8>> {

let url_copy = url.clone();

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


@@ -257,38 +182,126 @@ fn handle_response_status(s: &mut Cursive, url: &Url, meta: Vec<u8>, content: Ve

Status::Success(meta) => {

if meta.starts_with("text/") {

// display text files.

- {}

+ Some(content)

} else {

// download and try to open the rest.

content::download(content);

- return;

+ None

}

}

Status::Gone(_meta) => {

s.add_layer(Dialog::info("Sorry page is gone."));

- return;

+ None

}

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

- return follow_link(s, &new_url)

+ follow_link(s, &new_url);

+ None

}

Status::TransientCertificateRequired(_meta)

| Status::AuthorisedCertificatedRequired(_meta) => {

s.add_layer(Dialog::info(

"You need a valid certificate to access this page.",

));

- return;

+ None

}

Status::Input(message) => {

prompt_for_answer(s, url_copy, message);

+ None

}

other_status => {

s.add_layer(Dialog::info(format!("ERROR: {:?}", other_status)));

- return;

+ None

}

}

+ } else {

+ None

}

}


+fn draw_content(s: &mut Cursive, url: &Url, content: Option<Vec<u8>>) {

+ match content {

+ Some(data) => {

+ let mut main_view = match s.find_id::<SelectView>("main") {

+ Some(view) => view,

+ None => panic!("Can't find main view."),

+ };

+

+ // set title and clear old content

+ set_title(s, url.as_str());

+ main_view.clear();

+

+ let content_str = String::from_utf8_lossy(&data).to_string();

+

+ // draw new content lines

+ for line in content_str.lines() {

+ match Link::from_str(line) {

+ Ok(link) => match link {

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

+ let mut formatted = StyledString::new();

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

+ formatted.append(StyledString::styled(

+ www_label,

+ Style::from(Color::Dark(BaseColor::Green)).combine(Effect::Bold),

+ ));

+

+ let data = object! {

+ "type" => "www",

+ "url" => url.to_string()

+ };

+ main_view.add_item(formatted, json::stringify(data))

+ }

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

+ let mut formatted = StyledString::new();

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

+ formatted.append(StyledString::styled(

+ gopher_label,

+ Style::from(Color::Light(BaseColor::Magenta)).combine(Effect::Bold),

+ ));

+

+ let data = object! {

+ "type" => "gopher",

+ "url" => url.to_string()

+ };

+ main_view.add_item(formatted, json::stringify(data))

+ }

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

+ let mut formatted = StyledString::new();

+ formatted.append(StyledString::styled(

+ label,

+ Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold),

+ ));

+

+ let data = object! {

+ "type" => "gemini",

+ "url" => url.to_string()

+ };

+ main_view.add_item(formatted, json::stringify(data))

+ }

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

+ let mut formatted = StyledString::new();

+ formatted.append(StyledString::styled(

+ label,

+ Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold),

+ ));

+

+ let data = object! {

+ "type" => "gemini",

+ "url" => url.to_string()

+ };

+ main_view.add_item(formatted, json::stringify(data))

+ }

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

+ },

+ Err(_) => {

+ main_view.add_item(str::replace(line, "\t", " "), String::from("0"))

+ }

+ }

+ }

+ }

+ None => (),

+ };

+}

+

fn set_title(s: &mut Cursive, text: &str) {

let mut container = match s.find_id::<Dialog>("container") {

Some(view) => view,

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

index 93d69d5..2b70940 100644

--- a/src/status.rs

+++ b/src/status.rs

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

63 => Status::CertificateNotAccepted(meta),

64 => Status::FutureCertificateRejected(meta),

65 => Status::ExpiredCertificateRejected(meta),

- _ => Status::Unknown(meta)

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

}

}



---

Served by Pollux Gemini Server.

-- Response ended

-- Page fetched on Sun May 19 12:17:57 2024