From 15846feec2f3d66906ccd6bbd0ed2fdd129fe60e Mon Sep 17 00:00:00 2001 From: Julien Blanchard Date: Fri, 4 Oct 2019 11:56:58 +0200 Subject: [PATCH] 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. --- src/content.rs | 11 +-- src/history.rs | 18 ++--- src/link.rs | 24 +++--- src/main.rs | 207 ++++++++++++++++++++++++++----------------------- src/status.rs | 2 +- 5 files changed, 135 insertions(+), 127 deletions(-) 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, Vec), 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, Vec), 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) -> std::path::PathBuf { path } -fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option { - haystack.windows(needle.len()).position(|window| window == needle) +fn find_clrf(data: &[u8]) -> Option { + 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> = Mutex::new(vec![]); @@ -23,13 +23,11 @@ pub fn append(url: &str) { pub fn get_current_host() -> Option { 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 { 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; #[derive(Debug)] pub enum Link { @@ -50,19 +50,15 @@ impl FromStr for Link { fn make_link(url: String, label: String) -> Option { 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, content: Vec) { - 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::("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, content: Vec) { +fn handle_response_status( + s: &mut Cursive, + url: &Url, + meta: Vec, + content: Vec, +) -> Option> { 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, 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>) { + match content { + Some(data) => { + let mut main_view = match s.find_id::("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::("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), } }